do functions work finally maybe

This commit is contained in:
nora 2022-04-24 19:53:58 +02:00
parent ebd3dd8d82
commit 237fb34d97
12 changed files with 132 additions and 86 deletions

3
.rustfmt.toml Normal file
View file

@ -0,0 +1,3 @@
imports_granularity = "Crate"
newline_style = "Unix"
group_imports = "StdExternalCrate"

View file

@ -3,8 +3,7 @@
//!
//! All AST nodes are bump allocated into the lifetime `'ast`
use crate::errors::Span;
use crate::gc::Symbol;
use crate::{errors::Span, gc::Symbol};
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
#[cfg_attr(feature = "_debug", derive(dbg_pls::DebugPls))]

View file

@ -58,11 +58,12 @@
//!
//! ```
use crate::errors::Span;
use crate::vm::Value;
use bumpalo::collections::Vec;
use std::fmt::{Debug, Formatter};
use bumpalo::collections::Vec;
use crate::{errors::Span, vm::Value};
/// This struct contains all data for a function.
pub struct FnBlock<'bc> {
/// The bytecode of the function
@ -129,9 +130,12 @@ pub enum Instr {
/// Calls the function at the top of the stack, after the parameters
Call,
/// Returns from the function, removing that stack frame
Return,
/// Stop the program
Exit,
/// Shrinks the stack by `usize` elements, should always be emitted before backwards jumps
ShrinkStack(usize),
}

View file

@ -1,18 +1,20 @@
//! The compiler that compiles the AST down to bytecode
use crate::ast::{
Assignment, BinaryOp, BinaryOpKind, Block, Call, CallKind, Declaration, ElsePart, Expr, FnDecl,
Ident, IfStmt, Literal, Program, Stmt, UnaryOp, WhileStmt,
use std::{cell::RefCell, rc::Rc};
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>;
@ -119,8 +121,8 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
StackChange::Grow,
Span::dummy(),
);
// exit the program.
self.push_instr(Instr::Return, StackChange::None, Span::dummy());
// exit the program. here, we use `exit` instead of `return` because there is no stack frame
self.push_instr(Instr::Exit, StackChange::None, Span::dummy());
Ok(())
}
@ -128,7 +130,7 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
// padding for backwards jumps
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 {
@ -222,7 +224,7 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
.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::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);
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");
self.env = outer;

View file

@ -2,14 +2,17 @@
//!
//! The structure of the GC might change, but for now it's simply a `LinkedList` of `Object`s.
use crate::vm::Value;
use crate::{HashMap, HashSet};
use std::{
collections::LinkedList,
fmt::{Debug, Formatter},
hash::{Hash, Hasher},
ops::Deref,
ptr::NonNull,
};
use dbg_pls::DebugPls;
use std::collections::LinkedList;
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::ptr::NonNull;
use crate::{vm::Value, HashMap, HashSet};
/// 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

View file

@ -4,11 +4,13 @@
//! 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
use crate::errors::{CompilerError, Span};
use crate::gc::Symbol;
use crate::RtAlloc;
use std::iter::Peekable;
use std::str::CharIndices;
use std::{iter::Peekable, str::CharIndices};
use crate::{
errors::{CompilerError, Span},
gc::Symbol,
RtAlloc,
};
///
/// A single token generated from the lexer
@ -360,8 +362,7 @@ fn is_valid_ident_start(char: char) -> bool {
#[cfg(test)]
mod test {
use crate::lex::Lexer;
use crate::RtAlloc;
use crate::{lex::Lexer, RtAlloc};
type StdString = std::string::String;

View file

@ -1,4 +1,4 @@
#![deny(clippy::disallowed_type)]
#![deny(clippy::disallowed_types)]
mod ast;
mod bytecode;
@ -10,14 +10,14 @@ mod parse;
mod util;
mod vm;
use crate::ast::Program;
use crate::gc::RtAlloc;
use std::io::Write;
pub use bumpalo::Bump;
pub use lex::*;
pub use parse::*;
use crate::{ast::Program, gc::RtAlloc};
#[cfg(not(feature = "fxhash"))]
#[allow(clippy::disallowed_types)]
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);
if let Err(result) = result {
eprintln!("error: {}", result);
if let Err(msg) = result {
eprintln!("error: {msg}");
}
}
Err(err) => errors::display_error(program, err),

View file

@ -1,6 +1,7 @@
use dilaria::Config;
use std::io;
use dilaria::Config;
fn main() {
let mut args = std::env::args();

View file

@ -6,13 +6,16 @@
#[cfg(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 bumpalo::{collections::Vec, Bump};
use crate::{
ast::*,
errors::{CompilerError, Span},
lex::{Token, TokenKind},
};
#[derive(Debug)]
struct Parser<'ast, I>
where

View file

@ -3,16 +3,17 @@
//! These tests are horrible and break all the time. Never do it like this again.
//! That said it's too late to fix it.
use crate::errors::Span;
use crate::parse::Parser;
use crate::RtAlloc;
use bumpalo::Bump;
use prelude::*;
use crate::{errors::Span, parse::Parser, RtAlloc};
mod prelude {
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::lex::TokenKind::*;
pub(super) use crate::{
ast::{Expr, Stmt},
lex::TokenKind::*,
};
pub type Token = crate::lex::Token;
pub type TokenType = crate::lex::TokenKind;
pub(super) use bumpalo::Bump;
@ -58,9 +59,10 @@ fn test_number_literal<F: FnOnce(Vec<Token>, &Bump) -> Expr>(parser: F) {
}
mod assignment {
use bumpalo::Bump;
use super::prelude::*;
use crate::parse::test::rt;
use bumpalo::Bump;
fn parse_assignment(tokens: Vec<Token>, alloc: &Bump) -> Stmt {
let mut parser = parser(tokens, alloc);

View file

@ -6,9 +6,10 @@ macro_rules! assert_size {
};
}
pub(crate) use assert_size;
use std::fmt::Display;
pub(crate) use assert_size;
#[cfg(feature = "_debug")]
pub fn dbg(prefix: impl Display, x: impl dbg_pls::DebugPls) {
eprintln!("{prefix}{}", dbg_pls::color(&x))

View file

@ -1,21 +1,36 @@
use crate::bytecode::{FnBlock, Function, Instr};
use crate::gc::{Object, RtAlloc, Symbol};
use crate::util;
use crate::Config;
use std::fmt::{Debug, Display, Formatter};
use std::io::{Read, Write};
use std::{
fmt::{Debug, Display, Formatter},
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>;
// never get bigger than a machine word.
util::assert_size!(VmResult <= std::mem::size_of::<usize>());
type PublicVmError = ActualBackingVmError;
pub fn execute<'bc>(
bytecode: &'bc [FnBlock<'bc>],
alloc: RtAlloc,
cfg: &mut Config,
) -> Result<(), VmError> {
) -> Result<(), PublicVmError> {
let mut vm = Vm {
blocks: bytecode,
current: bytecode.first().ok_or("no bytecode found")?,
@ -28,7 +43,13 @@ pub fn execute<'bc>(
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)]
@ -86,7 +107,7 @@ impl<'bc> Vm<'bc, '_> {
None => return Ok(()),
}
if self.pc > 0 {
//debug_assert_eq!(self.current.stack_sizes[self.pc - 1], self.stack.len());
// debug_assert_eq!(self.current.stack_sizes[self.pc - 1], self.stack.len());
}
}
}
@ -109,56 +130,56 @@ impl<'bc> Vm<'bc, '_> {
match val {
Value::Bool(bool) => self.stack.push(Value::Bool(!bool)),
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) {
(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) {
(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) {
(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) {
(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) {
(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) {
(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) {
(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) {
(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())),
_ => Err("bad type".into()),
_ => Err(err("bad type")),
})?,
Instr::CmpGreaterEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
(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())),
_ => Err("bad type".into()),
_ => Err(err("bad type")),
})?,
Instr::CmpLess => self.bin_op(|lhs, rhs| match (lhs, rhs) {
(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())),
_ => Err("bad type".into()),
_ => Err(err("bad type")),
})?,
Instr::CmpLessEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
(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())),
_ => Err("bad type".into()),
_ => Err(err("bad type")),
})?,
Instr::CmpEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
(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::Object(_a), Value::Object(_b)) => todo!(),
(Value::Array, Value::Array) => Ok(TRUE),
_ => Err("bad type".into()),
_ => Err(err("bad type")),
})?,
Instr::CmpNotEq => self.bin_op(|lhs, rhs| match (lhs, rhs) {
(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::Object(_a), Value::Object(_b)) => todo!(),
(Value::Array, Value::Array) => Ok(FALSE),
_ => Err("bad type".into()),
_ => Err(err("bad type")),
})?,
Instr::Print => {
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) => {
let val = self.stack.pop().unwrap();
match val {
Value::Bool(false) => self.pc = (self.pc as isize + pos) as usize,
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::Call => self.call()?,
Instr::Return => self.ret()?,
Instr::Exit => return Err(Box::new(VmErrorInner::Exit)),
Instr::ShrinkStack(size) => {
assert!(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 inner_stack_offset = self.stack_offset;
// now, we get all the bookkeeping info out
let old_stack_offset = self.stack[bookkeeping_offset].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
// 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(())
}
fn type_error(&self) -> VmError {
"bad type".into()
}
fn step_debug(&self, current_instr: Instr) {
let curr_stack_size = self.stack.len();
// 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))
}