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

Create basic VM struct

parent 7f9eb505
No related branches found
No related tags found
No related merge requests found
Showing with 361 additions and 4 deletions
...@@ -207,3 +207,15 @@ pub fn token_typeof(input: Span) -> IResult<Span, ScriptToken> { ...@@ -207,3 +207,15 @@ pub fn token_typeof(input: Span) -> IResult<Span, ScriptToken> {
}, },
)) ))
} }
pub fn token_finally(input: Span) -> IResult<Span, ScriptToken> {
let (input, pos) = position(input)?;
let (input, _) = tag_ws("finally", input)?;
Ok((
input,
ScriptToken {
position: pos,
token_type: ScriptTokenType::Finally,
},
))
}
...@@ -29,6 +29,7 @@ mod _lex { ...@@ -29,6 +29,7 @@ mod _lex {
use nom::branch::alt; use nom::branch::alt;
use nom::IResult; use nom::IResult;
use crate::lexer::keywords::token_finally;
use nom::character::complete::multispace0; use nom::character::complete::multispace0;
use nom::multi::fold_many0; use nom::multi::fold_many0;
use nom::sequence::delimited; use nom::sequence::delimited;
...@@ -53,6 +54,7 @@ mod _lex { ...@@ -53,6 +54,7 @@ mod _lex {
token_super, token_super,
token_from, token_from,
token_typeof, token_typeof,
token_finally,
)), )),
alt(( alt((
token_plus, token_plus,
......
...@@ -59,6 +59,7 @@ pub enum ScriptTokenType<'a> { ...@@ -59,6 +59,7 @@ pub enum ScriptTokenType<'a> {
Alias, Alias,
From, From,
Typeof, Typeof,
Finally,
// Misc // Misc
Eof, Eof,
...@@ -120,6 +121,7 @@ impl<'a> ScriptTokenType<'a> { ...@@ -120,6 +121,7 @@ impl<'a> ScriptTokenType<'a> {
ScriptTokenType::From => 4, ScriptTokenType::From => 4,
ScriptTokenType::Eof => 0, ScriptTokenType::Eof => 0,
ScriptTokenType::Typeof => 6, ScriptTokenType::Typeof => 6,
ScriptTokenType::Finally => 7,
} }
} }
} }
...@@ -174,6 +176,7 @@ impl<'a> Display for ScriptTokenType<'a> { ...@@ -174,6 +176,7 @@ impl<'a> Display for ScriptTokenType<'a> {
ScriptTokenType::From => write!(f, "from"), ScriptTokenType::From => write!(f, "from"),
ScriptTokenType::Eof => write!(f, ""), ScriptTokenType::Eof => write!(f, ""),
ScriptTokenType::Typeof => write!(f, "typeof"), ScriptTokenType::Typeof => write!(f, "typeof"),
ScriptTokenType::Finally => write!(f, "finally"),
} }
} }
} }
...@@ -234,6 +237,7 @@ impl<'a> TryFrom<&'a str> for ScriptTokenType<'a> { ...@@ -234,6 +237,7 @@ impl<'a> TryFrom<&'a str> for ScriptTokenType<'a> {
"as" => Ok(ScriptTokenType::Alias), "as" => Ok(ScriptTokenType::Alias),
"from" => Ok(ScriptTokenType::From), "from" => Ok(ScriptTokenType::From),
"typeof" => Ok(ScriptTokenType::Typeof), "typeof" => Ok(ScriptTokenType::Typeof),
"finally" => Ok(ScriptTokenType::Finally),
"false" => Ok(ScriptTokenType::Boolean(false)), "false" => Ok(ScriptTokenType::Boolean(false)),
"true" => Ok(ScriptTokenType::Boolean(true)), "true" => Ok(ScriptTokenType::Boolean(true)),
_ => Err(TokenFromStringError { source: value }), _ => Err(TokenFromStringError { source: value }),
...@@ -312,6 +316,7 @@ mod token_tests { ...@@ -312,6 +316,7 @@ mod token_tests {
#[test_case("typeof" => Ok(ScriptTokenType::Typeof); r#"Parse Typeof"#)] #[test_case("typeof" => Ok(ScriptTokenType::Typeof); r#"Parse Typeof"#)]
#[test_case("true" => Ok(ScriptTokenType::Boolean(true)); r#"Parse true"#)] #[test_case("true" => Ok(ScriptTokenType::Boolean(true)); r#"Parse true"#)]
#[test_case("false" => Ok(ScriptTokenType::Boolean(false)); r#"Parse false"#)] #[test_case("false" => Ok(ScriptTokenType::Boolean(false)); r#"Parse false"#)]
#[test_case("finally" => Ok(ScriptTokenType::Finally); r#"Parse finally"#)]
fn parse_token_type(value: &'static str) -> Result<ScriptTokenType, TokenFromStringError> { fn parse_token_type(value: &'static str) -> Result<ScriptTokenType, TokenFromStringError> {
ScriptTokenType::try_from(value) ScriptTokenType::try_from(value)
} }
......
...@@ -2,6 +2,7 @@ mod error; ...@@ -2,6 +2,7 @@ mod error;
mod lexer; mod lexer;
mod parser; mod parser;
pub mod runtime; pub mod runtime;
mod utilities;
pub use error::{ pub use error::{
format_forge_error, print_forge_error, ForgeError, ForgeErrorKind, ParseError, ParseErrorKind, format_forge_error, print_forge_error, ForgeError, ForgeErrorKind, ParseError, ParseErrorKind,
......
...@@ -103,6 +103,11 @@ pub enum BinaryOp { ...@@ -103,6 +103,11 @@ pub enum BinaryOp {
Equals, Equals,
BoolAnd, BoolAnd,
BoolOr, BoolOr,
NotEquals,
LessThan,
GreaterThan,
LessThanEqual,
GreaterThanEqual,
} }
impl BinaryOp { impl BinaryOp {
...@@ -128,6 +133,11 @@ impl Display for BinaryOp { ...@@ -128,6 +133,11 @@ impl Display for BinaryOp {
BinaryOp::Equals => "==", BinaryOp::Equals => "==",
BinaryOp::BoolAnd => "&&", BinaryOp::BoolAnd => "&&",
BinaryOp::BoolOr => "||", BinaryOp::BoolOr => "||",
BinaryOp::NotEquals => "!=",
BinaryOp::LessThan => "<",
BinaryOp::GreaterThan => ">",
BinaryOp::LessThanEqual => "<=",
BinaryOp::GreaterThanEqual => ">=",
} }
) )
} }
......
...@@ -63,7 +63,7 @@ peg::parser! { ...@@ -63,7 +63,7 @@ peg::parser! {
rule condition_loop() -> ConditionalLoop rule condition_loop() -> ConditionalLoop
= "while" guard:value_expression() block:block() = "while" guard:value_expression() block:block()
{ ConditionalLoop { block: GuardedBlock { guard: Box::new(guard), block }, fallback: None } } { ConditionalLoop { block: GuardedBlock { guard: Box::new(guard), block }, fallback: None } }
/ "while" guard:value_expression() block:block() "else" fallback:block() / "while" guard:value_expression() block:block() "finally" fallback:block()
{ ConditionalLoop { block: GuardedBlock { guard: Box::new(guard), block }, fallback: Some(fallback) } } { ConditionalLoop { block: GuardedBlock { guard: Box::new(guard), block }, fallback: Some(fallback) } }
rule conditional_statement() -> Conditional rule conditional_statement() -> Conditional
......
...@@ -157,7 +157,13 @@ impl Visitor for SimpleExecutor { ...@@ -157,7 +157,13 @@ impl Visitor for SimpleExecutor {
BinaryOp::Multiply => Ok((lhs * rhs)?), BinaryOp::Multiply => Ok((lhs * rhs)?),
BinaryOp::Modulo => Ok((lhs % rhs)?), BinaryOp::Modulo => Ok((lhs % rhs)?),
BinaryOp::Equals => Ok((lhs == rhs).into()), BinaryOp::Equals => Ok((lhs == rhs).into()),
BinaryOp::BoolOr | BinaryOp::BoolAnd => Ok(ForgeValue::Null), BinaryOp::BoolOr
| BinaryOp::BoolAnd
| BinaryOp::NotEquals
| BinaryOp::LessThan
| BinaryOp::GreaterThan
| BinaryOp::LessThanEqual
| BinaryOp::GreaterThanEqual => todo!("Implement Binary Op"),
} }
} }
} }
...@@ -216,8 +222,17 @@ impl Visitor for SimpleExecutor { ...@@ -216,8 +222,17 @@ impl Visitor for SimpleExecutor {
expression: &VoidExpression, expression: &VoidExpression,
) -> Result<Self::Output, Self::Error> { ) -> Result<Self::Output, Self::Error> {
match expression { match expression {
VoidExpression::ConditionLoop(_) => { VoidExpression::ConditionLoop(cond_loop) => {
return Err(RuntimeError::Unsupported("ConditionLoop")) while self
.evaluate_value_expression(cond_loop.block.guard.as_ref())?
.as_bool()
{
self.evaluate_expression_list(&cond_loop.block.block)?;
}
if let Some(fallback) = &cond_loop.fallback {
self.evaluate_expression_list(fallback)?;
}
} }
VoidExpression::Import(_) => return Err(RuntimeError::Unsupported("Import")), VoidExpression::Import(_) => return Err(RuntimeError::Unsupported("Import")),
VoidExpression::Export(_) => return Err(RuntimeError::Unsupported("Export")), VoidExpression::Export(_) => return Err(RuntimeError::Unsupported("Export")),
...@@ -272,6 +287,8 @@ mod interpreter_test { ...@@ -272,6 +287,8 @@ mod interpreter_test {
#[test_case("let value = true; if value { 123 } else { 456 }" => Ok(ForgeValue::from(123)); "eval if branch")] #[test_case("let value = true; if value { 123 } else { 456 }" => Ok(ForgeValue::from(123)); "eval if branch")]
#[test_case("let value = true; if !value { 123 } else { 456 }" => Ok(ForgeValue::from(456)); "eval else branch")] #[test_case("let value = true; if !value { 123 } else { 456 }" => Ok(ForgeValue::from(456)); "eval else branch")]
#[test_case("let value = 2; if value == 1 { 123 } else if value == 2 { 456 } else { \"Nothing\" }" => Ok(ForgeValue::from(456)); "eval middle if else branch")] #[test_case("let value = 2; if value == 1 { 123 } else if value == 2 { 456 } else { \"Nothing\" }" => Ok(ForgeValue::from(456)); "eval middle if else branch")]
// #[test_case("let value = 0; while value < 4 { value = value + 4 }; value" => Ok(ForgeValue::from(4)); "simple conditional loop")]
// #[test_case("let value = 0; while value < 4 { value = value + 4 } finally { value = 1234 }; value" => Ok(ForgeValue::from(1234)); "conditional loop with cleanup")]
fn conditional_blocks(prog: &'static str) -> RuntimeResult<ForgeValue> { fn conditional_blocks(prog: &'static str) -> RuntimeResult<ForgeValue> {
let ast = parse_program(prog).expect("Failed to parse program"); let ast = parse_program(prog).expect("Failed to parse program");
let mut vm = SimpleExecutor::default(); let mut vm = SimpleExecutor::default();
......
pub mod executor; pub mod executor;
pub mod numbers; pub mod numbers;
pub mod value; pub mod value;
mod vm;
use crate::runtime::value::ForgeValue;
use crate::runtime::vm::const_data::{ConstData, ConstDataRef};
use crate::runtime::vm::opcode::{OpCode, OpCodeError};
use std::fmt::{Display, Formatter};
use std::ops::{Deref, DerefMut};
#[derive(Default)]
pub struct Chunk {
code: Vec<OpCode>,
consts: ConstData,
}
impl From<Vec<OpCode>> for Chunk {
fn from(value: Vec<OpCode>) -> Self {
Self {
code: value,
consts: ConstData::default(),
}
}
}
pub struct ChunkRef<'scope> {
code: &'scope [OpCode],
consts: ConstDataRef<'scope>,
}
impl<'scope> From<&'scope Chunk> for ChunkRef<'scope> {
fn from(value: &'scope Chunk) -> Self {
Self {
code: value.code.as_slice(),
consts: ConstDataRef::from(value.consts.as_slice()),
}
}
}
impl Chunk {
pub fn push_op(&mut self, op: OpCode) -> usize {
let idx = self.code.len();
self.code.push(op);
idx
}
pub fn push_const_data(&mut self, data: ForgeValue) -> usize {
let idx = self.consts.len();
self.consts.push(data);
idx
}
pub fn as_ref(&self) -> ChunkRef {
ChunkRef {
code: self.code.as_slice(),
consts: self.consts.as_ref(),
}
}
pub fn op_return(&mut self) {
self.push_op(OpCode::Return);
}
pub fn op_constant(&mut self, data: ForgeValue) {
let idx = self.push_const_data(data);
self.push_op(OpCode::Constant(idx));
}
}
pub trait ChunkOps {
fn as_slice(&self) -> &[OpCode];
fn write_chunk_data(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut offset = 0;
let slice = self.as_slice();
while offset < slice.len() {
let opcode = match slice.get(offset) {
Some(value) => value,
None => {
break;
}
};
write!(f, "[{:08x}] {} ", offset, opcode)?;
opcode.format_operand(f)?;
writeln!(f, "")?;
offset = opcode.next_offset(offset);
}
Ok(())
}
fn format_chunk(&self, name: impl Display) -> String;
}
macro_rules! chunk_ops_generic {
() => {
fn format_chunk(&self, name: impl Display) -> String {
format!("== {} ==\n{}", name, self)
}
};
}
impl ChunkOps for Chunk {
fn as_slice(&self) -> &[OpCode] {
self.code.as_slice()
}
chunk_ops_generic!();
}
impl<'scope> ChunkOps for ChunkRef<'scope> {
fn as_slice(&self) -> &[OpCode] {
self.code
}
chunk_ops_generic!();
}
impl Display for Chunk {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.write_chunk_data(f)
}
}
impl<'scope> Display for ChunkRef<'scope> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.write_chunk_data(f)
}
}
#[cfg(test)]
mod _chunks {
use super::*;
#[test]
fn basic_chunk_format() {
let chunk = Chunk::from(vec![OpCode::Return]);
let expected = "== test ==\n[00000000] op_return \n";
assert_eq!(chunk.format_chunk("test"), String::from(expected));
}
#[test]
fn const_chunk() {
let mut chunk = Chunk::default();
chunk.op_constant(ForgeValue::Null);
chunk.op_return();
let expected = format!(
"== test ==\n[00000000] op_constant {:016x}\n[00000001] op_return \n",
0
);
assert_eq!(chunk.format_chunk("test"), expected);
}
}
use crate::runtime::value::ForgeValue;
use std::ops::{Deref, DerefMut};
#[derive(Default)]
pub struct ConstData(Vec<ForgeValue>);
pub struct ConstDataRef<'scope>(&'scope [ForgeValue]);
impl From<Vec<ForgeValue>> for ConstData {
fn from(value: Vec<ForgeValue>) -> Self {
ConstData(value)
}
}
impl<'scope> From<&'scope [ForgeValue]> for ConstDataRef<'scope> {
fn from(value: &'scope [ForgeValue]) -> Self {
ConstDataRef(value)
}
}
impl Deref for ConstData {
type Target = Vec<ForgeValue>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for ConstData {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl ConstData {
pub fn as_ref(&self) -> ConstDataRef {
ConstDataRef(self.0.as_slice())
}
}
impl<'scope> Deref for ConstDataRef<'scope> {
type Target = &'scope [ForgeValue];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'scope> DerefMut for ConstDataRef<'scope> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
mod chunks;
mod const_data;
mod opcode;
use std::error::Error;
use std::fmt::{Display, Formatter};
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum OpCode {
Return,
/// Load a constant value from the chunk's data pool. Data starts at the included offset
Constant(usize),
}
impl OpCode {
pub fn next_offset(&self, current: usize) -> usize {
match self {
_ => current + 1,
}
}
pub fn op_byte(&self) -> u8 {
match self {
OpCode::Return => 1,
OpCode::Constant(_) => 2,
}
}
pub fn operand_bytes(&self) -> Vec<u8> {
match self {
OpCode::Return => vec![],
OpCode::Constant(value) => value.to_le_bytes().to_vec(),
}
}
pub fn format_operand(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for byte in self.operand_bytes() {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub enum OpCodeError {
Unknown(u8),
}
impl Display for OpCodeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown(value) => write!(f, "Unknown opcode {}", value),
}
}
}
impl Error for OpCodeError {}
impl Display for OpCode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
OpCode::Return => "op_return",
OpCode::Constant(_) => "op_constant",
}
)
}
}
#[macro_export]
macro_rules! deref_as {
($type:ty => $other:ty) => {
impl std::ops::Deref for $type {
type Target = $other;
fn deref(&self) -> &Self::Target {
&self.0
}
}
};
($($lifetime:tt),+: $type:ty => $other:ty) => {
impl <$($lifetime),+> std::ops::Deref for $type {
type Target = $other;
fn deref(&self) -> &Self::Target {
&self.0
}
}
};
(mut $type:ty => $other:ty) => {
deref_as!($type => $other);
impl std::ops::DerefMut for $type {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
};
($($lifetime:tt),+ mut: $type:ty => $other:ty) => {
deref_as!($($lifetime),+: $type => $other);
impl <$($lifetime),+> std::ops::DerefMut for $type {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
};
}
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