fix stack things and underscore and more

This commit is contained in:
nora 2022-01-08 12:03:44 +01:00
parent e9cad4b49e
commit d715213731
10 changed files with 113 additions and 31 deletions

View file

@ -25,7 +25,7 @@ use debug2::Formatter;
pub struct FnBlock<'bc> {
/// The bytecode of the function
pub code: Vec<'bc, Instr>,
/// The sizes of the stack required by the function at each instruction. This is only used
/// The sizes of the stack required by the function after the instruction at the same index. This is only used
/// during compilation to calculate local variable offsets.
pub stack_sizes: Vec<'bc, usize>,
/// The corresponding source code location of each instruction. This is debuginfo and only

View file

@ -213,11 +213,7 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
self.compile_block(ast_block)?;
self.push_instr(
Instr::ShrinkStack(self.current_stack_size() - pre_loop_stack_size),
StackChange::None,
span,
);
self.shrink_stack(pre_loop_stack_size, span);
let jmp_offset = self.back_jmp_offset(first_stmt_idx);
self.push_instr(Instr::Jmp(jmp_offset), StackChange::None, span);
@ -247,11 +243,7 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
self.compile_block(&while_stmt.body)?;
self.push_instr(
Instr::ShrinkStack(self.current_stack_size() - pre_loop_stack_size),
StackChange::None,
while_stmt.span,
);
self.shrink_stack(pre_loop_stack_size, while_stmt.span);
let jmp_offset = self.back_jmp_offset(cond_index);
self.push_instr(Instr::Jmp(jmp_offset), StackChange::None, while_stmt.span);
@ -370,6 +362,20 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
todo!()
}
fn shrink_stack(&mut self, jmp_target_stack_size: usize, span: Span) {
let amount = self.current_stack_size() - jmp_target_stack_size;
if amount == 0 {
return;
}
self.push_instr(
Instr::ShrinkStack(amount),
StackChange::ShrinkN(amount),
span,
);
}
fn end_loop(&mut self) {
let breaks = self.breaks.remove(&self.loop_nesting);
if let Some(breaks) = breaks {
@ -418,7 +424,7 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
fn push_instr(&mut self, instr: Instr, stack_change: StackChange, span: Span) -> usize {
let block = &mut self.blocks[self.current_block];
let stack_top = block.stack_sizes.last().copied().unwrap_or(0);
let new_stack_top = stack_top as isize + stack_change as isize;
let new_stack_top = stack_top as isize + stack_change.as_isize();
assert!(new_stack_top >= 0, "instruction popped stack below 0");
let new_stack_top = new_stack_top as usize;
@ -434,9 +440,20 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
}
#[derive(Debug, Copy, Clone)]
#[repr(i8)]
enum StackChange {
Shrink = -1,
None = 0,
Grow = 1,
Shrink,
None,
Grow,
ShrinkN(usize),
}
impl StackChange {
fn as_isize(&self) -> isize {
match self {
StackChange::Shrink => -1,
StackChange::None => 0,
StackChange::Grow => 1,
StackChange::ShrinkN(n) => -(*n as isize),
}
}
}

View file

@ -276,22 +276,28 @@ impl<'code, 'gc> Iterator for Lexer<'code, 'gc> {
}
char => {
if char.is_ascii_digit() {
let mut num_buffer = String::from(char); // we need to ignore `_`
let mut had_dot = false;
let end = loop {
// peek here because the character signaling the end should not be consumed
match self.code.peek() {
match self.code.peek().copied() {
Some((_, '.')) if !had_dot => {
let _ = self.code.next();
num_buffer.push('.');
had_dot = true;
}
Some((_, '_')) => {
let _ = self.code.next();
}
Some((_, next_char)) if next_char.is_ascii_digit() => {
let _ = self.code.next();
num_buffer.push(next_char);
}
Some((end, _)) => break *end,
Some((end, _)) => break end,
None => break self.src.len(), // reached EOF, so parse this number
}
};
let number_str = &self.src[start..end];
let number_str = &num_buffer;
let span = Span::start_end(start, end);
let number = number_str.parse::<f64>();
break match number {
@ -449,6 +455,16 @@ pls :) o(* ̄▽ ̄*)ブ
lex_test("3 . . 2 . . 1 . . 0");
}
#[test]
fn underscore_number() {
lex_test("1_000_000");
}
#[test]
fn trailing_underscore_number() {
lex_test("1_00_");
}
#[test]
fn larger_numbers() {
lex_test("123456789, 123456789.1234, 64785903");

View file

@ -33,10 +33,15 @@ type HashSet<T> = rustc_hash::FxHashSet<T>;
pub struct Config<'io> {
pub debug: bool,
pub step: bool,
pub stdout: &'io mut dyn Write,
}
pub fn run_program(program: &str, cfg: &mut Config) {
if cfg.debug {
eprintln!("Config: debug: {}, step: {}", cfg.debug, cfg.step);
}
let ast_alloc = Bump::new();
// SAFETY: I will try to 🥺
@ -73,7 +78,7 @@ fn process_ast(program: &str, ast: &Program, mut runtime: RtAlloc, cfg: &mut Con
}
}
let result = vm::execute(&code, runtime, cfg.stdout);
let result = vm::execute(code, runtime, cfg);
if let Err(result) = result {
eprintln!("error: {}", result);
}

View file

@ -9,13 +9,14 @@ fn main() {
let mut cfg = Config {
debug: false,
step: false,
stdout: &mut stdout,
};
for arg in args {
match &*arg {
"--debug" => cfg.debug = true,
"--shut-up-clippy" => println!("yeah shut up pls"), // please do
"--step" => cfg.step = true,
_ => {}
}
}

View file

@ -0,0 +1,11 @@
---
source: src/lex.rs
assertion_line: 375
expression: tokens
---
[
Number(
100.0,
),
]

View file

@ -0,0 +1,11 @@
---
source: src/lex.rs
assertion_line: 375
expression: tokens
---
[
Number(
1000000.0,
),
]

View file

@ -1,7 +1,8 @@
use crate::bytecode::{FnBlock, Instr};
use crate::gc::{Object, RtAlloc, Symbol};
use crate::Config;
use std::fmt::{Debug, Display, Formatter};
use std::io::Write;
use std::io::{Read, Write};
type VmError = &'static str;
type VmResult = Result<(), VmError>;
@ -9,7 +10,7 @@ type VmResult = Result<(), VmError>;
pub fn execute<'bc>(
bytecode: &'bc [FnBlock<'bc>],
alloc: RtAlloc,
stdout: &mut dyn Write,
cfg: &mut Config,
) -> Result<(), VmError> {
let mut vm = Vm {
_blocks: bytecode,
@ -17,7 +18,8 @@ pub fn execute<'bc>(
pc: 0,
stack: Vec::with_capacity(1024 << 5),
_alloc: alloc,
stdout,
stdout: cfg.stdout,
step: cfg.step,
};
vm.execute_function()
@ -48,9 +50,11 @@ struct Vm<'bc, 'io> {
_blocks: &'bc [FnBlock<'bc>],
current: &'bc FnBlock<'bc>,
_alloc: RtAlloc,
/// Index of the instruction currently being executed
pc: usize,
stack: Vec<Value>,
stdout: &'io mut dyn Write,
step: bool,
}
impl<'bc> Vm<'bc, '_> {
@ -69,11 +73,15 @@ impl<'bc> Vm<'bc, '_> {
}
fn dispatch_instr(&mut self, instr: Instr) -> VmResult {
if self.step {
self.step_debug();
}
match instr {
Instr::Nop => {}
Instr::Store(index) => {
let val = self.stack.pop().unwrap();
self.stack.insert(index, val);
self.stack[index] = val;
}
Instr::Load(index) => self.stack.push(self.stack[index]),
Instr::PushVal(value) => self.stack.push(value),
@ -163,7 +171,7 @@ impl<'bc> Vm<'bc, '_> {
}
Instr::Jmp(pos) => self.pc = (self.pc as isize + pos) as usize,
Instr::ShrinkStack(size) => {
assert!(self.stack.len() > size);
assert!(self.stack.len() >= size);
let new_len = self.stack.len() - size;
// SAFETY: We only ever shrink the vec, and we don't overflow. Value is copy so no leaks as a bonus
unsafe { self.stack.set_len(new_len) }
@ -188,6 +196,22 @@ impl<'bc> Vm<'bc, '_> {
fn type_error(&self) -> VmError {
"bad type"
}
fn step_debug(&self) {
let current_instr = &self.current.code[self.pc];
let curr_stack_size = self.stack.len();
let expected_stack_size = &self.current.stack_sizes[self.pc];
eprintln!(
"Current Instruction: {:?}
Current Stack size: {}
Expected Stack size after instruction: {}",
current_instr, curr_stack_size, expected_stack_size
);
let mut buf = [0; 64];
let _ = std::io::stdin().read(&mut buf);
}
}
impl Display for Value {

View file

@ -1,9 +1,5 @@
let i = 0;
while i < 1000 {
print "lol";
i = i + 1;
}
print "hi";
}

View file

@ -15,6 +15,7 @@ pub fn _run_test(code: &str) -> String {
let mut stdout = Vec::<u8>::new();
let mut cfg = dilaria::Config {
debug: false,
step: false,
stdout: &mut stdout,
};