//! A work-in-progress tree walk interpreter for Conlang //! //! Currently, major parts of the interpreter are not yet implemented, and major parts will never be //! implemented in its current form. Namely, since no [ConValue] has a stable location, it's //! meaningless to get a pointer to one, and would be undefined behavior to dereference a pointer to //! one in any situation. use crate::constructor::Constructor; use super::*; use cl_ast::{ast_visitor::Visit, *}; use std::borrow::Borrow; macro trace($($t:tt)*) {{ #[cfg(debug_assertions)] if std::env::var("CONLANG_TRACE").is_ok() { eprintln!($($t)*) } }} /// A work-in-progress tree walk interpreter for Conlang pub trait Interpret { /// Interprets this thing in the given [`Environment`]. /// /// Everything returns a value!™ fn interpret(&self, env: &mut Environment) -> IResult; } impl Interpret for File { fn interpret(&self, env: &mut Environment) -> IResult { trace!("// Running {}", self.name); /// Sorts items #[derive(Debug, Default)] struct ItemSorter<'ast>(pub [Vec<&'ast Item>; 8]); impl<'ast> Visit<'ast> for ItemSorter<'ast> { fn visit_item(&mut self, i: &'ast Item) { for stage in match &i.kind { ItemKind::Module(_) => [0].as_slice(), ItemKind::Use(_) => &[1, 6], ItemKind::Enum(_) | ItemKind::Struct(_) | ItemKind::Alias(_) => &[2], ItemKind::Function(_) => &[3, 7], ItemKind::Impl(_) => &[4], ItemKind::Const(_) | ItemKind::Static(_) => &[5], } { self.0[*stage].push(i) } } } let mut items = ItemSorter::default(); items.visit_file(self); for item in items.0.into_iter().flatten() { item.interpret(env)?; } Ok(ConValue::Empty) } } impl Interpret for Item { fn interpret(&self, env: &mut Environment) -> IResult { match &self.kind { ItemKind::Alias(item) => item.interpret(env), ItemKind::Const(item) => item.interpret(env), ItemKind::Static(item) => item.interpret(env), ItemKind::Module(item) => item.interpret(env), ItemKind::Function(item) => item.interpret(env), ItemKind::Struct(item) => item.interpret(env), ItemKind::Enum(item) => item.interpret(env), ItemKind::Impl(item) => item.interpret(env), ItemKind::Use(item) => item.interpret(env), } } } impl Interpret for Alias { fn interpret(&self, _env: &mut Environment) -> IResult { trace!("// TODO: {self}"); Ok(ConValue::Empty) } } impl Interpret for Const { fn interpret(&self, env: &mut Environment) -> IResult { let Const { name, ty: _, init } = self; trace!("// Defining const {name}"); let init = init.as_ref().interpret(env)?; env.insert(*name, init); Ok(ConValue::Empty) } } impl Interpret for Static { fn interpret(&self, env: &mut Environment) -> IResult { let Static { mutable: _, name, ty: _, init } = self; trace!("// Defining static {name}"); let init = init.as_ref().interpret(env)?; env.insert(*name, init); Ok(ConValue::Empty) } } impl Interpret for Module { // TODO: Keep modules around somehow, rather than putting them on the stack fn interpret(&self, env: &mut Environment) -> IResult { let Self { name, file } = self; trace!("// Defining module {name}"); let mut scope = env.frame(name.to_ref()); let out = match file { Some(file) => file.interpret(&mut scope), None => { eprintln!("Module {name} specified, but not imported."); Ok(ConValue::Empty) } }; let frame = scope .pop_values() .expect("Environment frames must be balanced"); env.insert(*name, ConValue::Module(frame.into())); out } } impl Interpret for Function { fn interpret(&self, env: &mut Environment) -> IResult { trace!("// Defining fn {}", self.name); // register the function in the current environment env.insert_fn(self); Ok(ConValue::Empty) } } impl Interpret for Struct { fn interpret(&self, env: &mut Environment) -> IResult { let Self { name, gens: _, kind } = self; trace!("// Defining struct {name}"); let mut frame = env.frame(name.to_ref()); match kind { StructKind::Empty => { let cs = Constructor { arity: 0, name: *name }; frame.insert("call".into(), ConValue::TupleConstructor(cs)); frame.insert("__nmemb".into(), ConValue::Int(0)); } StructKind::Tuple(args) => { let cs = Constructor { arity: args.len() as _, name: *name }; frame.insert("call".into(), ConValue::TupleConstructor(cs)); frame.insert("__nmemb".into(), ConValue::Int(args.len() as _)); } StructKind::Struct(members) => { // TODO: more precise type checking of structs for (idx, StructMember { vis: _, name: nm, ty: _ }) in members.iter().enumerate() { trace!("// Defining {name}::{nm}"); frame.insert(*nm, ConValue::Int(idx as _)); } frame.insert("__nmemb".into(), ConValue::Int(members.len() as _)); } } let frame = frame .pop_values() .expect("Environment frames must be balanced"); env.insert(*name, ConValue::Module(Box::new(frame))); Ok(ConValue::Empty) } } impl Interpret for Enum { fn interpret(&self, env: &mut Environment) -> IResult { let Self { name, gens: _, variants } = self; trace!("// Defining enum {name}"); let mut scope = env.frame(name.to_ref()); for (idx, Variant { name, kind, body }) in variants.iter().enumerate() { match (kind, body) { (StructKind::Empty, None) => scope.insert(*name, ConValue::Int(idx as _)), (StructKind::Empty, Some(idx)) => { let idx = idx.interpret(&mut scope)?; scope.insert(*name, idx) } (StructKind::Tuple(args), None) => { let cs = Constructor { arity: args.len() as _, name: *name }; scope.insert("call".into(), ConValue::TupleConstructor(cs)); } (StructKind::Struct(_), None) => {} _ => eprintln!("Well-formedness error in {self}"), } } let frame = scope .pop_values() .expect("Frame stack should remain balanced."); env.insert(*name, ConValue::Module(Box::new(frame))); Ok(ConValue::Empty) } } impl Interpret for Impl { fn interpret(&self, env: &mut Environment) -> IResult { let Self { gens: _, target: ImplKind::Type(Ty { span, kind: TyKind::Path(name), .. }), body, } = self else { trace!("TODO: impl X for Ty"); return Ok(ConValue::Empty); }; let mut frame = env.frame("impl"); body.interpret(&mut frame)?; let frame = frame .pop_values() .expect("Environment frames must be balanced"); match assignment::addrof_path(env, name.parts.as_slice()) .map_err(|err| err.with_span(*span))? { ConValue::Module(m) => m.extend(frame), other => eprintln!("TODO: impl for {other}"), } Ok(ConValue::Empty) } } impl Interpret for Use { fn interpret(&self, env: &mut Environment) -> IResult { let Self { absolute: _, tree } = self; tree.interpret(env) } } impl Interpret for UseTree { fn interpret(&self, env: &mut Environment) -> IResult { // TODO: raw-bind use items type Bindings = HashMap; use std::collections::HashMap; fn get_bindings( tree: &UseTree, env: &mut Environment, bindings: &mut Bindings, ) -> IResult<()> { match tree { UseTree::Tree(use_trees) => { for tree in use_trees { get_bindings(tree, env, bindings)?; } } UseTree::Path(PathPart::Ident(name), tree) => { let Ok(ConValue::Module(m)) = env.get(*name) else { Err(Error::TypeError())? }; let mut scope = env.with_frame(name.to_ref(), *m); let out = get_bindings(tree, &mut scope, bindings); return out; } UseTree::Alias(name, alias) => { bindings.insert(*alias, env.get(*name)?); } UseTree::Name(name) => { bindings.insert(*name, env.get(*name)?); } UseTree::Glob => { trace!("TODO: Improve glob imports"); if let Some((frame, name)) = env.pop_frame() { for (k, v) in &frame { bindings.insert(*k, v.clone()); } env.push_frame(name, frame); } } other => { eprintln!("ERROR: Cannot use {other}"); } } Ok(()) } let mut bindings = Bindings::new(); get_bindings(self, env, &mut bindings)?; for (name, value) in bindings { env.insert(name, value); } Ok(ConValue::Empty) } } impl Interpret for Stmt { fn interpret(&self, env: &mut Environment) -> IResult { let Self { span, kind, semi } = self; let out = match kind { StmtKind::Empty => Ok(ConValue::Empty), StmtKind::Item(stmt) => stmt.interpret(env), StmtKind::Expr(stmt) => stmt.interpret(env), } .map_err(|err| err.with_span(*span))?; Ok(match semi { Semi::Terminated => ConValue::Empty, Semi::Unterminated => out, }) } } impl Interpret for Expr { #[inline] fn interpret(&self, env: &mut Environment) -> IResult { let Self { span, kind } = self; kind.interpret(env).map_err(|err| err.with_span(*span)) } } impl Interpret for ExprKind { fn interpret(&self, env: &mut Environment) -> IResult { match self { ExprKind::Empty => Ok(ConValue::Empty), ExprKind::Closure(v) => v.interpret(env), ExprKind::Quote(q) => q.interpret(env), ExprKind::Let(v) => v.interpret(env), ExprKind::Match(v) => v.interpret(env), ExprKind::Assign(v) => v.interpret(env), ExprKind::Modify(v) => v.interpret(env), ExprKind::Binary(v) => v.interpret(env), ExprKind::Unary(v) => v.interpret(env), ExprKind::Cast(v) => v.interpret(env), ExprKind::Member(v) => v.interpret(env), ExprKind::Index(v) => v.interpret(env), ExprKind::Structor(v) => v.interpret(env), ExprKind::Path(v) => v.interpret(env), ExprKind::Literal(v) => v.interpret(env), ExprKind::Array(v) => v.interpret(env), ExprKind::ArrayRep(v) => v.interpret(env), ExprKind::AddrOf(v) => v.interpret(env), ExprKind::Block(v) => v.interpret(env), ExprKind::Group(v) => v.interpret(env), ExprKind::Tuple(v) => v.interpret(env), ExprKind::While(v) => v.interpret(env), ExprKind::If(v) => v.interpret(env), ExprKind::For(v) => v.interpret(env), ExprKind::Break(v) => v.interpret(env), ExprKind::Return(v) => v.interpret(env), ExprKind::Continue => Err(Error::Continue()), } } } impl Interpret for Closure { fn interpret(&self, env: &mut Environment) -> IResult { Ok(ConValue::Closure( crate::closure::Closure::new(env, self).into(), )) } } impl Interpret for Quote { fn interpret(&self, _env: &mut Environment) -> IResult { // TODO: squoosh down into a ConValue? Ok(ConValue::Quote(self.quote.as_ref().clone().into())) } } impl Interpret for Let { fn interpret(&self, env: &mut Environment) -> IResult { let Let { mutable: _, name, ty: _, init } = self; match init.as_ref().map(|i| i.interpret(env)).transpose()? { Some(value) => { match pattern::substitution(env, name, value) { Ok(sub) => { for (name, value) in sub { env.insert(name, value); } return Ok(ConValue::Bool(true)); } Err(_e) => trace!("{_e}"), }; } None => { for name in pattern::variables(name) { env.insert(*name, ConValue::Empty); } } } Ok(ConValue::Bool(false)) } } impl Interpret for Match { fn interpret(&self, env: &mut Environment) -> IResult { let Self { scrutinee, arms } = self; let scrutinee = scrutinee.interpret(env)?; for MatchArm(pat, expr) in arms { if let Ok(substitution) = pattern::substitution(env, pat, scrutinee.clone()) { let mut env = env.frame("match"); for (name, value) in substitution { env.insert(name, value); } return expr.interpret(&mut env); } } Err(Error::MatchNonexhaustive()) } } pub(crate) mod assignment { /// Pattern matching engine for assignment use super::*; use std::collections::HashMap; type Namespace = HashMap; pub(super) fn pat_assign(env: &mut Environment, pat: &Pattern, value: ConValue) -> IResult<()> { for (name, value) in pattern::substitution(env, pat, value) .map_err(|_| Error::PatFailed(pat.clone().into()))? { match env.get_mut(name)? { &mut ConValue::Ref(id) => { *(env.get_id_mut(id).ok_or(Error::StackOverflow(id))?) = value; } other => *other = value, } } Ok(()) } pub(super) fn assign(env: &mut Environment, pat: &Expr, value: ConValue) -> IResult<()> { if let Ok(pat) = Pattern::try_from(pat.clone()) { return pat_assign(env, &pat, value); } match &pat.kind { ExprKind::Member(member) => *addrof_member(env, member)? = value, ExprKind::Index(index) => *addrof_index(env, index)? = value, ExprKind::Path(path) => *addrof_path(env, &path.parts)? = value, ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match addrof(env, tail)? { &mut ConValue::Ref(r) => *env.get_id_mut(r).ok_or(Error::StackOverflow(r))? = value, _ => Err(Error::NotAssignable())?, }, _ => Err(Error::NotAssignable())?, } Ok(()) } pub(super) fn addrof<'e>(env: &'e mut Environment, pat: &Expr) -> IResult<&'e mut ConValue> { match &pat.kind { ExprKind::Path(path) => addrof_path(env, &path.parts), ExprKind::Member(member) => addrof_member(env, member), ExprKind::Index(index) => addrof_index(env, index), ExprKind::Group(Group { expr }) => addrof(env, expr), ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match *addrof(env, tail)? { ConValue::Ref(place) => env.get_id_mut(place).ok_or(Error::NotIndexable()), _ => Err(Error::TypeError()), }, _ => Err(Error::TypeError()), } } pub fn addrof_path<'e>( env: &'e mut Environment, path: &[PathPart], ) -> IResult<&'e mut ConValue> { match path { [PathPart::Ident(name)] => env.get_mut(*name), [PathPart::Ident(name), rest @ ..] => match env.get_mut(*name)? { ConValue::Module(env) => project_path_in_namespace(env, rest), _ => Err(Error::NotIndexable()), }, _ => Err(Error::NotAssignable()), } } pub fn addrof_member<'e>( env: &'e mut Environment, member: &Member, ) -> IResult<&'e mut ConValue> { let Member { head, kind } = member; let head = addrof(env, head)?; project_memberkind(head, kind) } 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)?; // match head { // ConValue::Slice(id, len) | ConValue::Array(id, len) => { // } // } for index in indices { head = project_index(head, &index)?; } Ok(head) } /// Performs member-access "projection" from a ConValue to a particular element pub fn project_memberkind<'v>( value: &'v mut ConValue, kind: &MemberKind, ) -> IResult<&'v mut ConValue> { match (value, kind) { (ConValue::Struct(_name, s), MemberKind::Struct(id)) => { s.get_mut(id).ok_or(Error::NotDefined(*id)) } (ConValue::TupleStruct(_name, s), MemberKind::Tuple(Literal::Int(id))) => { let len = s.len(); s.get_mut(*id as usize) .ok_or(Error::OobIndex(*id as _, len)) } (ConValue::Tuple(t), MemberKind::Tuple(Literal::Int(id))) => { let len = t.len(); t.get_mut(*id as usize) .ok_or(Error::OobIndex(*id as _, len)) } _ => Err(Error::TypeError()), } } /// Performs index "projection" from a ConValue to a particular element pub fn project_index<'v>( _value: &'v mut ConValue, _index: &ConValue, ) -> IResult<&'v mut ConValue> { Err(Error::NotIndexable()) } pub fn project_path_in_namespace<'e>( env: &'e mut Namespace, path: &[PathPart], ) -> IResult<&'e mut 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))? { ConValue::Module(env) => project_path_in_namespace(env, rest), _ => Err(Error::NotIndexable()), } } [PathPart::SelfTy, ..] => todo!("calc_address for `Self`"), [PathPart::SuperKw, ..] => todo!("calc_address for `super`"), } } } impl Interpret for Assign { fn interpret(&self, env: &mut Environment) -> IResult { let Assign { parts } = self; let (head, tail) = parts.borrow(); let init = tail.interpret(env)?; // Resolve the head pattern assignment::assign(env, head, init).map(|_| ConValue::Empty) } } impl Interpret for Modify { fn interpret(&self, env: &mut Environment) -> IResult { let Modify { kind: op, parts } = self; let (head, tail) = parts.borrow(); // Get the initializer and the tail let init = tail.interpret(env)?; // Resolve the head pattern let target = assignment::addrof(env, head)?; match op { ModifyKind::Add => target.add_assign(init), ModifyKind::Sub => target.sub_assign(init), ModifyKind::Mul => target.mul_assign(init), ModifyKind::Div => target.div_assign(init), ModifyKind::Rem => target.rem_assign(init), ModifyKind::And => target.bitand_assign(init), ModifyKind::Or => target.bitor_assign(init), ModifyKind::Xor => target.bitxor_assign(init), ModifyKind::Shl => target.shl_assign(init), ModifyKind::Shr => target.shr_assign(init), }?; Ok(ConValue::Empty) } } impl Interpret for Binary { fn interpret(&self, env: &mut Environment) -> IResult { let Binary { kind, parts } = self; let (head, tail) = parts.borrow(); let head = head.interpret(env)?; match kind { BinaryKind::LogAnd => { return if head.truthy()? { tail.interpret(env) } else { Ok(head) }; // Short circuiting } BinaryKind::LogOr => { return if !head.truthy()? { tail.interpret(env) } else { Ok(head) }; // Short circuiting } BinaryKind::LogXor => { return Ok(ConValue::Bool( head.truthy()? ^ tail.interpret(env)?.truthy()?, )); } _ => {} } let tail = tail.interpret(env)?; #[allow(unused)] let operator = match kind { BinaryKind::Lt => "lt", BinaryKind::LtEq => "lteq", BinaryKind::Equal => "eq", BinaryKind::NotEq => "neq", BinaryKind::GtEq => "gteq", BinaryKind::Gt => "gt", BinaryKind::RangeExc => "range_exc", BinaryKind::RangeInc => "range_inc", BinaryKind::LogAnd => "log_and", BinaryKind::LogOr => "log_or", BinaryKind::LogXor => "log_xor", BinaryKind::BitAnd => "bit_and", BinaryKind::BitOr => "bit_or", BinaryKind::BitXor => "bit_xor", BinaryKind::Shl => "shl", BinaryKind::Shr => "shr", BinaryKind::Add => "add", BinaryKind::Sub => "sub", BinaryKind::Mul => "mul", BinaryKind::Div => "div", BinaryKind::Rem => "rem", BinaryKind::Call => "call", }; if let ConValue::Struct(name, _) | ConValue::TupleStruct(name, _) = head && let Ok(ConValue::Module(m)) = env.get_mut(name) && let Some(f) = m.get(&operator.into()) { return f.clone().call(env, &[head, tail]); } if let Ok(ConValue::Module(m)) = env.get_mut(head.typename().into()) && let Some(f) = m.get(&operator.into()) { return f.clone().call(env, &[head, tail]); } match kind { BinaryKind::Lt => head.lt(&tail), BinaryKind::LtEq => head.lt_eq(&tail), BinaryKind::Equal => head.eq(&tail), BinaryKind::NotEq => head.neq(&tail), BinaryKind::GtEq => head.gt_eq(&tail), BinaryKind::Gt => head.gt(&tail), BinaryKind::RangeExc => env.call("RangeExc".into(), &[head, tail]), BinaryKind::RangeInc => env.call("RangeInc".into(), &[head, tail]), BinaryKind::BitAnd => head & tail, BinaryKind::BitOr => head | tail, BinaryKind::BitXor => head ^ tail, BinaryKind::Shl => head << tail, BinaryKind::Shr => head >> tail, BinaryKind::Add => head + tail, BinaryKind::Sub => head - tail, BinaryKind::Mul => head * tail, BinaryKind::Div => head / tail, BinaryKind::Rem => head % tail, BinaryKind::Call => match tail { ConValue::Empty => head.call(env, &[]), ConValue::Tuple(args) => head.call(env, &args), _ => Err(Error::TypeError()), }, _ => Ok(head), } } } impl Interpret for Unary { fn interpret(&self, env: &mut Environment) -> IResult { let Unary { kind, tail } = self; if *kind == UnaryKind::Loop { loop { match tail.interpret(env) { Err(Error { kind: ErrorKind::Break(value), .. }) => return Ok(value), Err(Error { kind: ErrorKind::Continue, .. }) => continue, e => e?, }; } } let operator = match kind { UnaryKind::Loop => unreachable!(), UnaryKind::Deref => "deref", UnaryKind::Neg => "neg", UnaryKind::Not => "not", UnaryKind::RangeExc => "RangeTo", UnaryKind::RangeInc => "RangeToInc", UnaryKind::At => "at", UnaryKind::Tilde => "tilde", }; let operand = tail.interpret(env)?; if let ConValue::Struct(name, _) | ConValue::TupleStruct(name, _) = operand && let Ok(ConValue::Module(m)) = env.get_mut(name) && let Some(f) = m.get(&operator.into()) { return f.clone().call(env, &[operand]); } if let Ok(ConValue::Module(m)) = env.get_mut(operand.typename().into()) && let Some(f) = m.get(&operator.into()) { return f.clone().call(env, &[operand]); } env.call(operator.into(), &[operand]) } } fn cast(env: &Environment, value: ConValue, ty: Sym) -> IResult { let value = match (value, ty.to_ref()) { (value, "str") => return Ok(ConValue::Str(format!("{value}").into())), (value, "String") => return Ok(ConValue::String(format!("{value}"))), (ConValue::Empty, _) => 0, (ConValue::Int(i), _) => i as _, (ConValue::Bool(b), _) => b as _, (ConValue::Char(c), _) => c as _, (ConValue::Ref(v), _) => { return cast( env, env.get_id(v).cloned().ok_or(Error::StackUnderflow())?, ty, ); } (ConValue::Float(f), "f32" | "f64") => return Ok(ConValue::Float(f)), (ConValue::Float(f), _) => f as _, _ => Err(Error::TypeError())?, }; Ok(match &*ty { "u8" => ConValue::Int(value as u8 as _), "i8" => ConValue::Int(value as i8 as _), "u16" => ConValue::Int(value as u16 as _), "i16" => ConValue::Int(value as i16 as _), "u32" => ConValue::Int(value as u32 as _), "i32" => ConValue::Int(value as i32 as _), "u64" => ConValue::Int(value), "i64" => ConValue::Int(value), "f32" => ConValue::Float(value as f32 as _), "f64" => ConValue::Float(value as f64 as _), "char" => ConValue::Char(char::from_u32(value as _).unwrap_or('\u{fffd}')), "bool" => ConValue::Bool(value != 0), _ => Err(Error::NotDefined(ty))?, }) } impl Interpret for Cast { fn interpret(&self, env: &mut Environment) -> IResult { let Cast { head, ty } = self; let value = head.interpret(env)?; if let TyKind::Tuple(TyTuple { types }) = &ty.kind && types.as_slice().is_empty() { return Ok(ConValue::Empty); } let TyKind::Path(Path { absolute: false, parts }) = &ty.kind else { Err(Error::TypeError())? }; match parts.as_slice() { [PathPart::Ident(ty)] => cast(env, value, *ty), _ => Err(Error::TypeError()), } } } impl Interpret for Member { fn interpret(&self, env: &mut Environment) -> IResult { let Member { head, kind } = self; // Attempt member access projection (fast path) if let Ok(member) = assignment::addrof_member(env, self) { return Ok(member.clone()); } let mut values = vec![]; // Evaluate if this can be Self'd let addr = match (&head.kind, kind) { (ExprKind::Path(p), MemberKind::Call(..)) => { p.as_sym() .and_then(|name| Some(ConValue::Ref(env.id_of(name).ok()?))) // "borrow" it } _ => None, }; let mut value = head.interpret(env)?; // Perform alternate member access match (&value, &kind) { (ConValue::Struct(_name, memb), MemberKind::Call(name, args)) if memb.contains_key(name) => { let f = memb.get(name).cloned().expect("function exists"); values.push(addr.unwrap_or(value)); for arg in &args.exprs { values.push(arg.interpret(env)?); } f.call(env, &values) } (_, MemberKind::Call(name, args)) => { values.push(addr.unwrap_or(value)); for arg in &args.exprs { values.push(arg.interpret(env)?); } env.call(*name, &values) } (_, kind) => assignment::project_memberkind(&mut value, kind).cloned(), } } } impl Interpret for Index { fn interpret(&self, env: &mut Environment) -> IResult { let Self { head, indices } = self; let mut head = head.interpret(env)?; for index in indices { head = head.index(&index.interpret(env)?, env)?; } Ok(head) } } impl Interpret for Structor { fn interpret(&self, env: &mut Environment) -> IResult { let Self { to: Path { absolute: _, parts }, init } = self; use std::collections::HashMap; // Look up struct/enum-struct definition // use that definition to place the struct parts let name = match parts.last() { Some(PathPart::Ident(name)) => *name, Some(PathPart::SelfTy) => "Self".into(), Some(PathPart::SuperKw) => "super".into(), None => "".into(), }; let mut map = HashMap::new(); for Fielder { name, init } in init { let value = match init { Some(init) => init.interpret(env)?, None => env.get(*name)?, }; map.insert(*name, value); } Ok(ConValue::Struct(name, Box::new(map))) } } impl Interpret for Path { fn interpret(&self, env: &mut Environment) -> IResult { let Self { absolute: _, parts } = self; assignment::addrof_path(env, parts).cloned() } } impl Interpret for Literal { fn interpret(&self, _env: &mut Environment) -> IResult { Ok(match self { Literal::String(value) => ConValue::from(value.as_str()), Literal::Char(value) => ConValue::Char(*value), Literal::Bool(value) => ConValue::Bool(*value), Literal::Float(value) => ConValue::Float(f64::from_bits(*value)), Literal::Int(value) => ConValue::Int(*value as _), }) } } impl Interpret for Array { fn interpret(&self, env: &mut Environment) -> IResult { let Self { values } = self; let mut out = vec![]; for expr in values { out.push(expr.interpret(env)?) } Ok(ConValue::Array(out.into())) } } impl Interpret for ArrayRep { fn interpret(&self, env: &mut Environment) -> IResult { let Self { value, repeat } = self; let value = value.interpret(env)?; let ConValue::Int(repeat) = repeat.interpret(env)? else { Err(Error::TypeError())? }; if repeat < 0 { // TODO: a special error just for you Err(Error::NotIndexable())? } Ok(ConValue::Array(vec![value; repeat as usize].into())) } } impl Interpret for AddrOf { fn interpret(&self, env: &mut Environment) -> IResult { let Self { mutable: _, expr } = self; match &expr.kind { ExprKind::Index(_) => todo!("AddrOf array index"), ExprKind::Path(Path { parts, .. }) => match parts.as_slice() { [PathPart::Ident(name)] => Ok(ConValue::Ref(env.id_of(*name)?)), _ => todo!("Path traversal in AddrOf(\"{self}\")"), }, _ => { let value = expr.interpret(env)?; let temp = env.stack_alloc(value)?; Ok(ConValue::Ref(temp)) } } } } impl Interpret for Block { fn interpret(&self, env: &mut Environment) -> IResult { let Self { stmts } = self; let mut env = env.frame("block"); let mut out = ConValue::Empty; for stmt in stmts { out = stmt.interpret(&mut env)?; } Ok(out) } } impl Interpret for Group { fn interpret(&self, env: &mut Environment) -> IResult { let Self { expr } = self; expr.interpret(env) } } impl Interpret for Tuple { fn interpret(&self, env: &mut Environment) -> IResult { let Self { exprs } = self; Ok(ConValue::Tuple( exprs .iter() .try_fold(vec![], |mut out, element| { out.push(element.interpret(env)?); Ok(out) })? .into(), )) } } impl Interpret for While { fn interpret(&self, env: &mut Environment) -> IResult { let Self { cond, pass, fail } = self; loop { if cond.interpret(env)?.truthy()? { match pass.interpret(env) { Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value), Err(Error { kind: ErrorKind::Continue, .. }) => continue, e => e?, }; } else { break fail.interpret(env); } } } } impl Interpret for If { fn interpret(&self, env: &mut Environment) -> IResult { let Self { cond, pass, fail } = self; if cond.interpret(env)?.truthy()? { pass.interpret(env) } else { fail.interpret(env) } } } impl Interpret for For { fn interpret(&self, env: &mut Environment) -> IResult { let Self { bind, cond, pass, fail } = self; let cond = cond.interpret(env)?; // TODO: A better iterator model let mut bounds: Box> = match &cond { ConValue::TupleStruct(name, values) => match name.to_ref() { "RangeExc" => match values.as_ref().as_ref() { &[ConValue::Int(from), ConValue::Int(to)] => { Box::new((from..to).map(ConValue::Int)) } _ => Err(Error::NotIterable())?, }, "RangeInc" => match values.as_ref().as_ref() { &[ConValue::Int(from), ConValue::Int(to)] => { Box::new((from..=to).map(ConValue::Int)) } _ => Err(Error::NotIterable())?, }, _ => Err(Error::NotIterable())?, }, ConValue::Array(a) => Box::new(a.iter().cloned()), &ConValue::Slice(head, len) => Box::new((head..head + len).map(ConValue::Ref)), // In the production compiler this may be fully unrolled ConValue::Tuple(t) => Box::new(t.iter().cloned()), ConValue::Str(s) => Box::new(s.chars().map(ConValue::Char)), _ => Err(Error::TypeError())?, }; loop { let mut env = env.frame("loop variable"); if let Some(value) = bounds.next() { for (name, value) in pattern::substitution(&env, bind, value)? { env.insert(name, value); } match pass.interpret(&mut env) { Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value), Err(Error { kind: ErrorKind::Continue, .. }) => continue, e => e?, }; } else { break fail.interpret(&mut env); } } } } impl Interpret for Else { fn interpret(&self, env: &mut Environment) -> IResult { let Self { body } = self; match body { Some(body) => body.interpret(env), None => Ok(ConValue::Empty), } } } impl Interpret for Return { fn interpret(&self, env: &mut Environment) -> IResult { let Self { body } = self; Err(Error::Return( body.as_ref() .map(|body| body.interpret(env)) .unwrap_or(Ok(ConValue::Empty))?, )) } } impl Interpret for Break { fn interpret(&self, env: &mut Environment) -> IResult { let Self { body } = self; Err(Error::Break( body.as_ref() .map(|body| body.interpret(env)) .unwrap_or(Ok(ConValue::Empty))?, )) } }