cl-interpret: Tuple structs + fix tuple member access
This commit is contained in:
		| @@ -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, `_` |         /// Checks whether this path refers to the sinkhole identifier, `_` | ||||||
|         pub fn is_sinkhole(&self) -> bool { |         pub fn is_sinkhole(&self) -> bool { | ||||||
|             if let [PathPart::Ident(id)] = self.parts.as_slice() { |             if let [PathPart::Ident(id)] = self.parts.as_slice() { | ||||||
|   | |||||||
| @@ -6,7 +6,8 @@ use cl_ast::{format::FmtAdapter, ExprKind, Sym}; | |||||||
| use super::{ | use super::{ | ||||||
|     builtin::Builtin, |     builtin::Builtin, | ||||||
|     error::{Error, IResult}, |     error::{Error, IResult}, | ||||||
|     function::Function, Callable, Environment, |     function::Function, | ||||||
|  |     Callable, Environment, | ||||||
| }; | }; | ||||||
| use std::{collections::HashMap, ops::*, rc::Rc}; | use std::{collections::HashMap, ops::*, rc::Rc}; | ||||||
|  |  | ||||||
| @@ -40,6 +41,8 @@ pub enum ConValue { | |||||||
|     RangeInc(Integer, Integer), |     RangeInc(Integer, Integer), | ||||||
|     /// A value of a product type |     /// A value of a product type | ||||||
|     Struct(Box<(Sym, HashMap<Sym, ConValue>)>), |     Struct(Box<(Sym, HashMap<Sym, ConValue>)>), | ||||||
|  |     /// A value of a product type with anonymous members | ||||||
|  |     TupleStruct(Box<(Sym, Box<[ConValue]>)>), | ||||||
|     /// An entire namespace |     /// An entire namespace | ||||||
|     Module(Box<HashMap<Sym, Option<ConValue>>>), |     Module(Box<HashMap<Sym, Option<ConValue>>>), | ||||||
|     /// A quoted expression |     /// A quoted expression | ||||||
| @@ -298,6 +301,20 @@ impl std::fmt::Display for ConValue { | |||||||
|                 } |                 } | ||||||
|                 ')'.fmt(f) |                 ')'.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) => { |             ConValue::Struct(parts) => { | ||||||
|                 let (name, map) = parts.as_ref(); |                 let (name, map) = parts.as_ref(); | ||||||
|                 use std::fmt::Write; |                 use std::fmt::Write; | ||||||
|   | |||||||
| @@ -21,12 +21,16 @@ pub struct Function { | |||||||
|     decl: Rc<FnDecl>, |     decl: Rc<FnDecl>, | ||||||
|     /// Stores data from the enclosing scopes |     /// Stores data from the enclosing scopes | ||||||
|     upvars: RefCell<Upvars>, |     upvars: RefCell<Upvars>, | ||||||
|  |     is_constructor: bool, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Function { | impl Function { | ||||||
|     pub fn new(decl: &FnDecl) -> Self { |     pub fn new(decl: &FnDecl) -> Self { | ||||||
|         // let upvars = collect_upvars(decl, env); |         // 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 { |     pub fn decl(&self) -> &FnDecl { | ||||||
|         &self.decl |         &self.decl | ||||||
| @@ -54,6 +58,9 @@ impl Callable for Function { | |||||||
|         if args.len() != bind.len() { |         if args.len() != bind.len() { | ||||||
|             return Err(Error::ArgNumber { want: bind.len(), got: args.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 { |         let Some(body) = body else { | ||||||
|             return Err(Error::NotDefined(*name)); |             return Err(Error::NotDefined(*name)); | ||||||
|         }; |         }; | ||||||
|   | |||||||
| @@ -113,8 +113,42 @@ impl Interpret for Function { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| impl Interpret for Struct { | impl Interpret for Struct { | ||||||
|     fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> { |     fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { | ||||||
|         println!("TODO: {self}"); |         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) |         Ok(ConValue::Empty) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -356,8 +390,8 @@ mod assignment { | |||||||
|                 (*a == b).then_some(()).ok_or(Error::NotAssignable) |                 (*a == b).then_some(()).ok_or(Error::NotAssignable) | ||||||
|             } |             } | ||||||
|             (Pattern::Literal(Literal::Float(a)), ConValue::Float(b)) => (f64::from_bits(*a) == b) |             (Pattern::Literal(Literal::Float(a)), ConValue::Float(b)) => (f64::from_bits(*a) == b) | ||||||
|             .then_some(()) |                 .then_some(()) | ||||||
|             .ok_or(Error::NotAssignable), |                 .ok_or(Error::NotAssignable), | ||||||
|             (Pattern::Literal(Literal::Int(a)), ConValue::Int(b)) => { |             (Pattern::Literal(Literal::Int(a)), ConValue::Int(b)) => { | ||||||
|                 (b == *a as _).then_some(()).ok_or(Error::NotAssignable) |                 (b == *a as _).then_some(()).ok_or(Error::NotAssignable) | ||||||
|             } |             } | ||||||
| @@ -376,10 +410,13 @@ mod assignment { | |||||||
|                 append_sub(sub, pat, Rc::unwrap_or_clone(r)) |                 append_sub(sub, pat, Rc::unwrap_or_clone(r)) | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             (Pattern::Struct(_path, patterns), ConValue::Struct(parts)) => { |             (Pattern::Struct(path, patterns), ConValue::Struct(parts)) => { | ||||||
|                 let (_name, mut values) = *parts; |                 let (name, mut values) = *parts; | ||||||
|  |                 if !path.ends_with(&name) { | ||||||
|  |                     Err(Error::TypeError)? | ||||||
|  |                 } | ||||||
|                 if patterns.len() != values.len() { |                 if patterns.len() != values.len() { | ||||||
|                     return Err(Error::TypeError); |                     return Err(Error::ArgNumber { want: patterns.len(), got: values.len() }); | ||||||
|                 } |                 } | ||||||
|                 for (name, pat) in patterns { |                 for (name, pat) in patterns { | ||||||
|                     let value = values.remove(name).ok_or(Error::TypeError)?; |                     let value = values.remove(name).ok_or(Error::TypeError)?; | ||||||
| @@ -393,6 +430,20 @@ mod assignment { | |||||||
|                 Ok(()) |                 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) => { |             (pat, value) => { | ||||||
|                 eprintln!("Could not match pattern `{pat}` with value `{value}`!"); |                 eprintln!("Could not match pattern `{pat}` with value `{value}`!"); | ||||||
|                 Err(Error::NotAssignable) |                 Err(Error::NotAssignable) | ||||||
| @@ -455,14 +506,17 @@ mod assignment { | |||||||
|         match path { |         match path { | ||||||
|             [PathPart::Ident(name)] => env.get_mut(*name), |             [PathPart::Ident(name)] => env.get_mut(*name), | ||||||
|             [PathPart::Ident(name), rest @ ..] => match 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::NotIndexable), | ||||||
|             }, |             }, | ||||||
|             _ => Err(Error::NotAssignable), |             _ => 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 Member { head, kind } = member; | ||||||
|         let ExprKind::Path(path) = head.as_ref() else { |         let ExprKind::Path(path) = head.as_ref() else { | ||||||
|             return Err(Error::TypeError); |             return Err(Error::TypeError); | ||||||
| @@ -470,15 +524,7 @@ mod assignment { | |||||||
|         let slot = addrof_path(env, &path.parts)? |         let slot = addrof_path(env, &path.parts)? | ||||||
|             .as_mut() |             .as_mut() | ||||||
|             .ok_or(Error::NotAssignable)?; |             .ok_or(Error::NotAssignable)?; | ||||||
|         Ok(match (slot, kind) { |         project_memberkind(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> { |     fn addrof_index<'e>(env: &'e mut Environment, index: &Index) -> IResult<&'e mut ConValue> { | ||||||
| @@ -489,19 +535,50 @@ mod assignment { | |||||||
|             .collect::<IResult<Vec<_>>>()?; |             .collect::<IResult<Vec<_>>>()?; | ||||||
|         let mut head = addrof(env, head)?; |         let mut head = addrof(env, head)?; | ||||||
|         for index in indices { |         for index in indices { | ||||||
|             head = match (head, index) { |             head = project_index(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) |         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, |         env: &'e mut Namespace, | ||||||
|         path: &[PathPart], |         path: &[PathPart], | ||||||
|     ) -> IResult<&'e mut Option<ConValue>> { |     ) -> IResult<&'e mut Option<ConValue>> { | ||||||
| @@ -510,11 +587,11 @@ mod assignment { | |||||||
|             [PathPart::Ident(name)] => env.get_mut(name).ok_or(Error::NotDefined(*name)), |             [PathPart::Ident(name)] => env.get_mut(name).ok_or(Error::NotDefined(*name)), | ||||||
|             [PathPart::Ident(name), rest @ ..] => { |             [PathPart::Ident(name), rest @ ..] => { | ||||||
|                 match env.get_mut(name).ok_or(Error::NotDefined(*name))? { |                 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), |                     _ => 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::SelfTy, ..] => todo!("calc_address for `Self`"), | ||||||
|             [PathPart::SuperKw, ..] => todo!("calc_address for `super`"), |             [PathPart::SuperKw, ..] => todo!("calc_address for `super`"), | ||||||
|         } |         } | ||||||
| @@ -726,16 +803,14 @@ impl Interpret for Cast { | |||||||
| impl Interpret for Member { | impl Interpret for Member { | ||||||
|     fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { |     fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { | ||||||
|         let Member { head, kind } = self; |         let Member { head, kind } = self; | ||||||
|  |         if let ExprKind::Path(_) = head.as_ref() { | ||||||
|  |             return assignment::addrof_member(env, self).cloned(); | ||||||
|  |         } | ||||||
|         let head = head.interpret(env)?; |         let head = head.interpret(env)?; | ||||||
|         match (head, kind) { |         match (head, kind) { | ||||||
|             (ConValue::Tuple(v), MemberKind::Tuple(Literal::Int(id))) => v |             (ConValue::Struct(parts), MemberKind::Call(name, args)) | ||||||
|                 .get(*id as usize) |                 if parts.1.contains_key(name) => | ||||||
|                 .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)) => { |  | ||||||
|                 let mut values = vec![]; |                 let mut values = vec![]; | ||||||
|                 for arg in &args.exprs { |                 for arg in &args.exprs { | ||||||
|                     values.push(arg.interpret(env)?); |                     values.push(arg.interpret(env)?); | ||||||
| @@ -753,7 +828,7 @@ impl Interpret for Member { | |||||||
|                 } |                 } | ||||||
|                 env.call(*name, &values) |                 env.call(*name, &values) | ||||||
|             } |             } | ||||||
|             _ => Err(Error::TypeError)?, |             (mut head, kind) => assignment::project_memberkind(&mut head, kind).cloned(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user