ANTLR实践

“除草帖。翻到了几年前做的一个语言解析的小项目,感觉笔记躺在自己笔记本里挺浪费,放在博客上或许还能体现点价值”。

先附上参考资料:

博客:ANTLR(一)_qq_33669252的博客-CSDN博客

相关教程:https://www.crifan.com/files/doc/docbook/antlr_tutorial/release/html/antlr_tutorial.html

很好的讲解(从简介到环境准备等):antlr v4 使用指南连载1——简介 - eventer - 博客园

举例(计算器):用 antlr 做一个计算器_十木禾的博客-CSDN博客_antlr 计算器

ANTLR简介

ANTLR是一个强大语言解析工具,可用于处理结构化文本,二进制文件,是一个更强大的正则表达式工具。

两个重要部分——词法laxer和语法parser。

词法就是:

  1. 标识符,即各类编程语言中所说的以下划线、字母开头的字符串

  2. 字面量,英文叫Literal,其实就是可以当作值的东西,放在操作符两边。如数字、单引号字符串、双引号字符串、各个进制写法等

  3. 字符,单字符(!、~、=、>等)、双字符(>=、<=)等

  4. 关键字,如Java中的class、package、import、public等

语法就很容易理解了,比如变量定义、类定义,这些都是语法。 分别放在两个文件里,一个叫xxxLexer.g4,一个叫xxxParser.g4。当然名字是否包含Lexer、Parser不是强制的,看各人喜好。不过两个文件内容的第一行可以看出来是lexer还是parser。此外antlr也提供一个combine模式,即把lexer、parser写在同一个文件里。

比如:

grammar Hello; //Definea grammar called Hello 
r :'hello' ID; //match key word hello followed by an identifier 
ID : [a-z]+; //match lower-case identifiers 
WS : [\t\r\n ]+->skip; //skip spaces,tabs,newlines,\r(Windows)

第一行是语法文件名Hello,保存之后文件要按这个名字取,即Hello.g4

第二行以小写字母开头,是一个语法规则。hello后面跟着一个ID标识符。ID标识符的定义在第三行定义

第三行以大写字母开头,是一个词法规则。ID由a-z这26个英文小写字母的一个或多个组成

第四行以大写字母开头,是一个词法规则。WS由制表符、换行符的一个或多个组成。->skip是action,表示当处理这个词法规则时采取的处理方法。skip表示跳过,不处理制表符、换行符,直接处理下一个词法规则。

Python  antlr4 安装: pip install antlr4-python2-runtime

antlr preview及插件安装:ANTLR4的IntelliJ插件安装及示例Hello.g4_Jeremy_ku的博客-CSDN博客_antlr g4

g4文件概览:

grammar Name;
options {...}
import ...;
tokens {...}
@actionName {...}
<<rule1>> ... <<rule2>>

这是一个完整的antlr语法规则文件,简要声明:

  1. grammar Name 这是词法跟语法都在同一个文件声明的写法,称之为combined。若要分开,可以使用lexer grammar Name和parser grammar Name。

  2. options 可以是如下四个选项。

    superClass:用于生成xxxLexer.java、xxxParser.java的父类

    language:目标语句,如java

    tokenVocab:toekn词库

    TokenLabelType:默认的是antlr的Token类型,这里可以使用自定义的token类,如MyToken。需要配合TokenFactory使用

  3. import 可以导入各个独立的lexer、parser文件,只能用于combined写法。

  4. actionName 可以是如下内容

    @header:定义类文件头。比如嵌入java的package、import声明

    @member:定义类文件内容。比如类成员、方法(如果要指定在lexer或者parser中,可以使用 @lexer::membere、@parser::member。)

  5. rule 语法规则。

截取部分对myparser.g4的语句解释

grammar myparser;

options {
language=Python2;
}
WS: [ \t\r\n]+ -> skip;

ADD: '+'; // 加法
SUB: '-'; // 减法
MUL: '*' |'\\ast' | '·'; // 乘法
DIV: '/'; // 除法
DOT: '\u2022' | '\u22c5'; // 点乘

EXP: '**'; // 指数运算
FACTORIAL: '!'; // 阶乘

TRI: '\u25b3' | '\\triangle'; // △
ANG: '\u2220' | '\\angle'; // ∠
SIBIAN: '\u25b1'; // ▱
DU: '\u00B0' | '^\\circ' | '^{\\circ }'; // °
UNION: '\u222a'; // ∪
INTER: '\u2229'; // ∩
BELON: '\u2208'; // ∈
NOTBELON: '\u2209' | '\\not ∈'; // ∉
CHILD: '\u2286' | '\u228a'; // ⊆ or ⊊
PARENT: '\u2287'; // ⊇
PARENT2: '\\supsetneqq';
INVERT: L_BRACE? ('C' |'\u2201' | '\\complement ' | '\\complement') UNDERSCORE L_BRACE UPPER R_BRACE R_BRACE?; // C_{A}

LEFT: '\\left';
RIGHT: '\\right.' | '\\right';
BEGIN: '\\begin';
EQU: '{equation}';
END: '\\end';
CASE: '{cases}';
//CC: '{c}';
EQSEP: '\\\\';
EQBRACE: '\\{';
R_EQBRACE: '\\}';
ARRAY: '{array}';
ALIGN: '{align}';
CR: '\\cr';
AND: '&';

L_PAREN: '(';
R_PAREN: ')';
L_BRACE: '{';
R_BRACE: '}';
L_BRACKET: '[';
R_BRACKET: ']';
L_ESCAPE: '\u201c'; // “
R_ESCAPE: '\u201d'; // ”
BAR: '|';

FUNC_FLAG: [fgh];
FUNC_INT: '\\int';
FUNC_SUM: '\\sum';
FUNC_PROD: '\\prod';

FUNC_LOG: '\\'? 'log';
FUNC_LOG10: '\\'? 'lg';
FUNC_LN: '\\'? 'ln';
FUNC_SIN: '\\'? 'sin';
FUNC_COS: '\\'? 'cos' | '\\ cos';
FUNC_TAN: '\\'? 'tan';
FUNC_CSC: '\\'? 'csc';
FUNC_SEC: '\\'? 'sec';
FUNC_COT: '\\'? 'cot';

FUNC_ARCSIN: '\\'? 'arcsin';
FUNC_ARCCOS: '\\'? 'arccos';
FUNC_ARCTAN: '\\'? 'arctan';
FUNC_ARCCSC: '\\'? 'arccsc';
FUNC_ARCSEC: '\\'?' arcsec';
FUNC_ARCCOT: '\\'? 'arccot';
FUNC_SGN: '\\'? 'sgn';

FUNC_SQRT: '\\sqrt';
FUNC_ROOT: '\\root';

CMD_TIMES: '\\times';
CMD_CDOT: '\\cdot';
CMD_DIV: '\\div';
CMD_FRAC: '\\frac' | '\\dfrac';
CMD_VEC: '\\overrightarrow' | '\\overline' | '\\widehat' | '\\vec';


condition: L_PAREN (inequality|equality|belong) R_PAREN
| L_PAREN (inequality|equality|belong) DELIMITER (inequality|equality|belong) R_PAREN
| L_PAREN (CHINESE+ | atom | inequality | equality | belong)+ R_PAREN
| L_PAREN text R_PAREN;

func_cond: poly DELIMITER? AND? condition| poly DELIMITER (belong | inequality | equality) | poly L_PAREN (belong | inequality) R_PAREN;
cc:L_BRACE LETTER+ R_BRACE;
equations: BEGIN EQU BEGIN CASE equality (EQSEP equality)+ END CASE END EQU
| BEGIN CASE (equality | inequality) DELIMITER* (AND? EQSEP (equality | inequality) DELIMITER*)+ END CASE
| L_BRACE? BEGIN ALIGN (AND? (inequality | equality)) (DELIMITER* CR? AND? (inequality | equality))+ DELIMITER* END ALIGN R_BRACE?
| LEFT? EQBRACE BEGIN ARRAY cc (poly | equality | func_cond | inequality) DELIMITER* (EQSEP (poly | equality | func_cond | inequality) DELIMITER*)+ (END ARRAY RIGHT?)?
| L_BRACE? BEGIN ALIGN AND? func_cond DELIMITER* (CR AND? func_cond)+ DELIMITER* END ALIGN R_BRACE?
;

zhixian: atom COLON equality;
yuan: YUAN atom COLON equality;
yuanname: YUAN atom;
hypo: (atom COLON)? (ANY|EXIST) (belong|inequality) (DELIMITER (inequality|equality))?;
not_hypo: NEG atom;
boolop: atom (WEDGE|VEE) atom;
//inequalities: LEFT EQBRACE BEGIN ARRAY CC inequality (EQSEP inequality)+ END ARRAY RIGHT;

