antlr4 访问器模式实现四则运算_d64793946的博客

CSDN博客 · · 2493 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

1.eclipse 或idea安装antlr4插件(以eclipse 为例 )

windows->Eclipse Marketplace->搜索antlr

搜索到antlr 4 ide 进行安装

2.插件安装完成后新建antlr4 项目

file ->new ->other->antlr4项目

填写项目名->完成

3.将项目转化为maven项目

右键项目-->configure->转化为maven项目

方便管理

加入如下依赖

  <dependencies>
  <dependency>
    <groupId>org.antlr</groupId>
    <artifactId>antlr4-runtime</artifactId>
    <version>4.7.2</version>
</dependency>
</dependencies>

注意maven里的依赖必须和eclipse里配置的antrl jar版本一致

如果不一致请按如下下载对应的jar配置到eclispe里

如果版本不匹配需要官网下载和mavne匹配的版本 我这里下载的是antlr-4.7.2-complete.jar

https://www.antlr.org/download.html  进入主页

找到ANTLR tool and Java Target标签

下载第一个即可.

eclipse配置最新的jar 

windows->preferences-->antlr4->tool 配置最新的jar

4.四则运算编写解释

内容如下:

grammar Math;

@header{package com.zetyun.aiops.core.math;} #生成包的路径

prog : stat+;# 定义一个语法prog 他由一个或多个语法stat组成

stat: expr NEWLINE          # printExpr #语法stat 可以是expr语法  (注意“#printExpr" 可以标志生成对应的监听器或者访问器的方法 ) 
    | ID '=' expr NEWLINE   # assign     #也可以是赋值语句
    | NEWLINE               # blank #也可空行
    ;

expr:  expr op=('*'|'/') expr   # MulDiv  #语法expr可以如下表示
| expr op=('+'|'-') expr        # AddSub
| INT                           # int
| ID                            # id
| '(' expr ')'                  # parens
;

MUL : '*' ; // assigns token name to '*' used above in grammar #词法
DIV : '/' ;
ADD : '+' ;
SUB : '-' ;
ID : [a-zA-Z]+ ;
INT : [0-9]+ ;
NEWLINE:'\r'? '\n' ;
WS : [ \t]+ -> skip;

1.语法说明:

一般我们解析语言分为,词法解析和语法解析

词法解析根据用户输入生成一系列token序列(即一个关键字,操作数,操作方法等的列表)

语法解析根据token生成语法树(这里就是根据编写的g4 文件生成对应的树状结构及树状结构的值)

2.g4 语法说明:

antrl4 分为词法规则和文法规则

文法(语法)规则一般对应非叶子节点,

词法一般对应叶子节点

词法规则一般以大写字母组成

文法规则有小写字母组成

为了避免歧义antrl定义了一套匹配规则

文法规则优先词法规则,

字符串常量属于词法规则

字符串常量优先其他的词法规则

相同优先级采用自上而下匹配即按申明顺序优先匹配

3.最左匹配原则

如果如下G4语法

expr:  expr op=('*'|'/') expr   # MulDiv
| expr op=('+'|'-') expr        # AddSub
| INT                           # int
| '(' expr ')'                  # parens
;

INT : [0-9]+ ;

则1+2*3可以解析为 如下两种

expr(1)+exp(2*3)

和exp(1+2)*exp(3)

antrl 是怎样解决如上歧义的呢

是根据申明匹配位置(即优先匹配最左)

expr:  expr op=('*'|'/') expr   # MulDiv
| expr op=('+'|'-') expr        # AddSub
| INT                           # int
| ID                            # id
| '(' expr ')'                  # parens
;

我们看到 expr op=('*'|'/') expr 是在expr op=('+'|'-') expr   的前面(即左边)

所以语法解析为expr(1)+exp(2*3)

如果换成

expr op=('+'|'-') expr        # AddSub

