cl-interpret: Assignment 2.0, now with more destructuring!
TODO: Destructuring `let`, destructuring patterns in function args..?
This commit is contained in:
parent
485afb7843
commit
145a24c5ff
@ -9,6 +9,7 @@ use std::{borrow::Borrow, rc::Rc};
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use cl_ast::*;
|
use cl_ast::*;
|
||||||
|
use cl_structures::intern::interned::Interned;
|
||||||
/// A work-in-progress tree walk interpreter for Conlang
|
/// A work-in-progress tree walk interpreter for Conlang
|
||||||
pub trait Interpret {
|
pub trait Interpret {
|
||||||
/// Interprets this thing in the given [`Environment`].
|
/// Interprets this thing in the given [`Environment`].
|
||||||
@ -36,7 +37,10 @@ impl Interpret for Item {
|
|||||||
ItemKind::Struct(item) => item.interpret(env),
|
ItemKind::Struct(item) => item.interpret(env),
|
||||||
ItemKind::Enum(item) => item.interpret(env),
|
ItemKind::Enum(item) => item.interpret(env),
|
||||||
ItemKind::Impl(item) => item.interpret(env),
|
ItemKind::Impl(item) => item.interpret(env),
|
||||||
ItemKind::Use(_) => todo!("namespaces and imports in the interpreter"),
|
ItemKind::Use(item) => {
|
||||||
|
eprintln!("TODO: namespaces and imports in the interpreter!\n{item}\n");
|
||||||
|
Ok(ConValue::Empty)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,7 +79,7 @@ impl Interpret for Module {
|
|||||||
Ok(ConValue::Empty)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let frame = env
|
let frame = env
|
||||||
.pop_frame()
|
.pop_frame()
|
||||||
.expect("Environment frames must be balanced");
|
.expect("Environment frames must be balanced");
|
||||||
@ -170,25 +174,195 @@ impl Interpret for ExprKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_place_expr<'e>(
|
mod assignment {
|
||||||
env: &'e mut Environment,
|
/// Pattern matching engine for assignment
|
||||||
expr: &ExprKind,
|
use super::*;
|
||||||
) -> IResult<(&'e mut Option<ConValue>, Sym)> {
|
use std::collections::HashMap;
|
||||||
match expr {
|
type Namespace = HashMap<Sym, Option<ConValue>>;
|
||||||
ExprKind::Path(Path { parts, .. }) if parts.len() == 1 => {
|
|
||||||
match parts.last().expect("parts should not be empty") {
|
pub(super) fn assign(env: &mut Environment, pat: &ExprKind, value: ConValue) -> IResult<()> {
|
||||||
PathPart::SuperKw => Err(Error::NotAssignable),
|
match (pat, value) {
|
||||||
PathPart::SelfKw => todo!("Assignment to `self`"),
|
(ExprKind::Empty, ConValue::Empty) => Ok(()),
|
||||||
PathPart::SelfTy => todo!("What does it mean to assign to capital-S Self?"),
|
(ExprKind::Path(path), value) => assign_path(env, path, value),
|
||||||
PathPart::Ident(s) => env.get_mut(*s).map(|v| (v, *s)),
|
(ExprKind::Group(Group { expr }), value) => assign(env, expr, value),
|
||||||
|
(ExprKind::Tuple(tuple), value) => assign_destructure_tuple(env, tuple, value),
|
||||||
|
(ExprKind::Array(array), value) => assign_destructure_array(env, array, value),
|
||||||
|
(ExprKind::Index(index), value) => assign_index(env, index, value),
|
||||||
|
(ExprKind::Member(member), value) => assign_member(env, member, value),
|
||||||
|
(ExprKind::Structor(structor), value) => {
|
||||||
|
assign_destructure_struct(env, structor, value)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
eprintln!("{pat} is not a valid pattern expression");
|
||||||
|
Err(Error::NotAssignable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::Index(_) => todo!("Assignment to an index operation"),
|
}
|
||||||
ExprKind::Path(_) => todo!("Path expression resolution (IMPORTANT)"),
|
|
||||||
ExprKind::Empty | ExprKind::Group(_) | ExprKind::Tuple(_) => {
|
fn assign_member(env: &mut Environment, member: &Member, value: ConValue) -> IResult<()> {
|
||||||
todo!("Pattern Destructuring?")
|
*addrof_member(env, member)? = value;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_index(env: &mut Environment, index: &Index, value: ConValue) -> IResult<()> {
|
||||||
|
*addrof_index(env, index)? = value;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_path(env: &mut Environment, path: &Path, value: ConValue) -> IResult<()> {
|
||||||
|
let Ok(addr) = addrof_path(env, &path.parts) else {
|
||||||
|
eprintln!("Cannot assign {value} to path {path}");
|
||||||
|
return Err(Error::NotAssignable);
|
||||||
|
};
|
||||||
|
*addr = Some(value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_destructure_array(
|
||||||
|
env: &mut Environment,
|
||||||
|
array: &Array,
|
||||||
|
value: ConValue,
|
||||||
|
) -> IResult<()> {
|
||||||
|
let Array { values } = array;
|
||||||
|
let ConValue::Array(inits) = &value else {
|
||||||
|
eprintln!("{value} does not match pattern {array}");
|
||||||
|
return Err(Error::TypeError);
|
||||||
|
};
|
||||||
|
if values.len() != inits.len() {
|
||||||
|
return Err(Error::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (init, expr) in inits.iter().zip(values) {
|
||||||
|
assign(env, &expr.kind, init.clone())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_destructure_tuple(
|
||||||
|
env: &mut Environment,
|
||||||
|
tuple: &Tuple,
|
||||||
|
value: ConValue,
|
||||||
|
) -> IResult<()> {
|
||||||
|
let Tuple { exprs } = tuple;
|
||||||
|
let ConValue::Tuple(inits) = &value else {
|
||||||
|
eprintln!("{value} does not match pattern {tuple}");
|
||||||
|
return Err(Error::TypeError);
|
||||||
|
};
|
||||||
|
if exprs.len() != inits.len() {
|
||||||
|
return Err(Error::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (init, expr) in inits.iter().zip(exprs) {
|
||||||
|
assign(env, &expr.kind, init.clone())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_destructure_struct(
|
||||||
|
env: &mut Environment,
|
||||||
|
pat: &Structor,
|
||||||
|
value: ConValue,
|
||||||
|
) -> IResult<()> {
|
||||||
|
let Structor { to: _, init: pat } = pat;
|
||||||
|
let ConValue::Struct(parts) = value else {
|
||||||
|
return Err(Error::TypeError);
|
||||||
|
};
|
||||||
|
let (_, members) = *parts;
|
||||||
|
for Fielder { name, init: pat } in pat {
|
||||||
|
let value = members.get(name).ok_or(Error::NotDefined(*name))?;
|
||||||
|
match pat {
|
||||||
|
Some(pat) => assign(env, &pat.kind, value.clone())?,
|
||||||
|
None => *env.get_mut(*name)? = Some(value.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn addrof<'e>(
|
||||||
|
env: &'e mut Environment,
|
||||||
|
pat: &ExprKind,
|
||||||
|
) -> IResult<&'e mut ConValue> {
|
||||||
|
match pat {
|
||||||
|
ExprKind::Path(path) => addrof_path(env, &path.parts)?
|
||||||
|
.as_mut()
|
||||||
|
.ok_or(Error::NotInitialized("".into())),
|
||||||
|
ExprKind::Member(member) => addrof_member(env, member),
|
||||||
|
ExprKind::Index(index) => addrof_index(env, index),
|
||||||
|
ExprKind::Group(Group { expr }) => addrof(env, expr),
|
||||||
|
_ => Err(Error::TypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addrof_path<'e>(
|
||||||
|
env: &'e mut Environment,
|
||||||
|
path: &[PathPart],
|
||||||
|
) -> IResult<&'e mut Option<ConValue>> {
|
||||||
|
match path {
|
||||||
|
[PathPart::Ident(name)] => env.get_mut(*name),
|
||||||
|
[PathPart::Ident(name), rest @ ..] => match env.get_mut(*name)? {
|
||||||
|
Some(ConValue::Module(env)) => addrof_path_within_namespace(env, rest),
|
||||||
|
_ => Err(Error::NotIndexable),
|
||||||
|
},
|
||||||
|
_ => Err(Error::NotAssignable),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addrof_member<'e>(env: &'e mut Environment, member: &Member) -> IResult<&'e mut ConValue> {
|
||||||
|
let Member { head, kind } = member;
|
||||||
|
let ExprKind::Path(path) = head.as_ref() else {
|
||||||
|
return Err(Error::TypeError);
|
||||||
|
};
|
||||||
|
let slot = addrof_path(env, &path.parts)?
|
||||||
|
.as_mut()
|
||||||
|
.ok_or(Error::NotAssignable)?;
|
||||||
|
Ok(match (slot, kind) {
|
||||||
|
(ConValue::Struct(s), MemberKind::Struct(id)) => {
|
||||||
|
s.1.get_mut(id).ok_or(Error::NotDefined(*id))?
|
||||||
|
}
|
||||||
|
(ConValue::Tuple(t), MemberKind::Tuple(Literal::Int(id))) => t
|
||||||
|
.get_mut(*id as usize)
|
||||||
|
.ok_or_else(|| Error::NotDefined(id.to_string().into()))?,
|
||||||
|
_ => Err(Error::TypeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addrof_index<'e>(env: &'e mut Environment, index: &Index) -> IResult<&'e mut ConValue> {
|
||||||
|
let Index { head, indices } = index;
|
||||||
|
let indices = indices
|
||||||
|
.iter()
|
||||||
|
.map(|index| index.interpret(env))
|
||||||
|
.collect::<IResult<Vec<_>>>()?;
|
||||||
|
let mut head = addrof(env, head)?;
|
||||||
|
for index in indices {
|
||||||
|
head = match (head, index) {
|
||||||
|
(ConValue::Array(a), ConValue::Int(i)) => {
|
||||||
|
let a_len = a.len();
|
||||||
|
a.get_mut(i as usize)
|
||||||
|
.ok_or(Error::OobIndex(i as usize, a_len))?
|
||||||
|
}
|
||||||
|
_ => Err(Error::NotIndexable)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(head)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addrof_path_within_namespace<'e>(
|
||||||
|
env: &'e mut Namespace,
|
||||||
|
path: &[PathPart],
|
||||||
|
) -> IResult<&'e mut Option<ConValue>> {
|
||||||
|
match path {
|
||||||
|
[] => Err(Error::NotAssignable),
|
||||||
|
[PathPart::Ident(name)] => env.get_mut(name).ok_or(Error::NotDefined(*name)),
|
||||||
|
[PathPart::Ident(name), rest @ ..] => {
|
||||||
|
match env.get_mut(name).ok_or(Error::NotDefined(*name))? {
|
||||||
|
Some(ConValue::Module(env)) => addrof_path_within_namespace(env, rest),
|
||||||
|
_ => Err(Error::NotIndexable),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[PathPart::SelfKw, rest @ ..] => addrof_path_within_namespace(env, rest),
|
||||||
|
[PathPart::SelfTy, ..] => todo!("calc_address for `Self`"),
|
||||||
|
[PathPart::SuperKw, ..] => todo!("calc_address for `super`"),
|
||||||
}
|
}
|
||||||
_ => Err(Error::NotAssignable),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,17 +372,7 @@ impl Interpret for Assign {
|
|||||||
let (head, tail) = parts.borrow();
|
let (head, tail) = parts.borrow();
|
||||||
let init = tail.interpret(env)?;
|
let init = tail.interpret(env)?;
|
||||||
// Resolve the head pattern
|
// Resolve the head pattern
|
||||||
let target = evaluate_place_expr(env, head)?;
|
assignment::assign(env, head, init).map(|_| ConValue::Empty)
|
||||||
use std::mem::discriminant as variant;
|
|
||||||
// runtime typecheck
|
|
||||||
match target.0 {
|
|
||||||
Some(value) if variant(value) == variant(&init) => {
|
|
||||||
*value = init;
|
|
||||||
}
|
|
||||||
value @ None => *value = Some(init),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
}
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Interpret for Modify {
|
impl Interpret for Modify {
|
||||||
@ -218,10 +382,7 @@ impl Interpret for Modify {
|
|||||||
// Get the initializer and the tail
|
// Get the initializer and the tail
|
||||||
let init = tail.interpret(env)?;
|
let init = tail.interpret(env)?;
|
||||||
// Resolve the head pattern
|
// Resolve the head pattern
|
||||||
let target = evaluate_place_expr(env, head)?;
|
let target = assignment::addrof(env, head)?;
|
||||||
let (Some(target), _) = target else {
|
|
||||||
return Err(Error::NotInitialized(target.1));
|
|
||||||
};
|
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
ModifyKind::Add => target.add_assign(init),
|
ModifyKind::Add => target.add_assign(init),
|
||||||
@ -472,7 +633,7 @@ impl Interpret for Structor {
|
|||||||
};
|
};
|
||||||
map.insert(*name, value);
|
map.insert(*name, value);
|
||||||
}
|
}
|
||||||
Ok(ConValue::Struct(Rc::new((name, map))))
|
Ok(ConValue::Struct(Box::new((name, map))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,15 +641,10 @@ impl Interpret for Path {
|
|||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
let Self { absolute: _, parts } = self;
|
let Self { absolute: _, parts } = self;
|
||||||
|
|
||||||
if parts.len() == 1 {
|
assignment::addrof_path(env, parts)
|
||||||
match parts.last().expect("parts should not be empty") {
|
.cloned()
|
||||||
PathPart::SuperKw | PathPart::SelfKw => todo!("Path navigation"),
|
.transpose()
|
||||||
PathPart::SelfTy => todo!("Path navigation to Self"),
|
.ok_or_else(|| Error::NotInitialized(format!("{self}").into()))?
|
||||||
PathPart::Ident(name) => env.get(*name),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
todo!("Path navigation!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Interpret for Literal {
|
impl Interpret for Literal {
|
||||||
|
@ -437,16 +437,17 @@ mod operators {
|
|||||||
env_eq!(env.y, 10);
|
env_eq!(env.y, 10);
|
||||||
env_eq!(env.z, 10);
|
env_eq!(env.z, 10);
|
||||||
}
|
}
|
||||||
#[test]
|
// Test is disabled, since new assignment system intentionally does not care.
|
||||||
#[should_panic]
|
// #[test]
|
||||||
fn assignment_accounts_for_type() {
|
// #[should_panic]
|
||||||
let mut env = Default::default();
|
// fn assignment_accounts_for_type() {
|
||||||
assert_eval!(env,
|
// let mut env = Default::default();
|
||||||
let x = "a string";
|
// assert_eval!(env,
|
||||||
let y = 0xdeadbeef;
|
// let x = "a string";
|
||||||
y = x; // should crash: type error
|
// let y = 0xdeadbeef;
|
||||||
);
|
// y = x; // should crash: type error
|
||||||
}
|
// );
|
||||||
|
// }
|
||||||
#[test]
|
#[test]
|
||||||
fn precedence() {
|
fn precedence() {
|
||||||
let mut env = Default::default();
|
let mut env = Default::default();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user