ぱたへね

はてなダイアリーはrustの色分けができないのでこっちに来た

Writing Compilers and Interpreters: その7

手書きパーサーを作成中。

このPascalコードをパースして、

BEGIN
    BEGIN {Temperature conversions.}
        five  := 3;
    END;
END.

fiveを代入先の識別子として認識するところまで来ました。

====== CROSS-REFERENCE TABLE ======

Identifier        Line numbers   
----------        ----------- 
five            003 
0 instructions generated

もうちょっと大きいPascalコードをパースさせると無限ループする。
デバッグする前に、パースした結果の構文ツリーがちゃんとできているか確認したい。

ちょっとしたことでもブログに書いていかないと、仕事が忙しくなるとモチベーションが消えそう。

Writing Compilers and Interpreters: その6

シンボルテーブルを作りました。
この本にでてくるのは、スタック型のデータ構造でシンボルを管理しています。ツリーで全部作りきった方が良さそうな気がするのですが、パースしながらネストに沿ってスタックを伸ばしたり縮めたりするようです。ネストの処理は9章でやるようです。

このソースコードに対して、シンボルテーブルが出力できるようになりました。

====== CROSS-REFERENCE TABLE ======

Identifier        Line numbers   
----------        ----------- 
abs             031 033 
epsilon         004 033 
input           001 
integer         007 
newton          001 
number          007 014 016 017 019 023 024 029 033 035 
output          001 
read            014 
real            008 
root            008 027 029 029 029 030 031 033 
sqr             033 
sqroot          008 023 024 031 031 
sqrt            023 
write           013 
writeln         012 017 020 024 025 030 
0 instructions generated

Writing Compilers and Interpreters: A Software Engineering Approach 3rd Edition その4

2章まで終了。
この本の面白いところは、まずはソースコードを入力して、トーカナイザーとパーサーとコードジェネレータを通すフレームワークを先に作る所です。

2章が終わった時点でできるのは、このようなPascalのコードを読み込んで、行番号つけて表示するだけ。

PROGRAM hello (output);

{Write 'Hello, world.' ten times.}

VAR
    i : integer;

BEGIN {hello}
    FOR i := 1 TO 10 DO BEGIN
        writeln('Hello, world.');
    END;
END {hello}.

実行結果がこう。

  1 PROGRAM hello (output);
  2 
  3 {Write 'Hello, world.' ten times.}
  4 
  5 VAR
  6     i : integer;
  7 
  8 BEGIN {hello}
  9     FOR i := 1 TO 10 DO BEGIN
 10         writeln('Hello, world.');
 11     END;
 12 END {hello}.
12 source lines
0 syntax errors
0 instructions generated

何にもしてないけど、パースが終わった時点でパーサーのサマリーを出力し、これまた何もしないコード生成が終わるとコードジェネレータのサマリーを出しています。

Writing Compilers and Interpreters: A Software Engineering Approach 3rd Edition その3

いくつかデザインパターンが出てきました。今まで本を読んでもさっぱり分からなかったのに、コキュートスでやった経験が生きていてなるほどと関心しています。こういうのは実際に苦労しないと身につかないですね。

ファクトリーパターン

起動時のオプションによってバックエンドを切り替えるとき、ファクトリーパターンが使えます。コキュートスも、C、Neon対応のC、FPGA用ソースなど、バックエンドをこれで切り替えると楽でした。次はそうしましょう。

class BackendFactory:
    def create_backend(self, operation):
        if operation == 'compile':
            return CodeGeneretor()
        elif operation == 'execute':
            return Executer()
        else:
            raise ValueError(operation)

オブザーバーパターン

コンパイラのそれぞれのパーツでMessageを全体に送受信する仕組みを共有します。Messageを受け取った側は、自分宛(もしくは自分が興味を持っている)Messageにのみ対応します。これも便利ですね。

class BackendMessageListener(MessageListener):
    def message_received(self, msg):
        type = msg.type
        body = msg.body
        if type == MessageType.INTERPRETER_SUMMARY:
            print('%d statements executed. %d runtime errors.' % (body[0], body[1]))
        elif type == MessageType.COMPILER_SUMMARY:
            print('%d instructions generated' % body)

この本には知らない事がいっぱい書いてあって勉強になります。

Writing Compilers and Interpreters: A Software Engineering Approach 3rd Edition その2

Writing Compilers and Interpretersを読んでる。
Tokenにソース上の位置情報を持たせてあって、あーなるほどーと関心した。
パーサーやスキャナーは抽象クラスを作って、それらを継承して言語固有のパーサーにするという設計が面白い。

オブザーバーパターンについても記載があり、オブジェクト指向の勉強にもなる本である。

Writing Compilers and Interpreters: A Software Engineering Approach 3rd Edition

手持ちになんちゃってでも良いので、そこそこ動くコンパイラが欲しいので、Writing Compilers and Interpreters: A Software Engineering Approach 3rd Editionを購入して勉強を始めた。

ATOMとPlanUMLでUML図を書いている。

なかなか思った通りに配置されず、ただただ図を書くのが面白かった。
ソースはこんな感じです。

https://github.com/natsutan/Limbus/blob/master/chap1/figure/frontend_class.uml