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> {
|
pub struct FnBlock<'bc> {
|
||||||
/// The bytecode of the function
|
/// The bytecode of the function
|
||||||
pub code: Vec<'bc, Instr>,
|
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.
|
/// during compilation to calculate local variable offsets.
|
||||||
pub stack_sizes: Vec<'bc, usize>,
|
pub stack_sizes: Vec<'bc, usize>,
|
||||||
/// The corresponding source code location of each instruction. This is debuginfo and only
|
/// 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.compile_block(ast_block)?;
|
||||||
|
|
||||||
self.push_instr(
|
self.shrink_stack(pre_loop_stack_size, span);
|
||||||
Instr::ShrinkStack(self.current_stack_size() - pre_loop_stack_size),
|
|
||||||
StackChange::None,
|
|
||||||
span,
|
|
||||||
);
|
|
||||||
|
|
||||||
let jmp_offset = self.back_jmp_offset(first_stmt_idx);
|
let jmp_offset = self.back_jmp_offset(first_stmt_idx);
|
||||||
self.push_instr(Instr::Jmp(jmp_offset), StackChange::None, span);
|
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.compile_block(&while_stmt.body)?;
|
||||||
|
|
||||||
self.push_instr(
|
self.shrink_stack(pre_loop_stack_size, while_stmt.span);
|
||||||
Instr::ShrinkStack(self.current_stack_size() - pre_loop_stack_size),
|
|
||||||
StackChange::None,
|
|
||||||
while_stmt.span,
|
|
||||||
);
|
|
||||||
let jmp_offset = self.back_jmp_offset(cond_index);
|
let jmp_offset = self.back_jmp_offset(cond_index);
|
||||||
self.push_instr(Instr::Jmp(jmp_offset), StackChange::None, while_stmt.span);
|
self.push_instr(Instr::Jmp(jmp_offset), StackChange::None, while_stmt.span);
|
||||||
|
|
||||||
|
|
@ -370,6 +362,20 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
|
||||||
todo!()
|
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) {
|
fn end_loop(&mut self) {
|
||||||
let breaks = self.breaks.remove(&self.loop_nesting);
|
let breaks = self.breaks.remove(&self.loop_nesting);
|
||||||
if let Some(breaks) = breaks {
|
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 {
|
fn push_instr(&mut self, instr: Instr, stack_change: StackChange, span: Span) -> usize {
|
||||||
let block = &mut self.blocks[self.current_block];
|
let block = &mut self.blocks[self.current_block];
|
||||||
let stack_top = block.stack_sizes.last().copied().unwrap_or(0);
|
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");
|
assert!(new_stack_top >= 0, "instruction popped stack below 0");
|
||||||
let new_stack_top = new_stack_top as usize;
|
let new_stack_top = new_stack_top as usize;
|
||||||
|
|
||||||
|
|
@ -434,9 +440,20 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[repr(i8)]
|
|
||||||
enum StackChange {
|
enum StackChange {
|
||||||
Shrink = -1,
|
Shrink,
|
||||||
None = 0,
|
None,
|
||||||
Grow = 1,
|
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 => {
|
char => {
|
||||||
if char.is_ascii_digit() {
|
if char.is_ascii_digit() {
|
||||||
|
let mut num_buffer = String::from(char); // we need to ignore `_`
|
||||||
let mut had_dot = false;
|
let mut had_dot = false;
|
||||||
let end = loop {
|
let end = loop {
|
||||||
// peek here because the character signaling the end should not be consumed
|
// 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 => {
|
Some((_, '.')) if !had_dot => {
|
||||||
let _ = self.code.next();
|
let _ = self.code.next();
|
||||||
|
num_buffer.push('.');
|
||||||
had_dot = true;
|
had_dot = true;
|
||||||
}
|
}
|
||||||
|
Some((_, '_')) => {
|
||||||
|
let _ = self.code.next();
|
||||||
|
}
|
||||||
Some((_, next_char)) if next_char.is_ascii_digit() => {
|
Some((_, next_char)) if next_char.is_ascii_digit() => {
|
||||||
let _ = self.code.next();
|
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
|
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 span = Span::start_end(start, end);
|
||||||
let number = number_str.parse::<f64>();
|
let number = number_str.parse::<f64>();
|
||||||
break match number {
|
break match number {
|
||||||
|
|
@ -449,6 +455,16 @@ pls :) o(* ̄▽ ̄*)ブ
|
||||||
lex_test("3 . . 2 . . 1 . . 0");
|
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]
|
#[test]
|
||||||
fn larger_numbers() {
|
fn larger_numbers() {
|
||||||
lex_test("123456789, 123456789.1234, 64785903");
|
lex_test("123456789, 123456789.1234, 64785903");
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,15 @@ type HashSet<T> = rustc_hash::FxHashSet<T>;
|
||||||
|
|
||||||
pub struct Config<'io> {
|
pub struct Config<'io> {
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
|
pub step: bool,
|
||||||
pub stdout: &'io mut dyn Write,
|
pub stdout: &'io mut dyn Write,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_program(program: &str, cfg: &mut Config) {
|
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();
|
let ast_alloc = Bump::new();
|
||||||
|
|
||||||
// SAFETY: I will try to 🥺
|
// 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 {
|
if let Err(result) = result {
|
||||||
eprintln!("error: {}", result);
|
eprintln!("error: {}", result);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,14 @@ fn main() {
|
||||||
|
|
||||||
let mut cfg = Config {
|
let mut cfg = Config {
|
||||||
debug: false,
|
debug: false,
|
||||||
|
step: false,
|
||||||
stdout: &mut stdout,
|
stdout: &mut stdout,
|
||||||
};
|
};
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
match &*arg {
|
match &*arg {
|
||||||
"--debug" => cfg.debug = true,
|
"--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::bytecode::{FnBlock, Instr};
|
||||||
use crate::gc::{Object, RtAlloc, Symbol};
|
use crate::gc::{Object, RtAlloc, Symbol};
|
||||||
|
use crate::Config;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::io::Write;
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
type VmError = &'static str;
|
type VmError = &'static str;
|
||||||
type VmResult = Result<(), VmError>;
|
type VmResult = Result<(), VmError>;
|
||||||
|
|
@ -9,7 +10,7 @@ type VmResult = Result<(), VmError>;
|
||||||
pub fn execute<'bc>(
|
pub fn execute<'bc>(
|
||||||
bytecode: &'bc [FnBlock<'bc>],
|
bytecode: &'bc [FnBlock<'bc>],
|
||||||
alloc: RtAlloc,
|
alloc: RtAlloc,
|
||||||
stdout: &mut dyn Write,
|
cfg: &mut Config,
|
||||||
) -> Result<(), VmError> {
|
) -> Result<(), VmError> {
|
||||||
let mut vm = Vm {
|
let mut vm = Vm {
|
||||||
_blocks: bytecode,
|
_blocks: bytecode,
|
||||||
|
|
@ -17,7 +18,8 @@ pub fn execute<'bc>(
|
||||||
pc: 0,
|
pc: 0,
|
||||||
stack: Vec::with_capacity(1024 << 5),
|
stack: Vec::with_capacity(1024 << 5),
|
||||||
_alloc: alloc,
|
_alloc: alloc,
|
||||||
stdout,
|
stdout: cfg.stdout,
|
||||||
|
step: cfg.step,
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.execute_function()
|
vm.execute_function()
|
||||||
|
|
@ -48,9 +50,11 @@ struct Vm<'bc, 'io> {
|
||||||
_blocks: &'bc [FnBlock<'bc>],
|
_blocks: &'bc [FnBlock<'bc>],
|
||||||
current: &'bc FnBlock<'bc>,
|
current: &'bc FnBlock<'bc>,
|
||||||
_alloc: RtAlloc,
|
_alloc: RtAlloc,
|
||||||
|
/// Index of the instruction currently being executed
|
||||||
pc: usize,
|
pc: usize,
|
||||||
stack: Vec<Value>,
|
stack: Vec<Value>,
|
||||||
stdout: &'io mut dyn Write,
|
stdout: &'io mut dyn Write,
|
||||||
|
step: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'bc> Vm<'bc, '_> {
|
impl<'bc> Vm<'bc, '_> {
|
||||||
|
|
@ -69,11 +73,15 @@ impl<'bc> Vm<'bc, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_instr(&mut self, instr: Instr) -> VmResult {
|
fn dispatch_instr(&mut self, instr: Instr) -> VmResult {
|
||||||
|
if self.step {
|
||||||
|
self.step_debug();
|
||||||
|
}
|
||||||
|
|
||||||
match instr {
|
match instr {
|
||||||
Instr::Nop => {}
|
Instr::Nop => {}
|
||||||
Instr::Store(index) => {
|
Instr::Store(index) => {
|
||||||
let val = self.stack.pop().unwrap();
|
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::Load(index) => self.stack.push(self.stack[index]),
|
||||||
Instr::PushVal(value) => self.stack.push(value),
|
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::Jmp(pos) => self.pc = (self.pc as isize + pos) as usize,
|
||||||
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;
|
||||||
// SAFETY: We only ever shrink the vec, and we don't overflow. Value is copy so no leaks as a bonus
|
// 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) }
|
unsafe { self.stack.set_len(new_len) }
|
||||||
|
|
@ -188,6 +196,22 @@ impl<'bc> Vm<'bc, '_> {
|
||||||
fn type_error(&self) -> VmError {
|
fn type_error(&self) -> VmError {
|
||||||
"bad type"
|
"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 {
|
impl Display for Value {
|
||||||
|
|
|
||||||
4
test.dil
4
test.dil
|
|
@ -1,9 +1,5 @@
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
while i < 1000 {
|
while i < 1000 {
|
||||||
print "lol";
|
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
print "hi";
|
|
||||||
|
|
@ -15,6 +15,7 @@ pub fn _run_test(code: &str) -> String {
|
||||||
let mut stdout = Vec::<u8>::new();
|
let mut stdout = Vec::<u8>::new();
|
||||||
let mut cfg = dilaria::Config {
|
let mut cfg = dilaria::Config {
|
||||||
debug: false,
|
debug: false,
|
||||||
|
step: false,
|
||||||
stdout: &mut stdout,
|
stdout: &mut stdout,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue