Antlr4是一款開源的語法分析器生成工具,能夠根據語法規則文件生成對應的語法分析器。現在很多流行的應用和開源項目裏都有使用,比如Hadoop、Hive以及Pig等都在使用ANTLR來做語法分析。
本文直接引用antlr4工具做自定義的語義分析
public int getMax(int c , int d){
return c + d;
}
int a = 5;
int b = 6;
int max = getMax(a,b);
print(max);
實現這樣一代碼。
(1)編寫文法及詞法
grammar my;
moyaoJava : statement* ; // match keyword hello followed by an identifier
statement
: localDeclaration
| assign
| print
| methodDeclaration
;
methodDeclaration
: 'public' type Identifier '(' parameterList? ')' '{' methodBody '}';
parameterList
: parameter (',' parameter)*
;
parameter
: type Identifier
;
methodBody
: localDeclaration* statement* RETURN expression ';'
;
localDeclaration
: varDeclaration | assignDeclaration | assignMethodDeclaration;
varDeclaration
: type Identifier ';';
assignDeclaration
: type Identifier EQ expression ';';
assign
: type Identifier EQ expression ';';
assignMethodDeclaration
: type Identifier EQ Identifier'('Identifier (',' Identifier)* ');'
;
print
: 'print(' Identifier ');'
;
type
: 'boolean'
| 'int'
;
MULTIPLY:'*';
DIVIDE:'/';
PLUS:'+';
MINUS:'-';
EQ: '=';
RETURN: 'return';
Identifier
: [a-zA-Z]+
;
expression
: INT |
Identifier MULTIPLY Identifier |
Identifier MINUS Identifier |
Identifier PLUS Identifier |
Identifier DIVIDE Identifier
;
INT:[1-9]+;
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
具體語法規則請參考《antrl4權威指南》。
(2)生成語法樹
(3)語義分析
//這裏採用antrl4中visit的方式生成語義
public class TestVisitor extends myBaseVisitor<Integer> {
//函數
class Func{
List<String> ps = new ArrayList<String>();
List<Integer> vs = new ArrayList<Integer>();
myParser.MethodBodyContext methodBody;
}
//變量表
Map<String, Integer> memory = new ConcurrentHashMap<String, Integer>();
//函數聲明
Map<String, Func> funcMemory = new ConcurrentHashMap<String, Func>();
/*
處理方法聲明文法
assignMethodDeclaration
: type Identifier EQ Identifier'('Identifier (',' Identifier)* ');'
;
實例
public int getMax(int c , int d){
return c + d;
}
*/
@Override
public Integer visitMethodDeclaration(myParser.MethodDeclarationContext ctx) {
Func f = new Func();//生成個方法
String mm = ctx.Identifier().getText();//取得函數名getMax
funcMemory.put(mm,f);//函數聲明下
List<myParser.ParameterContext> params = ctx.parameterList().parameter();
for (int i = 0; i <params.size() ; i++) {//函數的形參設置
f.ps.add(params.get(i).Identifier().getText());
}
f.methodBody = ctx.methodBody();//把函數的執行代碼保存起來
return 0;
}
/*
處理變量聲明文法
varDeclaration
: type Identifier ';';
實例
int a;
*/
@Override
public Integer visitVarDeclaration(myParser.VarDeclarationContext ctx) {
String name = ctx.Identifier().getText();//取得a
memory.put(name, 0);//放入字符表,初始化爲0
return super.visitVarDeclaration(ctx);
}
/*
處理變量聲明並賦值文法
assignDeclaration
: type Identifier EQ expression ';';
實例
int a = 5;
*/
@Override
public Integer visitAssignDeclaration(myParser.AssignDeclarationContext ctx) {
String name = ctx.Identifier().getText();//取得a
Integer value = visit(ctx.expression());//取得5值
memory.put(name, value);//放入字符表,初始化爲0
return super.visitAssignDeclaration(ctx);
}
/*
處理變量聲明並調用函數賦值文法
assignMethodDeclaration
: type Identifier EQ Identifier'('Identifier (',' Identifier)* ');'
;
實例
int max = getMax(a,b);
*/
@Override
public Integer visitAssignMethodDeclaration(myParser.AssignMethodDeclarationContext ctx) {
String name = ctx.Identifier().get(0).getText();//取得max
String mm = ctx.Identifier().get(1).getText();//取得函數名getMax
Func f = funcMemory.get(mm);//在函數聲明中找函數
List<TerminalNode> list = ctx.Identifier();
for (int i = 2; i < list.size(); i++) {//對形參賦值
String p = ctx.Identifier().get(i).getText();
f.vs.add(memory.get(p));
}
Integer value = visitMethodBody(f.methodBody);//調用函數方法體執行,並返回值
memory.put(name, value);//放入字符表,初始化爲value
return 0;
}
/*
處理函數體
methodBody
: localDeclaration* statement* RETURN expression ';'
;
實例
return c + d;
*/
@Override
public Integer visitMethodBody(myParser.MethodBodyContext ctx) {
myParser.MethodDeclarationContext pctx = (myParser.MethodDeclarationContext) ctx.getParent();//找到父親文法
String mm = pctx.Identifier().getText();
Func f = funcMemory.get(mm);//找到函數體所處的函數
//這裏偷懶,不應該放在全局變量裏的,變量應該有作用域的
for (int i = 0; i <f.ps.size() ; i++) {
memory.put(f.ps.get(i),f.vs.get(i));
}
//有執行所有聲明
ctx.localDeclaration().forEach(x->{
visitLocalDeclaration(x);
});
//有執行所有語句
ctx.statement().forEach(x->{
visitStatement(x);
});
return visitExpression( ctx.expression());//返回結果
}
/*
處理賦值文法
assign
: Identifier EQ expression ';';
實例
a = 5;
*/
@Override
public Integer visitAssign(myParser.AssignContext ctx) {
String name = ctx.Identifier().getText();//取得a
Integer value = visit(ctx.expression());//計得表達式結果
memory.put(name, value);//放入字符表,初始化爲value
return super.visitAssign(ctx);
}
/*
處理表達式文法
expression
: INT |
Identifier MULTIPLY Identifier |
Identifier MINUS Identifier |
Identifier PLUS Identifier |
Identifier DIVIDE Identifier
;
實例
a + b;
*/
@Override
public Integer visitExpression(myParser.ExpressionContext ctx) {
TerminalNode node = ctx.INT();
if(node != null){
return Integer.parseInt(node.getText());
}
TerminalNode plus = ctx.PLUS();//如果有+號的存在
if(plus != null){
String l = ctx.Identifier(0).getText();//取得a的值
String r = ctx.Identifier(1).getText();//取得b的值
return memory.get(l) + memory.get(r);//返回a+b的值
}
return super.visitExpression(ctx);
}
/*
處理打印文法
print
: 'print(' Identifier ');'
;
實例
print(a);
*/
@Override
public Integer visitPrint(myParser.PrintContext ctx) {
String name = ctx.Identifier().getText();//取得a
System.out.println(memory.get(name));//打印出a的值
return super.visitPrint(ctx);
}
}
此語義分析略顯簡單。如果可設計更優良的代碼,可參考《編譯原理中的目標程序運行時內存組織》及一些其它語言規範。我們可以把文法翻譯成
- 直接執行
- java虛擬機字節碼
- 彙編碼
- 其它
參考
- 編譯原理
- antrl4權威指南
- miniJava.g4
附miniJava.g4文件
grammar Minijava;
goal
: mainClass classDeclaration* EOF
;
mainClass
: 'class' Identifier '{' 'public' 'static' 'void' 'main' '(' 'String' '[' ']' Identifier ')' '{' statement '}' '}';
classDeclaration
: 'class' Identifier ( 'extends' Identifier )? '{' fieldDeclaration* methodDeclaration* '}';
fieldDeclaration
: varDeclaration ;
localDeclaration
: varDeclaration ;
varDeclaration
: type Identifier ';';
methodDeclaration
: 'public' type Identifier '(' parameterList? ')' '{' methodBody '}';
parameterList
: parameter (',' parameter)*
;
parameter
: type Identifier
;
methodBody
: localDeclaration* statement* RETURN expression ';'
;
type
: 'int' '[' ']'
| 'boolean'
| 'int'
| Identifier
;
statement
: '{' statement* '}'
#nestedStatement
| 'if' LP expression RP ifBlock 'else' elseBlock
#ifElseStatement
| 'while' LP expression RP whileBlock
#whileStatement
| 'System.out.println' LP expression RP ';'
#printStatement
| Identifier EQ expression ';'
#variableAssignmentStatement
| Identifier LSB expression RSB EQ expression ';'
#arrayAssignmentStatement
;
ifBlock
: statement
;
elseBlock
: statement
;
whileBlock
: statement
;
expression
: expression LSB expression RSB
# arrayAccessExpression
| expression DOTLENGTH
# arrayLengthExpression
| expression '.' Identifier '(' ( expression ( ',' expression )* )? ')'
# methodCallExpression
| NOT expression
# notExpression
| 'new' 'int' LSB expression RSB
# arrayInstantiationExpression
| 'new' Identifier '(' ')'
# objectInstantiationExpression
| expression POWER expression
# powExpression
| expression TIMES expression
# mulExpression
| expression PLUS expression
# addExpression
| expression MINUS expression
# subExpression
| expression LT expression
# ltExpression
| expression AND expression
# andExpression
| IntegerLiteral
# intLitExpression
| BooleanLiteral
# booleanLitExpression
| Identifier
# identifierExpression
| 'this'
# thisExpression
| '(' expression ')'
# parenExpression
;
AND:'&&';
LT:'<';
PLUS:'+';
MINUS:'-';
TIMES:'*';
POWER:'**';
NOT:'!';
LSB:'[';
RSB:']';
DOTLENGTH:'.length';
LP:'(';
RP:')';
RETURN: 'return';
EQ: '=';
BooleanLiteral
: 'true'
| 'false'
;
Identifier
: JavaLetter JavaLetterOrDigit*
;
fragment
JavaLetter
: [a-zA-Z$_] // these are the 'java letters' below 0xFF
;
fragment
JavaLetterOrDigit
: [a-zA-Z0-9$_] // these are the 'java letters or digits' below 0xFF
;
IntegerLiteral
: DecimalIntegerLiteral
;
fragment
DecimalIntegerLiteral
: DecimalNumeral IntegertypeSuffix?
;
fragment
IntegertypeSuffix
: [lL]
;
fragment
DecimalNumeral
: '0'
| NonZeroDigit (Digits? | Underscores Digits)
;
fragment
Digits
: Digit (DigitsAndUnderscores? Digit)?
;
fragment
Digit
: '0'
| NonZeroDigit
;
fragment
NonZeroDigit
: [1-9]
;
fragment
DigitsAndUnderscores
: DigitOrUnderscore+
;
fragment
DigitOrUnderscore
: Digit
| '_'
;
fragment
Underscores
: '_'+
;
WS
: [ \r\t\n]+ -> skip
;
MULTILINE_COMMENT
: '/*' .*? '*/' -> skip
;
LINE_COMMENT
: '//' .*? '\n' -> skip
;