ANTLR(ANother Tool for Language Recognition)是一个ALL(*)的词法分析器, 功能和yacc类似. 应用很多, 可以解析大量语言和自定义语言, 官方也提供了不同语言的语法文件.
官方的文档非常齐全, 作者也专门出过一本电子书
看了一下官方文档, 对比goyacc, 这个库可以生成更加干净,更容易理解的代码. 并且包括
Hive, Netbeans等软件都用Antlr做解析工具. 网上关于Antlr的资料也不少
ANTLR本身用java实现, 但Runtime的库也支持Golang, Java, Python等等. Antlr提供了goyacc的一个很好的替代品.
对Golang程序员来说, 这篇文章是很好的入门, 从安装Antlr到实现支持算术符优先级的计算器都有介绍
https://zhuanlan.zhihu.com/p/47179842
https://blog.gopheracademy.com/advent-2017/parsing-with-antlr4-and-go/
https://www.antlr.org/
https://www.antlr.org/papers/allstar-techreport.pdf
https://github.com/thesues/antlr-calc-golang-example
https://github.com/antlr/antlr-php-runtime-phpstan
https://github.com/antlr/antlr4
https://github.com/antlr/intellij-plugin-v4
https://blog.gopheracademy.com/advent-2017/parsing-with-antlr4-and-go/
https://github.com/thesues/antlr-calc-golang-example
$ wget http://www.antlr.org/download/antlr-4.7-complete.jar
$ alias antlr=’java -jar $PWD/antlr-4.7-complete.jar’
$java -jar antlr-4.5.1-complete.jar
Error: Invalid or corrupt jarfile antlr-4.5.1-complete.jar
antlr -Dlanguage=Go -o parser Calc.g4
$ tree
├── Calc.g4
└── parser
├── calc_lexer.go
├── calc_parser.go
├── calc_base_listener.go
└── calc_listener.go
// example1.go
package main
import (
“fmt”
“github.com/antlr/antlr4/runtime/Go/antlr”
"./parser" )
func main() {
// Setup the input
is := antlr.NewInputStream(“1 + 2 * 3”)
// Create the Lexer
lexer := parser.NewCalcLexer(is)
// Read all tokens
for {
t := lexer.NextToken()
if t.GetTokenType() == antlr.TokenEOF {
break
}
fmt.Printf("%s (%q)\n",
lexer.SymbolicNames[t.GetTokenType()], t.GetText())
} }
// example2.go
package main
import (
“./parser”
“github.com/antlr/antlr4/runtime/Go/antlr”
)
type calcListener struct {
*parser.BaseCalcListener
}
func main() {
// Setup the input
is := antlr.NewInputStream(“1 + 2 * 3”)
// Create the Lexer
lexer := parser.NewCalcLexer(is)
stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
// Create the Parser
p := parser.NewCalcParser(stream)
// Finally parse the expression
antlr.ParseTreeWalkerDefault.Walk(&calcListener{}, p.Start()) }
https://blog.gopheracademy.com/advent-2017/parsing-with-antlr4-and-go/
https://github.com/thesues/antlr-calc-golang-example
https://blog.csdn.net/RA681t58CJxsgCkJ31/article/details/102714446
https://blog.gopheracademy.com/advent-2017/parsing-with-antlr4-and-go/
https://liangshuang.name/2017/08/20/antlr/
https://abcdabcd987.com/notes-on-antlr4/
https://github.com/antlr/grammars-v4
https://blog.gopheracademy.com/advent-2017/parsing-with-antlr4-and-go/
https://github.com/antlr/grammars-v4/blob/master/basic/jvmBasic.g4
https://github.com/antlr/grammars-v4/blob/master/c/C.g4
https://github.com/antlr/grammars-v4/blob/master/graphql/GraphQL.g4
https://github.com/antlr/grammars-v4/blob/master/golang/GoParser.g4
https://studygolang.com/resources/10408
https://github.com/lightbend/config
https://stackoverflow.com/questions/53100633/antlr4-in-go-invalid-type-assertion-listener
https://www.zhihu.com/topic/20022823/hot
https://blog.csdn.net/diefen3773/article/details/101746629
https://www.jianshu.com/p/4bedad8dd70a
前面介绍LLVM的第一篇中,我们介绍过,编译器的后端基本都可以通过LLVM来解决。
那么,前端我们如何处理呢?我们选择ANTLR。
例子超丰富的ANTLR
ANTLR是用Java写的词法和语法分析工具。它比lex/flex/yacc/bison是更现代的工具。
最方便的一点是,ANTLR已经替我们写好了常用语言的语法规则,我们已经拥有了常见语言的分析器,可以在其基础上直接做我们想做的事情。网址在[https://github.com/antlr/grammars-v4/]
我们看一些例子吧。曾经火遍大江南北的谭浩强老师的《BASIC语言》还有人记得吗?DOS时代,GW-BASIC和QBasic是系统默认自带的语言,如同Unix上的cc编译器一样。
10 FOR I = 1 TO 10 STEP 1
20 PRINT I
30 NEXT I
40 END
我们来看一下ANTLR中对BASIC语言FOR循环的语法:
还有更简单的么?有啊,汇编语言:https://github.com/antlr/grammars-v4/blob/master/masm/MASM.g4
言归正题,我们来看第一种大型的语言,C语言2011版的:https://github.com/antlr/grammars-v4/blob/master/c/C.g4
看个类型的吧,C11的还真不少:
typeSpecifier
: (‘void’
| ‘char’
| ‘short’
| ‘int’
| ‘long’
| ‘float’
| ‘double’
| ‘signed’
| ‘unsigned’
| ‘_Bool’
| ‘_Complex’
| ‘m128’
| ‘__m128d’
| ‘__m128i’)
| ‘__extension’ ‘(‘ (‘m128’ | ‘__m128d’ | ‘__m128i’) ‘)’
| atomicTypeSpecifier
| structOrUnionSpecifier
| enumSpecifier
| typedefName
| ‘__typeof’ ‘(‘ constantExpression ‘)’ // GCC extension
;
我们通过语法规则,大致可以估算一下语言的复杂度:
语言 语法行数 地址
C11 926 https://github.com/antlr/grammars-v4/blob/master/c/C.g4
C++14 2353 https://github.com/antlr/grammars-v4/blob/master/cpp/CPP14.g4
Go 1170 https://github.com/antlr/grammars-v4/blob/master/golang/Golang.g4
Java7 1017 https://github.com/antlr/grammars-v4/blob/master/java/Java.g4
Java8 1780 https://github.com/antlr/grammars-v4/blob/master/java8/Java8.g4
Lua 336 https://github.com/antlr/grammars-v4/blob/master/lua/Lua.g4
Pascal 972 https://github.com/antlr/grammars-v4/blob/master/pascal/pascal.g4
Python3 1558 https://github.com/antlr/grammars-v4/blob/master/python3/Python3.g4
Swift 1163 https://github.com/antlr/grammars-v4/blob/master/swift/Swift.g4
ECMA Script 5 1504 https://github.com/antlr/grammars-v4/blob/master/ecmascript/ECMAScript.g4
Erlang 391 https://github.com/antlr/grammars-v4/blob/master/erlang/Erlang.g4
Fortran 77 1363 https://github.com/antlr/grammars-v4/blob/master/fortran77/fortran77.g4
Scala 704 https://github.com/antlr/grammars-v4/blob/master/scala/Scala.g4
SQLite 905 https://github.com/antlr/grammars-v4/blob/master/sqlite/SQLite.g4
Clojure 262 https://github.com/antlr/grammars-v4/blob/master/clojure/Clojure.g4
从词法复杂度上看:
C,Java7,Swift,Go这几门语法的复杂度是比较适中的
C++和Java 8确实是比较复杂的,比起它们的前辈C和Java 7都变复杂了不少
JavaScript和Python3已经比较复杂了
Clojure,Lua和Erlang是惊喜,规模小,表现力强
装上玩玩吧
既然ANTLR有这么丰富的例子供我们参考,我们就装一个玩玩吧。
在macOS上,通过Homebrew就可以安装。
在Linux上,通过下面的步骤来安装:
wget http://www.antlr.org/download/antlr-4.6-complete.jar
export CLASSPATH=”.:/path/to/antlr-4.6-complete.jar:$CLASSPATH”
alias antlr4=’java -jar /path/to/antlr-4.6-complete.jar’
alias grun=’java org.antlr.v4.gui.TestRig’
照抄个Hello,World的例子试一下吧:
grammar Hello ;
r : ‘hello’ ID ;
ID: [a-z]+ ;
WS : [ \t\r\n]+ -> skip ;
都是正则表达式,很容易理解,ID是小写字母组成的,WS是空格制表符回车换行符,空白符过滤掉。
输入antlr4 Hello.g4,就生成了好几个.java文件,调用javac编译一下。成功!
antlr4 Hello.g4
javac *.java
生成的文件,我们快速浏览一下:
第一个,HelloListener.java:
// Generated from Hello.g4 by ANTLR 4.6
import org.antlr.v4.runtime.tree.ParseTreeListener;
/**
Hello.tokens:
T__0=1
ID=2
WS=3
‘hello’=1
HelloParser.java是解析器。
打包一个可以直接利用java -jar
jar cvfm lottery.jar MANIFEST.MF jdbc.properties com
如果出现:
java.io.IOException: invalid header field
这样的错误的话,就说明MANIFEST.MF文件有问题,比如写成了这样:
Manifest-Version: 1.0
Main-Class:com.feishan.lottery.view.Index
Class-Path: jar/jdbc_feishan.jar jar/mysql5.0.3.jar
或者如果觉得打的可运行jar包那里都是对的,但执行java -jar lottery.jar 或者 java -cp lottery.jar com.test.Test
怎么运行都是 Invalid or corrupt jarfile 要么就是 Could not find or load main class 那么99%绝对是MANIFEST.MF文件有问题
注意:
Main-Class:后面应该有一个空格
Manifest-Version: 1.0
Main-Class: com.feishan.lottery.view.Index
Class-Path: jar/jdbc_feishan.jar jar/mysql5.0.3.jar
这样写就对了注意:最后一行也要有一个回车键。否则后面那一行是不能打包进去的
最后用java -jar lottery.jar就可以运行了
这个 manifest.mf 可以放在任何位置,也可以是其它的文件名,
只需要有 Main-Class: test.Test 一行,且该行以一个回车符结束即可
最后Manifest 技巧说明:
总是以Manifest-Version属性开头
每行最长72个字符,如果超过的化,采用续行
确认每行都以回车结束,否则改行将会被忽略
如果Class-Path 中的存在路径,使用”/”分隔目录,与平台无关
使用空行分隔主属性和package属性
使用”/”而不是”.”来分隔package 和class ,比如 com/example/myapp/
class 要以.class结尾,package 要以 / 结尾
Error: Invalid or corrupt jarfile jar
遇到的问题:IDEA打包可执行jar包,报错Error: Invalid or corrupt jarfile jar
检索问题,看到各种千奇百怪的方法,比如:修改文件后使用jar命令重新打包,还有说要把MANIFEST.MF 大写修改为小写的manifest等,这里谈一种有效的解决方法。
解决方案适用性:IDEA(其它IDE尚未测试)
原因:IDEA中,在File\Project Structure\Artifacts\添加artifacts的时候,默认会在src/main/java/META_INF/下创建目录,但是此目录(src/main/java/)已标记为Sources Root,只编译;
解决方案:将src/main/java/META_INF剪切到目录src/main/resources/META_INF/,src/main/resources/是Resources Root,最后文件会拷贝到out目录。
https://www.jianshu.com/p/18396dce6263
打jar包
1.jar -cvf hello.jar hello.class
2.这时java -jar hello.jar 是运行不了的
3.解压刚打的Jar包到新的目录,会发现里边多了一个META-INF文件夹里边有一个MANIFEST.MF文件
4.用记事本打开MANIFEST.MF文件,修改为
Manifest-Version: 1.0
Main-Class: Hello
Created-By: aaa
5.将MANIFEST.MF复制到和Hello.class 放在一起
然后打包:
jar cvfm abc.jarMANIFEST.MFHello.class
得到可运行的jar
java -jar * 就可以运行的jar包[ 其中*为jar包名]
打 Java 包的时候可以有一个清单文件:MANIFEST.MF,它是打包的关键性文件,主要是设置执行入口类和支持库的路径,在运行 Java应用程序时会根据此文件中给出的信息来查找入口类和支持库。
它的内容一般包括:
Manifest-Version: 1.0
Created-By: 1.6.0 (Sun Microsystems Inc.)
Main-Class: HelloWorld
其中比较容易忽略的是还可以有一个Class-Path属性的设置,Class-Path:用来指定支持库的路径,程序运行时依据 Class-Path项的设置路径来查找支持库,每一个支持库之间用空格分开。比如这样写清单文件:
Manifest-Version: 1.0
Class-Path: ./lib/msbase.jar ./lib/mssqlserver.jar ./lib/msutil.jar
Created-By: yourName
Main-Class: org.qiujy.test.TestDB
如果出现:
java.io.IOException: invalid header field
这样的错误的话,就说明MANIFEST.MF文件有问题,比如写成了这样:
Manifest-Version: 1.0
Main-Class:com.feishan.lottery.view.Index
Class-Path: jar/jdbc_feishan.jar jar/mysql5.0.3.jar
注意:
Main-Class:后面应该有一个空格:
Manifest-Version: 1.0
Main-Class: com.feishan.lottery.view.Index
Class-Path: jar/jdbc_feishan.jar jar/mysql5.0.3.jar
这样写就对了注意:最后一行也要有一个回车键。否则后面那一行是不能打包进去的
最后用java -jar lottery.jar就可以运行了
这个 manifest.mf (证明)可以放在任何位置,也可以是其它的文件名,
只需要有 Main-Class: test.Test 一行,且该行以一个回车符结束
https://blog.csdn.net/master_yao/article/details/51089745
mkdir -p src/main/resources
vi src/main/resources/META_INF
Manifest-Version: 1.0
Implementation-Vendor: ANTLR
Implementation-Title: ANTLR 4 Tool
Implementation-Version: 4.8
Implementation-Vendor-Id: org.antlr
Built-By: parrt
Build-Jdk: 1.7.0_65
Created-By: Apache Maven 3.6.3
Implementation-URL: http://www.antlr.org
Main-Class: org.antlr.v4.Tool
Archiver-Version: Plexus Archiver
jar cvfm antlr-4.5.1-complete.jar src/main/resources/META_INF
$java -jar antlr-4.5.1-complete.jar
错误: 找不到或无法加载主类 org.antlr.v4.Tool
https://blog.csdn.net/master_yao/article/details/51089745
https://tomassetti.me/category/language-engineering/antlr/#setup-antlr
$export CLASSPATH=”.:/Users/didi/PhpstormProjects/c/json-parser/antlr/antlr-4.5.1-complete.jar:$CLASSPATH”
$java -Xmx500M -cp “/Users/didi/PhpstormProjects/c/json-parser/antlr/antlr-4.5.1-complete.jar:$CLASSPATH” org.antlr.v4.Tool
错误: 找不到或无法加载主类 org.antlr.v4.Tool
https://www.jianshu.com/p/b4714b98389d
https://www.cnblogs.com/chunzhulovefeiyue/p/7577199.html
https://blog.csdn.net/cl2010abc/article/details/104944662