function: func_name EQUAL (poly | equations) (DELIMITER belong)?
| atom EQUAL equations;
projection: FUNC_FLAG COLON UPPER PORJ UPPER;

//functions: func_name EQUAL LEFT EQBRACE BEGIN ARRAY CC equation (EQSEP equation)+ END ARRAY RIGHT;

question: L_PAREN R_PAREN (CM|DU)?;
triangle: 'Rt'? TRI upper upper upper;
sibian: SIBIAN upper upper upper upper;
compare: poly L_PAREN R_PAREN poly;
mingtiname: 'MT' NUMBER;

mingti: 'MT' NUMBER EQUAL L_ESCAPE exprs R_ESCAPE;
mingti2: ('p:' | 'q:') expr;
expr: (projection | mingti | mingti2 | mingtiname | compare | geo_expr | zhixian_expr | yuan | yuanname | hypo | not_hypo| boolop | zhixian | vectoreq | equations | sibian | triangle | belong | point | partial | function | geo_atom | interval | poly | inequality | equality | question | sett) (question| DELIMITER? condition)? | condition;
exprs: text? (expr text)* expr?;

具体解释词法规则:

包括unicode编码(Unicode编码转换 - 站长工具)及LaTeX(LaTeX 公式编辑器

fragment DIGIT: [0-9]; // 0,1,2,3,4,5,6,7,8,9

词法片段,构成词法的元素,不是一个词法规则。在词法规则中可引用一个或多个词法片段。(antlr v4 使用指南连载5——如何编写词法定义 - eventer - 博客园

在词法规则中那些不会被语法规则直接调用的词法规则可以用一个fragment关键字来标识,fragment标识的规则只能为其它词法规则提供基础。

Antlr 中 fragment词法规则_jazwoo的博客-CSDN博客_antlr4 fragment

每一个规则后面都要需要有一个标签 ,以 # 开头,后面接标签内容。在 antlr 为每一个规则生成事件的时候会用到。

语法规则:text  ( ;  .  、: 中文字符 一-鿿?“”…《》①②③④【】 多项式)一个或多个;func_normal  基本函数

有些根据项目要求的具体修改举例

乘除法时第二项因子自动加了括号
因为def visitT2(self, ctx):
    if ctx.DOT()!=None:
        return self.visit(ctx.poly(0))+'*'+self.visit(ctx.poly(1))
    else:
        return self.visit(ctx.poly(0))+self.visit(ctx.getChild(1))+ '(' + self.visit(ctx.poly(1)) + ')'
>>>把括号去掉
def visitT2(self, ctx):
    if ctx.DOT()!=None:
        return self.visit(ctx.poly(0))+'*'+self.visit(ctx.poly(1))
    else:
        return self.visit(ctx.poly(0))+self.visit(ctx.getChild(1))+ self.visit(ctx.poly(1))
除法符号的修改
def visitT2(self, ctx):
    if ctx.DOT()!=None:
        return self.visit(ctx.poly(0))+'*'+self.visit(ctx.poly(1))
    elif ctx.getChild(1).getText() == '\div':
        return self.visit(ctx.poly(0))+'÷'+self.visit(ctx.poly(1))
    else:
        return self.visit(ctx.poly(0))+self.visit(ctx.getChild(1))+ self.visit(ctx.poly(1))
改exp2
def visitExp2(self, ctx):
    if ctx.getChild(0).getText() == 'm':
        return self.visit(ctx.getChild(0)) + "^{" + self.visit(ctx.getChild(3)) + "}"
    else:
        under = self.visit(ctx.getChild(0))
        upper = self.visit(ctx.poly())
        return '(' + under + ')' + '**' + '(' + upper + ')'
输出带中文单位的字段而不输出纯粹的中文单位

def visitUnit(self, ctx):
    return ctx.getText()

def visitUnit(self, ctx:myparserParser.UnitContext):
    if len(ctx.children) > 1:
        self.parse_result.append({'type': 'unit', 'text': ctx.getText(), 'sympy': self.visitor.visit(ctx)})
    else:
        self.parse_result.append({'type': 'unit', 'text': ctx.getText()})

代码逻辑

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值