GoConのLTに応募したので素振りさせてくださいー
書いてて思ったが素振りって野球エンジニア用語では・・・
https://www.ipa.go.jp/sec/reports/20180306.html
> brainfuckとかwhitespaceみたいな感じで改行文字のみで
> プログラミングする言語作ると生産性めっちゃあがるんじゃねーかな
みたいなことを某所に書いた。
そもそもWhitespaceのタブかスペースを"\r"に変えればよくね?
→_まずWhitespace作ってみよう
SS STSL # push 2 to stack LS # duplicate top of the stack TSSS # sum top 2 of the stack TLST # print top of the stack => 4
まずWhitespaceの本家が無くなっていた。
検索して以下のページを見つけたのでそれを参考に仕様を書き出した。
koturn.hatenablog.com/entry/2015/08/10/000000
実装はここでruby版の実装を主に参考にした。
でも大体命令リストだけで作れたのであんまり読んでないです。
github.com/hostilefork/whitespacers
あとWebarchiveで検索して本家からHaskell版のソースコードのアーカイブを取得
Whitespaceのコードは命令と引数に大別される。
ドキュメントによっては命令はIMPとCommandにさらに分割されると書いてあるが、
実装上分ける意味はあまりない。
命令は以下に大別される。
引数を取る命令に続く文字列は引数として解釈される。
引数の終わりはLFで、LFに達するまでのタブと空白がそれぞれ正負のビットとして解釈される。
つまり一つの引数はビット列である。
引数は数値型かラベルである。
空白、タブ、LFを除く全ての文字はコメントである。
パーサは簡易的なステートマシンとして実装した。
_ ST 空白とタブ文字を読み取った状態
switch文を二重に書いて、現在のステートと読み取ったバイトによって次のステートを決定する。
命令の読み取りが完了した場合、
引数がなければそのまま、引数があればLFを読み取るまで引数を読んだあと、
命令オブジェクトをリストに追加する。
type Command interface { AddBitToParam(bool) Exec(*Runtime) FinishReadParam() }
上記インターフェースを満たす構造体を命令ごとに作成した。
AddBitToParamとFinishReadParamは引数の読み取りに使う。
Execは命令ごとの処理をランタイムに対して行う。
命令の場所を覚えておくための識別子(ビット列)。
パーサ読み取り時に*ラベル=>命令オブジェクトのindex*がマップとして作成される。
ラベル定義命令(LSS)で定義する(パース時に作成されるためこの命令は何もしない)。
ジャンプ命令(LSL)でラベルにジャンプすることができる。
ランタイムは以下の構造体である。
type Runtime struct { stdin *bufio.Reader stdout io.Writer stack Stack commands Program heap map[int]int labels map[int]int callstack []int index int }
以上!
github.com/minoritea/whitespace
github.com/technohippy/go-whitespace