mirror of
https://github.com/Noratrieb/dilaria.git
synced 2026-01-14 17:35:03 +01:00
fix stack things and underscore and more
This commit is contained in:
parent
e9cad4b49e
commit
d715213731
10 changed files with 113 additions and 31 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
22
src/lex.rs
22
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::<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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
source: src/lex.rs
|
||||
assertion_line: 375
|
||||
expression: tokens
|
||||
|
||||
---
|
||||
[
|
||||
Number(
|
||||
100.0,
|
||||
),
|
||||
]
|
||||
11
src/snapshots/dilaria__lex__test__underscore_number.snap
Normal file
11
src/snapshots/dilaria__lex__test__underscore_number.snap
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
source: src/lex.rs
|
||||
assertion_line: 375
|
||||
expression: tokens
|
||||
|
||||
---
|
||||
[
|
||||
Number(
|
||||
1000000.0,
|
||||
),
|
||||
]
|
||||
34
src/vm.rs
34
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<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 {
|
||||
|
|
|
|||
4
test.dil
4
test.dil
|
|
@ -1,9 +1,5 @@
|
|||
let i = 0;
|
||||
|
||||
while i < 1000 {
|
||||
print "lol";
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
|
||||
print "hi";
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue