ANTLR实践
“除草帖。翻到了幾年前做的一個語言解析的小項目,感覺筆記躺在自己筆記本里挺浪費,放在博客上或許還能體現(xiàn)點價值”。
先附上參考資料:
博客:ANTLR(一)_qq_33669252的博客-CSDN博客
相關教程:https://www.crifan.com/files/doc/docbook/antlr_tutorial/release/html/antlr_tutorial.html
很好的講解(從簡介到環(huán)境準備等):antlr v4 使用指南連載1——簡介 - eventer - 博客園
舉例(計算器):用 antlr 做一個計算器_十木禾的博客-CSDN博客_antlr 計算器
ANTLR簡介
ANTLR是一個強大語言解析工具,可用于處理結構化文本,二進制文件,是一個更強大的正則表達式工具。
兩個重要部分——詞法laxer和語法parser。
詞法就是:
標識符,即各類編程語言中所說的以下劃線、字母開頭的字符串
字面量,英文叫Literal,其實就是可以當作值的東西,放在操作符兩邊。如數(shù)字、單引號字符串、雙引號字符串、各個進制寫法等
字符,單字符(!、~、=、>等)、雙字符(>=、<=)等
關鍵字,如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
第二行以小寫字母開頭,是一個語法規(guī)則。hello后面跟著一個ID標識符。ID標識符的定義在第三行定義
第三行以大寫字母開頭,是一個詞法規(guī)則。ID由a-z這26個英文小寫字母的一個或多個組成
第四行以大寫字母開頭,是一個詞法規(guī)則。WS由制表符、換行符的一個或多個組成。->skip是action,表示當處理這個詞法規(guī)則時采取的處理方法。skip表示跳過,不處理制表符、換行符,直接處理下一個詞法規(guī)則。
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語法規(guī)則文件,簡要聲明:
grammar Name?這是詞法跟語法都在同一個文件聲明的寫法,稱之為combined。若要分開,可以使用lexer grammar Name和parser grammar Name。
options?可以是如下四個選項。
superClass:用于生成xxxLexer.java、xxxParser.java的父類
language:目標語句,如java
tokenVocab:toekn詞庫
TokenLabelType:默認的是antlr的Token類型,這里可以使用自定義的token類,如MyToken。需要配合TokenFactory使用
import?可以導入各個獨立的lexer、parser文件,只能用于combined寫法。
actionName?可以是如下內容
@header:定義類文件頭。比如嵌入java的package、import聲明
@member:定義類文件內容。比如類成員、方法(如果要指定在lexer或者parser中,可以使用 @lexer::membere、@parser::member。)
rule?語法規(guī)則。
截取部分對myparser.g4的語句解釋
grammar myparser;options { language=Python2; } WS: [ \t\r\n]+ -> skip;ADD: '+'; // 加法 SUB: '-'; // 減法 MUL: '*' |'\\ast' | '·'; // 乘法 DIV: '/'; // 除法 DOT: '\u2022' | '\u22c5'; // 點乘EXP: '**'; // 指數(shù)運算 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?;具體解釋詞法規(guī)則:
包括unicode編碼(Unicode編碼轉換 - 站長工具)及LaTeX(LaTeX 公式編輯器)
fragment DIGIT: [0-9]; // 0,1,2,3,4,5,6,7,8,9詞法片段,構成詞法的元素,不是一個詞法規(guī)則。在詞法規(guī)則中可引用一個或多個詞法片段。(antlr v4 使用指南連載5——如何編寫詞法定義 - eventer - 博客園)
在詞法規(guī)則中那些不會被語法規(guī)則直接調用的詞法規(guī)則可以用一個fragment關鍵字來標識,fragment標識的規(guī)則只能為其它詞法規(guī)則提供基礎。
Antlr 中 fragment詞法規(guī)則_jazwoo的博客-CSDN博客_antlr4 fragment
每一個規(guī)則后面都要需要有一個標簽 ,以 # 開頭,后面接標簽內容。在 antlr 為每一個規(guī)則生成事件的時候會用到。
語法規(guī)則:text? ( ;? .? 、:?中文字符 一-??“”…《》①②③④【】?多項式)一個或多個;func_normal? 基本函數(shù)
有些根據(jù)項目要求的具體修改舉例
乘除法時第二項因子自動加了括號 因為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()})代碼邏輯
?
總結
- 上一篇: 全球及中国药店市场竞争现状调研与运营风险
- 下一篇: 装饰模式C++详解