Skip to content
Snippets Groups Projects
Verified Commit f8b051c1 authored by Louis's avatar Louis :fire:
Browse files

Support variable assignment, state, basic environment

parent 117901ce
No related branches found
No related tags found
No related merge requests found
Pipeline #496 passed with stages
in 55 seconds
use crate::parse::ast::{ use crate::parse::ast::{
BinaryOp, DeclareFunction, ExpressionList, Print, Program, UnaryOp, ValueExpression, BinaryOp, DeclareFunction, DeclareIdent, ExpressionList, Print, Program, UnaryOp,
VoidExpression, ValueExpression, VoidExpression,
}; };
use crate::parser::ast::Assignment;
use crate::runtime::executor::Visitor; use crate::runtime::executor::Visitor;
use crate::runtime::value::{ForgeValue, UnsupportedOperation}; use crate::runtime::value::{ForgeValue, UnsupportedOperation};
use std::collections::hash_map::Entry;
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct SimpleExecutor { pub struct SimpleScope {
data: HashMap<String, ForgeValue>, data: HashMap<String, ForgeValue>,
vtable: HashMap<String, DeclareFunction>, vtable: HashMap<String, DeclareFunction>,
} }
#[derive(Clone, Default)]
pub struct SimpleExecutor {
scope: SimpleScope,
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum RuntimeError { pub enum RuntimeError {
Unsupported(&'static str), Unsupported(&'static str),
UndefinedVariable(String),
VariableAlreadyDefined(String),
BadOperands(UnsupportedOperation), BadOperands(UnsupportedOperation),
} }
...@@ -37,13 +46,63 @@ impl Display for RuntimeError { ...@@ -37,13 +46,63 @@ impl Display for RuntimeError {
) )
} }
RuntimeError::BadOperands(unsupported) => write!(f, "[Runtime] {}", unsupported), 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 {} impl Error for RuntimeError {}
pub type RuntimeResult<T> = Result<T, RuntimeError>;
impl SimpleExecutor { impl SimpleExecutor {
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)
}
fn evaluate_expression_list( fn evaluate_expression_list(
&mut self, &mut self,
list: &ExpressionList, list: &ExpressionList,
...@@ -92,14 +151,24 @@ impl Visitor for SimpleExecutor { ...@@ -92,14 +151,24 @@ impl Visitor for SimpleExecutor {
ValueExpression::Grouped(group) => self.evaluate_value_expression(group.inner.as_ref()), ValueExpression::Grouped(group) => self.evaluate_value_expression(group.inner.as_ref()),
ValueExpression::Block(_) => Err(RuntimeError::Unsupported("Block")), ValueExpression::Block(_) => Err(RuntimeError::Unsupported("Block")),
ValueExpression::Literal(lit) => Ok(ForgeValue::from(lit.clone())), ValueExpression::Literal(lit) => Ok(ForgeValue::from(lit.clone())),
ValueExpression::DeclareIdentifier(_) => { ValueExpression::DeclareIdentifier(decl) => match decl {
Err(RuntimeError::Unsupported("DeclareIdentifier")) 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(_) => Err(RuntimeError::Unsupported("Assignment")), ValueExpression::Assignment(_) => Err(RuntimeError::Unsupported("Assignment")),
ValueExpression::ConditionalBlock(_) => { ValueExpression::ConditionalBlock(_) => {
Err(RuntimeError::Unsupported("ConditionalBlock")) Err(RuntimeError::Unsupported("ConditionalBlock"))
} }
ValueExpression::Identifier(_) => Err(RuntimeError::Unsupported("Identifier")), ValueExpression::Identifier(ident) => self
.get_variable(ident)
.cloned()
.ok_or_else(|| RuntimeError::UndefinedVariable(ident.to_string())),
ValueExpression::FunctionCall(_) => Err(RuntimeError::Unsupported("FunctionCall")), ValueExpression::FunctionCall(_) => Err(RuntimeError::Unsupported("FunctionCall")),
ValueExpression::DeclareFunction(_) => { ValueExpression::DeclareFunction(_) => {
Err(RuntimeError::Unsupported("DeclareFunction")) Err(RuntimeError::Unsupported("DeclareFunction"))
...@@ -174,4 +243,28 @@ mod interpreter_test { ...@@ -174,4 +243,28 @@ mod interpreter_test {
let mut vm = SimpleExecutor::default(); let mut vm = SimpleExecutor::default();
assert_eq!(vm.evaluate_program(&print_4), Ok(ForgeValue::Null)); 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)));
}
} }
...@@ -4,7 +4,7 @@ use crate::runtime::numbers::Number; ...@@ -4,7 +4,7 @@ use crate::runtime::numbers::Number;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::{Add, Div, Mul, Not, Rem, Sub}; use std::ops::{Add, Div, Mul, Not, Rem, Sub};
#[derive(Clone, Debug)] #[derive(Clone, Debug, Default)]
#[cfg_attr( #[cfg_attr(
feature = "serde", feature = "serde",
derive(serde::Serialize, serde::Deserialize), derive(serde::Serialize, serde::Deserialize),
...@@ -23,6 +23,7 @@ pub enum ForgeValue { ...@@ -23,6 +23,7 @@ pub enum ForgeValue {
Boolean(bool), Boolean(bool),
String(String), String(String),
List(Vec<ForgeValue>), List(Vec<ForgeValue>),
#[default]
Null, Null,
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment