mirror of
https://github.com/Noratrieb/dilaria.git
synced 2026-01-14 09:25:02 +01:00
do functions work finally maybe
This commit is contained in:
parent
ebd3dd8d82
commit
237fb34d97
12 changed files with 132 additions and 86 deletions
3
.rustfmt.toml
Normal file
3
.rustfmt.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
imports_granularity = "Crate"
|
||||||
|
newline_style = "Unix"
|
||||||
|
group_imports = "StdExternalCrate"
|
||||||
|
|
@ -3,8 +3,7 @@
|
||||||
//!
|
//!
|
||||||
//! All AST nodes are bump allocated into the lifetime `'ast`
|
//! All AST nodes are bump allocated into the lifetime `'ast`
|
||||||
|
|
||||||
use crate::errors::Span;
|
use crate::{errors::Span, gc::Symbol};
|
||||||
use crate::gc::Symbol;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "_debug", derive(dbg_pls::DebugPls))]
|
#[cfg_attr(feature = "_debug", derive(dbg_pls::DebugPls))]
|
||||||
|
|
|
||||||
|
|
@ -58,11 +58,12 @@
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::errors::Span;
|
|
||||||
use crate::vm::Value;
|
|
||||||
use bumpalo::collections::Vec;
|
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
use bumpalo::collections::Vec;
|
||||||
|
|
||||||
|
use crate::{errors::Span, vm::Value};
|
||||||
|
|
||||||
/// This struct contains all data for a function.
|
/// This struct contains all data for a function.
|
||||||
pub struct FnBlock<'bc> {
|
pub struct FnBlock<'bc> {
|
||||||
/// The bytecode of the function
|
/// The bytecode of the function
|
||||||
|
|
@ -129,9 +130,12 @@ pub enum Instr {
|
||||||
|
|
||||||
/// Calls the function at the top of the stack, after the parameters
|
/// Calls the function at the top of the stack, after the parameters
|
||||||
Call,
|
Call,
|
||||||
|
/// Returns from the function, removing that stack frame
|
||||||
Return,
|
Return,
|
||||||
|
|
||||||
|
/// Stop the program
|
||||||
|
Exit,
|
||||||
|
|
||||||
/// Shrinks the stack by `usize` elements, should always be emitted before backwards jumps
|
/// Shrinks the stack by `usize` elements, should always be emitted before backwards jumps
|
||||||
ShrinkStack(usize),
|
ShrinkStack(usize),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
//! The compiler that compiles the AST down to bytecode
|
//! The compiler that compiles the AST down to bytecode
|
||||||
|
|
||||||
use crate::ast::{
|
use std::{cell::RefCell, rc::Rc};
|
||||||
Assignment, BinaryOp, BinaryOpKind, Block, Call, CallKind, Declaration, ElsePart, Expr, FnDecl,
|
|
||||||
Ident, IfStmt, Literal, Program, Stmt, UnaryOp, WhileStmt,
|
use bumpalo::{collections::Vec, Bump};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::{
|
||||||
|
Assignment, BinaryOp, BinaryOpKind, Block, Call, CallKind, Declaration, ElsePart, Expr,
|
||||||
|
FnDecl, Ident, IfStmt, Literal, Program, Stmt, UnaryOp, WhileStmt,
|
||||||
|
},
|
||||||
|
bytecode::{FnBlock, Instr},
|
||||||
|
errors::{CompilerError, Span},
|
||||||
|
gc::Symbol,
|
||||||
|
vm::Value,
|
||||||
|
HashMap, RtAlloc,
|
||||||
};
|
};
|
||||||
use crate::bytecode::{FnBlock, Instr};
|
|
||||||
use crate::errors::{CompilerError, Span};
|
|
||||||
use crate::gc::Symbol;
|
|
||||||
use crate::vm::Value;
|
|
||||||
use crate::{HashMap, RtAlloc};
|
|
||||||
use bumpalo::collections::Vec;
|
|
||||||
use bumpalo::Bump;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
type CResult<T = ()> = Result<T, CompilerError>;
|
type CResult<T = ()> = Result<T, CompilerError>;
|
||||||
|
|
||||||
|
|
@ -119,8 +121,8 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
|
||||||
StackChange::Grow,
|
StackChange::Grow,
|
||||||
Span::dummy(),
|
Span::dummy(),
|
||||||
);
|
);
|
||||||
// exit the program.
|
// exit the program. here, we use `exit` instead of `return` because there is no stack frame
|
||||||
self.push_instr(Instr::Return, StackChange::None, Span::dummy());
|
self.push_instr(Instr::Exit, StackChange::None, Span::dummy());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,7 +130,7 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
|
||||||
// padding for backwards jumps
|
// padding for backwards jumps
|
||||||
self.push_instr(Instr::Nop, StackChange::None, block.span);
|
self.push_instr(Instr::Nop, StackChange::None, block.span);
|
||||||
|
|
||||||
self.compile_stmts(&block.stmts)
|
self.compile_stmts(block.stmts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_stmts(&mut self, stmts: &[Stmt]) -> CResult {
|
fn compile_stmts(&mut self, stmts: &[Stmt]) -> CResult {
|
||||||
|
|
@ -222,7 +224,7 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
|
||||||
.push(decl.params.len() + CALLCONV_OFFSET_DATA);
|
.push(decl.params.len() + CALLCONV_OFFSET_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.compile_stmts(&decl.body.stmts)?;
|
self.compile_stmts(decl.body.stmts)?;
|
||||||
|
|
||||||
self.push_instr(Instr::PushVal(Value::Null), StackChange::Grow, decl.span);
|
self.push_instr(Instr::PushVal(Value::Null), StackChange::Grow, decl.span);
|
||||||
self.push_instr(Instr::Return, StackChange::None, decl.span);
|
self.push_instr(Instr::Return, StackChange::None, decl.span);
|
||||||
|
|
@ -380,7 +382,7 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
|
||||||
let next_env = Env::new_inner(self.env.clone(), OuterEnvKind::Block);
|
let next_env = Env::new_inner(self.env.clone(), OuterEnvKind::Block);
|
||||||
self.env = next_env;
|
self.env = next_env;
|
||||||
|
|
||||||
self.compile_stmts(&block.stmts)?;
|
self.compile_stmts(block.stmts)?;
|
||||||
|
|
||||||
let outer = self.env.borrow().outer.clone().expect("outer env got lost");
|
let outer = self.env.borrow().outer.clone().expect("outer env got lost");
|
||||||
self.env = outer;
|
self.env = outer;
|
||||||
|
|
|
||||||
17
src/gc.rs
17
src/gc.rs
|
|
@ -2,14 +2,17 @@
|
||||||
//!
|
//!
|
||||||
//! The structure of the GC might change, but for now it's simply a `LinkedList` of `Object`s.
|
//! The structure of the GC might change, but for now it's simply a `LinkedList` of `Object`s.
|
||||||
|
|
||||||
use crate::vm::Value;
|
use std::{
|
||||||
use crate::{HashMap, HashSet};
|
collections::LinkedList,
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
ops::Deref,
|
||||||
|
ptr::NonNull,
|
||||||
|
};
|
||||||
|
|
||||||
use dbg_pls::DebugPls;
|
use dbg_pls::DebugPls;
|
||||||
use std::collections::LinkedList;
|
|
||||||
use std::fmt::{Debug, Formatter};
|
use crate::{vm::Value, HashMap, HashSet};
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::ptr::NonNull;
|
|
||||||
|
|
||||||
/// A pointer to a garbage collected value. This pointer *must* always be valid, and a value
|
/// A pointer to a garbage collected value. This pointer *must* always be valid, and a value
|
||||||
/// is only allowed to be freed once no Gc is pointing at it anymore. This is achieved through
|
/// is only allowed to be freed once no Gc is pointing at it anymore. This is achieved through
|
||||||
|
|
|
||||||
15
src/lex.rs
15
src/lex.rs
|
|
@ -4,11 +4,13 @@
|
||||||
//! For error handling, there is a single `Error` token, which contains the error. The lexer
|
//! For error handling, there is a single `Error` token, which contains the error. The lexer
|
||||||
//! is an iterator, and can therefore be used without any allocations
|
//! is an iterator, and can therefore be used without any allocations
|
||||||
|
|
||||||
use crate::errors::{CompilerError, Span};
|
use std::{iter::Peekable, str::CharIndices};
|
||||||
use crate::gc::Symbol;
|
|
||||||
use crate::RtAlloc;
|
use crate::{
|
||||||
use std::iter::Peekable;
|
errors::{CompilerError, Span},
|
||||||
use std::str::CharIndices;
|
gc::Symbol,
|
||||||
|
RtAlloc,
|
||||||
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
/// A single token generated from the lexer
|
/// A single token generated from the lexer
|
||||||
|
|
@ -360,8 +362,7 @@ fn is_valid_ident_start(char: char) -> bool {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::lex::Lexer;
|
use crate::{lex::Lexer, RtAlloc};
|
||||||
use crate::RtAlloc;
|
|
||||||
|
|
||||||
type StdString = std::string::String;
|
type StdString = std::string::String;
|
||||||
|
|
||||||
|
|
|
||||||
10
src/lib.rs
10
src/lib.rs
|
|
@ -1,4 +1,4 @@
|
||||||
#![deny(clippy::disallowed_type)]
|
#![deny(clippy::disallowed_types)]
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
mod bytecode;
|
mod bytecode;
|
||||||
|
|
@ -10,14 +10,14 @@ mod parse;
|
||||||
mod util;
|
mod util;
|
||||||
mod vm;
|
mod vm;
|
||||||
|
|
||||||
use crate::ast::Program;
|
|
||||||
use crate::gc::RtAlloc;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
pub use bumpalo::Bump;
|
pub use bumpalo::Bump;
|
||||||
pub use lex::*;
|
pub use lex::*;
|
||||||
pub use parse::*;
|
pub use parse::*;
|
||||||
|
|
||||||
|
use crate::{ast::Program, gc::RtAlloc};
|
||||||
|
|
||||||
#[cfg(not(feature = "fxhash"))]
|
#[cfg(not(feature = "fxhash"))]
|
||||||
#[allow(clippy::disallowed_types)]
|
#[allow(clippy::disallowed_types)]
|
||||||
type HashMap<K, V> = std::collections::HashMap<K, V>;
|
type HashMap<K, V> = std::collections::HashMap<K, V>;
|
||||||
|
|
@ -73,8 +73,8 @@ fn process_ast(program: &str, ast: &Program, mut runtime: RtAlloc, cfg: &mut Con
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = vm::execute(code, runtime, cfg);
|
let result = vm::execute(code, runtime, cfg);
|
||||||
if let Err(result) = result {
|
if let Err(msg) = result {
|
||||||
eprintln!("error: {}", result);
|
eprintln!("error: {msg}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => errors::display_error(program, err),
|
Err(err) => errors::display_error(program, err),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use dilaria::Config;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
use dilaria::Config;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut args = std::env::args();
|
let mut args = std::env::args();
|
||||||
|
|
||||||
|
|
|
||||||
13
src/parse.rs
13
src/parse.rs
|
|
@ -6,13 +6,16 @@
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
use crate::ast::*;
|
|
||||||
use crate::errors::{CompilerError, Span};
|
|
||||||
use crate::lex::{Token, TokenKind};
|
|
||||||
use bumpalo::collections::Vec;
|
|
||||||
use bumpalo::Bump;
|
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
|
use bumpalo::{collections::Vec, Bump};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::*,
|
||||||
|
errors::{CompilerError, Span},
|
||||||
|
lex::{Token, TokenKind},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Parser<'ast, I>
|
struct Parser<'ast, I>
|
||||||
where
|
where
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,17 @@
|
||||||
//! These tests are horrible and break all the time. Never do it like this again.
|
//! These tests are horrible and break all the time. Never do it like this again.
|
||||||
//! That said it's too late to fix it.
|
//! That said it's too late to fix it.
|
||||||
|
|
||||||
use crate::errors::Span;
|
|
||||||
use crate::parse::Parser;
|
|
||||||
use crate::RtAlloc;
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
|
use crate::{errors::Span, parse::Parser, RtAlloc};
|
||||||
|
|
||||||
mod prelude {
|
mod prelude {
|
||||||
pub(super) use super::{parser, rt, test_literal_bin_op, test_number_literal, token};
|
pub(super) use super::{parser, rt, test_literal_bin_op, test_number_literal, token};
|
||||||
pub(super) use crate::ast::{Expr, Stmt};
|
pub(super) use crate::{
|
||||||
pub(super) use crate::lex::TokenKind::*;
|
ast::{Expr, Stmt},
|
||||||
|
lex::TokenKind::*,
|
||||||
|
};
|
||||||
pub type Token = crate::lex::Token;
|
pub type Token = crate::lex::Token;
|
||||||
pub type TokenType = crate::lex::TokenKind;
|
pub type TokenType = crate::lex::TokenKind;
|
||||||
pub(super) use bumpalo::Bump;
|
pub(super) use bumpalo::Bump;
|
||||||
|
|
@ -58,9 +59,10 @@ fn test_number_literal<F: FnOnce(Vec<Token>, &Bump) -> Expr>(parser: F) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod assignment {
|
mod assignment {
|
||||||
|
use bumpalo::Bump;
|
||||||
|
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
use crate::parse::test::rt;
|
use crate::parse::test::rt;
|
||||||
use bumpalo::Bump;
|
|
||||||
|
|
||||||
fn parse_assignment(tokens: Vec<Token>, alloc: &Bump) -> Stmt {
|
fn parse_assignment(tokens: Vec<Token>, alloc: &Bump) -> Stmt {
|
||||||
let mut parser = parser(tokens, alloc);
|
let mut parser = parser(tokens, alloc);
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@ macro_rules! assert_size {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use assert_size;
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
pub(crate) use assert_size;
|
||||||
|
|
||||||
#[cfg(feature = "_debug")]
|
#[cfg(feature = "_debug")]
|
||||||
pub fn dbg(prefix: impl Display, x: impl dbg_pls::DebugPls) {
|
pub fn dbg(prefix: impl Display, x: impl dbg_pls::DebugPls) {
|
||||||
eprintln!("{prefix}{}", dbg_pls::color(&x))
|
eprintln!("{prefix}{}", dbg_pls::color(&x))
|
||||||
|
|
|
||||||
87
src/vm.rs
87
src/vm.rs
|
|
@ -1,21 +1,36 @@
|
||||||
use crate::bytecode::{FnBlock, Function, Instr};
|
use std::{
|
||||||
use crate::gc::{Object, RtAlloc, Symbol};
|
fmt::{Debug, Display, Formatter},
|
||||||
use crate::util;
|
io::{Read, Write},
|
||||||
use crate::Config;
|
};
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
|
||||||
use std::io::{Read, Write};
|
use crate::{
|
||||||
|
bytecode::{FnBlock, Function, Instr},
|
||||||
|
gc::{Object, RtAlloc, Symbol},
|
||||||
|
util, Config,
|
||||||
|
};
|
||||||
|
|
||||||
|
type ActualBackingVmError = &'static str;
|
||||||
|
|
||||||
|
type VmError = Box<VmErrorInner>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum VmErrorInner {
|
||||||
|
Exit,
|
||||||
|
Error(ActualBackingVmError),
|
||||||
|
}
|
||||||
|
|
||||||
type VmError = Box<&'static str>;
|
|
||||||
type VmResult = Result<(), VmError>;
|
type VmResult = Result<(), VmError>;
|
||||||
|
|
||||||
// never get bigger than a machine word.
|
// never get bigger than a machine word.
|
||||||
util::assert_size!(VmResult <= std::mem::size_of::<usize>());
|
util::assert_size!(VmResult <= std::mem::size_of::<usize>());
|
||||||
|
|
||||||
|
type PublicVmError = ActualBackingVmError;
|
||||||
|
|
||||||
pub fn execute<'bc>(
|
pub fn execute<'bc>(
|
||||||
bytecode: &'bc [FnBlock<'bc>],
|
bytecode: &'bc [FnBlock<'bc>],
|
||||||
alloc: RtAlloc,
|
alloc: RtAlloc,
|
||||||
cfg: &mut Config,
|
cfg: &mut Config,
|
||||||
) -> Result<(), VmError> {
|
) -> Result<(), PublicVmError> {
|
||||||
let mut vm = Vm {
|
let mut vm = Vm {
|
||||||
blocks: bytecode,
|
blocks: bytecode,
|
||||||
current: bytecode.first().ok_or("no bytecode found")?,
|
current: bytecode.first().ok_or("no bytecode found")?,
|
||||||
|
|
@ -28,7 +43,13 @@ pub fn execute<'bc>(
|
||||||
step: cfg.step,
|
step: cfg.step,
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.execute_function()
|
match vm.execute_function() {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(boxed) => match *boxed {
|
||||||
|
VmErrorInner::Exit => Ok(()),
|
||||||
|
VmErrorInner::Error(err) => Err(err),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -109,56 +130,56 @@ impl<'bc> Vm<'bc, '_> {
|
||||||
match val {
|
match val {
|
||||||
Value::Bool(bool) => self.stack.push(Value::Bool(!bool)),
|
Value::Bool(bool) => self.stack.push(Value::Bool(!bool)),
|
||||||
Value::Num(float) => self.stack.push(Value::Num(-float)),
|
Value::Num(float) => self.stack.push(Value::Num(-float)),
|
||||||
_ => return Err(self.type_error()),
|
_ => return Err(err("bad type")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instr::BinAdd => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::BinAdd => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Num(a), Value::Num(b)) => Ok(Value::Num(a + b)),
|
(Value::Num(a), Value::Num(b)) => Ok(Value::Num(a + b)),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::BinSub => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::BinSub => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Num(a), Value::Num(b)) => Ok(Value::Num(a - b)),
|
(Value::Num(a), Value::Num(b)) => Ok(Value::Num(a - b)),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::BinMul => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::BinMul => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Num(a), Value::Num(b)) => Ok(Value::Num(a * b)),
|
(Value::Num(a), Value::Num(b)) => Ok(Value::Num(a * b)),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::BinDiv => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::BinDiv => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Num(a), Value::Num(b)) => Ok(Value::Num(a / b)),
|
(Value::Num(a), Value::Num(b)) => Ok(Value::Num(a / b)),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::BinMod => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::BinMod => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Num(a), Value::Num(b)) => Ok(Value::Num(a % b)),
|
(Value::Num(a), Value::Num(b)) => Ok(Value::Num(a % b)),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::BinAnd => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::BinAnd => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(a && b)),
|
(Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(a && b)),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::BinOr => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::BinOr => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(a || b)),
|
(Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(a || b)),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::CmpGreater => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::CmpGreater => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Num(a), Value::Num(b)) => Ok(Value::Bool(a > b)),
|
(Value::Num(a), Value::Num(b)) => Ok(Value::Bool(a > b)),
|
||||||
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a.as_str() > b.as_str())),
|
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a.as_str() > b.as_str())),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::CmpGreaterEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::CmpGreaterEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Num(a), Value::Num(b)) => Ok(Value::Bool(a >= b)),
|
(Value::Num(a), Value::Num(b)) => Ok(Value::Bool(a >= b)),
|
||||||
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a.as_str() >= b.as_str())),
|
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a.as_str() >= b.as_str())),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::CmpLess => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::CmpLess => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Num(a), Value::Num(b)) => Ok(Value::Bool(a < b)),
|
(Value::Num(a), Value::Num(b)) => Ok(Value::Bool(a < b)),
|
||||||
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a.as_str() < b.as_str())),
|
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a.as_str() < b.as_str())),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::CmpLessEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::CmpLessEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Num(a), Value::Num(b)) => Ok(Value::Bool(a <= b)),
|
(Value::Num(a), Value::Num(b)) => Ok(Value::Bool(a <= b)),
|
||||||
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a.as_str() <= b.as_str())),
|
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a.as_str() <= b.as_str())),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::CmpEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::CmpEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Null, Value::Null) => Ok(TRUE),
|
(Value::Null, Value::Null) => Ok(TRUE),
|
||||||
|
|
@ -166,7 +187,7 @@ impl<'bc> Vm<'bc, '_> {
|
||||||
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a == b)),
|
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a == b)),
|
||||||
(Value::Object(_a), Value::Object(_b)) => todo!(),
|
(Value::Object(_a), Value::Object(_b)) => todo!(),
|
||||||
(Value::Array, Value::Array) => Ok(TRUE),
|
(Value::Array, Value::Array) => Ok(TRUE),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::CmpNotEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
Instr::CmpNotEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Value::Null, Value::Null) => Ok(FALSE),
|
(Value::Null, Value::Null) => Ok(FALSE),
|
||||||
|
|
@ -174,23 +195,24 @@ impl<'bc> Vm<'bc, '_> {
|
||||||
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a != b)),
|
(Value::String(a), Value::String(b)) => Ok(Value::Bool(a != b)),
|
||||||
(Value::Object(_a), Value::Object(_b)) => todo!(),
|
(Value::Object(_a), Value::Object(_b)) => todo!(),
|
||||||
(Value::Array, Value::Array) => Ok(FALSE),
|
(Value::Array, Value::Array) => Ok(FALSE),
|
||||||
_ => Err("bad type".into()),
|
_ => Err(err("bad type")),
|
||||||
})?,
|
})?,
|
||||||
Instr::Print => {
|
Instr::Print => {
|
||||||
let val = self.stack.pop().unwrap();
|
let val = self.stack.pop().unwrap();
|
||||||
writeln!(self.stdout, "{}", val).map_err(|_| "failed to write to stdout")?;
|
writeln!(self.stdout, "{}", val).map_err(|_| err("failed to write to stdout"))?;
|
||||||
}
|
}
|
||||||
Instr::JmpFalse(pos) => {
|
Instr::JmpFalse(pos) => {
|
||||||
let val = self.stack.pop().unwrap();
|
let val = self.stack.pop().unwrap();
|
||||||
match val {
|
match val {
|
||||||
Value::Bool(false) => self.pc = (self.pc as isize + pos) as usize,
|
Value::Bool(false) => self.pc = (self.pc as isize + pos) as usize,
|
||||||
Value::Bool(true) => {}
|
Value::Bool(true) => {}
|
||||||
_ => return Err("bad type".into()),
|
_ => return Err(err("bad type")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instr::Jmp(pos) => self.pc = (self.pc as isize + pos) as usize,
|
Instr::Jmp(pos) => self.pc = (self.pc as isize + pos) as usize,
|
||||||
Instr::Call => self.call()?,
|
Instr::Call => self.call()?,
|
||||||
Instr::Return => self.ret()?,
|
Instr::Return => self.ret()?,
|
||||||
|
Instr::Exit => return Err(Box::new(VmErrorInner::Exit)),
|
||||||
Instr::ShrinkStack(size) => {
|
Instr::ShrinkStack(size) => {
|
||||||
assert!(self.stack.len() >= size);
|
assert!(self.stack.len() >= size);
|
||||||
let new_len = self.stack.len() - size;
|
let new_len = self.stack.len() - size;
|
||||||
|
|
@ -246,6 +268,8 @@ impl<'bc> Vm<'bc, '_> {
|
||||||
|
|
||||||
let bookkeeping_offset = self.stack_offset + current_arity;
|
let bookkeeping_offset = self.stack_offset + current_arity;
|
||||||
|
|
||||||
|
let inner_stack_offset = self.stack_offset;
|
||||||
|
|
||||||
// now, we get all the bookkeeping info out
|
// now, we get all the bookkeeping info out
|
||||||
let old_stack_offset = self.stack[bookkeeping_offset].unwrap_native_int();
|
let old_stack_offset = self.stack[bookkeeping_offset].unwrap_native_int();
|
||||||
let old_pc = self.stack[bookkeeping_offset + 1].unwrap_native_int();
|
let old_pc = self.stack[bookkeeping_offset + 1].unwrap_native_int();
|
||||||
|
|
@ -259,15 +283,14 @@ impl<'bc> Vm<'bc, '_> {
|
||||||
|
|
||||||
// and kill the function stack frame
|
// and kill the function stack frame
|
||||||
// note: don't emit a return instruction from the whole global script.
|
// note: don't emit a return instruction from the whole global script.
|
||||||
todo!();
|
unsafe { self.stack.set_len(inner_stack_offset) };
|
||||||
|
|
||||||
|
// everything that remains...
|
||||||
|
self.stack.push(return_value);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_error(&self) -> VmError {
|
|
||||||
"bad type".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn step_debug(&self, current_instr: Instr) {
|
fn step_debug(&self, current_instr: Instr) {
|
||||||
let curr_stack_size = self.stack.len();
|
let curr_stack_size = self.stack.len();
|
||||||
// at this point, we've always incremented the pc already
|
// at this point, we've always incremented the pc already
|
||||||
|
|
@ -320,3 +343,7 @@ impl Display for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn err(msg: &'static str) -> VmError {
|
||||||
|
Box::new(VmErrorInner::Error(msg))
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue