From 7a8da33de9ccebe92e6f15ed38ada9334d9ecf16 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 22 Feb 2025 03:31:27 -0600 Subject: [PATCH] cl-interpret: Tuple structs + fix tuple member access --- compiler/cl-ast/src/ast_impl.rs | 8 ++ compiler/cl-interpret/src/convalue.rs | 19 +++- compiler/cl-interpret/src/function.rs | 9 +- compiler/cl-interpret/src/interpret.rs | 151 ++++++++++++++++++------- 4 files changed, 147 insertions(+), 40 deletions(-) diff --git a/compiler/cl-ast/src/ast_impl.rs b/compiler/cl-ast/src/ast_impl.rs index 0e29f88..80b02ab 100644 --- a/compiler/cl-ast/src/ast_impl.rs +++ b/compiler/cl-ast/src/ast_impl.rs @@ -940,6 +940,14 @@ mod path { } } + /// Checks whether this path ends in the given [Sym] + pub fn ends_with(&self, name: &Sym) -> bool { + match self.parts.as_slice() { + [.., PathPart::Ident(last)] => name == last, + _ => false, + } + } + /// Checks whether this path refers to the sinkhole identifier, `_` pub fn is_sinkhole(&self) -> bool { if let [PathPart::Ident(id)] = self.parts.as_slice() { diff --git a/compiler/cl-interpret/src/convalue.rs b/compiler/cl-interpret/src/convalue.rs index cdb033b..a97b6df 100644 --- a/compiler/cl-interpret/src/convalue.rs +++ b/compiler/cl-interpret/src/convalue.rs @@ -6,7 +6,8 @@ use cl_ast::{format::FmtAdapter, ExprKind, Sym}; use super::{ builtin::Builtin, error::{Error, IResult}, - function::Function, Callable, Environment, + function::Function, + Callable, Environment, }; use std::{collections::HashMap, ops::*, rc::Rc}; @@ -40,6 +41,8 @@ pub enum ConValue { RangeInc(Integer, Integer), /// A value of a product type Struct(Box<(Sym, HashMap)>), + /// A value of a product type with anonymous members + TupleStruct(Box<(Sym, Box<[ConValue]>)>), /// An entire namespace Module(Box>>), /// A quoted expression @@ -298,6 +301,20 @@ impl std::fmt::Display for ConValue { } ')'.fmt(f) } + ConValue::TupleStruct(parts) => { + let (name, tuple) = parts.as_ref(); + if !name.is_empty() { + write!(f, "{name}")?; + } + '('.fmt(f)?; + for (idx, element) in tuple.iter().enumerate() { + if idx > 0 { + ", ".fmt(f)? + } + element.fmt(f)? + } + ')'.fmt(f) + } ConValue::Struct(parts) => { let (name, map) = parts.as_ref(); use std::fmt::Write; diff --git a/compiler/cl-interpret/src/function.rs b/compiler/cl-interpret/src/function.rs index bd6b37a..a9e1bb5 100644 --- a/compiler/cl-interpret/src/function.rs +++ b/compiler/cl-interpret/src/function.rs @@ -21,12 +21,16 @@ pub struct Function { decl: Rc, /// Stores data from the enclosing scopes upvars: RefCell, + is_constructor: bool, } impl Function { pub fn new(decl: &FnDecl) -> Self { // let upvars = collect_upvars(decl, env); - Self { decl: decl.clone().into(), upvars: Default::default() } + Self { decl: decl.clone().into(), upvars: Default::default(), is_constructor: false } + } + pub fn new_constructor(decl: FnDecl) -> Self { + Self { decl: decl.into(), upvars: Default::default(), is_constructor: true } } pub fn decl(&self) -> &FnDecl { &self.decl @@ -54,6 +58,9 @@ impl Callable for Function { if args.len() != bind.len() { return Err(Error::ArgNumber { want: bind.len(), got: args.len() }); } + if self.is_constructor { + return Ok(ConValue::TupleStruct(Box::new((*name, args.into())))); + } let Some(body) = body else { return Err(Error::NotDefined(*name)); }; diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index cbeecca..fc35c6b 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -113,8 +113,42 @@ impl Interpret for Function { } } impl Interpret for Struct { - fn interpret(&self, _env: &mut Environment) -> IResult { - println!("TODO: {self}"); + fn interpret(&self, env: &mut Environment) -> IResult { + let Self { name, kind } = self; + match kind { + StructKind::Empty => {} + StructKind::Tuple(args) => { + // Constructs the AST from scratch. TODO: This, better. + let constructor = Function { + name: *name, + sign: TyFn { + args: TyKind::Tuple(TyTuple { + types: args.iter().map(|ty| ty.kind.clone()).collect(), + }) + .into(), + rety: Some( + Ty { + extents: cl_structures::span::Span::dummy(), + kind: TyKind::Path(Path::from(*name)), + } + .into(), + ), + }, + bind: args + .iter() + .enumerate() + .map(|(idx, _)| Param { + mutability: Mutability::Not, + name: idx.to_string().into(), + }) + .collect(), + body: None, + }; + let constructor = crate::function::Function::new_constructor(constructor); + env.insert(*name, Some(constructor.into())); + } + StructKind::Struct(_) => eprintln!("TODO: {self}"), + } Ok(ConValue::Empty) } } @@ -356,8 +390,8 @@ mod assignment { (*a == b).then_some(()).ok_or(Error::NotAssignable) } (Pattern::Literal(Literal::Float(a)), ConValue::Float(b)) => (f64::from_bits(*a) == b) - .then_some(()) - .ok_or(Error::NotAssignable), + .then_some(()) + .ok_or(Error::NotAssignable), (Pattern::Literal(Literal::Int(a)), ConValue::Int(b)) => { (b == *a as _).then_some(()).ok_or(Error::NotAssignable) } @@ -376,10 +410,13 @@ mod assignment { append_sub(sub, pat, Rc::unwrap_or_clone(r)) } - (Pattern::Struct(_path, patterns), ConValue::Struct(parts)) => { - let (_name, mut values) = *parts; + (Pattern::Struct(path, patterns), ConValue::Struct(parts)) => { + let (name, mut values) = *parts; + if !path.ends_with(&name) { + Err(Error::TypeError)? + } if patterns.len() != values.len() { - return Err(Error::TypeError); + return Err(Error::ArgNumber { want: patterns.len(), got: values.len() }); } for (name, pat) in patterns { let value = values.remove(name).ok_or(Error::TypeError)?; @@ -393,6 +430,20 @@ mod assignment { Ok(()) } + (Pattern::TupleStruct(path, patterns), ConValue::TupleStruct(parts)) => { + let (name, values) = *parts; + if !path.ends_with(&name) { + Err(Error::TypeError)? + } + if patterns.len() != values.len() { + Err(Error::ArgNumber { want: patterns.len(), got: values.len() })? + } + for (pat, value) in patterns.iter().zip(Vec::from(values).into_iter()) { + append_sub(sub, pat, value)?; + } + Ok(()) + } + (pat, value) => { eprintln!("Could not match pattern `{pat}` with value `{value}`!"); Err(Error::NotAssignable) @@ -455,14 +506,17 @@ mod assignment { 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), + Some(ConValue::Module(env)) => project_path_in_namespace(env, rest), _ => Err(Error::NotIndexable), }, _ => Err(Error::NotAssignable), } } - fn addrof_member<'e>(env: &'e mut Environment, member: &Member) -> IResult<&'e mut ConValue> { + pub 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); @@ -470,15 +524,7 @@ mod assignment { 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)?, - }) + project_memberkind(slot, kind) } fn addrof_index<'e>(env: &'e mut Environment, index: &Index) -> IResult<&'e mut ConValue> { @@ -489,19 +535,50 @@ mod assignment { .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)?, - } + head = project_index(head, &index)?; } Ok(head) } - pub fn addrof_path_within_namespace<'e>( + /// 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(s), MemberKind::Struct(id)) => { + s.1.get_mut(id).ok_or(Error::NotDefined(*id)) + } + (ConValue::TupleStruct(s), MemberKind::Tuple(Literal::Int(id))) => { + let len = s.1.len(); + s.1.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> { + match (value, 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), + } + } + + pub fn project_path_in_namespace<'e>( env: &'e mut Namespace, path: &[PathPart], ) -> IResult<&'e mut Option> { @@ -510,11 +587,11 @@ mod assignment { [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), + Some(ConValue::Module(env)) => project_path_in_namespace(env, rest), _ => Err(Error::NotIndexable), } } - [PathPart::SelfKw, rest @ ..] => addrof_path_within_namespace(env, rest), + [PathPart::SelfKw, rest @ ..] => project_path_in_namespace(env, rest), [PathPart::SelfTy, ..] => todo!("calc_address for `Self`"), [PathPart::SuperKw, ..] => todo!("calc_address for `super`"), } @@ -726,16 +803,14 @@ impl Interpret for Cast { impl Interpret for Member { fn interpret(&self, env: &mut Environment) -> IResult { let Member { head, kind } = self; + if let ExprKind::Path(_) = head.as_ref() { + return assignment::addrof_member(env, self).cloned(); + } let head = head.interpret(env)?; match (head, kind) { - (ConValue::Tuple(v), MemberKind::Tuple(Literal::Int(id))) => v - .get(*id as usize) - .cloned() - .ok_or(Error::OobIndex(*id as usize, v.len())), - (ConValue::Struct(parts), MemberKind::Struct(name)) => { - parts.1.get(name).cloned().ok_or(Error::NotDefined(*name)) - } - (ConValue::Struct(parts), MemberKind::Call(name, args)) => { + (ConValue::Struct(parts), MemberKind::Call(name, args)) + if parts.1.contains_key(name) => + { let mut values = vec![]; for arg in &args.exprs { values.push(arg.interpret(env)?); @@ -753,7 +828,7 @@ impl Interpret for Member { } env.call(*name, &values) } - _ => Err(Error::TypeError)?, + (mut head, kind) => assignment::project_memberkind(&mut head, kind).cloned(), } } }