cl-interpret: Environment/stack overhaul + Ref patterns
This commit is contained in:
parent
e165e029dc
commit
8b0a122dfc
@ -78,7 +78,8 @@ impl Fold for ConstantFolder {
|
|||||||
Ek::Unary(Unary { kind, tail }) => {
|
Ek::Unary(Unary { kind, tail }) => {
|
||||||
un_rule! (match (kind, self.fold_expr(*tail)) {
|
un_rule! (match (kind, self.fold_expr(*tail)) {
|
||||||
(Not, std::ops::Not::not, Int, Bool),
|
(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),
|
(Neg, |f| (-f64::from_bits(f)).to_bits(), Float),
|
||||||
(At, std::ops::Not::not, Float), /* Lmao */
|
(At, std::ops::Not::not, Float), /* Lmao */
|
||||||
})
|
})
|
||||||
|
@ -154,13 +154,26 @@ pub const Builtins: &[Builtin] = &builtins![
|
|||||||
Ok(())
|
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 {
|
fn builtins() @env {
|
||||||
for builtin in env.globals().values().flatten().filter(|v| matches!(v, ConValue::Builtin(_))) {
|
let len = env.globals().binds.len();
|
||||||
println!("{builtin}")
|
for builtin in 0..len {
|
||||||
|
if let Some(value @ ConValue::Builtin(_)) = env.get_id(builtin) {
|
||||||
|
println!("{builtin}: {value}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
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]
|
/// Returns the length of the input list as a [ConValue::Int]
|
||||||
fn len(list) @env {
|
fn len(list) @env {
|
||||||
Ok(match list {
|
Ok(match list {
|
||||||
@ -169,12 +182,25 @@ pub const Builtins: &[Builtin] = &builtins![
|
|||||||
ConValue::Ref(r) => {
|
ConValue::Ref(r) => {
|
||||||
return len(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
|
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 _,
|
ConValue::Tuple(t) => t.len() as _,
|
||||||
_ => Err(Error::TypeError())?,
|
_ => 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 {
|
fn chars(string) @env {
|
||||||
Ok(match string {
|
Ok(match string {
|
||||||
ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()),
|
ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()),
|
||||||
@ -296,23 +322,30 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn RangeExc(start, end) {
|
fn RangeExc(start, end) @env {
|
||||||
Ok(ConValue::TupleStruct(Box::new((
|
Ok(ConValue::TupleStruct(Box::new((
|
||||||
"RangeExc", Box::new([start.clone(), end.clone()])
|
"RangeExc", Box::new([start.clone(), end.clone()])
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn RangeInc(start, end) {
|
fn RangeInc(start, end) @env {
|
||||||
Ok(ConValue::TupleStruct(Box::new((
|
Ok(ConValue::TupleStruct(Box::new((
|
||||||
"RangeInc", Box::new([start.clone(), end.clone()])
|
"RangeInc", Box::new([start.clone(), end.clone()])
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn RangeTo(end) {
|
fn RangeTo(end) @env {
|
||||||
Ok(ConValue::TupleStruct(Box::new((
|
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()])
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use std::{collections::HashMap, fmt::Display};
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Closure {
|
pub struct Closure {
|
||||||
decl: cl_ast::Closure,
|
decl: cl_ast::Closure,
|
||||||
lift: HashMap<Sym, Option<ConValue>>,
|
lift: HashMap<Sym, ConValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Closure {
|
impl Closure {
|
||||||
@ -48,8 +48,8 @@ impl Callable for Closure {
|
|||||||
|
|
||||||
let mut env = env.frame("args");
|
let mut env = env.frame("args");
|
||||||
|
|
||||||
for (name, value) in pattern::substitution(&decl.arg, ConValue::Tuple(args.into()))? {
|
for (name, value) in pattern::substitution(&env, &decl.arg, ConValue::Tuple(args.into()))? {
|
||||||
env.insert(*name, Some(value));
|
env.insert(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = decl.body.interpret(&mut env);
|
let res = decl.body.interpret(&mut env);
|
||||||
@ -63,6 +63,6 @@ impl Callable for Closure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> cl_ast::Sym {
|
fn name(&self) -> cl_ast::Sym {
|
||||||
"{closure}".into()
|
Self::NAME.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
//! The most permanent fix is a temporary one.
|
//! The most permanent fix is a temporary one.
|
||||||
use cl_ast::{Expr, Sym, format::FmtAdapter};
|
use cl_ast::{Expr, Sym, format::FmtAdapter};
|
||||||
|
|
||||||
use crate::{closure::Closure, env::Place};
|
use crate::{closure::Closure, constructor::Constructor};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Callable, Environment,
|
Callable, Environment,
|
||||||
@ -54,23 +54,27 @@ pub enum ConValue {
|
|||||||
/// A string
|
/// A string
|
||||||
String(Sym),
|
String(Sym),
|
||||||
/// A reference
|
/// A reference
|
||||||
Ref(Place),
|
Ref(usize),
|
||||||
/// A reference to an array
|
/// A reference to an array
|
||||||
Slice(Place, usize),
|
Slice(usize, usize),
|
||||||
/// An Array
|
/// An Array
|
||||||
Array(Box<[ConValue]>),
|
Array(Box<[ConValue]>),
|
||||||
/// A tuple
|
/// A tuple
|
||||||
Tuple(Box<[ConValue]>),
|
Tuple(Box<[ConValue]>),
|
||||||
/// A value of a product type
|
/// A value of a product type
|
||||||
Struct(Box<(Sym, HashMap<Sym, ConValue>)>),
|
Struct(Box<(&'static str, HashMap<Sym, ConValue>)>),
|
||||||
/// A value of a product type with anonymous members
|
/// A value of a product type with anonymous members
|
||||||
TupleStruct(Box<(&'static str, Box<[ConValue]>)>),
|
TupleStruct(Box<(&'static str, Box<[ConValue]>)>),
|
||||||
/// An entire namespace
|
/// An entire namespace
|
||||||
Module(Box<HashMap<Sym, Option<ConValue>>>),
|
Module(Box<HashMap<Sym, ConValue>>),
|
||||||
|
/// A namespace, sans storage
|
||||||
|
Module2(HashMap<Sym, usize>),
|
||||||
/// A quoted expression
|
/// A quoted expression
|
||||||
Quote(Box<Expr>),
|
Quote(Box<Expr>),
|
||||||
/// A callable thing
|
/// A callable thing
|
||||||
Function(Rc<Function>),
|
Function(Rc<Function>),
|
||||||
|
/// A tuple constructor
|
||||||
|
TupleConstructor(Constructor),
|
||||||
/// A closure, capturing by reference
|
/// A closure, capturing by reference
|
||||||
Closure(Rc<Closure>),
|
Closure(Rc<Closure>),
|
||||||
/// A built-in function
|
/// A built-in function
|
||||||
@ -86,33 +90,65 @@ impl ConValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
pub fn typename(&self) -> IResult<&'static str> {
|
||||||
pub fn TupleStruct(name: Sym, values: Box<[ConValue]>) -> Self {
|
Ok(match self {
|
||||||
Self::TupleStruct(Box::new((name.to_ref(), values)))
|
ConValue::Empty => "Empty",
|
||||||
}
|
ConValue::Int(_) => "i64",
|
||||||
#[allow(non_snake_case)]
|
ConValue::Float(_) => "f64",
|
||||||
pub fn Struct(name: Sym, values: HashMap<Sym, ConValue>) -> Self {
|
ConValue::Bool(_) => "bool",
|
||||||
Self::Struct(Box::new((name, values)))
|
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<ConValue> {
|
#[allow(non_snake_case)]
|
||||||
let Self::Int(index) = index else {
|
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<Sym, ConValue>) -> Self {
|
||||||
|
Self::Struct(Box::new((id.to_ref(), values)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index(&self, index: &Self, _env: &Environment) -> IResult<ConValue> {
|
||||||
|
let &Self::Int(index) = index else {
|
||||||
Err(Error::TypeError())?
|
Err(Error::TypeError())?
|
||||||
};
|
};
|
||||||
match self {
|
match self {
|
||||||
ConValue::String(string) => string
|
ConValue::String(string) => string
|
||||||
.chars()
|
.chars()
|
||||||
.nth(*index as _)
|
.nth(index as _)
|
||||||
.map(ConValue::Char)
|
.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
|
ConValue::Array(arr) => arr
|
||||||
.get(*index as usize)
|
.get(index as usize)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or(Error::OobIndex(*index as usize, arr.len())),
|
.ok_or(Error::OobIndex(index as usize, arr.len())),
|
||||||
ConValue::Slice(id, start) => env
|
&ConValue::Slice(id, len) => {
|
||||||
.get_id(*id)
|
let index = if index < 0 {
|
||||||
.ok_or(Error::StackOverflow(*id))?
|
len.wrapping_add_signed(index)
|
||||||
.index(&ConValue::Int((*index as usize + start) as isize), env),
|
} else {
|
||||||
|
index as usize
|
||||||
|
};
|
||||||
|
if index < len {
|
||||||
|
Ok(ConValue::Ref(id + index))
|
||||||
|
} else {
|
||||||
|
Err(Error::OobIndex(index, len))
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => Err(Error::TypeError()),
|
_ => Err(Error::TypeError()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,11 +183,24 @@ impl Callable for ConValue {
|
|||||||
_ => "".into(),
|
_ => "".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
match self {
|
match self {
|
||||||
Self::Function(func) => func.call(interpreter, args),
|
Self::Function(func) => func.call(env, args),
|
||||||
Self::Closure(func) => func.call(interpreter, args),
|
Self::TupleConstructor(func) => func.call(env, args),
|
||||||
Self::Builtin(func) => func.call(interpreter, 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())),
|
_ => Err(Error::NotCallable(self.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,7 +356,7 @@ impl std::fmt::Display for ConValue {
|
|||||||
ConValue::Char(v) => v.fmt(f),
|
ConValue::Char(v) => v.fmt(f),
|
||||||
ConValue::String(v) => v.fmt(f),
|
ConValue::String(v) => v.fmt(f),
|
||||||
ConValue::Ref(v) => write!(f, "&<{}>", v),
|
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) => {
|
ConValue::Array(array) => {
|
||||||
'['.fmt(f)?;
|
'['.fmt(f)?;
|
||||||
for (idx, element) in array.iter().enumerate() {
|
for (idx, element) in array.iter().enumerate() {
|
||||||
@ -329,10 +378,8 @@ impl std::fmt::Display for ConValue {
|
|||||||
')'.fmt(f)
|
')'.fmt(f)
|
||||||
}
|
}
|
||||||
ConValue::TupleStruct(parts) => {
|
ConValue::TupleStruct(parts) => {
|
||||||
let (name, tuple) = parts.as_ref();
|
let (id, tuple) = parts.as_ref();
|
||||||
if !name.is_empty() {
|
write!(f, "{id}")?;
|
||||||
write!(f, "{name}")?;
|
|
||||||
}
|
|
||||||
'('.fmt(f)?;
|
'('.fmt(f)?;
|
||||||
for (idx, element) in tuple.iter().enumerate() {
|
for (idx, element) in tuple.iter().enumerate() {
|
||||||
if idx > 0 {
|
if idx > 0 {
|
||||||
@ -343,11 +390,9 @@ impl std::fmt::Display for ConValue {
|
|||||||
')'.fmt(f)
|
')'.fmt(f)
|
||||||
}
|
}
|
||||||
ConValue::Struct(parts) => {
|
ConValue::Struct(parts) => {
|
||||||
let (name, map) = parts.as_ref();
|
let (id, map) = parts.as_ref();
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
if !name.is_empty() {
|
write!(f, "{id} ")?;
|
||||||
write!(f, "{name} ")?;
|
|
||||||
}
|
|
||||||
let mut f = f.delimit_with("{", "\n}");
|
let mut f = f.delimit_with("{", "\n}");
|
||||||
for (k, v) in map.iter() {
|
for (k, v) in map.iter() {
|
||||||
write!(f, "\n{k}: {v},")?;
|
write!(f, "\n{k}: {v},")?;
|
||||||
@ -358,11 +403,15 @@ impl std::fmt::Display for ConValue {
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
let mut f = f.delimit_with("{", "\n}");
|
let mut f = f.delimit_with("{", "\n}");
|
||||||
for (k, v) in module.iter() {
|
for (k, v) in module.iter() {
|
||||||
write!(f, "\n{k}: ")?;
|
write!(f, "\n{k}: {v},")?;
|
||||||
match v {
|
}
|
||||||
Some(v) => write!(f, "{v},"),
|
Ok(())
|
||||||
None => write!(f, "_,"),
|
}
|
||||||
}?
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -372,6 +421,9 @@ impl std::fmt::Display for ConValue {
|
|||||||
ConValue::Function(func) => {
|
ConValue::Function(func) => {
|
||||||
write!(f, "{}", func.decl())
|
write!(f, "{}", func.decl())
|
||||||
}
|
}
|
||||||
|
ConValue::TupleConstructor(Constructor { name: index, arity }) => {
|
||||||
|
write!(f, "{index}(..{arity})")
|
||||||
|
}
|
||||||
ConValue::Closure(func) => {
|
ConValue::Closure(func) => {
|
||||||
write!(f, "{}", func.as_ref())
|
write!(f, "{}", func.as_ref())
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Lexical and non-lexical scoping for variables
|
//! Lexical and non-lexical scoping for variables
|
||||||
|
|
||||||
use crate::builtin::Builtin;
|
use crate::{builtin::Builtin, constructor::Constructor, modules::ModuleTree};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Callable, Interpret,
|
Callable, Interpret,
|
||||||
@ -17,37 +17,25 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
type StackFrame = HashMap<Sym, Option<ConValue>>;
|
pub type StackFrame = HashMap<Sym, ConValue>;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
pub type StackBinds = HashMap<Sym, usize>;
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct EnvFrame {
|
pub(crate) struct EnvFrame {
|
||||||
/// The length of the array when this stack frame was constructed
|
|
||||||
pub name: Option<&'static str>,
|
pub name: Option<&'static str>,
|
||||||
|
/// The length of the array when this stack frame was constructed
|
||||||
pub base: usize,
|
pub base: usize,
|
||||||
pub binds: HashMap<Sym, usize>,
|
/// The bindings of name to stack position
|
||||||
|
pub binds: StackBinds,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements a nested lexical scope
|
/// Implements a nested lexical scope
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
global: HashMap<Sym, Option<ConValue>>,
|
values: Vec<ConValue>,
|
||||||
values: Vec<Option<ConValue>>,
|
|
||||||
frames: Vec<EnvFrame>,
|
frames: Vec<EnvFrame>,
|
||||||
|
modules: ModuleTree,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Environment {
|
impl Display for Environment {
|
||||||
@ -55,21 +43,24 @@ impl Display for Environment {
|
|||||||
for EnvFrame { name, base: _, binds } in self.frames.iter().rev() {
|
for EnvFrame { name, base: _, binds } in self.frames.iter().rev() {
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"--- {} ---",
|
"--- {}[{}] ---",
|
||||||
if let Some(name) = name { name } else { "" }
|
if let Some(name) = name { name } else { "" },
|
||||||
|
binds.len(),
|
||||||
)?;
|
)?;
|
||||||
for (var, val) in binds {
|
let mut binds: Vec<_> = binds.iter().collect();
|
||||||
write!(f, "{var}: ")?;
|
binds.sort_by(|(_, a), (_, b)| a.cmp(b));
|
||||||
match self.values.get(*val) {
|
for (name, idx) in binds {
|
||||||
Some(Some(value)) => writeln!(f, "\t{value}"),
|
write!(f, "{idx:4} {name}: ")?;
|
||||||
Some(None) => writeln!(f, "<undefined>"),
|
match self.values.get(*idx) {
|
||||||
None => writeln!(f, "ERROR: {var} address blows the stack!"),
|
Some(value) => writeln!(f, "\t{value}"),
|
||||||
|
None => writeln!(f, "ERROR: {name}'s address blows the stack!"),
|
||||||
}?
|
}?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Environment {
|
impl Default for Environment {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut this = Self::no_builtins();
|
let mut this = Self::no_builtins();
|
||||||
@ -84,7 +75,11 @@ impl Environment {
|
|||||||
}
|
}
|
||||||
/// Creates an [Environment] with no [builtins](super::builtin)
|
/// Creates an [Environment] with no [builtins](super::builtin)
|
||||||
pub fn no_builtins() -> Self {
|
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
|
/// Reflexively evaluates a node
|
||||||
@ -92,38 +87,60 @@ impl Environment {
|
|||||||
node.interpret(self)
|
node.interpret(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a function inside the interpreter's scope,
|
/// Calls a function inside the Environment's scope,
|
||||||
/// and returns the result
|
/// and returns the result
|
||||||
pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> {
|
pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
let function = self.get(name)?;
|
let function = self.get(name)?;
|
||||||
function.call(self, args)
|
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.
|
/// Binds a value to the given name in the current scope.
|
||||||
pub fn bind(&mut self, name: &str, value: impl Into<ConValue>) {
|
pub fn bind(&mut self, name: impl Into<Sym>, value: impl Into<ConValue>) {
|
||||||
self.insert(name.into(), Some(value.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.
|
/// Gets all registered globals, bound or unbound.
|
||||||
pub fn globals(&self) -> &HashMap<Sym, Option<ConValue>> {
|
pub(crate) fn globals(&self) -> &EnvFrame {
|
||||||
&self.global
|
self.frames.first().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds builtins
|
/// Adds builtins
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # 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 {
|
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) -> &mut Self {
|
||||||
let Self { global, .. } = self;
|
if self.frames.len() != 1 {
|
||||||
for builtin in builtins {
|
panic!("Cannot add builtins to full stack: {self}")
|
||||||
global.insert(builtin.name(), Some(builtin.into()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for builtin in builtins {
|
||||||
|
self.insert(builtin.name(), builtin.into());
|
||||||
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
|
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 {
|
for (k, v) in frame {
|
||||||
self.insert(k, v);
|
self.insert(k, v);
|
||||||
}
|
}
|
||||||
@ -133,7 +150,7 @@ impl Environment {
|
|||||||
let mut out = HashMap::new();
|
let mut out = HashMap::new();
|
||||||
let EnvFrame { name, base, binds } = self.frames.pop()?;
|
let EnvFrame { name, base, binds } = self.frames.pop()?;
|
||||||
for (k, v) in binds {
|
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);
|
self.values.truncate(base);
|
||||||
Some((out, name.unwrap_or("")))
|
Some((out, name.unwrap_or("")))
|
||||||
@ -146,10 +163,22 @@ impl Environment {
|
|||||||
Frame::new(self, name)
|
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.
|
/// Resolves a variable mutably.
|
||||||
///
|
///
|
||||||
/// Returns a mutable reference to the variable's record, if it exists.
|
/// Returns a mutable reference to the variable's record, if it exists.
|
||||||
pub fn get_mut(&mut self, name: Sym) -> IResult<&mut Option<ConValue>> {
|
pub fn get_mut(&mut self, name: Sym) -> IResult<&mut ConValue> {
|
||||||
let at = self.id_of(name)?;
|
let at = self.id_of(name)?;
|
||||||
self.get_id_mut(at).ok_or(Error::NotDefined(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.
|
/// Returns a reference to the variable's contents, if it is defined and initialized.
|
||||||
pub fn get(&self, name: Sym) -> IResult<ConValue> {
|
pub fn get(&self, name: Sym) -> IResult<ConValue> {
|
||||||
let id = self.id_of(name)?;
|
let id = self.id_of(name)?;
|
||||||
let res = match id {
|
let res = self.values.get(id);
|
||||||
Place::Global(name) => self.global.get(&name),
|
Ok(res.ok_or(Error::NotDefined(name))?.clone())
|
||||||
Place::Local(id) => self.values.get(id),
|
|
||||||
};
|
|
||||||
match res.ok_or(Error::NotDefined(name))? {
|
|
||||||
Some(value) => Ok(value.clone()),
|
|
||||||
None => Err(Error::NotInitialized(name)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the [Place] associated with a [Sym]
|
/// Resolves the index associated with a [Sym]
|
||||||
pub fn id_of(&self, name: Sym) -> IResult<Place> {
|
pub fn id_of(&self, name: Sym) -> IResult<usize> {
|
||||||
for EnvFrame { binds, .. } in self.frames.iter().rev() {
|
for EnvFrame { binds, .. } in self.frames.iter().rev() {
|
||||||
if let Some(id) = binds.get(&name).copied() {
|
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> {
|
pub fn get_id(&self, id: usize) -> Option<&ConValue> {
|
||||||
let res = match at {
|
self.values.get(id)
|
||||||
Place::Global(name) => self.global.get(&name),
|
|
||||||
Place::Local(id) => self.values.get(id),
|
|
||||||
}?;
|
|
||||||
res.as_ref()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_id_mut(&mut self, at: Place) -> Option<&mut Option<ConValue>> {
|
pub fn get_id_mut(&mut self, id: usize) -> Option<&mut ConValue> {
|
||||||
match at {
|
self.values.get_mut(id)
|
||||||
Place::Global(name) => self.global.get_mut(&name),
|
}
|
||||||
Place::Local(id) => 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]
|
/// Inserts a new [ConValue] into this [Environment]
|
||||||
pub fn insert(&mut self, k: Sym, v: Option<ConValue>) {
|
pub fn insert(&mut self, k: Sym, v: ConValue) {
|
||||||
if self.bind_raw(k, self.values.len()).is_some() {
|
if self.bind_raw(k, self.values.len()).is_some() {
|
||||||
self.values.push(v);
|
self.values.push(v);
|
||||||
} else {
|
|
||||||
self.global.insert(k, v);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,42 +229,33 @@ impl Environment {
|
|||||||
pub fn insert_fn(&mut self, decl: &FnDecl) {
|
pub fn insert_fn(&mut self, decl: &FnDecl) {
|
||||||
let FnDecl { name, .. } = decl;
|
let FnDecl { name, .. } = decl;
|
||||||
let (name, function) = (*name, Rc::new(Function::new(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
|
// Tell the function to lift its upvars now, after it's been declared
|
||||||
function.lift_upvars(self);
|
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
|
/// Allocates a local variable
|
||||||
pub fn stack_alloc(&mut self, value: ConValue) -> IResult<usize> {
|
pub fn stack_alloc(&mut self, value: ConValue) -> IResult<usize> {
|
||||||
let adr = self.values.len();
|
let adr = self.values.len();
|
||||||
self.values.push(Some(value));
|
self.values.push(value);
|
||||||
Ok(adr)
|
Ok(adr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_raw(&mut self, name: Sym, id: usize) -> Option<()> {
|
/// Allocates some space on the stack
|
||||||
let EnvFrame { name: _, base: _, binds } = self.frames.last_mut()?;
|
pub fn alloca(&mut self, value: ConValue, len: usize) -> ConValue {
|
||||||
binds.insert(name, id);
|
let idx = self.values.len();
|
||||||
Some(())
|
self.values.extend(std::iter::repeat_n(value, len));
|
||||||
}
|
ConValue::Slice(idx, len)
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +266,28 @@ pub struct Frame<'scope> {
|
|||||||
}
|
}
|
||||||
impl<'scope> Frame<'scope> {
|
impl<'scope> Frame<'scope> {
|
||||||
fn new(scope: &'scope mut Environment, name: &'static str) -> Self {
|
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<StackFrame> {
|
||||||
|
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<StackBinds> {
|
||||||
|
let EnvFrame { name: _, base: _, binds } = self.frames.pop()?;
|
||||||
|
std::mem::forget(self);
|
||||||
|
Some(binds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Deref for Frame<'_> {
|
impl Deref for Frame<'_> {
|
||||||
@ -269,6 +303,8 @@ impl DerefMut for Frame<'_> {
|
|||||||
}
|
}
|
||||||
impl Drop for Frame<'_> {
|
impl Drop for Frame<'_> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.scope.exit();
|
if let Some(frame) = self.frames.pop() {
|
||||||
|
self.values.truncate(frame.base);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use cl_ast::{Pattern, Sym};
|
use cl_ast::{Pattern, Sym};
|
||||||
use cl_structures::span::Span;
|
use cl_structures::span::Span;
|
||||||
|
|
||||||
use super::{convalue::ConValue, env::Place};
|
use super::convalue::ConValue;
|
||||||
|
|
||||||
pub type IResult<T> = Result<T, Error>;
|
pub type IResult<T> = Result<T, Error>;
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ impl Error {
|
|||||||
Self { kind: ErrorKind::StackUnderflow, span: None }
|
Self { kind: ErrorKind::StackUnderflow, span: None }
|
||||||
}
|
}
|
||||||
/// Overflowed the stack
|
/// Overflowed the stack
|
||||||
pub fn StackOverflow(place: Place) -> Self {
|
pub fn StackOverflow(place: usize) -> Self {
|
||||||
Self { kind: ErrorKind::StackOverflow(place), span: None }
|
Self { kind: ErrorKind::StackOverflow(place), span: None }
|
||||||
}
|
}
|
||||||
/// Exited the last scope
|
/// Exited the last scope
|
||||||
@ -129,7 +129,7 @@ pub enum ErrorKind {
|
|||||||
/// Underflowed the stack
|
/// Underflowed the stack
|
||||||
StackUnderflow,
|
StackUnderflow,
|
||||||
/// Overflowed the stack
|
/// Overflowed the stack
|
||||||
StackOverflow(Place),
|
StackOverflow(usize),
|
||||||
/// Exited the last scope
|
/// Exited the last scope
|
||||||
ScopeExit,
|
ScopeExit,
|
||||||
/// Type incompatibility
|
/// Type incompatibility
|
||||||
|
@ -14,7 +14,7 @@ use std::{
|
|||||||
|
|
||||||
pub mod collect_upvars;
|
pub mod collect_upvars;
|
||||||
|
|
||||||
type Upvars = HashMap<Sym, Option<ConValue>>;
|
type Upvars = HashMap<Sym, ConValue>;
|
||||||
|
|
||||||
/// Represents a block of code which persists inside the Interpreter
|
/// Represents a block of code which persists inside the Interpreter
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -23,16 +23,12 @@ 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(), is_constructor: false }
|
Self { decl: decl.clone().into(), upvars: Default::default() }
|
||||||
}
|
|
||||||
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
|
||||||
@ -57,27 +53,21 @@ impl Callable for Function {
|
|||||||
let FnDecl { name, gens: _, bind, body, sign: _ } = &*self.decl;
|
let FnDecl { name, gens: _, bind, body, sign: _ } = &*self.decl;
|
||||||
|
|
||||||
// Check arg mapping
|
// Check arg mapping
|
||||||
if self.is_constructor {
|
|
||||||
return Ok(ConValue::TupleStruct(Box::new((
|
|
||||||
name.to_ref(),
|
|
||||||
args.into(),
|
|
||||||
))));
|
|
||||||
}
|
|
||||||
let Some(body) = body else {
|
let Some(body) = body else {
|
||||||
return Err(Error::NotDefined(*name));
|
return Err(Error::NotDefined(*name));
|
||||||
};
|
};
|
||||||
|
|
||||||
let upvars = self.upvars.take();
|
let upvars = self.upvars.take();
|
||||||
env.push_frame("upvars", upvars);
|
let mut env = env.with_frame("upvars", upvars);
|
||||||
|
|
||||||
// TODO: completely refactor data storage
|
// TODO: completely refactor data storage
|
||||||
let mut frame = env.frame("fn args");
|
let mut frame = env.frame("fn args");
|
||||||
for (name, value) in pattern::substitution(bind, ConValue::Tuple(args.into()))? {
|
for (name, value) in pattern::substitution(&frame, bind, ConValue::Tuple(args.into()))? {
|
||||||
frame.insert(*name, Some(value));
|
frame.insert(name, value);
|
||||||
}
|
}
|
||||||
let res = body.interpret(&mut frame);
|
let res = body.interpret(&mut frame);
|
||||||
drop(frame);
|
drop(frame);
|
||||||
if let Some((upvars, _)) = env.pop_frame() {
|
if let Some(upvars) = env.pop_values() {
|
||||||
self.upvars.replace(upvars);
|
self.upvars.replace(upvars);
|
||||||
}
|
}
|
||||||
match res {
|
match res {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//! Collects the "Upvars" of a function at the point of its creation, allowing variable capture
|
//! 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::{
|
use cl_ast::{
|
||||||
Function, Let, Path, PathPart, Pattern, Sym,
|
Function, Let, Path, PathPart, Pattern, Sym,
|
||||||
ast_visitor::{visit::*, walk::Walk},
|
ast_visitor::{visit::*, walk::Walk},
|
||||||
@ -13,7 +13,7 @@ pub fn collect_upvars(f: &Function, env: &Environment) -> super::Upvars {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CollectUpvars<'env> {
|
pub struct CollectUpvars<'env> {
|
||||||
env: &'env Environment,
|
env: &'env Environment,
|
||||||
upvars: HashMap<Sym, Place>,
|
upvars: HashMap<Sym, usize>,
|
||||||
blacklist: HashSet<Sym>,
|
blacklist: HashSet<Sym>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ impl<'env> CollectUpvars<'env> {
|
|||||||
Self { upvars: HashMap::new(), blacklist: HashSet::new(), env }
|
Self { upvars: HashMap::new(), blacklist: HashSet::new(), env }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(&mut self) -> HashMap<Sym, Place> {
|
pub fn finish(&mut self) -> HashMap<Sym, usize> {
|
||||||
std::mem::take(&mut self.upvars)
|
std::mem::take(&mut self.upvars)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ impl<'env> CollectUpvars<'env> {
|
|||||||
let Self { env, upvars, blacklist: _ } = self;
|
let Self { env, upvars, blacklist: _ } = self;
|
||||||
std::mem::take(upvars)
|
std::mem::take(upvars)
|
||||||
.into_iter()
|
.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()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,17 +47,21 @@ impl<'env> CollectUpvars<'env> {
|
|||||||
pub fn bind_name(&mut self, name: &Sym) {
|
pub fn bind_name(&mut self, name: &Sym) {
|
||||||
self.blacklist.insert(*name);
|
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<'_> {
|
impl<'a> Visit<'a> for CollectUpvars<'_> {
|
||||||
fn visit_block(&mut self, b: &'a cl_ast::Block) {
|
fn visit_block(&mut self, b: &'a cl_ast::Block) {
|
||||||
let blacklist = self.blacklist.clone();
|
self.scope(|cu| b.children(cu));
|
||||||
|
|
||||||
// visit the block
|
|
||||||
b.children(self);
|
|
||||||
|
|
||||||
// restore the blacklist
|
|
||||||
self.blacklist = blacklist;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_let(&mut self, l: &'a cl_ast::Let) {
|
fn visit_let(&mut self, l: &'a cl_ast::Let) {
|
||||||
@ -71,21 +75,6 @@ impl<'a> Visit<'a> for CollectUpvars<'_> {
|
|||||||
self.visit_pattern(name);
|
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) {
|
fn visit_path(&mut self, p: &'a cl_ast::Path) {
|
||||||
// TODO: path resolution in environments
|
// TODO: path resolution in environments
|
||||||
let Path { absolute: false, parts } = p else {
|
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) {
|
fn visit_pattern(&mut self, value: &'a cl_ast::Pattern) {
|
||||||
match p {
|
match value {
|
||||||
Pattern::Name(name) => {
|
Pattern::Name(name) => {
|
||||||
self.bind_name(name);
|
self.bind_name(name);
|
||||||
}
|
}
|
||||||
Pattern::RangeExc(_, _) | Pattern::RangeInc(_, _) => {}
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,14 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use cl_ast::{ast_visitor::Visit, *};
|
use cl_ast::{ast_visitor::Visit, *};
|
||||||
use std::borrow::Borrow;
|
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
|
/// A work-in-progress tree walk interpreter for Conlang
|
||||||
pub trait Interpret {
|
pub trait Interpret {
|
||||||
/// Interprets this thing in the given [`Environment`].
|
/// Interprets this thing in the given [`Environment`].
|
||||||
@ -18,6 +26,7 @@ pub trait Interpret {
|
|||||||
|
|
||||||
impl Interpret for File {
|
impl Interpret for File {
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
|
trace!("// Running {}", self.name);
|
||||||
/// Sorts items
|
/// Sorts items
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct ItemSorter<'ast>(pub [Vec<&'ast Item>; 8]);
|
struct ItemSorter<'ast>(pub [Vec<&'ast Item>; 8]);
|
||||||
@ -45,6 +54,7 @@ impl Interpret for File {
|
|||||||
Ok(ConValue::Empty)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for Item {
|
impl Interpret for Item {
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
@ -60,139 +70,157 @@ impl Interpret for Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for Alias {
|
impl Interpret for Alias {
|
||||||
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
|
||||||
println!("TODO: {self}");
|
trace!("// TODO: {self}");
|
||||||
Ok(ConValue::Empty)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for Const {
|
impl Interpret for Const {
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
let Const { name, ty: _, init } = self;
|
let Const { name, ty: _, init } = self;
|
||||||
|
trace!("// Defining const {name}");
|
||||||
|
|
||||||
let init = init.as_ref().interpret(env)?;
|
let init = init.as_ref().interpret(env)?;
|
||||||
env.insert(*name, Some(init));
|
env.insert(*name, init);
|
||||||
Ok(ConValue::Empty)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for Static {
|
impl Interpret for Static {
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
let Static { mutable: _, name, ty: _, init } = self;
|
let Static { mutable: _, name, ty: _, init } = self;
|
||||||
|
trace!("// Defining static {name}");
|
||||||
|
|
||||||
let init = init.as_ref().interpret(env)?;
|
let init = init.as_ref().interpret(env)?;
|
||||||
env.insert(*name, Some(init));
|
env.insert(*name, init);
|
||||||
Ok(ConValue::Empty)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for Module {
|
impl Interpret for Module {
|
||||||
// TODO: Keep modules around somehow, rather than putting them on the stack
|
// TODO: Keep modules around somehow, rather than putting them on the stack
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
let Self { name, file } = self;
|
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 {
|
let out = match file {
|
||||||
Some(file) => file.interpret(env),
|
Some(file) => file.interpret(&mut scope),
|
||||||
None => {
|
None => {
|
||||||
eprintln!("Module {name} specified, but not imported.");
|
eprintln!("Module {name} specified, but not imported.");
|
||||||
Ok(ConValue::Empty)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (frame, _) = env
|
let frame = scope
|
||||||
.pop_frame()
|
.pop_values()
|
||||||
.expect("Environment frames must be balanced");
|
.expect("Environment frames must be balanced");
|
||||||
env.insert(*name, Some(ConValue::Module(frame.into())));
|
env.insert(*name, ConValue::Module(frame.into()));
|
||||||
|
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for Function {
|
impl Interpret for Function {
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
|
trace!("// Defining fn {}", self.name);
|
||||||
|
|
||||||
// register the function in the current environment
|
// register the function in the current environment
|
||||||
env.insert_fn(self);
|
env.insert_fn(self);
|
||||||
Ok(ConValue::Empty)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for Struct {
|
impl Interpret for Struct {
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
let Self { name, gens: _, kind } = self;
|
let Self { name, gens: _, kind } = self;
|
||||||
|
trace!("// Defining struct {name}");
|
||||||
|
|
||||||
|
let mut frame = env.frame(name.to_ref());
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
StructKind::Empty => {}
|
StructKind::Empty => {
|
||||||
|
frame.insert_tup_constructor("call".into(), 0);
|
||||||
|
frame.insert("__nmemb".into(), ConValue::Int(0));
|
||||||
|
}
|
||||||
StructKind::Tuple(args) => {
|
StructKind::Tuple(args) => {
|
||||||
// Constructs the AST from scratch. TODO: This, better.
|
// Constructs the AST from scratch. TODO: This, better.
|
||||||
let constructor = Function {
|
frame.insert_tup_constructor("call".into(), args.len());
|
||||||
name: *name,
|
frame.insert("__nmemb".into(), ConValue::Int(args.len() as _));
|
||||||
gens: Default::default(),
|
}
|
||||||
sign: TyFn {
|
StructKind::Struct(members) => {
|
||||||
args: TyKind::Tuple(TyTuple {
|
// TODO: more precise type checking of structs
|
||||||
types: args.iter().map(|ty| ty.kind.clone()).collect(),
|
for (idx, StructMember { vis: _, name: nm, ty: _ }) in members.iter().enumerate() {
|
||||||
})
|
trace!("// Defining {name}::{nm}");
|
||||||
.into(),
|
frame.insert(*nm, ConValue::Int(idx as _));
|
||||||
rety: Some(
|
}
|
||||||
Ty {
|
frame.insert("__nmemb".into(), ConValue::Int(members.len() as _));
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
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)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for Enum {
|
impl Interpret for Enum {
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
let Self { name, gens: _, variants } = self;
|
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() {
|
for (idx, Variant { name, kind, body }) in variants.iter().enumerate() {
|
||||||
match (kind, body) {
|
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)) => {
|
(StructKind::Empty, Some(idx)) => {
|
||||||
let idx = idx.interpret(env)?;
|
let idx = idx.interpret(&mut scope)?;
|
||||||
env.insert(*name, Some(idx))
|
scope.insert(*name, idx)
|
||||||
|
}
|
||||||
|
(StructKind::Tuple(args), None) => {
|
||||||
|
scope.insert_tup_constructor(*name, args.len());
|
||||||
}
|
}
|
||||||
(StructKind::Tuple(_), None) => {}
|
|
||||||
(StructKind::Struct(_), None) => {}
|
(StructKind::Struct(_), None) => {}
|
||||||
_ => eprintln!("Well-formedness error in {self}"),
|
_ => eprintln!("Well-formedness error in {self}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (frame, _) = env
|
let frame = scope
|
||||||
.pop_frame()
|
.pop_values()
|
||||||
.expect("Frame stack should remain balanced.");
|
.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)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for Impl {
|
impl Interpret for Impl {
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
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 {
|
else {
|
||||||
eprintln!("TODO: impl X for Ty");
|
trace!("TODO: impl X for Ty");
|
||||||
return Ok(ConValue::Empty);
|
return Ok(ConValue::Empty);
|
||||||
};
|
};
|
||||||
env.push_frame("impl", Default::default());
|
let mut frame = env.frame("impl");
|
||||||
body.interpret(env)?;
|
body.interpret(&mut frame)?;
|
||||||
|
|
||||||
let (frame, _) = env
|
let frame = frame
|
||||||
.pop_frame()
|
.pop_values()
|
||||||
.expect("Environment frames must be balanced");
|
.expect("Environment frames must be balanced");
|
||||||
match assignment::addrof_path(env, name.parts.as_slice())
|
match assignment::addrof_path(env, name.parts.as_slice())
|
||||||
.map_err(|err| err.with_span(*span))?
|
.map_err(|err| err.with_span(*span))?
|
||||||
{
|
{
|
||||||
Some(ConValue::Module(m)) => m.extend(frame),
|
ConValue::Module(m) => m.extend(frame),
|
||||||
Some(other) => eprintln!("TODO: impl for {other}"),
|
other => eprintln!("TODO: impl for {other}"),
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
Ok(ConValue::Empty)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
@ -226,9 +254,8 @@ impl Interpret for UseTree {
|
|||||||
let Ok(ConValue::Module(m)) = env.get(*name) else {
|
let Ok(ConValue::Module(m)) = env.get(*name) else {
|
||||||
Err(Error::TypeError())?
|
Err(Error::TypeError())?
|
||||||
};
|
};
|
||||||
env.push_frame(name.to_ref(), *m);
|
let mut scope = env.with_frame(name.to_ref(), *m);
|
||||||
let out = get_bindings(tree, env, bindings);
|
let out = get_bindings(tree, &mut scope, bindings);
|
||||||
env.pop_frame();
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
UseTree::Alias(name, alias) => {
|
UseTree::Alias(name, alias) => {
|
||||||
@ -238,11 +265,10 @@ impl Interpret for UseTree {
|
|||||||
bindings.insert(*name, env.get(*name)?);
|
bindings.insert(*name, env.get(*name)?);
|
||||||
}
|
}
|
||||||
UseTree::Glob => {
|
UseTree::Glob => {
|
||||||
|
trace!("TODO: Improve glob imports");
|
||||||
if let Some((frame, name)) = env.pop_frame() {
|
if let Some((frame, name)) = env.pop_frame() {
|
||||||
for (k, v) in &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);
|
env.push_frame(name, frame);
|
||||||
}
|
}
|
||||||
@ -258,7 +284,7 @@ impl Interpret for UseTree {
|
|||||||
get_bindings(self, env, &mut bindings)?;
|
get_bindings(self, env, &mut bindings)?;
|
||||||
|
|
||||||
for (name, value) in bindings {
|
for (name, value) in bindings {
|
||||||
env.insert(name, Some(value));
|
env.insert(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ConValue::Empty)
|
Ok(ConValue::Empty)
|
||||||
@ -343,16 +369,19 @@ impl Interpret for Let {
|
|||||||
let Let { mutable: _, name, ty: _, init } = self;
|
let Let { mutable: _, name, ty: _, init } = self;
|
||||||
match init.as_ref().map(|i| i.interpret(env)).transpose()? {
|
match init.as_ref().map(|i| i.interpret(env)).transpose()? {
|
||||||
Some(value) => {
|
Some(value) => {
|
||||||
if let Ok(sub) = pattern::substitution(name, value) {
|
match pattern::substitution(env, name, value) {
|
||||||
for (name, value) in sub {
|
Ok(sub) => {
|
||||||
env.insert(*name, Some(value));
|
for (name, value) in sub {
|
||||||
|
env.insert(name, value);
|
||||||
|
}
|
||||||
|
return Ok(ConValue::Bool(true));
|
||||||
}
|
}
|
||||||
return Ok(ConValue::Bool(true));
|
Err(_e) => trace!("{_e}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
for name in pattern::variables(name) {
|
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 Self { scrutinee, arms } = self;
|
||||||
let scrutinee = scrutinee.interpret(env)?;
|
let scrutinee = scrutinee.interpret(env)?;
|
||||||
for MatchArm(pat, expr) in arms {
|
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");
|
let mut env = env.frame("match");
|
||||||
for (name, value) in substitution {
|
for (name, value) in substitution {
|
||||||
env.insert(*name, Some(value));
|
env.insert(name, value);
|
||||||
}
|
}
|
||||||
return expr.interpret(&mut env);
|
return expr.interpret(&mut env);
|
||||||
}
|
}
|
||||||
@ -377,21 +406,21 @@ impl Interpret for Match {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod assignment {
|
pub(crate) mod assignment {
|
||||||
/// Pattern matching engine for assignment
|
/// Pattern matching engine for assignment
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
type Namespace = HashMap<Sym, Option<ConValue>>;
|
type Namespace = HashMap<Sym, ConValue>;
|
||||||
|
|
||||||
pub(super) fn pat_assign(env: &mut Environment, pat: &Pattern, value: ConValue) -> IResult<()> {
|
pub(super) fn pat_assign(env: &mut Environment, pat: &Pattern, value: ConValue) -> IResult<()> {
|
||||||
for (name, value) in
|
for (name, value) in pattern::substitution(env, pat, value)
|
||||||
pattern::substitution(pat, value).map_err(|_| Error::PatFailed(pat.clone().into()))?
|
.map_err(|_| Error::PatFailed(pat.clone().into()))?
|
||||||
{
|
{
|
||||||
match env.get_mut(*name)? {
|
match env.get_mut(name)? {
|
||||||
&mut Some(ConValue::Ref(id)) => {
|
&mut ConValue::Ref(id) => {
|
||||||
*(env.get_id_mut(id).ok_or(Error::StackOverflow(id))?) = Some(value);
|
*(env.get_id_mut(id).ok_or(Error::StackOverflow(id))?) = value;
|
||||||
}
|
}
|
||||||
other => *other = Some(value),
|
other => *other = value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -404,11 +433,9 @@ mod assignment {
|
|||||||
match &pat.kind {
|
match &pat.kind {
|
||||||
ExprKind::Member(member) => *addrof_member(env, member)? = value,
|
ExprKind::Member(member) => *addrof_member(env, member)? = value,
|
||||||
ExprKind::Index(index) => *addrof_index(env, index)? = 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)? {
|
ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match addrof(env, tail)? {
|
||||||
&mut ConValue::Ref(r) => {
|
&mut ConValue::Ref(r) => *env.get_id_mut(r).ok_or(Error::StackOverflow(r))? = value,
|
||||||
*env.get_id_mut(r).ok_or(Error::StackOverflow(r))? = Some(value)
|
|
||||||
}
|
|
||||||
_ => Err(Error::NotAssignable())?,
|
_ => Err(Error::NotAssignable())?,
|
||||||
},
|
},
|
||||||
_ => 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> {
|
pub(super) fn addrof<'e>(env: &'e mut Environment, pat: &Expr) -> IResult<&'e mut ConValue> {
|
||||||
match &pat.kind {
|
match &pat.kind {
|
||||||
ExprKind::Path(path) => addrof_path(env, &path.parts)?
|
ExprKind::Path(path) => addrof_path(env, &path.parts),
|
||||||
.as_mut()
|
|
||||||
.ok_or(Error::NotInitialized("".into())),
|
|
||||||
ExprKind::Member(member) => addrof_member(env, member),
|
ExprKind::Member(member) => addrof_member(env, member),
|
||||||
ExprKind::Index(index) => addrof_index(env, index),
|
ExprKind::Index(index) => addrof_index(env, index),
|
||||||
ExprKind::Group(Group { expr }) => addrof(env, expr),
|
ExprKind::Group(Group { expr }) => addrof(env, expr),
|
||||||
ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match *addrof(env, tail)? {
|
ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match *addrof(env, tail)? {
|
||||||
ConValue::Ref(place) => env
|
ConValue::Ref(place) => env.get_id_mut(place).ok_or(Error::NotIndexable()),
|
||||||
.get_id_mut(place)
|
|
||||||
.ok_or(Error::NotIndexable())?
|
|
||||||
.as_mut()
|
|
||||||
.ok_or(Error::NotAssignable()),
|
|
||||||
_ => Err(Error::TypeError()),
|
_ => Err(Error::TypeError()),
|
||||||
},
|
},
|
||||||
_ => Err(Error::TypeError()),
|
_ => Err(Error::TypeError()),
|
||||||
@ -439,11 +460,11 @@ mod assignment {
|
|||||||
pub fn addrof_path<'e>(
|
pub fn addrof_path<'e>(
|
||||||
env: &'e mut Environment,
|
env: &'e mut Environment,
|
||||||
path: &[PathPart],
|
path: &[PathPart],
|
||||||
) -> IResult<&'e mut Option<ConValue>> {
|
) -> IResult<&'e mut ConValue> {
|
||||||
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)) => project_path_in_namespace(env, rest),
|
ConValue::Module(env) => project_path_in_namespace(env, rest),
|
||||||
_ => Err(Error::NotIndexable()),
|
_ => Err(Error::NotIndexable()),
|
||||||
},
|
},
|
||||||
_ => Err(Error::NotAssignable()),
|
_ => Err(Error::NotAssignable()),
|
||||||
@ -468,6 +489,11 @@ mod assignment {
|
|||||||
.collect::<IResult<Vec<_>>>()?;
|
.collect::<IResult<Vec<_>>>()?;
|
||||||
|
|
||||||
let mut head = addrof(env, head)?;
|
let mut head = addrof(env, head)?;
|
||||||
|
// match head {
|
||||||
|
// ConValue::Slice(id, len) | ConValue::Array(id, len) => {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
for index in indices {
|
for index in indices {
|
||||||
head = project_index(head, &index)?;
|
head = project_index(head, &index)?;
|
||||||
}
|
}
|
||||||
@ -499,30 +525,22 @@ mod assignment {
|
|||||||
|
|
||||||
/// Performs index "projection" from a ConValue to a particular element
|
/// Performs index "projection" from a ConValue to a particular element
|
||||||
pub fn project_index<'v>(
|
pub fn project_index<'v>(
|
||||||
value: &'v mut ConValue,
|
_value: &'v mut ConValue,
|
||||||
index: &ConValue,
|
_index: &ConValue,
|
||||||
) -> IResult<&'v mut ConValue> {
|
) -> IResult<&'v mut ConValue> {
|
||||||
match (value, index) {
|
Err(Error::NotIndexable())
|
||||||
(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()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn project_path_in_namespace<'e>(
|
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 ConValue> {
|
||||||
match path {
|
match path {
|
||||||
[] => Err(Error::NotAssignable()),
|
[] => Err(Error::NotAssignable()),
|
||||||
[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)) => project_path_in_namespace(env, rest),
|
ConValue::Module(env) => project_path_in_namespace(env, rest),
|
||||||
_ => Err(Error::NotIndexable()),
|
_ => Err(Error::NotIndexable()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -725,36 +743,39 @@ impl Interpret for Member {
|
|||||||
if let Ok(member) = assignment::addrof_member(env, self) {
|
if let Ok(member) = assignment::addrof_member(env, self) {
|
||||||
return Ok(member.clone());
|
return Ok(member.clone());
|
||||||
}
|
}
|
||||||
|
let mut values = vec![];
|
||||||
|
|
||||||
// Evaluate if this can be Self'd
|
// Evaluate if this can be Self'd
|
||||||
let value = match (&head.kind, kind) {
|
let addr = match (&head.kind, kind) {
|
||||||
(ExprKind::Path(p), MemberKind::Call(..)) => {
|
(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,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut value = head.interpret(env)?;
|
||||||
|
|
||||||
// Perform alternate member access
|
// Perform alternate member access
|
||||||
match (value.unwrap_or_else(|| head.interpret(env))?, kind) {
|
match (&value, &kind) {
|
||||||
(ConValue::Struct(parts), MemberKind::Call(name, args))
|
(ConValue::Struct(parts), MemberKind::Call(name, args))
|
||||||
if parts.1.contains_key(name) =>
|
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 {
|
for arg in &args.exprs {
|
||||||
values.push(arg.interpret(env)?);
|
values.push(arg.interpret(env)?);
|
||||||
}
|
}
|
||||||
(parts.1)
|
f.call(env, &values)
|
||||||
.get(name)
|
|
||||||
.cloned()
|
|
||||||
.ok_or(Error::NotDefined(*name))?
|
|
||||||
.call(env, &values)
|
|
||||||
}
|
}
|
||||||
(head, MemberKind::Call(name, args)) => {
|
(_, MemberKind::Call(name, args)) => {
|
||||||
let mut values = vec![head];
|
values.push(addr.unwrap_or(value));
|
||||||
for arg in &args.exprs {
|
for arg in &args.exprs {
|
||||||
values.push(arg.interpret(env)?);
|
values.push(arg.interpret(env)?);
|
||||||
}
|
}
|
||||||
env.call(*name, &values)
|
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;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// Look up struct/enum-struct definition
|
// Look up struct/enum-struct definition
|
||||||
|
// use that definition to place the struct parts
|
||||||
|
|
||||||
let name = match parts.last() {
|
let name = match parts.last() {
|
||||||
Some(PathPart::Ident(name)) => *name,
|
Some(PathPart::Ident(name)) => name.to_ref(),
|
||||||
Some(PathPart::SelfTy) => "Self".into(),
|
Some(PathPart::SelfTy) => "Self",
|
||||||
Some(PathPart::SuperKw) => "super".into(),
|
Some(PathPart::SuperKw) => "super",
|
||||||
None => "".into(),
|
None => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
@ -798,10 +820,7 @@ impl Interpret for Path {
|
|||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
let Self { absolute: _, parts } = self;
|
let Self { absolute: _, parts } = self;
|
||||||
|
|
||||||
assignment::addrof_path(env, parts)
|
assignment::addrof_path(env, parts).cloned()
|
||||||
.cloned()
|
|
||||||
.transpose()
|
|
||||||
.ok_or_else(|| Error::NotInitialized(format!("{self}").into()))?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Interpret for Literal {
|
impl Interpret for Literal {
|
||||||
@ -829,7 +848,15 @@ impl Interpret for ArrayRep {
|
|||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
let Self { value, repeat } = self;
|
let Self { value, repeat } = self;
|
||||||
let value = value.interpret(env)?;
|
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 {
|
impl Interpret for AddrOf {
|
||||||
@ -844,7 +871,7 @@ impl Interpret for AddrOf {
|
|||||||
_ => {
|
_ => {
|
||||||
let value = expr.interpret(env)?;
|
let value = expr.interpret(env)?;
|
||||||
let temp = env.stack_alloc(value)?;
|
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())?,
|
_ => Err(Error::NotIterable())?,
|
||||||
},
|
},
|
||||||
ConValue::Array(a) => Box::new(a.iter().cloned()),
|
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)),
|
ConValue::String(s) => Box::new(s.chars().map(ConValue::Char)),
|
||||||
_ => Err(Error::TypeError())?,
|
_ => Err(Error::TypeError())?,
|
||||||
};
|
};
|
||||||
loop {
|
loop {
|
||||||
let mut env = env.frame("loop variable");
|
let mut env = env.frame("loop variable");
|
||||||
if let Some(value) = bounds.next() {
|
if let Some(value) = bounds.next() {
|
||||||
for (name, value) in pattern::substitution(bind, value)? {
|
for (name, value) in pattern::substitution(&env, bind, value)? {
|
||||||
env.insert(*name, Some(value));
|
env.insert(name, value);
|
||||||
}
|
}
|
||||||
match pass.interpret(&mut env) {
|
match pass.interpret(&mut env) {
|
||||||
Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value),
|
Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value),
|
||||||
|
@ -9,7 +9,7 @@ use error::{Error, ErrorKind, IResult};
|
|||||||
use interpret::Interpret;
|
use interpret::Interpret;
|
||||||
|
|
||||||
/// Callable types can be called from within a Conlang program
|
/// 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 \
|
/// Calls this [Callable] in the provided [Environment], with [ConValue] args \
|
||||||
/// The Callable is responsible for checking the argument count and validating types
|
/// The Callable is responsible for checking the argument count and validating types
|
||||||
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue>;
|
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue>;
|
||||||
@ -23,6 +23,41 @@ pub mod interpret;
|
|||||||
|
|
||||||
pub mod function;
|
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<ConValue> {
|
||||||
|
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 closure;
|
||||||
|
|
||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
@ -31,6 +66,608 @@ pub mod pattern;
|
|||||||
|
|
||||||
pub mod env;
|
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<Self> {
|
||||||
|
let parent = self.tree.parent(self.index)?;
|
||||||
|
Some(Self { index: parent, ..self })
|
||||||
|
}
|
||||||
|
/// Gets the node's "encompassing Type"
|
||||||
|
pub fn selfty(self) -> Option<Self> {
|
||||||
|
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<Self> {
|
||||||
|
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<usize> {
|
||||||
|
self.tree.items(self.index)?.get(name).copied()
|
||||||
|
}
|
||||||
|
/// Returns true when this node represents type information
|
||||||
|
pub fn is_ty(self) -> Option<bool> {
|
||||||
|
self.tree.is_ty.get(self.index).copied()
|
||||||
|
}
|
||||||
|
/// Returns a reference to this node's children, if present
|
||||||
|
pub fn children(&self) -> Option<&HashMap<Sym, usize>> {
|
||||||
|
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<usize> {
|
||||||
|
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<Sym, usize>) {
|
||||||
|
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<usize>,
|
||||||
|
children: Vec<HashMap<Sym, usize>>,
|
||||||
|
items: Vec<StackBinds>,
|
||||||
|
is_ty: Vec<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Sym, usize>) {
|
||||||
|
self.children[node].extend(binds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets this node's parent
|
||||||
|
pub fn parent(&self, node: usize) -> Option<usize> {
|
||||||
|
if node == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.parents.get(node).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the node's "encompassing Type"
|
||||||
|
pub fn selfty(&self, node: usize) -> Option<usize> {
|
||||||
|
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<usize> {
|
||||||
|
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<usize> {
|
||||||
|
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<Sym, usize>> {
|
||||||
|
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<usize> {
|
||||||
|
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<F, T>(&mut self, path: &[PathPart], f: F) -> Option<T>
|
||||||
|
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<V: Visit<'a>>(&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;
|
pub mod error;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
convalue::ConValue,
|
convalue::ConValue,
|
||||||
|
env::Environment,
|
||||||
error::{Error, IResult},
|
error::{Error, IResult},
|
||||||
};
|
};
|
||||||
use cl_ast::{Literal, Pattern, Sym};
|
use cl_ast::{Literal, Pattern, Sym};
|
||||||
@ -43,7 +44,8 @@ pub fn variables(pat: &Pattern) -> Vec<&Sym> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rest_binding<'pat>(
|
fn rest_binding<'pat>(
|
||||||
sub: &mut HashMap<&'pat Sym, ConValue>,
|
env: &Environment,
|
||||||
|
sub: &mut HashMap<Sym, ConValue>,
|
||||||
mut patterns: &'pat [Pattern],
|
mut patterns: &'pat [Pattern],
|
||||||
mut values: VecDeque<ConValue>,
|
mut values: VecDeque<ConValue>,
|
||||||
) -> IResult<Option<(&'pat Pattern, VecDeque<ConValue>)>> {
|
) -> IResult<Option<(&'pat Pattern, VecDeque<ConValue>)>> {
|
||||||
@ -55,7 +57,7 @@ fn rest_binding<'pat>(
|
|||||||
let value = values
|
let value = values
|
||||||
.pop_front()
|
.pop_front()
|
||||||
.ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?;
|
.ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?;
|
||||||
append_sub(sub, pattern, value)?;
|
append_sub(env, sub, pattern, value)?;
|
||||||
patterns = tail;
|
patterns = tail;
|
||||||
}
|
}
|
||||||
// Bind the tail of the list
|
// Bind the tail of the list
|
||||||
@ -66,7 +68,7 @@ fn rest_binding<'pat>(
|
|||||||
let value = values
|
let value = values
|
||||||
.pop_back()
|
.pop_back()
|
||||||
.ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?;
|
.ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?;
|
||||||
append_sub(sub, pattern, value)?;
|
append_sub(env, sub, pattern, value)?;
|
||||||
patterns = head;
|
patterns = head;
|
||||||
}
|
}
|
||||||
// Bind the ..rest of the list
|
// Bind the ..rest of the list
|
||||||
@ -77,10 +79,51 @@ fn rest_binding<'pat>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rest_binding_ref<'pat>(
|
||||||
|
env: &Environment,
|
||||||
|
sub: &mut HashMap<Sym, ConValue>,
|
||||||
|
mut patterns: &'pat [Pattern],
|
||||||
|
mut head: usize,
|
||||||
|
mut tail: usize,
|
||||||
|
) -> IResult<Option<(&'pat Pattern, usize, usize)>> {
|
||||||
|
// 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
|
/// Appends a substitution to the provided table
|
||||||
pub fn append_sub<'pat>(
|
pub fn append_sub(
|
||||||
sub: &mut HashMap<&'pat Sym, ConValue>,
|
env: &Environment,
|
||||||
pat: &'pat Pattern,
|
sub: &mut HashMap<Sym, ConValue>,
|
||||||
|
pat: &Pattern,
|
||||||
value: ConValue,
|
value: ConValue,
|
||||||
) -> IResult<()> {
|
) -> IResult<()> {
|
||||||
match (pat, value) {
|
match (pat, value) {
|
||||||
@ -122,12 +165,21 @@ pub fn append_sub<'pat>(
|
|||||||
|
|
||||||
(Pattern::Name(name), _) if "_".eq(&**name) => Ok(()),
|
(Pattern::Name(name), _) if "_".eq(&**name) => Ok(()),
|
||||||
(Pattern::Name(name), value) => {
|
(Pattern::Name(name), value) => {
|
||||||
sub.insert(name, value);
|
sub.insert(*name, value);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
(Pattern::Ref(_, pat), ConValue::Ref(r)) => {
|
(Pattern::Ref(_, pat), ConValue::Ref(r)) => match env.get_id(r) {
|
||||||
todo!("Dereference <{r}> in pattern matching {pat}")
|
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) {
|
(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)) => {
|
(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)) => {
|
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(()),
|
_ => 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::Empty) if patterns.is_empty() => Ok(()),
|
||||||
(Pattern::Tuple(patterns), ConValue::Tuple(values)) => {
|
(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)) => {
|
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(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(Pattern::TupleStruct(path, patterns), ConValue::TupleStruct(parts)) => {
|
(Pattern::TupleStruct(path, patterns), ConValue::TupleStruct(parts)) => {
|
||||||
let (name, values) = *parts;
|
let (id, values) = *parts;
|
||||||
if !path.ends_with(name) {
|
|
||||||
Err(Error::TypeError())?
|
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)) => {
|
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(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(Pattern::Struct(path, patterns), ConValue::Struct(parts)) => {
|
(Pattern::Struct(path, patterns), ConValue::Struct(parts)) => {
|
||||||
let (name, mut values) = *parts;
|
let (id, mut values) = *parts;
|
||||||
if !path.ends_with(&name) {
|
let tid = path
|
||||||
Err(Error::TypeError())?
|
.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 {
|
for (name, pat) in patterns {
|
||||||
let value = values.remove(name).ok_or(Error::TypeError())?;
|
let value = values.remove(name).ok_or(Error::TypeError())?;
|
||||||
match pat {
|
match pat {
|
||||||
Some(pat) => append_sub(sub, pat, value)?,
|
Some(pat) => append_sub(env, sub, pat, value)?,
|
||||||
None => {
|
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
|
/// Constructs a substitution from a pattern and a value
|
||||||
pub fn substitution(pat: &Pattern, value: ConValue) -> IResult<HashMap<&Sym, ConValue>> {
|
pub fn substitution(
|
||||||
|
env: &Environment,
|
||||||
|
pat: &Pattern,
|
||||||
|
value: ConValue,
|
||||||
|
) -> IResult<HashMap<Sym, ConValue>> {
|
||||||
let mut sub = HashMap::new();
|
let mut sub = HashMap::new();
|
||||||
append_sub(&mut sub, pat, value)?;
|
append_sub(env, &mut sub, pat, value)?;
|
||||||
Ok(sub)
|
Ok(sub)
|
||||||
}
|
}
|
||||||
|
@ -23,18 +23,36 @@ pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
|
|||||||
menu::clear();
|
menu::clear();
|
||||||
Ok(ConValue::Empty)
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
/// Evaluates a quoted expression
|
|
||||||
fn eval(ConValue::Quote(quote)) @env {
|
fn eval(string) @env {
|
||||||
env.eval(quote.as_ref())
|
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::<cl_ast::Stmt>() {
|
||||||
|
Err(e) => Ok(ConValue::String(format!("{e}").into())),
|
||||||
|
Ok(v) => v.interpret(env),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a file
|
/// Executes a file
|
||||||
fn import(ConValue::String(path)) @env {
|
fn import(ConValue::String(path)) @env {
|
||||||
load_file(env, &**path).or(Ok(ConValue::Empty))
|
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
|
/// Gets a line of input from stdin
|
||||||
fn get_line() {
|
fn get_line(ConValue::String(prompt)) {
|
||||||
match repline::Repline::new("", "", "").read() {
|
match repline::Repline::new("", prompt.to_ref(), "").read() {
|
||||||
Ok(line) => Ok(ConValue::String(line.into())),
|
Ok(line) => Ok(ConValue::String(line.into())),
|
||||||
Err(repline::Error::CtrlD(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)),
|
Err(repline::Error::CtrlC(_)) => Err(cl_interpret::error::Error::Break(ConValue::Empty)),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user