Edited at

ANTLR 3 と C Runtime と Emscripten で、電卓を作成して見た。

More than 1 year has passed since last update.

ANTLR の C言語 で、 Lexer Parser を作るノウハウ的なものが、

Googleしてもでて来なかったので、記録を残して起きます。

で、なおかつ、Emscriptenを利用します。 なので、ブラウザーで動作します。 スゴ!!


ANTLR は、Parser を作るツール

ANTLRって何それ? 的な人向けに、軽めの解説です。

ANTLRは、LexerやParserを生成するツールです。 プログラム言語の開発とかで使えます。

ざっくりいうと、「文章を読み込んで、木構造にして返す。」これが、「Lexer->Parser」の中身です。

軽めの説明は以下もみてね

http://kyorohiro.blogspot.jp/2017/09/bake-tiny-calc-app-at-antlr3-and-clang.html


Emscriptenを使えば、C言語をJS化できる。

Emscriptenは、LLVMのバイトコードをJavaScriteに変換するツールです。

http://kripken.github.io/emscripten-site/

C言語の資産を利用しまくれます。

以下とか見ると、クールな使い方が沢山おがめます。

https://github.com/kripken/emscripten/wiki/Porting-Examples-and-Demos


実際に何か作って見る

https://github.com/kyorohiro/libantlr3c_emscripten


Install Mac

Antlr4は、C++のParserは出力できるけど。 C言語は出力できないので、

Antlr3を使いました!!

brew install antlr3

brew install libantlr3c


Emscripten用に C Runtimeをビルドしておく。

brew で ゲットし C Runtimeは、Macようなので、C言語ようにビルドしなおす必要があります。

以下に起きましたので、参考までにどうぞ!!

https://github.com/kyorohiro/libantlr3c_emscripten


Write Grammer


Calc.g3

grammar Calc;

options {
language = C;
output = AST;
ASTLabelType=pANTLR3_BASE_TREE;
}
gprog: (gstat {
pANTLR3_STRING s = $gstat.tree->toStringTree($gstat.tree);
printf(" tree \%s\n", s->chars);
})+;
gstat: gexpr NEWLINE -> gexpr | NEWLINE ->;
gexpr: gmultExpr (( PLUS^ | MINUS^ ) gmultExpr)*;
gmultExpr: gatom (TIMES^ gatom)*;
gatom: INT | '('! gexpr ')'!;
INT: '0'..'9'+;
NEWLINE: '\r'? '\n';
WS: (' '|'\t')+{$channel = HIDDEN;};
PLUS: '+';
MINUS: '-';
TIMES: '*';



Generate Parser

antlr3 Calc.g3


Write Main


Main.c

#include <stdio.h>

#include <antlr3.h>
#include "CalcLexer.h"
#include "CalcParser.h"

int run(pANTLR3_BASE_TREE tree) {
pANTLR3_COMMON_TOKEN tok = tree->getToken(tree);
if(tok == NULL) {
int len = tree->getChildCount(tree);
int ret = 0;
for(int i = 0; i < len; i++) {
int v = run((pANTLR3_BASE_TREE)tree->getChild(tree, i));
printf("ret = %d\r\n", v);
ret += v;
}
return ret;
}
switch(tok->type) {
case INT:{
char* s = (char*) tree->getText(tree)->chars;
//printf("int : t:%d; v=%s\r\n", tok->type, s);
return atoi(s);
}
break;
case PLUS:
//printf("plus : %d\r\n", tok->type);
return run((pANTLR3_BASE_TREE)tree->getChild(tree, 0)) + run((pANTLR3_BASE_TREE)tree->getChild(tree, 1));
case MINUS:
//printf("minus : %d\r\n", tok->type);
return run((pANTLR3_BASE_TREE)tree->getChild(tree, 0)) - run((pANTLR3_BASE_TREE)tree->getChild(tree, 1));
case TIMES:
//printf("times : %d\r\n", tok->type);
return run((pANTLR3_BASE_TREE)tree->getChild(tree, 0)) * run((pANTLR3_BASE_TREE)tree->getChild(tree, 1));
break;
default:
printf("def : %d\r\n", tok->type);
}
return 0;
}

int main(int argc, char** argv) {
char* src = "1+1\r\n""1+1\r\n";

pANTLR3_INPUT_STREAM input = antlr3StringStreamNew((pANTLR3_UINT8)src, ANTLR3_ENC_8BIT, strlen(src),(pANTLR3_UINT8)"ABCD");
pCalcLexer lex = CalcLexerNew(input);
pANTLR3_COMMON_TOKEN_STREAM tokens = antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT, TOKENSOURCE(lex));
pCalcParser parser = CalcParserNew(tokens);

CalcParser_gprog_return r = parser->gprog(parser);
pANTLR3_BASE_TREE tree = r.tree;
int v = run(tree);
printf("result: %d \r\n", v);

return 0;
}



Build

emcc -c CalcLexer.c -o CalcLexer.bc -I. -I/usr/local/Cellar/libantlr3c/3.4_1/include -DHAVE_MALLOC_H

emcc -c CalcParser.c -o CalcParser.bc -I. -I/usr/local/Cellar/libantlr3c/3.4_1/include -DHAVE_MALLOC_H
emcc -c Main.c -o Main.bc -I. -I/usr/local/Cellar/libantlr3c/3.4_1/include -DHAVE_MALLOC_H
emcc -o a.js Main.bc CalcLexer.bc CalcParser.bc libantlr3c.bc -DHAVE_MALLOC_H -O2

で、完了です!!

おわり!!


PS