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, `_`
pub fn is_sinkhole(&self) -> bool {
if let [PathPart::Ident(id)] = self.parts.as_slice() {

View File

@ -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<Sym, ConValue>)>),
/// A value of a product type with anonymous members
TupleStruct(Box<(Sym, Box<[ConValue]>)>),
/// An entire namespace
Module(Box<HashMap<Sym, Option<ConValue>>>),
/// 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;

View File

@ -21,12 +21,16 @@ pub struct Function {
decl: Rc<FnDecl>,
/// Stores data from the enclosing scopes
upvars: RefCell<Upvars>,
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));
};

View File

@ -113,8 +113,42 @@ impl Interpret for Function {
}
}
impl Interpret for Struct {
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
println!("TODO: {self}");
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
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)
}
}
@ -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::<IResult<Vec<_>>>()?;
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<ConValue>> {
@ -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<ConValue> {
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(),
}
}
}