From 8b0a122dfca8d6489b92e906a5515a6141c29758 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 18 Jul 2025 05:29:10 -0400 Subject: [PATCH] cl-interpret: Environment/stack overhaul + Ref patterns --- .../cl-ast/src/desugar/constant_folder.rs | 3 +- compiler/cl-interpret/src/builtin.rs | 47 +- compiler/cl-interpret/src/closure.rs | 8 +- compiler/cl-interpret/src/convalue.rs | 132 ++-- compiler/cl-interpret/src/env.rs | 228 ++++--- compiler/cl-interpret/src/error.rs | 6 +- compiler/cl-interpret/src/function.rs | 22 +- .../src/function/collect_upvars.rs | 52 +- compiler/cl-interpret/src/interpret.rs | 282 ++++---- compiler/cl-interpret/src/lib.rs | 639 +++++++++++++++++- compiler/cl-interpret/src/pattern.rs | 122 +++- compiler/cl-repl/src/cli.rs | 28 +- 12 files changed, 1216 insertions(+), 353 deletions(-) diff --git a/compiler/cl-ast/src/desugar/constant_folder.rs b/compiler/cl-ast/src/desugar/constant_folder.rs index be4fa6e..27087fd 100644 --- a/compiler/cl-ast/src/desugar/constant_folder.rs +++ b/compiler/cl-ast/src/desugar/constant_folder.rs @@ -78,7 +78,8 @@ impl Fold for ConstantFolder { Ek::Unary(Unary { kind, tail }) => { un_rule! (match (kind, self.fold_expr(*tail)) { (Not, std::ops::Not::not, Int, Bool), - (Neg, std::ops::Not::not, Int, Bool), + (Neg, std::ops::Not::not, Bool), + (Neg, |i| -(i as i128) as u128, Int), (Neg, |f| (-f64::from_bits(f)).to_bits(), Float), (At, std::ops::Not::not, Float), /* Lmao */ }) diff --git a/compiler/cl-interpret/src/builtin.rs b/compiler/cl-interpret/src/builtin.rs index b9f84e5..63249af 100644 --- a/compiler/cl-interpret/src/builtin.rs +++ b/compiler/cl-interpret/src/builtin.rs @@ -154,13 +154,26 @@ pub const Builtins: &[Builtin] = &builtins![ Ok(()) } + /// Gets all global variables in the environment + fn globals() @env { + let globals = env.globals(); + Ok(ConValue::Slice(globals.base, globals.binds.len())) + } + fn builtins() @env { - for builtin in env.globals().values().flatten().filter(|v| matches!(v, ConValue::Builtin(_))) { - println!("{builtin}") + let len = env.globals().binds.len(); + for builtin in 0..len { + if let Some(value @ ConValue::Builtin(_)) = env.get_id(builtin) { + println!("{builtin}: {value}") + } } Ok(()) } + fn alloca(ConValue::Int(len)) @env { + Ok(env.alloca(ConValue::Empty, *len as usize)) + } + /// Returns the length of the input list as a [ConValue::Int] fn len(list) @env { Ok(match list { @@ -169,12 +182,25 @@ pub const Builtins: &[Builtin] = &builtins![ ConValue::Ref(r) => { return len(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()]) } - ConValue::Array(t) => t.len() as _, + ConValue::Slice(_, len) => *len as _, + ConValue::Array(arr) => arr.len() as _, ConValue::Tuple(t) => t.len() as _, _ => Err(Error::TypeError())?, }) } + fn push(ConValue::Ref(index), item) @env{ + let Some(ConValue::Array(v)) = env.get_id_mut(*index) else { + Err(Error::TypeError())? + }; + + let mut items = std::mem::take(v).into_vec(); + items.push(item.clone()); + *v = items.into_boxed_slice(); + + Ok(ConValue::Empty) + } + fn chars(string) @env { Ok(match string { ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()), @@ -296,23 +322,30 @@ pub const Math: &[Builtin] = &builtins![ } #[allow(non_snake_case)] - fn RangeExc(start, end) { + fn RangeExc(start, end) @env { Ok(ConValue::TupleStruct(Box::new(( "RangeExc", Box::new([start.clone(), end.clone()]) )))) } #[allow(non_snake_case)] - fn RangeInc(start, end) { + fn RangeInc(start, end) @env { Ok(ConValue::TupleStruct(Box::new(( "RangeInc", Box::new([start.clone(), end.clone()]) )))) } #[allow(non_snake_case)] - fn RangeTo(end) { + fn RangeTo(end) @env { Ok(ConValue::TupleStruct(Box::new(( - "RangeInc", Box::new([end.clone()]) + "RangeTo", Box::new([end.clone()]) + )))) + } + + #[allow(non_snake_case)] + fn RangeToInc(end) @env { + Ok(ConValue::TupleStruct(Box::new(( + "RangeToInc", Box::new([end.clone()]) )))) } diff --git a/compiler/cl-interpret/src/closure.rs b/compiler/cl-interpret/src/closure.rs index 368c2ec..d05c28d 100644 --- a/compiler/cl-interpret/src/closure.rs +++ b/compiler/cl-interpret/src/closure.rs @@ -15,7 +15,7 @@ use std::{collections::HashMap, fmt::Display}; #[derive(Clone, Debug)] pub struct Closure { decl: cl_ast::Closure, - lift: HashMap>, + lift: HashMap, } impl Closure { @@ -48,8 +48,8 @@ impl Callable for Closure { let mut env = env.frame("args"); - for (name, value) in pattern::substitution(&decl.arg, ConValue::Tuple(args.into()))? { - env.insert(*name, Some(value)); + for (name, value) in pattern::substitution(&env, &decl.arg, ConValue::Tuple(args.into()))? { + env.insert(name, value); } let res = decl.body.interpret(&mut env); @@ -63,6 +63,6 @@ impl Callable for Closure { } fn name(&self) -> cl_ast::Sym { - "{closure}".into() + Self::NAME.into() } } diff --git a/compiler/cl-interpret/src/convalue.rs b/compiler/cl-interpret/src/convalue.rs index 74a39b3..dfd685f 100644 --- a/compiler/cl-interpret/src/convalue.rs +++ b/compiler/cl-interpret/src/convalue.rs @@ -3,7 +3,7 @@ //! The most permanent fix is a temporary one. use cl_ast::{Expr, Sym, format::FmtAdapter}; -use crate::{closure::Closure, env::Place}; +use crate::{closure::Closure, constructor::Constructor}; use super::{ Callable, Environment, @@ -54,23 +54,27 @@ pub enum ConValue { /// A string String(Sym), /// A reference - Ref(Place), + Ref(usize), /// A reference to an array - Slice(Place, usize), + Slice(usize, usize), /// An Array Array(Box<[ConValue]>), /// A tuple Tuple(Box<[ConValue]>), /// A value of a product type - Struct(Box<(Sym, HashMap)>), + Struct(Box<(&'static str, HashMap)>), /// A value of a product type with anonymous members TupleStruct(Box<(&'static str, Box<[ConValue]>)>), /// An entire namespace - Module(Box>>), + Module(Box>), + /// A namespace, sans storage + Module2(HashMap), /// A quoted expression Quote(Box), /// A callable thing Function(Rc), + /// A tuple constructor + TupleConstructor(Constructor), /// A closure, capturing by reference Closure(Rc), /// A built-in function @@ -86,33 +90,65 @@ impl ConValue { } } - #[allow(non_snake_case)] - pub fn TupleStruct(name: Sym, values: Box<[ConValue]>) -> Self { - Self::TupleStruct(Box::new((name.to_ref(), values))) - } - #[allow(non_snake_case)] - pub fn Struct(name: Sym, values: HashMap) -> Self { - Self::Struct(Box::new((name, values))) + pub fn typename(&self) -> IResult<&'static str> { + Ok(match self { + ConValue::Empty => "Empty", + ConValue::Int(_) => "i64", + ConValue::Float(_) => "f64", + ConValue::Bool(_) => "bool", + ConValue::Char(_) => "char", + ConValue::String(_) => "String", + ConValue::Ref(_) => "Ref", + ConValue::Slice(_, _) => "Slice", + ConValue::Array(_) => "Array", + ConValue::Tuple(_) => "Tuple", + ConValue::Struct(_) => "Struct", + ConValue::TupleStruct(_) => "TupleStruct", + ConValue::Module(_) => "", + ConValue::Module2(_) => "", + ConValue::Quote(_) => "Quote", + ConValue::Function(_) => "Fn", + ConValue::TupleConstructor(_) => "Fn", + ConValue::Closure(_) => "Fn", + ConValue::Builtin(_) => "Fn", + }) } - pub fn index(&self, index: &Self, env: &Environment) -> IResult { - let Self::Int(index) = index else { + #[allow(non_snake_case)] + pub fn TupleStruct(id: Sym, values: Box<[ConValue]>) -> Self { + Self::TupleStruct(Box::new((id.to_ref(), values))) + } + #[allow(non_snake_case)] + pub fn Struct(id: Sym, values: HashMap) -> Self { + Self::Struct(Box::new((id.to_ref(), values))) + } + + pub fn index(&self, index: &Self, _env: &Environment) -> IResult { + let &Self::Int(index) = index else { Err(Error::TypeError())? }; match self { ConValue::String(string) => string .chars() - .nth(*index as _) + .nth(index as _) .map(ConValue::Char) - .ok_or(Error::OobIndex(*index as usize, string.chars().count())), + .ok_or(Error::OobIndex(index as usize, string.chars().count())), ConValue::Array(arr) => arr - .get(*index as usize) + .get(index as usize) .cloned() - .ok_or(Error::OobIndex(*index as usize, arr.len())), - ConValue::Slice(id, start) => env - .get_id(*id) - .ok_or(Error::StackOverflow(*id))? - .index(&ConValue::Int((*index as usize + start) as isize), env), + .ok_or(Error::OobIndex(index as usize, arr.len())), + &ConValue::Slice(id, len) => { + let index = if index < 0 { + len.wrapping_add_signed(index) + } else { + index as usize + }; + if index < len { + Ok(ConValue::Ref(id + index)) + } else { + Err(Error::OobIndex(index, len)) + } + } _ => Err(Error::TypeError()), } } @@ -147,11 +183,24 @@ impl Callable for ConValue { _ => "".into(), } } - fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult { + fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult { match self { - Self::Function(func) => func.call(interpreter, args), - Self::Closure(func) => func.call(interpreter, args), - Self::Builtin(func) => func.call(interpreter, args), + Self::Function(func) => func.call(env, args), + Self::TupleConstructor(func) => func.call(env, args), + Self::Closure(func) => func.call(env, args), + Self::Builtin(func) => func.call(env, args), + Self::Module(m) => { + if let Some(func) = m.get(&"call".into()) { + func.call(env, args) + } else { + Err(Error::NotCallable(self.clone())) + } + } + &Self::Ref(ptr) => { + // Move onto stack, and call + let func = env.get_id(ptr).ok_or(Error::StackOverflow(ptr))?.clone(); + func.call(env, args) + } _ => Err(Error::NotCallable(self.clone())), } } @@ -307,7 +356,7 @@ impl std::fmt::Display for ConValue { ConValue::Char(v) => v.fmt(f), ConValue::String(v) => v.fmt(f), ConValue::Ref(v) => write!(f, "&<{}>", v), - ConValue::Slice(v, len) => write!(f, "&<{v}>[{len}..]"), + ConValue::Slice(id, len) => write!(f, "&<{id}>[{len}..]"), ConValue::Array(array) => { '['.fmt(f)?; for (idx, element) in array.iter().enumerate() { @@ -329,10 +378,8 @@ impl std::fmt::Display for ConValue { ')'.fmt(f) } ConValue::TupleStruct(parts) => { - let (name, tuple) = parts.as_ref(); - if !name.is_empty() { - write!(f, "{name}")?; - } + let (id, tuple) = parts.as_ref(); + write!(f, "{id}")?; '('.fmt(f)?; for (idx, element) in tuple.iter().enumerate() { if idx > 0 { @@ -343,11 +390,9 @@ impl std::fmt::Display for ConValue { ')'.fmt(f) } ConValue::Struct(parts) => { - let (name, map) = parts.as_ref(); + let (id, map) = parts.as_ref(); use std::fmt::Write; - if !name.is_empty() { - write!(f, "{name} ")?; - } + write!(f, "{id} ")?; let mut f = f.delimit_with("{", "\n}"); for (k, v) in map.iter() { write!(f, "\n{k}: {v},")?; @@ -358,11 +403,15 @@ impl std::fmt::Display for ConValue { use std::fmt::Write; let mut f = f.delimit_with("{", "\n}"); for (k, v) in module.iter() { - write!(f, "\n{k}: ")?; - match v { - Some(v) => write!(f, "{v},"), - None => write!(f, "_,"), - }? + write!(f, "\n{k}: {v},")?; + } + Ok(()) + } + ConValue::Module2(module) => { + use std::fmt::Write; + let mut f = f.delimit_with("{", "\n}"); + for (k, v) in module.iter() { + write!(f, "\n{k}: <{v}>,")?; } Ok(()) } @@ -372,6 +421,9 @@ impl std::fmt::Display for ConValue { ConValue::Function(func) => { write!(f, "{}", func.decl()) } + ConValue::TupleConstructor(Constructor { name: index, arity }) => { + write!(f, "{index}(..{arity})") + } ConValue::Closure(func) => { write!(f, "{}", func.as_ref()) } diff --git a/compiler/cl-interpret/src/env.rs b/compiler/cl-interpret/src/env.rs index 4731674..af0d303 100644 --- a/compiler/cl-interpret/src/env.rs +++ b/compiler/cl-interpret/src/env.rs @@ -1,6 +1,6 @@ //! Lexical and non-lexical scoping for variables -use crate::builtin::Builtin; +use crate::{builtin::Builtin, constructor::Constructor, modules::ModuleTree}; use super::{ Callable, Interpret, @@ -17,37 +17,25 @@ use std::{ rc::Rc, }; -type StackFrame = HashMap>; +pub type StackFrame = HashMap; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum Place { - Global(Sym), - Local(usize), -} - -impl Display for Place { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Place::Global(name) => name.fmt(f), - Place::Local(id) => id.fmt(f), - } - } -} +pub type StackBinds = HashMap; #[derive(Clone, Debug, Default)] -struct EnvFrame { - /// The length of the array when this stack frame was constructed +pub(crate) struct EnvFrame { pub name: Option<&'static str>, + /// The length of the array when this stack frame was constructed pub base: usize, - pub binds: HashMap, + /// The bindings of name to stack position + pub binds: StackBinds, } /// Implements a nested lexical scope #[derive(Clone, Debug)] pub struct Environment { - global: HashMap>, - values: Vec>, + values: Vec, frames: Vec, + modules: ModuleTree, } impl Display for Environment { @@ -55,21 +43,24 @@ impl Display for Environment { for EnvFrame { name, base: _, binds } in self.frames.iter().rev() { writeln!( f, - "--- {} ---", - if let Some(name) = name { name } else { "" } + "--- {}[{}] ---", + if let Some(name) = name { name } else { "" }, + binds.len(), )?; - for (var, val) in binds { - write!(f, "{var}: ")?; - match self.values.get(*val) { - Some(Some(value)) => writeln!(f, "\t{value}"), - Some(None) => writeln!(f, ""), - None => writeln!(f, "ERROR: {var} address blows the stack!"), + let mut binds: Vec<_> = binds.iter().collect(); + binds.sort_by(|(_, a), (_, b)| a.cmp(b)); + for (name, idx) in binds { + write!(f, "{idx:4} {name}: ")?; + match self.values.get(*idx) { + Some(value) => writeln!(f, "\t{value}"), + None => writeln!(f, "ERROR: {name}'s address blows the stack!"), }? } } Ok(()) } } + impl Default for Environment { fn default() -> Self { let mut this = Self::no_builtins(); @@ -84,7 +75,11 @@ impl Environment { } /// Creates an [Environment] with no [builtins](super::builtin) pub fn no_builtins() -> Self { - Self { values: Vec::new(), global: HashMap::new(), frames: vec![] } + Self { + values: Vec::new(), + frames: vec![EnvFrame::default()], + modules: ModuleTree::default(), + } } /// Reflexively evaluates a node @@ -92,38 +87,60 @@ impl Environment { node.interpret(self) } - /// Calls a function inside the interpreter's scope, + /// Calls a function inside the Environment's scope, /// and returns the result pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult { let function = self.get(name)?; function.call(self, args) } + pub fn modules_mut(&mut self) -> &mut ModuleTree { + &mut self.modules + } + + pub fn modules(&self) -> &ModuleTree { + &self.modules + } + /// Binds a value to the given name in the current scope. - pub fn bind(&mut self, name: &str, value: impl Into) { - self.insert(name.into(), Some(value.into())); + pub fn bind(&mut self, name: impl Into, value: impl Into) { + self.insert(name.into(), value.into()); + } + + pub fn bind_raw(&mut self, name: Sym, id: usize) -> Option<()> { + let EnvFrame { name: _, base: _, binds } = self.frames.last_mut()?; + binds.insert(name, id); + Some(()) } /// Gets all registered globals, bound or unbound. - pub fn globals(&self) -> &HashMap> { - &self.global + pub(crate) fn globals(&self) -> &EnvFrame { + self.frames.first().unwrap() } /// Adds builtins /// /// # Panics /// - /// Will panic if globals table is non-empty! + /// Will panic if stack contains more than the globals frame! pub fn add_builtins(&mut self, builtins: &'static [Builtin]) -> &mut Self { - let Self { global, .. } = self; - for builtin in builtins { - global.insert(builtin.name(), Some(builtin.into())); + if self.frames.len() != 1 { + panic!("Cannot add builtins to full stack: {self}") } + + for builtin in builtins { + self.insert(builtin.name(), builtin.into()); + } + self } pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) { - self.enter(name); + self.frames.push(EnvFrame { + name: Some(name), + base: self.values.len(), + binds: HashMap::new(), + }); for (k, v) in frame { self.insert(k, v); } @@ -133,7 +150,7 @@ impl Environment { let mut out = HashMap::new(); let EnvFrame { name, base, binds } = self.frames.pop()?; for (k, v) in binds { - out.insert(k, self.values.get_mut(v).and_then(std::mem::take)); + out.insert(k, self.values.get_mut(v).map(std::mem::take)?); } self.values.truncate(base); Some((out, name.unwrap_or(""))) @@ -146,10 +163,22 @@ impl Environment { Frame::new(self, name) } + /// Enters a nested scope, assigning the contents of `frame`, + /// and returning a [`Frame`] stack-guard. + /// + /// [`Frame`] implements Deref/DerefMut for [`Environment`]. + pub fn with_frame<'e>(&'e mut self, name: &'static str, frame: StackFrame) -> Frame<'e> { + let mut scope = self.frame(name); + for (k, v) in frame { + scope.insert(k, v); + } + scope + } + /// Resolves a variable mutably. /// /// Returns a mutable reference to the variable's record, if it exists. - pub fn get_mut(&mut self, name: Sym) -> IResult<&mut Option> { + pub fn get_mut(&mut self, name: Sym) -> IResult<&mut ConValue> { let at = self.id_of(name)?; self.get_id_mut(at).ok_or(Error::NotDefined(name)) } @@ -159,47 +188,40 @@ impl Environment { /// Returns a reference to the variable's contents, if it is defined and initialized. pub fn get(&self, name: Sym) -> IResult { let id = self.id_of(name)?; - let res = match id { - Place::Global(name) => self.global.get(&name), - Place::Local(id) => self.values.get(id), - }; - match res.ok_or(Error::NotDefined(name))? { - Some(value) => Ok(value.clone()), - None => Err(Error::NotInitialized(name)), - } + let res = self.values.get(id); + Ok(res.ok_or(Error::NotDefined(name))?.clone()) } - /// Resolves the [Place] associated with a [Sym] - pub fn id_of(&self, name: Sym) -> IResult { + /// Resolves the index associated with a [Sym] + pub fn id_of(&self, name: Sym) -> IResult { for EnvFrame { binds, .. } in self.frames.iter().rev() { if let Some(id) = binds.get(&name).copied() { - return Ok(Place::Local(id)); + return Ok(id); } } - Ok(Place::Global(name)) + Err(Error::NotDefined(name)) } - pub fn get_id(&self, at: Place) -> Option<&ConValue> { - let res = match at { - Place::Global(name) => self.global.get(&name), - Place::Local(id) => self.values.get(id), - }?; - res.as_ref() + pub fn get_id(&self, id: usize) -> Option<&ConValue> { + self.values.get(id) } - pub fn get_id_mut(&mut self, at: Place) -> Option<&mut Option> { - match at { - Place::Global(name) => self.global.get_mut(&name), - Place::Local(id) => self.values.get_mut(id), - } + pub fn get_id_mut(&mut self, id: usize) -> Option<&mut ConValue> { + self.values.get_mut(id) + } + + pub fn get_slice(&self, start: usize, len: usize) -> Option<&[ConValue]> { + self.values.get(start..start + len) + } + + pub fn get_slice_mut(&mut self, start: usize, len: usize) -> Option<&mut [ConValue]> { + self.values.get_mut(start..start + len) } /// Inserts a new [ConValue] into this [Environment] - pub fn insert(&mut self, k: Sym, v: Option) { + pub fn insert(&mut self, k: Sym, v: ConValue) { if self.bind_raw(k, self.values.len()).is_some() { self.values.push(v); - } else { - self.global.insert(k, v); } } @@ -207,42 +229,33 @@ impl Environment { pub fn insert_fn(&mut self, decl: &FnDecl) { let FnDecl { name, .. } = decl; let (name, function) = (*name, Rc::new(Function::new(decl))); - self.insert(name, Some(ConValue::Function(function.clone()))); + self.insert(name, ConValue::Function(function.clone())); // Tell the function to lift its upvars now, after it's been declared function.lift_upvars(self); } + pub fn insert_tup_constructor(&mut self, name: Sym, arity: usize) { + let cs = Constructor { arity: arity as _, name }; + self.insert(name, ConValue::TupleConstructor(cs)); + } + + /// Gets the current stack top position + pub fn pos(&self) -> usize { + self.values.len() + } + /// Allocates a local variable pub fn stack_alloc(&mut self, value: ConValue) -> IResult { let adr = self.values.len(); - self.values.push(Some(value)); + self.values.push(value); Ok(adr) } - pub fn bind_raw(&mut self, name: Sym, id: usize) -> Option<()> { - let EnvFrame { name: _, base: _, binds } = self.frames.last_mut()?; - binds.insert(name, id); - Some(()) - } -} - -/// Functions which aid in the implementation of [`Frame`] -impl Environment { - /// Enters a scope, creating a new namespace for variables - fn enter(&mut self, name: &'static str) -> &mut Self { - let new_frame = - EnvFrame { name: Some(name), base: self.values.len(), binds: HashMap::new() }; - self.frames.push(new_frame); - self - } - - /// Exits the scope, destroying all local variables and - /// returning the outer scope, if there is one - fn exit(&mut self) -> &mut Self { - if let Some(frame) = self.frames.pop() { - self.values.truncate(frame.base); - } - self + /// Allocates some space on the stack + pub fn alloca(&mut self, value: ConValue, len: usize) -> ConValue { + let idx = self.values.len(); + self.values.extend(std::iter::repeat_n(value, len)); + ConValue::Slice(idx, len) } } @@ -253,7 +266,28 @@ pub struct Frame<'scope> { } impl<'scope> Frame<'scope> { fn new(scope: &'scope mut Environment, name: &'static str) -> Self { - Self { scope: scope.enter(name) } + scope.frames.push(EnvFrame { + name: Some(name), + base: scope.values.len(), + binds: HashMap::new(), + }); + + Self { scope } + } + + pub fn pop_values(mut self) -> Option { + let mut out = HashMap::new(); + let binds = std::mem::take(&mut self.frames.last_mut()?.binds); + for (k, v) in binds { + out.insert(k, self.values.get_mut(v).map(std::mem::take)?); + } + Some(out) + } + + pub fn into_binds(mut self) -> Option { + let EnvFrame { name: _, base: _, binds } = self.frames.pop()?; + std::mem::forget(self); + Some(binds) } } impl Deref for Frame<'_> { @@ -269,6 +303,8 @@ impl DerefMut for Frame<'_> { } impl Drop for Frame<'_> { fn drop(&mut self) { - self.scope.exit(); + if let Some(frame) = self.frames.pop() { + self.values.truncate(frame.base); + } } } diff --git a/compiler/cl-interpret/src/error.rs b/compiler/cl-interpret/src/error.rs index ad392e9..f3a039f 100644 --- a/compiler/cl-interpret/src/error.rs +++ b/compiler/cl-interpret/src/error.rs @@ -3,7 +3,7 @@ use cl_ast::{Pattern, Sym}; use cl_structures::span::Span; -use super::{convalue::ConValue, env::Place}; +use super::convalue::ConValue; pub type IResult = Result; @@ -46,7 +46,7 @@ impl Error { Self { kind: ErrorKind::StackUnderflow, span: None } } /// Overflowed the stack - pub fn StackOverflow(place: Place) -> Self { + pub fn StackOverflow(place: usize) -> Self { Self { kind: ErrorKind::StackOverflow(place), span: None } } /// Exited the last scope @@ -129,7 +129,7 @@ pub enum ErrorKind { /// Underflowed the stack StackUnderflow, /// Overflowed the stack - StackOverflow(Place), + StackOverflow(usize), /// Exited the last scope ScopeExit, /// Type incompatibility diff --git a/compiler/cl-interpret/src/function.rs b/compiler/cl-interpret/src/function.rs index aff6f0a..985b33b 100644 --- a/compiler/cl-interpret/src/function.rs +++ b/compiler/cl-interpret/src/function.rs @@ -14,7 +14,7 @@ use std::{ pub mod collect_upvars; -type Upvars = HashMap>; +type Upvars = HashMap; /// Represents a block of code which persists inside the Interpreter #[derive(Clone, Debug)] @@ -23,16 +23,12 @@ pub struct Function { decl: Rc, /// Stores data from the enclosing scopes upvars: RefCell, - 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(), is_constructor: false } - } - pub fn new_constructor(decl: FnDecl) -> Self { - Self { decl: decl.into(), upvars: Default::default(), is_constructor: true } + Self { decl: decl.clone().into(), upvars: Default::default() } } pub fn decl(&self) -> &FnDecl { &self.decl @@ -57,27 +53,21 @@ impl Callable for Function { let FnDecl { name, gens: _, bind, body, sign: _ } = &*self.decl; // Check arg mapping - if self.is_constructor { - return Ok(ConValue::TupleStruct(Box::new(( - name.to_ref(), - args.into(), - )))); - } let Some(body) = body else { return Err(Error::NotDefined(*name)); }; let upvars = self.upvars.take(); - env.push_frame("upvars", upvars); + let mut env = env.with_frame("upvars", upvars); // TODO: completely refactor data storage let mut frame = env.frame("fn args"); - for (name, value) in pattern::substitution(bind, ConValue::Tuple(args.into()))? { - frame.insert(*name, Some(value)); + for (name, value) in pattern::substitution(&frame, bind, ConValue::Tuple(args.into()))? { + frame.insert(name, value); } let res = body.interpret(&mut frame); drop(frame); - if let Some((upvars, _)) = env.pop_frame() { + if let Some(upvars) = env.pop_values() { self.upvars.replace(upvars); } match res { diff --git a/compiler/cl-interpret/src/function/collect_upvars.rs b/compiler/cl-interpret/src/function/collect_upvars.rs index a762471..6cbb5f0 100644 --- a/compiler/cl-interpret/src/function/collect_upvars.rs +++ b/compiler/cl-interpret/src/function/collect_upvars.rs @@ -1,5 +1,5 @@ //! Collects the "Upvars" of a function at the point of its creation, allowing variable capture -use crate::env::{Environment, Place}; +use crate::env::Environment; use cl_ast::{ Function, Let, Path, PathPart, Pattern, Sym, ast_visitor::{visit::*, walk::Walk}, @@ -13,7 +13,7 @@ pub fn collect_upvars(f: &Function, env: &Environment) -> super::Upvars { #[derive(Clone, Debug)] pub struct CollectUpvars<'env> { env: &'env Environment, - upvars: HashMap, + upvars: HashMap, blacklist: HashSet, } @@ -22,7 +22,7 @@ impl<'env> CollectUpvars<'env> { Self { upvars: HashMap::new(), blacklist: HashSet::new(), env } } - pub fn finish(&mut self) -> HashMap { + pub fn finish(&mut self) -> HashMap { std::mem::take(&mut self.upvars) } @@ -30,7 +30,7 @@ impl<'env> CollectUpvars<'env> { let Self { env, upvars, blacklist: _ } = self; std::mem::take(upvars) .into_iter() - .map(|(k, v)| (k, env.get_id(v).cloned())) + .filter_map(|(k, v)| env.get_id(v).cloned().map(|v| (k, v))) .collect() } @@ -47,17 +47,21 @@ impl<'env> CollectUpvars<'env> { pub fn bind_name(&mut self, name: &Sym) { self.blacklist.insert(*name); } + + pub fn scope(&mut self, f: impl Fn(&mut CollectUpvars<'env>)) { + let blacklist = self.blacklist.clone(); + + // visit the scope + f(self); + + // restore the blacklist + self.blacklist = blacklist; + } } impl<'a> Visit<'a> for CollectUpvars<'_> { fn visit_block(&mut self, b: &'a cl_ast::Block) { - let blacklist = self.blacklist.clone(); - - // visit the block - b.children(self); - - // restore the blacklist - self.blacklist = blacklist; + self.scope(|cu| b.children(cu)); } fn visit_let(&mut self, l: &'a cl_ast::Let) { @@ -71,21 +75,6 @@ impl<'a> Visit<'a> for CollectUpvars<'_> { self.visit_pattern(name); } - fn visit_function(&mut self, f: &'a cl_ast::Function) { - let Function { name: _, gens: _, sign: _, bind, body } = f; - // parameters can never be upvars - bind.visit_in(self); - body.visit_in(self); - } - - fn visit_for(&mut self, f: &'a cl_ast::For) { - let cl_ast::For { bind, cond, pass, fail } = f; - self.visit_expr(cond); - self.visit_else(fail); - self.visit_pattern(bind); - self.visit_block(pass); - } - fn visit_path(&mut self, p: &'a cl_ast::Path) { // TODO: path resolution in environments let Path { absolute: false, parts } = p else { @@ -106,13 +95,18 @@ impl<'a> Visit<'a> for CollectUpvars<'_> { } } - fn visit_pattern(&mut self, p: &'a cl_ast::Pattern) { - match p { + fn visit_pattern(&mut self, value: &'a cl_ast::Pattern) { + match value { Pattern::Name(name) => { self.bind_name(name); } Pattern::RangeExc(_, _) | Pattern::RangeInc(_, _) => {} - _ => p.children(self), + _ => value.children(self), } } + + fn visit_match_arm(&mut self, value: &'a cl_ast::MatchArm) { + // MatchArms bind variables with a very small local scope + self.scope(|cu| value.children(cu)); + } } diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index 2ca1ff4..af3168b 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -8,6 +8,14 @@ use super::*; use cl_ast::{ast_visitor::Visit, *}; use std::borrow::Borrow; + +macro trace($($t:tt)*) {{ + #[cfg(debug_assertions)] + if std::env::var("CONLANG_TRACE").is_ok() { + eprintln!($($t)*) + } +}} + /// A work-in-progress tree walk interpreter for Conlang pub trait Interpret { /// Interprets this thing in the given [`Environment`]. @@ -18,6 +26,7 @@ pub trait Interpret { impl Interpret for File { fn interpret(&self, env: &mut Environment) -> IResult { + trace!("// Running {}", self.name); /// Sorts items #[derive(Debug, Default)] struct ItemSorter<'ast>(pub [Vec<&'ast Item>; 8]); @@ -45,6 +54,7 @@ impl Interpret for File { Ok(ConValue::Empty) } } + impl Interpret for Item { fn interpret(&self, env: &mut Environment) -> IResult { match &self.kind { @@ -60,139 +70,157 @@ impl Interpret for Item { } } } + impl Interpret for Alias { fn interpret(&self, _env: &mut Environment) -> IResult { - println!("TODO: {self}"); + trace!("// TODO: {self}"); Ok(ConValue::Empty) } } + impl Interpret for Const { fn interpret(&self, env: &mut Environment) -> IResult { let Const { name, ty: _, init } = self; + trace!("// Defining const {name}"); let init = init.as_ref().interpret(env)?; - env.insert(*name, Some(init)); + env.insert(*name, init); Ok(ConValue::Empty) } } + impl Interpret for Static { fn interpret(&self, env: &mut Environment) -> IResult { let Static { mutable: _, name, ty: _, init } = self; + trace!("// Defining static {name}"); let init = init.as_ref().interpret(env)?; - env.insert(*name, Some(init)); + env.insert(*name, init); Ok(ConValue::Empty) } } + impl Interpret for Module { // TODO: Keep modules around somehow, rather than putting them on the stack fn interpret(&self, env: &mut Environment) -> IResult { let Self { name, file } = self; - env.push_frame(name.to_ref(), Default::default()); + trace!("// Defining module {name}"); + + let mut scope = env.frame(name.to_ref()); + let out = match file { - Some(file) => file.interpret(env), + Some(file) => file.interpret(&mut scope), None => { eprintln!("Module {name} specified, but not imported."); Ok(ConValue::Empty) } }; - let (frame, _) = env - .pop_frame() + let frame = scope + .pop_values() .expect("Environment frames must be balanced"); - env.insert(*name, Some(ConValue::Module(frame.into()))); + env.insert(*name, ConValue::Module(frame.into())); out } } + impl Interpret for Function { fn interpret(&self, env: &mut Environment) -> IResult { + trace!("// Defining fn {}", self.name); + // register the function in the current environment env.insert_fn(self); Ok(ConValue::Empty) } } + impl Interpret for Struct { fn interpret(&self, env: &mut Environment) -> IResult { let Self { name, gens: _, kind } = self; + trace!("// Defining struct {name}"); + + let mut frame = env.frame(name.to_ref()); + match kind { - StructKind::Empty => {} + StructKind::Empty => { + frame.insert_tup_constructor("call".into(), 0); + frame.insert("__nmemb".into(), ConValue::Int(0)); + } StructKind::Tuple(args) => { // Constructs the AST from scratch. TODO: This, better. - let constructor = Function { - name: *name, - gens: Default::default(), - sign: TyFn { - args: TyKind::Tuple(TyTuple { - types: args.iter().map(|ty| ty.kind.clone()).collect(), - }) - .into(), - rety: Some( - Ty { - span: cl_structures::span::Span::dummy(), - kind: TyKind::Path(Path::from(*name)), - } - .into(), - ), - }, - bind: Pattern::Tuple( - args.iter() - .enumerate() - .map(|(idx, _)| Pattern::Name(idx.to_string().into())) - .collect(), - ), - body: None, - }; - let constructor = crate::function::Function::new_constructor(constructor); - env.insert(*name, Some(constructor.into())); + frame.insert_tup_constructor("call".into(), args.len()); + frame.insert("__nmemb".into(), ConValue::Int(args.len() as _)); + } + StructKind::Struct(members) => { + // TODO: more precise type checking of structs + for (idx, StructMember { vis: _, name: nm, ty: _ }) in members.iter().enumerate() { + trace!("// Defining {name}::{nm}"); + frame.insert(*nm, ConValue::Int(idx as _)); + } + frame.insert("__nmemb".into(), ConValue::Int(members.len() as _)); } - StructKind::Struct(_) => eprintln!("TODO: {self}"), } + + let frame = frame + .pop_values() + .expect("Environment frames must be balanced"); + + env.insert(*name, ConValue::Module(Box::new(frame))); Ok(ConValue::Empty) } } + impl Interpret for Enum { fn interpret(&self, env: &mut Environment) -> IResult { let Self { name, gens: _, variants } = self; - env.push_frame(name.to_ref(), Default::default()); + trace!("// Defining enum {name}"); + + let mut scope = env.frame(name.to_ref()); for (idx, Variant { name, kind, body }) in variants.iter().enumerate() { match (kind, body) { - (StructKind::Empty, None) => env.insert(*name, Some(ConValue::Int(idx as _))), + (StructKind::Empty, None) => scope.insert(*name, ConValue::Int(idx as _)), (StructKind::Empty, Some(idx)) => { - let idx = idx.interpret(env)?; - env.insert(*name, Some(idx)) + let idx = idx.interpret(&mut scope)?; + scope.insert(*name, idx) + } + (StructKind::Tuple(args), None) => { + scope.insert_tup_constructor(*name, args.len()); } - (StructKind::Tuple(_), None) => {} (StructKind::Struct(_), None) => {} _ => eprintln!("Well-formedness error in {self}"), } } - let (frame, _) = env - .pop_frame() + let frame = scope + .pop_values() .expect("Frame stack should remain balanced."); - env.insert(*name, Some(ConValue::Module(Box::new(frame)))); + env.insert(*name, ConValue::Module(Box::new(frame))); Ok(ConValue::Empty) } } + impl Interpret for Impl { fn interpret(&self, env: &mut Environment) -> IResult { - let Self { target: ImplKind::Type(Ty { span, kind: TyKind::Path(name) }), body } = self + let Self { + gens: _, + target: ImplKind::Type(Ty { span, kind: TyKind::Path(name), .. }), + body, + } = self else { - eprintln!("TODO: impl X for Ty"); + trace!("TODO: impl X for Ty"); return Ok(ConValue::Empty); }; - env.push_frame("impl", Default::default()); - body.interpret(env)?; + let mut frame = env.frame("impl"); + body.interpret(&mut frame)?; - let (frame, _) = env - .pop_frame() + let frame = frame + .pop_values() .expect("Environment frames must be balanced"); match assignment::addrof_path(env, name.parts.as_slice()) .map_err(|err| err.with_span(*span))? { - Some(ConValue::Module(m)) => m.extend(frame), - Some(other) => eprintln!("TODO: impl for {other}"), - None => {} + ConValue::Module(m) => m.extend(frame), + other => eprintln!("TODO: impl for {other}"), } Ok(ConValue::Empty) } @@ -226,9 +254,8 @@ impl Interpret for UseTree { let Ok(ConValue::Module(m)) = env.get(*name) else { Err(Error::TypeError())? }; - env.push_frame(name.to_ref(), *m); - let out = get_bindings(tree, env, bindings); - env.pop_frame(); + let mut scope = env.with_frame(name.to_ref(), *m); + let out = get_bindings(tree, &mut scope, bindings); return out; } UseTree::Alias(name, alias) => { @@ -238,11 +265,10 @@ impl Interpret for UseTree { bindings.insert(*name, env.get(*name)?); } UseTree::Glob => { + trace!("TODO: Improve glob imports"); if let Some((frame, name)) = env.pop_frame() { for (k, v) in &frame { - if let Some(v) = v { - bindings.insert(*k, v.clone()); - } + bindings.insert(*k, v.clone()); } env.push_frame(name, frame); } @@ -258,7 +284,7 @@ impl Interpret for UseTree { get_bindings(self, env, &mut bindings)?; for (name, value) in bindings { - env.insert(name, Some(value)); + env.insert(name, value); } Ok(ConValue::Empty) @@ -343,16 +369,19 @@ impl Interpret for Let { let Let { mutable: _, name, ty: _, init } = self; match init.as_ref().map(|i| i.interpret(env)).transpose()? { Some(value) => { - if let Ok(sub) = pattern::substitution(name, value) { - for (name, value) in sub { - env.insert(*name, Some(value)); + match pattern::substitution(env, name, value) { + Ok(sub) => { + for (name, value) in sub { + env.insert(name, value); + } + return Ok(ConValue::Bool(true)); } - return Ok(ConValue::Bool(true)); + Err(_e) => trace!("{_e}"), }; } None => { for name in pattern::variables(name) { - env.insert(*name, None); + env.insert(*name, ConValue::Empty); } } } @@ -365,10 +394,10 @@ impl Interpret for Match { let Self { scrutinee, arms } = self; let scrutinee = scrutinee.interpret(env)?; for MatchArm(pat, expr) in arms { - if let Ok(substitution) = pattern::substitution(pat, scrutinee.clone()) { + if let Ok(substitution) = pattern::substitution(env, pat, scrutinee.clone()) { let mut env = env.frame("match"); for (name, value) in substitution { - env.insert(*name, Some(value)); + env.insert(name, value); } return expr.interpret(&mut env); } @@ -377,21 +406,21 @@ impl Interpret for Match { } } -mod assignment { +pub(crate) mod assignment { /// Pattern matching engine for assignment use super::*; use std::collections::HashMap; - type Namespace = HashMap>; + type Namespace = HashMap; pub(super) fn pat_assign(env: &mut Environment, pat: &Pattern, value: ConValue) -> IResult<()> { - for (name, value) in - pattern::substitution(pat, value).map_err(|_| Error::PatFailed(pat.clone().into()))? + for (name, value) in pattern::substitution(env, pat, value) + .map_err(|_| Error::PatFailed(pat.clone().into()))? { - match env.get_mut(*name)? { - &mut Some(ConValue::Ref(id)) => { - *(env.get_id_mut(id).ok_or(Error::StackOverflow(id))?) = Some(value); + match env.get_mut(name)? { + &mut ConValue::Ref(id) => { + *(env.get_id_mut(id).ok_or(Error::StackOverflow(id))?) = value; } - other => *other = Some(value), + other => *other = value, } } Ok(()) @@ -404,11 +433,9 @@ mod assignment { match &pat.kind { ExprKind::Member(member) => *addrof_member(env, member)? = value, ExprKind::Index(index) => *addrof_index(env, index)? = value, - ExprKind::Path(path) => *addrof_path(env, &path.parts)? = Some(value), + ExprKind::Path(path) => *addrof_path(env, &path.parts)? = value, ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match addrof(env, tail)? { - &mut ConValue::Ref(r) => { - *env.get_id_mut(r).ok_or(Error::StackOverflow(r))? = Some(value) - } + &mut ConValue::Ref(r) => *env.get_id_mut(r).ok_or(Error::StackOverflow(r))? = value, _ => Err(Error::NotAssignable())?, }, _ => Err(Error::NotAssignable())?, @@ -418,18 +445,12 @@ mod assignment { pub(super) fn addrof<'e>(env: &'e mut Environment, pat: &Expr) -> IResult<&'e mut ConValue> { match &pat.kind { - ExprKind::Path(path) => addrof_path(env, &path.parts)? - .as_mut() - .ok_or(Error::NotInitialized("".into())), + ExprKind::Path(path) => addrof_path(env, &path.parts), ExprKind::Member(member) => addrof_member(env, member), ExprKind::Index(index) => addrof_index(env, index), ExprKind::Group(Group { expr }) => addrof(env, expr), ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match *addrof(env, tail)? { - ConValue::Ref(place) => env - .get_id_mut(place) - .ok_or(Error::NotIndexable())? - .as_mut() - .ok_or(Error::NotAssignable()), + ConValue::Ref(place) => env.get_id_mut(place).ok_or(Error::NotIndexable()), _ => Err(Error::TypeError()), }, _ => Err(Error::TypeError()), @@ -439,11 +460,11 @@ mod assignment { pub fn addrof_path<'e>( env: &'e mut Environment, path: &[PathPart], - ) -> IResult<&'e mut Option> { + ) -> IResult<&'e mut ConValue> { match path { [PathPart::Ident(name)] => env.get_mut(*name), [PathPart::Ident(name), rest @ ..] => match env.get_mut(*name)? { - Some(ConValue::Module(env)) => project_path_in_namespace(env, rest), + ConValue::Module(env) => project_path_in_namespace(env, rest), _ => Err(Error::NotIndexable()), }, _ => Err(Error::NotAssignable()), @@ -468,6 +489,11 @@ mod assignment { .collect::>>()?; let mut head = addrof(env, head)?; + // match head { + // ConValue::Slice(id, len) | ConValue::Array(id, len) => { + + // } + // } for index in indices { head = project_index(head, &index)?; } @@ -499,30 +525,22 @@ mod assignment { /// Performs index "projection" from a ConValue to a particular element pub fn project_index<'v>( - value: &'v mut ConValue, - index: &ConValue, + _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)) - } - (ConValue::Slice(_, _), _) => Err(Error::TypeError()), - _ => Err(Error::NotIndexable()), - } + Err(Error::NotIndexable()) } pub fn project_path_in_namespace<'e>( env: &'e mut Namespace, path: &[PathPart], - ) -> IResult<&'e mut Option> { + ) -> IResult<&'e mut ConValue> { match path { [] => Err(Error::NotAssignable()), [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)) => project_path_in_namespace(env, rest), + ConValue::Module(env) => project_path_in_namespace(env, rest), _ => Err(Error::NotIndexable()), } } @@ -725,36 +743,39 @@ impl Interpret for Member { if let Ok(member) = assignment::addrof_member(env, self) { return Ok(member.clone()); } + let mut values = vec![]; + // Evaluate if this can be Self'd - let value = match (&head.kind, kind) { + let addr = match (&head.kind, kind) { (ExprKind::Path(p), MemberKind::Call(..)) => { - p.as_sym().map(|name| Ok(ConValue::Ref(env.id_of(name)?))) // "borrow" it + p.as_sym() + .and_then(|name| Some(ConValue::Ref(env.id_of(name).ok()?))) // "borrow" it } _ => None, }; + + let mut value = head.interpret(env)?; + // Perform alternate member access - match (value.unwrap_or_else(|| head.interpret(env))?, kind) { + match (&value, &kind) { (ConValue::Struct(parts), MemberKind::Call(name, args)) if parts.1.contains_key(name) => { - let mut values = vec![]; + let f = parts.1.get(name).cloned().expect("function exists"); + values.push(addr.unwrap_or(value)); for arg in &args.exprs { values.push(arg.interpret(env)?); } - (parts.1) - .get(name) - .cloned() - .ok_or(Error::NotDefined(*name))? - .call(env, &values) + f.call(env, &values) } - (head, MemberKind::Call(name, args)) => { - let mut values = vec![head]; + (_, MemberKind::Call(name, args)) => { + values.push(addr.unwrap_or(value)); for arg in &args.exprs { values.push(arg.interpret(env)?); } env.call(*name, &values) } - (mut head, kind) => assignment::project_memberkind(&mut head, kind).cloned(), + (_, kind) => assignment::project_memberkind(&mut value, kind).cloned(), } } } @@ -774,12 +795,13 @@ impl Interpret for Structor { use std::collections::HashMap; // Look up struct/enum-struct definition + // use that definition to place the struct parts let name = match parts.last() { - Some(PathPart::Ident(name)) => *name, - Some(PathPart::SelfTy) => "Self".into(), - Some(PathPart::SuperKw) => "super".into(), - None => "".into(), + Some(PathPart::Ident(name)) => name.to_ref(), + Some(PathPart::SelfTy) => "Self", + Some(PathPart::SuperKw) => "super", + None => "", }; let mut map = HashMap::new(); @@ -798,10 +820,7 @@ impl Interpret for Path { fn interpret(&self, env: &mut Environment) -> IResult { let Self { absolute: _, parts } = self; - assignment::addrof_path(env, parts) - .cloned() - .transpose() - .ok_or_else(|| Error::NotInitialized(format!("{self}").into()))? + assignment::addrof_path(env, parts).cloned() } } impl Interpret for Literal { @@ -829,7 +848,15 @@ impl Interpret for ArrayRep { fn interpret(&self, env: &mut Environment) -> IResult { let Self { value, repeat } = self; let value = value.interpret(env)?; - Ok(ConValue::Array(vec![value; *repeat].into())) + let ConValue::Int(repeat) = repeat.interpret(env)? else { + Err(Error::TypeError())? + }; + if repeat < 0 { + // TODO: a special error just for you + Err(Error::NotIndexable())? + } + + Ok(ConValue::Array(vec![value; repeat as usize].into())) } } impl Interpret for AddrOf { @@ -844,7 +871,7 @@ impl Interpret for AddrOf { _ => { let value = expr.interpret(env)?; let temp = env.stack_alloc(value)?; - Ok(ConValue::Ref(env::Place::Local(temp))) + Ok(ConValue::Ref(temp)) } } } @@ -928,14 +955,17 @@ impl Interpret for For { _ => Err(Error::NotIterable())?, }, ConValue::Array(a) => Box::new(a.iter().cloned()), + &ConValue::Slice(head, len) => Box::new((head..head + len).map(ConValue::Ref)), + // In the production compiler this may be fully unrolled + ConValue::Tuple(t) => Box::new(t.iter().cloned()), ConValue::String(s) => Box::new(s.chars().map(ConValue::Char)), _ => Err(Error::TypeError())?, }; loop { let mut env = env.frame("loop variable"); if let Some(value) = bounds.next() { - for (name, value) in pattern::substitution(bind, value)? { - env.insert(*name, Some(value)); + for (name, value) in pattern::substitution(&env, bind, value)? { + env.insert(name, value); } match pass.interpret(&mut env) { Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value), diff --git a/compiler/cl-interpret/src/lib.rs b/compiler/cl-interpret/src/lib.rs index 5fdd71a..8665490 100644 --- a/compiler/cl-interpret/src/lib.rs +++ b/compiler/cl-interpret/src/lib.rs @@ -9,7 +9,7 @@ use error::{Error, ErrorKind, IResult}; use interpret::Interpret; /// Callable types can be called from within a Conlang program -pub trait Callable: std::fmt::Debug { +pub trait Callable { /// Calls this [Callable] in the provided [Environment], with [ConValue] args \ /// The Callable is responsible for checking the argument count and validating types fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult; @@ -23,6 +23,41 @@ pub mod interpret; pub mod function; +pub mod constructor { + use cl_ast::Sym; + + use crate::{ + Callable, + convalue::ConValue, + env::Environment, + error::{Error, IResult}, + }; + + #[derive(Clone, Copy, Debug)] + pub struct Constructor { + pub name: Sym, + pub arity: u32, + } + + impl Callable for Constructor { + fn call(&self, _env: &mut Environment, args: &[ConValue]) -> IResult { + let &Self { name, arity } = self; + if arity as usize == args.len() { + Ok(ConValue::TupleStruct(Box::new(( + name.to_ref(), + args.into(), + )))) + } else { + Err(Error::ArgNumber(arity as usize, args.len())) + } + } + + fn name(&self) -> cl_ast::Sym { + "tuple-constructor".into() + } + } +} + pub mod closure; pub mod builtin; @@ -31,6 +66,608 @@ pub mod pattern; pub mod env; +pub mod modules { + use crate::env::StackBinds; + use cl_ast::{PathPart, Sym}; + use std::collections::HashMap; + + /// Immutable object-oriented interface to a [ModuleTree] + #[derive(Clone, Copy, Debug)] + pub struct ModuleNode<'tree> { + tree: &'tree ModuleTree, + index: usize, + } + + /// Mutable object-oriented interface to a [ModuleTree] + #[derive(Debug)] + pub struct ModuleNodeMut<'tree> { + tree: &'tree mut ModuleTree, + index: usize, + } + + macro_rules! module_node_impl { + () => { + /// Gets the index from this node + pub fn index(self) -> usize { + self.index + } + /// Gets this node's parent + pub fn parent(self) -> Option { + let parent = self.tree.parent(self.index)?; + Some(Self { index: parent, ..self }) + } + /// Gets the node's "encompassing Type" + pub fn selfty(self) -> Option { + let selfty = self.tree.selfty(self.index)?; + Some(Self { index: selfty, ..self }) + } + /// Gets the child of this node with the given name + pub fn child(self, name: &Sym) -> Option { + let child = self.tree.child(self.index, name)?; + Some(Self { index: child, ..self }) + } + /// Gets a stack value in this node with the given name + pub fn item(self, name: &Sym) -> Option { + self.tree.items(self.index)?.get(name).copied() + } + /// Returns true when this node represents type information + pub fn is_ty(self) -> Option { + self.tree.is_ty.get(self.index).copied() + } + /// Returns a reference to this node's children, if present + pub fn children(&self) -> Option<&HashMap> { + self.tree.children(self.index) + } + /// Returns a reference to this node's items, if present + pub fn items(&self) -> Option<&StackBinds> { + self.tree.items(self.index) + } + /// Traverses a path starting at this node + /// + /// Returns a new node, and the unconsumed path portion. + pub fn find(self, path: &[PathPart]) -> (Self, &[PathPart]) { + let (index, path) = self.tree.find(self.index, path); + (Self { index, ..self }, path) + } + /// Traverses a path starting at this node + /// + /// Returns an item address if the path terminated in an item. + pub fn find_item(&self, path: &[PathPart]) -> Option { + self.tree.find_item(self.index, path) + } + }; + } + + impl ModuleNode<'_> { + module_node_impl! {} + } + + impl ModuleNodeMut<'_> { + module_node_impl! {} + /// Creates a new child in this node + pub fn add_child(self, name: Sym, is_ty: bool) -> Self { + let node = self.tree.add_child(self.index, name, is_ty); + self.tree.get_mut(node) + } + /// Creates an arbitrary edge in the module graph + pub fn add_import(&mut self, name: Sym, child: usize) { + self.tree.add_import(self.index, name, child) + } + pub fn add_imports(&mut self, binds: HashMap) { + self.tree.add_imports(self.index, binds) + } + /// Binds a new item in this node + pub fn add_item(&mut self, name: Sym, stack_index: usize) { + self.tree.add_item(self.index, name, stack_index) + } + /// Binds an entire stack frame in this node + pub fn add_items(&mut self, binds: StackBinds) { + self.tree.add_items(self.index, binds) + } + /// Constructs a borrowing [ModuleNode] + pub fn as_ref(&self) -> ModuleNode<'_> { + let Self { tree, index } = self; + ModuleNode { tree, index: *index } + } + } + + #[derive(Clone, Debug)] + pub struct ModuleTree { + parents: Vec, + children: Vec>, + items: Vec, + is_ty: Vec, + } + + impl ModuleTree { + /// Constructs a new ModuleTree with a single root module + pub fn new() -> Self { + Self { + parents: vec![0], + children: vec![HashMap::new()], + items: vec![HashMap::new()], + is_ty: vec![false], + } + } + + /// Gets a borrowed handle to the node at `index` + pub fn get(&self, index: usize) -> ModuleNode<'_> { + ModuleNode { tree: self, index } + } + + /// Gets a mutable handle to the node at `index` + pub fn get_mut(&mut self, index: usize) -> ModuleNodeMut<'_> { + ModuleNodeMut { tree: self, index } + } + + /// Creates a new child in this node + pub fn add_child(&mut self, parent: usize, name: Sym, is_ty: bool) -> usize { + let index = self.parents.len(); + self.children[parent].insert(name, index); + self.parents.push(parent); + self.children.push(HashMap::new()); + self.is_ty.push(is_ty); + index + } + + /// Binds a new item in this node + pub fn add_item(&mut self, node: usize, name: Sym, stack_index: usize) { + self.items[node].insert(name, stack_index); + } + + /// Creates an arbitrary child edge + pub fn add_import(&mut self, parent: usize, name: Sym, child: usize) { + self.children[parent].insert(name, child); + } + + /// Binds an entire stack frame in this node + pub fn add_items(&mut self, node: usize, binds: StackBinds) { + self.items[node].extend(binds); + } + + /// Binds an arbitrary set of child edges + pub fn add_imports(&mut self, node: usize, binds: HashMap) { + self.children[node].extend(binds); + } + + /// Gets this node's parent + pub fn parent(&self, node: usize) -> Option { + if node == 0 { + return None; + } + self.parents.get(node).copied() + } + + /// Gets the node's "encompassing Type" + pub fn selfty(&self, node: usize) -> Option { + if self.is_ty[node] { + return Some(node); + } + self.selfty(self.parent(node)?) + } + + /// Gets the child of this node with the given name + pub fn child(&self, node: usize, id: &Sym) -> Option { + self.children[node].get(id).copied() + } + + /// Gets a stack value in this node with the given name + pub fn item(&self, node: usize, name: &Sym) -> Option { + self.items.get(node).and_then(|map| map.get(name).copied()) + } + + /// Returns a reference to this node's children, if present + pub fn children(&self, node: usize) -> Option<&HashMap> { + self.children.get(node) + } + + /// Returns a reference to this node's items, if present + pub fn items(&self, node: usize) -> Option<&StackBinds> { + self.items.get(node) + } + + /// Traverses a path starting at this node + /// + /// Returns a new node, and the unconsumed path portion. + pub fn find<'p>(&self, node: usize, path: &'p [PathPart]) -> (usize, &'p [PathPart]) { + match path { + [PathPart::SuperKw, tail @ ..] => match self.parent(node) { + Some(node) => self.find(node, tail), + None => (node, path), + }, + [PathPart::Ident(name), tail @ ..] => match self.child(node, name) { + Some(node) => self.find(node, tail), + None => (node, path), + }, + [PathPart::SelfTy, tail @ ..] => match self.selfty(node) { + Some(node) => self.find(node, tail), + None => (node, path), + }, + [] => (node, path), + } + } + + /// Traverses a path starting at this node + /// + /// Returns an item address if the path terminated in an item. + pub fn find_item(&self, node: usize, path: &[PathPart]) -> Option { + let (node, [PathPart::Ident(name)]) = self.find(node, path) else { + return None; + }; + self.item(node, name) + } + } + + impl Default for ModuleTree { + fn default() -> Self { + Self::new() + } + } +} + +pub mod collector { + use std::ops::{Deref, DerefMut}; + + use crate::{ + convalue::ConValue, + env::Environment, + modules::{ModuleNode, ModuleNodeMut}, + }; + use cl_ast::{ + ast_visitor::{Visit, Walk}, + *, + }; + + pub struct Collector<'env> { + module: usize, + env: &'env mut Environment, + } + + impl Collector<'_> { + pub fn as_node(&self) -> ModuleNode<'_> { + self.env.modules().get(self.module) + } + + pub fn as_node_mut(&mut self) -> ModuleNodeMut { + self.env.modules_mut().get_mut(self.module) + } + + pub fn scope(&mut self, name: Sym, is_ty: bool, f: impl Fn(&mut Collector<'_>)) { + let module = match self.as_node_mut().child(&name) { + Some(m) => m, + None => self.as_node_mut().add_child(name, is_ty), + } + .index(); + + let mut frame = self.env.frame(name.to_ref()); + f(&mut Collector { env: &mut frame, module }); + let binds = frame.into_binds().unwrap_or_default(); + + self.modules_mut().add_items(module, binds); + } + + pub fn in_foreign_scope(&mut self, path: &[PathPart], f: F) -> Option + where F: Fn(&mut Collector<'_>) -> T { + let (module, []) = self.env.modules_mut().find(self.module, path) else { + return None; + }; + + let mut frame = self.env.frame("impl"); + let out = f(&mut Collector { env: &mut frame, module }); + let binds = frame.into_binds().unwrap_or_default(); + + self.env.modules_mut().add_items(module, binds); + Some(out) + } + } + + impl<'env> Deref for Collector<'env> { + type Target = Environment; + fn deref(&self) -> &Self::Target { + self.env + } + } + + impl DerefMut for Collector<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.env + } + } + + impl<'a, 'env> Visit<'a> for Collector<'env> { + fn visit_file(&mut self, value: &'a File) { + let mut sorter = ItemSorter::default(); + sorter.visit(value); + sorter.visit_all(self); + } + fn visit_block(&mut self, value: &'a Block) { + let mut sorter = ItemSorter::default(); + sorter.visit(value); + sorter.visit_all(self); + } + fn visit_module(&mut self, value: &'a cl_ast::Module) { + self.scope(value.name, false, |scope| value.children(scope)); + } + fn visit_alias(&mut self, value: &'a cl_ast::Alias) { + let Alias { name, from } = value; + match from.as_ref().map(Box::as_ref) { + Some(Ty { kind: TyKind::Path(path), .. }) => { + let mut node = if path.absolute { + self.modules_mut().get_mut(0) + } else { + self.as_node_mut() + }; + if let Some(item) = node.find_item(&path.parts) { + node.add_item(*name, item); + } + } + Some(other) => todo!("Type expressions in the collector: {other}"), + None => self.scope(*name, true, |_| {}), + } + } + fn visit_enum(&mut self, value: &'a cl_ast::Enum) { + let Enum { name, gens: _, variants } = value; + + self.scope(*name, true, |frame| { + for (idx, Variant { name, kind, body }) in variants.iter().enumerate() { + frame.visit(body); + frame.scope(*name, false, |frame| { + frame.bind("__discriminant", idx as isize); + match kind { + StructKind::Empty => { + frame.insert_tup_constructor("call".into(), 0); + frame.bind("__nmemb", ConValue::Int(0)); + } + StructKind::Tuple(args) => { + // Constructs the AST from scratch. TODO: This, better. + frame.insert_tup_constructor("call".into(), args.len()); + frame.bind("__nmemb", ConValue::Int(args.len() as _)); + } + StructKind::Struct(members) => { + // TODO: more precise type checking of structs + for (idx, memb) in members.iter().enumerate() { + let StructMember { vis: _, name, ty: _ } = memb; + frame.bind(*name, idx as isize); + } + frame.bind("__nmemb", ConValue::Int(members.len() as _)); + } + } + }); + } + }); + } + fn visit_struct(&mut self, value: &'a cl_ast::Struct) { + let Struct { name, gens: _, kind } = value; + + self.scope(*name, true, |frame| { + match kind { + StructKind::Empty => { + frame.insert_tup_constructor("call".into(), 0); + frame.bind("__nmemb", ConValue::Int(0)); + } + StructKind::Tuple(args) => { + // Constructs the AST from scratch. TODO: This, better. + frame.insert_tup_constructor("call".into(), args.len()); + frame.bind("__nmemb", ConValue::Int(args.len() as _)); + } + StructKind::Struct(members) => { + // TODO: more precise type checking of structs + for (idx, memb) in members.iter().enumerate() { + let StructMember { vis: _, name, ty: _ } = memb; + frame.bind(*name, idx as isize); + } + frame.bind("__nmemb", ConValue::Int(members.len() as _)); + } + } + }); + } + fn visit_const(&mut self, value: &'a cl_ast::Const) { + let Const { name, ty: _, init } = value; + self.visit(init); + self.bind(*name, ()); + } + fn visit_static(&mut self, value: &'a cl_ast::Static) { + let Static { mutable: _, name, ty: _, init } = value; + self.visit(init); + self.bind(*name, ()); + } + fn visit_function(&mut self, value: &'a cl_ast::Function) { + let Function { name, gens: _, sign: _, bind: _, body } = value; + self.scope(*name, false, |scope| { + scope.visit(body); + let f = crate::function::Function::new(value); + scope.bind("call", f); + }); + } + fn visit_impl(&mut self, value: &'a cl_ast::Impl) { + let Impl { gens: _, target: ImplKind::Type(Ty { kind: TyKind::Path(name), .. }), body } = + value + else { + eprintln!("TODO: impl X for Ty"); + return; + }; + self.in_foreign_scope(&name.parts, |scope| { + body.visit_in(scope); + }); + } + fn visit_use(&mut self, value: &'a cl_ast::Use) { + fn traverse(dest: &mut Collector<'_>, node: usize, tree: &UseTree) { + match tree { + UseTree::Tree(ts) => ts.iter().for_each(|tree| traverse(dest, node, tree)), + UseTree::Path(PathPart::Ident(name), tree) => { + if let (node, []) = dest.modules().find(node, &[PathPart::Ident(*name)]) { + traverse(dest, node, tree) + } + } + UseTree::Path(PathPart::SuperKw, tree) => { + if let Some(node) = dest.modules().parent(node) { + traverse(dest, node, tree) + } + } + UseTree::Path(PathPart::SelfTy, tree) => { + if let Some(node) = dest.modules().selfty(node) { + traverse(dest, node, tree) + } + } + UseTree::Alias(name, as_name) => { + if let Some(child) = dest.modules().child(node, name) { + dest.as_node_mut().add_import(*as_name, child); + } + if let Some(item) = dest.modules().item(node, name) { + dest.as_node_mut().add_item(*as_name, item); + } + } + UseTree::Name(name) => { + if let Some(child) = dest.modules().child(node, name) { + dest.as_node_mut().add_import(*name, child); + } + if let Some(item) = dest.modules().item(node, name) { + dest.as_node_mut().add_item(*name, item); + } + } + UseTree::Glob => { + let &mut Collector { module, ref mut env } = dest; + if let Some(children) = env.modules().children(node) { + for (name, index) in children.clone() { + env.modules_mut().add_import(module, name, index); + } + } + if let Some(items) = env.modules().items(node).cloned() { + env.modules_mut().add_items(node, items); + } + } + } + } + + let Use { absolute, tree } = value; + let node = if *absolute { 0 } else { self.module }; + traverse(self, node, tree); + } + } + + // fn make_tuple_constructor(name: Sym, args: &[Ty]) -> ConValue { + // let span = match ( + // args.first().map(|a| a.span.head), + // args.last().map(|a| a.span.tail), + // ) { + // (Some(head), Some(tail)) => Span(head, tail), + // _ => Span::dummy(), + // }; + + // let constructor = Function { + // name, + // gens: Default::default(), + // sign: TyFn { + // args: Ty { kind: TyKind::Tuple(TyTuple { types: args.to_vec() }), span }.into(), + // rety: Some(Ty { span: Span::dummy(), kind: TyKind::Path(Path::from(name)) + // }.into()), }, + // bind: Pattern::Tuple( + // args.iter() + // .enumerate() + // .map(|(idx, _)| Pattern::Name(idx.to_string().into())) + // .collect(), + // ), + // body: None, + // }; + // // ConValue::TupleConstructor(crate::constructor::Constructor {ind}) + // todo!("Tuple constructor {constructor}") + // } + + /// Sorts items + #[derive(Debug, Default)] + struct ItemSorter<'ast> { + modules: Vec<&'ast Module>, + structs: Vec<&'ast Struct>, + enums: Vec<&'ast Enum>, + aliases: Vec<&'ast Alias>, + consts: Vec<&'ast Const>, + statics: Vec<&'ast Static>, + functions: Vec<&'ast Function>, + impls: Vec<&'ast Impl>, + imports: Vec<&'ast Use>, + } + + impl<'a> ItemSorter<'a> { + fn visit_all>(&self, v: &mut V) { + let Self { + modules, + aliases, + enums, + structs, + consts, + statics, + functions, + impls, + imports, + } = self; + + // 0 + for item in modules { + item.visit_in(v); + } + // 1 + for item in structs { + item.visit_in(v); + } + for item in enums { + item.visit_in(v); + } + for item in aliases { + item.visit_in(v); + } + // 2 + // 5 + for item in consts { + item.visit_in(v); + } + for item in statics { + item.visit_in(v); + } + for item in functions { + item.visit_in(v); + } + // 4 + for item in impls { + item.visit_in(v); + } + // 3 + for item in imports { + item.visit_in(v); + } + } + } + + impl<'a> Visit<'a> for ItemSorter<'a> { + fn visit_module(&mut self, value: &'a cl_ast::Module) { + self.modules.push(value); + } + fn visit_alias(&mut self, value: &'a cl_ast::Alias) { + self.aliases.push(value); + } + fn visit_enum(&mut self, value: &'a cl_ast::Enum) { + self.enums.push(value); + } + fn visit_struct(&mut self, value: &'a cl_ast::Struct) { + self.structs.push(value); + } + fn visit_const(&mut self, value: &'a cl_ast::Const) { + self.consts.push(value); + } + fn visit_static(&mut self, value: &'a cl_ast::Static) { + self.statics.push(value); + } + fn visit_function(&mut self, value: &'a cl_ast::Function) { + self.functions.push(value); + } + fn visit_impl(&mut self, value: &'a cl_ast::Impl) { + self.impls.push(value); + } + fn visit_use(&mut self, value: &'a cl_ast::Use) { + self.imports.push(value); + } + } +} + pub mod error; #[cfg(test)] diff --git a/compiler/cl-interpret/src/pattern.rs b/compiler/cl-interpret/src/pattern.rs index bcf52f4..907399a 100644 --- a/compiler/cl-interpret/src/pattern.rs +++ b/compiler/cl-interpret/src/pattern.rs @@ -5,6 +5,7 @@ use crate::{ convalue::ConValue, + env::Environment, error::{Error, IResult}, }; use cl_ast::{Literal, Pattern, Sym}; @@ -43,7 +44,8 @@ pub fn variables(pat: &Pattern) -> Vec<&Sym> { } fn rest_binding<'pat>( - sub: &mut HashMap<&'pat Sym, ConValue>, + env: &Environment, + sub: &mut HashMap, mut patterns: &'pat [Pattern], mut values: VecDeque, ) -> IResult)>> { @@ -55,7 +57,7 @@ fn rest_binding<'pat>( let value = values .pop_front() .ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?; - append_sub(sub, pattern, value)?; + append_sub(env, sub, pattern, value)?; patterns = tail; } // Bind the tail of the list @@ -66,7 +68,7 @@ fn rest_binding<'pat>( let value = values .pop_back() .ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?; - append_sub(sub, pattern, value)?; + append_sub(env, sub, pattern, value)?; patterns = head; } // Bind the ..rest of the list @@ -77,10 +79,51 @@ fn rest_binding<'pat>( } } +fn rest_binding_ref<'pat>( + env: &Environment, + sub: &mut HashMap, + mut patterns: &'pat [Pattern], + mut head: usize, + mut tail: usize, +) -> IResult> { + // Bind the head of the list + while let [pattern, pat_tail @ ..] = patterns { + if matches!(pattern, Pattern::Rest(_)) { + break; + } + if head >= tail { + return Err(Error::PatFailed(Box::new(pattern.clone()))); + } + + append_sub(env, sub, pattern, ConValue::Ref(head))?; + head += 1; + patterns = pat_tail; + } + // Bind the tail of the list + while let [pat_head @ .., pattern] = patterns { + if matches!(pattern, Pattern::Rest(_)) { + break; + } + if head >= tail { + return Err(Error::PatFailed(Box::new(pattern.clone()))); + }; + append_sub(env, sub, pattern, ConValue::Ref(tail))?; + tail -= 1; + patterns = pat_head; + } + // Bind the ..rest of the list + match (patterns, tail - head) { + ([], 0) | ([Pattern::Rest(None)], _) => Ok(None), + ([Pattern::Rest(Some(pattern))], _) => Ok(Some((pattern.as_ref(), head, tail))), + _ => Err(Error::PatFailed(Box::new(Pattern::Array(patterns.into())))), + } +} + /// Appends a substitution to the provided table -pub fn append_sub<'pat>( - sub: &mut HashMap<&'pat Sym, ConValue>, - pat: &'pat Pattern, +pub fn append_sub( + env: &Environment, + sub: &mut HashMap, + pat: &Pattern, value: ConValue, ) -> IResult<()> { match (pat, value) { @@ -122,12 +165,21 @@ pub fn append_sub<'pat>( (Pattern::Name(name), _) if "_".eq(&**name) => Ok(()), (Pattern::Name(name), value) => { - sub.insert(name, value); + sub.insert(*name, value); Ok(()) } - (Pattern::Ref(_, pat), ConValue::Ref(r)) => { - todo!("Dereference <{r}> in pattern matching {pat}") + (Pattern::Ref(_, pat), ConValue::Ref(r)) => match env.get_id(r) { + Some(value) => append_sub(env, sub, pat, value.clone()), + None => Err(Error::PatFailed(pat.clone())), + }, + + (Pattern::Ref(_, pat), ConValue::Slice(head, len)) => { + let mut values = Vec::with_capacity(len); + for idx in head..(head + len) { + values.push(env.get_id(idx).cloned().ok_or(Error::StackOverflow(idx))?); + } + append_sub(env, sub, pat, ConValue::Array(values.into_boxed_slice())) } (Pattern::RangeExc(head, tail), value) => match (head.as_ref(), tail.as_ref(), value) { @@ -195,48 +247,64 @@ pub fn append_sub<'pat>( }, (Pattern::Array(patterns), ConValue::Array(values)) => { - match rest_binding(sub, patterns, values.into_vec().into())? { + match rest_binding(env, sub, patterns, values.into_vec().into())? { Some((pattern, values)) => { - append_sub(sub, pattern, ConValue::Array(Vec::from(values).into())) + append_sub(env, sub, pattern, ConValue::Array(Vec::from(values).into())) } _ => Ok(()), } } + (Pattern::Array(patterns), ConValue::Slice(head, len)) => { + match rest_binding_ref(env, sub, patterns, head, head + len)? { + Some((pat, head, tail)) => { + append_sub(env, sub, pat, ConValue::Slice(head, tail - head)) + } + None => Ok(()), + } + } + (Pattern::Tuple(patterns), ConValue::Empty) if patterns.is_empty() => Ok(()), (Pattern::Tuple(patterns), ConValue::Tuple(values)) => { - match rest_binding(sub, patterns, values.into_vec().into())? { + match rest_binding(env, sub, patterns, values.into_vec().into())? { Some((pattern, values)) => { - append_sub(sub, pattern, ConValue::Tuple(Vec::from(values).into())) + append_sub(env, sub, pattern, ConValue::Tuple(Vec::from(values).into())) } _ => Ok(()), } } (Pattern::TupleStruct(path, patterns), ConValue::TupleStruct(parts)) => { - let (name, values) = *parts; - if !path.ends_with(name) { - Err(Error::TypeError())? + let (id, values) = *parts; + + let tid = path + .as_sym() + .ok_or_else(|| Error::PatFailed(pat.clone().into()))?; + if id != tid.to_ref() { + return Err(Error::PatFailed(pat.clone().into())); } - match rest_binding(sub, patterns, values.into_vec().into())? { + match rest_binding(env, sub, patterns, values.into_vec().into())? { Some((pattern, values)) => { - append_sub(sub, pattern, ConValue::Tuple(Vec::from(values).into())) + append_sub(env, sub, pattern, ConValue::Tuple(Vec::from(values).into())) } _ => Ok(()), } } (Pattern::Struct(path, patterns), ConValue::Struct(parts)) => { - let (name, mut values) = *parts; - if !path.ends_with(&name) { - Err(Error::TypeError())? + let (id, mut values) = *parts; + let tid = path + .as_sym() + .ok_or_else(|| Error::PatFailed(pat.clone().into()))?; + if id != tid.to_ref() { + return Err(Error::PatFailed(pat.clone().into())); } for (name, pat) in patterns { let value = values.remove(name).ok_or(Error::TypeError())?; match pat { - Some(pat) => append_sub(sub, pat, value)?, + Some(pat) => append_sub(env, sub, pat, value)?, None => { - sub.insert(name, value); + sub.insert(*name, value); } } } @@ -251,8 +319,12 @@ pub fn append_sub<'pat>( } /// Constructs a substitution from a pattern and a value -pub fn substitution(pat: &Pattern, value: ConValue) -> IResult> { +pub fn substitution( + env: &Environment, + pat: &Pattern, + value: ConValue, +) -> IResult> { let mut sub = HashMap::new(); - append_sub(&mut sub, pat, value)?; + append_sub(env, &mut sub, pat, value)?; Ok(sub) } diff --git a/compiler/cl-repl/src/cli.rs b/compiler/cl-repl/src/cli.rs index 3d61807..de2490d 100644 --- a/compiler/cl-repl/src/cli.rs +++ b/compiler/cl-repl/src/cli.rs @@ -23,18 +23,36 @@ pub fn run(args: Args) -> Result<(), Box> { menu::clear(); Ok(ConValue::Empty) } - /// Evaluates a quoted expression - fn eval(ConValue::Quote(quote)) @env { - env.eval(quote.as_ref()) + + fn eval(string) @env { + use cl_interpret::error::Error; + let string = match *string { + ConValue::String(string) => string, + ConValue::Ref(v) => { + let string = env.get_id(v).cloned().unwrap_or_default(); + return eval(env, &[string]) + } + _ => Err(Error::TypeError())? + }; + match Parser::new("eval", Lexer::new(string.to_ref())).parse::() { + Err(e) => Ok(ConValue::String(format!("{e}").into())), + Ok(v) => v.interpret(env), + } } + /// Executes a file fn import(ConValue::String(path)) @env { load_file(env, &**path).or(Ok(ConValue::Empty)) } + fn putchar(ConValue::Char(c)) { + print!("{c}"); + Ok(ConValue::Empty) + } + /// Gets a line of input from stdin - fn get_line() { - match repline::Repline::new("", "", "").read() { + fn get_line(ConValue::String(prompt)) { + match repline::Repline::new("", prompt.to_ref(), "").read() { Ok(line) => Ok(ConValue::String(line.into())), Err(repline::Error::CtrlD(line)) => Ok(ConValue::String(line.into())), Err(repline::Error::CtrlC(_)) => Err(cl_interpret::error::Error::Break(ConValue::Empty)),