Arantium Maestum

プログラミング、囲碁、読書の話題

2021-09-01から1ヶ月間の記事一覧

LispKit Lisp処理系の実装 5:SECD抽象機械の状態遷移〜リスト操作と比較・算術演算

前回の記事ではSECD抽象機械のメモリとレジスタを定義していった。今回(と次回)はSECD抽象機械が実際にどうやってプログラムを実行するかを見ていく。 SECDマシン上でプログラムを実行するということは、Controlレジスタcが指し示すコンパイルされたプログ…

LispKit Lisp処理系の実装 4:SECD抽象機械のメモリとレジスタ

前回はLispKit Lispの挙動を確認するためもあってASTを直接実行するインタプリタを実装した。これからはLispKit LispのプログラムをSECD抽象機械上で実行するために必要なものを揃えていく。 今回と次回はSECDマシン自体の実装の話。 SECDマシン SECD抽象機…

LispKit Lisp処理系の実装 3:インタプリタ〜条件分岐から関数適用まで

前回に続いてインタプリタの実装。今回はeval_の後半、IFによる条件分岐以降の項目を見ていく。 条件分岐のパターンマッチ 条件式を評価してその結果に対してパターンマッチ、分岐部分の式は片方しか評価されない。 case Cons(Alpha("IF"), Cons(e1, Cons(e2…

LispKit Lisp処理系の実装 2:インタプリタ〜変数から算術演算子まで

前回のパーサの出力するASTをそのまま実行するインタプリタを実装する。 インタプリタを実装する意義 「SECD抽象機械へのコンパイラ実装の前にインタプリタを作る」というのはHendersonのFunctional Programming Application & Implementationでの流れに従っ…

LispKit Lisp処理系の実装 1:パーサ

前回書いたとおりまずはパーサを実装する。 最近散々パーサ実装について書いてきてなんだが、Lispなのでかなりの手抜きパーサ。 字句解析 まずはトークンの定義: from dataclasses import dataclass @dataclass class Lparen: pass @dataclass class Rparen…

LispKit Lisp処理系の実装 序

純粋関数型Lisp方言であるLispKit LispのインタプリタとSECD抽象機械の機械語へのコンパイラを作成する。 タネ本はHendersonの「Functional Programming - Application & Interpretation」で、この本に載っているISWIMやPASCALのコードを大体なぞるようにPyt…

PythonでLanguage Implementation Patternsのパーサと戯れる Packratパーサのメモリ効率化

前回まで見てきたBacktrackingパーサ、Packratパーサは「これまで見たすべてのトークン」をメモリ上のバッファに保持し続け、それらに対してのパース結果もすべてキャッシュに残し続ける実装になっていて、パース終了直前にはソースファイルのすべてのトーク…

PythonでLanguage Implementation Patternsのパーサと戯れる Packratパーサのキャッシュクリア

前回のコードだとparse関数を複数回呼び出すと前回のキャッシュが残っていてパースがうまくいかない問題があった。parse呼び出しごとにキャッシュをクリアするようにしたい。 キャッシュ xparser.pyモジュールのトップレベルにキャッシュのリストとそのリス…

PythonでLanguage Implementation Patternsのパーサと戯れる Packratパーサ

前回の記事は同じパースを何回も繰り返すので非効率だと書いたが、まずはどの程度非効率なのかを計測してからPackratパーサで効率化を図る。 計測 まずは問題の程度を計測したい。 こんなデコレータを定義する: def _print_call(func): @functools.wraps(fu…

PythonでLanguage Implementation Patternsのパーサと戯れる Backtrack可能なパーサ

前回実装したBacktrackableストリームを使って「任意のトークンを消費してパースを試した上でバックトラックできる」パーサを実装していく。 言語 前回同様の言語。再掲するとこのようなもの: list : '[' elements ']'; elements : element (',' element)*;…

PythonでLanguage Implementation Patternsのパーサと戯れる Backtrackableストリームオブジェクト

前回のような固定数のトークン先読みではなく、任意のトークンを消費しつつパースし続け、失敗したらバックトラックするようなパーサを作りたい。 今回の記事はまずバックトラック機能を提供するトークンストリームのクラスを作成する。(パーサ本体は次回)…

PythonでLanguage Implementation Patternsのパーサと戯れる LL(2)再帰的パーサ

先読み要素を増やすことで前回のLL(1)パーサより複雑な構文をパースできるようになる。 言語 ここまで見てきたリスト言語に変数間の代入を追加する: list : '[' elements ']'; elements : element (',' element)*; element : NAME | assign | list; assign …

PythonでLanguage Implementation Patternsのパーサと戯れる ネストしたリストのLL(1)再帰的パーサ

前回の記事の言語とパーサを拡張して、ネストありリスト言語の再帰下降パーサを作る。 言語 文法はこの通り: list : '[' elements ']'; elements : element (',' element)*; element : NAME | list; # elementが再帰的にlistになり得る NAME : ('a'..'z' | …

PythonでLanguage Implementation Patternsのパーサと戯れる 簡単なリスト言語の非再帰的なパーサ

(注:この記事を出した当初はパースが成功した場合Trueを返す実装にしていたが、Language Implementation Patternsと同じように成功した場合は何も返さないように修正した。実装がスッキリするということと、失敗は例外で表現しているので返り値には意味が…

PythonでLanguage Implementation Patternsのパーサと戯れる lexerとPeekableストリームオブジェクト

パーサを作るにあたってまずはlexerから。 lexerと簡単なパーサで共通しているのは「消費可能なストリームの先頭を『消費することなく』先読みする必要がある」という点。lexerだと入力の文字のストリーム、パーサだとlexerの返すトークンストリーム、と対象…

PythonでLanguage Implementation Patternsのパーサと戯れる 序

前2回の記事でも書いたとおり、Language Implementation Patternsのはじめの方をPythonで実装している。 具体的にはPackrat Parsingまで、本に出てくるクラスベースな実装ではなく、lookaheadやbacktrackの機能はトークンストリームの方に持たせた上で相互…