2023.3.24
ANTLR4生成语法分析器
- Cymbol.g4 一个小语言:函数定义(声明)、调用关系,绘制函数调用图
- 形参:某个分割符隔开的一连串东西,代码
- 函数体block和stat语句嵌套,互递归,?表示括号()中的部分可有可无
- 表达式expr也是递归定义,表达式通过运算符组合成表达式, !逻辑求反,^幂运算
- 修改Cymbol.g4
- grammar
- @header{package cymbol}
- prog : varDecl | functionDecl)* EOF;
- varDecl : type ID ( ‘=’ expr)? ‘;’;
- type: ‘int’ | ‘double’ | ‘void’;
- functionDecl : type ID ‘(‘
- 二义性:c语言用规则(then与前面离他最近的没匹配的if匹配)来消除歧义
- 运算符优先性带来的结合性与二义性:
- 左递归(左结合)右递归:
- expr : | expr ‘*’expr | expr ‘-’expr | DIGIT;
- 不能一开始就展开*,离根更近表示优先级越低
- 左结合:
- Expr是若干个term相减,term是若干个factor相乘
- 左递归:在最左边调用了自己
- 右递归:expr先生成term
,再由term生成-term
- 函数调用图:遍历语法分析树
- 遇到function declare:通过ID获取函数名
- 遇到function call:获取被调用函数的函数名
- Listener
- Listener:ANTLR4自动做深度优先遍历,每当遍历到一个节点,就触发一个事件,只需要自己编写处理事件的函数即可
- 每个节点都有两个机会可以处理:enter node和exit node,在合适的节点上override事件即可(实现接口函数)
- 如果需要ANLTR4为生成的事件细分为epxr展开的各种分支,需要在expr分支中加上注释
- Expr: ID ‘(‘ expr ‘)’ # FunctionCall
- Expr: lhs = expr ‘(‘ op = ‘*’ | op =’/’ ) rhs = expr # MulDiv 事件触发后,查看op即可区分乘除,lhs和rhs同理
- Gradle中:generateGrammarSource即可生成java代码
- 要实现一个类,在callgraph中CallGraphListener extends CymbolBaseListener,覆写enterFunctionDeclare和enterFunctionCall
- 工具graph viz :生成特定模板的代码,自动帮助画图
- Enterfunctiondeclare要求加入node,enterfunctioncall要求加边edge,currentfunctionname,
- ParseTreeWalker用dfs生成树
- visitor
- 自己控制怎么遍历,责任也越多,listener更简单