LL(1)文法first集

本文介绍如何计算LL(1)文法的first集,包括处理非终极符直接和间接递归的情况。首先计算可导空的非终极符集合,然后通过广度优先遍历(first集关系图)的方法进行计算。在预处理阶段,求出每个非终结符的出度,并使用队列进行遍历,将终极符加入非终结符的first集中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LL(1) 文法 first集

 思路:  利用递归(但普通递归的方法不能处理非终极符直接递归和间接递归的情况) 考虑到这种情况,见下方利用bfs遍历first集关系图的方法

第一步先计算可导空的非终极符集合
  令first(X)=空
  若 X->a... 则将a加入first(X) 
  若 X->ABC.. ,则计算first(A) ,first(B) ,first(C)..直到遇到第一个不导空的非终结符或者终极符,则将他们都加入first(X) 并除去^(空串)的情况
  若 右部可以导空,将右部所有的非终结符的first集加入first(X),并加上空串的情况

package first_collection;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * first集
 * 第一步先计算可导空的非终极符集合
 * 令first(X)=空
 * 若 X->a... 则将a加入first(X) 
 * 若 X->ABC.. ,则计算first(A) ,first(B) ,first(C)..直到遇到第一个不导空的非终结符或者终极符,则将他们都加入first(X) 并除去^(空串)的情况
 * 若 右部可以导空,将右部所有的非终结符的first集加入first(X),并加上空串的情况
 * 
 * @author parting_soul
 *
 */
public class FirstCollection {
	private Set<Character> emptyNonTerminal;
	private List<Rule> ruleLists;
	private Set<Character> terminalC;
	private Set<Character> nonterminalC;
	private Map<String, Set<Character>> first;

	public FirstCollection() {
		ruleLists = new ArrayList<>();
		terminalC = new HashSet<>();
		nonterminalC = new HashSet<>();
		init();
	}

	/**
	 * 输入产生式,求出可导空的非终结符
	 */
	private void init() {
		inputRuleLists();
		// 计算可导空的非终结符集合
		EmptyNonterminalSet nonterminalSet = new EmptyNonterminalSet(ruleLists);
		nonterminalSet.solve();
		emptyNonTerminal = nonterminalSet.getR();
		first = new HashMap<String, Set<Character>>();
		initRuleVisit();
	}

	private void initRuleVisit() {
		for (Rule r : ruleLists) {
			r.isVisit = false;
		}
	}

	public void getTerminalFirst(char noTernimalLeft) {
		Rule r = null;
		Set<Character> terminalFirstSet = null;
		// 判断之前是否存在该非终结符的first集
		if ((terminalFirstSet = first.get(noTernimalLeft + "")) == null) {
			// 没有计算过,新建一个集合用来保存该非终结符的first集
			terminalFirstSet = new HashSet<>();
			first.put(noTernimalLeft + "", terminalFirstSet);
			// System.out.println(noTernimalLeft + " create ");
		}
		for (int i = 0; i < ruleLists.size(); i++) {
			// 遍历所有的产生式,计算每一个左边为该非终结符产生式的first集
			r = ruleLists.get(i);
			// 如果该产生式别计算过,直接选择下一个产生式
			if (r.isVisit)
				continue;
			if (r.left.equals(noTernimalLeft + "")) {
				// System.out.println(r);
				// 找到左部为该非终极符的产生式,标记为访问过
				r.isVisit = true;
				String right = r.right;
				for (int j = 0; j < right.length(); j++) {
					// 右部从左到右扫描每一个字符
					char c = right.charAt(j);
					if (!isNonTerminal(c)) {
						// 如果是非终结符,直接加入到first集,然后停止计算
						terminalFirstSet.add(c);
						// System.out.println(r.left + " -> " + r.right + " " +
						// c);
						break;
					} else {
						// 如果当前为非终结符,递归计算该非终结符的first集
						getTerminalFirst(c);
						// System.out.println(r.left + " -> " + r.right + " " +
						// c + " nonterminal " + first.get(c + ""));
						// 将该非终结符的first集加入当前待求的first集中
						terminalFirstSet.addAll(first.get(c + ""));
						// 除去空串
						terminalFirstSet.remove('^');
						if (!emptyNonTerminal.contains(c)) {
							// 如果当前非终结符不可导空,不用继续计算,跳出循环
							break;
						}
					}
				}
				// 判断当前产生式最后一个字符是否可导空
				char last = right.charAt(right.length() - 1);
				if (emptyNonTerminal.contains(last)) {
					// 可导空则加入空串
					terminalFirstSet.add('^');
				}
			}
		}
	}

