LoginSignup
3
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-09-27

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

実際に何か作って見る

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

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0