Antlr一个领域语言利器——入门篇

Antlr(Another Tool for Language Recognition)为开源的语法分析器,可以将输入的内容自动生成语法树;开发者可以使用它自定义自己的领域语言,只需创建语法规则文件,使用Antlr根据该规则文件生成相对应的类,再这些类的基础上我们可以用于实现自己的功能;Antlr4为Antlr的最新版本目前看到的基本也是Antlr4;
这些类主要包括两个方面的内容:1、实现了对输入的内容进行词法分析(Lexer)部分;2、根据词法构建出对应的语法树(Syntax Tree);
使用场景: 1、领域特定语言;2、文本解析;3、算术运算等等;在各类开源框架中也都有看到Antlr4的身影,如Hive、Spark、Presto、Hibernate 等等;

Antlr4基础语法

1、语法基本格式

//声明语法头,生成的Java类将使用此前缀,必须与xx.g4文件同名
grammar Name;
options {}   //选项:语言选,输出选项等;项
lnaguage=Java;output=AST
import ... ;  //如g4文件过大可拆分,使用import引入

tokens {...}  //为那些没有关联词法规则的grammar定义tokens类型
channels {...} // 词法分析时才可定义
@actionName {...}   //动作:可将目标语言嵌入到g4中,有可调用对应外部代码

//规则,此语法文件所定义的规则,分为词法规则与语法规则
规则;
rule1
...

ruleN

三个基本动作:

此动作可在运行脚本后,所生成的类中自动带上包路径,不用手动移动。

@members {}

将代码中变量或方法注入到recognizer类中

@after {System.out.println(“after matching rule;”);}

规则:

词法规则(lexer):以大写字母开头,用于词法分析;
语法规则(parser):以小写字母开头,用于语法分析,字符串与lexer组合匹配分析句子;
以 : 开始 ; 结束,用 | 分隔对多规则分隔

2、简单示例

+匹配前一个匹配项最少一次, * 匹配前一个匹配项0次或多次 ?可选 ~ 取反

grammar Hello;
init  :'hello' ID ;          // parser. 匹配关键字'hello'后⾯跟⼀个ID
ID :[a-z]+ ;             // lexer. 匹配⼩写字母组成的ID
WS :[ \t\r\n]+ -> skip ;   //lexer. 系统级规则匹配时跳过空格、tabs、换⾏符

一个Antlr4计算器

1、语法文件:

grammar Calc;

prog : stat+;
stat: expr NEWLINE          # printExpr
| ID '=' expr NEWLINE   # assign
| NEWLINE               # blank
;

expr: expr op=('*'|'/') expr    # MulDiv
| 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' ;
DELIMITER : ';';
WS : [ \t]+ -> skip;

可使用Maven插件、IDEA插件、或Antlr4自带命令行工具生成相关词法、语法分析器类;
Antlr4提供了两种方式可以访问语法树:观察者模式、访问者模式;

观察者模式: 通过结点监听触发处理方法,无需定义遍历语法树顺序,动作与文法解耦,处理时需通过自定义map存储中间结果,继承:XXXBaseListener类实现;
访问者模式: 主动遍历语法树,动作与文法解耦,visitor访问节点时有返回值,无需定义map存储中间结果,继承:XXXBaseVisitor类实现;

2、实现计算逻辑

这里使用访问者模式实现计算器,继承:CalcBaseVisitor类,重写visitMulDiv与visitAddSub方法实现:加减乘除计算逻辑;

@Override
public Integer visitMulDiv(CalcParser.MulDivContext ctx) {
    Integer left = ctx.expr(0).accept(this);
    Integer right = ctx.expr(1).accept(this);
    if (ctx.op.getType() == CalcParser.MUL){
        return left * right;
    }else{
        return left / right;
    }
}
@Override
public Integer visitAddSub(CalcParser.AddSubContext ctx) {
    Integer left = ctx.expr(0).accept(this);
    Integer right = ctx.expr(1).accept(this);
    if (ctx.op.getType() == CalcParser.ADD){
        return left + right;
    }else{
        return left - right;
    }
}

3、使用该语法树计算器

CharStream input = CharStreams.fromString("22*3+12\r\n");
CalcLexer lexer=new CalcLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CalcParser parser = new CalcParser(tokens);
ParseTree tree = parser.prog(); // parse
EvalVisitor vt=new EvalVisitor();
System.out.printf("%s\n", vt.visit(tree));

Antlr的实现可不只是计算器这种小玩意,我们通常有多个数据源,在没有统一的查询入口的时候通常只能打开各个客户端工具进行查询,有了Antlr我们可以很方便的实现一种称之为:XQL的语言,可实现通过XQL就可查询Kafka、Redis、MySQL、文件(csv、json、parquet)等数据源的数据;
通过Antlr实现的统一XQL查询引擎:

Antlr一个领域语言利器——入门篇

