cl-interpret: Tuple structs + fix tuple member access
This commit is contained in:
parent
697d139cfd
commit
7a8da33de9
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
};
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user