語義分析(三)

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虛擬機字節碼
  • 彙編碼
  • 其它

參考

  1. 編譯原理
  2. antrl4權威指南
  3. 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
	;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章