在Linux下建立ANTLR3.4的C語言環境--詳細
阿新 • • 發佈:2021-02-05
技術標籤:antlr
在Linux下建立ANTLR3.4C的環境
基於https://www.coder4.com/archives/4016,補充一些細節和我個人的理解
因為最近要做這方面的專案,但一直找不到一個詳細的、直接的教程,所以總結了一下,給大家做個參考
我的開發環境是WSL2–Ubuntu 20.04 LTS
一、編譯安裝 antlr c 庫
建議全程儘量科學上網,不然網速太慢了。。。
wget http://www.antlr3.org/download/C/libantlr3c-3.4.tar.gz
tar -xzvf ./libantlr3c-3.4.tar.gz
cd libantlr3c-3.4
./configure --enable-64bit
make
sudo make install
二、下載 antlr 3.4 jar包
注意:必須要用3.4的jar包,我又試了3.5.2,也不行。。。
cd /usr/local/lib
wget http://www.antlr3.org/download/antlr-3.4-complete.jar
為jar包建立一個永久的環境變數
sudo vim ~/.bashrc
在最後加一行
export CLASSPATH=/usr/local/lib/antlr-3.4-complete.jar:$CLASSPATH
儲存後
source ~/.bashrc
然後用下面的程式碼驗證,需要自己安裝java
java org.antlr.Tool
三、編寫語法檔案(ExprCppTree.g)
cd ~
vim ExprCppTree.g
程式碼直接照搬的
grammar ExprCppTree;
options {
language = C;
output = AST;
ASTLabelType=pANTLR3_BASE_TREE;
}
@header {
#include <assert.h>
}
// The suffix '^' means make it a root.
// The suffix '!' means ignore it.
expr: multExpr ((PLUS^ | MINUS^) multExpr)*
;
PLUS: '+';
MINUS: '-';
multExpr
: atom (TIMES^ atom)*
;
TIMES: '*';
atom: INT
| ID
| '('! expr ')'!
;
stmt: expr NEWLINE -> expr // tree rewrite syntax
| ID ASSIGN expr NEWLINE -> ^(ASSIGN ID expr) // tree notation
| NEWLINE -> // ignore
;
ASSIGN: '=';
prog
: (stmt {pANTLR3_STRING s = $stmt.tree->toStringTree($stmt.tree);
assert(s->chars);
printf(" tree \%s\n", s->chars);
}
)+
;
ID: ('a'..'z'|'A'..'Z')+ ;
INT: '~'? '0'..'9'+ ;
NEWLINE: '\r'? '\n' ;
WS : (' '|'\t')+ {$channel = HIDDEN;};
編譯
java org.antlr.Tool ExprCppTree.g
看一下檔案,應該有這些
ExprCppTree.g ExprCppTreeLexer.c ExprCppTreeLexer.h ExprCppTreeParser.c ExprCppTreeParser.h ExprCppTree.tokens
四、編寫驅動檔案(main.cpp)
vim main.cpp
#include "ExprCppTreeLexer.h"
#include "ExprCppTreeParser.h"
#include <cassert>
#include <map>
#include <string>
#include <iostream>
using std::map;
using std::string;
using std::cout;
class ExprTreeEvaluator {
map<string,int> memory;
public:
int run(pANTLR3_BASE_TREE);
};
pANTLR3_BASE_TREE getChild(pANTLR3_BASE_TREE, unsigned);
const char* getText(pANTLR3_BASE_TREE tree);
int main(int argc, char* argv[])
{
pANTLR3_INPUT_STREAM input;
pExprCppTreeLexer lex;
pANTLR3_COMMON_TOKEN_STREAM tokens;
pExprCppTreeParser parser;
assert(argc > 1);
input = antlr3FileStreamNew((pANTLR3_UINT8)argv[1],ANTLR3_ENC_8BIT);
lex = ExprCppTreeLexerNew(input);
tokens = antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT,
TOKENSOURCE(lex));
parser = ExprCppTreeParserNew(tokens);
ExprCppTreeParser_prog_return r = parser->prog(parser);
pANTLR3_BASE_TREE tree = r.tree;
ExprTreeEvaluator eval;
int rr = eval.run(tree);
cout << "Evaluator result: " << rr << '\n';
parser->free(parser);
tokens->free(tokens);
lex->free(lex);
input->close(input);
return 0;
}
int ExprTreeEvaluator::run(pANTLR3_BASE_TREE tree)
{
pANTLR3_COMMON_TOKEN tok = tree->getToken(tree);
if(tok) {
switch(tok->type) {
case INT: {
const char* s = getText(tree);
if(s[0] == '~') {
return -atoi(s+1);
}
else {
return atoi(s);
}
}
case ID: {
string var(getText(tree));
return memory[var];
}
case PLUS:
return run(getChild(tree,0)) + run(getChild(tree,1));
case MINUS:
return run(getChild(tree,0)) - run(getChild(tree,1));
case TIMES:
return run(getChild(tree,0)) * run(getChild(tree,1));
case ASSIGN: {
string var(getText(getChild(tree,0)));
int val = run(getChild(tree,1));
memory[var] = val;
return val;
}
default:
cout << "Unhandled token: #" << tok->type << '\n';
return -1;
}
}
else {
int k = tree->getChildCount(tree);
int r = 0;
for(int i = 0; i < k; i++) {
r = run(getChild(tree, i));
}
return r;
}
}
pANTLR3_BASE_TREE getChild(pANTLR3_BASE_TREE tree, unsigned i)
{
assert(i < tree->getChildCount(tree));
return (pANTLR3_BASE_TREE) tree->getChild(tree, i);
}
const char* getText(pANTLR3_BASE_TREE tree)
{
return (const char*) tree->getText(tree)->chars;
}
五、編譯,測試
需要安裝g++,然後用下面的程式碼編譯,生成的可執行檔案是test
g++ -g -Wall *.cpp *.c /usr/local/lib/libantlr3c.a -o test -I. -I /usr/local/include/
忽略一堆warning。。。
建立測試檔案data
vim data
寫入
1+2*(3+4)
儲存退出
用test分析data
./test ./data
得到輸出結果
tree (+ 1 (* 2 (+ 3 4)))
Evaluator result: 15