diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler.rs index 8dd466b..58f16eb 100644 --- a/dust-lang/src/compiler.rs +++ b/dust-lang/src/compiler.rs @@ -1,8 +1,9 @@ //! Compilation tools and errors //! //! This module provides two compilation options: -//! - [`compile`], which compiles the entire input and returns a chunk -//! - [`Compiler`], which compiles the input a token at a time while assembling a chunk +//! - [`compile`] borrows a string and returns a chunk, handling the entire compilation process and +//! turning any resulting [`ComplileError`] into a [`DustError`]. +//! - [`Compiler`] uses a lexer to get tokens and assembles a chunk. use std::{ fmt::{self, Display, Formatter}, mem::replace, @@ -41,10 +42,12 @@ pub fn compile(source: &str) -> Result { Compiler::new(lexer).map_err(|error| DustError::Compile { error, source })?; compiler - .parse_top_level() + .compile() .map_err(|error| DustError::Compile { error, source })?; - Ok(compiler.finish(None, None)) + let chunk = compiler.finish(None, None); + + Ok(chunk) } /// Tool for compiling the input a token at a time while assembling a chunk. @@ -119,6 +122,20 @@ impl<'src> Compiler<'src> { ) } + pub fn compile(&mut self) -> Result<(), CompileError> { + loop { + self.parse(Precedence::None)?; + + if self.is_eof() || self.allow(Token::RightBrace)? { + self.parse_implicit_return()?; + + break; + } + } + + Ok(()) + } + fn is_eof(&self) -> bool { matches!(self.current_token, Token::Eof) } @@ -1199,20 +1216,6 @@ impl<'src> Compiler<'src> { Ok(()) } - fn parse_top_level(&mut self) -> Result<(), CompileError> { - loop { - self.parse(Precedence::None)?; - - if self.is_eof() || self.allow(Token::RightBrace)? { - self.parse_implicit_return()?; - - break; - } - } - - Ok(()) - } - fn parse_expression(&mut self) -> Result<(), CompileError> { self.parse(Precedence::None)?; @@ -1408,7 +1411,7 @@ impl<'src> Compiler<'src> { function_compiler.return_type = Some((*return_type).clone()); function_compiler.expect(Token::LeftBrace)?; - function_compiler.parse_top_level()?; + function_compiler.compile()?; self.previous_token = function_compiler.previous_token; self.previous_position = function_compiler.previous_position; diff --git a/dust-lang/src/disassembler.rs b/dust-lang/src/disassembler.rs index 9e4bfef..aa4afd4 100644 --- a/dust-lang/src/disassembler.rs +++ b/dust-lang/src/disassembler.rs @@ -15,30 +15,22 @@ //! //! ```text //! ┌──────────────────────────────────────────────────────────────────────────────┐ -//! │ │ -//! │ │ -//! │ write_line("Hello, world!") │ -//! │ │ +//! │ dust │ //! │ 3 instructions, 1 constants, 0 locals, returns none │ //! │ │ //! │ Instructions │ //! │ ------------ │ -//! │ i BYTECODE OPERATION INFO TYPE POSITION │ -//! │--- -------- ------------- -------------------- ---------------- ------------ │ -//! │ 0 03 LOAD_CONSTANT R0 = C0 str (11, 26) │ -//! │ 1 1390117 CALL_NATIVE write_line(R0) (0, 27) │ -//! │ 2 18 RETURN (27, 27) │ -//! │┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈│ -//! │ Locals │ -//! │ ------ │ -//! │ i IDENTIFIER TYPE MUTABLE SCOPE REGISTER │ -//! │ --- ---------- ---------------- ------- ------- -------- │ +//! │ i POSITION OPERATION TYPE INFO │ +//! │ --- ---------- ------------- -------------- -------------------------------- │ +//! │ 0 (11, 24) LOAD_CONSTANT str R0 = C0 │ +//! │ 1 (0, 25) CALL_NATIVE none write_line(R0..R1) │ +//! │ 2 (25, 25) RETURN none │ //! │┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈│ //! │ Constants │ //! │ --------- │ -//! │ i VALUE │ -//! │ --- --------------- │ -//! │ 0 Hello, world! │ +//! │ i TYPE VALUE │ +//! │ --- ---------------- ----------------- │ +//! │ 0 str Hello, world! │ //! └──────────────────────────────────────────────────────────────────────────────┘ //! ``` use std::env::current_exe; @@ -207,6 +199,97 @@ impl<'a> Disassembler<'a> { self.push("", false, false, false, true); } + fn push_instruction_section(&mut self) { + for line in INSTRUCTION_HEADER { + self.push_header(line); + } + + for (index, (instruction, r#type, position)) in self.chunk.instructions().iter().enumerate() + { + let position = position.to_string(); + let operation = instruction.operation().to_string(); + let type_display = { + let mut type_string = r#type.to_string(); + + if type_string.len() > 14 { + type_string = format!("{type_string:.11}..."); + } + + type_string + }; + let info = instruction.disassembly_info(); + let instruction_display = + format!("{index:^3} {position:^10} {operation:13} {type_display:^14} {info:^32}"); + + self.push_details(&instruction_display); + } + } + + fn push_local_section(&mut self) { + for line in LOCAL_HEADER { + self.push_header(line); + } + + for ( + index, + Local { + identifier_index, + r#type, + scope, + is_mutable, + }, + ) in self.chunk.locals().iter().enumerate() + { + let identifier_display = self + .chunk + .constants() + .get(*identifier_index as usize) + .map(|value| value.to_string()) + .unwrap_or_else(|| "unknown".to_string()); + let type_display = r#type.to_string(); + let scope = scope.to_string(); + let local_display = format!( + "{index:^3} {scope:^7} {is_mutable:^7} {type_display:^32} {identifier_display:^16}" + ); + + self.push_details(&local_display); + } + } + + fn push_constant_section(&mut self) { + for line in CONSTANT_HEADER { + self.push_header(line); + } + + for (index, value) in self.chunk.constants().iter().enumerate() { + if let ConcreteValue::Function(chunk) = value { + let mut function_disassembler = chunk.disassembler().style(self.style); + + function_disassembler.indent = self.indent + 1; + + let function_disassembly = function_disassembler.disassemble(); + + self.output.push_str(&function_disassembly); + + continue; + } + + let type_display = value.r#type().to_string(); + let value_display = { + let mut value_string = value.to_string(); + + if value_string.len() > 15 { + value_string = format!("{value_string:.12}..."); + } + + value_string + }; + let constant_display = format!("{index:^3} {type_display:^16} {value_display:^17}"); + + self.push_details(&constant_display); + } + } + pub fn disassemble(mut self) -> String { let width = Disassembler::default_width(); let top_border = "┌".to_string() + &"─".repeat(width - 2) + "┐"; @@ -251,93 +334,18 @@ impl<'a> Disassembler<'a> { self.push_chunk_info(&info_line); self.push_empty(); - for line in INSTRUCTION_HEADER { - self.push_header(line); + if !self.chunk.is_empty() { + self.push_instruction_section(); } - for (index, (instruction, r#type, position)) in self.chunk.instructions().iter().enumerate() - { - let position = position.to_string(); - let operation = instruction.operation().to_string(); - let type_display = { - let mut type_string = r#type.to_string(); - - if type_string.len() > 14 { - type_string = format!("{type_string:.11}..."); - } - - type_string - }; - let info = instruction.disassembly_info(); - let instruction_display = - format!("{index:^3} {position:^10} {operation:13} {type_display:^14} {info:^32}"); - - self.push_details(&instruction_display); + if !self.chunk.locals().is_empty() { + self.push_border(§ion_border); + self.push_local_section(); } - self.push_border(§ion_border); - - for line in LOCAL_HEADER { - self.push_header(line); - } - - for ( - index, - Local { - identifier_index, - r#type, - scope, - is_mutable, - }, - ) in self.chunk.locals().iter().enumerate() - { - let identifier_display = self - .chunk - .constants() - .get(*identifier_index as usize) - .map(|value| value.to_string()) - .unwrap_or_else(|| "unknown".to_string()); - let type_display = r#type.to_string(); - let scope = scope.to_string(); - let local_display = format!( - "{index:^3} {scope:^7} {is_mutable:^7} {type_display:^32} {identifier_display:^16}" - ); - - self.push_details(&local_display); - } - - self.push_border(§ion_border); - - for line in CONSTANT_HEADER { - self.push_header(line); - } - - for (index, value) in self.chunk.constants().iter().enumerate() { - if let ConcreteValue::Function(chunk) = value { - let mut function_disassembler = chunk.disassembler().style(self.style); - - function_disassembler.indent = self.indent + 1; - - let function_disassembly = function_disassembler.disassemble(); - - self.output.push_str(&function_disassembly); - - continue; - } - - let type_display = value.r#type().to_string(); - let value_display = { - let mut value_string = value.to_string(); - - if value_string.len() > 15 { - value_string = format!("{value_string:.12}..."); - } - - value_string - }; - let constant_display = format!("{index:^3} {type_display:^16} {value_display:^17}"); - - self.push_details(&constant_display); + if !self.chunk.constants().is_empty() { + self.push_border(§ion_border); + self.push_constant_section(); } self.push_border(&bottom_border); diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index b9ae606..600d1a9 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -703,7 +703,11 @@ impl Instruction { let arguments_start = destination.index().saturating_sub(argument_count); let arguments_end = arguments_start + argument_count; - format!("{destination} = {function}(R{arguments_start}..R{arguments_end})") + if function.returns_value() { + format!("{destination} = {function}(R{arguments_start}..R{arguments_end})") + } else { + format!("{function}(R{arguments_start}..R{arguments_end})") + } } Operation::Return => { let Return {