From 145a24c5ff98aa9bd8eb7906c208dcd8ecaaf5bb Mon Sep 17 00:00:00 2001 From: John Date: Tue, 28 Jan 2025 06:23:37 -0600 Subject: [PATCH] cl-interpret: Assignment 2.0, now with more destructuring! TODO: Destructuring `let`, destructuring patterns in function args..? --- compiler/cl-interpret/src/interpret.rs | 242 ++++++++++++++++++++----- compiler/cl-interpret/src/tests.rs | 21 ++- 2 files changed, 210 insertions(+), 53 deletions(-) diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index 8ada42c..7c66bfe 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -9,6 +9,7 @@ use std::{borrow::Borrow, rc::Rc}; use super::*; use cl_ast::*; +use cl_structures::intern::interned::Interned; /// A work-in-progress tree walk interpreter for Conlang pub trait Interpret { /// Interprets this thing in the given [`Environment`]. @@ -36,7 +37,10 @@ impl Interpret for Item { ItemKind::Struct(item) => item.interpret(env), ItemKind::Enum(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) } }; - + let frame = env .pop_frame() .expect("Environment frames must be balanced"); @@ -170,25 +174,195 @@ impl Interpret for ExprKind { } } -fn evaluate_place_expr<'e>( - env: &'e mut Environment, - expr: &ExprKind, -) -> IResult<(&'e mut Option, Sym)> { - match expr { - ExprKind::Path(Path { parts, .. }) if parts.len() == 1 => { - match parts.last().expect("parts should not be empty") { - PathPart::SuperKw => Err(Error::NotAssignable), - PathPart::SelfKw => todo!("Assignment to `self`"), - PathPart::SelfTy => todo!("What does it mean to assign to capital-S Self?"), - PathPart::Ident(s) => env.get_mut(*s).map(|v| (v, *s)), +mod assignment { + /// Pattern matching engine for assignment + use super::*; + use std::collections::HashMap; + type Namespace = HashMap>; + + pub(super) fn assign(env: &mut Environment, pat: &ExprKind, value: ConValue) -> IResult<()> { + match (pat, value) { + (ExprKind::Empty, ConValue::Empty) => Ok(()), + (ExprKind::Path(path), value) => assign_path(env, path, value), + (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(_) => { - todo!("Pattern Destructuring?") + } + + fn assign_member(env: &mut Environment, member: &Member, value: ConValue) -> IResult<()> { + *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> { + 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::>>()?; + 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> { + 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 init = tail.interpret(env)?; // Resolve the head pattern - let target = evaluate_place_expr(env, head)?; - 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) + assignment::assign(env, head, init).map(|_| ConValue::Empty) } } impl Interpret for Modify { @@ -218,10 +382,7 @@ impl Interpret for Modify { // Get the initializer and the tail let init = tail.interpret(env)?; // Resolve the head pattern - let target = evaluate_place_expr(env, head)?; - let (Some(target), _) = target else { - return Err(Error::NotInitialized(target.1)); - }; + let target = assignment::addrof(env, head)?; match op { ModifyKind::Add => target.add_assign(init), @@ -472,7 +633,7 @@ impl Interpret for Structor { }; 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 { let Self { absolute: _, parts } = self; - if parts.len() == 1 { - match parts.last().expect("parts should not be empty") { - PathPart::SuperKw | PathPart::SelfKw => todo!("Path navigation"), - PathPart::SelfTy => todo!("Path navigation to Self"), - PathPart::Ident(name) => env.get(*name), - } - } else { - todo!("Path navigation!") - } + assignment::addrof_path(env, parts) + .cloned() + .transpose() + .ok_or_else(|| Error::NotInitialized(format!("{self}").into()))? } } impl Interpret for Literal { diff --git a/compiler/cl-interpret/src/tests.rs b/compiler/cl-interpret/src/tests.rs index 94f8df1..fda15ce 100644 --- a/compiler/cl-interpret/src/tests.rs +++ b/compiler/cl-interpret/src/tests.rs @@ -437,16 +437,17 @@ mod operators { env_eq!(env.y, 10); env_eq!(env.z, 10); } - #[test] - #[should_panic] - fn assignment_accounts_for_type() { - let mut env = Default::default(); - assert_eval!(env, - let x = "a string"; - let y = 0xdeadbeef; - y = x; // should crash: type error - ); - } + // Test is disabled, since new assignment system intentionally does not care. + // #[test] + // #[should_panic] + // fn assignment_accounts_for_type() { + // let mut env = Default::default(); + // assert_eval!(env, + // let x = "a string"; + // let y = 0xdeadbeef; + // y = x; // should crash: type error + // ); + // } #[test] fn precedence() { let mut env = Default::default();