🔥
Rustで作る自作言語(9) 可変参照
可変参照の文法
可変参照の文法は、次のようにします。
let r = &3; // 参照を作る
*r; // 参照の値へのアクセスは*
r := 4; // 参照への代入は:=演算子を用いる
r += 3;
単項演算子のパーサー
まず、単項演算子のASTを定義します。
pub enum Expr {
EUnary(String, Box<Expr>),
}
そして、次のような単項演算子のパーサーを書きます。
pub fn unary(input: &str) -> IResult<&str, Expr> {
let (input, op) = opt(alt((symbol("-"), symbol("&"), symbol("*"))))(input)?;
let (input, e) = term(input)?;
match op {
Some(op) => Ok((input, Expr::EUnary(op.to_owned(), Box::new(e)))),
None => Ok((input, e)),
}
}
それに伴い、最も優先度の高い二項演算子のパーサーで、unaryを使うように変更します。
型推論
参照型を定義します。
pub enum Type {
TRef(Box<Type>),
}
TypeInfer::typeinfer_expr
に、単項演算子の場合のマッチアームを追加します。
Expr::EUnary(op, e) => {
let ty = self.typeinfer_expr(e)?;
match &op as &str {
"-" => {
unify(&Type::TInt, &ty)?;
Ok(Type::TInt)
}
"*" => {
let newtvar = self.newTVar();
unify(&Type::TRef(Box::new(newtvar.clone())), &ty)?;
Ok(newtvar)
}
"&" => {
let newtvar = self.newTVar();
unify(&newtvar, &ty)?;
Ok(Type::TRef(Box::new(newtvar)))
}
op => Err(TypeInferError::UnimplementedOperatorError(op.to_owned())),
}
}
代入演算子
代入演算子のパーサー
代入演算子:=
は、一番優先順位が低い演算子としました。
pub fn expr_op_0n(input: &str) -> IResult<&str, Expr> {
let (input, e1) = expr_op_4n(input)?;
let (input, optional) = opt(|input| {
let (input, op) = alt((tag(":="),))(input)?;
let (input, e2) = expr_op_4n(input)?;
Ok((input, (op, e2)))
})(input)?;
match optional {
Some((op, e2)) => Ok((input, e_bin_op(op, e1, e2))),
None => Ok((input, e1)),
}
}
代入演算子の型推論
":=" => {
let ty = self.newTVar();
unify(&Type::TRef(Box::new(ty.clone())), &self.typeinfer_expr(e1)?)?;
unify(&ty, &self.typeinfer_expr(e2)?)?;
Ok(Type::TUnit)
}
:=
演算子の型は、Ref[a] -> a -> ()
です。
代入演算子の評価
(Value::VRef(v1), v2) => match &op as &str {
":=" => {
*v1.borrow_mut() = v2;
Ok(Value::VUnit)
}
_ => Err(EvalError::UnimplementedOperatorError(op)),
},
現在の型推論の実装の問題点
実は、現在の型推論の実装では、型安全性が失われてしまっています。
Welcome to lunalang repl!
>>let vec = &[];
>>vec := [1]
Ok(VUnit)
>>vec := [1==1]
Ok(VUnit)
このように、vecにVec[Int]
型の値[1]
を代入した後に、Vec[Bool]
型の値[1==1]
を代入出来てしまっています。
これは、vecの型が、Ref[Vec[a]]
と多相な型になっているため、使うたびにインスタンス化されて、新しい型変数が導入されるのでこのようなことが起こっています。
これを防ぐには、値制限というものを導入します。値制限というのは、let束縛の右辺の式が参照に関する値のときは、その型を多相化しない、という制限です。この実装は次回以降やることにします。
Discussion