schala/schala-lang/src/tree_walk_eval/mod.rs

181 lines
4.9 KiB
Rust

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<T> = Result<T, RuntimeError>;
#[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<u8> 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<String> 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<Item = String>) -> 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<Primitive> for MemoryValue {
fn from(prim: Primitive) -> Self {
Self::Primitive(prim)
}
}
#[derive(Debug)]
enum RuntimeValue {
Expression(Expression),
Evaluated(Primitive),
}
impl From<Expression> for RuntimeValue {
fn from(expr: Expression) -> Self {
Self::Expression(expr)
}
}
impl From<Primitive> for RuntimeValue {
fn from(prim: Primitive) -> Self {
Self::Evaluated(prim)
}
}
/// A fully-reduced value
#[derive(Debug, Clone)]
enum Primitive {
Tuple(Vec<Primitive>),
List(Vec<Primitive>),
Literal(Literal),
Callable(Callable),
Object { type_id: TypeId, tag: u32, ordered_fields: Option<Vec<String>>, items: Vec<Primitive> },
}
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(..) => "<some-callable>".to_string(),
}
}
fn unit() -> Self {
Primitive::Tuple(vec![])
}
}
impl From<Literal> 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<Result<String, String>> {
let mut evaluator = evaluator::Evaluator::new(self, type_context);
evaluator.evaluate(reduced, repl)
}
}