use std::{
collections::{HashMap, HashSet},
fmt::Debug,
sync::Arc,
};
use rust_decimal::Decimal;
use crate::{
ast::{self, Expression, List},
builtins::NIL,
};
type NativeFunction = fn(&mut Interpreter, &[Arc<Value>]) -> Arc<Value>;
#[derive(Debug, Default)]
pub struct Interpreter {
stack: Vec<HashMap<Arc<str>, Arc<Value>>>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Value {
Identifier(Arc<str>),
Number(Decimal),
String(Arc<str>),
Pair(Arc<Value>, Arc<Value>),
RustFn(NativeFunction),
DelshFn {
args: Arc<[Arc<str>]>,
command: Arc<Value>,
},
}
impl Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt_pair(
f: &mut std::fmt::Formatter<'_>,
a: Arc<Value>,
b: Arc<Value>,
) -> std::fmt::Result {
a.fmt(f)?;
if let Value::Pair(a, b) = &*b {
f.write_str(" ")?;
fmt_pair(f, a.clone(), b.clone())
} else if b == NIL.clone() {
f.write_str(")")
} else {
f.write_str(" . ")?;
b.fmt(f)?;
f.write_str(")")
}
}
match self {
Value::Identifier(identifier) => f.write_str(identifier),
Value::Number(number) => write!(f, "{number}"),
Value::String(string) => write!(f, "\"{string}\""),
Value::Pair(a, b) => {
f.write_str("(")?;
fmt_pair(f, a.clone(), b.clone())
}
Value::RustFn(_) => f.write_str("builtin function"),
Value::DelshFn { .. } => f.write_str("function"),
}
}
}
impl Value {
pub fn identifier(&self) -> Option<&Arc<str>> {
if let Value::Identifier(identifier) = self {
Some(identifier)
} else {
None
}
}
pub fn number(&self) -> Option<&Decimal> {
if let Value::Number(number) = self {
Some(number)
} else {
None
}
}
pub fn string(&self) -> Option<&Arc<str>> {
if let Value::String(string) = self {
Some(string)
} else {
None
}
}
pub fn pair(&self) -> Option<(&Arc<Value>, &Arc<Value>)> {
if let Value::Pair(a, b) = self {
Some((a, b))
} else {
None
}
}
pub fn list(&self) -> Option<Vec<Arc<Value>>> {
let mut list = self;
let mut builder = Vec::new();
loop {
if let Value::Pair(head, rest) = &list {
builder.push(head.clone());
list = rest;
} else if list == &**NIL {
return Some(builder);
} else {
return None;
}
}
}
pub fn set(&self) -> Option<HashSet<Arc<Value>>> {
let mut head = self;
let mut builder = HashSet::new();
loop {
if let Value::Pair(car, cdr) = &head {
builder.insert(car.clone());
head = cdr;
} else if head == &**NIL {
return Some(builder);
} else {
return None;
}
}
}
pub fn rust_fn(&self) -> Option<&NativeFunction> {
if let Value::RustFn(func) = self {
Some(func)
} else {
None
}
}
pub fn is_function(&self) -> bool {
matches!(self, Value::RustFn(_) | Value::DelshFn { .. })
}
}
pub fn wrap_in_quote(value: Arc<Value>) -> Arc<Value> {
Arc::new(Value::Pair(
Arc::new(Value::Identifier("QUOTE".into())),
Arc::new(Value::Pair(value, NIL.clone())),
))
}
pub fn vec_to_value(list: &[Arc<Value>]) -> Arc<Value> {
if list.is_empty() {
NIL.clone()
} else if list.len() == 1 {
Arc::new(Value::Pair(list[0].clone(), NIL.clone()))
} else {
Arc::new(Value::Pair(list[0].clone(), vec_to_value(&list[1..])))
}
}
impl Interpreter {
pub fn new() -> Self {
Self {
stack: vec![HashMap::new()],
}
}
fn current_stack_frame_mut(&mut self) -> &mut HashMap<Arc<str>, Arc<Value>> {
self.stack
.last_mut()
.expect("there should always be at least one stack frame")
}
pub fn push_frame(&mut self) {
self.stack.push(HashMap::new());
}
pub fn pop_frame(&mut self) {
if self.stack.len() == 1 {
return;
}
self.stack.pop();
}
pub fn get_atom(&self, name: &str) -> Option<Arc<Value>> {
for frame in self.stack.iter().rev() {
if let Some(value) = frame.get(name) {
return Some(value.clone());
}
}
None
}
pub fn set_atom(&mut self, name: Arc<str>, value: Arc<Value>) {
self.current_stack_frame_mut()
.insert(name.to_uppercase().into(), value);
}
pub fn run_function(&mut self, function: &Value, arguments: &[Arc<Value>]) -> Arc<Value> {
match function {
Value::RustFn(func) => func(self, arguments),
Value::DelshFn { args, command } => {
self.push_frame();
for (name, value) in args.iter().zip(arguments) {
let value = self.eval(value.clone());
self.set_atom(name.clone(), value);
}
let return_value = self.eval(command.clone());
self.pop_frame();
return_value
}
_ => panic!("expected a function"),
}
}
pub fn eval(&mut self, value: Arc<Value>) -> Arc<Value> {
match &*value {
Value::Identifier(identifier) => {
self.get_atom(identifier).expect("a defined atom").clone()
}
Value::Pair(function, arguments) => {
let function = self.eval(function.clone());
let arguments = arguments.list().expect("arguments to be a list");
self.run_function(&function, &arguments)
}
_ => value,
}
}
fn ast_list_to_value(&mut self, list: &List) -> Arc<Value> {
let contains_dot = list.items.iter().any(|item| item.dot().is_some());
let mut head = (!contains_dot).then(|| NIL.clone());
for item in list.items.iter().rev() {
let Some(item) = item.expression() else {
continue;
};
let Some(head) = head.as_mut() else {
head = Some(self.ast_expression_value(item));
continue;
};
*head = Arc::new(Value::Pair(self.ast_expression_value(item), head.clone()));
}
head.unwrap_or_else(|| NIL.clone())
}
pub fn ast_expression_value(&mut self, expr: &Expression) -> Arc<Value> {
let suffix_value = match &expr.suffix {
ast::ExpressionSuffix::Nothing => NIL.clone(),
ast::ExpressionSuffix::Number { value, .. } => Arc::new(Value::Number(*value)),
ast::ExpressionSuffix::String { value, .. } => Arc::new(Value::String(value.clone())),
ast::ExpressionSuffix::Identifier { name, .. } => {
Arc::new(Value::Identifier(name.clone()))
}
ast::ExpressionSuffix::List(list) => self.ast_list_to_value(list),
};
if expr.is_quoted() {
wrap_in_quote(suffix_value)
} else {
suffix_value
}
}
pub fn run_ast_command(&mut self, command: &ast::Command) -> Arc<Value> {
match command {
ast::Command::Statement(statement) => {
let function_name = &statement.function_name;
let function = self
.get_atom(function_name)
.expect("expected a defined value")
.clone();
let arguments = statement
.args
.iter()
.map(|arg| self.ast_expression_value(arg))
.collect::<Box<_>>();
self.run_function(&function, &arguments)
}
ast::Command::Expression(expression) => {
let value = self.ast_expression_value(expression);
self.eval(value)
}
ast::Command::PanicMode(_) => NIL.clone(),
}
}
}
|