📌
Rustで作る自作言語(1)
次回: https://zenn.dev/taka2/articles/22d139f741d207
Rustで自作言語を書いていきます。言語の簡単なコンセプトは次の通りです。
- 静的型付き非純粋関数型言語
- 記法を割と一般的な言語に寄せつつ好みの文法にする
- Effect systemが入ると嬉しい
現状決めていることは、以下の通りです。
- パーサーはnomを使う
- とりあえずtree walking型インタプリタにする
- なるべくドキュメントを書く
- 作業ログを残す(この記事)
リポジトリ: https://github.com/taka231/lunalang
今回の目標
四則演算が出来るようになる
3 * 3 + 4 * 4 # => 25
4 / 2 - 6 / 3 # => 0
(10 + 20) * 3 # => 90
ASTの定義
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum Expr {
EInt(i64),
EBinOp(String, Box<Expr>, Box<Expr>),
}
例
1+1 # => EBinOp("+", EInt(1), EInt(1))
後々のことも考えてスマートコンストラクタを定義しておきます。
pub fn e_int(n: i64) -> Expr {
Expr::EInt(n)
}
pub fn e_bin_op(str: &str, e1: Expr, e2: Expr) -> Expr {
Expr::EBinOp(str.to_string(), Box::new(e1), Box::new(e2))
}
パーサー
中心となるのは以下の部分です。
pub fn term(input: &str) -> IResult<&str, Expr> {
alt((expr_int, delimited(symbol("("), expr_op_6l, symbol(")"))))(input)
}
pub fn expr_op_7l(input: &str) -> IResult<&str, Expr> {
let (input, e1) = term(input)?;
let (input, e2) = many0(|input| {
let (input, op) = alt((tag("*"), tag("/")))(input)?;
let (input, ex) = term(input)?;
Ok((input, (op, ex)))
})(input)?;
Ok((
input,
e2.iter()
.fold(e1, |acc, (op, ex)| e_bin_op(&op, acc, ex.clone())),
))
}
pub fn expr_op_6l(input: &str) -> IResult<&str, Expr> {
let (input, e1) = expr_op_7l(input)?;
let (input, e2) = many0(|input| {
let (input, op) = alt((tag("+"), tag("-")))(input)?;
let (input, ex) = expr_op_7l(input)?;
Ok((input, (op, ex)))
})(input)?;
Ok((
input,
e2.iter()
.fold(e1, |acc, (op, ex)| e_bin_op(&op, acc, ex.clone())),
))
}
Evalを書く
まずは評価された結果の値を定義します。
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum Value {
VInt(i64),
}
次にeval本体を実装します。現状ではEvalは何も持っていませんが、これは後々にEvalに状態をもたせるための余地です。
pub struct Eval {}
impl Eval {
fn new() -> Eval {
Eval {}
}
fn eval(&self, ast: Expr) -> Result<Value, &str> {
match ast {
Expr::EBinOp(op, e1, e2) => {
let v1 = self.eval(*e1)?;
let v2 = self.eval(*e2)?;
match (v1, v2) {
(Value::VInt(n1), Value::VInt(n2)) => match &op as &str {
"+" => Ok(v_int(n1 + n2)),
"-" => Ok(v_int(n1 - n2)),
"*" => Ok(v_int(n1 * n2)),
"/" => Ok(v_int(n1 / n2)),
_ => Err("unimplemented operator"),
},
}
}
Expr::EInt(n) => Ok(v_int(n)),
}
}
}
REPLの実装
pub fn repl() {
println!("Welcome to lunalang repl!");
let eval = Eval::new();
loop {
let mut program = String::new();
io::stdin()
.read_line(&mut program)
.expect("Failed to read line.");
if program == ":q\n" {
break;
}
let ast = parser_expr(&program, &PRIORITY_HASHMAP);
match ast {
Ok((_, ast)) => {
let result = eval.eval_expr(ast);
println!("{:?}", result);
}
Err(err) => {
println!("{:?}", err);
}
}
}
}
Discussion