總監(jiān):阿毛啊,下面咱們負(fù)責(zé)的項(xiàng)目你負(fù)責(zé)詞法、語(yǔ)法那塊的工作吧?
我:emmm........詞法語(yǔ)法我倒是大學(xué)有學(xué)過(guò),編譯原理????大學(xué)學(xué)的就不好,難于上青天啊。。
總監(jiān):你覺(jué)得有什么問(wèn)題嗎?
我:好的(內(nèi)心一萬(wàn)個(gè)拒絕),我前期先調(diào)研一下吧。
總監(jiān):好的這個(gè)功能比較難,好好用心啊。畫(huà)大餅開(kāi)始。。。。
經(jīng)過(guò)幾天的調(diào)研分析,antlr是最適合不過(guò)的了。下面咱們就一起學(xué)習(xí)一下吧。
ANTLR 官方網(wǎng)址 http://www.antlr.org/
ANTLR 官方 Github https://github.com/antlr/antlr4
大量語(yǔ)法文件例子 https://github.com/antlr/grammars-v4
Antlr是一款強(qiáng)大的語(yǔ)法分析器生成工具,可用于讀取、處理、執(zhí)行和翻譯結(jié)構(gòu)化的文本或二進(jìn)制文件。它被廣泛的應(yīng)用于學(xué)術(shù)領(lǐng)域和工業(yè)生產(chǎn)實(shí)踐,是眾多語(yǔ)言、工具和框架的基石。Twitter所使用Antlr進(jìn)行語(yǔ)法分析,每天超過(guò)20億次查詢,Hadoop生態(tài)環(huán)境中的Hive、Pig、數(shù)據(jù)倉(cāng)庫(kù)和分析系統(tǒng)所使用的分析系統(tǒng)都使用到了Antlr。
Antlr是用JAVA寫(xiě)的語(yǔ)言識(shí)別工具,它用來(lái)聲明語(yǔ)言的語(yǔ)法,簡(jiǎn)稱為“元語(yǔ)言”(meta-language)。
Antlr 語(yǔ)法識(shí)別一般分為二個(gè)階段:
1.詞法分析階段 (lexical analysis)
對(duì)應(yīng)的分析程序叫做 lexer ,負(fù)責(zé)將符號(hào)(token)分組成符號(hào)類(token class or token type)
2.解析階段
根據(jù)詞法,構(gòu)建出一棵分析樹(shù)(parse tree)或叫語(yǔ)法樹(shù)(syntax tree)
1.定制特定領(lǐng)域語(yǔ)言(DSL)
類似hibernate中的HQL,用DSL來(lái)定義要執(zhí)行操作的高層語(yǔ)法,這種語(yǔ)法接近人可理解的語(yǔ)言,由DSL到計(jì)算機(jī)語(yǔ)言的翻譯則通過(guò)ANTLR來(lái)做,可在ANTLR的結(jié)構(gòu)語(yǔ)言中定義DSL命令具體要執(zhí)行何種操作。
2.文本解析 可利用ANTLR解析JSON,HTML,XML,EDIFACT,或自定義的報(bào)文格式。解析出來(lái)的信息需要做什么處理也可以在結(jié)構(gòu)文件中定義。
3.數(shù)學(xué)計(jì)算 加減乘除,線性方程,幾何運(yùn)算,微積分等等
/** Optional javadoc style comment */
grammar Name;
options {...}
import ... ;
tokens {...}
channels {...} // lexer only
@actionName {...}
rule1 // parser and lexer rules, possibly intermingled
...
ruleN
聲明語(yǔ)法頭,類似于java類的定義
grammar SPL;
選項(xiàng),如語(yǔ)言選項(xiàng),輸出選項(xiàng),回溯選項(xiàng),記憶選項(xiàng)等等
options { output=AST; language=Java; }
options { tokenVocab=MySqlLexer; }
動(dòng)作(Actions)實(shí)際上是用目標(biāo)語(yǔ)言寫(xiě)成的、嵌入到規(guī)則中的代碼(以花括號(hào)包裹)。它們通常直接操作輸入的標(biāo)號(hào),但是他們也可以用來(lái)調(diào)用相應(yīng)的外部代碼。屬性,到目前為止我的理解還不多,感覺(jué)像是C++中類里面的成員。常用屬性或動(dòng)作說(shuō)明:
這是核心,表示規(guī)則,以 “:” 開(kāi)始, “;” 結(jié)束, 多規(guī)則以 "|" 分隔。
ID : [a-zA-Z0-9|'_']+ ; //數(shù)字
STR:'\'' ('\'\'' | ~('\''))* '\'';
WS: [ \t\n\r]+ -> skip ; // 系統(tǒng)級(jí)規(guī)則 ,即忽略換行與空格
sqlStatement
: ddlStatement
| dmlStatement | transactionStatement
| replicationStatement | preparedStatement
| administrationStatement | utilityStatement
;
/**
* This grammar is an example illustrating the three kinds
* of comments.
*/
grammar T;
/* a multi-line
comment
*/
/** This rule matches a declarator for my language */
decl : ID ; // match a variable name
ID, LPAREN, RIGHT_CURLY // token names
expr, simpleDeclarator, d2, header_file // rule names
grammar Dsl; //定義規(guī)則文件grammar
@header { //一種action,定義生成的詞法語(yǔ)法解析文件的頭,當(dāng)使用java的時(shí)候,生成的類需要包名,可以在這里統(tǒng)一定義
package antlr;
}
//parsers
sta:(sql ender)*; //定義sta規(guī)則,里面包含了*(0個(gè)以上)個(gè) sql ender組合規(guī)則
ender:';'; //定義ender規(guī)則,是一個(gè)分號(hào)
sql //定義sql規(guī)則,sql規(guī)則有兩條分支:select/load
: SELECT ~(';')* as tableName //select語(yǔ)法規(guī)則,以lexer SELECT開(kāi)頭, 以as tableName 結(jié)尾,其中as 和tableName分別是兩個(gè)parser
| LOAD format '.' path as tableName //load語(yǔ)法規(guī)則,大致就是 load json.'path' as table1,load語(yǔ)法里面含有format,path, as,tableName四種規(guī)則
; //sql規(guī)則結(jié)束符
as: AS; //定義as規(guī)則,其內(nèi)容指向AS這個(gè)lexer
tableName: identifier; //tableName 規(guī)則,指向identifier規(guī)則
format: identifier; //format規(guī)則,也指向identifier規(guī)則
path: quotedIdentifier; //path,指向quotedIdentifier
identifier: IDENTIFIER | quotedIdentifier; //identifier,指向lexer IDENTIFIER 或者parser quotedIdentifier
quotedIdentifier: BACKQUOTED_IDENTIFIER; //quotedIdentifier,指向lexer BACKQUOTED_IDENTIFIER
//lexers antlr將某個(gè)句子進(jìn)行分詞的時(shí)候,分詞單元就是如下的lexer
//keywords 定義一些關(guān)鍵字的lexer,忽略大小寫(xiě)
AS: [Aa][Ss];
LOAD: [Ll][Oo][Aa][Dd];
SELECT: [Ss][Ee][Ll][Ee][Cc][Tt];
//base 定義一些基礎(chǔ)的lexer,
fragment DIGIT:[0-9]; //匹配數(shù)字
fragment LETTER:[a-zA-Z]; //匹配字母
STRING //匹配帶引號(hào)的文本
: '\'' ( ~('\''|'\\') | ('\\' .) )* '\''
| '"' ( ~('"'|'\\') | ('\\' .) )* '"'
;
IDENTIFIER //匹配只含有數(shù)字字母和下劃線的文本
: (LETTER | DIGIT | '_')+
;
BACKQUOTED_IDENTIFIER //匹配被``包裹的文本
: '`' ( ~'`' | '``' )* '`'
;
//--hiden 定義需要隱藏的文本,指向channel(HIDDEN)就會(huì)隱藏。這里的channel可以自定義,到時(shí)在后臺(tái)獲取不同的channel的數(shù)據(jù)進(jìn)行不同的處理
SIMPLE_COMMENT: '--' ~[\r\n]* '\r'? '\n'? -> channel(HIDDEN); //忽略行注釋
BRACKETED_EMPTY_COMMENT: '/**/' -> channel(HIDDEN); //忽略多行注釋
BRACKETED_COMMENT : '/*' ~[+] .*? '*/' -> channel(HIDDEN) ; //忽略多行注釋
WS: [ \r\n\t]+ -> channel(HIDDEN); //忽略空白符
// 匹配其他的不能使用上面的lexer進(jìn)行分詞的文本
UNRECOGNIZED: .;
1、Listener (觀察者模式,通過(guò)結(jié)點(diǎn)監(jiān)聽(tīng),觸發(fā)處理方法)
2、Visitor (訪問(wèn)者模式,主動(dòng)遍歷)
下載Java安裝
我使用的是Java8,這里就不詳細(xì)介紹Java的安裝了。
1.下載antlr-4.7.2-complete.jar
https://www.antlr.org/download/antlr-4.7.2-complete.jar
Linux下的安裝方式:
$ cd /usr/local/lib
$ sudo curl -O https://www.antlr.org/download/antlr-4.7.2-complete.jar
2.新建運(yùn)行腳本 antlr4.bat 和 grun.bat,放置于任意目錄,如 E:/tools/antlr4
antlr4.bat 內(nèi)容:
java org.antlr.v4.Tool %*
grun.bat 內(nèi)容:
java org.antlr.v4.gui.TestRig %*
注:antlr依賴于java,如果java環(huán)境變量沒(méi)有設(shè)置,請(qǐng)先行設(shè)置好。
3.設(shè)置antlr4的系統(tǒng)環(huán)境變量
Windows環(huán)境配置:
E:/tools/antlr4/antlr-4.7.2-complete.jar
E:/tools/antlr4
Windows環(huán)境變量配置
Linux及Mac:
// 設(shè)置環(huán)境變量
$ vim .bash_profile
$ export CLASSPATH=".:/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH"
$ alias antlr4='java -jar /usr/local/lib/antlr-4.7.2-complete.jar'
$ alias grun='java org.antlr.v4.gui.TestRig'
$ wq
$ source .bash_profile
$ antlr4
ANTLR Parser Generator Version 4.7.2
-o ___ specify output directory where all output is generated
-lib ___ specify location of grammars, tokens files
-atn generate rule augmented transition network diagrams
-encoding ___ specify grammar file encoding; e.g., euc-jp
-message-format ___ specify output style for messages in antlr, gnu, vs2005
-long-messages show exception details when available for errors and warnings
-listener generate parse tree listener (default)
-no-listener don't generate parse tree listener
-visitor generate parse tree visitor
-no-visitor don't generate parse tree visitor (default)
-package ___ specify a package/namespace for the generated code
-depend generate file dependencies
-D<option>=value set/override a grammar-level option
-Werror treat warnings as errors
-XdbgST launch StringTemplate visualizer on generated code
-XdbgSTWait wait for STViz to close before continuing
-Xforce-atn use the ATN simulator for all predictions
-Xlog dump lots of logging info to antlr-timestamp.log
-Xexact-output-dir all output goes into -o dir regardless of paths/package
$ grun
java org.antlr.v4.gui.TestRig GrammarName startRuleName
[-tokens] [-tree] [-gui] [-ps file.ps] [-encoding encodingname]
[-trace] [-diagnostics] [-SLL]
[input-filename(s)]
Use startRuleName='tokens' if GrammarName is a lexer grammar.
Omitting input-filename makes rig read from stdin.
安裝成功
$ mkdir test
$ cd test/
$ vim Hello.g4
輸入:
// Define a grammar called Hello
grammar Hello;
r : 'hello' ID ; // match keyword hello followed by an identifier
ID : [a-z]+ ; // match lower-case identifiers
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
$ wq! // 保存
$ antlr4 Hello.g4
$ ls
Hello.g4 HelloBaseListener.java HelloLexer.tokens
Hello.interp HelloLexer.interp HelloListener.java
Hello.tokens HelloLexer.java HelloParser.java
$ javac Hello*.java
$ ls
Hello.g4 HelloLexer.java
Hello.interp HelloLexer.tokens
Hello.tokens HelloListener.class
HelloBaseListener.class HelloListener.java
HelloBaseListener.java HelloParser$RContext.class
HelloLexer.class HelloParser.class
HelloLexer.interp HelloParser.java
$ grun Hello r -gui // 按回車(chē)
hello nihao // 輸入這個(gè),再按回車(chē)
^D // 按ctrl+D 就出現(xiàn)gui圖形界面了
GUI界面
java編譯過(guò)程
和生成Java代碼類似,不同的地方在于編譯g4文件命令:
antlr4 -Dlanguage=Python3 -visitor Expr.g4
這樣就會(huì)生成python代碼:
? antlr4 -Dlanguage=Python3 Hello.g4
? ls
Hello.g4 Hello.tokens HelloLexer.py HelloListener.py
Hello.interp HelloLexer.interp HelloLexer.tokens HelloParser.py
python編譯
當(dāng)然還可以生成其他代碼,感興趣的可以嘗試學(xué)一下。
上面就是我在學(xué)習(xí)Antlr中所了解的知識(shí),歡迎大家指正,一起學(xué)習(xí),一起進(jìn)步。
關(guān)注私信小編,Antlr權(quán)威指南分享給你,另外小編會(huì)不定時(shí)分享一些學(xué)習(xí)知識(shí)點(diǎn)。一起學(xué)習(xí)吧。
在本教程中,我們學(xué)習(xí)如何ant在 Debian 11 上安裝。
Apache Ant 是一個(gè) Java 庫(kù)和命令行工具,其任務(wù)是驅(qū)動(dòng)構(gòu)建文件中描述的進(jìn)程作為相互依賴的目標(biāo)和擴(kuò)展點(diǎn)。Ant 的主要已知用途是構(gòu)建 Java 應(yīng)用程序。Ant 提供了許多內(nèi)置任務(wù),允許編譯、組裝、測(cè)試和運(yùn)行 Java 應(yīng)用程序。Ant 也可以有效地用于構(gòu)建非 Java 應(yīng)用程序,例如 C 或 C++ 應(yīng)用程序。更一般地說(shuō),Ant 可用于試驗(yàn)任何類型的過(guò)程,這些過(guò)程可以用目標(biāo)和任務(wù)來(lái)描述。這個(gè)包包含腳本和核心任務(wù)庫(kù)。
ant在 Debian 11上安裝有三種方式。我們可以使用apt-get,apt和aptitude. 在以下部分中,我們將描述每種方法。您可以選擇其中之一。
apt-get使用以下命令更新 apt 數(shù)據(jù)庫(kù)。
sudo apt-get update
更新 apt 數(shù)據(jù)庫(kù)后,我們可以ant通過(guò)apt-get運(yùn)行以下命令進(jìn)行安裝:
sudo apt-get -y install ant
apt使用以下命令更新 apt 數(shù)據(jù)庫(kù)。
sudo apt update
更新 apt 數(shù)據(jù)庫(kù)后,我們可以ant通過(guò)apt運(yùn)行以下命令進(jìn)行安裝:
sudo apt -y install ant
如果你想遵循這個(gè)方法,你可能需要先安裝aptitude,因?yàn)?aptitude 通常不會(huì)在 Debian 上默認(rèn)安裝。aptitude使用以下命令更新 apt 數(shù)據(jù)庫(kù)。
sudo aptitude update
更新 apt 數(shù)據(jù)庫(kù)后,我們可以ant通過(guò)aptitude運(yùn)行以下命令進(jìn)行安裝:
sudo aptitude -y install ant
要僅卸載ant軟件包,我們可以使用以下命令:
sudo apt-get remove ant
要卸載antDebian 11 不再需要的及其依賴項(xiàng),我們可以使用以下命令:
sudo apt-get -y autoremove ant
要從 Debian 11 中刪除ant配置和數(shù)據(jù),我們可以使用以下命令:
sudo apt-get -y purge ant
我們可以使用以下命令刪除ant配置、數(shù)據(jù)及其所有依賴項(xiàng),我們可以使用以下命令:
sudo apt-get -y autoremove --purge ant