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

@@ -8,6 +8,14 @@
use super::*;
use cl_ast::{ast_visitor::Visit, *};
use std::borrow::Borrow;
macro trace($($t:tt)*) {{
#[cfg(debug_assertions)]
if std::env::var("CONLANG_TRACE").is_ok() {
eprintln!($($t)*)
}
}}
/// A work-in-progress tree walk interpreter for Conlang
pub trait Interpret {
/// Interprets this thing in the given [`Environment`].
@@ -18,6 +26,7 @@ pub trait Interpret {
impl Interpret for File {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
trace!("// Running {}", self.name);
/// Sorts items
#[derive(Debug, Default)]
struct ItemSorter<'ast>(pub [Vec<&'ast Item>; 8]);
@@ -45,6 +54,7 @@ impl Interpret for File {
Ok(ConValue::Empty)
}
}
impl Interpret for Item {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
match &self.kind {
@@ -60,139 +70,157 @@ impl Interpret for Item {
}
}
}
impl Interpret for Alias {
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
println!("TODO: {self}");
trace!("// TODO: {self}");
Ok(ConValue::Empty)
}
}
impl Interpret for Const {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Const { name, ty: _, init } = self;
trace!("// Defining const {name}");
let init = init.as_ref().interpret(env)?;
env.insert(*name, Some(init));
env.insert(*name, init);
Ok(ConValue::Empty)
}
}
impl Interpret for Static {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Static { mutable: _, name, ty: _, init } = self;
trace!("// Defining static {name}");
let init = init.as_ref().interpret(env)?;
env.insert(*name, Some(init));
env.insert(*name, init);
Ok(ConValue::Empty)
}
}
impl Interpret for Module {
// TODO: Keep modules around somehow, rather than putting them on the stack
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { name, file } = self;
env.push_frame(name.to_ref(), Default::default());
trace!("// Defining module {name}");
let mut scope = env.frame(name.to_ref());
let out = match file {
Some(file) => file.interpret(env),
Some(file) => file.interpret(&mut scope),
None => {
eprintln!("Module {name} specified, but not imported.");
Ok(ConValue::Empty)
}
};
let (frame, _) = env
.pop_frame()
let frame = scope
.pop_values()
.expect("Environment frames must be balanced");
env.insert(*name, Some(ConValue::Module(frame.into())));
env.insert(*name, ConValue::Module(frame.into()));
out
}
}
impl Interpret for Function {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
trace!("// Defining fn {}", self.name);
// register the function in the current environment
env.insert_fn(self);
Ok(ConValue::Empty)
}
}
impl Interpret for Struct {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { name, gens: _, kind } = self;
trace!("// Defining struct {name}");
let mut frame = env.frame(name.to_ref());
match kind {
StructKind::Empty => {}
StructKind::Empty => {
frame.insert_tup_constructor("call".into(), 0);
frame.insert("__nmemb".into(), ConValue::Int(0));
}
StructKind::Tuple(args) => {
// Constructs the AST from scratch. TODO: This, better.
let constructor = Function {
name: *name,
gens: Default::default(),
sign: TyFn {
args: TyKind::Tuple(TyTuple {
types: args.iter().map(|ty| ty.kind.clone()).collect(),
})
.into(),
rety: Some(
Ty {
span: cl_structures::span::Span::dummy(),
kind: TyKind::Path(Path::from(*name)),
}
.into(),
),
},
bind: Pattern::Tuple(
args.iter()
.enumerate()
.map(|(idx, _)| Pattern::Name(idx.to_string().into()))
.collect(),
),
body: None,
};
let constructor = crate::function::Function::new_constructor(constructor);
env.insert(*name, Some(constructor.into()));
frame.insert_tup_constructor("call".into(), args.len());
frame.insert("__nmemb".into(), ConValue::Int(args.len() as _));
}
StructKind::Struct(members) => {
// TODO: more precise type checking of structs
for (idx, StructMember { vis: _, name: nm, ty: _ }) in members.iter().enumerate() {
trace!("// Defining {name}::{nm}");
frame.insert(*nm, ConValue::Int(idx as _));
}
frame.insert("__nmemb".into(), ConValue::Int(members.len() as _));
}
StructKind::Struct(_) => eprintln!("TODO: {self}"),
}
let frame = frame
.pop_values()
.expect("Environment frames must be balanced");
env.insert(*name, ConValue::Module(Box::new(frame)));
Ok(ConValue::Empty)
}
}
impl Interpret for Enum {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { name, gens: _, variants } = self;
env.push_frame(name.to_ref(), Default::default());
trace!("// Defining enum {name}");
let mut scope = env.frame(name.to_ref());
for (idx, Variant { name, kind, body }) in variants.iter().enumerate() {
match (kind, body) {
(StructKind::Empty, None) => env.insert(*name, Some(ConValue::Int(idx as _))),
(StructKind::Empty, None) => scope.insert(*name, ConValue::Int(idx as _)),
(StructKind::Empty, Some(idx)) => {
let idx = idx.interpret(env)?;
env.insert(*name, Some(idx))
let idx = idx.interpret(&mut scope)?;
scope.insert(*name, idx)
}
(StructKind::Tuple(args), None) => {
scope.insert_tup_constructor(*name, args.len());
}
(StructKind::Tuple(_), None) => {}
(StructKind::Struct(_), None) => {}
_ => eprintln!("Well-formedness error in {self}"),
}
}
let (frame, _) = env
.pop_frame()
let frame = scope
.pop_values()
.expect("Frame stack should remain balanced.");
env.insert(*name, Some(ConValue::Module(Box::new(frame))));
env.insert(*name, ConValue::Module(Box::new(frame)));
Ok(ConValue::Empty)
}
}
impl Interpret for Impl {
fn interpret(&self, env: &mut Environment) -> IResult<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 {
eprintln!("TODO: impl X for Ty");
trace!("TODO: impl X for Ty");
return Ok(ConValue::Empty);
};
env.push_frame("impl", Default::default());
body.interpret(env)?;
let mut frame = env.frame("impl");
body.interpret(&mut frame)?;
let (frame, _) = env
.pop_frame()
let frame = frame
.pop_values()
.expect("Environment frames must be balanced");
match assignment::addrof_path(env, name.parts.as_slice())
.map_err(|err| err.with_span(*span))?
{
Some(ConValue::Module(m)) => m.extend(frame),
Some(other) => eprintln!("TODO: impl for {other}"),
None => {}
ConValue::Module(m) => m.extend(frame),
other => eprintln!("TODO: impl for {other}"),
}
Ok(ConValue::Empty)
}
@@ -226,9 +254,8 @@ impl Interpret for UseTree {
let Ok(ConValue::Module(m)) = env.get(*name) else {
Err(Error::TypeError())?
};
env.push_frame(name.to_ref(), *m);
let out = get_bindings(tree, env, bindings);
env.pop_frame();
let mut scope = env.with_frame(name.to_ref(), *m);
let out = get_bindings(tree, &mut scope, bindings);
return out;
}
UseTree::Alias(name, alias) => {
@@ -238,11 +265,10 @@ impl Interpret for UseTree {
bindings.insert(*name, env.get(*name)?);
}
UseTree::Glob => {
trace!("TODO: Improve glob imports");
if let Some((frame, name)) = env.pop_frame() {
for (k, v) in &frame {
if let Some(v) = v {
bindings.insert(*k, v.clone());
}
bindings.insert(*k, v.clone());
}
env.push_frame(name, frame);
}
@@ -258,7 +284,7 @@ impl Interpret for UseTree {
get_bindings(self, env, &mut bindings)?;
for (name, value) in bindings {
env.insert(name, Some(value));
env.insert(name, value);
}
Ok(ConValue::Empty)
@@ -343,16 +369,19 @@ impl Interpret for Let {
let Let { mutable: _, name, ty: _, init } = self;
match init.as_ref().map(|i| i.interpret(env)).transpose()? {
Some(value) => {
if let Ok(sub) = pattern::substitution(name, value) {
for (name, value) in sub {
env.insert(*name, Some(value));
match pattern::substitution(env, name, value) {
Ok(sub) => {
for (name, value) in sub {
env.insert(name, value);
}
return Ok(ConValue::Bool(true));
}
return Ok(ConValue::Bool(true));
Err(_e) => trace!("{_e}"),
};
}
None => {
for name in pattern::variables(name) {
env.insert(*name, None);
env.insert(*name, ConValue::Empty);
}
}
}
@@ -365,10 +394,10 @@ impl Interpret for Match {
let Self { scrutinee, arms } = self;
let scrutinee = scrutinee.interpret(env)?;
for MatchArm(pat, expr) in arms {
if let Ok(substitution) = pattern::substitution(pat, scrutinee.clone()) {
if let Ok(substitution) = pattern::substitution(env, pat, scrutinee.clone()) {
let mut env = env.frame("match");
for (name, value) in substitution {
env.insert(*name, Some(value));
env.insert(name, value);
}
return expr.interpret(&mut env);
}
@@ -377,21 +406,21 @@ impl Interpret for Match {
}
}
mod assignment {
pub(crate) mod assignment {
/// Pattern matching engine for assignment
use super::*;
use std::collections::HashMap;
type Namespace = HashMap<Sym, Option<ConValue>>;
type Namespace = HashMap<Sym, ConValue>;
pub(super) fn pat_assign(env: &mut Environment, pat: &Pattern, value: ConValue) -> IResult<()> {
for (name, value) in
pattern::substitution(pat, value).map_err(|_| Error::PatFailed(pat.clone().into()))?
for (name, value) in pattern::substitution(env, pat, value)
.map_err(|_| Error::PatFailed(pat.clone().into()))?
{
match env.get_mut(*name)? {
&mut Some(ConValue::Ref(id)) => {
*(env.get_id_mut(id).ok_or(Error::StackOverflow(id))?) = Some(value);
match env.get_mut(name)? {
&mut ConValue::Ref(id) => {
*(env.get_id_mut(id).ok_or(Error::StackOverflow(id))?) = value;
}
other => *other = Some(value),
other => *other = value,
}
}
Ok(())
@@ -404,11 +433,9 @@ mod assignment {
match &pat.kind {
ExprKind::Member(member) => *addrof_member(env, member)? = value,
ExprKind::Index(index) => *addrof_index(env, index)? = value,
ExprKind::Path(path) => *addrof_path(env, &path.parts)? = Some(value),
ExprKind::Path(path) => *addrof_path(env, &path.parts)? = value,
ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match addrof(env, tail)? {
&mut ConValue::Ref(r) => {
*env.get_id_mut(r).ok_or(Error::StackOverflow(r))? = Some(value)
}
&mut ConValue::Ref(r) => *env.get_id_mut(r).ok_or(Error::StackOverflow(r))? = value,
_ => Err(Error::NotAssignable())?,
},
_ => Err(Error::NotAssignable())?,
@@ -418,18 +445,12 @@ mod assignment {
pub(super) fn addrof<'e>(env: &'e mut Environment, pat: &Expr) -> IResult<&'e mut ConValue> {
match &pat.kind {
ExprKind::Path(path) => addrof_path(env, &path.parts)?
.as_mut()
.ok_or(Error::NotInitialized("".into())),
ExprKind::Path(path) => addrof_path(env, &path.parts),
ExprKind::Member(member) => addrof_member(env, member),
ExprKind::Index(index) => addrof_index(env, index),
ExprKind::Group(Group { expr }) => addrof(env, expr),
ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match *addrof(env, tail)? {
ConValue::Ref(place) => env
.get_id_mut(place)
.ok_or(Error::NotIndexable())?
.as_mut()
.ok_or(Error::NotAssignable()),
ConValue::Ref(place) => env.get_id_mut(place).ok_or(Error::NotIndexable()),
_ => Err(Error::TypeError()),
},
_ => Err(Error::TypeError()),
@@ -439,11 +460,11 @@ mod assignment {
pub fn addrof_path<'e>(
env: &'e mut Environment,
path: &[PathPart],
) -> IResult<&'e mut Option<ConValue>> {
) -> IResult<&'e mut ConValue> {
match path {
[PathPart::Ident(name)] => env.get_mut(*name),
[PathPart::Ident(name), rest @ ..] => match env.get_mut(*name)? {
Some(ConValue::Module(env)) => project_path_in_namespace(env, rest),
ConValue::Module(env) => project_path_in_namespace(env, rest),
_ => Err(Error::NotIndexable()),
},
_ => Err(Error::NotAssignable()),
@@ -468,6 +489,11 @@ mod assignment {
.collect::<IResult<Vec<_>>>()?;
let mut head = addrof(env, head)?;
// match head {
// ConValue::Slice(id, len) | ConValue::Array(id, len) => {
// }
// }
for index in indices {
head = project_index(head, &index)?;
}
@@ -499,30 +525,22 @@ mod assignment {
/// Performs index "projection" from a ConValue to a particular element
pub fn project_index<'v>(
value: &'v mut ConValue,
index: &ConValue,
_value: &'v mut ConValue,
_index: &ConValue,
) -> IResult<&'v mut ConValue> {
match (value, index) {
(ConValue::Array(a), ConValue::Int(i)) => {
let a_len = a.len();
a.get_mut(*i as usize)
.ok_or(Error::OobIndex(*i as usize, a_len))
}
(ConValue::Slice(_, _), _) => Err(Error::TypeError()),
_ => Err(Error::NotIndexable()),
}
Err(Error::NotIndexable())
}
pub fn project_path_in_namespace<'e>(
env: &'e mut Namespace,
path: &[PathPart],
) -> IResult<&'e mut Option<ConValue>> {
) -> IResult<&'e mut ConValue> {
match path {
[] => Err(Error::NotAssignable()),
[PathPart::Ident(name)] => env.get_mut(name).ok_or(Error::NotDefined(*name)),
[PathPart::Ident(name), rest @ ..] => {
match env.get_mut(name).ok_or(Error::NotDefined(*name))? {
Some(ConValue::Module(env)) => project_path_in_namespace(env, rest),
ConValue::Module(env) => project_path_in_namespace(env, rest),
_ => Err(Error::NotIndexable()),
}
}
@@ -725,36 +743,39 @@ impl Interpret for Member {
if let Ok(member) = assignment::addrof_member(env, self) {
return Ok(member.clone());
}
let mut values = vec![];
// Evaluate if this can be Self'd
let value = match (&head.kind, kind) {
let addr = match (&head.kind, kind) {
(ExprKind::Path(p), MemberKind::Call(..)) => {
p.as_sym().map(|name| Ok(ConValue::Ref(env.id_of(name)?))) // "borrow" it
p.as_sym()
.and_then(|name| Some(ConValue::Ref(env.id_of(name).ok()?))) // "borrow" it
}
_ => None,
};
let mut value = head.interpret(env)?;
// Perform alternate member access
match (value.unwrap_or_else(|| head.interpret(env))?, kind) {
match (&value, &kind) {
(ConValue::Struct(parts), MemberKind::Call(name, args))
if parts.1.contains_key(name) =>
{
let mut values = vec![];
let f = parts.1.get(name).cloned().expect("function exists");
values.push(addr.unwrap_or(value));
for arg in &args.exprs {
values.push(arg.interpret(env)?);
}
(parts.1)
.get(name)
.cloned()
.ok_or(Error::NotDefined(*name))?
.call(env, &values)
f.call(env, &values)
}
(head, MemberKind::Call(name, args)) => {
let mut values = vec![head];
(_, MemberKind::Call(name, args)) => {
values.push(addr.unwrap_or(value));
for arg in &args.exprs {
values.push(arg.interpret(env)?);
}
env.call(*name, &values)
}
(mut head, kind) => assignment::project_memberkind(&mut head, kind).cloned(),
(_, kind) => assignment::project_memberkind(&mut value, kind).cloned(),
}
}
}
@@ -774,12 +795,13 @@ impl Interpret for Structor {
use std::collections::HashMap;
// Look up struct/enum-struct definition
// use that definition to place the struct parts
let name = match parts.last() {
Some(PathPart::Ident(name)) => *name,
Some(PathPart::SelfTy) => "Self".into(),
Some(PathPart::SuperKw) => "super".into(),
None => "".into(),
Some(PathPart::Ident(name)) => name.to_ref(),
Some(PathPart::SelfTy) => "Self",
Some(PathPart::SuperKw) => "super",
None => "",
};
let mut map = HashMap::new();
@@ -798,10 +820,7 @@ impl Interpret for Path {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { absolute: _, parts } = self;
assignment::addrof_path(env, parts)
.cloned()
.transpose()
.ok_or_else(|| Error::NotInitialized(format!("{self}").into()))?
assignment::addrof_path(env, parts).cloned()
}
}
impl Interpret for Literal {
@@ -829,7 +848,15 @@ impl Interpret for ArrayRep {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { value, repeat } = self;
let value = value.interpret(env)?;
Ok(ConValue::Array(vec![value; *repeat].into()))
let ConValue::Int(repeat) = repeat.interpret(env)? else {
Err(Error::TypeError())?
};
if repeat < 0 {
// TODO: a special error just for you
Err(Error::NotIndexable())?
}
Ok(ConValue::Array(vec![value; repeat as usize].into()))
}
}
impl Interpret for AddrOf {
@@ -844,7 +871,7 @@ impl Interpret for AddrOf {
_ => {
let value = expr.interpret(env)?;
let temp = env.stack_alloc(value)?;
Ok(ConValue::Ref(env::Place::Local(temp)))
Ok(ConValue::Ref(temp))
}
}
}
@@ -928,14 +955,17 @@ impl Interpret for For {
_ => Err(Error::NotIterable())?,
},
ConValue::Array(a) => Box::new(a.iter().cloned()),
&ConValue::Slice(head, len) => Box::new((head..head + len).map(ConValue::Ref)),
// In the production compiler this may be fully unrolled
ConValue::Tuple(t) => Box::new(t.iter().cloned()),
ConValue::String(s) => Box::new(s.chars().map(ConValue::Char)),
_ => Err(Error::TypeError())?,
};
loop {
let mut env = env.frame("loop variable");
if let Some(value) = bounds.next() {
for (name, value) in pattern::substitution(bind, value)? {
env.insert(*name, Some(value));
for (name, value) in pattern::substitution(&env, bind, value)? {
env.insert(name, value);
}
match pass.interpret(&mut env) {
Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value),