diff --git a/dust-cli/src/main.rs b/dust-cli/src/main.rs index 13d3bf4..30de6db 100644 --- a/dust-cli/src/main.rs +++ b/dust-cli/src/main.rs @@ -1,18 +1,19 @@ -use std::io::{self, stdout, Read}; -use std::time::{Duration, Instant}; -use std::{fs::read_to_string, path::PathBuf}; - -use clap::builder::StyledStr; -use clap::error::ErrorKind; -use clap::{ - builder::{styling::AnsiColor, Styles}, - crate_authors, crate_description, crate_version, ColorChoice, Parser, Subcommand, ValueHint, +use std::{ + fs::read_to_string, + io::{self, stdout, Read}, + path::PathBuf, + time::{Duration, Instant}, +}; + +use clap::{ + builder::{styling::AnsiColor, StyledStr, Styles}, + crate_authors, crate_description, crate_version, + error::ErrorKind, + Args, ColorChoice, Error, Parser, Subcommand, ValueHint, }; -use clap::{Args, Error}; use color_print::cstr; use dust_lang::{CompileError, Compiler, DustError, DustString, Lexer, Span, Token, Vm}; -use tracing::subscriber::set_global_default; -use tracing::Level; +use tracing::{subscriber::set_global_default, Level}; use tracing_subscriber::FmtSubscriber; const CLI_HELP_TEMPLATE: &str = cstr!( @@ -214,6 +215,7 @@ fn main() { let subscriber = FmtSubscriber::builder() .with_max_level(log_level) .with_thread_names(true) + .with_file(false) .finish(); set_global_default(subscriber).expect("Failed to set tracing subscriber"); diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index b2ff1b2..c6dab80 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -197,7 +197,7 @@ impl<'src> Compiler<'src> { /// [`CompileError`] if any are found. After calling this function, check its return value for /// an error, then call [`Compiler::finish`] to get the compiled chunk. pub fn compile(&mut self) -> Result<(), CompileError> { - let span = span!(Level::INFO, "Compiling"); + let span = span!(Level::INFO, "Compile"); let _enter = span.enter(); info!( diff --git a/dust-lang/src/instruction/get_local.rs b/dust-lang/src/instruction/get_local.rs index 8e993df..7403ab0 100644 --- a/dust-lang/src/instruction/get_local.rs +++ b/dust-lang/src/instruction/get_local.rs @@ -1,10 +1,25 @@ use crate::{Instruction, Operation}; +use super::InstructionData; + pub struct GetLocal { pub destination: u8, pub local_index: u8, } +impl From for GetLocal { + fn from(data: InstructionData) -> Self { + let InstructionData { + a_field, b_field, .. + } = data; + + GetLocal { + destination: a_field, + local_index: b_field, + } + } +} + impl From<&Instruction> for GetLocal { fn from(instruction: &Instruction) -> Self { let destination = instruction.a_field(); diff --git a/dust-lang/src/vm/call_stack.rs b/dust-lang/src/vm/call_stack.rs index 345ee1b..6eafd51 100644 --- a/dust-lang/src/vm/call_stack.rs +++ b/dust-lang/src/vm/call_stack.rs @@ -26,6 +26,10 @@ impl CallStack { self.calls.is_empty() } + pub fn len(&self) -> usize { + self.calls.len() + } + pub fn push(&mut self, call: FunctionCall) { self.calls.push(call); } diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index 3b0744f..4c00b8a 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -41,7 +41,7 @@ impl Vm { } pub fn run(mut self) -> Option { - let span = span!(Level::INFO, "Running"); + let span = span!(Level::INFO, "Run"); let _enter = span.enter(); if self.threads.len() == 1 { diff --git a/dust-lang/src/vm/record.rs b/dust-lang/src/vm/record.rs index 0bf76ea..2f96695 100644 --- a/dust-lang/src/vm/record.rs +++ b/dust-lang/src/vm/record.rs @@ -7,6 +7,7 @@ use crate::{DustString, Function, FunctionType, Local, Span, Value}; use super::{run_action::RunAction, Pointer, Register}; +#[derive(Debug)] pub struct Record { pub ip: usize, pub actions: SmallVec<[RunAction; 32]>, @@ -113,6 +114,12 @@ impl Record { self.stack[to_register] = register; } + pub fn reserve_registers(&mut self, count: usize) { + for _ in 0..count { + self.stack.push(Register::Empty); + } + } + pub fn open_register(&self, register_index: u8) -> &Value { trace!("Open register R{register_index}"); @@ -151,7 +158,7 @@ impl Record { } } - pub fn replace_register_or_clone_constant( + pub fn empty_register_or_clone_constant( &mut self, register_index: u8, new_register: Register, @@ -175,6 +182,24 @@ impl Record { } } + pub fn clone_register_value_or_constant(&self, register_index: u8) -> Value { + assert!( + (register_index as usize) < self.stack.len(), + "VM Error: Register index out of bounds" + ); + + let register = &self.stack[register_index as usize]; + + match register { + Register::Value(value) => value.clone(), + Register::Pointer(pointer) => match pointer { + Pointer::Stack(register_index) => self.open_register(*register_index).clone(), + Pointer::Constant(constant_index) => self.get_constant(*constant_index).clone(), + }, + Register::Empty => panic!("VM Error: Register {register_index} is empty"), + } + } + /// DRY helper to get a value from an Argument pub fn get_argument(&self, index: u8, is_constant: bool) -> &Value { if is_constant { diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/run_action.rs index 5a64256..7c9e07b 100644 --- a/dust-lang/src/vm/run_action.rs +++ b/dust-lang/src/vm/run_action.rs @@ -2,7 +2,8 @@ use tracing::trace; use crate::{ instruction::{ - Call, CallNative, Close, LoadBoolean, LoadConstant, LoadFunction, LoadList, LoadSelf, Point, + Call, CallNative, Close, GetLocal, LoadBoolean, LoadConstant, LoadFunction, LoadList, + LoadSelf, Point, }, vm::VmError, AbstractList, ConcreteValue, Instruction, InstructionData, Type, Value, @@ -194,15 +195,14 @@ pub fn load_self(instruction_data: InstructionData, record: &mut Record) -> Thre } pub fn get_local(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { - let InstructionData { - a_field: a, - b_field: b, - .. - } = instruction_data; - let local_register_index = record.get_local_register(b); + let GetLocal { + destination, + local_index, + } = instruction_data.into(); + let local_register_index = record.get_local_register(local_index); let register = Register::Pointer(Pointer::Stack(local_register_index)); - record.set_register(a, register); + record.set_register(destination, register); ThreadSignal::Continue } diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index 71aadea..648b019 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -1,4 +1,4 @@ -use tracing::{info, trace}; +use tracing::{info, span, trace, Level}; use crate::{ vm::{FunctionCall, Register}, @@ -26,6 +26,12 @@ impl Thread { } pub fn run(&mut self) -> Option { + let mut current_call = FunctionCall { + name: None, + record_index: 0, + return_register: 0, + ip: 0, + }; let mut active = &mut self.records[0]; info!( @@ -59,6 +65,8 @@ impl Thread { let action = active.actions[active.ip]; let signal = (action.logic)(action.data, active); + trace!("Thread Signal: {:?}", signal); + active.ip += 1; match signal { @@ -73,8 +81,7 @@ impl Thread { let mut arguments = Vec::with_capacity(argument_count as usize); for register_index in first_argument_register..return_register { - let value = active - .replace_register_or_clone_constant(register_index, Register::Empty); + let value = active.clone_register_value_or_constant(register_index); arguments.push(value); } @@ -89,20 +96,21 @@ impl Thread { active.ip = 0; } + self.call_stack.push(current_call); + active.reserve_registers(arguments.len()); + + current_call = FunctionCall { + name: active.name().cloned(), + record_index: active.index(), + return_register, + ip: active.ip, + }; + active = &mut self.records[record_index]; for (index, argument) in arguments.into_iter().enumerate() { active.set_register(index as u8, Register::Value(argument)); } - - let function_call = FunctionCall { - name: active.name().cloned(), - record_index: active.index(), - return_register, - ip: 0, - }; - - self.call_stack.push(function_call); } ThreadSignal::LoadFunction { from_record_index, @@ -121,47 +129,41 @@ impl Thread { active.set_register(to_register_index, register); } ThreadSignal::Return(should_return_value) => { - let returning_call = match self.call_stack.pop() { - Some(function_call) => function_call, - None => { - if should_return_value { - return active.last_assigned_register().map(|register| { - active.replace_register_or_clone_constant( - register, - Register::Empty, - ) - }); - } else { - return None; - } - } - }; - let outer_call = self.call_stack.last(); - let record_index = outer_call.map_or(0, |call| call.record_index as usize); + trace!("{:#?}", self.call_stack); + + if self.call_stack.is_empty() { + return None; + } + + let outer_call = self.call_stack.pop().unwrap(); + let record_index = outer_call.record_index as usize; if should_return_value { let return_register = active .last_assigned_register() .unwrap_or_else(|| panic!("Expected return value")); let return_value = active - .replace_register_or_clone_constant(return_register, Register::Empty); + .empty_register_or_clone_constant(return_register, Register::Empty); active = &mut self.records[record_index]; + active.reserve_registers((current_call.return_register + 1) as usize); active.set_register( - returning_call.return_register, + current_call.return_register, Register::Value(return_value), ); } else { active = &mut self.records[record_index]; - active.ip = record_index; } + + current_call = outer_call; } } } } } +#[derive(Debug)] pub enum ThreadSignal { Continue, Call { diff --git a/examples/fibonacci.ds b/examples/fibonacci.ds index 6a59bba..f36a668 100644 --- a/examples/fibonacci.ds +++ b/examples/fibonacci.ds @@ -5,4 +5,4 @@ fn fib (n: int) -> int { fib(n - 1) + fib(n - 2) } -write_line(fib(0)) +write_line(fib(2))