一个完整的语法分析、词法分析例子——Universal Pasrser

需求:用户用formal notation指定语法、词法,然后可以匹配相应的文本。用法类似正则表达式,只需给出formal notation,不需要为每一种格式的文本单独写匹配器。

formal notation主要是3个部分:

1)BNF 列表 table:给出上下文无关文法的产生式规则,以及所有的符号(终端和非终端)

2)起始符号 start

3)终端符号Regular Expression Table:用RE给出每个终端符号的匹配规则

其中BNF目前只接受left factor过的,去左递归的上下文无关文法。


关于空格的处理有3种方式,本案采用第2种:

1)作为文法的一部分:

--> S*

--> +  

2)作为terminal symbol的一部分

对每个terminal symbol的regular expression做处理,两头加上匹配空白的规则 re = "\\s+" + re + "\\s+"

3)如果不是用正则表达式匹配终端符号,而是自己写term(TOKEN type)函数

对于空格,skip掉就可以了


文法匹配就是 top down的 recursive descend 算法,

1)对于当前符号,如果是终结符号,到终结符号RE表找到对应的RE做匹配,匹配成功要把指针移动到后一个位置。

2)如果是非终结符号,尝试每个production, 直到某个production成功。(left factor 保证只有一个production是可行的),注意epsilon production放到最后。


下一步工作:

1)支持没有进行left factor的文法。现在是一个production失败了才回溯尝试下一个production。一般的情况是,一个符号V即使走某条production成功匹配了,但有可能导致V后面的符号无法匹配成功,这时候也要回溯,尝试V的别的production。

2)自动消除左递归,这样用户只需要自然的写BNF就行了

package excercise;
import java.util.*;
import java.util.regex.*;
class CFG {
	private Map> BNF;
	private Map termRegex;
	private String start, text;
	private int i = 0;
	public CFG(Map> productions, String start, Map termRegex) {
		this.BNF = productions;
		this.termRegex = termRegex;
		this.start = start;
	}
	public boolean recognize(String text) {
		this.text = text;
		return match(start) && i == text.length();
	}
	private boolean match(String V) {
		if (V.isEmpty()) return true; //epsilon
		List production = BNF.get(V);
		if (production == null) { //no production for this symbol, should be terminal symbol
			String re = termRegex.get(V); //get the regex for this terminal symbol
			if (re == null) throw new RuntimeException("invalid CFG.");
			re = "\\s*" + re + "\\s*";
			Pattern pattern = Pattern.compile(re);
			Matcher matcher = pattern.matcher(text);
			if (matcher.find(i) && matcher.start() == i) {
				i = matcher.end();
				return true;
			}
			return false;
		}
		//try each production. if one matches, succeed, or recover the pointer and try next.
		int save = i;
		for (String[] p : production) {
			boolean flag = true;
			i = save;
			for (String T : p) {
				if (!match(T)) {
					flag = false;
					break; 
				}
			}
			if (flag) {
				System.out.println(V + "->" + String.join(" ", p));
				return true;
			}
		}
		return false;
	}
	public static void main(String[] args) {
	//1) E -> TE'
	//2) E'-> +TE' | -TE' | e
	//3) T -> FT'
	//3) T'-> *FT' | /FT' | e
	//4) F -> int | (E)
		//specify the BNF table, should be left factored, left recursion removed
		Map> BNF = new HashMap>();
		List pE = new ArrayList();
		pE.add(new String[]{"T", "EA"});
		BNF.put("E", pE);
		List pEA = new ArrayList();
		pEA.add(new String[]{"+", "T", "EA"});
		pEA.add(new String[]{"-", "T", "EA"});
		pEA.add(new String[]{""});
		BNF.put("EA", pEA);
		List pT = new ArrayList();
		pT.add(new String[]{"F", "TA"});
		BNF.put("T", pT);
		List pTA = new ArrayList();
		pTA.add(new String[]{"*", "F", "TA"});
		pTA.add(new String[]{"/", "F", "TA"});
		pTA.add(new String[]{""});
		BNF.put("TA", pTA);
		List pF = new ArrayList();
		pF.add(new String[]{"INT"});
		pF.add(new String[]{"(", "E", ")"});
		BNF.put("F", pF);
		
		//specify terminal symbol regular expression table
		Map termRegex = new HashMap();
		termRegex.put("+", "\\+");
		termRegex.put("-", "\\-");
		termRegex.put("*", "\\*");
		termRegex.put("/", "/");
		termRegex.put("(", "\\(");
		termRegex.put(")", "\\)");
		termRegex.put("INT", "[0-9]");
		
		CFG cfg = new CFG(BNF, "E", termRegex);
		boolean b = cfg.recognize("(1 + 1)*2");
		System.out.println(b);
	}
}





你可能感兴趣的:(算法,系统分析设计,同类问题汇总,parser)