diff --git a/dust-cli/src/main.rs b/dust-cli/src/main.rs index 1590c90..85027df 100644 --- a/dust-cli/src/main.rs +++ b/dust-cli/src/main.rs @@ -1,13 +1,13 @@ use std::{ fmt::{self, Display, Formatter}, - fs::{read_to_string, File, OpenOptions}, + fs::{File, OpenOptions}, io::{stdin, stdout, Read, Write}, time::Instant, }; use clap::{Args, Parser, Subcommand, ValueEnum}; use colored::Colorize; -use dust_lang::{compile, display_token_list, format, lex, run_source, vm::run_chunk, Chunk}; +use dust_lang::{compile, display_token_list, format, lex, vm::run_chunk}; use env_logger::Target; use log::{Level, LevelFilter}; diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 5767340..1d76af9 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -4,7 +4,7 @@ //! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they //! belong to a named function. -use std::fmt::{self, Debug, Display, Formatter}; +use std::fmt::{self, Debug, Display, Formatter, Write}; use std::hash::{Hash, Hasher}; use serde::{Deserialize, Serialize}; @@ -193,7 +193,7 @@ impl Debug for Chunk { let disassembly = self.disassembler().style(false).disassemble(); if cfg!(debug_assertions) { - write!(f, "\n",)?; + f.write_char('\n')?; } write!(f, "{}", disassembly) diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler.rs index 40e088a..f5da9e4 100644 --- a/dust-lang/src/compiler.rs +++ b/dust-lang/src/compiler.rs @@ -4,7 +4,6 @@ //! - [`compile`], which compiles the entire input and returns a chunk //! - [`Compiler`], which compiles the input a token at a time while assembling a chunk use std::{ - collections::HashMap, fmt::{self, Display, Formatter}, mem::replace, num::{ParseFloatError, ParseIntError}, @@ -49,7 +48,7 @@ pub fn compile(source: &str) -> Result { pub struct Compiler<'src> { chunk: Chunk, lexer: Lexer<'src>, - local_declarations: HashMap, + local_declarations: Vec, current_token: Token<'src>, current_position: Span, @@ -79,7 +78,7 @@ impl<'src> Compiler<'src> { Ok(Compiler { chunk, lexer, - local_declarations: HashMap::new(), + local_declarations: Vec::new(), current_token, current_position, previous_token: Token::Eof, @@ -119,9 +118,7 @@ impl<'src> Compiler<'src> { .iter() .filter_map(|(instruction, _)| { if instruction.yields_value() { - let to_register = instruction.a(); - - Some(to_register + 1) + Some(instruction.a() + 1) } else { None } @@ -199,20 +196,21 @@ impl<'src> Compiler<'src> { let identifier = ConcreteValue::string(identifier); let identifier_index = self.chunk.push_or_get_constant(identifier); - let local_index = self.chunk.locals().len() as u8; + let local_index = self.chunk.locals().len(); self.chunk .locals_mut() .push(Local::new(identifier_index, r#type, is_mutable, scope)); self.local_declarations.insert(local_index, register_index); - (local_index, identifier_index) + (local_index as u8, identifier_index) } fn redeclare_local(&mut self, local_index: u8, register_index: u8) -> Result<(), CompileError> { - let local = self.get_local(local_index)?; + let local_index = local_index as usize; - if !self.current_scope.contains(&local.scope) { + if !self.local_declarations.len() <= local_index { + let local = self.get_local(local_index as u8)?; let identifier = self .chunk .constants() @@ -228,7 +226,7 @@ impl<'src> Compiler<'src> { }); } - self.local_declarations.insert(local_index, register_index); + self.local_declarations[local_index] = register_index; Ok(()) } @@ -696,19 +694,22 @@ impl<'src> Compiler<'src> { let local = self.get_local(local_index)?; is_mutable_local = local.is_mutable; - *self.local_declarations.get(&local_index).ok_or_else(|| { - let identifier = self - .chunk - .constants() - .get(local.identifier_index as usize) - .unwrap() - .to_string(); + *self + .local_declarations + .get(local_index as usize) + .ok_or_else(|| { + let identifier = self + .chunk + .constants() + .get(local.identifier_index as usize) + .unwrap() + .to_string(); - CompileError::UndeclaredVariable { - identifier, - position: self.current_position, - } - })? + CompileError::UndeclaredVariable { + identifier, + position: self.current_position, + } + })? } Operation::LoadConstant => { is_constant = true; @@ -785,7 +786,6 @@ impl<'src> Compiler<'src> { } else { self.next_register() }; - let mut new_instruction = match operator { Token::Plus => Instruction::add(register, left, right), Token::PlusEqual => Instruction::add(register, left, right), @@ -827,12 +827,7 @@ impl<'src> Compiler<'src> { self.emit_instruction(new_instruction, operator_position); - if let Token::PlusEqual - | Token::MinusEqual - | Token::StarEqual - | Token::SlashEqual - | Token::PercentEqual = operator - { + if is_assignment { self.previous_expression_type = Type::None; } else { self.previous_expression_type = self.get_instruction_type(&left_instruction)?; @@ -859,7 +854,6 @@ impl<'src> Compiler<'src> { })?; let (push_back_left, left_is_constant, _, left) = self.handle_binary_argument(&left_instruction)?; - let operator = self.current_token; let operator_position = self.current_position; let rule = ParseRule::from(&operator); @@ -1028,9 +1022,10 @@ impl<'src> Compiler<'src> { }); } - let register = self.next_register() - 1; - self.parse_expression()?; + + let register = self.local_declarations[local_index as usize]; + self.redeclare_local(local_index, register)?; self.emit_instruction( Instruction::set_local(register, local_index), diff --git a/dust-lang/src/formatter.rs b/dust-lang/src/formatter.rs index 5d0c751..7e4b0b5 100644 --- a/dust-lang/src/formatter.rs +++ b/dust-lang/src/formatter.rs @@ -8,7 +8,7 @@ use crate::{CompileError, DustError, LexError, Lexer, Token}; pub fn format( source: &str, colored: bool, - indent: usize, + _indent: usize, line_numbers: bool, ) -> Result { let lexer = Lexer::new(source); diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 0ec5410..eba0f94 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -1,12 +1,15 @@ //! An operation and its arguments for the Dust virtual machine. //! -//! Each instruction is a 32-bit unsigned integer that is divided into five fields: -//! - Bits 0-6: The operation code. -//! - Bit 7: A flag indicating whether the B argument is a constant. -//! - Bit 8: A flag indicating whether the C argument is a constant. -//! - Bits 9-16: The A argument, -//! - Bits 17-24: The B argument. -//! - Bits 25-32: The C argument. +//! Each instruction is a 64-bit unsigned integer that is divided into five fields: +//! - Bits 0-8: The operation code. +//! - Bit 9: Boolean flag indicating whether the B argument is a constant. +//! - Bit 10: Boolean flag indicating whether the C argument is a constant. +//! - Bit 11: Boolean flag indicating whether the A argument is a local. +//! - Bit 12: Boolean flag indicating whether the B argument is a local. +//! - Bit 13: Boolean flag indicating whether the C argument is a local. +//! - Bits 17-32: The A argument, +//! - Bits 33-48: The B argument. +//! - Bits 49-63: The C argument. //! //! Be careful when working with instructions directly. When modifying an instruction, be sure to //! account for the fact that setting the A, B, or C arguments to 0 will have no effect. It is @@ -16,571 +19,486 @@ use serde::{Deserialize, Serialize}; use crate::{Chunk, NativeFunction, Operation}; +pub struct InstructionBuilder { + operation: Operation, + a: u16, + b: u16, + c: u16, + b_is_constant: bool, + c_is_constant: bool, + a_is_local: bool, + b_is_local: bool, + c_is_local: bool, +} + +impl InstructionBuilder { + pub fn build(&self) -> Instruction { + Instruction( + (self.operation as u64) + | ((self.b_is_constant as u64) << 9) + | ((self.c_is_constant as u64) << 10) + | ((self.a_is_local as u64) << 11) + | ((self.b_is_local as u64) << 12) + | ((self.c_is_local as u64) << 13) + | ((self.a as u64) << 16) + | ((self.b as u64) << 32) + | ((self.c as u64) << 48), + ) + } + + pub fn set_a(&mut self, a: u16) -> &mut Self { + self.a = a; + self + } + + pub fn set_b(&mut self, b: u16) -> &mut Self { + self.b = b; + self + } + + pub fn set_b_to_boolean(&mut self, b: bool) -> &mut Self { + self.b = b as u16; + self + } + + pub fn set_c(&mut self, c: u16) -> &mut Self { + self.c = c; + self + } + + pub fn set_c_to_boolean(&mut self, c: bool) -> &mut Self { + self.c = c as u16; + self + } + + pub fn set_b_is_constant(&mut self, b_is_constant: bool) -> &mut Self { + self.b_is_constant = b_is_constant; + self + } + + pub fn set_c_is_constant(&mut self, c_is_constant: bool) -> &mut Self { + self.c_is_constant = c_is_constant; + self + } + + pub fn set_a_is_local(&mut self, a_is_local: bool) -> &mut Self { + self.a_is_local = a_is_local; + self + } + + pub fn set_b_is_local(&mut self, b_is_local: bool) -> &mut Self { + self.b_is_local = b_is_local; + self + } + + pub fn set_c_is_local(&mut self, c_is_local: bool) -> &mut Self { + self.c_is_local = c_is_local; + self + } +} + /// An operation and its arguments for the Dust virtual machine. /// /// See the [module-level documentation](index.html) for more information. #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct Instruction(u32); +pub struct Instruction(u64); impl Instruction { - pub fn with_operation(operation: Operation) -> Instruction { - Instruction(operation as u32) - } - - pub fn r#move(to_register: u8, from_register: u8) -> Instruction { - let mut instruction = Instruction(Operation::Move as u32); - - instruction.set_a(to_register); - instruction.set_b(from_register); - - instruction - } - - pub fn close(from_register: u8, to_register: u8) -> Instruction { - let mut instruction = Instruction(Operation::Close as u32); - - instruction.set_b(from_register); - instruction.set_c(to_register); - - instruction - } - - pub fn load_boolean(to_register: u8, value: bool, skip: bool) -> Instruction { - let mut instruction = Instruction(Operation::LoadBoolean as u32); - - instruction.set_a(to_register); - instruction.set_b_to_boolean(value); - instruction.set_c_to_boolean(skip); - - instruction - } - - pub fn load_constant(to_register: u8, constant_index: u8, skip: bool) -> Instruction { - let mut instruction = Instruction(Operation::LoadConstant as u32); - - instruction.set_a(to_register); - instruction.set_b(constant_index); - instruction.set_c_to_boolean(skip); - - instruction - } - - pub fn load_list(to_register: u8, start_register: u8) -> Instruction { - let mut instruction = Instruction(Operation::LoadList as u32); - - instruction.set_a(to_register); - instruction.set_b(start_register); - - instruction - } - - pub fn load_self(to_register: u8) -> Instruction { - let mut instruction = Instruction(Operation::LoadSelf as u32); - - instruction.set_a(to_register); - - instruction - } - - pub fn define_local(to_register: u8, local_index: u8, is_mutable: bool) -> Instruction { - let mut instruction = Instruction(Operation::DefineLocal as u32); - - instruction.set_a(to_register); - instruction.set_b(local_index); - instruction.set_c(if is_mutable { 1 } else { 0 }); - - instruction - } - - pub fn get_local(to_register: u8, local_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::GetLocal as u32); - - instruction.set_a(to_register); - instruction.set_b(local_index); - - instruction - } - - pub fn set_local(from_register: u8, local_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::SetLocal as u32); - - instruction.set_a(from_register); - instruction.set_b(local_index); - - instruction - } - - pub fn add(to_register: u8, left_index: u8, right_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::Add as u32); - - instruction.set_a(to_register); - instruction.set_b(left_index); - instruction.set_c(right_index); - - instruction - } - - pub fn subtract(to_register: u8, left_index: u8, right_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::Subtract as u32); - - instruction.set_a(to_register); - instruction.set_b(left_index); - instruction.set_c(right_index); - - instruction - } - - pub fn multiply(to_register: u8, left_index: u8, right_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::Multiply as u32); - - instruction.set_a(to_register); - instruction.set_b(left_index); - instruction.set_c(right_index); - - instruction - } - - pub fn divide(to_register: u8, left_index: u8, right_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::Divide as u32); - - instruction.set_a(to_register); - instruction.set_b(left_index); - instruction.set_c(right_index); - - instruction - } - - pub fn modulo(to_register: u8, left_index: u8, right_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::Modulo as u32); - - instruction.set_a(to_register); - instruction.set_b(left_index); - instruction.set_c(right_index); - - instruction - } - - pub fn test(test_register: u8, test_value: bool) -> Instruction { - let mut instruction = Instruction(Operation::Test as u32); - - instruction.set_b(test_register); - instruction.set_c_to_boolean(test_value); - - instruction - } - - pub fn test_set(to_register: u8, argument_index: u8, test_value: bool) -> Instruction { - let mut instruction = Instruction(Operation::TestSet as u32); - - instruction.set_a(to_register); - instruction.set_b(argument_index); - instruction.set_c_to_boolean(test_value); - - instruction - } - - pub fn equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::Equal as u32); - - instruction.set_a_to_boolean(comparison_boolean); - instruction.set_b(left_index); - instruction.set_c(right_index); - - instruction - } - - pub fn less(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::Less as u32); - - instruction.set_a_to_boolean(comparison_boolean); - instruction.set_b(left_index); - instruction.set_c(right_index); - - instruction - } - - pub fn less_equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::LessEqual as u32); - - instruction.set_a_to_boolean(comparison_boolean); - instruction.set_b(left_index); - instruction.set_c(right_index); - - instruction - } - - pub fn negate(to_register: u8, from_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::Negate as u32); - - instruction.set_a(to_register); - instruction.set_b(from_index); - - instruction - } - - pub fn not(to_register: u8, from_index: u8) -> Instruction { - let mut instruction = Instruction(Operation::Not as u32); - - instruction.set_a(to_register); - instruction.set_b(from_index); - - instruction - } - - pub fn jump(jump_offset: u8, is_positive: bool) -> Instruction { - let mut instruction = Instruction(Operation::Jump as u32); - - instruction.set_b(jump_offset); - instruction.set_c_to_boolean(is_positive); - - instruction - } - - pub fn call(to_register: u8, function_register: u8, argument_count: u8) -> Instruction { - let mut instruction = Instruction(Operation::Call as u32); - - instruction.set_a(to_register); - instruction.set_b(function_register); - instruction.set_c(argument_count); - - instruction - } - - pub fn call_native( - to_register: u8, - native_fn: NativeFunction, - argument_count: u8, - ) -> Instruction { - let mut instruction = Instruction(Operation::CallNative as u32); - let native_fn_byte = native_fn as u8; - - instruction.set_a(to_register); - instruction.set_b(native_fn_byte); - instruction.set_c(argument_count); - - instruction - } - - pub fn r#return(should_return_value: bool) -> Instruction { - let mut instruction = Instruction(Operation::Return as u32); - - instruction.set_b_to_boolean(should_return_value); - - instruction - } - - pub fn operation(&self) -> Operation { - Operation::from((self.0 & 0b0000_0000_0011_1111) as u8) - } - - pub fn set_operation(&mut self, operation: Operation) { - self.0 |= u8::from(operation) as u32; - } - - pub fn data(&self) -> (Operation, u8, u8, u8, bool, bool) { - ( - self.operation(), - self.a(), - self.b(), - self.c(), - self.b_is_constant(), - self.c_is_constant(), - ) - } - - pub fn a(&self) -> u8 { - (self.0 >> 24) as u8 - } - - pub fn b(&self) -> u8 { - (self.0 >> 16) as u8 - } - - pub fn c(&self) -> u8 { - (self.0 >> 8) as u8 - } - - pub fn a_as_boolean(&self) -> bool { - self.a() != 0 - } - - pub fn b_as_boolean(&self) -> bool { - self.b() != 0 - } - - pub fn c_as_boolean(&self) -> bool { - self.c() != 0 - } - - pub fn set_a_to_boolean(&mut self, boolean: bool) -> &mut Self { - self.set_a(if boolean { 1 } else { 0 }); - - self - } - - pub fn set_b_to_boolean(&mut self, boolean: bool) -> &mut Self { - self.set_b(if boolean { 1 } else { 0 }); - - self - } - - pub fn set_c_to_boolean(&mut self, boolean: bool) -> &mut Self { - self.set_c(if boolean { 1 } else { 0 }); - - self - } - - pub fn set_a(&mut self, to_register: u8) { - self.0 |= (to_register as u32) << 24; - } - - pub fn set_b(&mut self, argument: u8) { - self.0 |= (argument as u32) << 16; - } - - pub fn set_c(&mut self, argument: u8) { - self.0 |= (argument as u32) << 8; - } - - pub fn b_is_constant(&self) -> bool { - self.0 & 0b1000_0000 != 0 - } - - pub fn c_is_constant(&self) -> bool { - self.0 & 0b0100_0000 != 0 - } - - pub fn set_b_is_constant(&mut self) -> &mut Self { - self.0 |= 0b1000_0000; - - self - } - - pub fn set_c_is_constant(&mut self) -> &mut Self { - self.0 |= 0b0100_0000; - - self - } - - pub fn returns_or_panics(&self) -> bool { - match self.operation() { - Operation::Return => true, - Operation::CallNative => { - let native_function = NativeFunction::from(self.b()); - - matches!(native_function, NativeFunction::Panic) - } - _ => false, + pub fn builder(operation: Operation) -> InstructionBuilder { + InstructionBuilder { + operation, + a: 0, + b: 0, + c: 0, + b_is_constant: false, + c_is_constant: false, + a_is_local: false, + b_is_local: false, + c_is_local: false, } } - pub fn yields_value(&self) -> bool { - matches!( - self.operation(), - Operation::Add - | Operation::Call - | Operation::CallNative - | Operation::Divide - | Operation::GetLocal - | Operation::LoadBoolean - | Operation::LoadConstant - | Operation::LoadList - | Operation::LoadSelf - | Operation::Modulo - | Operation::Multiply - | Operation::Negate - | Operation::Not - | Operation::Subtract - | Operation::TestSet - ) + pub fn r#move(to_register: u16, from_register: u16) -> Instruction { + Instruction::builder(Operation::Move) + .set_a(to_register) + .set_b(from_register) + .build() + } + + pub fn close(from_register: u16, to_register: u16) -> Instruction { + Instruction::builder(Operation::Close) + .set_b(from_register) + .set_c(to_register) + .build() + } + + pub fn load_boolean(to_register: u16, value: bool, skip: bool) -> Instruction { + Instruction::builder(Operation::LoadBoolean) + .set_a(to_register) + .set_b_to_boolean(value) + .set_c_to_boolean(skip) + .build() + } + + pub fn load_constant(to_register: u16, constant_index: u16, skip: bool) -> Instruction { + Instruction::builder(Operation::LoadConstant) + .set_a(to_register) + .set_b(constant_index) + .set_c_to_boolean(skip) + .build() + } + + pub fn load_list(to_register: u16, start_register: u16) -> Instruction { + Instruction::builder(Operation::LoadList) + .set_a(to_register) + .set_b(start_register) + .build() + } + + pub fn load_self(to_register: u16) -> Instruction { + Instruction::builder(Operation::LoadSelf) + .set_a(to_register) + .build() + } + + pub fn define_local(to_register: u16, local_index: u16, is_mutable: bool) -> Instruction { + Instruction::builder(Operation::DefineLocal) + .set_a(to_register as u16) + .set_b(local_index as u16) + .set_c_to_boolean(is_mutable) + .build() + } + + pub fn get_local(to_register: u16, local_index: u16) -> Instruction { + Instruction::builder(Operation::GetLocal) + .set_a(to_register) + .set_b(local_index) + .build() + } + + pub fn set_local(from_register: u16, local_index: u16) -> Instruction { + Instruction::builder(Operation::SetLocal) + .set_a(from_register) + .set_b(local_index) + .build() + } + + // pub fn add(to_register: u16, left_index: u16, right_index: u16) -> Instruction { + // Instruction::builder(Operation::Add) + // .set_a(to_register) + // .set_b(left_index) + // .set_c(right_index) + // .build() + // } + + // pub fn subtract(to_register: u16, left_index: u16, right_index: u16) -> Instruction { + // let mut instruction = Instruction(Operation::Subtract as u32); + + // instruction.set_a(to_register); + // instruction.set_b(left_index); + // instruction.set_c(right_index); + + // instruction + // } + + // pub fn multiply(to_register: u16, left_index: u16, right_index: u16) -> Instruction { + // let mut instruction = Instruction(Operation::Multiply as u32); + + // instruction.set_a(to_register); + // instruction.set_b(left_index); + // instruction.set_c(right_index); + + // instruction + // } + + // pub fn divide(to_register: u16, left_index: u16, right_index: u16) -> Instruction { + // let mut instruction = Instruction(Operation::Divide as u32); + + // instruction.set_a(to_register); + // instruction.set_b(left_index); + // instruction.set_c(right_index); + + // instruction + // } + + // pub fn modulo(to_register: u16, left_index: u16, right_index: u16) -> Instruction { + // let mut instruction = Instruction(Operation::Modulo as u32); + + // instruction.set_a(to_register); + // instruction.set_b(left_index); + // instruction.set_c(right_index); + + // instruction + // } + + // pub fn test(test_register: u16, test_value: bool) -> Instruction { + // Instruction::builder(Operation::Test) + // .set_b(test_register) + // .set_c_to_boolean(test_value) + // .build() + // } + + // pub fn test_set(to_register: u16, argument_index: u16, test_value: bool) -> Instruction { + // Instruction::builder(Operation::TestSet) + // .set_a(to_register) + // .set_b(argument_index) + // .set_c_to_boolean(test_value) + // .build() + // } + + // pub fn equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction { + // let mut instruction = Instruction(Operation::Equal as u32); + + // instruction.set_a_to_boolean(comparison_boolean); + // instruction.set_b(left_index); + // instruction.set_c(right_index); + + // instruction + // } + + // pub fn less(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction { + // let mut instruction = Instruction(Operation::Less as u32); + + // instruction.set_a_to_boolean(comparison_boolean); + // instruction.set_b(left_index); + // instruction.set_c(right_index); + + // instruction + // } + + // pub fn less_equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction { + // let mut instruction = Instruction(Operation::LessEqual as u32); + + // instruction.set_a_to_boolean(comparison_boolean); + // instruction.set_b(left_index); + // instruction.set_c(right_index); + + // instruction + // } + + // pub fn negate(to_register: u16, from_index: u16) -> Instruction { + // let mut instruction = Instruction(Operation::Negate as u32); + + // instruction.set_a(to_register); + // instruction.set_b(from_index); + + // instruction + // } + + // pub fn not(to_register: u16, from_index: u16) -> Instruction { + // let mut instruction = Instruction(Operation::Not as u32); + + // instruction.set_a(to_register); + // instruction.set_b(from_index); + + // instruction + // } + + pub fn jump(jump_offset: u16, is_positive: bool) -> Instruction { + Instruction::builder(Operation::Jump) + .set_b(jump_offset) + .set_c_to_boolean(is_positive) + .build() + } + + pub fn call(to_register: u16, function_register: u16, argument_count: u16) -> Instruction { + Instruction::builder(Operation::Call) + .set_a(to_register) + .set_b(function_register) + .set_c(argument_count) + .build() + } + + pub fn call_native( + to_register: u16, + native_fn: NativeFunction, + argument_count: u16, + ) -> Instruction { + Instruction::builder(Operation::CallNative) + .set_a(to_register) + .set_b(native_fn as u16) + .set_c(argument_count) + .build() + } + + pub fn r#return(should_return_value: bool) -> Instruction { + Instruction::builder(Operation::Return) + .set_b_to_boolean(should_return_value) + .build() } pub fn disassembly_info(&self, chunk: &Chunk) -> String { + let InstructionBuilder { + operation, + a, + b, + c, + b_is_constant, + c_is_constant, + a_is_local, + b_is_local, + c_is_local, + } = InstructionBuilder::from(self); let format_arguments = || { - let first_argument = if self.b_is_constant() { - format!("C{}", self.b()) + let first_argument = if b_is_constant { + format!("C{}", b) } else { - format!("R{}", self.b()) + format!("R{}", b) }; - let second_argument = if self.c_is_constant() { - format!("C{}", self.c()) + let second_argument = if c_is_constant { + format!("C{}", c) } else { - format!("R{}", self.c()) + format!("R{}", c) }; (first_argument, second_argument) }; - match self.operation() { - Operation::Move => format!("R{} = R{}", self.a(), self.b()), + match operation { + Operation::Move => format!("R{a} = R{b}"), Operation::Close => { - let from_register = self.b(); - let to_register = self.c().saturating_sub(1); - - format!("R{from_register}..=R{to_register}") + format!("R{b}..R{c}") } Operation::LoadBoolean => { - let to_register = self.a(); - let boolean = self.b_as_boolean(); - let jump = self.c_as_boolean(); + let boolean = b != 0; + let jump = c != 0; if jump { - format!("R{to_register} = {boolean} && JUMP +1") + format!("R{a} = {boolean} && JUMP +1") } else { - format!("R{to_register} = {boolean}") + format!("R{a} {boolean}") } } Operation::LoadConstant => { - let register_index = self.a(); - let constant_index = self.b(); - let jump = self.c_as_boolean(); + let jump = c != 0; if jump { - format!("R{register_index} = C{constant_index} JUMP +1") + format!("R{a} = C{b} JUMP +1") } else { - format!("R{register_index} = C{constant_index}") + format!("R{a} = C{b}") } } Operation::LoadList => { - let to_register = self.a(); - let first_index = self.b(); - let last_index = self.c(); - - format!("R{to_register} = [R{first_index}..=R{last_index}]",) + format!("R{a} = [R{b}..=R{c}]",) } Operation::LoadSelf => { - let to_register = self.a(); let name = chunk .name() .map(|idenifier| idenifier.as_str()) .unwrap_or("self"); - format!("R{to_register} = {name}") + format!("R{a} = {name}") } Operation::DefineLocal => { - let to_register = self.a(); - let local_index = self.b(); - - format!("L{local_index} = R{to_register}") + format!("L{b} = R{a}") } Operation::GetLocal => { - let local_index = self.b(); - - format!("R{} = L{}", self.a(), local_index) + format!("R{a} = L{b}") } Operation::SetLocal => { - let local_index = self.b(); - let register = self.a(); - - format!("L{local_index} = R{register}") + format!("L{b} = R{a}") } Operation::Add => { - let to_register = self.a(); let (first_argument, second_argument) = format_arguments(); - format!("R{to_register} = {first_argument} + {second_argument}",) + format!("R{a} = {first_argument} + {second_argument}",) } Operation::Subtract => { - let to_register = self.a(); let (first_argument, second_argument) = format_arguments(); - format!("R{to_register} = {first_argument} - {second_argument}",) + format!("R{a} = {first_argument} - {second_argument}",) } Operation::Multiply => { - let to_register = self.a(); let (first_argument, second_argument) = format_arguments(); - format!("R{to_register} = {first_argument} * {second_argument}",) + format!("R{a} = {first_argument} * {second_argument}",) } Operation::Divide => { - let to_register = self.a(); let (first_argument, second_argument) = format_arguments(); - format!("R{to_register} = {first_argument} / {second_argument}",) + format!("R{a} = {first_argument} / {second_argument}",) } Operation::Modulo => { - let to_register = self.a(); let (first_argument, second_argument) = format_arguments(); - format!("R{to_register} = {first_argument} % {second_argument}",) + format!("R{a} = {first_argument} % {second_argument}",) } Operation::Test => { - let test_register = if self.b_is_constant() { - format!("C{}", self.b()) + let test_register = if b_is_constant { + format!("C{b}") } else { - format!("R{}", self.b()) + format!("R{b}") }; - let test_value = self.c_as_boolean(); + let test_value = c != 0; let bang = if test_value { "" } else { "!" }; format!("if {bang}{test_register} {{ JUMP +1 }}",) } Operation::TestSet => { - let to_register = self.a(); - let test_register = self.b(); - let test_value = self.c_as_boolean(); + let test_register = if b_is_constant { + format!("C{b}") + } else { + format!("R{b}") + }; + let test_value = c != 0; let bang = if test_value { "" } else { "!" }; - format!("if {bang}R{test_register} {{ JUMP +1 }} else {{ R{to_register} = R{test_register} }}") + format!( + "if {bang}R{test_register} {{ JUMP +1 }} else {{ R{a} = R{test_register} }}" + ) } Operation::Equal => { - let comparison_symbol = if self.a_as_boolean() { "==" } else { "!=" }; - + let comparison_symbol = if a != 0 { "==" } else { "!=" }; let (first_argument, second_argument) = format_arguments(); format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}") } Operation::Less => { - let comparison_symbol = if self.a_as_boolean() { "<" } else { ">=" }; + let comparison_symbol = if a != 0 { "<" } else { ">=" }; let (first_argument, second_argument) = format_arguments(); format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}") } Operation::LessEqual => { - let comparison_symbol = if self.a_as_boolean() { "<=" } else { ">" }; + let comparison_symbol = if a != 0 { "<=" } else { ">" }; let (first_argument, second_argument) = format_arguments(); format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}") } Operation::Negate => { - let to_register = self.a(); - let argument = if self.b_is_constant() { - format!("C{}", self.b()) + let argument = if b_is_constant { + format!("C{b}") } else { - format!("R{}", self.b()) + format!("R{b}") }; - format!("R{to_register} = -{argument}") + format!("R{a} = -{argument}") } Operation::Not => { - let to_register = self.a(); - let argument = if self.b_is_constant() { - format!("C{}", self.b()) + let argument = if b_is_constant { + format!("C{b}") } else { - format!("R{}", self.b()) + format!("R{b}") }; - format!("R{to_register} = !{argument}") + format!("R{a} = !{argument}") } Operation::Jump => { - let jump_distance = self.b(); - let is_positive = self.c_as_boolean(); + let is_positive = c != 0; if is_positive { - format!("JUMP +{jump_distance}") + format!("JUMP +{b}") } else { - format!("JUMP -{jump_distance}") + format!("JUMP -{b}") } } Operation::Call => { - let to_register = self.a(); - let function_register = self.b(); - let argument_count = self.c(); + let argument_count = c; - let mut output = format!("R{to_register} = R{function_register}("); + let mut output = format!("R{a} = R{b}("); if argument_count != 0 { - let first_argument = function_register + 1; + let first_argument = b + 1; for (index, register) in (first_argument..first_argument + argument_count).enumerate() @@ -598,18 +516,17 @@ impl Instruction { output } Operation::CallNative => { - let to_register = self.a(); - let native_function = NativeFunction::from(self.b()); - let argument_count = self.c(); + let native_function = NativeFunction::from(b); + let argument_count = c; let mut output = String::new(); let native_function_name = native_function.as_str(); - output.push_str(&format!("R{} = {}(", to_register, native_function_name)); + output.push_str(&format!("R{a} = {}(", native_function_name)); if argument_count != 0 { - let first_argument = to_register.saturating_sub(argument_count); + let first_argument = a.saturating_sub(argument_count); - for register in first_argument..to_register { + for register in first_argument..a { if register != first_argument { output.push_str(", "); } @@ -623,10 +540,10 @@ impl Instruction { output } Operation::Return => { - let should_return_value = self.b_as_boolean(); + let should_return_value = b != 0; if should_return_value { - "->".to_string() + "RETURN".to_string() } else { "".to_string() } @@ -635,12 +552,6 @@ impl Instruction { } } -impl From<&Instruction> for u32 { - fn from(instruction: &Instruction) -> Self { - instruction.0 - } -} - #[cfg(test)] mod tests { use super::*; @@ -719,7 +630,7 @@ mod tests { assert_eq!(instruction.operation(), Operation::DefineLocal); assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 1); - assert_eq!(instruction.c(), true as u8); + assert_eq!(instruction.c(), true as u16); assert!(instruction.b_is_constant()); } diff --git a/dust-lang/src/native_function/mod.rs b/dust-lang/src/native_function/mod.rs index f2058f7..287d2de 100644 --- a/dust-lang/src/native_function/mod.rs +++ b/dust-lang/src/native_function/mod.rs @@ -74,8 +74,8 @@ macro_rules! define_native_function { } } - impl From for NativeFunction { - fn from(byte: u8) -> Self { + impl From for NativeFunction { + fn from(byte: u16) -> Self { match byte { $( $byte => NativeFunction::$name, @@ -126,7 +126,7 @@ define_native_function! { // (AssertNotEqual, 2_u8, "assert_not_equal", false), ( Panic, - 3_u8, + 3, "panic", FunctionType { type_parameters: None, @@ -143,7 +143,7 @@ define_native_function! { // (ToInteger, 7_u8, "to_integer", true), ( ToString, - 8_u8, + 8, "to_string", FunctionType { type_parameters: None, @@ -204,7 +204,7 @@ define_native_function! { // (ReadFile, 49_u8, "read_file", true), ( ReadLine, - 50_u8, + 50, "read_line", FunctionType { type_parameters: None, @@ -220,7 +220,7 @@ define_native_function! { // (PrependFile, 54_u8, "prepend_file", false), ( Write, - 55_u8, + 55, "write", FunctionType { type_parameters: None, @@ -232,7 +232,7 @@ define_native_function! { // (WriteFile, 56_u8, "write_file", false), ( WriteLine, - 57_u8, + 57, "write_line", FunctionType { type_parameters: None, diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index b67e7ba..cab14e3 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,7 +1,6 @@ //! Virtual machine and errors use std::{ cmp::Ordering, - collections::HashMap, fmt::{self, Display, Formatter}, io, }; @@ -34,7 +33,7 @@ pub struct Vm<'a> { chunk: &'a Chunk, stack: Vec, parent: Option<&'a Vm<'a>>, - local_definitions: HashMap, + local_definitions: Vec>, ip: usize, last_assigned_register: Option, @@ -49,7 +48,7 @@ impl<'a> Vm<'a> { chunk, stack: Vec::new(), parent, - local_definitions: HashMap::new(), + local_definitions: vec![None; chunk.locals().len()], ip: 0, last_assigned_register: None, current_position: Span(0, 0), @@ -158,12 +157,12 @@ impl<'a> Vm<'a> { let from_register = instruction.a(); let to_local = instruction.b(); - self.define_local(to_local, from_register)?; + self.local_definitions[to_local as usize] = Some(from_register); } Operation::GetLocal => { let to_register = instruction.a(); let local_index = instruction.b(); - let local_register = self.local_definitions.get(&local_index).copied().ok_or( + let local_register = self.local_definitions[local_index as usize].ok_or( VmError::UndefinedLocal { local_index, position: self.current_position, @@ -176,16 +175,8 @@ impl<'a> Vm<'a> { Operation::SetLocal => { let from_register = instruction.a(); let to_local = instruction.b(); - let local_register = self.local_definitions.get(&to_local).copied().ok_or( - VmError::UndefinedLocal { - local_index: to_local, - position: self.current_position, - }, - )?; - let register = Register::Pointer(Pointer::Stack(from_register)); - self.define_local(to_local, from_register)?; - self.set_register(local_register, register)?; + self.local_definitions[to_local as usize] = Some(from_register); } Operation::Add => { let to_register = instruction.a(); @@ -640,14 +631,6 @@ impl<'a> Vm<'a> { Ok(instruction) } - - fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> { - log::debug!("Define local L{}", local_index); - - self.local_definitions.insert(local_index, register_index); - - Ok(()) - } } #[derive(Clone, Debug, PartialEq)] diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs index 34469db..4c9924c 100644 --- a/dust-lang/tests/functions.rs +++ b/dust-lang/tests/functions.rs @@ -23,8 +23,8 @@ fn function() { ], vec![ConcreteValue::string("a"), ConcreteValue::string("b"),], vec![ - Local::new(0, Type::Integer, false, Scope::default(), 0), - Local::new(1, Type::Integer, false, Scope::default(), 1) + Local::new(0, Type::Integer, false, Scope::default()), + Local::new(1, Type::Integer, false, Scope::default()) ] )))) ); @@ -64,8 +64,8 @@ fn function_call() { ], vec![ConcreteValue::string("a"), ConcreteValue::string("b"),], vec![ - Local::new(0, Type::Integer, false, Scope::default(), 0), - Local::new(1, Type::Integer, false, Scope::default(), 1) + Local::new(0, Type::Integer, false, Scope::default()), + Local::new(1, Type::Integer, false, Scope::default()) ] )), ConcreteValue::Integer(1), @@ -93,6 +93,7 @@ fn function_declaration() { }, vec![ (Instruction::load_constant(0, 0, false), Span(0, 40)), + (Instruction::define_local(0, 0, false), Span(3, 6)), (Instruction::r#return(false), Span(40, 40)) ], vec![ @@ -109,8 +110,8 @@ fn function_declaration() { ], vec![ConcreteValue::string("a"), ConcreteValue::string("b")], vec![ - Local::new(0, Type::Integer, false, Scope::default(), 0), - Local::new(1, Type::Integer, false, Scope::default(), 1) + Local::new(0, Type::Integer, false, Scope::default()), + Local::new(1, Type::Integer, false, Scope::default()) ] )), ConcreteValue::string("add"), @@ -124,7 +125,6 @@ fn function_declaration() { }), false, Scope::default(), - 0 ),], )), ); diff --git a/dust-lang/tests/logic.rs b/dust-lang/tests/logic.rs index a4e3f27..99ded80 100644 --- a/dust-lang/tests/logic.rs +++ b/dust-lang/tests/logic.rs @@ -71,7 +71,9 @@ fn variable_and() { }, vec![ (Instruction::load_boolean(0, true, false), Span(8, 12)), + (Instruction::define_local(0, 0, false), Span(4, 5)), (Instruction::load_boolean(1, false, false), Span(22, 27)), + (Instruction::define_local(1, 1, false), Span(18, 19)), (Instruction::get_local(2, 0), Span(29, 30)), (Instruction::test(2, true), Span(31, 33)), (Instruction::jump(1, true), Span(31, 33)), @@ -80,8 +82,8 @@ fn variable_and() { ], vec![ConcreteValue::string("a"), ConcreteValue::string("b"),], vec![ - Local::new(0, Type::Boolean, false, Scope::default(), 0), - Local::new(1, Type::Boolean, false, Scope::default(), 1), + Local::new(0, Type::Boolean, false, Scope::default()), + Local::new(1, Type::Boolean, false, Scope::default()), ] )) ); diff --git a/dust-lang/tests/loops.rs b/dust-lang/tests/loops.rs index 7635586..ee6f979 100644 --- a/dust-lang/tests/loops.rs +++ b/dust-lang/tests/loops.rs @@ -15,6 +15,7 @@ fn r#while() { }, vec![ (Instruction::load_constant(0, 0, false), Span(12, 13)), + (Instruction::define_local(0, 0, true), Span(8, 9)), ( *Instruction::less(true, 0, 2).set_c_is_constant(), Span(23, 24) @@ -31,7 +32,7 @@ fn r#while() { ConcreteValue::Integer(5), ConcreteValue::Integer(1), ], - vec![Local::new(1, Type::Integer, true, Scope::default(), 0)] + vec![Local::new(1, Type::Integer, true, Scope::default())] )), ); diff --git a/dust-lang/tests/math.rs b/dust-lang/tests/math.rs index eabdb47..ad201a6 100644 --- a/dust-lang/tests/math.rs +++ b/dust-lang/tests/math.rs @@ -45,6 +45,7 @@ fn add_assign() { }, vec![ (Instruction::load_constant(0, 0, false), Span(12, 13)), + (Instruction::define_local(0, 0, true), Span(8, 9)), (*Instruction::add(0, 0, 2).set_c_is_constant(), Span(17, 19)), (Instruction::get_local(1, 0), Span(23, 24)), (Instruction::r#return(true), Span(24, 24)) @@ -54,7 +55,7 @@ fn add_assign() { ConcreteValue::string("a"), ConcreteValue::Integer(2) ], - vec![Local::new(1, Type::Integer, true, Scope::default(), 0)] + vec![Local::new(1, Type::Integer, true, Scope::default())] )) ); @@ -138,6 +139,7 @@ fn divide_assign() { }, vec![ (Instruction::load_constant(0, 0, false), Span(12, 13)), + (Instruction::define_local(0, 0, true), Span(8, 9)), ( *Instruction::divide(0, 0, 0).set_c_is_constant(), Span(17, 19) @@ -146,7 +148,7 @@ fn divide_assign() { (Instruction::r#return(true), Span(24, 24)) ], vec![ConcreteValue::Integer(2), ConcreteValue::string("a")], - vec![Local::new(1, Type::Integer, true, Scope::default(), 0)] + vec![Local::new(1, Type::Integer, true, Scope::default())] )) ); @@ -261,6 +263,7 @@ fn multiply_assign() { }, vec![ (Instruction::load_constant(0, 0, false), Span(12, 13)), + (Instruction::define_local(0, 0, true), Span(8, 9)), ( *Instruction::multiply(0, 0, 2).set_c_is_constant(), Span(17, 19) @@ -273,7 +276,7 @@ fn multiply_assign() { ConcreteValue::string("a"), ConcreteValue::Integer(3) ], - vec![Local::new(1, Type::Integer, true, Scope::default(), 0)] + vec![Local::new(1, Type::Integer, true, Scope::default())] )) ); @@ -341,6 +344,7 @@ fn subtract_assign() { }, vec![ (Instruction::load_constant(0, 0, false), Span(12, 14)), + (Instruction::define_local(0, 0, true), Span(8, 9)), ( *Instruction::subtract(0, 0, 2).set_c_is_constant(), Span(18, 20) @@ -353,7 +357,7 @@ fn subtract_assign() { ConcreteValue::string("x"), ConcreteValue::Integer(2) ], - vec![Local::new(1, Type::Integer, true, Scope::default(), 0)] + vec![Local::new(1, Type::Integer, true, Scope::default())] )), ); diff --git a/dust-lang/tests/scopes.rs b/dust-lang/tests/scopes.rs index 69f66dd..9a3d011 100644 --- a/dust-lang/tests/scopes.rs +++ b/dust-lang/tests/scopes.rs @@ -60,11 +60,11 @@ fn block_scope() { ConcreteValue::string("e"), ], vec![ - Local::new(1, Type::Integer, false, Scope::new(0, 0), 0), - Local::new(3, Type::Integer, false, Scope::new(1, 1), 0), - Local::new(5, Type::Integer, false, Scope::new(2, 2), 0), - Local::new(7, Type::Integer, false, Scope::new(1, 1), 0), - Local::new(8, Type::Integer, false, Scope::new(0, 0), 0), + Local::new(1, Type::Integer, false, Scope::new(0, 0)), + Local::new(3, Type::Integer, false, Scope::new(1, 1)), + Local::new(5, Type::Integer, false, Scope::new(2, 2)), + Local::new(7, Type::Integer, false, Scope::new(1, 1)), + Local::new(8, Type::Integer, false, Scope::new(0, 0)), ] )), ); @@ -136,15 +136,15 @@ fn multiple_block_scopes() { ConcreteValue::string("e"), ], vec![ - Local::new(1, Type::Integer, false, Scope::new(0, 0), 0), - Local::new(3, Type::Integer, false, Scope::new(1, 1), 0), - Local::new(5, Type::Integer, false, Scope::new(2, 2), 0), - Local::new(6, Type::Integer, false, Scope::new(1, 1), 0), - Local::new(7, Type::Integer, false, Scope::new(0, 0), 0), - Local::new(3, Type::Integer, false, Scope::new(1, 3), 0), - Local::new(5, Type::Integer, false, Scope::new(2, 4), 0), - Local::new(6, Type::Integer, false, Scope::new(1, 3), 0), - Local::new(8, Type::Integer, false, Scope::new(0, 0), 0), + Local::new(1, Type::Integer, false, Scope::new(0, 0)), + Local::new(3, Type::Integer, false, Scope::new(1, 1)), + Local::new(5, Type::Integer, false, Scope::new(2, 2)), + Local::new(6, Type::Integer, false, Scope::new(1, 1)), + Local::new(7, Type::Integer, false, Scope::new(0, 0)), + Local::new(3, Type::Integer, false, Scope::new(1, 3)), + Local::new(5, Type::Integer, false, Scope::new(2, 4)), + Local::new(6, Type::Integer, false, Scope::new(1, 3)), + Local::new(8, Type::Integer, false, Scope::new(0, 0)), ] )), ); diff --git a/dust-lang/tests/variables.rs b/dust-lang/tests/variables.rs index 520f38b..cd91ad3 100644 --- a/dust-lang/tests/variables.rs +++ b/dust-lang/tests/variables.rs @@ -15,10 +15,11 @@ fn define_local() { }, vec![ (Instruction::load_constant(0, 0, false), Span(8, 10)), + (Instruction::define_local(0, 0, false), Span(4, 5)), (Instruction::r#return(false), Span(11, 11)) ], vec![ConcreteValue::Integer(42), ConcreteValue::string("x")], - vec![Local::new(1, Type::Integer, false, Scope::default(), 0)] + vec![Local::new(1, Type::Integer, false, Scope::default())] )), ); @@ -57,8 +58,9 @@ fn set_local() { }, vec![ (Instruction::load_constant(0, 0, false), Span(12, 14)), + (Instruction::define_local(0, 0, true), Span(8, 9)), (Instruction::load_constant(1, 2, false), Span(20, 22)), - (Instruction::set_local(0, 0), Span(16, 17)), + (Instruction::set_local(1, 0), Span(16, 17)), (Instruction::get_local(2, 0), Span(24, 25)), (Instruction::r#return(true), Span(25, 25)), ], @@ -67,7 +69,7 @@ fn set_local() { ConcreteValue::string("x"), ConcreteValue::Integer(42) ], - vec![Local::new(1, Type::Integer, true, Scope::default(), 0)] + vec![Local::new(1, Type::Integer, true, Scope::default())] )), );