cl-interpret: Environment/stack overhaul + Ref patterns

This commit is contained in:
2025-07-18 05:29:10 -04:00
parent e165e029dc
commit 8b0a122dfc
12 changed files with 1216 additions and 353 deletions

View File

@@ -5,6 +5,7 @@
use crate::{
convalue::ConValue,
env::Environment,
error::{Error, IResult},
};
use cl_ast::{Literal, Pattern, Sym};
@@ -43,7 +44,8 @@ pub fn variables(pat: &Pattern) -> Vec<&Sym> {
}
fn rest_binding<'pat>(
sub: &mut HashMap<&'pat Sym, ConValue>,
env: &Environment,
sub: &mut HashMap<Sym, ConValue>,
mut patterns: &'pat [Pattern],
mut values: VecDeque<ConValue>,
) -> IResult<Option<(&'pat Pattern, VecDeque<ConValue>)>> {
@@ -55,7 +57,7 @@ fn rest_binding<'pat>(
let value = values
.pop_front()
.ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?;
append_sub(sub, pattern, value)?;
append_sub(env, sub, pattern, value)?;
patterns = tail;
}
// Bind the tail of the list
@@ -66,7 +68,7 @@ fn rest_binding<'pat>(
let value = values
.pop_back()
.ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?;
append_sub(sub, pattern, value)?;
append_sub(env, sub, pattern, value)?;
patterns = head;
}
// Bind the ..rest of the list
@@ -77,10 +79,51 @@ fn rest_binding<'pat>(
}
}
fn rest_binding_ref<'pat>(
env: &Environment,
sub: &mut HashMap<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
pub fn append_sub<'pat>(
sub: &mut HashMap<&'pat Sym, ConValue>,
pat: &'pat Pattern,
pub fn append_sub(
env: &Environment,
sub: &mut HashMap<Sym, ConValue>,
pat: &Pattern,
value: ConValue,
) -> IResult<()> {
match (pat, value) {
@@ -122,12 +165,21 @@ pub fn append_sub<'pat>(
(Pattern::Name(name), _) if "_".eq(&**name) => Ok(()),
(Pattern::Name(name), value) => {
sub.insert(name, value);
sub.insert(*name, value);
Ok(())
}
(Pattern::Ref(_, pat), ConValue::Ref(r)) => {
todo!("Dereference <{r}> in pattern matching {pat}")
(Pattern::Ref(_, pat), ConValue::Ref(r)) => match env.get_id(r) {
Some(value) => append_sub(env, sub, pat, value.clone()),
None => Err(Error::PatFailed(pat.clone())),
},
(Pattern::Ref(_, pat), ConValue::Slice(head, len)) => {
let mut values = Vec::with_capacity(len);
for idx in head..(head + len) {
values.push(env.get_id(idx).cloned().ok_or(Error::StackOverflow(idx))?);
}
append_sub(env, sub, pat, ConValue::Array(values.into_boxed_slice()))
}
(Pattern::RangeExc(head, tail), value) => match (head.as_ref(), tail.as_ref(), value) {
@@ -195,48 +247,64 @@ pub fn append_sub<'pat>(
},
(Pattern::Array(patterns), ConValue::Array(values)) => {
match rest_binding(sub, patterns, values.into_vec().into())? {
match rest_binding(env, sub, patterns, values.into_vec().into())? {
Some((pattern, values)) => {
append_sub(sub, pattern, ConValue::Array(Vec::from(values).into()))
append_sub(env, sub, pattern, ConValue::Array(Vec::from(values).into()))
}
_ => Ok(()),
}
}
(Pattern::Array(patterns), ConValue::Slice(head, len)) => {
match rest_binding_ref(env, sub, patterns, head, head + len)? {
Some((pat, head, tail)) => {
append_sub(env, sub, pat, ConValue::Slice(head, tail - head))
}
None => Ok(()),
}
}
(Pattern::Tuple(patterns), ConValue::Empty) if patterns.is_empty() => Ok(()),
(Pattern::Tuple(patterns), ConValue::Tuple(values)) => {
match rest_binding(sub, patterns, values.into_vec().into())? {
match rest_binding(env, sub, patterns, values.into_vec().into())? {
Some((pattern, values)) => {
append_sub(sub, pattern, ConValue::Tuple(Vec::from(values).into()))
append_sub(env, sub, pattern, ConValue::Tuple(Vec::from(values).into()))
}
_ => Ok(()),
}
}
(Pattern::TupleStruct(path, patterns), ConValue::TupleStruct(parts)) => {
let (name, values) = *parts;
if !path.ends_with(name) {
Err(Error::TypeError())?
let (id, values) = *parts;
let tid = path
.as_sym()
.ok_or_else(|| Error::PatFailed(pat.clone().into()))?;
if id != tid.to_ref() {
return Err(Error::PatFailed(pat.clone().into()));
}
match rest_binding(sub, patterns, values.into_vec().into())? {
match rest_binding(env, sub, patterns, values.into_vec().into())? {
Some((pattern, values)) => {
append_sub(sub, pattern, ConValue::Tuple(Vec::from(values).into()))
append_sub(env, sub, pattern, ConValue::Tuple(Vec::from(values).into()))
}
_ => Ok(()),
}
}
(Pattern::Struct(path, patterns), ConValue::Struct(parts)) => {
let (name, mut values) = *parts;
if !path.ends_with(&name) {
Err(Error::TypeError())?
let (id, mut values) = *parts;
let tid = path
.as_sym()
.ok_or_else(|| Error::PatFailed(pat.clone().into()))?;
if id != tid.to_ref() {
return Err(Error::PatFailed(pat.clone().into()));
}
for (name, pat) in patterns {
let value = values.remove(name).ok_or(Error::TypeError())?;
match pat {
Some(pat) => append_sub(sub, pat, value)?,
Some(pat) => append_sub(env, sub, pat, value)?,
None => {
sub.insert(name, value);
sub.insert(*name, value);
}
}
}
@@ -251,8 +319,12 @@ pub fn append_sub<'pat>(
}
/// Constructs a substitution from a pattern and a value
pub fn substitution(pat: &Pattern, value: ConValue) -> IResult<HashMap<&Sym, ConValue>> {
pub fn substitution(
env: &Environment,
pat: &Pattern,
value: ConValue,
) -> IResult<HashMap<Sym, ConValue>> {
let mut sub = HashMap::new();
append_sub(&mut sub, pat, value)?;
append_sub(env, &mut sub, pat, value)?;
Ok(sub)
}