C
Emscripten
antlr

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