0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ANTLR4でPL/0インタプリタを作りたい!(2回目 苦悩編 ~え?LLVM Builder禁止縛りですか?~)

Posted at

前回までのあらすじ

今回のオチ

  • antlr4とllvm builderが共存……できない……
    • llvm-builder「-fno-exceptions指定でお願いします」
    • antlr4「-fexceptionsをお願いしますー」
    • 板挟みになったプログラマ「どうしろと…」
  • PL/0構文厄介すぎる
    • 変数のスコープがぁ……
  • よし、PL/0フルセットサポートをあきらめよう!

1. pl0のgrammerを使おう

こちらに、BSD licenseのpl0 grammerがある。こちらをベースに検討していきたい。

2. c++開発環境への移行

さて、LLVM でコンパイルできる LLVM IRを生成するためには、LLVM Builderを使うのが一番普通そう。

LLVM APIを使ってみよう! 〜 Brainf**kコンパイラをIRBuilderで書き直してみた 〜

LLVM Builderを使える開発言語は、https://github.com/llvm/llvm-project/tree/main/llvm/bindings を見る限りだとこのあたりが選択肢になる。

  • C++
  • go
  • python
  • ocaml

よし、C++で作るか! (これが後々の悲劇を招くことを今の我々は知る由もなかったのだ…)

2.1 C言語用のコードを生成

前回では、antlr4を使ったjavaのコードを生成した。オプションを変更する事で、C++用のコードも生成できる。

$ antlr4 -Dlanguage=Cpp MyGrammar.g4

これを試す場合には、http://www.antlr.org から cpp 用のruntimeもダウンロードし、インストールまでする必要がある。

今回はこのようなフォルダ構成でテストをしている。

- pl0.g4
- main.cpp ( listenerの実装もひとまずここ)
- antlr4-runtime (pl0.g4から生成したファイルを置く)
- testdata

この環境ために、Makefileを雑に書いた。cmake使った方がいいかなーと思いつつ、まあ動けばいい!!

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

# CXX=clang++
CXX=g++

all : a.out

.PHONY: antlr4/pl0.tokens
antlr4/pl0.tokens : pl0.g4
        $(ANTLR4) -Dlanguage=Cpp pl0.g4 -o antlr4-runtime

OBJS = \
    main.o \
    antlr4-runtime/pl0BaseListener.o \
    antlr4-runtime/pl0Lexer.o \
    antlr4-runtime/pl0Listener.o \
    antlr4-runtime/pl0Parser.o \

.SUFFIXES: .cpp .o
.cpp.o:
        $(CXX) \
                -pg -g \
                -c \
                "-I/usr/local/include/antlr4-runtime/" \
                $< \
                -o $@

a.out : ${OBJS}
        $(CXX) \
                -pg -g \
                -I/usr/local/include/antlr4-runtime/ \
                ${OBJS} \
                /usr/local/lib/libantlr4-runtime.a \
                -o a.out

clean :
        rm -f ${OBJS}
        rm -f a.out

3. それではLLVM Builderとつなご… つながらない!

3.1 LLVM Builderとantlr4が仲良くしてくれない

こちらの記事を参考にしてくっつけてみよう!と思った。

とりあえず、makefileを修正して、llvm-config指定してコンパイルですね。

.SUFFIXES: .cpp .o
.cpp.o:
        $(CXX) \
                -pg -g \
                -c \
                `llvm-config --cxxflags --ldflags --libs --system-libs` \
                "-I/usr/local/include/antlr4-runtime/" \
                $< \
                -o $@

ところが、"exception handling disabled" と怒られた。おや?

g++ \
        -pg -g \
        -c \
        `llvm-config --cxxflags --ldflags --libs --system-libs` \
                "-I/usr/local/include/antlr4-runtime/" \
        main.cpp \
        -o main.o
In file included from /usr/local/include/antlr4-runtime/Lexer.h:8,
                 from /usr/local/include/antlr4-runtime/antlr4-runtime.h:31,
                 from main.cpp:5:
/usr/local/include/antlr4-runtime/Recognizer.h: 
In member function ‘virtual const std::vector<short unsigned int> antlr4::Recognizer::getSerializedATN() const ’:
/usr/local/include/antlr4-runtime/Recognizer.h:69:13: 
error: exception handling disabled, 
use ‘-fexceptions’ to enable
   69 |       throw "there is no serialized ATN";
      |

llvm-configさん、やはりあなたでしたか……

$ llvm-config --cxxflags --ldflags --libs --system-libs
-I/usr/lib/llvm-11/include -std=c++14   -fno-exceptions -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
-L/usr/lib/llvm-11/lib
-lLLVM-11

ぐぬぅ・・・そうしたら、llvm-configの後ろにつけたらどうだ!

g++ \
        -pg -g \
        -c \
        `llvm-config --cxxflags --ldflags --libs --system-libs` \
        -fexceptions \
                "-I/usr/local/include/antlr4-runtime/" \
        main.cpp \
        -o main.o
g++ \
        -pg -g \
        -I/usr/local/include/antlr4-runtime/ \
        main.o antlr4-runtime/pl0BaseListener.o antlr4-runtime/pl0Lexer.o antlr4-runtime/pl0Listener.o antlr4-runtime/pl0Parser.o  \
        /usr/local/lib/libantlr4-runtime.a \
        -o a.out
/usr/bin/ld: main.o:(.data.rel+0x0): undefined reference to `llvm::DisableABIBreakingChecks'
collect2: error: ld returned 1 exit status
make: *** [Makefile:32: a.out] エラー 1

終わった…… antlr4とLLVM Builderを使ってPL/0コンパイラを作ろうはここで終了……

3.2 ということで

パンが無ければ パンツ ケーキ を食べればいいじゃない!の精神で、LLVM IRを手書きする方針に変更します。

ということで、2月はここまで。うん、なかなかのスローペースだな。

4. 現代らしからぬ制約が必要になりそう

PL/0構文のためのLLVM IRを手書きするプログラムを作り始めたわけだが、なかなかに「いやらしい」。

4.1. 関数内関数内関数内関数内関数内関数内...

PL/0の構文としては、以下のように、block内でProcedure内を定義できる、が、更にProcedure内でblockも定義できる。

program = block "." .

block = [ "const" ident "=" number {"," ident "=" number} ";"]
        [ "var" ident {"," ident} ";"]
        { "procedure" ident ";" block ";" } statement .

つまり、関数内に関数が置ける、更にその下にも関数が置ける……

VAR x;

PROCEDURE fa ;
  VAR x;

  PROCEDURE fb ;
    VAR x;
  ;
;
.

image.png

この場合、PROCEDURE fb内でxというidentが使われたら、スコープとして一番内側にあるfb::x を意味すると解釈しなければならない。

ということで、今回は・・・

ある程度テストプログラム側に制約を持たせて逃げます!

  • programで定義された変数は、globalとして扱う。
  • procedureで定義された変数も、globalとして扱う。
  • それぞれの変数のスコープについては、とりあえず考えずに作る。
  • 「一つ上の変数を参照する」みたいなことは非サポートとする。
  • 変数の二重定義については、とりあえず考えずに作る。

とりあえず、ここまで……

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?