use std::{convert::From, fmt::Write}; use crate::{ reduced_ir::{Callable, Expression, FunctionDefinition, Literal, ReducedIR}, symbol_table::DefId, type_inference::{TypeContext, TypeId}, util::ScopeStack, }; mod evaluator; mod test; type EvalResult = Result; #[derive(Debug)] pub struct State<'a> { environments: ScopeStack<'a, Memory, MemoryValue>, } //TODO - eh, I dunno, maybe it doesn't matter exactly how memory works in the tree-walking //evaluator #[derive(Debug, PartialEq, Eq, Hash, Clone)] enum Memory { Index(u32), } // This is for function param lookups, and is a hack impl From for Memory { fn from(n: u8) -> Self { Memory::Index(4_000_000 + (n as u32)) } } impl From<&DefId> for Memory { fn from(id: &DefId) -> Self { Self::Index(id.as_u32()) } } #[derive(Debug)] struct RuntimeError { msg: String, } impl From for RuntimeError { fn from(msg: String) -> Self { Self { msg } } } impl From<&str> for RuntimeError { fn from(msg: &str) -> Self { Self { msg: msg.to_string() } } } impl RuntimeError { #[allow(dead_code)] fn get_msg(&self) -> String { format!("Runtime error: {}", self.msg) } } fn delim_wrapped(lhs: char, rhs: char, terms: impl Iterator) -> String { let mut buf = String::new(); write!(buf, "{}", lhs).unwrap(); for term in terms.map(Some).intersperse(None) { match term { Some(e) => write!(buf, "{}", e).unwrap(), None => write!(buf, ", ").unwrap(), }; } write!(buf, "{}", rhs).unwrap(); buf } /// Anything that can be stored in memory; that is, a function definition, or a fully-evaluated /// program value. #[derive(Debug)] enum MemoryValue { Function(FunctionDefinition), Primitive(Primitive), } impl From for MemoryValue { fn from(prim: Primitive) -> Self { Self::Primitive(prim) } } #[derive(Debug)] enum RuntimeValue { Expression(Expression), Evaluated(Primitive), } impl From for RuntimeValue { fn from(expr: Expression) -> Self { Self::Expression(expr) } } impl From for RuntimeValue { fn from(prim: Primitive) -> Self { Self::Evaluated(prim) } } /// A fully-reduced value #[derive(Debug, Clone)] enum Primitive { Tuple(Vec), List(Vec), Literal(Literal), Callable(Callable), Object { type_id: TypeId, tag: u32, ordered_fields: Option>, items: Vec }, } impl Primitive { fn to_repl(&self, type_context: &TypeContext) -> String { match self { Primitive::Object { type_id, items, tag, ordered_fields: _ } if items.is_empty() => type_context.variant_local_name(type_id, *tag).unwrap().to_string(), Primitive::Object { type_id, items, tag, ordered_fields: None } => { format!( "{}{}", type_context.variant_local_name(type_id, *tag).unwrap(), delim_wrapped('(', ')', items.iter().map(|item| item.to_repl(type_context))) ) } Primitive::Object { type_id, items, tag, ordered_fields: Some(fields) } => { let mut buf = format!("{} {{ ", type_context.variant_local_name(type_id, *tag).unwrap()); for item in fields.iter().zip(items.iter()).map(Some).intersperse(None) { match item { Some((name, val)) => write!(buf, "{}: {}", name, val.to_repl(type_context)).unwrap(), None => write!(buf, ", ").unwrap(), } } write!(buf, " }}").unwrap(); buf } Primitive::Literal(lit) => match lit { Literal::Nat(n) => format!("{}", n), Literal::Int(i) => format!("{}", i), Literal::Float(f) => format!("{}", f), Literal::Bool(b) => format!("{}", b), Literal::StringLit(s) => format!("\"{}\"", s), }, Primitive::Tuple(terms) => delim_wrapped('(', ')', terms.iter().map(|x| x.to_repl(type_context))), Primitive::List(terms) => delim_wrapped('[', ']', terms.iter().map(|x| x.to_repl(type_context))), Primitive::Callable(..) => "".to_string(), } } fn unit() -> Self { Primitive::Tuple(vec![]) } } impl From for Primitive { fn from(lit: Literal) -> Self { Primitive::Literal(lit) } } impl<'a> State<'a> { pub fn new() -> Self { Self { environments: ScopeStack::new(Some("global".to_string())) } } pub fn evaluate( &mut self, reduced: ReducedIR, type_context: &TypeContext, repl: bool, ) -> Vec> { let mut evaluator = evaluator::Evaluator::new(self, type_context); evaluator.evaluate(reduced, repl) } }