	public Map<String, Set<Character>> getFirstCollection() {
		for (Character c : nonterminalC) {
			// System.out.println(" current " + c);
			// 遍历每一个非终结符,求出该非终结符的first集
			getTerminalFirst(c);
		}
		return first;
	}

	public static void main(String[] args) {
		FirstCollection collection = new FirstCollection();
		collection.printTerAndNon();
		Map<String, Set<Character>> first = collection.getFirstCollection();
		System.out.println(first);
	}

	/**
	 * 输入产生式
	 */
	private void inputRuleLists() {
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("rule.txt"))));
			String str = null;
			while ((str = reader.readLine()) != null) {
				// System.out.println(str);
				saveRule(str);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					reader = null;
				}
			}
		}
	}

	/**
	 * 分析产生式
	 * 
	 * @param str
	 */
	private void saveRule(String str) {
		String[] strs = str.split("->");
		// System.out.println(strs.length);
		if (strs.length == 2) {
			Rule r = new Rule();
			r.left = strs[0];
			r.right = strs[1];
			if (isNonTerminal(r.left.charAt(0))) {
				nonterminalC.add(r.left.charAt(0));
				for (int i = 0; i < r.right.length(); i++) {
					char c = r.right.charAt(i);
					if (!isNonTerminal(c)) {
						terminalC.add(c);
					} else {
						nonterminalC.add(c);
					}
				}
				ruleLists.add(r);
			}
		}
	}

	void printTerAndNon() {
		System.out.println("终结符:");
		for (Character c : terminalC) {
			System.out.print(c + " ");
		}
		System.out.println();
		System.out.println("非终结符:");
		for (Character c : nonterminalC) {
			System.out.print(c + " ");
		}
		System.out.println();
		System.out.println("产生式");
		for (Rule r : ruleLists) {
			System.out.println(r.left + "->" + r.right);
		}
		System.out.println("可导空非终极符");
		System.out.println(emptyNonTerminal);
	}

	/**
	 * 是否为终极符
	 * 
	 * @param c
	 * @return
	 */
	private boolean isNonTerminal(char c) {
		if (c >= 'A' && c <= 'Z') {
			return true;
		}
		return false;
	}

}

/**
 * 求出可导空的非终极符集合类
 * 
 * @author parting_soul
 *
 */
class EmptyNonterminalSet {
	private List<Rule> L;

	private Set<Character> R = new HashSet<>();

	public EmptyNonterminalSet(List<Rule> L) {
		this.L = L;
	}

	public Set<Character> getR() {
		return R;
	}

	public void solve() {
		// 先扫描一遍所有的产生式,将右端为空的且左端为终结符的产生式左端加入集合R中(这里^号表示空)
		for (int i = 0; i < L.size(); i++) {
			Rule r = L.get(i);
			if (r.right.equals("^") && !isTerminator(r.left.charAt(0))) {
				R.add(r.left.charAt(0));
				r.isVisit = true;
			}
		}

		int rLength = 0;
		while (rLength != R.size()) {
			rLength = R.size();
			for (int i = 0; i < L.size(); i++) {
				Rule r = L.get(i);
				if (!r.isVisit && isBelongToR(r.right)) {
					R.add(r.left.charAt(0));
					r.isVisit = true;
				}
			}
		}
	}

	/**
	 * 判断产生式右端的所有字符是否属于R
	 * 
	 * @return
	 */
	private boolean isBelongToR(String right) {
		for (int i = 0; i < right.length(); i++) {
			if (!R.contains(right.charAt(i))) {
				// System.out.println(right.charAt(i));
				return false;
			}
		}
		return true;
	}

	// 判断是否是终结符
	public boolean isTerminator(Character c) {
		return Character.isLowerCase(c);
	}

	public void print() {
		for (Rule r : L) {
			System.out.println(r);
		}
	}

	public void printR() {
		for (Character c : R) {
			System.out.print(c + " ");
		}
		System.out.println();
	}

}

class Rule {
	String left;
	String right;
	boolean isVisit;

