ANTLR4でPL/0インタプリタを作りたい！(1回目 環境構築編)


# はじめに

「毎月１つは新しい技術に触れよう！」と志した結果、1月のテーマは「ANTLR4」になりました（なんとなく）

- 多分、1年くらいかけて、PL/0 インタプリタを開発するんだと思います（完成するかな？）
    - なつかしいですね。20年前作りましたね。
    - 上手くいったら、これLLVMに繋げたいなあ……

# ANTLR4とは？

- https://github.com/antlr/antlr4

>ANTLR (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing, or translating structured text or binary files. It's widely used to build languages, tools, and frameworks. From a grammar, ANTLR generates a parser that can build parse trees and also generates a listener interface (or visitor) that makes it easy to respond to the recognition of phrases of interest.
>

（邦訳）ANTLR(ANother Tool for Language Recognition)は、強力なparser生成器です。構造化された文章やバイナリファイルに対する、読み込み、処理、実行、翻訳に使えます。言語、ツール、フレームワークに広く使われています。grammerから、ANTLRはparserを生成します、oarserがoarse treeを生成し、listener interface(もしくはvisitor)を生成します。これによって、興味のあるフレーズの認識が容易に実現できます。

# 非常にわかりやすい参考文献

- [ANTLRで数式を逆ポーランド記法に変換してみた](https://qiita.com/n_osa/items/c75fc571fb466ed9d4e9)
    - こちらを参考にしつつ、試行錯誤でやっています。

# 環境構築

https://github.com/antlr/antlr4/blob/master/doc/getting-started.md#unix

環境構築手段はこのあたりを参考に。

```bash
$ cd /usr/local/lib
$ curl -O https://www.antlr.org/download/antlr-4.9-complete.jar
```

```bash
export CLASSPATH=".:/usr/local/lib/antlr-4.9-complete.jar:$CLASSPATH"
alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.9-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
alias grun='java -Xmx500M -cp "/usr/local/lib/antlr-4.9-complete.jar:$CLASSPATH" org.antlr.v4.gui.TestRig'
```

#　Step0. 入力された数字を表示する

## grammerファイルを作る。

- grammerは、ファイル名を合わせる事（ここでは‘Cal'を使っている）
- あとでsum表示に使うつもりなのでsum

```plaintext:Cal.g4
grammar Cal;
sum : exp ;
exp : NUM ;
NUM : [0-9]+ ;
WS : [\t\r\n]+ -> skip;
```

## コンパイルする

Makefileを使ってサクッとコンパイルする。なお、コンパイルするときはjavacに全部のjavaファイルを同時に喰わせた方がいいっぽい（そうでないとundefinedなsymbolと怒られる）。

```makefile:Makefile
export CLASSPATH=".:/usr/local/lib/antlr-4.9-complete.jar:$CLASSPATH"
ANTLR4=java -Xmx500M -cp "/usr/local/lib/antlr-4.9-complete.jar:$CLASSPATH" org.antlr.v4.Tool

all: Cal.tokens
        javac *.java

Cal.tokens : Cal.g4
        $(ANTLR4) $<

clean:
        rm -rf *.class
```

## 動作確認

`grun Cal sum -gui ` で動作確認できる。なお、入力が終わったら改行して`Ctrl+D`しないと反映されない。（ここだと、123"改行""Ctrl+D"までやって、画面表示される）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89434/0972f835-d7ab-6b2a-b3e3-1f86e3032986.png)


## Step1. "1+1"に対応してみる。

grammerを見直して、"1+1"にも対応させてみよう。

expは、`NUM` だけでなく、`NUM + NUM` も食べれますよ、と。

```plaintext
grammar Cal;
sum : exp ;
exp : NUM |
      exp PLUS exp;
PLUS: '+' ;
NUM : [0-9]+ ;
WS : [\t\r\n]+ -> skip;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89434/2bbb1581-564b-c4b4-1cdd-2d991ff08ff6.png)

### 対応する関数を実装

- exitExp()は、exp要素から抜けるときに実行される関数。
- つまり、"123 + 456"の場合…３回呼ばれる。
    - 123のexpから抜ける場合
    - 456のexpから抜ける場合
    - ”123+456"のexpから抜ける場合、
- テストコードの振る舞い
    - 数値のexpから抜けるときには、Stackに数字を積む(push)
    - PLUS含むexpから抜けるときは、Stackから２つ数字を抜き(pop)、加算して数字を積む(push)
- exitSum()が呼ばれるので、Stackに残った値を表示する。

```java:CalInterfaceListener.java
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.misc.Interval;

import java.util.*;

public class CalInterfaceListener extends CalBaseListener {
    CalParser parser;

    Stack<Integer> mStack;

    public CalInterfaceListener(CalParser parser) {
        this.parser = parser;
        mStack = new Stack<Integer>();
    }

    @Override
    public void exitExp(CalParser.ExpContext ctx) {
        if( ctx.PLUS() != null ){
           int v1 = Integer.parseInt(mStack.pop().toString() );
           int v2 = Integer.parseInt(mStack.pop().toString() );
           Integer v3 = v1 + v2;
           mStack.push ( v3 );
        }
        if( ctx.NUM() != null ){
          mStack.push( Integer.parseInt( ctx.NUM().toString() ) );
        }
    }
    @Override
    public void exitSum(CalParser.SumContext ctx) {
        System.out.println( mStack.pop() );
    }
}
```

# つみのこし・・・

ここに、PL/0のgrammerがあるのではあるが… うん、"*"を使った構文のあたりのやり方が分からない！

`if( ctx.XXX() != null ){`が0個のときもTRUEになるっぽい。ここらへんは２月の宿題ということで。

https://github.com/antlr/grammars-v4/blob/master/pl0/pl0.g4

```plaintext
expression
   : ('+' | '-')? term (('+' | '-') term)*
   ;

term
   : factor (('*' | '/') factor)*
   ;

factor
   : ident
   | number
   | '(' expression ')'
   ;

ident
   : STRING
   ;

number
   : NUMBER
   ;
```