在沉默了數(shù)月之后,博主心血來潮想繼續(xù)介紹QParserGenerator,在這里我們將不再繼續(xù)介紹任何有關(guān)于LALR(1)的算法(那東西只會(huì)把你的腦子變成一團(tuán)漿糊),讓我們來看一下QParserGenerator的具體用法。
說到ParserGenerator不得不提的是BNF,應(yīng)此QParserGenerator也有它自己的BNF,這時(shí)有人會(huì)問BNF究竟是什么呢?簡(jiǎn)單的說BNF就是用來描述一種語法的東西,比如在Basic中If后面跟表達(dá)式然后是Then中間是語句塊末尾必須要有End If等等的一系列描述,更專業(yè)的解釋我們可以看一下
維基百科上的解釋。
好了,說完了BNF那讓我們來看一下QParserGenerator的BNF到底是長(zhǎng)啥樣的
%token "%" "token" "start" "|" "-" ">" ";" "[" "]";
%start start;
strings -> strings "{String}"
| "{String}"
;
vs -> vs "{Letter}"
| vs "{String}"
| "{Letter}"
| "{String}"
;
option -> "[" vs "]"
;
oneProductionRight -> oneProductionRight option
| oneProductionRight vs
| option
| vs
;
someProductionRight -> someProductionRight "|" oneProductionRight
| oneProductionRight
;
token -> "%" "token" strings ";"
;
someTokens -> someTokens token
| token
;
production -> "{Letter}" "-" ">" someProductionRight ";"
;
someProductions -> someProductions production
| production
;
start -> someTokens "%" "start" "{Letter}" ";" someProductions
| "%" "start" "{Letter}" ";" someProductions
;
也許有人會(huì)問,不對(duì)啊根據(jù)
維基百科上的說明BNF不應(yīng)該是長(zhǎng)這樣的,其實(shí)QParserGenerator是一個(gè)BNF的生成器,它可以將輸入的BNF通過一系列的運(yùn)算最后生成LALR(1)分析表,為了BNF文件的美觀和方便處理我特地把他設(shè)計(jì)成了這個(gè)樣子的而已,好了下面我們就以這個(gè)BNF文件來說明應(yīng)該如何來書寫B(tài)NF文件。
首先可以看到最頂上有一些以%token開頭的字符串(在C語言中我們將用雙引號(hào)括起來的字符序列稱為字符串)以及最后的一個(gè)分號(hào),其實(shí)這里的這些字符串正是BNF中說說的終結(jié)符,所以我們規(guī)定,所有其他沒用%token聲明的符號(hào)都是非終結(jié)符。終結(jié)符是用來做移進(jìn)操作的,在某種特定的語言中他表現(xiàn)為一個(gè)token,而非終結(jié)符可以理解為一個(gè)代詞,通常一個(gè)非終結(jié)符都可以展開為一條或多條規(guī)則(
產(chǎn)生式)。至于說為什么每條內(nèi)容后面都會(huì)有分號(hào)呢,只是為了處理上的方便(消除語法上的沖突?)。
好了,我們把終結(jié)符和非終結(jié)符這兩個(gè)專業(yè)術(shù)語給解釋完了,接下來可以看到的是一個(gè)以%start開頭后跟一個(gè)非終結(jié)符的語句,他表明了所有規(guī)則(
產(chǎn)生式)是從哪里開始的(有始無終的節(jié)奏-_-||杯具啊)。
最后就是我們的重頭了,多空一行也不為過吧。這里有一大堆的產(chǎn)生式,那我們?nèi)绾蝸黹喿x他呢,其實(shí)上面已經(jīng)介紹了有個(gè)表明了所有規(guī)則開頭的非終結(jié)符,好那讓我們來找一下他所對(duì)應(yīng)的產(chǎn)生式在哪里
start -> someTokens "%" "start" "{Letter}" ";" someProductions
| "%" "start" "{Letter}" ";" someProductions
;
可以看到所有的規(guī)則可分為左半部分和右半部分,左邊總是一個(gè)非終結(jié)符來說明他應(yīng)該被哪一些規(guī)則來替代,而右邊則是這些規(guī)則的具體內(nèi)容包含了一些終結(jié)符和非終結(jié)符序列,中間則用一個(gè)箭頭符號(hào)來分割。在所有的規(guī)則中非終結(jié)符都是不帶引號(hào)的,而終結(jié)符都是用引號(hào)將其括起來的,在終結(jié)符中有一些內(nèi)置的變量來表達(dá)一些特定的表達(dá)式,這個(gè)會(huì)在下文中做出說明。當(dāng)然對(duì)于同一個(gè)終結(jié)符來說我們可以用任意多個(gè)規(guī)則來說明他,他們都是
或的關(guān)系,由于BNF中不可能存在
且的關(guān)系,應(yīng)此我們并不需要考慮他。
下面讓我們來看一下預(yù)定義的終結(jié)符有哪些,從
Parser.cpp的代碼中可知預(yù)定義的終結(jié)符有"{String}"、"{Digit}"、"{Real}"、"{Letter}"。
"{String}":表示正則表達(dá)式\"[^\"]*\"
"{Digit}":表示正則表達(dá)式[0-9]+
"{Real}":表示正則表達(dá)式[0-9]*.[0-9]+
"{Letter}":表示正則表達(dá)式((_[0-9]+)|([_a-zA-Z]+))[_0-9a-zA-Z]*
從這些正則表達(dá)式中可見"{String}"表示一個(gè)帶雙引號(hào)的字符串,"{Digit}"則表示一個(gè)數(shù)字,"{Real}"則表示一個(gè)浮點(diǎn)數(shù),"{Letter}"則表示一個(gè)不帶雙引號(hào)的字符串。當(dāng)然這些正則表達(dá)式寫的并不完備,比如"{String}"中沒有支持轉(zhuǎn)義等等。
然后讓我們來看一下每條規(guī)則支持哪些語法,首先從下面幾條文法中可知,可用方括號(hào)將一些可選項(xiàng)括起來。
1 vs -> vs "{Letter}"
2 | vs "{String}"
3 | "{Letter}"
4 | "{String}"
5 ;
6
7 option -> "[" vs "]"
8 ;
而對(duì)于一個(gè)規(guī)則來說他可以用若干條產(chǎn)生式來說明他,其中每條產(chǎn)生式之間是
或的關(guān)系。
1 oneProductionRight -> oneProductionRight option
2 | oneProductionRight vs
3 | option
4 | vs
5 ;
6
7 someProductionRight -> someProductionRight "|" oneProductionRight
8 | oneProductionRight
9 ;
其他一些規(guī)則則說明了一些上文提到的規(guī)則,比如開頭是一些token的定義等。終于把QParserGenerator的文法文件的結(jié)構(gòu)給介紹完了,在接下來的一篇文章中我們將介紹如何用QParserGenerator來生成一個(gè)帶括號(hào)優(yōu)先級(jí)的四則混合運(yùn)算計(jì)算器,其文法可見
Calculator.txt,QLanguage整個(gè)項(xiàng)目的代碼可見
https://github.com/lwch/QLanguage/。
posted on 2013-10-09 11:17
lwch 閱讀(1458)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
QLanguage