cl-interpret: Tuple structs + fix tuple member access

This commit is contained in:
John 2025-02-22 03:31:27 -06:00
parent 697d139cfd
commit 7a8da33de9
4 changed files with 147 additions and 40 deletions

View File

@ -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() {

View File

@ -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;

View File

@ -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));
}; };

View File

@ -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(),
} }
} }
} }