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, `_`
|
/// 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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user