🔥

Rustで作る自作言語(9) 可変参照

2023/06/15に公開

可変参照の文法

可変参照の文法は、次のようにします。

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束縛の右辺の式が参照に関する値のときは、その型を多相化しない、という制限です。この実装は次回以降やることにします。

GitHubで編集を提案

Discussion