Newer
Older
use crate::parse::ast::{
BinaryOp, DeclareFunction, DeclareIdent, ExpressionList, Print, Program, UnaryOp,
ValueExpression, VoidExpression,
};
use crate::parser::ast::Assignment;
use crate::runtime::executor::Visitor;
use crate::runtime::value::{ForgeValue, UnsupportedOperation};
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
#[derive(Clone, Default)]
data: HashMap<String, ForgeValue>,
vtable: HashMap<String, DeclareFunction>,
}
#[derive(Clone, Default)]
pub struct SimpleExecutor {
scope: SimpleScope,
}
#[derive(Clone, Debug, PartialEq)]
pub enum RuntimeError {
Unsupported(&'static str),
UndefinedVariable(String),
VariableAlreadyDefined(String),
BadOperands(UnsupportedOperation),
}
impl From<UnsupportedOperation> for RuntimeError {
fn from(value: UnsupportedOperation) -> Self {
Self::BadOperands(value)
}
}
impl Display for RuntimeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
RuntimeError::Unsupported(expr) => {
write!(
f,
"[Runtime] Encountered an unsupported expression: {}",
expr
)
}
RuntimeError::BadOperands(unsupported) => write!(f, "[Runtime] {}", unsupported),
RuntimeError::UndefinedVariable(var) => {
write!(f, "[Runtime] Attempt to access undefined variable {}", var)
}
RuntimeError::VariableAlreadyDefined(var) => write!(
f,
"[Runtime] Attempt to redefine already defined variable {}",
var
),
}
}
}
impl Error for RuntimeError {}
pub type RuntimeResult<T> = Result<T, RuntimeError>;
impl SimpleExecutor {
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
pub fn get_variable(&self, identifier: impl ToString) -> Option<&ForgeValue> {
self.scope.data.get(&identifier.to_string())
}
pub fn set_variable(
&mut self,
identifier: impl ToString,
value: ForgeValue,
) -> RuntimeResult<()> {
let entry = self.scope.data.entry(identifier.to_string());
match entry {
Entry::Occupied(mut e) => {
e.insert(value);
Ok(())
}
Entry::Vacant(_) => Err(RuntimeError::UndefinedVariable(identifier.to_string())),
}
}
pub fn define_variable(
&mut self,
identifier: impl ToString,
value: ForgeValue,
) -> RuntimeResult<()> {
let entry = self.scope.data.entry(identifier.to_string());
match entry {
Entry::Vacant(e) => {
e.insert(value);
Ok(())
}
Entry::Occupied(_) => Err(RuntimeError::VariableAlreadyDefined(identifier.to_string())),
}
}
fn process_assignment(&mut self, assignment: &Assignment) -> RuntimeResult<ForgeValue> {
let value = self.evaluate_value_expression(assignment.value.as_ref())?;
self.set_variable(&assignment.ident, value.clone())?;
Ok(value)
}
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
fn evaluate_expression_list(
&mut self,
list: &ExpressionList,
) -> Result<ForgeValue, RuntimeError> {
let mut last_val = Ok(ForgeValue::Null);
for expr in &list.expressions {
last_val = Ok(self.evaluate_expression(expr)?);
}
if list.is_void {
Ok(ForgeValue::Null)
} else {
last_val
}
}
}
impl Visitor for SimpleExecutor {
type Output = ForgeValue;
type Error = RuntimeError;
fn evaluate_value_expression(
&mut self,
expression: &ValueExpression,
) -> Result<Self::Output, Self::Error> {
match expression {
ValueExpression::Unary { operator, operand } => {
let value = self.evaluate_value_expression(operand.as_ref())?;
match operator {
UnaryOp::Negate => Ok(value.invert()),
UnaryOp::Not => Ok(!value),
}
}
ValueExpression::Binary { operator, lhs, rhs } => {
let lhs = self.evaluate_value_expression(lhs.as_ref())?;
let rhs = self.evaluate_value_expression(rhs.as_ref())?;
match operator {
BinaryOp::Add => Ok(lhs + rhs),
BinaryOp::Subtract => Ok((lhs - rhs)?),
BinaryOp::Divide => Ok((lhs / rhs)?),
BinaryOp::Multiply => Ok((lhs * rhs)?),
BinaryOp::Modulo => Ok((lhs % rhs)?),
BinaryOp::Equals => Ok((lhs == rhs).into()),
}
}
ValueExpression::Grouped(group) => self.evaluate_value_expression(group.inner.as_ref()),
ValueExpression::Block(block) => self.evaluate_expression_list(block),
ValueExpression::Literal(lit) => Ok(ForgeValue::from(lit.clone())),
ValueExpression::DeclareIdentifier(decl) => match decl {
DeclareIdent::WithValue(assignment) => {
self.define_variable(&assignment.ident, ForgeValue::Null)?;
self.process_assignment(assignment)
}
DeclareIdent::WithoutValue(identifier) => {
self.define_variable(identifier, ForgeValue::Null)?;
Ok(ForgeValue::Null)
}
},
ValueExpression::Assignment(assign) => self.process_assignment(assign),
ValueExpression::ConditionalBlock(condition) => {
let mut has_found = false;
let mut last_value = ForgeValue::Null;
for block in condition.blocks.iter() {
let guard_val = self.evaluate_value_expression(block.guard.as_ref())?;
if guard_val.as_bool() {
has_found = true;
last_value = self.evaluate_expression_list(&block.block)?;
break;
}
}
if !has_found {
if let Some(value) = &condition.fallback {
last_value = self.evaluate_expression_list(value)?;
}
}
Ok(last_value)
}
ValueExpression::Identifier(ident) => self
.get_variable(ident)
.cloned()
.ok_or_else(|| RuntimeError::UndefinedVariable(ident.to_string())),
ValueExpression::FunctionCall(_) => Err(RuntimeError::Unsupported("FunctionCall")),
ValueExpression::DeclareFunction(_) => {
Err(RuntimeError::Unsupported("DeclareFunction"))
}
}
}
fn evaluate_void_expression(
&mut self,
expression: &VoidExpression,
) -> Result<Self::Output, Self::Error> {
match expression {
VoidExpression::ConditionLoop(_) => {
return Err(RuntimeError::Unsupported("ConditionLoop"))
}
VoidExpression::Import(_) => return Err(RuntimeError::Unsupported("Import")),
VoidExpression::Export(_) => return Err(RuntimeError::Unsupported("Export")),
VoidExpression::Print(Print { expr }) => {
let value = self.evaluate_value_expression(expr.as_ref())?;
println!("{}", value);
}
}
Ok(ForgeValue::Null)
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
}
fn evaluate_program(&mut self, program: &Program) -> Result<Self::Output, Self::Error> {
self.evaluate_expression_list(&program.0)
}
}
#[cfg(test)]
mod interpreter_test {
use crate::parse::parse_program;
use crate::runtime::executor::simple::{RuntimeError, SimpleExecutor};
use crate::runtime::executor::Visitor;
use crate::runtime::value::ForgeValue;
#[test]
fn the_basics() {
let add_numbers = parse_program("1 + 1").expect("Failed to parse");
let add_strings = parse_program("\"foo\" + \" \" + \"bar\"").expect("Failed to parse");
let concat_string_num = parse_program("\"#\" + 1").expect("Failed to parse");
let bad_mult = parse_program("false * 123").expect("Failed to parse");
let mut vm = SimpleExecutor::default();
assert_eq!(vm.evaluate_program(&add_numbers), Ok(ForgeValue::from(2)));
assert_eq!(
vm.evaluate_program(&add_strings),
Ok(ForgeValue::from("foo bar"))
);
assert_eq!(
vm.evaluate_program(&concat_string_num),
Ok(ForgeValue::from("#1"))
);
assert!(matches!(
vm.evaluate_program(&bad_mult),
Err(RuntimeError::BadOperands(_))
))
}
#[test]
fn combos() {
let add_numbers = parse_program("1 + -1").expect("Failed to parse");
let mut vm = SimpleExecutor::default();
assert_eq!(vm.evaluate_program(&add_numbers), Ok(ForgeValue::from(0)));
}
#[test]
fn print() {
let print_4 = parse_program("print 2 + 2").expect("Failed to parse");
let mut vm = SimpleExecutor::default();
assert_eq!(vm.evaluate_program(&print_4), Ok(ForgeValue::Null));
}
#[test]
fn define_variables() {
let simple_define = parse_program("let foo").expect("Failed to parse");
let assignment = parse_program("let bar = 123").expect("Failed to parse");
let redecl_err = parse_program("let foo = 123").expect("Failed to parse");
let missing_err = parse_program("print missing").expect("Failed to parse");
let use_vars = parse_program("let splop = bar * 2").expect("Failed to parse");
let mut vm = SimpleExecutor::default();
assert_eq!(vm.evaluate_program(&simple_define), Ok(ForgeValue::Null));
assert_eq!(vm.evaluate_program(&assignment), Ok(ForgeValue::from(123)));
assert_eq!(
vm.evaluate_program(&redecl_err),
Err(RuntimeError::VariableAlreadyDefined(String::from("foo")))
);
assert_eq!(
vm.evaluate_program(&missing_err),
Err(RuntimeError::UndefinedVariable(String::from("missing")))
);
assert_eq!(vm.evaluate_program(&use_vars), Ok(ForgeValue::from(246)));
assert_eq!(vm.get_variable("splop"), Some(&ForgeValue::from(246)));
}