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

Visitors need return and error types, start basic expressions in simple interpreter

parent 24a93bb4
No related branches found
No related tags found
No related merge requests found
Pipeline #494 passed with stages
in 1 minute and 7 seconds
......@@ -64,8 +64,7 @@ pub enum Expression {
Void(VoidExpression),
}
#[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))]
#[derive(Clone, Eq, Copy, PartialEq, Debug)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
......@@ -89,8 +88,7 @@ impl Display for UnaryOp {
}
}
#[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))]
#[derive(Clone, Eq, Copy, PartialEq, Debug)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
......
......@@ -30,8 +30,7 @@ peg::parser! {
#[cache_left_rec]
rule value_expression() -> ValueExpression
= "(" ex:value_expression() ")" { ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) }
/ co:conditional() { ValueExpression::ConditionalBlock(co) }
= co:conditional() { ValueExpression::ConditionalBlock(co) }
/ decl:declare_variable() { ValueExpression::DeclareIdentifier(decl) }
/ decl:declare_function() { ValueExpression::DeclareFunction(decl) }
/ name:bare_identifier() "(" params:param_list()? ")"
......@@ -40,9 +39,14 @@ peg::parser! {
{ ValueExpression::Binary { lhs: Box::new(left), rhs: Box::new(right), operator: op } }
/ op:unary_operator() operand:value_expression()
{ ValueExpression::Unary { operator: op, operand: Box::new(operand) } }
/ grouped()
/ ident:bare_identifier() { ValueExpression::Identifier(ident) }
/ li:literal() { ValueExpression::Literal(li) }
rule grouped() -> ValueExpression
= "(" ex:value_expression() ")"
{ ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) }
rule print() -> Print
= "print" ex:value_expression() { ex.into() }
......
......@@ -3,6 +3,10 @@ use crate::parse::parse_program;
#[test]
fn binary_ops() {
parse_program("1+1").expect("Failed binary add");
parse_program("1 + 2 + 3 + 4").expect("Failed binary add");
parse_program("1 + (2 + 3) + 4").expect("Failed binary add");
parse_program("(1 + 2) + 3 + 4").expect("Failed binary add");
parse_program("1-1").expect("Failed binary sub");
parse_program("1*1").expect("Failed binary mul");
parse_program("1/1").expect("Failed binary div");
......
mod printer;
mod simple;
use crate::parser::ast::{Expression, Program, ValueExpression, VoidExpression};
use std::error::Error;
pub trait Visitor {
fn evaluate_value_expression(&mut self, expression: &ValueExpression);
fn evaluate_void_expression(&mut self, expression: &VoidExpression);
fn evaluate_expression(&mut self, expression: &Expression) {
type Output;
type Error: Error;
fn evaluate_value_expression(
&mut self,
expression: &ValueExpression,
) -> Result<Self::Output, Self::Error>;
fn evaluate_void_expression(
&mut self,
expression: &VoidExpression,
) -> Result<Self::Output, Self::Error>;
fn evaluate_expression(
&mut self,
expression: &Expression,
) -> Result<Self::Output, Self::Error> {
match expression {
Expression::Value(expr) => self.evaluate_value_expression(expr),
Expression::Void(expr) => self.evaluate_void_expression(expr),
}
}
fn evaluate_program(&mut self, program: &Program);
fn evaluate_program(&mut self, program: &Program) -> Result<Self::Output, Self::Error>;
}
pub use printer::TreePrinter;
use crate::parser::ast::*;
use crate::runtime::executor::Visitor;
use std::error::Error;
use std::fmt::{Display, Formatter};
pub struct TreePrinter {
indent: usize,
buffer: String,
should_indent: bool,
}
impl TreePrinter {
......@@ -11,6 +14,15 @@ impl TreePrinter {
Self {
indent: 0,
buffer: String::new(),
should_indent: true,
}
}
pub fn with_indent(indent: usize) -> Self {
Self {
indent,
buffer: String::new(),
should_indent: true,
}
}
......@@ -27,17 +39,25 @@ impl TreePrinter {
}
fn write(&mut self, value: impl ToString) {
if self.should_indent {
self.should_indent = false;
self.write_indent();
}
self.buffer.push_str(value.to_string().as_str());
}
fn writeln(&mut self, value: impl ToString) {
self.buffer.push_str(value.to_string().as_str());
self.buffer.push('\n');
self.write(value);
self.new_line();
}
fn write_indent(&mut self) {
self.write(self.get_indent());
}
fn new_line(&mut self) {
self.buffer.push('\n');
self.should_indent = true;
}
fn format_expression_list(&self, list: &ExpressionList) -> String {
......@@ -72,8 +92,20 @@ impl TreePrinter {
}
}
#[derive(Debug, Clone, Copy)]
pub struct TreeError;
impl Display for TreeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("Tree Error")
}
}
impl Error for TreeError {}
impl Visitor for TreePrinter {
fn evaluate_value_expression(&mut self, expression: &ValueExpression) {
type Output = ();
type Error = TreeError;
fn evaluate_value_expression(&mut self, expression: &ValueExpression) -> Result<(), TreeError> {
match expression {
ValueExpression::Unary { operand, operator } => {
self.write(operator);
......@@ -150,10 +182,7 @@ impl Visitor for TreePrinter {
.params
.iter()
.map(|param| {
let mut inner = TreePrinter {
indent: self.indent,
buffer: String::new(),
};
let mut inner = TreePrinter::with_indent(self.indent);
inner.evaluate_value_expression(param);
inner.take_value()
})
......@@ -169,11 +198,7 @@ impl Visitor for TreePrinter {
.params
.iter()
.map(|param| {
let mut inner = TreePrinter {
indent: self.indent,
buffer: String::new(),
};
let mut inner = TreePrinter::with_indent(self.indent);
match param {
DeclareIdent::WithValue(assign) => {
inner.write(&assign.ident);
......@@ -195,9 +220,11 @@ impl Visitor for TreePrinter {
self.writeln("}");
}
}
Ok(())
}
fn evaluate_void_expression(&mut self, expression: &VoidExpression) {
fn evaluate_void_expression(&mut self, expression: &VoidExpression) -> Result<(), TreeError> {
match expression {
VoidExpression::ConditionLoop(cond) => {
self.write("while ");
......@@ -237,9 +264,12 @@ impl Visitor for TreePrinter {
self.evaluate_value_expression(print.expr.as_ref());
}
}
Ok(())
}
fn evaluate_program(&mut self, program: &Program) {
fn evaluate_program(&mut self, program: &Program) -> Result<(), TreeError> {
self.writeln(self.format_expression_list(&program.0));
Ok(())
}
}
use crate::parse::ast::{
BinaryOp, DeclareFunction, ExpressionList, Program, UnaryOp, ValueExpression, VoidExpression,
};
use crate::runtime::executor::Visitor;
use crate::runtime::value::{ForgeValue, UnsupportedOperation};
use std::collections::HashMap;
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
#[derive(Clone, Default)]
pub struct SimpleExecutor {
data: HashMap<String, ForgeValue>,
vtable: HashMap<String, DeclareFunction>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum RuntimeError {
Unsupported(&'static str),
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),
}
}
}
impl Error for RuntimeError {}
impl SimpleExecutor {
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(_) => Err(RuntimeError::Unsupported("Block")),
ValueExpression::Literal(lit) => Ok(ForgeValue::from(lit.clone())),
ValueExpression::DeclareIdentifier(_) => {
Err(RuntimeError::Unsupported("DeclareIdentifier"))
}
ValueExpression::Assignment(_) => Err(RuntimeError::Unsupported("Assignment")),
ValueExpression::ConditionalBlock(_) => {
Err(RuntimeError::Unsupported("ConditionalBlock"))
}
ValueExpression::Identifier(_) => Err(RuntimeError::Unsupported("Identifier")),
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> {
Err(RuntimeError::Unsupported("Void Expression"))
}
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)));
}
}
use crate::parse::ast::BinaryOp;
use crate::parser::ast::LiteralNode;
use crate::runtime::numbers::Number;
use std::fmt::{Display, Formatter};
use std::ops::{Add, Div, Mul, Not, Rem, Sub};
#[derive(Clone, Debug)]
#[cfg_attr(
......@@ -25,6 +27,20 @@ pub enum ForgeValue {
}
impl ForgeValue {
pub fn null() -> Self {
ForgeValue::Null
}
pub fn invert(&self) -> Self {
match self {
ForgeValue::Number(num) => match num {
Number::Integer(val) => Number::Integer(-*val).into(),
Number::Float(val) => Number::Float(-*val).into(),
},
val => val.clone(),
}
}
/// Perform type coercion to force this value into a bool
///
/// ## True
......@@ -140,3 +156,290 @@ impl From<LiteralNode> for ForgeValue {
}
}
}
impl From<String> for ForgeValue {
fn from(value: String) -> Self {
ForgeValue::String(value)
}
}
impl<'a> From<&'a str> for ForgeValue {
fn from(value: &'a str) -> Self {
ForgeValue::String(String::from(value))
}
}
impl From<bool> for ForgeValue {
fn from(value: bool) -> Self {
ForgeValue::Boolean(value)
}
}
impl From<Number> for ForgeValue {
fn from(value: Number) -> Self {
ForgeValue::Number(value)
}
}
macro_rules! from_number {
($($typ:ty),+) => {
$(
impl From<$typ> for ForgeValue {
fn from(value: $typ) -> Self {
ForgeValue::Number(Number::from(value))
}
}
)+
};
}
from_number!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64);
impl PartialEq for ForgeValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(ForgeValue::String(s1), ForgeValue::String(s2)) => s1.eq(s2),
(ForgeValue::Number(n1), ForgeValue::Number(n2)) => n1.eq(n2),
(ForgeValue::Boolean(b1), ForgeValue::Boolean(b2)) => b1.eq(b2),
(ForgeValue::Null, ForgeValue::Null) => true,
_ => false,
}
}
}
impl Add for ForgeValue {
type Output = ForgeValue;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(ForgeValue::String(s1), ForgeValue::String(s2)) => ForgeValue::String({
let mut buffer = String::with_capacity(s1.len() + s2.len());
buffer.push_str(s1.as_str());
buffer.push_str(s2.as_str());
buffer
}),
(ForgeValue::Number(n1), ForgeValue::Number(n2)) => ForgeValue::Number(n1 + n2),
(ForgeValue::String(val1), ForgeValue::Number(val2)) => {
ForgeValue::String(format!("{}{}", val1, val2))
}
(ForgeValue::Number(val1), ForgeValue::String(val2)) => {
ForgeValue::String(format!("{}{}", val1, val2))
}
(ForgeValue::List(first), ForgeValue::List(second)) => {
ForgeValue::List(first.iter().chain(second.iter()).cloned().collect())
}
(other_value, ForgeValue::List(mut list))
| (ForgeValue::List(mut list), other_value) => {
list.push(other_value);
ForgeValue::List(list)
}
// Boolean && null values are ignored
(ForgeValue::Number(val), ForgeValue::Null)
| (ForgeValue::Number(val), ForgeValue::Boolean(_))
| (ForgeValue::Null, ForgeValue::Number(val))
| (ForgeValue::Boolean(_), ForgeValue::Number(val)) => ForgeValue::Number(val),
(ForgeValue::Boolean(val), ForgeValue::Null)
| (ForgeValue::Null, ForgeValue::Boolean(val)) => ForgeValue::Boolean(val),
(ForgeValue::Null, ForgeValue::String(val))
| (ForgeValue::Boolean(_), ForgeValue::String(val))
| (ForgeValue::String(val), ForgeValue::Null)
| (ForgeValue::String(val), ForgeValue::Boolean(_)) => ForgeValue::String(val),
(ForgeValue::Null, ForgeValue::Null) => ForgeValue::Null,
(ForgeValue::Boolean(b1), ForgeValue::Boolean(b2)) => ForgeValue::Boolean(b1 && b2),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct UnsupportedOperation {
operation: BinaryOp,
left_type: &'static str,
right_type: &'static str,
}
impl Display for UnsupportedOperation {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Cannot perform {} with lhs type {} and rhs type {}",
self.operation, self.left_type, self.right_type
)
}
}
impl Sub for ForgeValue {
type Output = Result<ForgeValue, UnsupportedOperation>;
fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(ForgeValue::Number(n1), ForgeValue::Number(n2)) => Ok(ForgeValue::Number(n1 - n2)),
(ForgeValue::Null, ForgeValue::Null) => Ok(ForgeValue::Null),
(ForgeValue::Number(_), _) => Err(UnsupportedOperation {
operation: BinaryOp::Subtract,
left_type: "number",
right_type: "non-number",
}),
(_, _) => Err(UnsupportedOperation {
operation: BinaryOp::Subtract,
left_type: "non-number",
right_type: "non-number",
}),
}
}
}
impl Div for ForgeValue {
type Output = Result<ForgeValue, UnsupportedOperation>;
fn div(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(ForgeValue::Number(n1), ForgeValue::Number(n2)) => Ok(ForgeValue::Number(n1 / n2)),
(ForgeValue::Null, ForgeValue::Null) => Ok(ForgeValue::Null),
(ForgeValue::Number(_), _) => Err(UnsupportedOperation {
operation: BinaryOp::Divide,
left_type: "number",
right_type: "non-number",
}),
(_, _) => Err(UnsupportedOperation {
operation: BinaryOp::Divide,
left_type: "non-number",
right_type: "non-number",
}),
}
}
}
impl Mul for ForgeValue {
type Output = Result<ForgeValue, UnsupportedOperation>;
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(ForgeValue::Number(n1), ForgeValue::Number(n2)) => Ok(ForgeValue::Number(n1 * n2)),
(ForgeValue::Null, ForgeValue::Null) => Ok(ForgeValue::Null),
(ForgeValue::Number(_), _) => Err(UnsupportedOperation {
operation: BinaryOp::Multiply,
left_type: "number",
right_type: "non-number",
}),
(_, _) => Err(UnsupportedOperation {
operation: BinaryOp::Multiply,
left_type: "non-number",
right_type: "non-number",
}),
}
}
}
impl Rem for ForgeValue {
type Output = Result<ForgeValue, UnsupportedOperation>;
fn rem(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(ForgeValue::Number(n1), ForgeValue::Number(n2)) => Ok(ForgeValue::Number(n1 % n2)),
(ForgeValue::Null, ForgeValue::Null) => Ok(ForgeValue::Null),
(ForgeValue::Number(_), _) => Err(UnsupportedOperation {
operation: BinaryOp::Modulo,
left_type: "number",
right_type: "non-number",
}),
(_, _) => Err(UnsupportedOperation {
operation: BinaryOp::Modulo,
left_type: "non-number",
right_type: "non-number",
}),
}
}
}
impl Not for ForgeValue {
type Output = ForgeValue;
fn not(self) -> Self::Output {
match self {
ForgeValue::Number(num) => (num != Number::Integer(0)).into(),
ForgeValue::Boolean(b) => (!b).into(),
ForgeValue::String(st) => (!st.is_empty()).into(),
ForgeValue::List(ls) => (!ls.is_empty()).into(),
ForgeValue::Null => ForgeValue::Null,
}
}
}
#[cfg(test)]
mod value_tests {
use crate::runtime::value::ForgeValue;
#[test]
fn strict_type_equality() {
assert_ne!(ForgeValue::from(22), ForgeValue::from("22"));
assert_ne!(ForgeValue::from(true), ForgeValue::from("true"));
assert_ne!(ForgeValue::from(false), ForgeValue::from("false"));
assert_ne!(ForgeValue::from(true), ForgeValue::from(1));
assert_ne!(ForgeValue::from(false), ForgeValue::from(0));
}
#[test]
fn add_strings_concats() {
let first = ForgeValue::from("hello_");
let second = ForgeValue::from("world");
assert_eq!(
first + second,
ForgeValue::String(String::from("hello_world"))
);
}
#[test]
fn add_numbers() {
let first = ForgeValue::from(250);
let second = ForgeValue::from(250);
let third = ForgeValue::from(250);
let fourth = ForgeValue::from(250.0);
assert_eq!(first + second, ForgeValue::from(500));
assert_eq!(third + fourth, ForgeValue::from(500.0));
}
#[test]
fn add_bool_noop() {
assert_eq!(
ForgeValue::from("Something") + ForgeValue::from(true),
ForgeValue::from("Something")
);
assert_eq!(
ForgeValue::from("Something") + ForgeValue::from(false),
ForgeValue::from("Something")
);
assert_eq!(
ForgeValue::from(1000) + ForgeValue::from(true),
ForgeValue::from(1000)
);
assert_eq!(
ForgeValue::from(1000) + ForgeValue::from(false),
ForgeValue::from(1000)
);
}
#[test]
fn add_null_noop() {
assert_eq!(
ForgeValue::from("Something") + ForgeValue::null(),
ForgeValue::from("Something")
);
assert_eq!(
ForgeValue::from(1000) + ForgeValue::null(),
ForgeValue::from(1000)
);
assert_eq!(
ForgeValue::from(true) + ForgeValue::null(),
ForgeValue::from(true)
);
assert_eq!(
ForgeValue::from(false) + ForgeValue::null(),
ForgeValue::from(false)
);
}
}
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