一个简单的语言的语法(二):ANTLR的重写规则
生活随笔
收集整理的這篇文章主要介紹了
一个简单的语言的语法(二):ANTLR的重写规则
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
們使用ANTLR來描述了Jerry語言的基本語法,并通過ANTLRWorks來實驗該語法對樣本代碼生成的解析樹。但如同上一篇最后所述,這樣得到的解析樹中有太多對后續處理來說無用的冗余信息。我們需要消除這些冗余信息,得到抽象語法樹(AST)。
本篇將以之前做的語法為基礎,通過添加樹重寫規則來將ANTLR默認生成的解析樹簡化整理為抽象語法樹。
本文涉及的源碼和運行時庫打包在附件里了,懶得復制粘貼的話就直接下載附件的版本,用ANTLRWorks來查看和編輯語法文件吧~
修改后的語法文件如下:
Jerry.g(ANTLR 3.1語法文件,以Java為生成目標語言)
Java代碼 ?grammar?Jerry;?? ?? options?{?? ????language?=?Java;?? ????output?=?AST;?? ????ASTLabelType?=?CommonTree;?? }?? ?? tokens?{?? ????//?imaginary?tokens?? ????VAR_DECL;?? ????SIMPLE_TYPE;?? ????ARRAY_TYPE;?? ????ARRAY_LITERAL;?? ????SIMPLE_VAR_ACCESS;?? ????ARRAY_VAR_ACCESS;?? ????UNARY_MINUS;?? ????BLOCK;?? ????EXPR_STMT;?? }?? ?? //?parser?rules?? ?? program?:???statementList?EOF!?? ????????{?? ????????????System.out.println(?? ????????????????null?==?$statementList.tree???? ????????????????"null"?:?? ????????????????$statementList.tree.toStringTree());?? ????????}?? ????;?? ?? statementList?? ????:???statement*?? ????;?? ?? statement?? ????:???expressionStatement?? ????|???variableDeclaration?? ????|???blockStatement?? ????|???ifStatement?? ????|???whileStatement?? ????|???breakStatement?? ????|???readStatement?? ????|???writeStatement?? ????;?? ?? expressionStatement?? ????:???expression?SEMICOLON?? ????????????->?^(?EXPR_STMT?expression?)?? ????;?? ?? variableDeclaration?? ????:???typeSpecifier?? ????????????(?Identifier?? ????????????????(???->?^(?VAR_DECL?^(?SIMPLE_TYPE?typeSpecifier?)?Identifier?)?? ????????????????|?(?LBRACK?Integer?RBRACK?)+?? ????????????????????->?^(?VAR_DECL?^(?ARRAY_TYPE?typeSpecifier?Integer+?)?Identifier?)?? ????????????????|?EQ?expression?? ????????????????????->?^(?VAR_DECL?^(?SIMPLE_TYPE?typeSpecifier?)?Identifier?expression?)?? ????????????????|?(?LBRACK?Integer?RBRACK?)+?EQ?arrayLiteral?? ????????????????????->?^(?VAR_DECL?^(?ARRAY_TYPE?typeSpecifier?Integer+?)?Identifier?arrayLiteral?)?? ????????????????)?? ????????????)?? ????????????(?COMMA?id=Identifier?? ????????????????(???->?$variableDeclaration?^(?VAR_DECL?^(?SIMPLE_TYPE?typeSpecifier?)?$id?)?? ????????????????|?(?LBRACK?dim1+=Integer?RBRACK?)+?? ????????????????????->?$variableDeclaration?^(?VAR_DECL?^(?ARRAY_TYPE?typeSpecifier?$dim1+?)?$id?)?? ????????????????|?EQ?exp=expression?? ????????????????????->?$variableDeclaration?^(?VAR_DECL?^(?SIMPLE_TYPE?typeSpecifier?)?$id?$exp?)?? ????????????????|?(?LBRACK?dim2+=Integer?RBRACK?)+?EQ?al=arrayLiteral?? ????????????????????->?$variableDeclaration?^(?VAR_DECL?^(?ARRAY_TYPE?typeSpecifier?$dim2+?)?$id?$al?)?? ????????????????)?? ????????????????{?if?(null?!=?$dim1)?$dim1.clear();?if?(null?!=?$dim2)?$dim2.clear();?}?? ????????????)*?? ????????SEMICOLON?? ????;?? ?? typeSpecifier?? ????:???INT?|?REAL?? ????;?? ?? arrayLiteral?? ????:???LBRACE?? ????????????arrayLiteralElement?(?COMMA?arrayLiteralElement?)*?? ????????RBRACE?? ????????????->?^(?ARRAY_LITERAL?arrayLiteralElement+?)?? ????;?? ?? arrayLiteralElement?? ????:???expression?? ????|???arrayLiteral?? ????;?? ?? blockStatement?? ????:???LBRACE?statementList?RBRACE?? ????????????->?^(?BLOCK?statementList?)?? ????;?? ?? ifStatement?? ????:???IF^?LPAREN!?expression?RPAREN!?statement?(?ELSE!?statement?)??? ????;?? ?? whileStatement?? ????:???WHILE^?LPAREN!?expression?RPAREN!?statement?? ????;?? ?? breakStatement?? ????:???BREAK?SEMICOLON!?? ????;?? ?? readStatement?? ????:???READ^?variableAccess?SEMICOLON!?? ????;?? ?? writeStatement?? ????:???WRITE^?expression?SEMICOLON!?? ????;?? ?? variableAccess?? ????:???Identifier?? ????????(???->?^(?SIMPLE_VAR_ACCESS?Identifier?)?? ????????|?(?LBRACK?Integer?RBRACK?)+?? ????????????->?^(?ARRAY_VAR_ACCESS?Identifier?Integer+?)?? ????????)?? ????;?? ?? expression?? ????:???assignmentExpression?? ????|???logicalOrExpression?? ????;?? ?? assignmentExpression?? ????:???variableAccess?EQ^?expression?? ????;?? ?? logicalOrExpression?? ????:???logicalAndExpression?(?OROR^?logicalAndExpression?)*?? ????;?? ?? logicalAndExpression?? ????:???relationalExpression?(?ANDAND^?relationalExpression?)*?? ????;?? ?? relationalExpression?? ????:???additiveExpression?(?relationalOperator^?additiveExpression?)??? ????|???BANG^?relationalExpression?? ????;?? ?? additiveExpression?? ????:???multiplicativeExpression?(?additiveOperator^?multiplicativeExpression?)*?? ????;?? ???? multiplicativeExpression?? ????:???primaryExpression?(?multiplicativeOperator^?primaryExpression?)*?? ????;?? ?? primaryExpression?? ????:???variableAccess?? ????|???Integer?? ????|???RealNumber?? ????|???LPAREN!?expression?RPAREN!?? ????|???MINUS?primaryExpression?? ????????????->?^(?UNARY_MINUS?primaryExpression?)?? ????;?? ?? relationalOperator????? ????:???LT?|?GT?|?EQEQ?|?LE?|?GE?|?NE?? ????;?? ?? additiveOperator?? ????:???PLUS?|?MINUS?? ????;?? ?? multiplicativeOperator?? ????:???MUL?|?DIV?? ????;?? ?? //?lexer?rules?? ?? LPAREN??:???'('?? ????;?? ?? RPAREN??:???')'?? ????;?? ?? LBRACK??:???'['?? ????;?? ?? RBRACK??:???']'?? ????;?? ?? LBRACE??:???'{'?? ????;?? ?? RBRACE??:???'}'?? ????;?? ?? COMMA???:???','?? ????;?? ?? SEMICOLON?? ????:???';'?? ????;?? ?? PLUS????:???'+'?? ????;?? ?? MINUS???:???'-'?? ????;?? ?? MUL?:???'*'?? ????;?? ?? DIV?:???'/'?? ????;?? ?? EQEQ????:???'=='?? ????;?? ?? NE??:???'!='?? ????;?? ?? LT??:???'<'?? ????;?? ?? LE??:???'<='?? ????;?? ?? GT??:???'>'?? ????;?? ?? GE??:???'>='?? ????;?? ?? BANG????:???'!'?? ????;?? ?? ANDAND??:???'&&'?? ????;?? ?? OROR????:???'||'?? ????;?? ?? EQ??:???'='?? ????;?? ?? IF??:???'if'?? ????;?? ?? ELSE????:???'else'?? ????;?? ?? WHILE???:???'while'?? ????;?? ?? BREAK???:???'break'?? ????;?? ?? READ????:???'read'?? ????;?? ?? WRITE???:???'write'?? ????;?? ?? INT?:???'int'?? ????;?? ?? REAL????:???'real'?? ????;?? ?? Identifier?? ????:???LetterOrUnderscore?(?LetterOrUnderscore?|?Digit?)*?? ????;?? ?? Integer?:???Digit+?? ????;?? ?? RealNumber?? ????:???Digit+?'.'?Digit+?? ????;?? ?? fragment?? Digit???:???'0'..'9'?? ????;?? ?? fragment?? LetterOrUnderscore?? ????:???Letter?|?'_'?? ????;?? ?? fragment?? Letter??:???(?'a'..'z'?|?'A'..'Z'?)?? ????;?? ?? WS??:???(?'?'?|?'\t'?|?'\r'?|?'\n'?)+?{?$channel?=?HIDDEN;?}????? ????;?? ?? Comment?? ????:???'/*'?(?options?{?greedy?=?false;?}?:?.?)*?'*/'?{?$channel?=?HIDDEN;?}?? ????;?? ?? LineComment?? ????:???'//'?~('\n'|'\r')*?'\r'??'\n'?{?$channel?=?HIDDEN;?}?? ????;??
稍微說明一下修改點。應該觀察到lexer rules部分是完全沒有改變的,修改的主要是一些選項和parser rules。
首先,在文件的開頭添加了一組選項:
Java代碼 ?options?{?? ????language?=?Java;?? ????output?=?AST;?? ????ASTLabelType?=?CommonTree;?? }??
ANTLR會知道應該使用生成AST的模式,以CommonTree作為AST的節點類型,并以Java作為生成的解析器源碼的語言。上一篇是在 ANTLRWorks里編輯和實驗語法的,這次我們需要生成實際能運行的解析器,所以需要指定這些選項(默認就是生成Java源碼,不過后續文章中我應該 會換用CSharp2目標。這個以后再說)。
接下來,可以看到除了原本在lexer rules里定義的實際存在的token類型之外,這次我們在語法文件的開頭還增加了一組虛擬的token類型。這些token類型是為了讓生成出來的抽象語法樹易于解析而添加的。
例如,觀察VAR_DECL這個token類型。在原本的語法中,沒有任何關鍵字能清楚的標識出當前處理的內容是一個變量聲明。為了方便后續分析,我們可以“制造”出一個虛構的token作為一個變量聲明語句的根元素,然后以變量的類型、標識符和初始值為子元素。
然后就是最重要的部分,樹重寫規則了。有兩種形式來表述樹重寫規則:一是直接在原本的語法規則上添加樹生成用的運算符(^和!),二是在原本的語法規則后添加一個箭頭("->"),并在箭頭后顯式指定需要生成的節點的結構。
看兩個例子:
while語句。原本的語法是:
Java代碼 ?whileStatement?:?'while'?'('?expression?')'?statement?;??
這里我們想讓生成出來的子樹以'while'為根節點,以expression和statement為子節點。
可以直接在該語法上添加樹生成運算符:在某個元素后加上帽子符號('^')來表示它是生成的子樹的根節點,在某個元素后加上嘆號('!')來表示生成的子樹中應該忽略該元素。于是修改得到的語法是:
Java代碼 ?whileStatement?:?'while'^?'('!?expression?')'!?statement?;??
也可以顯式指定樹重寫規則。一棵子樹用這種方式來表示:
Java代碼 ?^(?root?element1?element2?...?)??
這里我們要的就是:
Java代碼 ?whileStatement?:?'while'?'('?expression?')'?statement?? ????->?^(?'while'?expression?statement?)?? ??;??
這種形式我們能一目了然看到最終生成的子樹的結構。
兩種形式是等價的,可以根據具體情況來選擇能簡單而清晰的表示出樹改寫規則的版本。
對表達式相關的語法規則,我們幾乎都是用添加運算符的形式來表示樹改寫規則,因為對左結合的雙目運算符,這樣是最簡潔的。
ANTLR生成的解析器使用LL(*)算法;與一般的LL解析器一樣,ANTLR不支持左遞歸的語法規則。這使得書寫左結合的雙目運算符時,一般得寫成這樣的形式:
Java代碼 ?exprWithHigherPrecedence?? ??:?exprWithLowerPrecedence?(?op?exprWithLowerPrecedence?)*?? ??;??
而不能以左遞歸來指定左結合。(但右結合還是可以用右遞歸來指定的。)
那么在表示樹改寫規則的時候,使用運算符來修飾語法就是這樣:
Java代碼 ?exprWithHigherPrecedence?? ??:?exprWithLowerPrecedence?(?op^?exprWithLowerPrecedence?)*?? ??;??
只是在op的后面添加了一個帽子符號('^'),表明在沒有匹配到op運算符時就直接返回exprWithLowerPrecedence規則所 生成的樹;而如果匹配到了op運算符,則每匹配到一次就生成一個新的以op為根節點的、前后兩個較低優先級的表達式節點為子節點的樹。
這個樹改寫規則如果要顯式指定,就得寫成:
Java代碼 ?exprWithHigherPrecedence?? ??:?exprWithLowerPrecedence?? ??????(?op?exp=exprWithLowerPrecedence?? ??????????->?^(?op?$exprWithHigherPrecedence?$exp?)?? ??????)*?? ??;??
后者相比之下麻煩多了,所以一般都會使用前者。
可惜C風格的變量聲明語句的語法很麻煩,結果variableDeclaration在修改后膨脹了好多 T T
最不爽的地方就是C風格的數組變量聲明是把數組的維度寫在變量名后面的。這就使得語句開頭的類型(例如int、char等)可能只是變量的實際類型的一部分,而另一部分要在變量名的之前(例如表示指針的星號('*'))或之后(例如表示數組的方括號('[' ']'))。
就不能把整個類型寫在一起么……T T 于是衍生出來的Java和C#明顯都吸取了這個教訓。
在語法的program規則中,我們添加了一條嵌入語法動作,讓生成的解析器在匹配完program規則后將其對應的抽象語法樹以字符串的形式輸出出來。
如果是在ANTLRWorks里編輯該語法文件,可以在菜單里選擇Generate -> Generate Code來生成出解析器的源碼。這里例子中我們會得到JerryLexer.java和JerryParser.java。
要運行這個解析器,還需要寫一個簡單的啟動程序來調用生成出來的JerryLexer和JerryParser。源碼如下:
TestJerry.java
Java代碼 ?import?org.antlr.runtime.*;?? ?? public?class?TestJerry?{?? ????public?static?void?main(String[]?args)?throws?Exception?{?? ????????//?Create?an?input?character?stream?from?standard?in?? ????????ANTLRInputStream?input?=?new?ANTLRInputStream(System.in);?? ????????//?Create?an?JerryLexer?that?feeds?from?that?stream?? ????????JerryLexer?lexer?=?new?JerryLexer(input);?? ????????//?Create?a?stream?of?tokens?fed?by?the?lexer?? ????????CommonTokenStream?tokens?=?new?CommonTokenStream(lexer);?? ????????//?Create?a?parser?that?feeds?off?the?token?stream?? ????????JerryParser?parser?=?new?JerryParser(tokens);?? ????????//?Begin?parsing?at?rule?prog?? ????????parser.program();?? ????}?? }??
它指定從標準輸入流得到要解析的Jerry代碼,然后通過JerryLexer將代碼解析成token流,再將token流交給JerryParser進行句法分析。
將JerryLexer.java、JerryParser.java和TestJerry.java放在跟ANTLRWorks同一目錄下,然后編譯它們:
引用javac -Xlint:unchecked -cp antlrworks-1.2.2.jar JerryLexer.java JerryParser.java TestJerry.java
(因為ANTLRWorks里含有ANTLR的運行時庫,而我正好又是用ANTLRWorks來編輯語法文件的,所以直接用ANTLRWorks 的JAR包放在classpath里來得到需要的ANTLR運行時類。實際開發的話可以從ANTLR官網獲得只含有ANTLR運行時庫的JAR包并在編譯 和運行的時候將其添加到classpath里。)
上一篇的最后有這樣的一段Jerry例子:
C代碼 ?//?line?comment?? //?declare?variables?with/without?initializers?? int?i?=?1,?j;?? int?x?=?i?+?2?*?3?-?4?/?(?6?-?-?7?);?? int?array[2][3]?=?{?? ??{?0,?1,?2?},?? ??{?3,?4,?6?}?? };?? ?? /*? ??block?comment? */?? ?? while?(i?<?10)?i?=?i?+?1;?? while?(!x?>?0?&&?i?<?10)?{?? ??x?=?x?-?1;?? ??if?(i?<?5)?break;?? ??else?read?i;?? }?? ?? write?x?-?j;??
(語法是符合要求的,至于代碼的意義就別追究了,只是用來演示各種語法結構隨便寫的)
用本篇的ANTLR語法文件生成的解析器,我們可以解析這個例子,得到對應的抽象語法樹的字符串表示。表示方法是:
Java代碼 ?(root?element1?element2?...)??
跟LISP的S-expression非常類似。
于是執行測試程序。將要解析的代碼保存到JerrySample.txt中,然后執行下面的命令:
引用java -cp ".;antlrworks-1.2.2.jar" TestJerry < JerrySample.txt
得到輸出:
Java代碼 ?(VAR_DECL?(SIMPLE_TYPE?int)?i?1)?(VAR_DECL?(SIMPLE_TYPE?int)?j)?(VAR_DECL?(SIMPLE_TYPE?int)?x?(-?(+?(SIMPLE_VAR_ACCESS?i)?(*?2?3))?(/?4?(-?6?(UNARY_MINUS?7)))))?(VAR_DECL?(ARRAY_TYPE?int?2?3)?array?(ARRAY_LITERAL?(ARRAY_LITERAL?0?1?2)?(ARRAY_LITERAL?3?4?6)))?(while?(<?(SIMPLE_VAR_ACCESS?i)?10)?(=?(SIMPLE_VAR_ACCESS?i)?(+?(SIMPLE_VAR_ACCESS?i)?1)))?(while?(&&?(!?(>?(SIMPLE_VAR_ACCESS?x)?0))?(<?(SIMPLE_VAR_ACCESS?i)?10))?(BLOCK?(=?(SIMPLE_VAR_ACCESS?x)?(-?(SIMPLE_VAR_ACCESS?x)?1))?(if?(<?(SIMPLE_VAR_ACCESS?i)?5)?break?(read?(SIMPLE_VAR_ACCESS?i)))))?(write?(-?(SIMPLE_VAR_ACCESS?x)?(SIMPLE_VAR_ACCESS?j)))??
這樣太亂了看不清楚。將其格式稍微整理一下得到:
Java代碼 ?(VAR_DECL?? ??(SIMPLE_TYPE?int)?? ??i?? ??1?? )?? (VAR_DECL?? ??(SIMPLE_TYPE?int)?? ??j?? )?? (VAR_DECL?? ??(SIMPLE_TYPE?int)?? ??x?? ??(-?? ????(+?(SIMPLE_VAR_ACCESS?i)?(*?2?3))?? ????(/?4?(-?6?(UNARY_MINUS?7)))?? ??)?? )?? (VAR_DECL?? ??(ARRAY_TYPE?? ????int?? ????2?? ????3?? ??)?? ??array?? ??(ARRAY_LITERAL?? ????(ARRAY_LITERAL?0?1?2)?? ????(ARRAY_LITERAL?3?4?6)?? ??)?? )?? ?? (while?? ??(<?(SIMPLE_VAR_ACCESS?i)?10)?? ??(=?(SIMPLE_VAR_ACCESS?i)?(+?(SIMPLE_VAR_ACCESS?i)?1))?? )?? (while?? ??(&&?? ????(!?(>?(SIMPLE_VAR_ACCESS?x)?0))?? ????(<?(SIMPLE_VAR_ACCESS?i)?10)?? ??)?? ??(BLOCK?? ????(=?(SIMPLE_VAR_ACCESS?x)?(-?(SIMPLE_VAR_ACCESS?x)?1))?? ????(if?? ??????(<?(SIMPLE_VAR_ACCESS?i)?5)?? ??????break?? ??????(read?(SIMPLE_VAR_ACCESS?i))?? ????)?? ??)?? )?? (write?? ??(-?(SIMPLE_VAR_ACCESS?x)?(SIMPLE_VAR_ACCESS?j)))??
可以跟原本的代碼對比一下,看看是否保持了原本的結構。
得到這棵抽象語法樹之后,接下來就可以對樹來做匹配和分析了。由于樹本身已經有了結構,下面就可以用更干凈的描述方式來表述我們要對樹做的處理。
本篇將以之前做的語法為基礎,通過添加樹重寫規則來將ANTLR默認生成的解析樹簡化整理為抽象語法樹。
本文涉及的源碼和運行時庫打包在附件里了,懶得復制粘貼的話就直接下載附件的版本,用ANTLRWorks來查看和編輯語法文件吧~
修改后的語法文件如下:
Jerry.g(ANTLR 3.1語法文件,以Java為生成目標語言)
Java代碼 ?
稍微說明一下修改點。應該觀察到lexer rules部分是完全沒有改變的,修改的主要是一些選項和parser rules。
首先,在文件的開頭添加了一組選項:
Java代碼 ?
ANTLR會知道應該使用生成AST的模式,以CommonTree作為AST的節點類型,并以Java作為生成的解析器源碼的語言。上一篇是在 ANTLRWorks里編輯和實驗語法的,這次我們需要生成實際能運行的解析器,所以需要指定這些選項(默認就是生成Java源碼,不過后續文章中我應該 會換用CSharp2目標。這個以后再說)。
接下來,可以看到除了原本在lexer rules里定義的實際存在的token類型之外,這次我們在語法文件的開頭還增加了一組虛擬的token類型。這些token類型是為了讓生成出來的抽象語法樹易于解析而添加的。
例如,觀察VAR_DECL這個token類型。在原本的語法中,沒有任何關鍵字能清楚的標識出當前處理的內容是一個變量聲明。為了方便后續分析,我們可以“制造”出一個虛構的token作為一個變量聲明語句的根元素,然后以變量的類型、標識符和初始值為子元素。
然后就是最重要的部分,樹重寫規則了。有兩種形式來表述樹重寫規則:一是直接在原本的語法規則上添加樹生成用的運算符(^和!),二是在原本的語法規則后添加一個箭頭("->"),并在箭頭后顯式指定需要生成的節點的結構。
看兩個例子:
while語句。原本的語法是:
Java代碼 ?
這里我們想讓生成出來的子樹以'while'為根節點,以expression和statement為子節點。
可以直接在該語法上添加樹生成運算符:在某個元素后加上帽子符號('^')來表示它是生成的子樹的根節點,在某個元素后加上嘆號('!')來表示生成的子樹中應該忽略該元素。于是修改得到的語法是:
Java代碼 ?
也可以顯式指定樹重寫規則。一棵子樹用這種方式來表示:
Java代碼 ?
這里我們要的就是:
Java代碼 ?
這種形式我們能一目了然看到最終生成的子樹的結構。
兩種形式是等價的,可以根據具體情況來選擇能簡單而清晰的表示出樹改寫規則的版本。
對表達式相關的語法規則,我們幾乎都是用添加運算符的形式來表示樹改寫規則,因為對左結合的雙目運算符,這樣是最簡潔的。
ANTLR生成的解析器使用LL(*)算法;與一般的LL解析器一樣,ANTLR不支持左遞歸的語法規則。這使得書寫左結合的雙目運算符時,一般得寫成這樣的形式:
Java代碼 ?
而不能以左遞歸來指定左結合。(但右結合還是可以用右遞歸來指定的。)
那么在表示樹改寫規則的時候,使用運算符來修飾語法就是這樣:
Java代碼 ?
只是在op的后面添加了一個帽子符號('^'),表明在沒有匹配到op運算符時就直接返回exprWithLowerPrecedence規則所 生成的樹;而如果匹配到了op運算符,則每匹配到一次就生成一個新的以op為根節點的、前后兩個較低優先級的表達式節點為子節點的樹。
這個樹改寫規則如果要顯式指定,就得寫成:
Java代碼 ?
后者相比之下麻煩多了,所以一般都會使用前者。
可惜C風格的變量聲明語句的語法很麻煩,結果variableDeclaration在修改后膨脹了好多 T T
最不爽的地方就是C風格的數組變量聲明是把數組的維度寫在變量名后面的。這就使得語句開頭的類型(例如int、char等)可能只是變量的實際類型的一部分,而另一部分要在變量名的之前(例如表示指針的星號('*'))或之后(例如表示數組的方括號('[' ']'))。
就不能把整個類型寫在一起么……T T 于是衍生出來的Java和C#明顯都吸取了這個教訓。
在語法的program規則中,我們添加了一條嵌入語法動作,讓生成的解析器在匹配完program規則后將其對應的抽象語法樹以字符串的形式輸出出來。
如果是在ANTLRWorks里編輯該語法文件,可以在菜單里選擇Generate -> Generate Code來生成出解析器的源碼。這里例子中我們會得到JerryLexer.java和JerryParser.java。
要運行這個解析器,還需要寫一個簡單的啟動程序來調用生成出來的JerryLexer和JerryParser。源碼如下:
TestJerry.java
Java代碼 ?
它指定從標準輸入流得到要解析的Jerry代碼,然后通過JerryLexer將代碼解析成token流,再將token流交給JerryParser進行句法分析。
將JerryLexer.java、JerryParser.java和TestJerry.java放在跟ANTLRWorks同一目錄下,然后編譯它們:
引用javac -Xlint:unchecked -cp antlrworks-1.2.2.jar JerryLexer.java JerryParser.java TestJerry.java
(因為ANTLRWorks里含有ANTLR的運行時庫,而我正好又是用ANTLRWorks來編輯語法文件的,所以直接用ANTLRWorks 的JAR包放在classpath里來得到需要的ANTLR運行時類。實際開發的話可以從ANTLR官網獲得只含有ANTLR運行時庫的JAR包并在編譯 和運行的時候將其添加到classpath里。)
上一篇的最后有這樣的一段Jerry例子:
C代碼 ?
(語法是符合要求的,至于代碼的意義就別追究了,只是用來演示各種語法結構隨便寫的)
用本篇的ANTLR語法文件生成的解析器,我們可以解析這個例子,得到對應的抽象語法樹的字符串表示。表示方法是:
Java代碼 ?
跟LISP的S-expression非常類似。
于是執行測試程序。將要解析的代碼保存到JerrySample.txt中,然后執行下面的命令:
引用java -cp ".;antlrworks-1.2.2.jar" TestJerry < JerrySample.txt
得到輸出:
Java代碼 ?
這樣太亂了看不清楚。將其格式稍微整理一下得到:
Java代碼 ?
可以跟原本的代碼對比一下,看看是否保持了原本的結構。
得到這棵抽象語法樹之后,接下來就可以對樹來做匹配和分析了。由于樹本身已經有了結構,下面就可以用更干凈的描述方式來表述我們要對樹做的處理。
轉載于:https://www.cnblogs.com/shihao/archive/2012/06/02/2532218.html
總結
以上是生活随笔為你收集整理的一个简单的语言的语法(二):ANTLR的重写规则的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C/C++ static和const关
- 下一篇: windows7 设置 Local Se