use crate::scheme::environment::Environment;
use crate::scheme::parser::Position;
use crate::scheme::value::{Procedure, Value};
use crate::grove::{Grove, Node};
use crate::fot::FotBuilder;
use gc::Gc;
use std::rc::Rc;
use std::cell::RefCell;
thread_local! {
static EVALUATOR_CONTEXT: RefCell<Option<EvaluatorContext>> = RefCell::new(None);
}
#[derive(Clone)]
pub struct EvaluatorContext {
pub grove: Option<Rc<dyn Grove>>,
pub current_node: Option<Rc<Box<dyn Node>>>,
pub backend: Option<Rc<RefCell<dyn FotBuilder>>>,
}
pub fn get_evaluator_context() -> Option<EvaluatorContext> {
EVALUATOR_CONTEXT.with(|ctx| ctx.borrow().clone())
}
fn has_evaluator_context() -> bool {
EVALUATOR_CONTEXT.with(|ctx| ctx.borrow().is_some())
}
fn set_evaluator_context(ctx: EvaluatorContext) {
EVALUATOR_CONTEXT.with(|c| *c.borrow_mut() = Some(ctx));
}
fn clear_evaluator_context() {
EVALUATOR_CONTEXT.with(|c| *c.borrow_mut() = None);
}
use crate::scheme::value::SourceInfo;
#[derive(Debug, Clone)]
pub struct CallFrame {
pub function_name: String,
pub source: Option<SourceInfo>,
}
impl CallFrame {
pub fn new(function_name: String, source: Option<SourceInfo>) -> Self {
CallFrame {
function_name,
source,
}
}
}
#[derive(Debug, Clone)]
pub struct EvalError {
pub message: String,
pub call_stack: Vec<CallFrame>,
}
impl EvalError {
pub fn new(message: String) -> Self {
EvalError {
message,
call_stack: Vec::new(),
}
}
pub fn with_stack(message: String, call_stack: Vec<CallFrame>) -> Self {
EvalError {
message,
call_stack,
}
}
}
impl std::fmt::Display for EvalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)?;
for (i, frame) in self.call_stack.iter().rev().enumerate() {
if let Some(ref source) = frame.source {
if i == 0 {
writeln!(f, "\n{}:{}:{}:I: called from here",
source.file, source.pos.line, source.pos.column)?;
} else {
writeln!(f, "{}:{}:{}:I: called from here",
source.file, source.pos.line, source.pos.column)?;
}
} else {
if i == 0 {
writeln!(f, "\n{}:I: called from here", frame.function_name)?;
} else {
writeln!(f, "{}:I: called from here", frame.function_name)?;
}
}
}
Ok(())
}
}
impl std::error::Error for EvalError {}
pub type EvalResult = Result<Value, EvalError>;
#[derive(Clone)]
pub struct ConstructionRule {
pub element_name: String,
pub expr: Value,
pub source_file: Option<String>,
pub source_pos: Option<Position>,
}
pub struct ProcessingMode {
pub rules: Vec<ConstructionRule>,
pub default_rule: Option<Value>,
}
impl ProcessingMode {
pub fn new() -> Self {
ProcessingMode {
rules: Vec::new(),
default_rule: None,
}
}
pub fn add_rule(&mut self, element_name: String, expr: Value, source_file: Option<String>, source_pos: Option<Position>) {
self.rules.push(ConstructionRule {
element_name,
expr,
source_file,
source_pos
});
}
pub fn add_default_rule(&mut self, expr: Value) {
self.default_rule = Some(expr);
}
pub fn find_match(&self, gi: &str) -> Option<&ConstructionRule> {
self.rules.iter().find(|rule| rule.element_name == gi)
}
}
pub struct Evaluator {
grove: Option<Rc<dyn Grove>>,
current_node: Option<Rc<Box<dyn Node>>>,
processing_mode: ProcessingMode,
backend: Option<Rc<RefCell<dyn FotBuilder>>>,
call_stack: Vec<CallFrame>,
current_source_file: Option<String>,
current_position: Option<Position>,
line_mappings: Vec<LineMapping>,
}
#[derive(Debug, Clone)]
pub struct LineMapping {
pub output_line: usize,
pub source_file: String,
pub source_line: usize,
}
impl Evaluator {
pub fn new() -> Self {
Evaluator {
grove: None,
current_node: None,
processing_mode: ProcessingMode::new(),
backend: None,
call_stack: Vec::new(),
current_source_file: None,
current_position: None,
line_mappings: Vec::new(),
}
}
pub fn with_grove(grove: Rc<dyn Grove>) -> Self {
Evaluator {
grove: Some(grove),
current_node: None,
processing_mode: ProcessingMode::new(),
backend: None,
call_stack: Vec::new(),
current_source_file: None,
current_position: None,
line_mappings: Vec::new(),
}
}
pub fn set_line_mappings(&mut self, mappings: Vec<LineMapping>) {
self.line_mappings = mappings;
}
pub fn set_source_file(&mut self, file: String) {
self.current_source_file = Some(file);
}
pub fn source_file(&self) -> Option<&str> {
self.current_source_file.as_deref()
}
pub fn set_position(&mut self, position: Position) {
self.current_position = Some(position);
}
fn push_call_frame(&mut self, function_name: String, source: Option<SourceInfo>) {
self.call_stack.push(CallFrame::new(function_name, source));
}
fn pop_call_frame(&mut self) {
self.call_stack.pop();
}
fn error_with_stack(&self, message: String) -> EvalError {
let full_message = match (&self.current_source_file, &self.current_position) {
(Some(file), Some(pos)) => {
format!("{}:{}:{}:E: {}", file, pos.line, pos.column, message)
}
(Some(file), None) => {
format!("{}:E: {}", file, message)
}
_ => message,
};
EvalError::with_stack(full_message, self.call_stack.clone())
}
pub fn set_backend(&mut self, backend: Rc<RefCell<dyn FotBuilder>>) {
self.backend = Some(backend);
}
pub fn set_grove(&mut self, grove: Rc<dyn Grove>) {
self.grove = Some(grove);
}
pub fn grove(&self) -> Option<&Rc<dyn Grove>> {
self.grove.as_ref()
}
pub fn set_current_node(&mut self, node: Box<dyn Node>) {
self.current_node = Some(Rc::new(node));
}
pub fn current_node(&self) -> Option<Rc<Box<dyn Node>>> {
self.current_node.clone()
}
pub fn clear_current_node(&mut self) {
self.current_node = None;
}
pub fn process_root(&mut self, env: Gc<Environment>) -> EvalResult {
let root_node = match &self.grove {
Some(grove) => grove.root(),
None => return Err(EvalError::new("No grove set".to_string())),
};
self.current_node = Some(Rc::new(root_node));
self.process_node(env)
}
pub fn process_node(&mut self, env: Gc<Environment>) -> EvalResult {
let node = match &self.current_node {
Some(n) => n.clone(),
None => return Err(EvalError::new("No current node".to_string())),
};
let gi = match node.gi() {
Some(gi) => gi,
None => {
return Ok(Value::Unspecified);
}
};
let rule = self.processing_mode.find_match(&gi);
if let Some(rule) = rule {
let saved_file = self.current_source_file.clone();
let saved_pos = self.current_position.clone();
if let Some(ref rule_file) = rule.source_file {
self.current_source_file = Some(rule_file.clone());
}
if let Some(ref rule_pos) = rule.source_pos {
self.current_position = Some(rule_pos.clone());
}
let result = self.eval(rule.expr.clone(), env);
self.current_source_file = saved_file;
self.current_position = saved_pos;
result
} else if let Some(ref default_expr) = self.processing_mode.default_rule {
self.eval(default_expr.clone(), env)
} else {
self.eval_process_children(env)
}
}
pub fn eval(&mut self, expr: Value, env: Gc<Environment>) -> EvalResult {
let context_was_set = has_evaluator_context();
let previous_context = get_evaluator_context();
set_evaluator_context(EvaluatorContext {
grove: self.grove.clone(),
current_node: self.current_node.clone(),
backend: self.backend.clone(),
});
let result = self.eval_inner(expr, env);
if context_was_set {
if let Some(prev_ctx) = previous_context {
set_evaluator_context(prev_ctx);
}
} else {
clear_evaluator_context();
}
result
}
fn eval_inner(&mut self, expr: Value, env: Gc<Environment>) -> EvalResult {
match expr {
Value::Nil => Ok(Value::Nil),
Value::Bool(_) => Ok(expr),
Value::Integer(_) => Ok(expr),
Value::Real(_) => Ok(expr),
Value::Char(_) => Ok(expr),
Value::String(_) => Ok(expr),
Value::Procedure(_) => Ok(expr),
Value::Vector(_) => Ok(expr), Value::Unspecified => Ok(expr),
Value::Error => Ok(expr),
Value::Node(_) => Ok(expr),
Value::NodeList(_) => Ok(expr),
Value::Sosofo => Ok(expr),
Value::Symbol(ref name) => env
.lookup(name)
.ok_or_else(|| self.error_with_stack(format!("Undefined variable: {}", name))),
Value::Keyword(_) => Ok(expr),
Value::Pair(_) => self.eval_list(expr, env),
}
}
fn eval_list(&mut self, expr: Value, env: Gc<Environment>) -> EvalResult {
if let Value::Pair(ref p) = expr {
let pair_data = p.borrow();
if let Some(ref pos) = pair_data.pos {
if !self.line_mappings.is_empty() {
if let Some(mapping) = self.line_mappings.iter().find(|m| m.output_line == pos.line) {
self.current_source_file = Some(mapping.source_file.clone());
self.current_position = Some(Position {
line: mapping.source_line,
column: pos.column,
});
} else {
self.current_position = Some(pos.clone());
}
} else {
self.current_position = Some(pos.clone());
}
}
}
let (operator, args) = self.list_car_cdr(&expr)?;
if let Value::Symbol(ref sym) = operator {
match &**sym {
"quote" => self.eval_quote(args),
"if" => self.eval_if(args, env),
"define" => self.eval_define(args, env),
"set!" => self.eval_set(args, env),
"lambda" => self.eval_lambda(args, env),
"let" => self.eval_let(args, env),
"let*" => self.eval_let_star(args, env),
"letrec" => self.eval_letrec(args, env),
"begin" => self.eval_begin(args, env),
"cond" => self.eval_cond(args, env),
"case" => self.eval_case(args, env),
"and" => self.eval_and(args, env),
"or" => self.eval_or(args, env),
"apply" => self.eval_apply(args, env),
"map" => self.eval_map(args, env),
"for-each" => self.eval_for_each(args, env),
"node-list-filter" => self.eval_node_list_filter(args, env),
"node-list-map" => self.eval_node_list_map(args, env),
"node-list-some?" => self.eval_node_list_some(args, env),
"load" => self.eval_load(args, env),
"define-language" => self.eval_define_language(args, env),
"declare-flow-object-class" => self.eval_declare_flow_object_class(args, env),
"declare-characteristic" => self.eval_declare_characteristic(args, env),
"element" => self.eval_element(args, env),
"default" => self.eval_default(args, env),
"process-children" => self.eval_process_children(env),
"make" => self.eval_make(args, env),
_ => self.eval_application(operator, args, env),
}
} else {
self.eval_application(operator, args, env)
}
}
fn list_car_cdr(&self, list: &Value) -> Result<(Value, Value), EvalError> {
if let Value::Pair(ref p) = list {
let pair = p.borrow();
Ok((pair.car.clone(), pair.cdr.clone()))
} else {
Err(EvalError::new("Expected list".to_string()))
}
}
fn vec_to_list(&self, vec: Vec<Value>) -> Value {
let mut result = Value::Nil;
for val in vec.iter().rev() {
result = Value::cons(val.clone(), result);
}
result
}
fn list_to_vec(&self, list: Value) -> Result<Vec<Value>, EvalError> {
let mut result = Vec::new();
let mut current = list;
loop {
match current {
Value::Nil => break,
Value::Pair(ref p) => {
let pair = p.borrow();
result.push(pair.car.clone());
let cdr = pair.cdr.clone();
drop(pair); current = cdr;
}
_ => return Err(EvalError::new("Improper list".to_string())),
}
}
Ok(result)
}
fn eval_quote(&mut self, args: Value) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() != 1 {
return Err(EvalError::new("quote requires exactly 1 argument".to_string()));
}
Ok(args_vec[0].clone())
}
fn eval_if(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() < 2 || args_vec.len() > 3 {
return Err(EvalError::new(
"if requires 2 or 3 arguments".to_string(),
));
}
let test = self.eval_inner(args_vec[0].clone(), env.clone())?;
if test.is_true() {
self.eval_inner(args_vec[1].clone(), env)
} else if args_vec.len() == 3 {
self.eval_inner(args_vec[2].clone(), env)
} else {
Ok(Value::Unspecified)
}
}
fn eval_define(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() < 2 {
return Err(EvalError::new(
"define requires at least 2 arguments".to_string(),
));
}
match &args_vec[0] {
Value::Symbol(ref name) => {
if args_vec.len() != 2 {
return Err(EvalError::new(
"define with symbol requires exactly 2 arguments".to_string(),
));
}
let value = self.eval_inner(args_vec[1].clone(), env.clone())?;
env.define(name, value);
Ok(Value::Unspecified)
}
Value::Pair(_) => {
let (name_val, params) = self.list_car_cdr(&args_vec[0])?;
if let Value::Symbol(ref name) = name_val {
let params_vec = if params.is_nil() {
Vec::new()
} else {
self.list_to_vec(params)?
};
let mut param_names = Vec::new();
for param in params_vec {
if let Value::Symbol(ref pname) = param {
param_names.push(pname.to_string());
} else {
return Err(EvalError::new(format!(
"Parameter must be a symbol, got: {:?}",
param
)));
}
}
let body = if args_vec.len() == 2 {
args_vec[1].clone()
} else {
let mut body_list = Value::Nil;
for expr in args_vec[1..].iter().rev() {
body_list = Value::cons(expr.clone(), body_list);
}
Value::cons(Value::symbol("begin"), body_list)
};
let source_info = self.current_source_file.as_ref().map(|file| {
use crate::scheme::parser::Position;
SourceInfo::new(file.clone(), Position::new())
});
let lambda_value = Value::lambda_with_source(
param_names,
body,
env.clone(),
source_info,
Some(name.to_string()),
);
env.define(name, lambda_value);
Ok(Value::Unspecified)
} else {
Err(EvalError::new(
"First element of define must be a symbol".to_string(),
))
}
}
_ => Err(EvalError::new(
"First argument to define must be symbol or list".to_string(),
)),
}
}
fn eval_define_language(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.is_empty() {
return Err(EvalError::new(
"define-language requires at least 1 argument".to_string(),
));
}
if let Value::Symbol(ref name) = args_vec[0] {
env.define(name, args_vec[0].clone());
Ok(Value::Unspecified)
} else {
Err(EvalError::new(
"First argument to define-language must be a symbol".to_string(),
))
}
}
fn eval_declare_flow_object_class(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.is_empty() {
return Err(EvalError::new(
"declare-flow-object-class requires at least 1 argument".to_string(),
));
}
if let Value::Symbol(ref name) = args_vec[0] {
env.define(name, args_vec[0].clone());
Ok(Value::Unspecified)
} else {
Err(EvalError::new(
"First argument to declare-flow-object-class must be a symbol".to_string(),
))
}
}
fn eval_declare_characteristic(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() < 3 {
return Err(EvalError::new(
"declare-characteristic requires at least 3 arguments (name, public-id, default-value)".to_string(),
));
}
if let Value::Symbol(ref name) = args_vec[0] {
let default_value = self.eval(args_vec[2].clone(), env.clone())?;
env.define(name, default_value);
Ok(Value::Unspecified)
} else {
Err(EvalError::new(
"First argument to declare-characteristic must be a symbol".to_string(),
))
}
}
fn eval_element(&mut self, args: Value, _env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() < 2 {
return Err(self.error_with_stack(
"element requires at least 2 arguments (element-name and construction-expression)".to_string(),
));
}
if args_vec.len() > 2 {
return Err(self.error_with_stack(
"element construction rule can only contain one sosofo expression\nTo combine multiple sosofos, use (sosofo-append ...)".to_string(),
));
}
let element_name = if let Value::Symbol(ref name) = args_vec[0] {
name.clone()
} else {
return Err(self.error_with_stack(
"First argument to element must be a symbol".to_string(),
));
};
self.processing_mode.add_rule(
element_name.to_string(),
args_vec[1].clone(),
self.current_source_file.clone(),
self.current_position.clone()
);
Ok(Value::Unspecified)
}
fn eval_default(&mut self, args: Value, _env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.is_empty() {
return Err(self.error_with_stack(
"default requires at least 1 argument (construction-expression)".to_string(),
));
}
if args_vec.len() > 1 {
return Err(self.error_with_stack(
"default construction rule can only contain one sosofo expression\nTo combine multiple sosofos, use (sosofo-append ...)".to_string(),
));
}
self.processing_mode.add_default_rule(args_vec[0].clone());
Ok(Value::Unspecified)
}
fn eval_process_children(&mut self, env: Gc<Environment>) -> EvalResult {
let current_node = match &self.current_node {
Some(node) => node.clone(),
None => return Err(EvalError::new("No current node".to_string())),
};
let mut children = current_node.children();
let mut result = Value::Unspecified;
while !children.is_empty() {
if let Some(child_node) = children.first() {
let saved_node = self.current_node.clone();
self.current_node = Some(Rc::new(child_node));
result = self.process_node(env.clone())?;
self.current_node = saved_node;
}
children = children.rest();
}
Ok(result)
}
fn eval_make(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.is_empty() {
return Err(EvalError::new(
"make requires at least a flow object type".to_string(),
));
}
let fo_type = match &args_vec[0] {
Value::Symbol(s) => s.as_ref(),
_ => return Err(EvalError::new(
"make: first argument must be a flow object type symbol".to_string(),
)),
};
let mut i = 1;
let mut system_id = None;
let mut data = None;
let mut path = None;
let mut body_exprs = Vec::new();
while i < args_vec.len() {
match &args_vec[i] {
Value::Keyword(kw) => {
if i + 1 >= args_vec.len() {
return Err(EvalError::new(
format!("make: keyword {} requires a value", kw),
));
}
let value = self.eval(args_vec[i + 1].clone(), env.clone())?;
match kw.as_ref() {
"system-id" => {
if let Value::String(s) = value {
system_id = Some(s);
} else {
return Err(EvalError::new(
"make: system-id must be a string".to_string(),
));
}
}
"data" => {
if let Value::String(s) = value {
data = Some(s);
} else {
return Err(EvalError::new(
"make: data must be a string".to_string(),
));
}
}
"path" => {
if let Value::String(s) = value {
path = Some(s);
} else {
return Err(EvalError::new(
"make: path must be a string".to_string(),
));
}
}
_ => {
}
}
i += 2;
}
_ => {
body_exprs.push(args_vec[i].clone());
i += 1;
}
}
}
let backend = self.backend.clone();
match backend {
Some(ref backend) => {
match fo_type {
"entity" => {
if let Some(sid) = system_id {
for expr in body_exprs {
self.eval(expr, env.clone())?;
}
let content = backend.borrow().current_output().to_string();
backend.borrow_mut().entity(&sid, &content)
.map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
backend.borrow_mut().clear_buffer();
} else {
return Err(EvalError::new(
"make entity requires system-id: keyword".to_string(),
));
}
}
"formatting-instruction" => {
if let Some(d) = data {
backend.borrow_mut().formatting_instruction(&d)
.map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
} else {
return Err(EvalError::new(
"make formatting-instruction requires data: keyword".to_string(),
));
}
}
"literal" => {
if let Some(d) = data {
backend.borrow_mut().formatting_instruction(&d)
.map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
} else {
return Err(EvalError::new(
"make literal requires data: keyword or a string body".to_string(),
));
}
}
"directory" => {
if let Some(p) = path {
let prev_dir = backend.borrow().current_directory().map(|s| s.to_string());
backend.borrow_mut().directory(&p)
.map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
for expr in body_exprs {
self.eval(expr, env.clone())?;
}
backend.borrow_mut().set_current_directory(prev_dir);
} else {
return Err(EvalError::new(
"make directory requires path: keyword".to_string(),
));
}
}
_ => {
return Ok(Value::Unspecified);
}
}
}
None => {
return Err(EvalError::new(
"make: no backend available".to_string(),
));
}
}
Ok(Value::Unspecified)
}
fn eval_set(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() != 2 {
return Err(EvalError::new(
"set! requires exactly 2 arguments".to_string(),
));
}
if let Value::Symbol(ref name) = args_vec[0] {
let value = self.eval(args_vec[1].clone(), env.clone())?;
env.set(name, value)
.map_err(|e| EvalError::new(e))?;
Ok(Value::Unspecified)
} else {
Err(EvalError::new(
"First argument to set! must be a symbol".to_string(),
))
}
}
fn eval_lambda(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() < 2 {
return Err(EvalError::new(
"lambda requires at least 2 arguments (params and body)".to_string(),
));
}
let params_list = &args_vec[0];
let params_vec = if params_list.is_nil() {
Vec::new()
} else {
self.list_to_vec(params_list.clone())?
};
let mut param_names = Vec::new();
for param in params_vec {
if let Value::Symbol(ref name) = param {
param_names.push(name.to_string());
} else {
return Err(EvalError::new(format!(
"Lambda parameter must be a symbol, got: {:?}",
param
)));
}
}
let body = if args_vec.len() == 2 {
args_vec[1].clone()
} else {
let mut body_list = Value::Nil;
for expr in args_vec[1..].iter().rev() {
body_list = Value::cons(expr.clone(), body_list);
}
Value::cons(Value::symbol("begin"), body_list)
};
let source_info = match (&self.current_source_file, &self.current_position) {
(Some(file), Some(pos)) => {
Some(SourceInfo::new(file.clone(), pos.clone()))
}
(Some(file), None) => {
use crate::scheme::parser::Position;
Some(SourceInfo::new(file.clone(), Position::new()))
}
_ => None,
};
Ok(Value::lambda_with_source(param_names, body, env, source_info, None))
}
fn eval_let(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() < 2 {
return Err(EvalError::new(
"let requires at least 2 arguments".to_string(),
));
}
if let Value::Symbol(ref loop_name) = args_vec[0] {
if args_vec.len() < 3 {
return Err(EvalError::new(
"named let requires at least 3 arguments".to_string(),
));
}
let bindings_list = &args_vec[1];
let bindings = self.list_to_vec(bindings_list.clone())?;
let body = &args_vec[2..];
let mut var_names = Vec::new();
let mut init_values = Vec::new();
for binding in &bindings {
let binding_vec = self.list_to_vec(binding.clone())?;
if binding_vec.len() != 2 {
return Err(EvalError::new(
"named let binding must have exactly 2 elements".to_string(),
));
}
var_names.push(binding_vec[0].clone());
init_values.push(binding_vec[1].clone());
}
let lambda_params = self.vec_to_list(var_names);
let mut lambda_body = vec![Value::symbol("lambda"), lambda_params];
lambda_body.extend_from_slice(body);
let lambda_expr = self.vec_to_list(lambda_body);
let letrec_binding = Value::cons(
Value::symbol(loop_name),
Value::cons(lambda_expr, Value::Nil),
);
let letrec_bindings = Value::cons(letrec_binding, Value::Nil);
let mut call_expr = vec![Value::symbol(loop_name)];
call_expr.extend_from_slice(&init_values);
let call = self.vec_to_list(call_expr);
return self.eval_letrec(self.vec_to_list(vec![letrec_bindings, call]), env);
}
let bindings_list = &args_vec[0];
let bindings = self.list_to_vec(bindings_list.clone())?;
let new_env = Environment::extend(env.clone());
for binding in bindings {
let binding_vec = self.list_to_vec(binding)?;
if binding_vec.len() != 2 {
return Err(EvalError::new(
"let binding must have exactly 2 elements".to_string(),
));
}
if let Value::Symbol(ref name) = binding_vec[0] {
let value = self.eval_inner(binding_vec[1].clone(), env.clone())?;
new_env.define(name, value);
} else {
return Err(EvalError::new(
"Binding variable must be a symbol".to_string(),
));
}
}
let body = &args_vec[1..];
self.eval_sequence(body, new_env)
}
fn eval_let_star(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() < 2 {
return Err(EvalError::new(
"let* requires at least 2 arguments".to_string(),
));
}
let bindings_list = &args_vec[0];
let bindings = self.list_to_vec(bindings_list.clone())?;
let current_env = Environment::extend(env);
for binding in bindings {
let binding_vec = self.list_to_vec(binding)?;
if binding_vec.len() != 2 {
return Err(EvalError::new(
"let* binding must have exactly 2 elements".to_string(),
));
}
if let Value::Symbol(ref name) = binding_vec[0] {
let value = self.eval_inner(binding_vec[1].clone(), current_env.clone())?;
current_env.define(name, value);
} else {
return Err(EvalError::new(
"Binding variable must be a symbol".to_string(),
));
}
}
let body = &args_vec[1..];
self.eval_sequence(body, current_env)
}
fn eval_letrec(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() < 2 {
return Err(EvalError::new(
"letrec requires at least 2 arguments".to_string(),
));
}
let bindings_list = &args_vec[0];
let bindings = self.list_to_vec(bindings_list.clone())?;
let new_env = Environment::extend(env);
let mut var_names = Vec::new();
for binding in &bindings {
let binding_vec = self.list_to_vec(binding.clone())?;
if binding_vec.len() != 2 {
return Err(EvalError::new(
"letrec binding must have exactly 2 elements".to_string(),
));
}
if let Value::Symbol(ref name) = binding_vec[0] {
var_names.push(name.to_string());
new_env.define(name, Value::Unspecified);
} else {
return Err(EvalError::new(
"Binding variable must be a symbol".to_string(),
));
}
}
for (i, binding) in bindings.iter().enumerate() {
let binding_vec = self.list_to_vec(binding.clone())?;
let value = self.eval_inner(binding_vec[1].clone(), new_env.clone())?;
new_env.set(&var_names[i], value)
.map_err(|e| EvalError::new(e))?;
}
let body = &args_vec[1..];
self.eval_sequence(body, new_env)
}
fn eval_begin(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
self.eval_sequence(&args_vec, env)
}
fn eval_cond(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let clauses = self.list_to_vec(args)?;
for clause in clauses {
let clause_vec = self.list_to_vec(clause)?;
if clause_vec.is_empty() {
return Err(EvalError::new("Empty cond clause".to_string()));
}
if let Value::Symbol(ref sym) = clause_vec[0] {
if &**sym == "else" {
return self.eval_sequence(&clause_vec[1..], env);
}
}
let test = self.eval_inner(clause_vec[0].clone(), env.clone())?;
if test.is_true() {
if clause_vec.len() == 1 {
return Ok(test);
} else {
return self.eval_sequence(&clause_vec[1..], env);
}
}
}
Ok(Value::Unspecified)
}
fn eval_case(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let case_position = self.current_position.clone();
let case_file = self.current_source_file.clone();
let args_vec = self.list_to_vec(args)?;
if args_vec.is_empty() {
return Err(EvalError::new("case requires at least 1 argument".to_string()));
}
let key = self.eval_inner(args_vec[0].clone(), env.clone())?;
for clause in &args_vec[1..] {
let clause_vec = self.list_to_vec(clause.clone())?;
if clause_vec.is_empty() {
return Err(EvalError::new("Empty case clause".to_string()));
}
if let Value::Symbol(ref sym) = clause_vec[0] {
if &**sym == "else" {
return self.eval_sequence(&clause_vec[1..], env);
}
}
let datums = self.list_to_vec(clause_vec[0].clone())?;
for datum in datums {
if key.equal(&datum) {
if clause_vec.len() == 1 {
return Ok(Value::Unspecified);
} else {
return self.eval_sequence(&clause_vec[1..], env);
}
}
}
}
self.current_position = case_position.clone();
self.current_source_file = case_file;
Err(self.error_with_stack(format!(
"no clause in case expression matched {:?}",
key
)))
}
fn eval_and(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.is_empty() {
return Ok(Value::bool(true));
}
let mut result = Value::bool(true);
for expr in args_vec {
result = self.eval_inner(expr, env.clone())?;
if !result.is_true() {
return Ok(Value::bool(false));
}
}
Ok(result)
}
fn eval_or(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
for expr in args_vec {
let result = self.eval_inner(expr, env.clone())?;
if result.is_true() {
return Ok(result);
}
}
Ok(Value::bool(false))
}
fn eval_sequence(&mut self, exprs: &[Value], env: Gc<Environment>) -> EvalResult {
if exprs.is_empty() {
return Ok(Value::Unspecified);
}
let mut result = Value::Unspecified;
for expr in exprs {
result = self.eval_inner(expr.clone(), env.clone())?;
}
Ok(result)
}
fn eval_apply(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() != 2 {
return Err(EvalError::new(
"apply requires exactly 2 arguments".to_string(),
));
}
let proc = self.eval_inner(args_vec[0].clone(), env.clone())?;
let arg_list = self.eval_inner(args_vec[1].clone(), env)?;
let arg_values = self.list_to_vec(arg_list)?;
self.apply(proc, arg_values)
}
fn eval_map(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() < 2 {
return Err(EvalError::new("map requires at least 2 arguments".to_string()));
}
let proc = self.eval_inner(args_vec[0].clone(), env.clone())?;
let mut lists = Vec::new();
for i in 1..args_vec.len() {
let list = self.eval_inner(args_vec[i].clone(), env.clone())?;
let list_vec = self.list_to_vec(list)?;
lists.push(list_vec);
}
if lists.is_empty() {
return Ok(Value::Nil);
}
let length = lists[0].len();
for list in &lists[1..] {
if list.len() != length {
return Err(EvalError::new(
"map: all lists must have the same length".to_string(),
));
}
}
let mut result_vec = Vec::new();
for i in 0..length {
let mut proc_args = Vec::new();
for list in &lists {
proc_args.push(list[i].clone());
}
let result = self.apply(proc.clone(), proc_args)?;
result_vec.push(result);
}
let mut result_list = Value::Nil;
for elem in result_vec.into_iter().rev() {
result_list = Value::cons(elem, result_list);
}
Ok(result_list)
}
fn eval_for_each(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() < 2 {
return Err(EvalError::new(
"for-each requires at least 2 arguments".to_string(),
));
}
let proc = self.eval_inner(args_vec[0].clone(), env.clone())?;
let mut lists = Vec::new();
for i in 1..args_vec.len() {
let list = self.eval_inner(args_vec[i].clone(), env.clone())?;
let list_vec = self.list_to_vec(list)?;
lists.push(list_vec);
}
if lists.is_empty() {
return Ok(Value::Unspecified);
}
let length = lists[0].len();
for list in &lists[1..] {
if list.len() != length {
return Err(EvalError::new(
"for-each: all lists must have the same length".to_string(),
));
}
}
for i in 0..length {
let mut proc_args = Vec::new();
for list in &lists {
proc_args.push(list[i].clone());
}
self.apply(proc.clone(), proc_args)?;
}
Ok(Value::Unspecified)
}
fn eval_node_list_filter(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() != 2 {
return Err(EvalError::new("node-list-filter requires exactly 2 arguments".to_string()));
}
let pred = self.eval_inner(args_vec[0].clone(), env.clone())?;
let node_list_val = self.eval_inner(args_vec[1].clone(), env.clone())?;
match node_list_val {
Value::NodeList(ref nl) => {
let mut filtered_nodes = Vec::new();
let mut index = 0;
loop {
if let Some(node) = nl.get(index) {
let node_val = Value::node(node);
let result = self.apply(pred.clone(), vec![node_val.clone()])?;
if !matches!(result, Value::Bool(false)) {
if let Value::Node(n) = node_val {
filtered_nodes.push(n.as_ref().clone_node());
}
}
index += 1;
} else {
break;
}
}
Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(filtered_nodes))))
}
_ => Err(EvalError::new(format!("node-list-filter: second argument not a node-list: {:?}", node_list_val))),
}
}
fn eval_node_list_map(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() != 2 {
return Err(EvalError::new("node-list-map requires exactly 2 arguments".to_string()));
}
let proc = self.eval_inner(args_vec[0].clone(), env.clone())?;
let node_list_val = self.eval_inner(args_vec[1].clone(), env.clone())?;
let mut result_nodes: Vec<Box<dyn crate::grove::Node>> = Vec::new();
match node_list_val {
Value::Node(ref n) => {
let node_val = Value::node(n.as_ref().clone_node());
let result = self.apply(proc, vec![node_val])?;
match result {
Value::Node(n) => {
result_nodes.push(n.as_ref().clone_node());
}
Value::NodeList(nl) => {
let mut index = 0;
while let Some(node) = nl.get(index) {
result_nodes.push(node);
index += 1;
}
}
_ => {
}
}
}
Value::NodeList(ref nl) => {
let mut index = 0;
loop {
if let Some(node) = nl.get(index) {
let node_val = Value::node(node);
let result = self.apply(proc.clone(), vec![node_val])?;
match result {
Value::Node(n) => {
result_nodes.push(n.as_ref().clone_node());
index += 1;
}
Value::NodeList(nl_result) => {
let mut nl_index = 0;
while let Some(node) = nl_result.get(nl_index) {
result_nodes.push(node);
nl_index += 1;
}
index += 1;
}
_ => {
break;
}
}
} else {
break;
}
}
}
_ => return Err(EvalError::new(format!("node-list-map: second argument must be a node or node-list: {:?}", node_list_val))),
}
Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(result_nodes))))
}
fn eval_node_list_some(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() != 2 {
return Err(EvalError::new("node-list-some? requires exactly 2 arguments".to_string()));
}
let pred = self.eval_inner(args_vec[0].clone(), env.clone())?;
let node_list_val = self.eval_inner(args_vec[1].clone(), env.clone())?;
match node_list_val {
Value::NodeList(ref nl) => {
let mut index = 0;
loop {
if let Some(node) = nl.get(index) {
let node_val = Value::node(node);
let result = self.apply(pred.clone(), vec![node_val])?;
if !matches!(result, Value::Bool(false)) {
return Ok(Value::bool(true));
}
index += 1;
} else {
break;
}
}
Ok(Value::bool(false))
}
_ => Err(EvalError::new(format!("node-list-some?: second argument not a node-list: {:?}", node_list_val))),
}
}
fn eval_load(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
let args_vec = self.list_to_vec(args)?;
if args_vec.len() != 1 {
return Err(EvalError::new(
"load requires exactly 1 argument".to_string(),
));
}
let filename_val = self.eval_inner(args_vec[0].clone(), env.clone())?;
let filename = match filename_val {
Value::String(s) => s.to_string(),
_ => return Err(EvalError::new(
format!("load: filename must be a string, got {:?}", filename_val)
)),
};
let contents = std::fs::read_to_string(&filename)
.map_err(|e| EvalError::new(format!("load: cannot read file '{}': {}", filename, e)))?;
let mut parser = crate::scheme::parser::Parser::new_with_filename(&contents, filename.clone());
let mut result = Value::Unspecified;
let prev_source_file = self.current_source_file.clone();
let prev_position = self.current_position.clone();
self.current_source_file = Some(filename.clone());
let eval_result = loop {
let pos = parser.current_position();
match parser.parse() {
Ok(expr) => {
self.current_position = Some(pos);
match self.eval_inner(expr, env.clone()) {
Ok(val) => result = val,
Err(e) => break Err(e),
}
}
Err(e) => {
let error_msg = e.to_string();
if error_msg.contains("Unexpected end of input")
|| error_msg.contains("Expected")
|| error_msg.contains("EOF") {
break Ok(result);
}
break Err(EvalError::new(
format!("load: parse error in '{}': {}", filename, e)
));
}
}
};
self.current_source_file = prev_source_file;
self.current_position = prev_position;
eval_result
}
fn eval_application(
&mut self,
operator: Value,
args: Value,
env: Gc<Environment>,
) -> EvalResult {
let application_pos = self.current_position.clone();
let application_file = self.current_source_file.clone();
let proc = self.eval_inner(operator, env.clone())?;
let mut evaled_args = Vec::new();
let mut current_args = args;
loop {
match current_args {
Value::Nil => break,
Value::Pair(ref p) => {
let pair_borrow = p.borrow();
if let Some(ref pos) = pair_borrow.pos {
if !self.line_mappings.is_empty() {
if let Some(mapping) = self.line_mappings.iter().find(|m| m.output_line == pos.line) {
self.current_source_file = Some(mapping.source_file.clone());
self.current_position = Some(Position {
line: mapping.source_line,
column: pos.column,
});
} else {
self.current_position = Some(pos.clone());
}
} else {
self.current_position = Some(pos.clone());
}
}
let arg = pair_borrow.car.clone();
let cdr = pair_borrow.cdr.clone();
drop(pair_borrow);
evaled_args.push(self.eval_inner(arg, env.clone())?);
current_args = cdr;
}
_ => return Err(EvalError::new("Improper argument list".to_string())),
}
}
self.current_position = application_pos;
self.current_source_file = application_file;
self.apply(proc, evaled_args)
}
fn apply(&mut self, proc: Value, args: Vec<Value>) -> EvalResult {
if let Value::Procedure(ref p) = proc {
match &**p {
Procedure::Primitive { name: _, func } => {
func(&args).map_err(|e| self.error_with_stack(e))
}
Procedure::Lambda { params, body, env, source, name } => {
if args.len() != params.len() {
return Err(self.error_with_stack(format!(
"Lambda expects {} arguments, got {}",
params.len(),
args.len()
)));
}
let saved_file = self.current_source_file.clone();
let saved_pos = self.current_position.clone();
let pushed_frame = if let Some(func_name) = name.clone() {
let call_site = match (&saved_file, &saved_pos) {
(Some(file), Some(pos)) => Some(SourceInfo {
file: file.clone(),
pos: pos.clone(),
}),
_ => None,
};
self.push_call_frame(func_name, call_site);
true
} else {
false
};
if let Some(ref src) = source {
self.current_source_file = Some(src.file.clone());
self.current_position = Some(src.pos.clone());
}
let lambda_env = Environment::extend(env.clone());
for (param_name, arg_value) in params.iter().zip(args.iter()) {
lambda_env.define(param_name, arg_value.clone());
}
let result = self.eval_inner((**body).clone(), lambda_env);
self.current_source_file = saved_file;
self.current_position = saved_pos;
if pushed_frame {
self.pop_call_frame();
}
result
}
}
} else {
Err(self.error_with_stack(format!(
"Not a procedure: {:?}",
proc
)))
}
}
}
impl Default for Evaluator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_env() -> Gc<Environment> {
Environment::new_global()
}
#[test]
fn test_eval_self_evaluating() {
let mut eval = Evaluator::new();
let env = make_env();
assert!(eval.eval(Value::integer(42), env.clone()).unwrap().is_integer());
assert!(eval.eval(Value::bool(true), env.clone()).unwrap().is_bool());
assert!(eval.eval(Value::string("hello".to_string()), env).unwrap().is_string());
}
#[test]
fn test_eval_quote() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("quote"),
Value::cons(
Value::cons(
Value::integer(1),
Value::cons(Value::integer(2), Value::cons(Value::integer(3), Value::Nil)),
),
Value::Nil,
),
);
let result = eval.eval(expr, env).unwrap();
assert!(result.is_list());
}
#[test]
fn test_eval_if_true() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("if"),
Value::cons(
Value::bool(true),
Value::cons(Value::integer(1), Value::cons(Value::integer(2), Value::Nil)),
),
);
let result = eval.eval(expr, env).unwrap();
if let Value::Integer(n) = result {
assert_eq!(n, 1);
} else {
panic!("Expected integer 1");
}
}
#[test]
fn test_eval_if_false() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("if"),
Value::cons(
Value::bool(false),
Value::cons(Value::integer(1), Value::cons(Value::integer(2), Value::Nil)),
),
);
let result = eval.eval(expr, env).unwrap();
if let Value::Integer(n) = result {
assert_eq!(n, 2);
} else {
panic!("Expected integer 2");
}
}
#[test]
fn test_eval_define() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("define"),
Value::cons(Value::symbol("x"), Value::cons(Value::integer(42), Value::Nil)),
);
eval.eval(expr, env.clone()).unwrap();
assert!(env.is_defined("x"));
if let Value::Integer(n) = env.lookup("x").unwrap() {
assert_eq!(n, 42);
}
}
#[test]
fn test_eval_symbol_lookup() {
let mut eval = Evaluator::new();
let env = make_env();
env.define("x", Value::integer(99));
let result = eval.eval(Value::symbol("x"), env).unwrap();
if let Value::Integer(n) = result {
assert_eq!(n, 99);
} else {
panic!("Expected integer 99");
}
}
#[test]
fn test_eval_and() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("and"),
Value::cons(Value::bool(true), Value::cons(Value::bool(true), Value::Nil)),
);
let result = eval.eval(expr, env.clone()).unwrap();
assert!(result.is_true());
let expr = Value::cons(
Value::symbol("and"),
Value::cons(Value::bool(true), Value::cons(Value::bool(false), Value::Nil)),
);
let result = eval.eval(expr, env).unwrap();
assert!(!result.is_true());
}
#[test]
fn test_eval_or() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("or"),
Value::cons(Value::bool(false), Value::cons(Value::bool(true), Value::Nil)),
);
let result = eval.eval(expr, env.clone()).unwrap();
assert!(result.is_true());
let expr = Value::cons(
Value::symbol("or"),
Value::cons(Value::bool(false), Value::cons(Value::bool(false), Value::Nil)),
);
let result = eval.eval(expr, env).unwrap();
assert!(!result.is_true());
}
#[test]
fn test_eval_lambda_creation() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("lambda"),
Value::cons(
Value::cons(Value::symbol("x"), Value::Nil),
Value::cons(Value::symbol("x"), Value::Nil),
),
);
let result = eval.eval(expr, env).unwrap();
assert!(result.is_procedure());
}
#[test]
fn test_eval_lambda_application() {
let mut eval = Evaluator::new();
let env = make_env();
let lambda_expr = Value::cons(
Value::symbol("lambda"),
Value::cons(
Value::cons(Value::symbol("x"), Value::Nil),
Value::cons(Value::symbol("x"), Value::Nil),
),
);
let app_expr = Value::cons(lambda_expr, Value::cons(Value::integer(42), Value::Nil));
let result = eval.eval(app_expr, env).unwrap();
if let Value::Integer(n) = result {
assert_eq!(n, 42);
} else {
panic!("Expected integer 42");
}
}
#[test]
fn test_eval_lambda_multiple_params() {
let mut eval = Evaluator::new();
let env = make_env();
let params = Value::cons(Value::symbol("x"), Value::cons(Value::symbol("y"), Value::Nil));
let body = Value::symbol("x");
let lambda_expr = Value::cons(Value::symbol("lambda"), Value::cons(params, Value::cons(body, Value::Nil)));
let app_expr = Value::cons(
lambda_expr,
Value::cons(Value::integer(1), Value::cons(Value::integer(2), Value::Nil)),
);
let result = eval.eval(app_expr, env).unwrap();
if let Value::Integer(n) = result {
assert_eq!(n, 1);
} else {
panic!("Expected integer 1");
}
}
#[test]
fn test_eval_lambda_wrong_arg_count() {
let mut eval = Evaluator::new();
let env = make_env();
let lambda_expr = Value::cons(
Value::symbol("lambda"),
Value::cons(
Value::cons(Value::symbol("x"), Value::Nil),
Value::cons(Value::symbol("x"), Value::Nil),
),
);
let app_expr = Value::cons(
lambda_expr,
Value::cons(Value::integer(1), Value::cons(Value::integer(2), Value::Nil)),
);
let result = eval.eval(app_expr, env);
assert!(result.is_err());
}
#[test]
fn test_eval_lambda_closure() {
let mut eval = Evaluator::new();
let env = make_env();
env.define("x", Value::integer(10));
let lambda_expr = Value::cons(
Value::symbol("lambda"),
Value::cons(
Value::cons(Value::symbol("y"), Value::Nil),
Value::cons(Value::symbol("x"), Value::Nil),
),
);
let app_expr = Value::cons(lambda_expr, Value::cons(Value::integer(20), Value::Nil));
let result = eval.eval(app_expr, env).unwrap();
if let Value::Integer(n) = result {
assert_eq!(n, 10); } else {
panic!("Expected integer 10 from closure");
}
}
#[test]
fn test_eval_lambda_no_params() {
let mut eval = Evaluator::new();
let env = make_env();
let lambda_expr = Value::cons(
Value::symbol("lambda"),
Value::cons(Value::Nil, Value::cons(Value::integer(42), Value::Nil)),
);
let app_expr = Value::cons(lambda_expr, Value::Nil);
let result = eval.eval(app_expr, env).unwrap();
if let Value::Integer(n) = result {
assert_eq!(n, 42);
} else {
panic!("Expected integer 42");
}
}
#[test]
fn test_eval_lambda_multiple_body_expressions() {
let mut eval = Evaluator::new();
let env = make_env();
let params = Value::cons(Value::symbol("x"), Value::Nil);
let body1 = Value::integer(1);
let body2 = Value::integer(2);
let body3 = Value::symbol("x");
let lambda_expr = Value::cons(
Value::symbol("lambda"),
Value::cons(
params,
Value::cons(body1, Value::cons(body2, Value::cons(body3, Value::Nil))),
),
);
let app_expr = Value::cons(lambda_expr, Value::cons(Value::integer(99), Value::Nil));
let result = eval.eval(app_expr, env).unwrap();
if let Value::Integer(n) = result {
assert_eq!(n, 99);
} else {
panic!("Expected integer 99");
}
}
#[test]
fn test_element_rule_multiple_sosofos_error() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("element"),
Value::cons(
Value::symbol("foo"),
Value::cons(
Value::symbol("expr1"),
Value::cons(Value::symbol("expr2"), Value::Nil),
),
),
);
let result = eval.eval(expr, env);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("can only contain one sosofo expression"));
assert!(err_msg.contains("sosofo-append"));
}
#[test]
fn test_element_rule_single_sosofo_ok() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("element"),
Value::cons(
Value::symbol("foo"),
Value::cons(Value::symbol("expr"), Value::Nil),
),
);
let result = eval.eval(expr, env);
assert!(result.is_ok());
}
#[test]
fn test_default_rule_multiple_sosofos_error() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("default"),
Value::cons(
Value::symbol("expr1"),
Value::cons(Value::symbol("expr2"), Value::Nil),
),
);
let result = eval.eval(expr, env);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("can only contain one sosofo expression"));
assert!(err_msg.contains("sosofo-append"));
}
#[test]
fn test_default_rule_single_sosofo_ok() {
let mut eval = Evaluator::new();
let env = make_env();
let expr = Value::cons(
Value::symbol("default"),
Value::cons(Value::symbol("expr"), Value::Nil),
);
let result = eval.eval(expr, env);
assert!(result.is_ok());
}
}