文章首发地址:https://mp.weixin.qq.com/s/BcuiM3ifm-PCOBZUja7vJg
参考资料:
antlr4
语法规则

Original: https://www.cnblogs.com/softlin/p/16308992.html
Author: AiFly
Title: Antlr一个领域语言利器——入门篇

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/619919/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 米哈游大量招募新同学,校招提前批最后一天!

    米哈游大量招募新同学: 1.周末双休,工作日早十晚七,上班不打卡,全凭自觉; 2.团队氛围很不错,有成长空间,拒绝无意义加班和内卷; 3.免费晚餐线上订餐,不限量零食饮料还有咖啡和…

    Java 2023年6月8日
    090
  • Netty内存池的整体架构

    一、为什么要实现内存管理? Netty 作为底层网络通信框架,网络IO读写必定是非常频繁的操作,考虑到更高效的网络传输性能,堆外内存DirectByteBuffer必然是最合适的选…

    Java 2023年6月15日
    091
  • markdown首行缩进

    markdown 好像没有首行缩进的快捷键, 而使用tab键也没有用, 空格也最后也只会保留一个. 但 markdown 是支持 HTML 标签的, 就是说我们可以使用 style…

    Java 2023年6月7日
    0114
  • 头秃了,Spring Boot 自动配置源码解析了解一波~

    前言 源码版本 @SpringBootApplication 干了什么? @EnableAutoConfiguration 干了什么? 总结 为什么 Spring Boot这么火?…

    Java 2023年6月14日
    080
  • JDK成长记9:集合篇的总结和练习

    这一节主要是抛出一些面试题让大家检验一下学习成果,也会小结一下集合篇的知识点。 所以不会特别长。 练习-模拟面试 练习-模拟面试 先给大家讲一个简单的面试场景 快手Java面试一、…

    Java 2023年6月5日
    084
  • Spring 源码(13)Spring Bean 的创建过程(4)

    Spring Bean的创建过程非常的复杂,上一篇重点介绍了 Spring在创建 Bean的过程中,使用 InstantiationBeanPostProcessor进行提前创建 …

    Java 2023年6月14日
    082
  • Sharepoint 2013 系列篇(安装部署)–上篇

    前言 sharepoint的部署是按照物理拓扑图的架构来部署,按照物理拓扑图架构分为一层拓扑图架构,二层拓扑图架构,三层拓扑图架构,多层拓扑图架构。 按照分层的拓扑图部署是按照需求…

    Java 2023年6月7日
    0102
  • 使用 Proxychains 代理联网

    前言 Proxychains 是 Linux 系统中一款简单好用的代理工具,可以指定特定命令走代理进行网络请求,适用于比较特殊的网络环境。最新版本为 proxychains4 安装…

    Java 2023年6月7日
    090
  • 多态

    同一批事物,它们都是由一个事物泛生得出的(也就是继承),这一批事物我们可以用一个分类去归纳。比如我们生存需要进食,牛肉、猪肉、鸡肉等,当我们处于一个饥饿的状态下,不管什么肉,都会食…

    Java 2023年6月5日
    095
  • 【校招VIP】[产品][一本][6分]简历原则上写一页

    关注 【校招VIP】公众号,回复 【简历】,添加校招顾问微信,即可获取简历指导! 简历背景:21届一本产品 简历评分: 6分 一、学员简历 ​ ​ ​ 二、 指导意见: 简历有两大…

    Java 2023年6月5日
    097
  • Stream 流

    1.stream.forEach() 与 collection.forEach() 虽然都是迭代方法,但执行结果完全不同。 List strl=Arrays.asList(&quo…

    Java 2023年6月8日
    085
  • PreparedStatement报错问题

    package com.lian.lesson3; import com.lian.lesson2.utils.JDBCUtils; import java.sql.*; publ…

    Java 2023年6月15日
    073
  • Java对象的内存布局

    Mark Word 用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。 Mark Word在32位J…

    Java 2023年5月29日
    074
  • 软件工程 结构法方法 第2篇随笔

    建立系统的功能模型图 首先:建立系统环境图,确定系统边界 其中: 数据流为:销售的商品,日销售额等;三个输入流,三个输出流 ​ 数据源为:营业员,经理,收款员 ​ 数据潭为:经理,…

    Java 2023年6月16日
    092
  • Docker 核心知识回顾

    Docker 核心知识回顾 最近公司为了提高项目治理能力、提升开发效率,将之前的CICD项目扩展成 devops进行项目管理。开发人员需要对自己的负责的项目进行流水线的部署,包括写…

    Java 2023年6月7日
    0102
  • 通过实现仿照FeignClient框架原理的示例来看清FeignClient的本质

    前言 FeignClient的实现原理网上一搜一大把,此处我就不详细再说明,比如:Feign原理 (图解) – 疯狂创客圈 – 博客园 (cnblogs.c…

    Java 2023年6月9日
    086
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球