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
;