diff --git a/src/bytecode.rs b/src/bytecode.rs index 3672dcd..ee869b7 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -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 diff --git a/src/compile.rs b/src/compile.rs index 0bbb79e..af2b5e4 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -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), + } + } } diff --git a/src/lex.rs b/src/lex.rs index a22f739..04e205c 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -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::(); 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"); diff --git a/src/lib.rs b/src/lib.rs index dbf6504..b85f553 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,10 +33,15 @@ type HashSet = rustc_hash::FxHashSet; 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); } diff --git a/src/main.rs b/src/main.rs index 5e58915..dfe7190 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, _ => {} } } diff --git a/src/snapshots/dilaria__lex__test__trailing_underscore_number.snap b/src/snapshots/dilaria__lex__test__trailing_underscore_number.snap new file mode 100644 index 0000000..6869b58 --- /dev/null +++ b/src/snapshots/dilaria__lex__test__trailing_underscore_number.snap @@ -0,0 +1,11 @@ +--- +source: src/lex.rs +assertion_line: 375 +expression: tokens + +--- +[ + Number( + 100.0, + ), +] diff --git a/src/snapshots/dilaria__lex__test__underscore_number.snap b/src/snapshots/dilaria__lex__test__underscore_number.snap new file mode 100644 index 0000000..ce20b0f --- /dev/null +++ b/src/snapshots/dilaria__lex__test__underscore_number.snap @@ -0,0 +1,11 @@ +--- +source: src/lex.rs +assertion_line: 375 +expression: tokens + +--- +[ + Number( + 1000000.0, + ), +] diff --git a/src/vm.rs b/src/vm.rs index 02dd450..645dc04 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -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, 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 { diff --git a/test.dil b/test.dil index 791f9b0..7a64ee2 100644 --- a/test.dil +++ b/test.dil @@ -1,9 +1,5 @@ let i = 0; while i < 1000 { - print "lol"; i = i + 1; -} - - -print "hi"; \ No newline at end of file +} \ No newline at end of file diff --git a/tests/common.rs b/tests/common.rs index 5f01285..55d82c3 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -15,6 +15,7 @@ pub fn _run_test(code: &str) -> String { let mut stdout = Vec::::new(); let mut cfg = dilaria::Config { debug: false, + step: false, stdout: &mut stdout, };