diff --git a/dust-cli/src/main.rs b/dust-cli/src/main.rs index 85027df..cdd2978 100644 --- a/dust-cli/src/main.rs +++ b/dust-cli/src/main.rs @@ -1,131 +1,38 @@ -use std::{ - fmt::{self, Display, Formatter}, - fs::{File, OpenOptions}, - io::{stdin, stdout, Read, Write}, - time::Instant, -}; +use std::fs::read_to_string; +use std::io::Write; -use clap::{Args, Parser, Subcommand, ValueEnum}; +use clap::Parser; use colored::Colorize; -use dust_lang::{compile, display_token_list, format, lex, vm::run_chunk}; -use env_logger::Target; +use dust_lang::{compile, run}; use log::{Level, LevelFilter}; #[derive(Parser)] -#[command(version, author, about)] struct Cli { - #[command(flatten)] - input: Input, - - #[command(flatten)] - output: Output, - - /// Log level: INFO, DEBUG, TRACE, WARN or ERROR. This overrides the DUST_LOG environment variable. - #[arg(short, long, value_enum, value_name = "LEVEL", group = "log")] - log_level: Option, - - /// Path to a file for log output - #[arg(long, group = "log")] - log_file: Option, - - /// Which CLI feature to use, defaults to "run" - #[command(subcommand)] - command: Option, -} - -#[derive(Args, Clone)] -#[group(required = true, multiple = true)] -struct Input { - /// Path to a Dust source file. If this and `input` are not provided, input will be read from - /// stdin. - source_file: Option, - - /// Dust source code to compile. If this and `source_file` are not provided, input will be read - /// from stdin. - #[arg(short, long, conflicts_with = "source_file")] - input: Option>, - - /// The format of the input - #[arg(short, long, value_enum, default_value = "source")] - read_format: IoFormat, -} - -#[derive(Args, Clone)] -#[group(required = false, multiple = false)] -struct Output { - /// Path to a file for output. If not provided, output will be written to stdout. + /// Source code sent via command line #[arg(short, long)] - output_file: Option, + command: Option, - /// The format of the output - #[arg(short, long, value_enum, default_value = "postcard")] - write_format: IoFormat, + /// Whether to output the disassembled chunk + #[arg(short, long)] + parse: bool, + + /// Whether to style the disassembled chunk + #[arg(long)] + style_disassembly: Option, + + /// Log level + #[arg(short, long)] + log: Option, + + /// Path to a source code file + path: Option, } -#[derive(Subcommand, Clone, Copy)] -enum Command { - /// Compile Dust to an intermediate format - Compile, - /// Compile and display the disassembled chunk - Disassemble { - /// Whether to style the output - #[arg(short, long, default_value = "true")] - style: bool, - }, - /// Format and display the source code - Format { - /// Whether to color the output - #[arg(short, long, default_value = "true")] - color: bool, - - /// Number of spaces per indent level - #[arg(short, long, default_value = "4")] - indent: usize, - - /// Whether to include line numbers in the output - #[arg(short, long, default_value = "true")] - line_numbers: bool, - }, - /// Compile and run the Dust code - Run, - /// Lex and display the token list - Tokenize { - /// Whether to style the output - #[arg(short, long, default_value = "true")] - style: bool, - }, -} - -#[derive(ValueEnum, Clone, Copy)] -enum IoFormat { - Json, - Postcard, - Source, -} - -impl Display for IoFormat { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - IoFormat::Json => write!(f, "json"), - IoFormat::Postcard => write!(f, "postcard"), - IoFormat::Source => write!(f, "source"), - } - } -} - -fn main() -> Result<(), String> { - let Cli { - input, - output, - log_level, - log_file, - command, - } = Cli::parse(); - let command = command.unwrap_or(Command::Run); - let start = Instant::now(); +fn main() { + let args = Cli::parse(); let mut logger = env_logger::builder(); - logger.format(move |buf, record| { + logger.format(|buf, record| { let level_display = match record.level() { Level::Info => "INFO".bold().white(), Level::Debug => "DEBUG".bold().blue(), @@ -138,197 +45,53 @@ fn main() -> Result<(), String> { .map(|path| path.split("::").last().unwrap_or(path)) .unwrap_or("unknown") .dimmed(); - let elapsed = start.elapsed().as_secs_f32(); - let elapsed_display = format!("T+{elapsed:0.09}").dimmed(); - let display = format!( - "[{elapsed_display}] {level_display:5} {module:^6} {args}", - args = record.args() - ); + let display = format!("{level_display:5} {module:^6} {args}", args = record.args()); writeln!(buf, "{display}") }); - if let Some(path) = log_file { - let log_file = OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(path) - .expect("Failed to open log file"); - - logger.target(Target::Pipe(Box::new(log_file))); - } - - if let Some(level) = log_level { + if let Some(level) = args.log { logger.filter_level(level).init(); } else { logger.parse_env("DUST_LOG").init(); } - let input_bytes = if let Some(source_input) = input.input { - source_input - } else if let Some(path) = &input.source_file { - let mut source_file = File::open(path).expect("Failed to open source file"); - let file_length = source_file - .metadata() - .expect("Failed to read file metadata") - .len(); - let mut buffer = Vec::with_capacity(file_length as usize); - - source_file - .read_to_end(&mut buffer) - .expect("Failed to read source file"); - - buffer + let source = if let Some(path) = &args.path { + &read_to_string(path).expect("Failed to read file") + } else if let Some(command) = &args.command { + command } else { - let mut buffer = Vec::new(); + eprintln!("No input provided"); - stdin() - .read_to_end(&mut buffer) - .expect("Failed to read from stdin"); - - buffer + return; }; - match command { - Command::Format { - color, - indent, - line_numbers, - } => { - let source = String::from_utf8(input_bytes).expect("Failed to parse input as UTF-8"); - let formatted = match format(&source, color, indent, line_numbers) { - Ok(formatted) => formatted, - Err(dust_error) => { - let report = dust_error.report(); + if args.parse { + let style = args.style_disassembly.unwrap_or(true); - return Err(report); - } - }; + log::info!("Parsing source"); - if let Some(path) = output.output_file { - let mut file = OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(path) - .expect("Failed to open output file"); + match compile(source) { + Ok(chunk) => { + let disassembly = chunk.disassembler().style(style).disassemble(); - file.write_all(formatted.as_bytes()) - .expect("Failed to write to output file"); - } else { - println!("{}", formatted); + println!("{}", disassembly); } - - return Ok(()); - } - Command::Tokenize { style } => { - let source = String::from_utf8(input_bytes).expect("Failed to parse input as UTF-8"); - let tokens = match lex(&source) { - Ok(tokens) => tokens, - Err(dust_error) => { - let report = dust_error.report(); - - return Err(report); - } - }; - - if let Some(path) = output.output_file { - let mut file = OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(path) - .expect("Failed to open output file"); - - display_token_list(&tokens, style, &mut file) - } else { - display_token_list(&tokens, style, &mut stdout()) + Err(error) => { + eprintln!("{}", error.report()); } - - return Ok(()); } - _ => {} + + return; } - let chunk = match input.read_format { - IoFormat::Source => { - let source = String::from_utf8(input_bytes).expect("Failed to parse input as UTF-8"); - - match compile(&source) { - Ok(chunk) => chunk, - Err(dust_error) => { - let report = dust_error.report(); - - return Err(report); - } - } + match run(source) { + Ok(Some(value)) => println!("{}", value), + Ok(None) => {} + Err(error) => { + eprintln!("{}", error.report()); } - IoFormat::Json => { - serde_json::from_slice(&input_bytes).expect("Failed to deserialize chunk from JSON") - } - IoFormat::Postcard => { - postcard::from_bytes(&input_bytes).expect("Failed to deserialize chunk from Postcard") - } - }; - - match command { - Command::Run => match run_chunk(&chunk) { - Ok(Some(value)) => { - println!("{}", value); - - return Ok(()); - } - Ok(None) => return Ok(()), - Err(dust_error) => { - let report = dust_error.report(); - - return Err(report); - } - }, - Command::Disassemble { style } => { - let disassembly = chunk.disassembler().style(style).disassemble(); - - println!("{disassembly}"); - - return Ok(()); - } - _ => {} } - - let output_bytes = match output.write_format { - IoFormat::Source => { - return Err("Invalid options, cannot compile chunk as source code.".to_string()) - } - IoFormat::Json => serde_json::to_vec(&chunk).expect("Failed to serialize chunk as JSON"), - IoFormat::Postcard => { - let length = postcard::experimental::serialized_size(&chunk) - .expect("Failed to calculate Postcard size"); - let mut buffer = vec![0_u8; length as usize]; - - postcard::to_slice(&chunk, &mut buffer).expect("Failed to serialize chunk as Postcard"); - - buffer - } - }; - - if let Some(path) = output.output_file { - let mut file = OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(path) - .expect("Failed to open output file"); - - file.write_all(&output_bytes) - .expect("Failed to write to output file"); - } else { - stdout() - .write_all(&output_bytes) - .expect("Failed to write to stdout"); - } - - Ok(()) } #[cfg(test)] diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 581eb32..b759641 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -19,7 +19,7 @@ pub struct Chunk { name: Option, r#type: FunctionType, - instructions: Vec<(Instruction, Span)>, + instructions: Vec<(Instruction, Type, Span)>, constants: Vec, locals: Vec, } @@ -42,7 +42,7 @@ impl Chunk { pub fn with_data( name: Option, r#type: FunctionType, - instructions: Vec<(Instruction, Span)>, + instructions: Vec<(Instruction, Type, Span)>, constants: Vec, locals: Vec, ) -> Self { @@ -111,15 +111,15 @@ impl Chunk { } } - pub fn instructions(&self) -> &Vec<(Instruction, Span)> { + pub fn instructions(&self) -> &Vec<(Instruction, Type, Span)> { &self.instructions } - pub fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Span)> { + pub fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Type, Span)> { &mut self.instructions } - pub fn get_instruction(&self, index: usize) -> Result<&(Instruction, Span), ChunkError> { + pub fn get_instruction(&self, index: usize) -> Result<&(Instruction, Type, Span), ChunkError> { self.instructions .get(index) .ok_or(ChunkError::InstructionIndexOutOfBounds { index }) diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler.rs index bbda60a..6067304 100644 --- a/dust-lang/src/compiler.rs +++ b/dust-lang/src/compiler.rs @@ -120,7 +120,7 @@ impl<'src> Compiler<'src> { self.chunk .instructions() .iter() - .filter_map(|(instruction, _)| { + .filter_map(|(instruction, _, _)| { if instruction.yields_value() { Some(instruction.a() + 1) } else { @@ -230,7 +230,7 @@ impl<'src> Compiler<'src> { } } - fn pop_last_instruction(&mut self) -> Result<(Instruction, Span), CompileError> { + fn pop_last_instruction(&mut self) -> Result<(Instruction, Type, Span), CompileError> { self.chunk .instructions_mut() .pop() @@ -248,7 +248,7 @@ impl<'src> Compiler<'src> { .instructions() .iter() .rev() - .map(|(instruction, _)| instruction.operation()), + .map(|(instruction, _, _)| instruction.operation()), ) { *nth = operation; } @@ -267,7 +267,7 @@ impl<'src> Compiler<'src> { .rev() .skip(minimum) .take(maximum) - .find_map(|(instruction, _)| { + .find_map(|(instruction, _, _)| { if let Operation::LoadBoolean | Operation::LoadConstant = instruction.operation() { Some(instruction) } else { @@ -277,107 +277,133 @@ impl<'src> Compiler<'src> { } pub fn get_instruction_type(&self, instruction: &Instruction) -> Result { - use Operation::*; - match instruction.operation() { - Add | Divide | Modulo | Multiply | Subtract => { - if instruction.b_is_constant() { - self.chunk - .get_constant_type(instruction.b()) - .map_err(|error| CompileError::Chunk { - error, - position: self.current_position, - }) - } else { - self.get_register_type(instruction.b()) - } - } - LoadBoolean | Not => Ok(Type::Boolean), - Negate => { - if instruction.b_is_constant() { - self.chunk - .get_constant_type(instruction.b()) - .map_err(|error| CompileError::Chunk { - error, - position: self.current_position, - }) - } else { - self.get_register_type(instruction.b()) - } - } - LoadConstant => self - .chunk - .get_constant_type(instruction.b()) - .map_err(|error| CompileError::Chunk { - error, - position: self.current_position, - }), - LoadList => self.get_register_type(instruction.a()), - LoadSelf => Ok(Type::SelfChunk), - GetLocal => self - .chunk - .get_local_type(instruction.b()) - .cloned() - .map_err(|error| CompileError::Chunk { - error, - position: self.current_position, - }), - Call => { - let function_register = instruction.b(); - let function_type = self.get_register_type(function_register)?; + Operation::LoadBoolean => Ok(Type::Boolean), + Operation::LoadConstant => { + let LoadConstant { constant_index, .. } = LoadConstant::from(instruction); - match function_type { - Type::Function(FunctionType { return_type, .. }) => Ok(*return_type), - Type::SelfChunk => { - let return_type = self - .return_type - .as_ref() - .unwrap_or_else(|| &self.chunk.r#type().return_type); + self.chunk + .get_constant(constant_index) + .map(|value| value.r#type()) + .map_err(|error| CompileError::Chunk { + error, + position: self.current_position, + }) + } + Operation::LoadList => { + let LoadList { start_register, .. } = LoadList::from(instruction); + let first_instruction = self + .chunk + .get_instruction(start_register as usize) + .map_err(|error| CompileError::Chunk { + error, + position: self.current_position, + })? + .0; // TODO: Handle the case that the first instruction is Close + let item_type = self.get_instruction_type(&first_instruction)?; - Ok(return_type.clone()) - } - _ => Err(CompileError::ExpectedFunctionType { + Ok(Type::List(Box::new(item_type))) + } + Operation::LoadSelf => Ok(Type::SelfChunk), + Operation::GetLocal => { + let GetLocal { local_index, .. } = GetLocal::from(instruction); + + self.get_local(local_index) + .map(|local| local.r#type.clone()) + } + Operation::Add => { + let Add { left, .. } = Add::from(instruction); + + self.get_argument_type(&left) + } + Operation::Subtract => { + let Subtract { left, .. } = Subtract::from(instruction); + + self.get_argument_type(&left) + } + Operation::Multiply => { + let Multiply { left, .. } = Multiply::from(instruction); + + self.get_argument_type(&left) + } + Operation::Divide => { + let Divide { left, .. } = Divide::from(instruction); + + self.get_argument_type(&left) + } + Operation::Modulo => { + let Modulo { left, .. } = Modulo::from(instruction); + + self.get_argument_type(&left) + } + Operation::Negate => { + let Negate { argument, .. } = Negate::from(instruction); + + self.get_argument_type(&argument) + } + Operation::Not => { + let Not { argument, .. } = Not::from(instruction); + + self.get_argument_type(&argument) + } + Operation::Call => { + let Call { function, .. } = Call::from(instruction); + let function_type = self.get_argument_type(&function)?; + + if let Type::Function(FunctionType { return_type, .. }) = function_type { + Ok(*return_type) + } else { + Err(CompileError::ExpectedFunctionType { found: function_type, position: self.current_position, - }), + }) } } - CallNative => { - let native_function = NativeFunction::from(instruction.b()); + Operation::CallNative => { + let CallNative { function, .. } = CallNative::from(instruction); - Ok(*native_function.r#type().return_type) + Ok(*function.r#type().return_type) } - _ => Ok(Type::None), + + Operation::Move + | Operation::Close + | Operation::DefineLocal + | Operation::SetLocal + | Operation::Test + | Operation::TestSet + | Operation::Equal + | Operation::Less + | Operation::LessEqual + | Operation::Jump + | Operation::Return => Ok(Type::None), } } + pub fn get_argument_type(&self, argument: &Argument) -> Result { + let r#type = match argument { + Argument::Register(register_index) => self.get_register_type(*register_index)?, + Argument::Constant(constant_index) => self + .chunk + .get_constant(*constant_index) + .map_err(|error| CompileError::Chunk { + error, + position: self.current_position, + })? + .r#type(), + Argument::Local(local_index) => self.get_local(*local_index)?.r#type.clone(), + }; + + Ok(r#type) + } + pub fn get_register_type(&self, register_index: u16) -> Result { - for (index, (instruction, _)) in self.chunk.instructions().iter().enumerate() { + for (instruction, r#type, _) in self.chunk.instructions() { if instruction.a() == register_index { if let Operation::LoadList = instruction.operation() { - let mut length = (instruction.c() - instruction.b() + 1) as usize; - let mut item_type = Type::Any; - let distance_to_end = self.chunk.len() - index; + let LoadList { start_register, .. } = LoadList::from(instruction); + let item_type = self.get_register_type(start_register)?; - for (instruction, _) in self - .chunk - .instructions() - .iter() - .rev() - .skip(distance_to_end) - .take(length) - { - if let Operation::Close = instruction.operation() { - length -= (instruction.c() - instruction.b()) as usize; - } else if let Type::Any = item_type { - item_type = self.get_instruction_type(instruction)?; - } - } - - return Ok(Type::List { - item_type: Box::new(item_type), - length, - }); + return Ok(Type::List(Box::new(item_type))); } if let Operation::LoadSelf = instruction.operation() { @@ -385,7 +411,7 @@ impl<'src> Compiler<'src> { } if instruction.yields_value() { - return self.get_instruction_type(instruction); + return Ok(r#type.clone()); } } } @@ -425,8 +451,13 @@ impl<'src> Compiler<'src> { instruction.operation().to_string().bold(), position.to_string() ); + let r#type = self + .get_instruction_type(&instruction) + .unwrap_or(Type::None); - self.chunk.instructions_mut().push((instruction, position)); + self.chunk + .instructions_mut() + .push((instruction, r#type, position)); } fn emit_constant( @@ -587,9 +618,7 @@ impl<'src> Compiler<'src> { self.emit_constant(value, position)?; - self.previous_expression_type = Type::String { - length: Some(text.len()), - }; + self.previous_expression_type = Type::String; Ok(()) } else { @@ -616,7 +645,7 @@ impl<'src> Compiler<'src> { self.advance()?; self.parse_expression()?; - let (previous_instruction, previous_position) = self.pop_last_instruction()?; + let (previous_instruction, _, previous_position) = self.pop_last_instruction()?; let argument = if let Some(argument) = previous_instruction.destination_as_argument() { argument } else { @@ -654,20 +683,41 @@ impl<'src> Compiler<'src> { Ok(()) } + fn handle_binary_argument( + &mut self, + instruction: &Instruction, + ) -> Result<(Argument, bool), CompileError> { + let argument = instruction.destination_as_argument().ok_or_else(|| { + CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: self.previous_position, + } + })?; + let push_back = matches!( + instruction.operation(), + Operation::Add + | Operation::Subtract + | Operation::Multiply + | Operation::Divide + | Operation::Modulo + | Operation::Equal + | Operation::Less + | Operation::LessEqual + | Operation::Test + | Operation::TestSet + ); + + Ok((argument, push_back)) + } fn parse_math_binary(&mut self) -> Result<(), CompileError> { - let (left_instruction, left_position) = + let (left_instruction, left_type, left_position) = self.chunk.instructions_mut().pop().ok_or_else(|| { CompileError::ExpectedExpression { found: self.previous_token.to_owned(), position: self.previous_position, } })?; - let left = left_instruction.destination_as_argument().ok_or_else(|| { - CompileError::ExpectedExpression { - found: self.previous_token.to_owned(), - position: left_position, - } - })?; + let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?; let left_is_mutable_local = if let Argument::Local(local_index) = left { self.get_local(local_index)?.is_mutable } else { @@ -695,70 +745,24 @@ impl<'src> Compiler<'src> { self.advance()?; self.parse_sub_expression(&rule.precedence)?; - let (right_instruction, right_position) = self.pop_last_instruction()?; - let right = right_instruction.destination_as_argument().ok_or_else(|| { - CompileError::ExpectedExpression { - found: self.previous_token.to_owned(), - position: right_position, - } - })?; - + let (right_instruction, right_type, right_position) = self.pop_last_instruction()?; + let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?; let destination = if is_assignment { left.index() } else { self.next_register() }; let instruction = match operator { - Token::Plus => Instruction::from(Add { - destination, - left, - right, - }), - Token::PlusEqual => Instruction::from(Add { - destination, - left, - right, - }), - Token::Minus => Instruction::from(Subtract { - destination, - left, - right, - }), - Token::MinusEqual => Instruction::from(Subtract { - destination, - left, - right, - }), - Token::Star => Instruction::from(Multiply { - destination, - left, - right, - }), - Token::StarEqual => Instruction::from(Multiply { - destination, - left, - right, - }), - Token::Slash => Instruction::from(Divide { - destination, - left, - right, - }), - Token::SlashEqual => Instruction::from(Divide { - destination, - left, - right, - }), - Token::Percent => Instruction::from(Modulo { - destination, - left, - right, - }), - Token::PercentEqual => Instruction::from(Modulo { - destination, - left, - right, - }), + Token::Plus => Instruction::add(destination, left, right), + Token::PlusEqual => Instruction::add(destination, left, right), + Token::Minus => Instruction::subtract(destination, left, right), + Token::MinusEqual => Instruction::subtract(destination, left, right), + Token::Star => Instruction::multiply(destination, left, right), + Token::StarEqual => Instruction::multiply(destination, left, right), + Token::Slash => Instruction::divide(destination, left, right), + Token::SlashEqual => Instruction::divide(destination, left, right), + Token::Percent => Instruction::modulo(destination, left, right), + Token::PercentEqual => Instruction::modulo(destination, left, right), _ => { return Err(CompileError::ExpectedTokenMultiple { expected: &[ @@ -779,6 +783,18 @@ impl<'src> Compiler<'src> { } }; + if push_back_left { + self.chunk + .instructions_mut() + .push((left_instruction, left_type, left_position)); + } + + if push_back_right { + self.chunk + .instructions_mut() + .push((right_instruction, right_type, right_position)); + } + self.emit_instruction(instruction, operator_position); if is_assignment { @@ -799,7 +815,7 @@ impl<'src> Compiler<'src> { }); } - let (left_instruction, left_position) = + let (left_instruction, left_type, left_position) = self.chunk.instructions_mut().pop().ok_or_else(|| { CompileError::ExpectedExpression { found: self.previous_token.to_owned(), @@ -819,7 +835,7 @@ impl<'src> Compiler<'src> { self.advance()?; self.parse_sub_expression(&rule.precedence)?; - let (right_instruction, right_position) = + let (right_instruction, right_type, right_position) = self.chunk.instructions_mut().pop().ok_or_else(|| { CompileError::ExpectedExpression { found: self.previous_token.to_owned(), @@ -906,7 +922,7 @@ impl<'src> Compiler<'src> { } fn parse_logical_binary(&mut self) -> Result<(), CompileError> { - let (left_instruction, left_position) = self.pop_last_instruction()?; + let (left_instruction, left_type, left_position) = self.pop_last_instruction()?; if !left_instruction.yields_value() { return Err(CompileError::ExpectedExpression { @@ -1049,7 +1065,7 @@ impl<'src> Compiler<'src> { Token::Bool => Ok(Type::Boolean), Token::FloatKeyword => Ok(Type::Float), Token::Int => Ok(Type::Integer), - Token::Str => Ok(Type::String { length: None }), + Token::Str => Ok(Type::String), _ => Err(CompileError::ExpectedTokenMultiple { expected: &[ TokenKind::Bool, @@ -1126,10 +1142,7 @@ impl<'src> Compiler<'src> { self.emit_instruction(load_list, Span(start, end)); - self.previous_expression_type = Type::List { - item_type: Box::new(item_type), - length: (destination - start_register) as usize, - }; + self.previous_expression_type = Type::List(Box::new(item_type)); Ok(()) } @@ -1150,7 +1163,7 @@ impl<'src> Compiler<'src> { self.chunk.instructions_mut().pop(); self.chunk.instructions_mut().pop(); self.chunk.instructions_mut().pop(); - } else if let Some((instruction, _)) = self.chunk.instructions().last() { + } else if let Some((instruction, _, _)) = self.chunk.instructions().last() { let test_register = instruction.a(); let test = Instruction::from(Test { argument: Argument::Register(test_register), @@ -1226,7 +1239,7 @@ impl<'src> Compiler<'src> { self.chunk .instructions_mut() - .insert(if_block_end, (jump, self.current_position)); + .insert(if_block_end, (jump, Type::None, self.current_position)); } } 2.. => { @@ -1238,7 +1251,7 @@ impl<'src> Compiler<'src> { self.chunk .instructions_mut() - .insert(if_block_end, (jump, self.current_position)); + .insert(if_block_end, (jump, Type::None, self.current_position)); } } @@ -1249,7 +1262,7 @@ impl<'src> Compiler<'src> { self.chunk .instructions_mut() - .insert(if_block_start, (jump, if_block_start_position)); + .insert(if_block_start, (jump, Type::None, if_block_start_position)); if self.chunk.len() >= 4 { let mut optimizer = Optimizer::new(&mut self.chunk); @@ -1306,7 +1319,7 @@ impl<'src> Compiler<'src> { self.chunk .instructions_mut() - .insert(block_start, (jump, self.current_position)); + .insert(block_start, (jump, Type::None, self.current_position)); let jump_back_distance = block_end - expression_start + 1; let jump_back = Instruction::from(Jump { @@ -1623,7 +1636,7 @@ impl<'src> Compiler<'src> { } fn parse_call(&mut self) -> Result<(), CompileError> { - let (last_instruction, _) = + let (last_instruction, _, _) = self.chunk .instructions() .last() diff --git a/dust-lang/src/disassembler.rs b/dust-lang/src/disassembler.rs index 61c4368..b76605d 100644 --- a/dust-lang/src/disassembler.rs +++ b/dust-lang/src/disassembler.rs @@ -50,8 +50,8 @@ use crate::{value::ConcreteValue, Chunk, Local}; const INSTRUCTION_HEADER: [&str; 4] = [ "Instructions", "------------", - " i POSITION OPERATION INFO ", - "--- ---------- ------------- ------------------------------------", + " i POSITION OPERATION TYPE INFO ", + "--- ---------- ------------- ---------------- ------------------------------------", ]; const CONSTANT_HEADER: [&str; 4] = [ @@ -255,13 +255,14 @@ impl<'a> Disassembler<'a> { self.push_header(line); } - for (index, (instruction, position)) in self.chunk.instructions().iter().enumerate() { + for (index, (instruction, r#type, position)) in self.chunk.instructions().iter().enumerate() + { let position = position.to_string(); let operation = instruction.operation().to_string(); let info = instruction.disassembly_info(self.chunk); let instruction_display = - format!("{index:^3} {position:^10} {operation:13} {info:^36}"); + format!("{index:^3} {position:^10} {operation:13} {type:^16} {info:^36}"); self.push_details(&instruction_display); } diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index 121f354..f432777 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -121,11 +121,191 @@ impl Instruction { Instruction(operation as u64) } + pub fn r#move(from: u16, to: u16) -> Instruction { + Instruction::from(Move { from, to }) + } + + pub fn close(from: u16, to: u16) -> Instruction { + Instruction::from(Close { from, to }) + } + + pub fn load_boolean(destination: u16, value: bool, jump_next: bool) -> Instruction { + Instruction::from(LoadBoolean { + destination, + value, + jump_next, + }) + } + + pub fn load_constant(destination: u16, constant_index: u16, jump_next: bool) -> Instruction { + Instruction::from(LoadConstant { + destination, + constant_index, + jump_next, + }) + } + + pub fn load_list(destination: u16, start_register: u16) -> Instruction { + Instruction::from(LoadList { + destination, + start_register, + }) + } + + pub fn load_self(destination: u16) -> Instruction { + Instruction::from(LoadSelf { destination }) + } + + pub fn define_local(register: u16, local_index: u16, is_mutable: bool) -> Instruction { + Instruction::from(DefineLocal { + local_index, + register, + is_mutable, + }) + } + + pub fn get_local(destination: u16, local_index: u16) -> Instruction { + Instruction::from(GetLocal { + destination, + local_index, + }) + } + + pub fn set_local(register: u16, local_index: u16) -> Instruction { + Instruction::from(SetLocal { + local_index, + register, + }) + } + + pub fn add(destination: u16, left: Argument, right: Argument) -> Instruction { + Instruction::from(Add { + destination, + left, + right, + }) + } + + pub fn subtract(destination: u16, left: Argument, right: Argument) -> Instruction { + Instruction::from(Subtract { + destination, + left, + right, + }) + } + + pub fn multiply(destination: u16, left: Argument, right: Argument) -> Instruction { + Instruction::from(Multiply { + destination, + left, + right, + }) + } + + pub fn divide(destination: u16, left: Argument, right: Argument) -> Instruction { + Instruction::from(Divide { + destination, + left, + right, + }) + } + + pub fn modulo(destination: u16, left: Argument, right: Argument) -> Instruction { + Instruction::from(Modulo { + destination, + left, + right, + }) + } + + pub fn test(argument: Argument, value: bool) -> Instruction { + Instruction::from(Test { argument, value }) + } + + pub fn test_set(destination: u16, argument: Argument, value: bool) -> Instruction { + Instruction::from(TestSet { + destination, + argument, + value, + }) + } + + pub fn equal(value: bool, left: Argument, right: Argument) -> Instruction { + Instruction::from(Equal { value, left, right }) + } + + pub fn less(value: bool, left: Argument, right: Argument) -> Instruction { + Instruction::from(Less { value, left, right }) + } + + pub fn less_equal(value: bool, left: Argument, right: Argument) -> Instruction { + Instruction::from(LessEqual { value, left, right }) + } + + pub fn negate(destination: u16, argument: Argument) -> Instruction { + Instruction::from(Negate { + destination, + argument, + }) + } + + pub fn not(destination: u16, argument: Argument) -> Instruction { + Instruction::from(Not { + destination, + argument, + }) + } + + pub fn jump(offset: u16, is_positive: bool) -> Instruction { + Instruction::from(Jump { + offset, + is_positive, + }) + } + + pub fn call(destination: u16, function: Argument, argument_count: u16) -> Instruction { + Instruction::from(Call { + destination, + function, + argument_count, + }) + } + + pub fn call_native( + destination: u16, + function: NativeFunction, + argument_count: u16, + ) -> Instruction { + Instruction::from(CallNative { + destination, + function, + argument_count, + }) + } + + pub fn r#return(should_return_value: bool) -> Instruction { + Instruction::from(Return { + should_return_value, + }) + } + pub fn destination_as_argument(&self) -> Option { - if self.yields_value() { - Some(Argument::Register(self.a())) - } else { - None + match self.operation() { + Operation::LoadConstant => Some(Argument::Constant(self.b())), + Operation::GetLocal => Some(Argument::Local(self.b())), + Operation::LoadBoolean + | Operation::LoadList + | Operation::LoadSelf + | Operation::Add + | Operation::Subtract + | Operation::Multiply + | Operation::Divide + | Operation::Modulo + | Operation::Negate + | Operation::Not + | Operation::Call + | Operation::CallNative => Some(Argument::Register(self.a())), + _ => None, } } @@ -284,27 +464,25 @@ impl Instruction { | Operation::Multiply | Operation::Divide | Operation::Modulo - | Operation::Equal - | Operation::Less - | Operation::LessEqual | Operation::Negate | Operation::Not | Operation::Call => true, - Operation::CallNative => { let function = NativeFunction::from(self.b()); function.returns_value() } - Operation::Move | Operation::Close | Operation::DefineLocal | Operation::SetLocal + | Operation::Equal + | Operation::Less + | Operation::LessEqual | Operation::Test | Operation::TestSet | Operation::Jump - | Operation::Return => true, + | Operation::Return => false, } } @@ -313,12 +491,12 @@ impl Instruction { Operation::Move => { let Move { from, to } = Move::from(self); - format!("{to} = {from}") + format!("R{to} = R{from}") } Operation::Close => { let Close { from, to } = Close::from(self); - format!("{from}..={to}") + format!("R{from}..=R{to}") } Operation::LoadBoolean => { let LoadBoolean { @@ -336,14 +514,14 @@ impl Instruction { Operation::LoadConstant => { let LoadConstant { destination, - constant_index: constant, + constant_index, jump_next, } = LoadConstant::from(self); if jump_next { - format!("R{destination} = C{constant} JUMP +1") + format!("R{destination} = C{constant_index} JUMP +1") } else { - format!("R{destination} = C{constant}") + format!("R{destination} = C{constant_index}") } } Operation::LoadList => { diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index d8e510c..e07dcd5 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -30,7 +30,7 @@ pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict}; pub use crate::scope::Scope; pub use crate::token::{display_token_list, Token, TokenKind, TokenOwned}; pub use crate::value::{AbstractValue, ConcreteValue, RangeValue, Value, ValueError, ValueRef}; -pub use crate::vm::{run_source, Vm, VmError}; +pub use crate::vm::{run, Vm, VmError}; use std::fmt::Display; diff --git a/dust-lang/src/native_function/mod.rs b/dust-lang/src/native_function/mod.rs index 287d2de..5386996 100644 --- a/dust-lang/src/native_function/mod.rs +++ b/dust-lang/src/native_function/mod.rs @@ -148,7 +148,7 @@ define_native_function! { FunctionType { type_parameters: None, value_parameters: Some(vec![(0, Type::Any)]), - return_type: Box::new(Type::String { length: None }) + return_type: Box::new(Type::String) }, logic::to_string ), @@ -209,7 +209,7 @@ define_native_function! { FunctionType { type_parameters: None, value_parameters: None, - return_type: Box::new(Type::String { length: None }) + return_type: Box::new(Type::String) }, logic::read_line ), @@ -224,7 +224,7 @@ define_native_function! { "write", FunctionType { type_parameters: None, - value_parameters: Some(vec![(0, Type::String { length: None })]), + value_parameters: Some(vec![(0, Type::String)]), return_type: Box::new(Type::None) }, logic::write @@ -236,7 +236,7 @@ define_native_function! { "write_line", FunctionType { type_parameters: None, - value_parameters: Some(vec![(0, Type::String { length: None })]), + value_parameters: Some(vec![(0, Type::String)]), return_type: Box::new(Type::None) }, logic::write_line diff --git a/dust-lang/src/optimizer.rs b/dust-lang/src/optimizer.rs index baa1ee2..23051cc 100644 --- a/dust-lang/src/optimizer.rs +++ b/dust-lang/src/optimizer.rs @@ -1,6 +1,6 @@ //! Tool used by the compiler to optimize a chunk's bytecode. -use crate::{Chunk, Instruction, Operation, Span}; +use crate::{Chunk, Instruction, Operation, Span, Type}; /// An instruction optimizer that mutably borrows instructions from a chunk. #[derive(Debug)] @@ -93,7 +93,7 @@ impl<'a> Optimizer<'a> { true } - fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Span)> { + fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Type, Span)> { self.chunk.instructions_mut() } @@ -109,7 +109,7 @@ impl<'a> Optimizer<'a> { .instructions() .iter() .rev() - .map(|(instruction, _)| instruction.operation()), + .map(|(instruction, _, _)| instruction.operation()), ) { *nth = operation; } diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index 5d64a59..5fbd53d 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -22,14 +22,7 @@ pub enum Type { concrete_type: Option>, }, Integer, - List { - item_type: Box, - length: usize, - }, - ListEmpty, - ListOf { - item_type: Box, - }, + List(Box), Map { pairs: HashMap, }, @@ -38,9 +31,7 @@ pub enum Type { r#type: Box, }, SelfChunk, - String { - length: Option, - }, + String, Struct(StructType), Tuple { fields: Option>, @@ -106,23 +97,7 @@ impl Type { return Ok(()); } } - ( - Type::List { - item_type: left_type, - length: left_length, - }, - Type::List { - item_type: right_type, - length: right_length, - }, - ) => { - if left_length != right_length { - return Err(TypeConflict { - actual: other.clone(), - expected: self.clone(), - }); - } - + (Type::List(left_type), Type::List(right_type)) => { if left_type.check(right_type).is_err() { return Err(TypeConflict { actual: other.clone(), @@ -132,51 +107,6 @@ impl Type { return Ok(()); } - ( - Type::ListOf { - item_type: left_type, - }, - Type::ListOf { - item_type: right_type, - }, - ) => { - if left_type.check(right_type).is_err() { - return Err(TypeConflict { - actual: other.clone(), - expected: self.clone(), - }); - } - } - ( - Type::List { - item_type: list_item_type, - .. - }, - Type::ListOf { - item_type: list_of_item_type, - }, - ) - | ( - Type::ListOf { - item_type: list_of_item_type, - }, - Type::List { - item_type: list_item_type, - .. - }, - ) => { - // TODO: This is a hack, remove it. - if let Type::Any = **list_of_item_type { - return Ok(()); - } - - if list_item_type.check(list_of_item_type).is_err() { - return Err(TypeConflict { - actual: other.clone(), - expected: self.clone(), - }); - } - } ( Type::Function(FunctionType { type_parameters: left_type_parameters, @@ -237,9 +167,7 @@ impl Display for Type { } } Type::Integer => write!(f, "int"), - Type::List { item_type, length } => write!(f, "[{item_type}; {length}]"), - Type::ListEmpty => write!(f, "[]"), - Type::ListOf { item_type } => write!(f, "[{item_type}]"), + Type::List(item_type) => write!(f, "[{item_type}]"), Type::Map { pairs } => { write!(f, "map ")?; @@ -258,7 +186,7 @@ impl Display for Type { Type::None => write!(f, "none"), Type::Range { r#type } => write!(f, "{type} range"), Type::SelfChunk => write!(f, "self"), - Type::String { .. } => write!(f, "str"), + Type::String => write!(f, "str"), Type::Struct(struct_type) => write!(f, "{struct_type}"), Type::Tuple { fields } => { if let Some(fields) = fields { @@ -310,34 +238,10 @@ impl Ord for Type { (Type::Generic { .. }, _) => Ordering::Greater, (Type::Integer, Type::Integer) => Ordering::Equal, (Type::Integer, _) => Ordering::Greater, - ( - Type::List { - item_type: left_item_type, - length: left_length, - }, - Type::List { - item_type: right_item_type, - length: right_length, - }, - ) => { - if left_length == right_length { - left_item_type.cmp(right_item_type) - } else { - left_length.cmp(right_length) - } + (Type::List(left_item_type), Type::List(right_item_type)) => { + left_item_type.cmp(right_item_type) } (Type::List { .. }, _) => Ordering::Greater, - (Type::ListEmpty, Type::ListEmpty) => Ordering::Equal, - (Type::ListEmpty, _) => Ordering::Greater, - ( - Type::ListOf { - item_type: left_item_type, - }, - Type::ListOf { - item_type: right_item_type, - }, - ) => left_item_type.cmp(right_item_type), - (Type::ListOf { .. }, _) => Ordering::Greater, (Type::Map { pairs: left_pairs }, Type::Map { pairs: right_pairs }) => { left_pairs.iter().cmp(right_pairs.iter()) } @@ -350,7 +254,7 @@ impl Ord for Type { (Type::Range { .. }, _) => Ordering::Greater, (Type::SelfChunk, Type::SelfChunk) => Ordering::Equal, (Type::SelfChunk, _) => Ordering::Greater, - (Type::String { length: left }, Type::String { length: right }) => left.cmp(right), + (Type::String, Type::String) => Ordering::Equal, (Type::String { .. }, _) => Ordering::Greater, (Type::Struct(left_struct), Type::Struct(right_struct)) => { left_struct.cmp(right_struct) diff --git a/dust-lang/src/value/concrete_value.rs b/dust-lang/src/value/concrete_value.rs index 60195e7..e23abd3 100644 --- a/dust-lang/src/value/concrete_value.rs +++ b/dust-lang/src/value/concrete_value.rs @@ -47,15 +47,10 @@ impl ConcreteValue { ConcreteValue::List(list) => { let item_type = list.first().map_or(Type::Any, |item| item.r#type()); - Type::List { - item_type: Box::new(item_type), - length: list.len(), - } + Type::List(Box::new(item_type)) } ConcreteValue::Range(range) => range.r#type(), - ConcreteValue::String(string) => Type::String { - length: Some(string.len()), - }, + ConcreteValue::String(_) => Type::String, } } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 4d83492..3e023c3 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -10,7 +10,7 @@ use crate::{ Instruction, NativeFunction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef, }; -pub fn run_source(source: &str) -> Result, DustError> { +pub fn run(source: &str) -> Result, DustError> { let chunk = compile(source)?; let mut vm = Vm::new(&chunk, None); @@ -18,13 +18,6 @@ pub fn run_source(source: &str) -> Result, DustError> { .map_err(|error| DustError::Runtime { error, source }) } -pub fn run_chunk(chunk: &Chunk) -> Result, DustError> { - let mut vm = Vm::new(chunk, None); - - vm.run() - .map_err(|error| DustError::Runtime { error, source: "" }) -} - /// Dust virtual machine. /// /// See the [module-level documentation](index.html) for more information. @@ -617,19 +610,18 @@ impl<'a> Vm<'a> { } fn read(&mut self) -> Result { - let (instruction, position) = + let (instruction, _type, position) = self.chunk .get_instruction(self.ip) - .copied() .map_err(|error| VmError::Chunk { error, position: self.current_position, })?; self.ip += 1; - self.current_position = position; + self.current_position = *position; - Ok(instruction) + Ok(*instruction) } } diff --git a/dust-lang/tests/basic.rs b/dust-lang/tests/basic.rs index 8645d3c..04ba54f 100644 --- a/dust-lang/tests/basic.rs +++ b/dust-lang/tests/basic.rs @@ -22,7 +22,7 @@ fn constant() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(42)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(42)))); } #[test] @@ -43,7 +43,7 @@ fn empty() { vec![] )) ); - assert_eq!(run_source(source), Ok(None)); + assert_eq!(run(source), Ok(None)); } #[test] @@ -61,13 +61,11 @@ fn parentheses_precedence() { }, vec![ ( - *Instruction::add(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::add(0, Argument::Constant(0), Argument::Constant(1)), Span(3, 4) ), ( - *Instruction::multiply(1, 0, 2).set_c_is_constant(), + Instruction::multiply(1, Argument::Register(0), Argument::Constant(2)), Span(8, 9) ), (Instruction::r#return(true), Span(11, 11)), @@ -81,5 +79,5 @@ fn parentheses_precedence() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(9)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(9)))); } diff --git a/dust-lang/tests/comparison.rs b/dust-lang/tests/comparison.rs index 4d07519..14a9d14 100644 --- a/dust-lang/tests/comparison.rs +++ b/dust-lang/tests/comparison.rs @@ -15,9 +15,7 @@ fn equal() { }, vec![ ( - *Instruction::equal(true, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::equal(true, Argument::Constant(0), Argument::Constant(1)), Span(2, 4) ), (Instruction::jump(1, true), Span(2, 4)), @@ -30,7 +28,7 @@ fn equal() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false)))); } #[test] @@ -48,9 +46,7 @@ fn greater() { }, vec![ ( - *Instruction::less_equal(false, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::less_equal(false, Argument::Constant(0), Argument::Constant(1)), Span(2, 3) ), (Instruction::jump(1, true), Span(2, 3)), @@ -63,7 +59,7 @@ fn greater() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false)))); } #[test] @@ -81,9 +77,7 @@ fn greater_than_or_equal() { }, vec![ ( - *Instruction::less(false, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::less(false, Argument::Constant(0), Argument::Constant(1)), Span(2, 4) ), (Instruction::jump(1, true), Span(2, 4)), @@ -96,7 +90,7 @@ fn greater_than_or_equal() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false)))); } #[test] @@ -114,9 +108,7 @@ fn less_than() { }, vec![ ( - *Instruction::less(true, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::less(true, Argument::Constant(0), Argument::Constant(1)), Span(2, 3) ), (Instruction::jump(1, true), Span(2, 3)), @@ -129,7 +121,7 @@ fn less_than() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(true)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true)))); } #[test] @@ -147,9 +139,7 @@ fn less_than_or_equal() { }, vec![ ( - *Instruction::less_equal(true, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::less_equal(true, Argument::Constant(0), Argument::Constant(1)), Span(2, 4) ), (Instruction::jump(1, true), Span(2, 4)), @@ -162,7 +152,7 @@ fn less_than_or_equal() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(true)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true)))); } #[test] @@ -180,9 +170,7 @@ fn not_equal() { }, vec![ ( - *Instruction::equal(false, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::equal(false, Argument::Constant(0), Argument::Constant(1)), Span(2, 4) ), (Instruction::jump(1, true), Span(2, 4)), @@ -195,5 +183,5 @@ fn not_equal() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(true)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true)))); } diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs index 4c9924c..99f363c 100644 --- a/dust-lang/tests/functions.rs +++ b/dust-lang/tests/functions.rs @@ -5,7 +5,7 @@ fn function() { let source = "fn(a: int, b: int) -> int { a + b }"; assert_eq!( - run_source(source), + run(source), Ok(Some(ConcreteValue::Function(Chunk::with_data( None, FunctionType { @@ -18,7 +18,10 @@ fn function() { })) }, vec![ - (Instruction::add(2, 0, 1), Span(30, 31)), + ( + Instruction::add(2, Argument::Local(0), Argument::Local(1)), + Span(30, 31) + ), (Instruction::r#return(true), Span(35, 35)), ], vec![ConcreteValue::string("a"), ConcreteValue::string("b"),], @@ -47,7 +50,7 @@ fn function_call() { (Instruction::load_constant(0, 0, false), Span(0, 36)), (Instruction::load_constant(1, 1, false), Span(36, 37)), (Instruction::load_constant(2, 2, false), Span(39, 40)), - (Instruction::call(3, 0, 2), Span(35, 41)), + (Instruction::call(3, Argument::Constant(0), 2), Span(35, 41)), (Instruction::r#return(true), Span(41, 41)), ], vec![ @@ -59,7 +62,10 @@ fn function_call() { return_type: Box::new(Type::Integer) }, vec![ - (Instruction::add(2, 0, 1), Span(30, 31)), + ( + Instruction::add(2, Argument::Local(0), Argument::Local(1)), + Span(30, 31) + ), (Instruction::r#return(true), Span(35, 36)), ], vec![ConcreteValue::string("a"), ConcreteValue::string("b"),], @@ -75,7 +81,7 @@ fn function_call() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(3)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(3)))); } #[test] @@ -105,7 +111,10 @@ fn function_declaration() { return_type: Box::new(Type::Integer) }, vec![ - (Instruction::add(2, 0, 1), Span(35, 36)), + ( + Instruction::add(2, Argument::Local(0), Argument::Local(1)), + Span(35, 36) + ), (Instruction::r#return(true), Span(40, 40)), ], vec![ConcreteValue::string("a"), ConcreteValue::string("b")], @@ -129,5 +138,5 @@ fn function_declaration() { )), ); - assert_eq!(run_source(source), Ok(None)); + assert_eq!(run(source), Ok(None)); } diff --git a/dust-lang/tests/lists.rs b/dust-lang/tests/lists.rs index 70bf0bf..f9796bf 100644 --- a/dust-lang/tests/lists.rs +++ b/dust-lang/tests/lists.rs @@ -11,10 +11,7 @@ fn empty_list() { FunctionType { type_parameters: None, value_parameters: None, - return_type: Box::new(Type::List { - item_type: Box::new(Type::Any), - length: 0 - }) + return_type: Box::new(Type::List(Box::new(Type::Any))), }, vec![ (Instruction::load_list(0, 0), Span(0, 2)), @@ -25,7 +22,7 @@ fn empty_list() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::list([])))); + assert_eq!(run(source), Ok(Some(ConcreteValue::list([])))); } #[test] @@ -39,10 +36,7 @@ fn list() { FunctionType { type_parameters: None, value_parameters: None, - return_type: Box::new(Type::List { - item_type: Box::new(Type::Integer), - length: 3 - }) + return_type: Box::new(Type::List(Box::new(Type::Integer))), }, vec![ (Instruction::load_constant(0, 0, false), Span(1, 2)), @@ -61,7 +55,7 @@ fn list() { ); assert_eq!( - run_source(source), + run(source), Ok(Some(ConcreteValue::list([ ConcreteValue::Integer(1), ConcreteValue::Integer(2), @@ -81,26 +75,22 @@ fn list_with_complex_expression() { FunctionType { type_parameters: None, value_parameters: None, - return_type: Box::new(Type::List { - item_type: Box::new(Type::Integer), - length: 3 - }) + return_type: Box::new(Type::List(Box::new(Type::Integer))), }, vec![ (Instruction::load_constant(0, 0, false), Span(1, 2)), ( - *Instruction::add(1, 1, 2) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::add(1, Argument::Constant(1), Argument::Constant(2)), Span(6, 7) ), ( - *Instruction::multiply(2, 3, 4) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::multiply(2, Argument::Constant(3), Argument::Constant(4)), Span(14, 15) ), - (Instruction::subtract(3, 1, 2), Span(10, 11)), + ( + Instruction::subtract(3, Argument::Register(1), Argument::Register(2)), + Span(10, 11) + ), (Instruction::close(1, 3), Span(17, 18)), (Instruction::load_list(4, 0), Span(0, 18)), (Instruction::r#return(true), Span(18, 18)), @@ -117,7 +107,7 @@ fn list_with_complex_expression() { ); assert_eq!( - run_source(source), + run(source), Ok(Some(ConcreteValue::list([ ConcreteValue::Integer(1), ConcreteValue::Integer(-15) @@ -136,17 +126,12 @@ fn list_with_simple_expression() { FunctionType { type_parameters: None, value_parameters: None, - return_type: Box::new(Type::List { - item_type: Box::new(Type::Integer), - length: 3 - }) + return_type: Box::new(Type::List(Box::new(Type::Integer))), }, vec![ (Instruction::load_constant(0, 0, false), Span(1, 2)), ( - *Instruction::add(1, 1, 2) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::add(1, Argument::Constant(1), Argument::Constant(2)), Span(6, 7) ), (Instruction::load_constant(2, 3, false), Span(11, 12)), @@ -164,7 +149,7 @@ fn list_with_simple_expression() { ); assert_eq!( - run_source(source), + run(source), Ok(Some(ConcreteValue::list([ ConcreteValue::Integer(1), ConcreteValue::Integer(5), diff --git a/dust-lang/tests/logic.rs b/dust-lang/tests/logic.rs index 99ded80..d3acba1 100644 --- a/dust-lang/tests/logic.rs +++ b/dust-lang/tests/logic.rs @@ -15,7 +15,7 @@ fn and() { }, vec![ (Instruction::load_boolean(0, true, false), Span(0, 4)), - (Instruction::test(0, true), Span(5, 7)), + (Instruction::test(Argument::Register(0), true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)), (Instruction::load_boolean(1, false, false), Span(8, 13)), (Instruction::r#return(true), Span(13, 13)), @@ -25,7 +25,7 @@ fn and() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false)))); } #[test] @@ -43,7 +43,7 @@ fn or() { }, vec![ (Instruction::load_boolean(0, true, false), Span(0, 4)), - (Instruction::test(0, false), Span(5, 7)), + (Instruction::test(Argument::Register(0), false), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)), (Instruction::load_boolean(1, false, false), Span(8, 13)), (Instruction::r#return(true), Span(13, 13)), @@ -53,7 +53,7 @@ fn or() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(true)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true)))); } #[test] @@ -75,7 +75,7 @@ fn variable_and() { (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::test(Argument::Register(2), true), Span(31, 33)), (Instruction::jump(1, true), Span(31, 33)), (Instruction::get_local(3, 1), Span(34, 35)), (Instruction::r#return(true), Span(35, 35)), @@ -88,5 +88,5 @@ fn variable_and() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false)))); } diff --git a/dust-lang/tests/loops.rs b/dust-lang/tests/loops.rs index ee6f979..5b5c1b4 100644 --- a/dust-lang/tests/loops.rs +++ b/dust-lang/tests/loops.rs @@ -17,11 +17,14 @@ fn r#while() { (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(), + Instruction::less(true, Argument::Register(0), Argument::Constant(2)), Span(23, 24) ), (Instruction::jump(2, true), Span(41, 42)), - (*Instruction::add(0, 0, 3).set_c_is_constant(), Span(35, 36)), + ( + Instruction::add(0, Argument::Register(0), Argument::Constant(3)), + Span(35, 36) + ), (Instruction::jump(3, false), Span(41, 42)), (Instruction::get_local(1, 0), Span(41, 42)), (Instruction::r#return(true), Span(42, 42)), @@ -36,5 +39,5 @@ fn r#while() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(5)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(5)))); } diff --git a/dust-lang/tests/math.rs b/dust-lang/tests/math.rs index ad201a6..6082f73 100644 --- a/dust-lang/tests/math.rs +++ b/dust-lang/tests/math.rs @@ -15,9 +15,7 @@ fn add() { }, vec![ ( - *Instruction::add(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::add(0, Argument::Constant(0), Argument::Constant(1)), Span(2, 3) ), (Instruction::r#return(true), Span(5, 5)) @@ -27,7 +25,7 @@ fn add() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(3)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(3)))); } #[test] @@ -46,7 +44,10 @@ 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::add(0, Argument::Register(0), Argument::Constant(2)), + Span(17, 19) + ), (Instruction::get_local(1, 0), Span(23, 24)), (Instruction::r#return(true), Span(24, 24)) ], @@ -59,7 +60,7 @@ fn add_assign() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(3)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(3)))); } #[test] @@ -109,9 +110,7 @@ fn divide() { }, vec![ ( - *Instruction::divide(0, 0, 0) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::divide(0, Argument::Constant(0), Argument::Constant(0)), Span(2, 3) ), (Instruction::r#return(true), Span(5, 5)) @@ -121,7 +120,7 @@ fn divide() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(1)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1)))); } #[test] @@ -141,7 +140,7 @@ fn divide_assign() { (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(), + Instruction::divide(0, Argument::Register(0), Argument::Constant(0)), Span(17, 19) ), (Instruction::get_local(1, 0), Span(23, 24)), @@ -152,7 +151,7 @@ fn divide_assign() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(1)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1)))); } #[test] @@ -186,22 +185,21 @@ fn math_operator_precedence() { }, vec![ ( - *Instruction::add(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::add(0, Argument::Constant(0), Argument::Constant(1)), Span(2, 3) ), ( - *Instruction::multiply(1, 2, 3) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::multiply(1, Argument::Constant(2), Argument::Constant(3)), Span(10, 11) ), ( - *Instruction::divide(2, 1, 4).set_c_is_constant(), + Instruction::divide(2, Argument::Register(1), Argument::Constant(4)), Span(14, 15) ), - (Instruction::subtract(3, 0, 2), Span(6, 7)), + ( + Instruction::subtract(3, Argument::Register(0), Argument::Register(2)), + Span(6, 7) + ), (Instruction::r#return(true), Span(17, 17)), ], vec![ @@ -215,7 +213,7 @@ fn math_operator_precedence() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(1)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1)))); } #[test] @@ -233,9 +231,7 @@ fn multiply() { }, vec![ ( - *Instruction::multiply(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::multiply(0, Argument::Constant(0), Argument::Constant(1)), Span(2, 3) ), (Instruction::r#return(true), Span(5, 5)), @@ -245,7 +241,7 @@ fn multiply() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(2)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(2)))); } #[test] @@ -265,7 +261,7 @@ fn multiply_assign() { (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(), + Instruction::multiply(0, Argument::Register(0), Argument::Constant(2)), Span(17, 19) ), (Instruction::get_local(1, 0), Span(22, 23)), @@ -280,7 +276,7 @@ fn multiply_assign() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(6)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(6)))); } #[test] @@ -314,9 +310,7 @@ fn subtract() { }, vec![ ( - *Instruction::subtract(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), + Instruction::subtract(0, Argument::Constant(0), Argument::Constant(1)), Span(2, 3) ), (Instruction::r#return(true), Span(5, 5)), @@ -326,7 +320,7 @@ fn subtract() { )) ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(-1)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(-1)))); } #[test] @@ -346,7 +340,7 @@ fn subtract_assign() { (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(), + Instruction::subtract(0, Argument::Register(0), Argument::Constant(2)), Span(18, 20) ), (Instruction::get_local(1, 0), Span(24, 25)), @@ -361,7 +355,7 @@ fn subtract_assign() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(40)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(40)))); } #[test] diff --git a/dust-lang/tests/native_functions.rs b/dust-lang/tests/native_functions.rs index 5df5587..fb7d2cd 100644 --- a/dust-lang/tests/native_functions.rs +++ b/dust-lang/tests/native_functions.rs @@ -31,7 +31,7 @@ fn panic() { ); assert_eq!( - run_source(source), + run(source), Err(DustError::Runtime { error: VmError::NativeFunction(NativeFunctionError::Panic { message: Some("Goodbye world! 42".to_string()), @@ -53,7 +53,7 @@ fn to_string() { FunctionType { type_parameters: None, value_parameters: None, - return_type: Box::new(Type::String { length: None }), + return_type: Box::new(Type::String), }, vec![ (Instruction::load_constant(0, 0, false), Span(10, 12)), @@ -68,5 +68,5 @@ fn to_string() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::string("42")))) + assert_eq!(run(source), Ok(Some(ConcreteValue::string("42")))) } diff --git a/dust-lang/tests/scopes.rs b/dust-lang/tests/scopes.rs index 9a3d011..565bb41 100644 --- a/dust-lang/tests/scopes.rs +++ b/dust-lang/tests/scopes.rs @@ -9,7 +9,7 @@ fn allow_access_to_parent_scope() { } "#; - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(1)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1)))); } #[test] @@ -69,7 +69,7 @@ fn block_scope() { )), ); - assert_eq!(run_source(source), Ok(None)); + assert_eq!(run(source), Ok(None)); } #[test] @@ -149,7 +149,7 @@ fn multiple_block_scopes() { )), ); - assert_eq!(run_source(source), Ok(None)); + assert_eq!(run(source), Ok(None)); } #[test] @@ -162,7 +162,7 @@ fn disallow_access_to_child_scope() { "#; assert_eq!( - run_source(source), + run(source), Err(DustError::Compile { error: CompileError::VariableOutOfScope { identifier: "x".to_string(), @@ -187,7 +187,7 @@ fn disallow_access_to_child_scope_nested() { "#; assert_eq!( - run_source(source), + run(source), Err(DustError::Compile { error: CompileError::VariableOutOfScope { identifier: "x".to_string(), @@ -212,7 +212,7 @@ fn disallow_access_to_sibling_scope() { "#; assert_eq!( - run_source(source), + run(source), Err(DustError::Compile { error: CompileError::VariableOutOfScope { identifier: "x".to_string(), @@ -239,7 +239,7 @@ fn disallow_access_to_sibling_scope_nested() { "#; assert_eq!( - run_source(source), + run(source), Err(DustError::Compile { error: CompileError::VariableOutOfScope { identifier: "x".to_string(), diff --git a/dust-lang/tests/unary_operations.rs b/dust-lang/tests/unary_operations.rs index e4b6a14..325cf70 100644 --- a/dust-lang/tests/unary_operations.rs +++ b/dust-lang/tests/unary_operations.rs @@ -14,7 +14,7 @@ fn negate() { return_type: Box::new(Type::Integer), }, vec![ - (*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)), + (Instruction::negate(0, Argument::Constant(0)), Span(0, 1)), (Instruction::r#return(true), Span(5, 5)), ], vec![ConcreteValue::Integer(42)], @@ -22,7 +22,7 @@ fn negate() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(-42)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(-42)))); } #[test] @@ -40,7 +40,7 @@ fn not() { }, vec![ (Instruction::load_boolean(0, true, false), Span(1, 5)), - (Instruction::not(1, 0), Span(0, 1)), + (Instruction::not(1, Argument::Register(0)), Span(0, 1)), (Instruction::r#return(true), Span(5, 5)), ], vec![], @@ -48,5 +48,5 @@ fn not() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false)))); } diff --git a/dust-lang/tests/variables.rs b/dust-lang/tests/variables.rs index cd91ad3..90f3a34 100644 --- a/dust-lang/tests/variables.rs +++ b/dust-lang/tests/variables.rs @@ -23,7 +23,7 @@ fn define_local() { )), ); - assert_eq!(run_source(source), Ok(None)); + assert_eq!(run(source), Ok(None)); } #[test] @@ -73,5 +73,5 @@ fn set_local() { )), ); - assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(42)))); + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(42)))); }