1. 程式人生 > >一個完整的語法分析、詞法分析例子——Universal Pasrser

一個完整的語法分析、詞法分析例子——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)作為文法的一部分:

<SPACE> --> S*

<E> --> <SPACE> <E> <SPACE> + <SPACE>  <E> <SPACE>

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<String, List<String[]>> BNF;
	private Map<String, String> termRegex;
	private String start, text;
	private int i = 0;
	public CFG(Map<String, List<String[]>> productions, String start, Map<String, String> 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<String[]> 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<String, List<String[]>> BNF = new HashMap<String, List<String[]>>();
		List<String[]> pE = new ArrayList<String[]>();
		pE.add(new String[]{"T", "EA"});
		BNF.put("E", pE);
		List<String[]> pEA = new ArrayList<String[]>();
		pEA.add(new String[]{"+", "T", "EA"});
		pEA.add(new String[]{"-", "T", "EA"});
		pEA.add(new String[]{""});
		BNF.put("EA", pEA);
		List<String[]> pT = new ArrayList<String[]>();
		pT.add(new String[]{"F", "TA"});
		BNF.put("T", pT);
		List<String[]> pTA = new ArrayList<String[]>();
		pTA.add(new String[]{"*", "F", "TA"});
		pTA.add(new String[]{"/", "F", "TA"});
		pTA.add(new String[]{""});
		BNF.put("TA", pTA);
		List<String[]> pF = new ArrayList<String[]>();
		pF.add(new String[]{"INT"});
		pF.add(new String[]{"(", "E", ")"});
		BNF.put("F", pF);
		
		//specify terminal symbol regular expression table
		Map<String, String> termRegex = new HashMap<String, String>();
		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);
	}
}