979 lines
35 KiB
Rust
979 lines
35 KiB
Rust
//! A work-in-progress tree walk interpreter for Conlang
|
|
//!
|
|
//! Currently, major parts of the interpreter are not yet implemented, and major parts will never be
|
|
//! implemented in its current form. Namely, since no [ConValue] has a stable location, it's
|
|
//! meaningless to get a pointer to one, and would be undefined behavior to dereference a pointer to
|
|
//! one in any situation.
|
|
|
|
use super::*;
|
|
use cl_ast::{ast_visitor::Visit, *};
|
|
use std::borrow::Borrow;
|
|
/// A work-in-progress tree walk interpreter for Conlang
|
|
pub trait Interpret {
|
|
/// Interprets this thing in the given [`Environment`].
|
|
///
|
|
/// Everything returns a value!™
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue>;
|
|
}
|
|
|
|
impl Interpret for File {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
/// Sorts items
|
|
#[derive(Debug, Default)]
|
|
struct ItemSorter<'ast>(pub [Vec<&'ast Item>; 8]);
|
|
impl<'ast> Visit<'ast> for ItemSorter<'ast> {
|
|
fn visit_item(&mut self, i: &'ast Item) {
|
|
for stage in match &i.kind {
|
|
ItemKind::Module(_) => [0].as_slice(),
|
|
ItemKind::Use(_) => &[1, 6],
|
|
ItemKind::Enum(_) | ItemKind::Struct(_) | ItemKind::Alias(_) => &[2],
|
|
ItemKind::Function(_) => &[3, 7],
|
|
ItemKind::Impl(_) => &[4],
|
|
ItemKind::Const(_) | ItemKind::Static(_) => &[5],
|
|
} {
|
|
self.0[*stage].push(i)
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut items = ItemSorter::default();
|
|
items.visit_file(self);
|
|
for item in items.0.into_iter().flatten() {
|
|
item.interpret(env)?;
|
|
}
|
|
|
|
Ok(ConValue::Empty)
|
|
}
|
|
}
|
|
impl Interpret for Item {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
match &self.kind {
|
|
ItemKind::Alias(item) => item.interpret(env),
|
|
ItemKind::Const(item) => item.interpret(env),
|
|
ItemKind::Static(item) => item.interpret(env),
|
|
ItemKind::Module(item) => item.interpret(env),
|
|
ItemKind::Function(item) => item.interpret(env),
|
|
ItemKind::Struct(item) => item.interpret(env),
|
|
ItemKind::Enum(item) => item.interpret(env),
|
|
ItemKind::Impl(item) => item.interpret(env),
|
|
ItemKind::Use(item) => item.interpret(env),
|
|
}
|
|
}
|
|
}
|
|
impl Interpret for Alias {
|
|
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
|
|
println!("TODO: {self}");
|
|
Ok(ConValue::Empty)
|
|
}
|
|
}
|
|
impl Interpret for Const {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Const { name, ty: _, init } = self;
|
|
|
|
let init = init.as_ref().interpret(env)?;
|
|
env.insert(*name, Some(init));
|
|
Ok(ConValue::Empty)
|
|
}
|
|
}
|
|
impl Interpret for Static {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Static { mutable: _, name, ty: _, init } = self;
|
|
|
|
let init = init.as_ref().interpret(env)?;
|
|
env.insert(*name, Some(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());
|
|
let out = match file {
|
|
Some(file) => file.interpret(env),
|
|
None => {
|
|
eprintln!("Module {name} specified, but not imported.");
|
|
Ok(ConValue::Empty)
|
|
}
|
|
};
|
|
|
|
let (frame, _) = env
|
|
.pop_frame()
|
|
.expect("Environment frames must be balanced");
|
|
env.insert(*name, Some(ConValue::Module(frame.into())));
|
|
|
|
out
|
|
}
|
|
}
|
|
impl Interpret for Function {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
// 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;
|
|
match kind {
|
|
StructKind::Empty => {}
|
|
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()));
|
|
}
|
|
StructKind::Struct(_) => eprintln!("TODO: {self}"),
|
|
}
|
|
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());
|
|
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, Some(idx)) => {
|
|
let idx = idx.interpret(env)?;
|
|
env.insert(*name, Some(idx))
|
|
}
|
|
(StructKind::Tuple(_), None) => eprintln!("TODO: Enum-tuple variants: {kind}"),
|
|
(StructKind::Struct(_), None) => {
|
|
eprintln!("TODO: Enum-struct members: {kind}")
|
|
}
|
|
_ => eprintln!("Well-formedness error in {self}"),
|
|
}
|
|
}
|
|
let (frame, _) = env
|
|
.pop_frame()
|
|
.expect("Frame stack should remain balanced.");
|
|
env.insert(*name, Some(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
|
|
else {
|
|
eprintln!("TODO: impl X for Ty");
|
|
return Ok(ConValue::Empty);
|
|
};
|
|
env.push_frame("impl", Default::default());
|
|
body.interpret(env)?;
|
|
|
|
let (frame, _) = env
|
|
.pop_frame()
|
|
.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 => {}
|
|
}
|
|
Ok(ConValue::Empty)
|
|
}
|
|
}
|
|
|
|
impl Interpret for Use {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { absolute: _, tree } = self;
|
|
tree.interpret(env)
|
|
}
|
|
}
|
|
|
|
impl Interpret for UseTree {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
// TODO: raw-bind use items
|
|
type Bindings = HashMap<Sym, ConValue>;
|
|
use std::collections::HashMap;
|
|
|
|
fn get_bindings(
|
|
tree: &UseTree,
|
|
env: &mut Environment,
|
|
bindings: &mut Bindings,
|
|
) -> IResult<()> {
|
|
match tree {
|
|
UseTree::Tree(use_trees) => {
|
|
for tree in use_trees {
|
|
get_bindings(tree, env, bindings)?;
|
|
}
|
|
}
|
|
UseTree::Path(PathPart::Ident(name), tree) => {
|
|
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();
|
|
return out;
|
|
}
|
|
UseTree::Alias(name, alias) => {
|
|
bindings.insert(*alias, env.get(*name)?);
|
|
}
|
|
UseTree::Name(name) => {
|
|
bindings.insert(*name, env.get(*name)?);
|
|
}
|
|
UseTree::Glob => {
|
|
if let Some((frame, name)) = env.pop_frame() {
|
|
for (k, v) in &frame {
|
|
if let Some(v) = v {
|
|
bindings.insert(*k, v.clone());
|
|
}
|
|
}
|
|
env.push_frame(name, frame);
|
|
}
|
|
}
|
|
other => {
|
|
eprintln!("ERROR: Cannot use {other}");
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
let mut bindings = Bindings::new();
|
|
get_bindings(self, env, &mut bindings)?;
|
|
|
|
for (name, value) in bindings {
|
|
env.insert(name, Some(value));
|
|
}
|
|
|
|
Ok(ConValue::Empty)
|
|
}
|
|
}
|
|
|
|
impl Interpret for Stmt {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { span, kind, semi } = self;
|
|
let out = match kind {
|
|
StmtKind::Empty => Ok(ConValue::Empty),
|
|
StmtKind::Item(stmt) => stmt.interpret(env),
|
|
StmtKind::Expr(stmt) => stmt.interpret(env),
|
|
}
|
|
.map_err(|err| err.with_span(*span))?;
|
|
Ok(match semi {
|
|
Semi::Terminated => ConValue::Empty,
|
|
Semi::Unterminated => out,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Interpret for Expr {
|
|
#[inline]
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { span, kind } = self;
|
|
kind.interpret(env).map_err(|err| err.with_span(*span))
|
|
}
|
|
}
|
|
|
|
impl Interpret for ExprKind {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
match self {
|
|
ExprKind::Empty => Ok(ConValue::Empty),
|
|
ExprKind::Closure(v) => v.interpret(env),
|
|
ExprKind::Quote(q) => q.interpret(env),
|
|
ExprKind::Let(v) => v.interpret(env),
|
|
ExprKind::Match(v) => v.interpret(env),
|
|
ExprKind::Assign(v) => v.interpret(env),
|
|
ExprKind::Modify(v) => v.interpret(env),
|
|
ExprKind::Binary(v) => v.interpret(env),
|
|
ExprKind::Unary(v) => v.interpret(env),
|
|
ExprKind::Cast(v) => v.interpret(env),
|
|
ExprKind::Member(v) => v.interpret(env),
|
|
ExprKind::Index(v) => v.interpret(env),
|
|
ExprKind::Structor(v) => v.interpret(env),
|
|
ExprKind::Path(v) => v.interpret(env),
|
|
ExprKind::Literal(v) => v.interpret(env),
|
|
ExprKind::Array(v) => v.interpret(env),
|
|
ExprKind::ArrayRep(v) => v.interpret(env),
|
|
ExprKind::AddrOf(v) => v.interpret(env),
|
|
ExprKind::Block(v) => v.interpret(env),
|
|
ExprKind::Group(v) => v.interpret(env),
|
|
ExprKind::Tuple(v) => v.interpret(env),
|
|
ExprKind::While(v) => v.interpret(env),
|
|
ExprKind::If(v) => v.interpret(env),
|
|
ExprKind::For(v) => v.interpret(env),
|
|
ExprKind::Break(v) => v.interpret(env),
|
|
ExprKind::Return(v) => v.interpret(env),
|
|
ExprKind::Continue => Err(Error::Continue()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Interpret for Closure {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
Ok(ConValue::Closure(
|
|
crate::closure::Closure::new(env, self).into(),
|
|
))
|
|
}
|
|
}
|
|
|
|
impl Interpret for Quote {
|
|
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
|
|
// TODO: squoosh down into a ConValue?
|
|
Ok(ConValue::Quote(self.quote.clone()))
|
|
}
|
|
}
|
|
|
|
impl Interpret for Let {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Let { mutable: _, name, ty: _, init } = self;
|
|
match init.as_ref().map(|i| i.interpret(env)).transpose()? {
|
|
Some(value) => {
|
|
for (name, value) in pattern::substitution(name, value)? {
|
|
env.insert(*name, Some(value));
|
|
}
|
|
}
|
|
None => {
|
|
for name in pattern::variables(name) {
|
|
env.insert(*name, None);
|
|
}
|
|
}
|
|
}
|
|
Ok(ConValue::Empty)
|
|
}
|
|
}
|
|
|
|
impl Interpret for Match {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
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()) {
|
|
let mut env = env.frame("match");
|
|
for (name, value) in substitution {
|
|
env.insert(*name, Some(value));
|
|
}
|
|
return expr.interpret(&mut env);
|
|
}
|
|
}
|
|
Err(Error::MatchNonexhaustive())
|
|
}
|
|
}
|
|
|
|
mod assignment {
|
|
/// Pattern matching engine for assignment
|
|
use super::*;
|
|
use std::collections::HashMap;
|
|
type Namespace = HashMap<Sym, Option<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()))?
|
|
{
|
|
match env.get_mut(*name)? {
|
|
&mut Some(ConValue::Ref(id)) => {
|
|
*(env.get_id_mut(id).ok_or(Error::StackOverflow(id))?) = Some(value);
|
|
}
|
|
other => *other = Some(value),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub(super) fn assign(env: &mut Environment, pat: &Expr, value: ConValue) -> IResult<()> {
|
|
if let Ok(pat) = Pattern::try_from(pat.clone()) {
|
|
return pat_assign(env, &pat, value);
|
|
}
|
|
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::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)
|
|
}
|
|
_ => Err(Error::NotAssignable())?,
|
|
},
|
|
_ => Err(Error::NotAssignable())?,
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
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::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()),
|
|
_ => Err(Error::TypeError()),
|
|
},
|
|
_ => Err(Error::TypeError()),
|
|
}
|
|
}
|
|
|
|
pub fn addrof_path<'e>(
|
|
env: &'e mut Environment,
|
|
path: &[PathPart],
|
|
) -> IResult<&'e mut Option<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),
|
|
_ => Err(Error::NotIndexable()),
|
|
},
|
|
_ => Err(Error::NotAssignable()),
|
|
}
|
|
}
|
|
|
|
pub fn addrof_member<'e>(
|
|
env: &'e mut Environment,
|
|
member: &Member,
|
|
) -> IResult<&'e mut ConValue> {
|
|
let Member { head, kind } = member;
|
|
|
|
let head = addrof(env, head)?;
|
|
project_memberkind(head, kind)
|
|
}
|
|
|
|
fn addrof_index<'e>(env: &'e mut Environment, index: &Index) -> IResult<&'e mut ConValue> {
|
|
let Index { head, indices } = index;
|
|
let indices = indices
|
|
.iter()
|
|
.map(|index| index.interpret(env))
|
|
.collect::<IResult<Vec<_>>>()?;
|
|
|
|
let mut head = addrof(env, head)?;
|
|
for index in indices {
|
|
head = project_index(head, &index)?;
|
|
}
|
|
Ok(head)
|
|
}
|
|
|
|
/// Performs member-access "projection" from a ConValue to a particular element
|
|
pub fn project_memberkind<'v>(
|
|
value: &'v mut ConValue,
|
|
kind: &MemberKind,
|
|
) -> IResult<&'v mut ConValue> {
|
|
match (value, kind) {
|
|
(ConValue::Struct(s), MemberKind::Struct(id)) => {
|
|
s.1.get_mut(id).ok_or(Error::NotDefined(*id))
|
|
}
|
|
(ConValue::TupleStruct(s), MemberKind::Tuple(Literal::Int(id))) => {
|
|
let len = s.1.len();
|
|
s.1.get_mut(*id as usize)
|
|
.ok_or(Error::OobIndex(*id as _, len))
|
|
}
|
|
(ConValue::Tuple(t), MemberKind::Tuple(Literal::Int(id))) => {
|
|
let len = t.len();
|
|
t.get_mut(*id as usize)
|
|
.ok_or(Error::OobIndex(*id as _, len))
|
|
}
|
|
_ => Err(Error::TypeError()),
|
|
}
|
|
}
|
|
|
|
/// Performs index "projection" from a ConValue to a particular element
|
|
pub fn project_index<'v>(
|
|
value: &'v mut ConValue,
|
|
index: &ConValue,
|
|
) -> IResult<&'v mut ConValue> {
|
|
match (value, index) {
|
|
(ConValue::Array(a), ConValue::Int(i)) => {
|
|
let a_len = a.len();
|
|
a.get_mut(*i as usize)
|
|
.ok_or(Error::OobIndex(*i as usize, a_len))
|
|
}
|
|
(ConValue::Slice(_, _), _) => Err(Error::TypeError()),
|
|
_ => Err(Error::NotIndexable()),
|
|
}
|
|
}
|
|
|
|
pub fn project_path_in_namespace<'e>(
|
|
env: &'e mut Namespace,
|
|
path: &[PathPart],
|
|
) -> IResult<&'e mut Option<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),
|
|
_ => Err(Error::NotIndexable()),
|
|
}
|
|
}
|
|
[PathPart::SelfTy, ..] => todo!("calc_address for `Self`"),
|
|
[PathPart::SuperKw, ..] => todo!("calc_address for `super`"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Interpret for Assign {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Assign { parts } = self;
|
|
let (head, tail) = parts.borrow();
|
|
let init = tail.interpret(env)?;
|
|
// Resolve the head pattern
|
|
assignment::assign(env, head, init).map(|_| ConValue::Empty)
|
|
}
|
|
}
|
|
impl Interpret for Modify {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Modify { kind: op, parts } = self;
|
|
let (head, tail) = parts.borrow();
|
|
// Get the initializer and the tail
|
|
let init = tail.interpret(env)?;
|
|
// Resolve the head pattern
|
|
let target = assignment::addrof(env, head)?;
|
|
|
|
match op {
|
|
ModifyKind::Add => target.add_assign(init),
|
|
ModifyKind::Sub => target.sub_assign(init),
|
|
ModifyKind::Mul => target.mul_assign(init),
|
|
ModifyKind::Div => target.div_assign(init),
|
|
ModifyKind::Rem => target.rem_assign(init),
|
|
ModifyKind::And => target.bitand_assign(init),
|
|
ModifyKind::Or => target.bitor_assign(init),
|
|
ModifyKind::Xor => target.bitxor_assign(init),
|
|
ModifyKind::Shl => target.shl_assign(init),
|
|
ModifyKind::Shr => target.shr_assign(init),
|
|
}?;
|
|
Ok(ConValue::Empty)
|
|
}
|
|
}
|
|
impl Interpret for Binary {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Binary { kind, parts } = self;
|
|
let (head, tail) = parts.borrow();
|
|
|
|
let head = head.interpret(env)?;
|
|
match kind {
|
|
BinaryKind::LogAnd => {
|
|
return if head.truthy()? {
|
|
tail.interpret(env)
|
|
} else {
|
|
Ok(head)
|
|
}; // Short circuiting
|
|
}
|
|
BinaryKind::LogOr => {
|
|
return if !head.truthy()? {
|
|
tail.interpret(env)
|
|
} else {
|
|
Ok(head)
|
|
}; // Short circuiting
|
|
}
|
|
BinaryKind::LogXor => {
|
|
return Ok(ConValue::Bool(
|
|
head.truthy()? ^ tail.interpret(env)?.truthy()?,
|
|
));
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
let tail = tail.interpret(env)?;
|
|
match kind {
|
|
BinaryKind::Lt => head.lt(&tail),
|
|
BinaryKind::LtEq => head.lt_eq(&tail),
|
|
BinaryKind::Equal => head.eq(&tail),
|
|
BinaryKind::NotEq => head.neq(&tail),
|
|
BinaryKind::GtEq => head.gt_eq(&tail),
|
|
BinaryKind::Gt => head.gt(&tail),
|
|
BinaryKind::RangeExc => env.call("RangeExc".into(), &[head, tail]),
|
|
BinaryKind::RangeInc => env.call("RangeInc".into(), &[head, tail]),
|
|
BinaryKind::BitAnd => head & tail,
|
|
BinaryKind::BitOr => head | tail,
|
|
BinaryKind::BitXor => head ^ tail,
|
|
BinaryKind::Shl => head << tail,
|
|
BinaryKind::Shr => head >> tail,
|
|
BinaryKind::Add => head + tail,
|
|
BinaryKind::Sub => head - tail,
|
|
BinaryKind::Mul => head * tail,
|
|
BinaryKind::Div => head / tail,
|
|
BinaryKind::Rem => head % tail,
|
|
BinaryKind::Call => match tail {
|
|
ConValue::Empty => head.call(env, &[]),
|
|
ConValue::Tuple(args) => head.call(env, &args),
|
|
_ => Err(Error::TypeError()),
|
|
},
|
|
_ => Ok(head),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Interpret for Unary {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Unary { kind, tail } = self;
|
|
match kind {
|
|
UnaryKind::Loop => loop {
|
|
match tail.interpret(env) {
|
|
Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value),
|
|
Err(Error { kind: ErrorKind::Continue, .. }) => continue,
|
|
e => e?,
|
|
};
|
|
},
|
|
UnaryKind::Deref => {
|
|
let operand = tail.interpret(env)?;
|
|
env.call("deref".into(), &[operand])
|
|
}
|
|
UnaryKind::Neg => {
|
|
let operand = tail.interpret(env)?;
|
|
env.call("neg".into(), &[operand])
|
|
}
|
|
UnaryKind::Not => {
|
|
let operand = tail.interpret(env)?;
|
|
env.call("not".into(), &[operand])
|
|
}
|
|
UnaryKind::RangeExc => {
|
|
let operand = tail.interpret(env)?;
|
|
env.call("RangeTo".into(), &[operand])
|
|
}
|
|
UnaryKind::RangeInc => {
|
|
let operand = tail.interpret(env)?;
|
|
env.call("RangeToInc".into(), &[operand])
|
|
}
|
|
UnaryKind::At => {
|
|
let operand = tail.interpret(env)?;
|
|
println!("{operand}");
|
|
Ok(operand)
|
|
}
|
|
UnaryKind::Tilde => unimplemented!("Tilde operator"),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn cast(env: &Environment, value: ConValue, ty: Sym) -> IResult<ConValue> {
|
|
let value = match value {
|
|
ConValue::Empty => 0,
|
|
ConValue::Int(i) => i as _,
|
|
ConValue::Bool(b) => b as _,
|
|
ConValue::Char(c) => c as _,
|
|
ConValue::Ref(v) => {
|
|
return cast(
|
|
env,
|
|
env.get_id(v).cloned().ok_or(Error::StackUnderflow())?,
|
|
ty,
|
|
);
|
|
}
|
|
// TODO: This, better
|
|
ConValue::Float(_) if ty.starts_with('f') => return Ok(value),
|
|
ConValue::Float(f) => f as _,
|
|
_ if (*ty).eq("str") => return Ok(ConValue::String(format!("{value}").into())),
|
|
_ => Err(Error::TypeError())?,
|
|
};
|
|
Ok(match &*ty {
|
|
"u8" => ConValue::Int(value as u8 as _),
|
|
"i8" => ConValue::Int(value as i8 as _),
|
|
"u16" => ConValue::Int(value as u16 as _),
|
|
"i16" => ConValue::Int(value as i16 as _),
|
|
"u32" => ConValue::Int(value as u32 as _),
|
|
"i32" => ConValue::Int(value as i32 as _),
|
|
"u64" => ConValue::Int(value),
|
|
"i64" => ConValue::Int(value),
|
|
"f32" => ConValue::Float(value as f32 as _),
|
|
"f64" => ConValue::Float(value as f64 as _),
|
|
"char" => ConValue::Char(char::from_u32(value as _).unwrap_or('\u{fffd}')),
|
|
"bool" => ConValue::Bool(value < 0),
|
|
_ => Err(Error::NotDefined(ty))?,
|
|
})
|
|
}
|
|
|
|
impl Interpret for Cast {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Cast { head, ty } = self;
|
|
let value = head.interpret(env)?;
|
|
if TyKind::Empty == ty.kind {
|
|
return Ok(ConValue::Empty);
|
|
};
|
|
let TyKind::Path(Path { absolute: false, parts }) = &ty.kind else {
|
|
Err(Error::TypeError())?
|
|
};
|
|
match parts.as_slice() {
|
|
[PathPart::Ident(ty)] => cast(env, value, *ty),
|
|
_ => Err(Error::TypeError()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Interpret for Member {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Member { head, kind } = self;
|
|
// Attempt member access projection (fast path)
|
|
if let Ok(member) = assignment::addrof_member(env, self) {
|
|
return Ok(member.clone());
|
|
}
|
|
// Evaluate if this can be Self'd
|
|
let value = match (&head.kind, kind) {
|
|
(ExprKind::Path(p), MemberKind::Call(..)) => {
|
|
p.as_sym().map(|name| Ok(ConValue::Ref(env.id_of(name)?))) // "borrow" it
|
|
}
|
|
_ => None,
|
|
};
|
|
// Perform alternate member access
|
|
match (value.unwrap_or_else(|| head.interpret(env))?, kind) {
|
|
(ConValue::Struct(parts), MemberKind::Call(name, args))
|
|
if parts.1.contains_key(name) =>
|
|
{
|
|
let mut values = vec![];
|
|
for arg in &args.exprs {
|
|
values.push(arg.interpret(env)?);
|
|
}
|
|
(parts.1)
|
|
.get(name)
|
|
.cloned()
|
|
.ok_or(Error::NotDefined(*name))?
|
|
.call(env, &values)
|
|
}
|
|
(head, MemberKind::Call(name, args)) => {
|
|
let mut values = vec![head];
|
|
for arg in &args.exprs {
|
|
values.push(arg.interpret(env)?);
|
|
}
|
|
env.call(*name, &values)
|
|
}
|
|
(mut head, kind) => assignment::project_memberkind(&mut head, kind).cloned(),
|
|
}
|
|
}
|
|
}
|
|
impl Interpret for Index {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { head, indices } = self;
|
|
let mut head = head.interpret(env)?;
|
|
for index in indices {
|
|
head = head.index(&index.interpret(env)?, env)?;
|
|
}
|
|
Ok(head)
|
|
}
|
|
}
|
|
impl Interpret for Structor {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { to: Path { absolute: _, parts }, init } = self;
|
|
use std::collections::HashMap;
|
|
|
|
// Look up struct/enum-struct definition
|
|
|
|
let name = match parts.last() {
|
|
Some(PathPart::Ident(name)) => *name,
|
|
Some(PathPart::SelfTy) => "Self".into(),
|
|
Some(PathPart::SuperKw) => "super".into(),
|
|
None => "".into(),
|
|
};
|
|
|
|
let mut map = HashMap::new();
|
|
for Fielder { name, init } in init {
|
|
let value = match init {
|
|
Some(init) => init.interpret(env)?,
|
|
None => env.get(*name)?,
|
|
};
|
|
map.insert(*name, value);
|
|
}
|
|
Ok(ConValue::Struct(Box::new((name, map))))
|
|
}
|
|
}
|
|
|
|
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()))?
|
|
}
|
|
}
|
|
impl Interpret for Literal {
|
|
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
|
|
Ok(match self {
|
|
Literal::String(value) => ConValue::from(value.as_str()),
|
|
Literal::Char(value) => ConValue::Char(*value),
|
|
Literal::Bool(value) => ConValue::Bool(*value),
|
|
Literal::Float(value) => ConValue::Float(f64::from_bits(*value)),
|
|
Literal::Int(value) => ConValue::Int(*value as _),
|
|
})
|
|
}
|
|
}
|
|
impl Interpret for Array {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { values } = self;
|
|
let mut out = vec![];
|
|
for expr in values {
|
|
out.push(expr.interpret(env)?)
|
|
}
|
|
Ok(ConValue::Array(out.into()))
|
|
}
|
|
}
|
|
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()))
|
|
}
|
|
}
|
|
impl Interpret for AddrOf {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { mutable: _, expr } = self;
|
|
match &expr.kind {
|
|
ExprKind::Index(_) => todo!("AddrOf array index"),
|
|
ExprKind::Path(Path { parts, .. }) => match parts.as_slice() {
|
|
[PathPart::Ident(name)] => Ok(ConValue::Ref(env.id_of(*name)?)),
|
|
_ => todo!("Path traversal in AddrOf(\"{self}\")"),
|
|
},
|
|
_ => {
|
|
let value = expr.interpret(env)?;
|
|
let temp = env.stack_alloc(value)?;
|
|
Ok(ConValue::Ref(env::Place::Local(temp)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
impl Interpret for Block {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { stmts } = self;
|
|
let mut env = env.frame("block");
|
|
let mut out = ConValue::Empty;
|
|
for stmt in stmts {
|
|
out = stmt.interpret(&mut env)?;
|
|
}
|
|
Ok(out)
|
|
}
|
|
}
|
|
impl Interpret for Group {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { expr } = self;
|
|
expr.interpret(env)
|
|
}
|
|
}
|
|
impl Interpret for Tuple {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { exprs } = self;
|
|
Ok(ConValue::Tuple(
|
|
exprs
|
|
.iter()
|
|
.try_fold(vec![], |mut out, element| {
|
|
out.push(element.interpret(env)?);
|
|
Ok(out)
|
|
})?
|
|
.into(),
|
|
))
|
|
}
|
|
}
|
|
impl Interpret for While {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { cond, pass, fail } = self;
|
|
loop {
|
|
if cond.interpret(env)?.truthy()? {
|
|
match pass.interpret(env) {
|
|
Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value),
|
|
Err(Error { kind: ErrorKind::Continue, .. }) => continue,
|
|
e => e?,
|
|
};
|
|
} else {
|
|
break fail.interpret(env);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
impl Interpret for If {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { cond, pass, fail } = self;
|
|
if cond.interpret(env)?.truthy()? {
|
|
pass.interpret(env)
|
|
} else {
|
|
fail.interpret(env)
|
|
}
|
|
}
|
|
}
|
|
impl Interpret for For {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { bind, cond, pass, fail } = self;
|
|
let cond = cond.interpret(env)?;
|
|
// TODO: A better iterator model
|
|
let mut bounds: Box<dyn Iterator<Item = ConValue>> = match &cond {
|
|
ConValue::TupleStruct(inner) => match &**inner {
|
|
("RangeExc", values) => match **values {
|
|
[ConValue::Int(from), ConValue::Int(to)] => {
|
|
Box::new((from..to).map(ConValue::Int))
|
|
}
|
|
_ => Err(Error::NotIterable())?,
|
|
},
|
|
("RangeInc", values) => match **values {
|
|
[ConValue::Int(from), ConValue::Int(to)] => {
|
|
Box::new((from..=to).map(ConValue::Int))
|
|
}
|
|
_ => Err(Error::NotIterable())?,
|
|
},
|
|
_ => Err(Error::NotIterable())?,
|
|
},
|
|
ConValue::Array(a) => Box::new(a.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));
|
|
}
|
|
match pass.interpret(&mut env) {
|
|
Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value),
|
|
Err(Error { kind: ErrorKind::Continue, .. }) => continue,
|
|
e => e?,
|
|
};
|
|
} else {
|
|
break fail.interpret(&mut env);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
impl Interpret for Else {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { body } = self;
|
|
match body {
|
|
Some(body) => body.interpret(env),
|
|
None => Ok(ConValue::Empty),
|
|
}
|
|
}
|
|
}
|
|
impl Interpret for Return {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { body } = self;
|
|
Err(Error::Return(
|
|
body.as_ref()
|
|
.map(|body| body.interpret(env))
|
|
.unwrap_or(Ok(ConValue::Empty))?,
|
|
))
|
|
}
|
|
}
|
|
impl Interpret for Break {
|
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
let Self { body } = self;
|
|
Err(Error::Break(
|
|
body.as_ref()
|
|
.map(|body| body.interpret(env))
|
|
.unwrap_or(Ok(ConValue::Empty))?,
|
|
))
|
|
}
|
|
}
|