diff --git a/forge-script-lang/src/parser/grammar.rs b/forge-script-lang/src/parser/grammar.rs index e4fd9f4f7c1cf1c36e655d52230969f18f9ee8f3..6cf95b30b34002fa815892970be78551c48922b3 100644 --- a/forge-script-lang/src/parser/grammar.rs +++ b/forge-script-lang/src/parser/grammar.rs @@ -10,7 +10,7 @@ peg::parser! { = ex:expression_list() eof() { Program(ex) } pub rule expression() -> Expression - = ex:value_expression() { Expression::Value(ex) } + = ex:precedence_expression() { Expression::Value(ex) } / ex:void_expression() { Expression::Void(ex) } rule void_expression() -> VoidExpression @@ -21,14 +21,16 @@ peg::parser! { #[cache_left_rec] rule value_expression() -> ValueExpression - = co:conditional_statement() { ValueExpression::ConditionalBlock(co) } + = + co:conditional_statement() { ValueExpression::ConditionalBlock(co) } / t:type_of() { ValueExpression::Typeof(t) } + / precedence_expression() / decl:declare_variable() { ValueExpression::DeclareIdentifier(decl) } / decl:declare_function() { ValueExpression::DeclareFunction(decl) } / name:simple_identifier() "(" params:param_list()? ")" { ValueExpression::FunctionCall(FunctionCall { name, params: params.unwrap_or_default() }) } - / left:value_expression() op:binary_operator() right:value_expression() - { ValueExpression::Binary { lhs: Box::new(left), rhs: Box::new(right), operator: op } } + // / left:value_expression() op:binary_operator() right:value_expression() + // { 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() @@ -82,15 +84,37 @@ peg::parser! { rule expression_list() -> ExpressionList = ex:(expression() ** ";") term:";"? { ExpressionList { expressions: ex, is_void: term.is_some() } } - rule binary_operator() -> BinaryOp - = "+" { BinaryOp::Add } - / "-" { BinaryOp::Subtract } - / "*" { BinaryOp::Multiply } - / "/" { BinaryOp::Divide } - / "%" { BinaryOp::Modulo } - / "==" { BinaryOp::Equals } - / "&&" { BinaryOp::BoolAnd } - / "||" { BinaryOp::BoolOr } + #[cache_left_rec] + rule precedence_expression() -> ValueExpression + = precedence! { + "-" z:(@) { ValueExpression::Unary { operator: UnaryOp::Negate, operand: Box::new(z) } } + -- + x:(@) "+" y:@ { ValueExpression::Binary { operator: BinaryOp::Add, lhs: Box::new(x), rhs: Box::new(y) } } + x:(@) "-" y:@ { ValueExpression::Binary { operator: BinaryOp::Subtract, lhs: Box::new(x), rhs: Box::new(y) } } + -- + x:(@) "*" y:@ { ValueExpression::Binary { operator: BinaryOp::Multiply, lhs: Box::new(x), rhs: Box::new(y) } } + x:(@) "/" y:@ { ValueExpression::Binary { operator: BinaryOp::Divide, lhs: Box::new(x), rhs: Box::new(y) } } + x:(@) "%" y:@ { ValueExpression::Binary { operator: BinaryOp::Modulo, lhs: Box::new(x), rhs: Box::new(y) } } + -- + x:(@) "==" y:@ { ValueExpression::Binary { operator: BinaryOp::Equals, lhs: Box::new(x), rhs: Box::new(y) } } + x:(@) "&&" y:@ { ValueExpression::Binary { operator: BinaryOp::BoolAnd, lhs: Box::new(x), rhs: Box::new(y) } } + x:(@) "||" y:@ { ValueExpression::Binary { operator: BinaryOp::BoolOr, lhs: Box::new(x), rhs: Box::new(y) } } + -- + "!" z:(@) { ValueExpression::Unary { operator: UnaryOp::Not, operand: Box::new(z) } } + -- + "(" ex:precedence_expression() ")" { ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) } + val:value_expression() { val } + } + + // rule binary_operator() -> BinaryOp + // = "+" { BinaryOp::Add } + // / "-" { BinaryOp::Subtract } + // / "*" { BinaryOp::Multiply } + // / "/" { BinaryOp::Divide } + // / "%" { BinaryOp::Modulo } + // / "==" { BinaryOp::Equals } + // / "&&" { BinaryOp::BoolAnd } + // / "||" { BinaryOp::BoolOr } rule unary_operator() -> UnaryOp = "!" { UnaryOp::Not } diff --git a/forge-script-lang/src/runtime/vm/chunk_builder.rs b/forge-script-lang/src/runtime/vm/chunk_builder.rs new file mode 100644 index 0000000000000000000000000000000000000000..d67f6d1c723f2fcabca3ef88b081aee2fe565b3e --- /dev/null +++ b/forge-script-lang/src/runtime/vm/chunk_builder.rs @@ -0,0 +1,138 @@ +use crate::format_forge_error; +use crate::parse::ast::{BinaryOp, Program, UnaryOp, ValueExpression, VoidExpression}; +use crate::parser::ast::GroupedExpression; +use crate::parser::parse_program; +use crate::runtime::executor::Visitor; +use crate::runtime::value::ForgeValue; +use crate::runtime::vm::{Chunk, ChunkRef, OpCode, VmError}; + +pub struct ChunkBuilder { + current_chunk: Chunk, +} + +impl ChunkBuilder { + pub fn new() -> Self { + Self { + current_chunk: Chunk::default(), + } + } + + pub fn parse(code: &str) -> Result<Chunk, VmError> { + let mut builder = ChunkBuilder::new(); + let program = + parse_program(code).map_err(|fe| VmError::ast_parser(format_forge_error(code, &fe)))?; + builder.evaluate_program(&program)?; + Ok(builder.take_chunk()) + } + + pub fn chunk_ref(&self) -> ChunkRef { + self.current_chunk.as_ref() + } + + pub fn take_chunk(&mut self) -> Chunk { + std::mem::take(&mut self.current_chunk) + } +} + +impl Visitor for ChunkBuilder { + type Output = (); + type Error = VmError; + + fn evaluate_value_expression( + &mut self, + expression: &ValueExpression, + ) -> Result<Self::Output, Self::Error> { + match expression { + ValueExpression::Unary { operator, operand } => { + self.evaluate_value_expression(operand.as_ref())?; + match operator { + UnaryOp::Not => Err(VmError::chunk_parser("Unsupported expression")), + UnaryOp::Negate => { + self.current_chunk.push_op(OpCode::Invert); + Ok(()) + } + } + } + ValueExpression::Binary { lhs, rhs, operator } => { + self.evaluate_value_expression(lhs.as_ref())?; + self.evaluate_value_expression(rhs.as_ref())?; + match operator { + BinaryOp::Add => { + self.current_chunk.push_op(OpCode::Add); + Ok(()) + } + BinaryOp::Subtract => { + self.current_chunk.push_op(OpCode::Subtract); + Ok(()) + } + BinaryOp::Multiply => { + self.current_chunk.push_op(OpCode::Multiply); + Ok(()) + } + BinaryOp::Divide => { + self.current_chunk.push_op(OpCode::Divide); + Ok(()) + } + BinaryOp::Modulo => Err(VmError::chunk_parser("Unsupported expression")), + BinaryOp::Equals => Err(VmError::chunk_parser("Unsupported expression")), + BinaryOp::BoolAnd => Err(VmError::chunk_parser("Unsupported expression")), + BinaryOp::BoolOr => Err(VmError::chunk_parser("Unsupported expression")), + BinaryOp::NotEquals => Err(VmError::chunk_parser("Unsupported expression")), + BinaryOp::LessThan => Err(VmError::chunk_parser("Unsupported expression")), + BinaryOp::GreaterThan => Err(VmError::chunk_parser("Unsupported expression")), + BinaryOp::LessThanEqual => Err(VmError::chunk_parser("Unsupported expression")), + BinaryOp::GreaterThanEqual => { + Err(VmError::chunk_parser("Unsupported expression")) + } + } + } + ValueExpression::Grouped(GroupedExpression { inner }) => { + self.evaluate_value_expression(inner.as_ref()) + } + ValueExpression::Block(_) => Err(VmError::chunk_parser("Unsupported expression")), + ValueExpression::Literal(val) => { + self.current_chunk + .op_constant(ForgeValue::from(val.clone())); + Ok(()) + } + ValueExpression::DeclareIdentifier(_) => { + Err(VmError::chunk_parser("Unsupported expression")) + } + ValueExpression::Assignment(_) => Err(VmError::chunk_parser("Unsupported expression")), + ValueExpression::ConditionalBlock(_) => { + Err(VmError::chunk_parser("Unsupported expression")) + } + ValueExpression::Identifier(_) => Err(VmError::chunk_parser("Unsupported expression")), + ValueExpression::FunctionCall(_) => { + Err(VmError::chunk_parser("Unsupported expression")) + } + ValueExpression::DeclareFunction(_) => { + Err(VmError::chunk_parser("Unsupported expression")) + } + ValueExpression::Typeof(_) => Err(VmError::chunk_parser("Unsupported expression")), + } + } + + fn evaluate_void_expression( + &mut self, + expression: &VoidExpression, + ) -> Result<Self::Output, Self::Error> { + match expression { + VoidExpression::ConditionLoop(_) => { + Err(VmError::chunk_parser("Unsupported expression")) + } + VoidExpression::Import(_) => Err(VmError::chunk_parser("Unsupported expression")), + VoidExpression::Export(_) => Err(VmError::chunk_parser("Unsupported expression")), + VoidExpression::Print(_) => Err(VmError::chunk_parser("Unsupported expression")), + } + } + + fn evaluate_program(&mut self, program: &Program) -> Result<Self::Output, Self::Error> { + for expr in program.0.iter() { + self.evaluate_expression(expr)?; + } + + self.current_chunk.push_op(OpCode::Return); + Ok(()) + } +} diff --git a/forge-script-lang/src/runtime/vm/machine.rs b/forge-script-lang/src/runtime/vm/machine.rs index 5159ba38466001a82663eb68f52a5a789f77086b..2be97ec3b72f21d8e517ff906cd58e9ae531f47f 100644 --- a/forge-script-lang/src/runtime/vm/machine.rs +++ b/forge-script-lang/src/runtime/vm/machine.rs @@ -1,8 +1,12 @@ use crate::parser::ast::BinaryOp; +use crate::parser::parse_program; use crate::runtime::value::{ForgeValue, UnsupportedOperation}; +use crate::runtime::vm::chunk_builder::ChunkBuilder; use crate::runtime::vm::chunks::Chunk; use crate::runtime::vm::{ChunkOps, OpCode}; +use crate::{format_forge_error, ForgeErrorKind}; use std::collections::VecDeque; +use std::error::Error; use std::fmt::{Display, Formatter}; const INITIAL_STACK: usize = 512; @@ -29,10 +33,25 @@ pub enum RuntimeErrorKind { UnsupportedOperation(BinaryOp), } -#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug)] +pub enum CompilerErrorKind { + AstParser(String), + Chunkbuilder(String), +} + +#[derive(Clone, Eq, PartialEq, Debug)] pub enum VmError { RuntimeError(RuntimeErrorKind), - CompilerError, + CompilerError(CompilerErrorKind), +} + +impl VmError { + pub fn ast_parser(reason: impl ToString) -> VmError { + VmError::CompilerError(CompilerErrorKind::AstParser(reason.to_string())) + } + pub fn chunk_parser(reason: impl ToString) -> VmError { + VmError::CompilerError(CompilerErrorKind::Chunkbuilder(reason.to_string())) + } } impl From<UnsupportedOperation> for VmError { @@ -44,12 +63,40 @@ impl From<UnsupportedOperation> for VmError { impl Display for VmError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - Self::RuntimeError(kind) => write!(f, "{:?}", kind), - Self::CompilerError => write!(f, "Compiler Error"), + Self::RuntimeError(kind) => write!(f, "[Runtime Error] {}", kind), + Self::CompilerError(kind) => write!(f, "[Compiler Error] {}", kind), } } } +impl Display for RuntimeErrorKind { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + RuntimeErrorKind::MissingEof => write!(f, "Unexpected end of file"), + RuntimeErrorKind::InvalidConstData => { + write!(f, "Tried to access invalid constant data") + } + RuntimeErrorKind::InvalidStackData => write!(f, "Tried to access invalid stack data"), + RuntimeErrorKind::UnsupportedOperation(op) => write!(f, "Unsupported op: {:?}", op), + } + } +} + +impl Display for CompilerErrorKind { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + CompilerErrorKind::AstParser(err) => err.as_str(), + CompilerErrorKind::Chunkbuilder(err) => err.as_str(), + } + ) + } +} + +impl Error for VmError {} + pub type VmResult = Result<ForgeValue, VmError>; impl Forge { @@ -105,6 +152,16 @@ impl Forge { Ok((second, first)) } + pub fn compile(value: impl ToString) -> Result<Chunk, VmError> { + let value = value.to_string(); + ChunkBuilder::parse(value.as_str()) + } + + pub fn compile_and_run(value: impl ToString) -> VmResult { + let chunk = Forge::compile(value)?; + Forge::exec(chunk) + } + /// Execute a chunk of code in a new VM instance pub fn exec(chunk: impl ChunkOps) -> VmResult { let mut vm = Forge::from_chunk(chunk.as_chunk()); @@ -128,7 +185,6 @@ impl Forge { OpCode::Return => return Ok(self.peek_stack().cloned().unwrap_or_default()), OpCode::Constant(idx) => { if let Some(value) = self.get_const(idx) { - println!("Load Const {:?}", &value); self.push_stack(value.clone()); } else { return Err(VmError::RuntimeError(RuntimeErrorKind::InvalidConstData)); diff --git a/forge-script-lang/src/runtime/vm/mod.rs b/forge-script-lang/src/runtime/vm/mod.rs index d1d1b4336caa5632865ccc4c73061803c3cda1c3..3486e51db982c37d2ee167f2017bd183dbb9918a 100644 --- a/forge-script-lang/src/runtime/vm/mod.rs +++ b/forge-script-lang/src/runtime/vm/mod.rs @@ -1,3 +1,4 @@ +mod chunk_builder; mod chunks; mod const_data; mod machine; diff --git a/forge-script/src/repl.rs b/forge-script/src/repl.rs index e23a11c4ab0102a6bc2d35fc8b0264c7f8253cc5..1535fa84aa6e5f0f805c8354277c6d703429cf3c 100644 --- a/forge-script/src/repl.rs +++ b/forge-script/src/repl.rs @@ -64,8 +64,14 @@ impl Repl { println!("=============\n"); } other => { - println!("{}", other); self.buffer.push(String::from(other)); + println!( + "{}", + match Forge::compile_and_run(other) { + Ok(val) => format!("{}", val), + Err(e) => format!("{}", e), + } + ); } } }