日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

一个简单的语言的语法(二):ANTLR的重写规则

發布時間:2025/5/22 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一个简单的语言的语法(二):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)))??

  • 可以跟原本的代碼對比一下,看看是否保持了原本的結構。

    得到這棵抽象語法樹之后,接下來就可以對樹來做匹配和分析了。由于樹本身已經有了結構,下面就可以用更干凈的描述方式來表述我們要對樹做的處理。

    轉載于:https://www.cnblogs.com/shihao/archive/2012/06/02/2532218.html

    總結

    以上是生活随笔為你收集整理的一个简单的语言的语法(二):ANTLR的重写规则的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。