上篇定義了 Jerry 語言基礎詞法/語法,使用 ANTLRWorks 測試生成具體語法樹(Parse Tree),節點過多含標點、關鍵字等雜訊。本篇引入樹重寫規則,將其精簡為抽象語法樹(AST)。
options {
language = Java;
output = AST;
ASTLabelType = CommonTree;
}
tokens {
VAR_DECL; SIMPLE_TYPE; ARRAY_TYPE; ARRAY_LITERAL;
SIMPLE_VAR_ACCESS; ARRAY_VAR_ACCESS; UNARY_MINUS;
BLOCK; EXPR_STMT;
}
這些 Token 不存在於輸入文本,僅作為 AST 節點類型標識,便於後續 Tree Grammar 匹配。
^:標記為當前子樹根節點!:排除,不出現在 AST 中範例:
whileStatement : WHILE^ LPAREN! expression RPAREN! statement;
生成:^(WHILE expression statement)
語法:原規則 -> ^(ROOT child1 child2 ...)
範例:
whileStatement : 'while' '(' expression ')' statement -> ^('while' expression statement);
優勢:結構一目瞭然,適合複雜重組。
ANTLR 禁用左遞歸,採用迴圈模擬左結合:
additiveExpression
: multiplicativeExpression (additiveOperator^ multiplicativeExpression)* ;
additiveOperator^ 使每個運算符成為新子樹根,左操作數為前一子樹,右操作數為下一 multiplicativeExpression,自然形成左結合 AST。
C 風格聲明:int a, b[10], c = 5; 類型資訊分散。重寫規則需處理:
使用 $variableDeclaration 引用前一分支生成的樹,追加新變量節點:
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)
| ... 類似四種情況 ...
)
)*
SEMICOLON
;
blockStatement
: LBRACE statementList RBRACE -> ^(BLOCK statementList)
| LBRACE RBRACE -> EMPTY_BLOCK
;
public class TestJerry {
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(System.in);
JerryLexer lexer = new JerryLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
JerryParser parser = new JerryParser(tokens);
parser.program(); // 內部嵌入動作列印 AST
}
}
javac -cp antlrworks-1.2.2.jar JerryLexer.java JerryParser.java TestJerry.java
java -cp ".;antlrworks-1.2.2.jar" TestJerry < JerrySample.txt
(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) arr (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)))
VAR_DECL, UNARY_MINUS, BLOCK 等讓後續處理按語義分派,而非語法結構。下一篇將展示基於此 AST 的 Tree Grammar 實作,完成型別檢查、符號表建立、中間代碼生成等語義分析階段。