|expr:  expr op=('*'|'/') expr   # MulDiv
| INT                           # int
| ID                            # id
| '(' expr ')'                  # parens

则·会·匹配exp(1+2)*exp(3)
5.通过visit模式实战四则运算

右键Math.g4 

Run as >external Tools configurations-> 将tool 的标签下的arguments 改为 -listener -visitor -encoding UTF-8 生成访问者模式的基类

然后 Run as >generate ANTLR recognizer

生成MathVisitor 接口和MathBaseVisitor 的默认实现

我们继承MathBaseVisitor 实现打印四则运算中的表达式

package com.zetyun.aiops.core.math;

import java.util.HashMap;
import java.util.Map;

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;

import com.zetyun.aiops.core.math.MathParser.AddSubContext;
import com.zetyun.aiops.core.math.MathParser.AssignContext;
import com.zetyun.aiops.core.math.MathParser.BlankContext;
import com.zetyun.aiops.core.math.MathParser.IdContext;
import com.zetyun.aiops.core.math.MathParser.IntContext;
import com.zetyun.aiops.core.math.MathParser.MulDivContext;
import com.zetyun.aiops.core.math.MathParser.ParensContext;
import com.zetyun.aiops.core.math.MathParser.PrintExprContext;
import com.zetyun.aiops.core.math.MathParser.ProgContext;

public class MathVis extends MathBaseVisitor<Integer>
{
    private static Map<String,Integer> assignMap = new HashMap<>();

    @Override
    public Integer visitProg(ProgContext ctx)
    {
        // TODO Auto-generated method stub
        return super.visitProg(ctx);
    }

    @Override
    public Integer visitPrintExpr(PrintExprContext ctx)
    {
        Integer value=visit(ctx.expr());
        System.out.println(value);
        return 0;
    }

    @Override
    public Integer visitAssign(AssignContext ctx)
    {
        String id = ctx.ID().getText();
        Integer value=visit(ctx.expr());
        assignMap.put(id, value);
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Integer visitBlank(BlankContext ctx)
    {
        return 0;
    }

    @Override
    public Integer visitParens(ParensContext ctx)
    {
        // TODO Auto-generated method stub
        return visit(ctx.expr());
    }

    @Override
    public Integer visitMulDiv(MulDivContext ctx)
    {
        if(ctx.op.getType()==MathParser.MUL)
        {
            return visit(ctx.expr(0))*visit(ctx.expr(1));
        }
        else
        {
            return visit(ctx.expr(0))/visit(ctx.expr(1));
        }
    }

    @Override
    public Integer visitAddSub(AddSubContext ctx)
    {
        if(ctx.op.getType()==MathParser.ADD)
        {
            return visit(ctx.expr(0))+visit(ctx.expr(1));
        }
        else
        {
            return visit(ctx.expr(0))-visit(ctx.expr(1));
        }
    }

    @Override
    public Integer visitId(IdContext ctx)
    {
        String id = ctx.ID().getText();
        if(assignMap.containsKey(id))
        {
            return assignMap.get(id);
        }
        return 0;
    }

    @Override
    public Integer visitInt(IntContext ctx)
    {
        return Integer.valueOf(ctx.INT().getText().trim());
    }

    public static void main(String[] args)
    {
        CharStream input =CharStreams.fromString("a=3 \n b=4 \n a*b \n 4*5 \n");

        MathLexer lexer = new MathLexer(input);

        CommonTokenStream tokens = new CommonTokenStream(lexer);

        MathParser parser = new MathParser(tokens);
        //parser.resginsetList();
        ParseTree tree = parser.prog();
        MathVis mathVis=new MathVis();
        mathVis.visit(tree);
        
    }
    

}
 

运行main 函数打印出运算结果 12 和20

 

 

 

 

 

 

本文来自:CSDN博客

感谢作者:CSDN博客

查看原文:antlr4 访问器模式实现四则运算_d64793946的博客

2493 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传