	@Override
	public String toString() {
		return "Rule [left=" + left + ", right=" + right + ", isVisit=" + isVisit + "]";
	}

}

利用bfs的方法

first集 思想: 利用图的广度遍历
对文法先进行预处理,求出每一个非终结符的出度
然后将待求的非终结符加入到队列,并标记为访问过,然后开始广度优先遍历,
队列为空,则停止遍历,若出队的是终极符,则加入到该非终结符的first集中
若出队的是非终结符,则将该非终结符中没有被访问过的出度加入到队列中


package first_collection_bfs;

import java.io.*;
import java.util.*;

/**
 * first集 思想: 利用图的广度遍历
 * 对文法先进行预处理,求出每一个非终结符的出度
 * 然后将待求的非终结符加入到队列,并标记为访问过,然后开始广度优先遍历,
 * 队列为空,则停止遍历,若出队的是终极符,则加入到该非终结符的first集中
 * 若出队的是非终结符,则将该非终结符中没有被访问过的出度加入到队列中
 *
 * @author parting_soul
 */
public class FirstCollection {
    private Set<Character> emptyNonTerminal;
    private List<Rule> ruleLists;
    private Set<Character> terminalC;
    private Set<Character> nonterminalC;
    private Map<String, Set<Character>> first;
    private Map<Character, Set<Character>> map;
    private Queue<Character> queue;

    private Map<Character, Boolean> isVisistMap;

    public FirstCollection() {
        ruleLists = new ArrayList<>();
        terminalC = new HashSet<>();
        nonterminalC = new HashSet<>();
        map = new HashMap<>();
        queue = new LinkedList<>();
        isVisistMap = new HashMap<>();
        init();
    }

    /**
     * 输入产生式,求出可导空的非终结符
     */
    private void init() {
        inputRuleLists();
        // 计算可导空的非终结符集合
        EmptyNonterminalSet nonterminalSet = new EmptyNonterminalSet(ruleLists);
        nonterminalSet.solve();
        emptyNonTerminal = nonterminalSet.getR();
        getMap();
        first = new HashMap<>();
    }

    /**
     * 预处理,得到每一个非终结符的出度
     */
    private void getMap() {
        for (char c : nonterminalC) {
            for (int j = 0; j < ruleLists.size(); j++) {
                Rule rule = ruleLists.get(j);
                if (rule.left.equals(c + "")) {
                    Set<Character> characters = map.get(c);
                    if (characters == null) {
                        characters = new HashSet<>();
                    }
                    for (int k = 0; k < rule.right.length(); k++) {
                        char currentC = rule.right.charAt(k);
                        if ("^".equals(currentC + "")) break;
                        characters.add(currentC);
                        if (!isNonTerminal(currentC) || !emptyNonTerminal.contains(currentC)) {
                            break;
                        }
                    }
                    map.put(c, characters);
                }

            }
        }
    }

    /**
     * bfs求非终结符first
     *
     * @param noTernimalLeft
     */
    public void getNonTerminalFirstByBFS(char noTernimalLeft) {
        queue.clear();
        //将待求的非终结符加入队列
        queue.offer(noTernimalLeft);
        //标记为访问过
        isVisistMap.put(noTernimalLeft, true);
        Set<Character> sets = new HashSet<>();
        first.put(noTernimalLeft + "", sets);
        while (queue.size() != 0) {
            //队首出队
            char currentC = queue.poll();
            if (!isNonTerminal(currentC)) {
                //如果是终结符加入到该非终结符的first中
                sets.add(currentC);
                continue;
            }
            //如果是非终结符,就遍历该非终结符的出度
            Set<Character> characters = map.get(currentC);
            for (char c : characters) {
                if (!isNonTerminal(c)) {
                    //如果是终结符,直接加入队列
                    queue.offer(c);
                    continue;
                }
                //如果是非终结符就先判断是否访问过,没有访问过就入队,并标记为访问过
                boolean isVisist = isVisistMap.get(c);
                if (isVisist) continue;
                isVisistMap.put(c, true);
                queue.offer(c);
            }
        }

    }

