cl-interpret: Pure value stack v1, references v2
References actually work! :D They can also be stale :(
This commit is contained in:
parent
06bcb6b7c6
commit
4bc088f277
@ -5,10 +5,7 @@ use crate::{
|
||||
env::Environment,
|
||||
error::{Error, IResult},
|
||||
};
|
||||
use std::{
|
||||
io::{stdout, Write},
|
||||
slice,
|
||||
};
|
||||
use std::io::{stdout, Write};
|
||||
|
||||
/// A function built into the interpreter.
|
||||
#[derive(Clone, Copy)]
|
||||
@ -78,7 +75,7 @@ pub macro builtin(
|
||||
$(#[$($meta)*])*
|
||||
fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
|
||||
// Set up the builtin! environment
|
||||
$(let $env = _env;)?
|
||||
$(#[allow(unused)]let $env = _env;)?
|
||||
// Allow for single argument `fn foo(args @ ..)` pattern
|
||||
#[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)]
|
||||
let [$($arg),*] = _args else {
|
||||
@ -152,19 +149,21 @@ pub const Builtins: &[Builtin] = &builtins![
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn builtins() @env {
|
||||
for builtin in env.builtins().values().flatten() {
|
||||
println!("{builtin}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
// fn builtins() @env {
|
||||
// for builtin in env.builtins().values().flatten() {
|
||||
// println!("{builtin}");
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/// Returns the length of the input list as a [ConValue::Int]
|
||||
fn len(list) @env {
|
||||
Ok(match list {
|
||||
ConValue::Empty => 0,
|
||||
ConValue::String(s) => s.chars().count() as _,
|
||||
ConValue::Ref(r) => return len(env, slice::from_ref(&r.borrow())),
|
||||
ConValue::Ref(r) => {
|
||||
return len(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
|
||||
}
|
||||
ConValue::Array(t) => t.len() as _,
|
||||
ConValue::Tuple(t) => t.len() as _,
|
||||
_ => Err(Error::TypeError)?,
|
||||
@ -176,6 +175,10 @@ pub const Builtins: &[Builtin] = &builtins![
|
||||
Ok(ConValue::Empty)
|
||||
}
|
||||
|
||||
fn slice_of(ConValue::Ref(arr), ConValue::Int(start)) {
|
||||
Ok(ConValue::Slice(*arr, *start as usize))
|
||||
}
|
||||
|
||||
/// Returns a shark
|
||||
fn shark() {
|
||||
Ok('\u{1f988}')
|
||||
@ -330,9 +333,9 @@ pub const Math: &[Builtin] = &builtins![
|
||||
}
|
||||
|
||||
/// Does the opposite of `&`
|
||||
fn deref(tail) {
|
||||
fn deref(tail) @env {
|
||||
Ok(match tail {
|
||||
ConValue::Ref(v) => v.take(),
|
||||
ConValue::Ref(v) => env.get_id(*v).cloned().ok_or(Error::StackOverflow(*v))?,
|
||||
_ => tail.clone(),
|
||||
})
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use super::{
|
||||
function::Function,
|
||||
Callable, Environment,
|
||||
};
|
||||
use std::{cell::RefCell, collections::HashMap, ops::*, rc::Rc};
|
||||
use std::{collections::HashMap, ops::*, rc::Rc};
|
||||
|
||||
/*
|
||||
A Value can be:
|
||||
@ -33,7 +33,6 @@ struct {
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
type Integer = isize;
|
||||
|
||||
/// A Conlang value stores data in the interpreter
|
||||
@ -53,7 +52,9 @@ pub enum ConValue {
|
||||
/// A string
|
||||
String(Sym),
|
||||
/// A reference
|
||||
Ref(Rc<RefCell<ConValue>>),
|
||||
Ref(usize),
|
||||
/// A reference to an array
|
||||
Slice(usize, usize),
|
||||
/// An Array
|
||||
Array(Box<[ConValue]>),
|
||||
/// A tuple
|
||||
@ -90,7 +91,7 @@ impl ConValue {
|
||||
Self::Struct(Box::new((name, values)))
|
||||
}
|
||||
|
||||
pub fn index(&self, index: &Self) -> IResult<ConValue> {
|
||||
pub fn index(&self, index: &Self, env: &Environment) -> IResult<ConValue> {
|
||||
let Self::Int(index) = index else {
|
||||
Err(Error::TypeError)?
|
||||
};
|
||||
@ -104,6 +105,10 @@ impl ConValue {
|
||||
.get(*index as usize)
|
||||
.cloned()
|
||||
.ok_or(Error::OobIndex(*index as usize, arr.len())),
|
||||
ConValue::Slice(id, start) => env
|
||||
.get_id(*id)
|
||||
.ok_or(Error::StackOverflow(*id))?
|
||||
.index(&ConValue::Int((*index as usize + start) as isize), env),
|
||||
_ => Err(Error::TypeError),
|
||||
}
|
||||
}
|
||||
@ -295,7 +300,8 @@ impl std::fmt::Display for ConValue {
|
||||
ConValue::Bool(v) => v.fmt(f),
|
||||
ConValue::Char(v) => v.fmt(f),
|
||||
ConValue::String(v) => v.fmt(f),
|
||||
ConValue::Ref(v) => write!(f, "&{}", v.borrow()),
|
||||
ConValue::Ref(v) => write!(f, "&<{}>", v),
|
||||
ConValue::Slice(v, len) => write!(f, "&<{v}>[{len}..]"),
|
||||
ConValue::Array(array) => {
|
||||
'['.fmt(f)?;
|
||||
for (idx, element) in array.iter().enumerate() {
|
||||
|
@ -19,30 +19,35 @@ use std::{
|
||||
|
||||
type StackFrame = HashMap<Sym, Option<ConValue>>;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct EnvFrame {
|
||||
/// The length of the array when this stack frame was constructed
|
||||
pub name: Option<&'static str>,
|
||||
pub base: usize,
|
||||
pub binds: HashMap<Sym, usize>,
|
||||
}
|
||||
|
||||
/// Implements a nested lexical scope
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Environment {
|
||||
builtin: StackFrame,
|
||||
global: Vec<(StackFrame, &'static str)>,
|
||||
frames: Vec<(StackFrame, &'static str)>,
|
||||
values: Vec<Option<ConValue>>,
|
||||
frames: Vec<EnvFrame>,
|
||||
}
|
||||
|
||||
impl Display for Environment {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for (frame, name) in self
|
||||
.global
|
||||
.iter()
|
||||
.rev()
|
||||
.take(2)
|
||||
.rev()
|
||||
.chain(self.frames.iter())
|
||||
{
|
||||
writeln!(f, "--- {name} ---")?;
|
||||
for (var, val) in frame {
|
||||
for EnvFrame { name, base: _, binds } in self.frames.iter().rev() {
|
||||
writeln!(
|
||||
f,
|
||||
"--- {} ---",
|
||||
if let Some(name) = name { name } else { "" }
|
||||
)?;
|
||||
for (var, val) in binds {
|
||||
write!(f, "{var}: ")?;
|
||||
match val {
|
||||
Some(value) => writeln!(f, "\t{value}"),
|
||||
None => writeln!(f, "<undefined>"),
|
||||
match self.values.get(*val) {
|
||||
Some(Some(value)) => writeln!(f, "\t{value}"),
|
||||
Some(None) => writeln!(f, "<undefined>"),
|
||||
None => writeln!(f, "ERROR: {var} address blows the stack!"),
|
||||
}?
|
||||
}
|
||||
}
|
||||
@ -51,18 +56,18 @@ impl Display for Environment {
|
||||
}
|
||||
impl Default for Environment {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
builtin: to_hashmap(Builtins.iter().chain(Math.iter())),
|
||||
global: vec![(HashMap::new(), "globals")],
|
||||
frames: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut values = Vec::new();
|
||||
|
||||
fn to_hashmap(from: impl IntoIterator<Item = &'static Builtin>) -> HashMap<Sym, Option<ConValue>> {
|
||||
from.into_iter()
|
||||
.map(|v| (v.name(), Some(v.into())))
|
||||
.collect()
|
||||
let mut builtins = EnvFrame { name: Some("builtin"), base: 0, binds: HashMap::new() };
|
||||
for bu in Builtins.iter().chain(Math.iter()) {
|
||||
builtins.binds.insert(bu.name(), values.len());
|
||||
values.push(Some(bu.into()));
|
||||
}
|
||||
let globals =
|
||||
EnvFrame { name: Some("globals"), base: values.len(), binds: HashMap::new() };
|
||||
|
||||
Self { values, frames: vec![builtins, globals] }
|
||||
}
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
@ -71,36 +76,12 @@ impl Environment {
|
||||
}
|
||||
/// Creates an [Environment] with no [builtins](super::builtin)
|
||||
pub fn no_builtins() -> Self {
|
||||
Self {
|
||||
builtin: HashMap::new(),
|
||||
global: vec![(Default::default(), "globals")],
|
||||
frames: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn builtins(&self) -> &StackFrame {
|
||||
&self.builtin
|
||||
}
|
||||
|
||||
pub fn add_builtin(&mut self, builtin: &'static Builtin) -> &mut Self {
|
||||
self.builtin.insert(builtin.name(), Some(builtin.into()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) {
|
||||
for builtin in builtins {
|
||||
self.add_builtin(builtin);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
|
||||
self.frames.push((frame, name));
|
||||
}
|
||||
|
||||
pub fn pop_frame(&mut self) -> Option<(StackFrame, &'static str)> {
|
||||
self.frames.pop()
|
||||
let globals = EnvFrame { name: Some("globals"), base: 0, binds: HashMap::new() };
|
||||
|
||||
Self { values: Vec::new(), frames: vec![Default::default(), globals] }
|
||||
}
|
||||
|
||||
/// Reflexively evaluates a node
|
||||
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
|
||||
node.interpret(self)
|
||||
}
|
||||
@ -108,102 +89,148 @@ impl Environment {
|
||||
/// Calls a function inside the interpreter's scope,
|
||||
/// and returns the result
|
||||
pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> {
|
||||
// FIXME: Clone to satisfy the borrow checker
|
||||
let function = self.get(name)?.clone();
|
||||
let function = self.get(name)?;
|
||||
function.call(self, args)
|
||||
}
|
||||
|
||||
/// Adds builtins
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if globals table is non-empty!
|
||||
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) {
|
||||
let Self { values, frames } = self;
|
||||
// Globals must be EMPTY to add builtins
|
||||
assert_eq!(frames[1].binds.len(), 0);
|
||||
for builtin in builtins {
|
||||
if let Some(EnvFrame { name: _, base: _, binds }) = frames.first_mut() {
|
||||
binds.insert(builtin.name(), values.len());
|
||||
values.push(Some(builtin.into()));
|
||||
}
|
||||
}
|
||||
frames[1].base = values.len();
|
||||
}
|
||||
|
||||
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
|
||||
self.enter(name);
|
||||
for (k, v) in frame {
|
||||
self.insert(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_frame(&mut self) -> Option<(StackFrame, &'static str)> {
|
||||
let mut out = HashMap::new();
|
||||
let EnvFrame { name, base, binds } = self.frames.pop()?;
|
||||
for (k, v) in binds {
|
||||
out.insert(k, self.values.get_mut(v).and_then(std::mem::take));
|
||||
}
|
||||
self.values.truncate(base);
|
||||
Some((out, name.unwrap_or("")))
|
||||
}
|
||||
|
||||
/// Enters a nested scope, returning a [`Frame`] stack-guard.
|
||||
///
|
||||
/// [`Frame`] implements Deref/DerefMut for [`Environment`].
|
||||
pub fn frame(&mut self, name: &'static str) -> Frame {
|
||||
Frame::new(self, name)
|
||||
}
|
||||
|
||||
/// Resolves a variable mutably.
|
||||
///
|
||||
/// Returns a mutable reference to the variable's record, if it exists.
|
||||
pub fn get_mut(&mut self, id: Sym) -> IResult<&mut Option<ConValue>> {
|
||||
for (frame, _) in self.frames.iter_mut().rev() {
|
||||
if let Some(var) = frame.get_mut(&id) {
|
||||
return Ok(var);
|
||||
}
|
||||
}
|
||||
for (frame, _) in self.global.iter_mut().rev() {
|
||||
if let Some(var) = frame.get_mut(&id) {
|
||||
return Ok(var);
|
||||
}
|
||||
}
|
||||
self.builtin.get_mut(&id).ok_or(Error::NotDefined(id))
|
||||
pub fn get_mut(&mut self, name: Sym) -> IResult<&mut Option<ConValue>> {
|
||||
let id = self.id_of(name)?;
|
||||
self.values.get_mut(id).ok_or(Error::NotDefined(name))
|
||||
}
|
||||
|
||||
/// Resolves a variable immutably.
|
||||
///
|
||||
/// Returns a reference to the variable's contents, if it is defined and initialized.
|
||||
pub fn get(&self, id: Sym) -> IResult<ConValue> {
|
||||
for (frame, _) in self.frames.iter().rev() {
|
||||
match frame.get(&id) {
|
||||
Some(Some(var)) => return Ok(var.clone()),
|
||||
Some(None) => return Err(Error::NotInitialized(id)),
|
||||
_ => (),
|
||||
}
|
||||
pub fn get(&self, name: Sym) -> IResult<ConValue> {
|
||||
let id = self.id_of(name)?;
|
||||
match self.values.get(id).ok_or(Error::NotDefined(name))? {
|
||||
Some(value) => Ok(value.clone()),
|
||||
None => Err(Error::NotInitialized(name)),
|
||||
}
|
||||
for (frame, _) in self.global.iter().rev() {
|
||||
match frame.get(&id) {
|
||||
Some(Some(var)) => return Ok(var.clone()),
|
||||
Some(None) => return Err(Error::NotInitialized(id)),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
self.builtin
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.flatten()
|
||||
.ok_or(Error::NotDefined(id))
|
||||
}
|
||||
|
||||
pub(crate) fn get_local(&self, id: Sym) -> IResult<ConValue> {
|
||||
for (frame, _) in self.frames.iter().rev() {
|
||||
match frame.get(&id) {
|
||||
Some(Some(var)) => return Ok(var.clone()),
|
||||
Some(None) => return Err(Error::NotInitialized(id)),
|
||||
_ => (),
|
||||
for EnvFrame { binds, .. } in self.frames.iter().skip(2).rev() {
|
||||
if let Some(var) = binds.get(&id) {
|
||||
if let Some(var) = self.values.get(*var) {
|
||||
return match var {
|
||||
Some(value) => Ok(value.clone()),
|
||||
None => Err(Error::NotInitialized(id)),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::NotInitialized(id))
|
||||
Err(Error::NotDefined(id))
|
||||
}
|
||||
|
||||
pub fn id_of(&self, name: Sym) -> IResult<usize> {
|
||||
for EnvFrame { binds, .. } in self.frames.iter().rev() {
|
||||
if let Some(id) = binds.get(&name).copied() {
|
||||
return Ok(id);
|
||||
}
|
||||
}
|
||||
Err(Error::NotDefined(name))
|
||||
}
|
||||
|
||||
pub fn get_id(&self, id: usize) -> Option<&ConValue> {
|
||||
self.values.get(id)?.as_ref()
|
||||
}
|
||||
|
||||
pub fn get_id_mut(&mut self, id: usize) -> Option<&mut Option<ConValue>> {
|
||||
self.values.get_mut(id)
|
||||
}
|
||||
|
||||
/// Inserts a new [ConValue] into this [Environment]
|
||||
pub fn insert(&mut self, id: Sym, value: Option<ConValue>) {
|
||||
if let Some((frame, _)) = self.frames.last_mut() {
|
||||
frame.insert(id, value);
|
||||
} else if let Some((frame, _)) = self.global.last_mut() {
|
||||
frame.insert(id, value);
|
||||
pub fn insert(&mut self, k: Sym, v: Option<ConValue>) {
|
||||
if self.bind_raw(k, self.values.len()).is_some() {
|
||||
self.values.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience function for registering a [FnDecl] as a [Function]
|
||||
pub fn insert_fn(&mut self, decl: &FnDecl) {
|
||||
let FnDecl { name, .. } = decl;
|
||||
let (name, function) = (name, Rc::new(Function::new(decl)));
|
||||
if let Some((frame, _)) = self.frames.last_mut() {
|
||||
frame.insert(*name, Some(ConValue::Function(function.clone())));
|
||||
} else if let Some((frame, _)) = self.global.last_mut() {
|
||||
frame.insert(*name, Some(ConValue::Function(function.clone())));
|
||||
}
|
||||
let (name, function) = (*name, Rc::new(Function::new(decl)));
|
||||
self.insert(name, Some(ConValue::Function(function.clone())));
|
||||
// Tell the function to lift its upvars now, after it's been declared
|
||||
function.lift_upvars(self);
|
||||
}
|
||||
|
||||
/// Inserts a temporary/anonymous variable, and returns its address
|
||||
pub fn insert_temporary(&mut self, value: ConValue) -> IResult<usize> {
|
||||
let adr = self.values.len();
|
||||
self.values.push(Some(value));
|
||||
Ok(adr)
|
||||
}
|
||||
|
||||
pub fn bind_raw(&mut self, name: Sym, id: usize) -> Option<()> {
|
||||
let EnvFrame { name: _, base: _, binds } = self.frames.last_mut()?;
|
||||
binds.insert(name, id);
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Functions which aid in the implementation of [`Frame`]
|
||||
impl Environment {
|
||||
/// Enters a scope, creating a new namespace for variables
|
||||
fn enter(&mut self, name: &'static str) -> &mut Self {
|
||||
self.frames.push((Default::default(), name));
|
||||
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 {
|
||||
self.frames.pop();
|
||||
if let Some(frame) = self.frames.pop() {
|
||||
self.values.truncate(frame.base);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ pub enum Error {
|
||||
Continue,
|
||||
/// Underflowed the stack
|
||||
StackUnderflow,
|
||||
/// Overflowed the stack
|
||||
StackOverflow(usize),
|
||||
/// Exited the last scope
|
||||
ScopeExit,
|
||||
/// Type incompatibility
|
||||
@ -70,6 +72,9 @@ impl std::fmt::Display for Error {
|
||||
Error::BadBreak(value) => write!(f, "rogue break: {value}"),
|
||||
Error::Continue => "continue".fmt(f),
|
||||
Error::StackUnderflow => "Stack underflow".fmt(f),
|
||||
Error::StackOverflow(id) => {
|
||||
write!(f, "Attempt to access <{id}> resulted in stack overflow.")
|
||||
}
|
||||
Error::ScopeExit => "Exited the last scope. This is a logic bug.".fmt(f),
|
||||
Error::TypeError => "Incompatible types".fmt(f),
|
||||
Error::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f),
|
||||
|
@ -8,7 +8,7 @@
|
||||
use super::*;
|
||||
use cl_ast::{ast_visitor::Visit, *};
|
||||
use cl_structures::intern::interned::Interned;
|
||||
use std::{borrow::Borrow, cell::RefCell, rc::Rc};
|
||||
use std::borrow::Borrow;
|
||||
/// A work-in-progress tree walk interpreter for Conlang
|
||||
pub trait Interpret {
|
||||
/// Interprets this thing in the given [`Environment`].
|
||||
@ -86,6 +86,7 @@ impl Interpret for Static {
|
||||
}
|
||||
}
|
||||
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, kind } = self;
|
||||
env.push_frame(Interned::to_ref(name), Default::default());
|
||||
@ -203,6 +204,7 @@ impl Interpret for Use {
|
||||
|
||||
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;
|
||||
|
||||
@ -371,8 +373,8 @@ mod assignment {
|
||||
pattern::substitution(pat, value).map_err(|_| Error::PatFailed(pat.clone().into()))?
|
||||
{
|
||||
match env.get_mut(*name)? {
|
||||
Some(ConValue::Ref(r)) => {
|
||||
r.replace(value);
|
||||
&mut Some(ConValue::Ref(id)) => {
|
||||
*(env.get_id_mut(id).ok_or(Error::StackOverflow(id))?) = Some(value);
|
||||
}
|
||||
other => *other = Some(value),
|
||||
}
|
||||
@ -388,15 +390,18 @@ mod assignment {
|
||||
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> {
|
||||
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()
|
||||
@ -484,6 +489,7 @@ mod assignment {
|
||||
a.get_mut(*i as usize)
|
||||
.ok_or(Error::OobIndex(*i as usize, a_len))
|
||||
}
|
||||
(ConValue::Slice(_, _), _) => Err(Error::TypeError),
|
||||
_ => Err(Error::NotIndexable),
|
||||
}
|
||||
}
|
||||
@ -641,16 +647,23 @@ impl Interpret for Unary {
|
||||
}
|
||||
}
|
||||
|
||||
fn cast(value: ConValue, ty: Sym) -> IResult<ConValue> {
|
||||
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((*v).borrow().clone(), ty),
|
||||
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 {
|
||||
@ -681,7 +694,7 @@ impl Interpret for Cast {
|
||||
Err(Error::TypeError)?
|
||||
};
|
||||
match parts.as_slice() {
|
||||
[PathPart::Ident(ty)] => cast(value, *ty),
|
||||
[PathPart::Ident(ty)] => cast(env, value, *ty),
|
||||
_ => Err(Error::TypeError),
|
||||
}
|
||||
}
|
||||
@ -724,7 +737,7 @@ impl Interpret for Index {
|
||||
let Self { head, indices } = self;
|
||||
let mut head = head.interpret(env)?;
|
||||
for index in indices {
|
||||
head = head.index(&index.interpret(env)?)?;
|
||||
head = head.index(&index.interpret(env)?, env)?;
|
||||
}
|
||||
Ok(head)
|
||||
}
|
||||
@ -803,8 +816,15 @@ impl Interpret for AddrOf {
|
||||
let Self { mutable: _, expr } = self;
|
||||
match &expr.kind {
|
||||
ExprKind::Index(_) => todo!("AddrOf array index"),
|
||||
ExprKind::Path(_) => todo!("Path traversal in addrof"),
|
||||
_ => Ok(ConValue::Ref(Rc::new(RefCell::new(expr.interpret(env)?)))),
|
||||
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.insert_temporary(value)?;
|
||||
Ok(ConValue::Ref(temp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,9 @@ pub fn append_sub<'pat>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
(Pattern::Ref(_, pat), ConValue::Ref(r)) => append_sub(sub, pat, r.borrow().clone()),
|
||||
(Pattern::Ref(_, pat), ConValue::Ref(r)) => {
|
||||
todo!("Dereference <{r}> in pattern matching {pat}")
|
||||
}
|
||||
|
||||
(Pattern::Array(patterns), ConValue::Array(values)) => {
|
||||
match rest_binding(sub, patterns, values.into_vec().into())? {
|
||||
|
Loading…
x
Reference in New Issue
Block a user