超高機能電卓を作る(1日目)
手を動かして一から作ってみないとわからないことが多いので、Rustで電卓を作ることにしました。 目標は実質プログラミング言語になるくらいまで機能を与えることです。
完成するころにはすべてを忘れていそうなので、何か機能を作ったらメモ代わりに記事を書くはずです。
今日作ったもの
lexerとparser、簡単な計算機構
lexer
取得した&str
をVec<char>
に分解し、パターンマッチでトークンに分解していきます。更新可能なusize
を使うことで現在位置を更新していきます。
特に複雑なことをしていないので手書きですぐにできました。
parser
手書きが面倒だったのでLL(1)パーサを自動生成させました。使ったのはllmakerという自作パーサジェネレータです。
二項演算子は優先順位のある左結合なので、左再帰除去をしたりASTの構造を工夫したりして乗り切りました。 優先順位は
(*|/)[opsymb]*
(+|-)[opsymb]*
^[opsymb]*
(=|<|>)[opsymb]*
&[opsymb]*
|[opsymb]*
としています。
基本的な値はintとfloatとboolのみで、if文が存在しています。
今のところ衝突する構文は存在していないのでparserも簡単にできました。
計算機構
この電卓のASTは再帰型にしたので、再帰関数を使いながら展開していきます。
IntConst
, FloatConst
, BoolConst
ならその値をそのまま返し、IfThenElse
ならそれぞれの中身をさらに展開し1個目の値がBoolConst
のtrueなら2個目の値を返し、falseなら3個目の値を返します。BinApply
なら二項演算子用の計算をする関数に渡してその値を返し、Apply
なら普通の関数に適用する関数に渡してその値を返します。
二項演算子用の関数では二項演算子用の文字列でパターンマッチをして計算をさせています。例外処理はしておらず、いまのところ失敗する組み合わせではpanic!
を使っています。
普通の関数に適用する関数も同様な仕組みです。
この二つは自動生成できるようにしたいですね。
今のところ整数と小数の四則演算、整数同士の比較、整数と小数間の型変換、sin
の計算だけはできます。
次にしたいこと
型検査器を実装したいです。あとは例外処理とかprimitiveの自動生成とか。 部分適用できるようにしたいです。