    public Map<String, Set<Character>> getFirstCollection() {
        for (Character c : nonterminalC) {
            // System.out.println(" current " + c);
            // 遍历每一个非终结符,求出该非终结符的first集
            getNonTerminalFirstByBFS(c);
            //重置非终结符为未访问
            initNonTernimalVisit();
            //若该非终结符可导空,加入^
            if (emptyNonTerminal.contains(c)) {
                first.get(c.charValue() + "").add('^');
            }
        }
        return first;
    }

    private void initNonTernimalVisit() {
        for (char c : nonterminalC) {
            isVisistMap.put(c, false);
        }
    }


    public static void main(String[] args) {
        FirstCollection collection = new FirstCollection();
        collection.printTerAndNon();
        Map<String, Set<Character>> first = collection.getFirstCollection();
        System.out.println(first);
    }

    /**
     * 输入产生式
     */
    private void inputRuleLists() {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("rule1.txt"))));
            String str = null;
            while ((str = reader.readLine()) != null) {
                // System.out.println(str);
                saveRule(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    reader = null;
                }
            }
        }
    }

    /**
     * 分析产生式
     *
     * @param str
     */
    private void saveRule(String str) {
        String[] strs = str.split("->");
        // System.out.println(strs.length);
        if (strs.length == 2) {
            Rule r = new Rule();
            r.left = strs[0];
            r.right = strs[1];
            if (isNonTerminal(r.left.charAt(0))) {
                nonterminalC.add(r.left.charAt(0));
                isVisistMap.put(r.left.charAt(0), false);
                for (int i = 0; i < r.right.length(); i++) {
                    char c = r.right.charAt(i);
                    if (!isNonTerminal(c)) {
                        terminalC.add(c);
                    } else {
                        nonterminalC.add(c);
                    }
                }
                ruleLists.add(r);
            }
        }
    }

    void printTerAndNon() {
        System.out.println("终结符:");
        for (Character c : terminalC) {
            System.out.print(c + " ");
        }
        System.out.println();
        System.out.println("非终结符:");
        for (Character c : nonterminalC) {
            System.out.print(c + " ");
        }
        System.out.println();
        System.out.println("产生式");
        for (Rule r : ruleLists) {
            System.out.println(r.left + "->" + r.right);
        }
        System.out.println("可导空非终极符");
        System.out.println(emptyNonTerminal);
    }

    /**
     * 是否为非终极符
     *
     * @param c
     * @return
     */
    private boolean isNonTerminal(char c) {
        if (c >= 'A' && c <= 'Z') {
            return true;
        }
        return false;
    }

}

/**
 * 求出可导空的非终极符集合类
 *
 * @author parting_soul
 */
class EmptyNonterminalSet {
    private List<Rule> L;

    private Set<Character> R = new HashSet<>();

    public EmptyNonterminalSet(List<Rule> L) {
        this.L = L;
    }

    public Set<Character> getR() {
        return R;
    }

    public void solve() {
        // 先扫描一遍所有的产生式,将右端为空的且左端为终结符的产生式左端加入集合R中(这里#号表示空)
        for (int i = 0; i < L.size(); i++) {
            Rule r = L.get(i);
            if (r.right.equals("^") && !isTerminator(r.left.charAt(0))) {
                R.add(r.left.charAt(0));
                r.isVisit = true;
            }
        }

        int rLength = 0;
        while (rLength != R.size()) {
            rLength = R.size();
            for (int i = 0; i < L.size(); i++) {
                Rule r = L.get(i);
                if (!r.isVisit && isBelongToR(r.right)) {
                    R.add(r.left.charAt(0));
                    r.isVisit = true;
                }
            }
        }
    }

    /**
     * 判断产生式右端的所有字符是否属于R
     *
     * @return
     */
    private boolean isBelongToR(String right) {
        for (int i = 0; i < right.length(); i++) {
            if (!R.contains(right.charAt(i))) {
                // System.out.println(right.charAt(i));
                return false;
            }
        }
        return true;
    }

    // 判断是否是终结符
    public boolean isTerminator(Character c) {
        return Character.isLowerCase(c);
    }

    public void print() {
        for (Rule r : L) {
            System.out.println(r);
        }
    }

    public void printR() {
        for (Character c : R) {
            System.out.print(c + " ");
        }
        System.out.println();
    }

}

class Rule {
    String left;
    String right;
    boolean isVisit;

    @Override
    public String toString() {
        return "Rule [left=" + left + ", right=" + right + ", isVisit=" + isVisit + "]";
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值