From fd8eae872c6cc6acaa9c80480fa1918bff9bf0df Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 3 Mar 2025 22:52:53 -0500 Subject: [PATCH] Continue function calls; Implement some built-in functions --- dust-lang/src/chunk/disassembler.rs | 53 +++++---- dust-lang/src/chunk/mod.rs | 4 +- dust-lang/src/compiler/mod.rs | 134 +++++++++++++++-------- dust-lang/src/instruction/call.rs | 13 ++- dust-lang/src/instruction/call_native.rs | 68 +++--------- dust-lang/src/instruction/mod.rs | 25 +++-- dust-lang/src/native_function/io.rs | 36 ++++++ dust-lang/src/native_function/mod.rs | 50 ++++----- dust-lang/src/native_function/string.rs | 31 ++++++ dust-lang/src/vm/mod.rs | 11 +- dust-lang/src/vm/thread.rs | 24 ++-- examples/fibonacci.ds | 2 +- 12 files changed, 282 insertions(+), 169 deletions(-) create mode 100644 dust-lang/src/native_function/io.rs create mode 100644 dust-lang/src/native_function/string.rs diff --git a/dust-lang/src/chunk/disassembler.rs b/dust-lang/src/chunk/disassembler.rs index da763b6..220caac 100644 --- a/dust-lang/src/chunk/disassembler.rs +++ b/dust-lang/src/chunk/disassembler.rs @@ -65,18 +65,18 @@ const LOCAL_BORDERS: [&str; 3] = [ "╰─────┴────────────────┴──────────────────────────┴────────────┴───────┴───────╯", ]; -const ARGUMENT_LIST_COLUMNS: [(&str, usize); 2] = [("i", 5), ("REGISTERS", 21)]; +const ARGUMENT_LIST_COLUMNS: [(&str, usize); 3] = [("i", 5), ("REGISTERS", 21), ("TYPES", 21)]; const ARGUMENT_LIST_BORDERS: [&str; 3] = [ - "╭─────┬─────────────────────╮", - "├─────┼─────────────────────┤", - "╰─────┴─────────────────────╯", + "╭─────┬─────────────────────┬─────────────────────╮", + "├─────┼─────────────────────┼─────────────────────┤", + "╰─────┴─────────────────────┴─────────────────────╯", ]; -const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 5), ("TYPE", 26), ("VALUE", 26)]; +const CONSTANT_COLUMNS: [(&str, usize); 3] = [("ADDRESS", 9), ("TYPE", 26), ("VALUE", 26)]; const CONSTANT_BORDERS: [&str; 3] = [ - "╭─────┬──────────────────────────┬──────────────────────────╮", - "├─────┼──────────────────────────┼──────────────────────────┤", - "╰─────┴──────────────────────────┴──────────────────────────╯", + "╭─────────┬──────────────────────────┬──────────────────────────╮", + "├─────────┼──────────────────────────┼──────────────────────────┤", + "╰─────────┴──────────────────────────┴──────────────────────────╯", ]; const INDENTATION: &str = "│ "; @@ -280,7 +280,7 @@ impl<'a, W: Write> Disassembler<'a, W> { column_name_line.push('│'); self.write_center_border_bold("Instructions")?; self.write_center_border(INSTRUCTION_BORDERS[0])?; - self.write_center_border(&column_name_line)?; + self.write_center_border_bold(&column_name_line)?; self.write_center_border(INSTRUCTION_BORDERS[1])?; for (index, instruction) in self.chunk.instructions.iter().enumerate() { @@ -312,7 +312,7 @@ impl<'a, W: Write> Disassembler<'a, W> { column_name_line.push('│'); self.write_center_border_bold("Locals")?; self.write_center_border(LOCAL_BORDERS[0])?; - self.write_center_border(&column_name_line)?; + self.write_center_border_bold(&column_name_line)?; self.write_center_border(LOCAL_BORDERS[1])?; for ( @@ -367,7 +367,7 @@ impl<'a, W: Write> Disassembler<'a, W> { column_name_line.push('│'); self.write_center_border_bold("Constants")?; self.write_center_border(CONSTANT_BORDERS[0])?; - self.write_center_border(&column_name_line)?; + self.write_center_border_bold(&column_name_line)?; self.write_center_border(CONSTANT_BORDERS[1])?; for (index, value) in self.chunk.character_constants.iter().enumerate() { @@ -381,7 +381,9 @@ impl<'a, W: Write> Disassembler<'a, W> { value_string }; - let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); + let register_display = format!("C_CHAR_{index}"); + let constant_display = + format!("│{register_display:^9}│{type_display:^26}│{value_display:^26}│"); self.write_center_border(&constant_display)?; } @@ -397,7 +399,9 @@ impl<'a, W: Write> Disassembler<'a, W> { value_string }; - let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); + let register_display = format!("C_FLOAT_{index}"); + let constant_display = + format!("│{register_display:^9}│{type_display:^26}│{value_display:^26}│"); self.write_center_border(&constant_display)?; } @@ -413,7 +417,9 @@ impl<'a, W: Write> Disassembler<'a, W> { value_string }; - let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); + let register_display = format!("C_INT_{index}"); + let constant_display = + format!("│{register_display:^9}│{type_display:^26}│{value_display:^26}│"); self.write_center_border(&constant_display)?; } @@ -429,7 +435,9 @@ impl<'a, W: Write> Disassembler<'a, W> { value_string }; - let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); + let register_display = format!("C_STR_{index}"); + let constant_display = + format!("│{register_display:^9}│{type_display:^26}│{value_display:^26}│"); self.write_center_border(&constant_display)?; } @@ -449,16 +457,23 @@ impl<'a, W: Write> Disassembler<'a, W> { column_name_line.push('│'); self.write_center_border_bold("Argument Lists")?; self.write_center_border(ARGUMENT_LIST_BORDERS[0])?; - self.write_center_border(&column_name_line)?; + self.write_center_border_bold(&column_name_line)?; self.write_center_border(ARGUMENT_LIST_BORDERS[1])?; - for (index, argument_list) in self.chunk.argument_lists.iter().enumerate() { + for (index, (value_argument_list, type_argument_list)) in + self.chunk.argument_lists.iter().enumerate() + { let argument_list_display = format!( - "│{index:^5}│{:^21}│", - argument_list + "│{index:^5}│{:^21}│{:^21}│", + value_argument_list .iter() .map(|index| index.to_string()) .collect::>() + .join(", "), + type_argument_list + .iter() + .map(|r#type| r#type.to_string()) + .collect::>() .join(", ") ); diff --git a/dust-lang/src/chunk/mod.rs b/dust-lang/src/chunk/mod.rs index a16d807..3af6a51 100644 --- a/dust-lang/src/chunk/mod.rs +++ b/dust-lang/src/chunk/mod.rs @@ -24,7 +24,7 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; -use crate::{DustString, Function, FunctionType, Instruction, Span}; +use crate::{DustString, Function, FunctionType, Instruction, Span, Type}; /// Representation of a Dust program or function. /// @@ -42,7 +42,7 @@ pub struct Chunk { pub string_constants: Vec, pub locals: Vec, pub prototypes: Vec>, - pub argument_lists: Vec>, + pub argument_lists: Vec<(Vec, Vec)>, pub boolean_register_count: u16, pub byte_register_count: u16, diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index e813acc..0d2475d 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -110,7 +110,7 @@ pub struct Compiler<'src> { /// Lists of arguments for each function call. The integers represent the register of each /// argument. Note that the type of each argument is not stored, so the caller must check the /// function's type to determine the type of each argument. - argument_lists: Vec>, + argument_lists: Vec<(Vec, Vec)>, /// The first boolean register index that the compiler should use. This is used to avoid reusing /// the registers that are used for the function's arguments. @@ -357,7 +357,7 @@ impl<'src> Compiler<'src> { .find_map(|(instruction, r#type, _)| { if (instruction.operation() == Operation::LOAD_CONSTANT && instruction.b_type() == TypeCode::INTEGER) - || r#type == &Type::Integer + || instruction.yields_value() && r#type == &Type::Integer { Some(instruction.a_field() + 1) } else { @@ -402,7 +402,10 @@ impl<'src> Compiler<'src> { .iter() .rev() .find_map(|(instruction, _, _)| { - if instruction.operation() == Operation::LOAD_FUNCTION { + if matches!( + instruction.operation(), + Operation::LOAD_FUNCTION | Operation::LOAD_SELF + ) { Some(instruction.a_field() + 1) } else { None @@ -862,6 +865,8 @@ impl<'src> Compiler<'src> { Operation::LOAD_ENCODED | Operation::LOAD_LIST | Operation::LOAD_SELF + | Operation::CALL + | Operation::CALL_NATIVE | Operation::ADD | Operation::SUBTRACT | Operation::MULTIPLY @@ -1201,7 +1206,7 @@ impl<'src> Compiler<'src> { let local_index = if let Ok(local_index) = self.get_local_index(identifier) { local_index } else if let Some(native_function) = NativeFunction::from_str(identifier) { - return self.parse_call_native(native_function); + return self.parse_call_native(native_function, start_position); } else if self.function_name.as_deref() == Some(identifier) && !self.is_main { let destination = self.next_function_register(); let load_self = Instruction::load_self(destination, false); @@ -1656,41 +1661,6 @@ impl<'src> Compiler<'src> { Ok(()) } - fn parse_call_native(&mut self, function: NativeFunction) -> Result<(), CompileError> { - let start = self.previous_position.0; - let mut first_argument_index = None; - - self.expect(Token::LeftParenthesis)?; - - while !self.allow(Token::RightParenthesis)? { - self.parse_expression()?; - self.allow(Token::Comma)?; - - if first_argument_index.is_none() { - first_argument_index = Some(self.instructions.last().unwrap().0.a_field()); - } - } - - let end = self.previous_position.1; - let destination = match function.r#type().return_type.as_ref() { - Type::Boolean => self.next_boolean_register(), - Type::Byte => self.next_byte_register(), - Type::Character => self.next_character_register(), - Type::Float => self.next_float_register(), - Type::Integer => self.next_integer_register(), - Type::String => self.next_string_register(), - Type::None => 0, - _ => todo!(), - }; - let return_type = *function.r#type().return_type; - let call_native = - Instruction::call_native(destination, function, first_argument_index.unwrap_or(0)); - - self.emit_instruction(call_native, return_type, Span(start, end)); - - Ok(()) - } - fn parse_expression(&mut self) -> Result<(), CompileError> { self.parse(Precedence::None)?; @@ -2036,11 +2006,7 @@ impl<'src> Compiler<'src> { } let load_function = Instruction::load_function(destination, prototype_index, false); - let r#type = if identifier.is_some() { - Type::None - } else { - Type::Function(function_type) - }; + let r#type = Type::Function(function_type); self.emit_instruction(load_function, r#type, Span(function_start, function_end)); @@ -2072,7 +2038,11 @@ impl<'src> Compiler<'src> { }); } }; - let function_register = if last_instruction.operation() == Operation::LOAD_FUNCTION { + let last_operation = last_instruction.operation(); + let function_register = if matches!( + last_operation, + Operation::LOAD_FUNCTION | Operation::LOAD_SELF + ) { last_instruction.a_field() } else if last_instruction.operation() == Operation::MOVE { self.instructions.pop(); @@ -2086,7 +2056,9 @@ impl<'src> Compiler<'src> { }); }; - let mut argument_list = Vec::new(); + let type_argument_list = Vec::new(); + + let mut value_argument_list = Vec::new(); while !self.allow(Token::RightParenthesis)? { self.parse_expression()?; @@ -2102,12 +2074,13 @@ impl<'src> Compiler<'src> { _ => todo!(), }; - argument_list.push(argument_index); + value_argument_list.push(argument_index); } let argument_list_index = self.argument_lists.len() as u16; - self.argument_lists.push(argument_list); + self.argument_lists + .push((value_argument_list, type_argument_list)); let end = self.current_position.1; let destination = match function_return_type { @@ -2120,11 +2093,13 @@ impl<'src> Compiler<'src> { Type::String => self.next_string_register(), _ => todo!(), }; + let is_recursive = last_operation == Operation::LOAD_SELF; let call = Instruction::call( destination, function_register, argument_list_index, function_return_type.type_code(), + is_recursive, ); self.emit_instruction(call, function_return_type, Span(start, end)); @@ -2132,6 +2107,69 @@ impl<'src> Compiler<'src> { Ok(()) } + fn parse_call_native( + &mut self, + function: NativeFunction, + start: Span, + ) -> Result<(), CompileError> { + let mut type_argument_list = Vec::new(); + + if self.allow(Token::Less)? { + while !self.allow(Token::Greater)? { + let r#type = self.parse_type_from(self.current_token, self.current_position)?; + + type_argument_list.push(r#type); + self.advance()?; + + self.allow(Token::Comma)?; + } + } + + self.expect(Token::LeftParenthesis)?; + + let mut value_argument_list = Vec::new(); + + while !self.allow(Token::RightParenthesis)? { + self.parse_expression()?; + self.allow(Token::Comma)?; + + let argument_index = match self.get_last_instruction_type() { + Type::Boolean => self.next_boolean_register() - 1, + Type::Byte => self.next_byte_register() - 1, + Type::Character => self.next_character_register() - 1, + Type::Float => self.next_float_register() - 1, + Type::Integer => self.next_integer_register() - 1, + Type::String => self.next_string_register() - 1, + _ => todo!(), + }; + + value_argument_list.push(argument_index); + } + + let argument_list_index = self.argument_lists.len() as u16; + + self.argument_lists + .push((value_argument_list, type_argument_list)); + + let end = self.current_position.1; + let return_type = function.r#type().return_type.as_ref().clone(); + let destination = match return_type { + Type::None => 0, + Type::Boolean => self.next_boolean_register(), + Type::Byte => self.next_byte_register(), + Type::Character => self.next_character_register(), + Type::Float => self.next_float_register(), + Type::Integer => self.next_integer_register(), + Type::String => self.next_string_register(), + _ => todo!(), + }; + let call_native = Instruction::call_native(destination, function, argument_list_index); + + self.emit_instruction(call_native, return_type, Span(start.0, end)); + + Ok(()) + } + fn parse_semicolon(&mut self) -> Result<(), CompileError> { let (_, last_instruction_type, _) = self.instructions.last_mut().unwrap(); diff --git a/dust-lang/src/instruction/call.rs b/dust-lang/src/instruction/call.rs index 7ea459b..1cf1b77 100644 --- a/dust-lang/src/instruction/call.rs +++ b/dust-lang/src/instruction/call.rs @@ -9,6 +9,7 @@ pub struct Call { pub function_register: u16, pub argument_list_index: u16, pub return_type: TypeCode, + pub is_recursive: bool, } impl From for Call { @@ -17,12 +18,14 @@ impl From for Call { let function_register = instruction.b_field(); let argument_list_index = instruction.c_field(); let return_type = instruction.b_type(); + let is_recursive = instruction.b_is_constant(); Call { destination, function_register, argument_list_index, return_type, + is_recursive, } } } @@ -31,6 +34,7 @@ impl From for Instruction { fn from(call: Call) -> Self { let a_field = call.destination; let b_field = call.function_register; + let b_is_constant = call.is_recursive; let b_type = call.return_type; let c_field = call.argument_list_index; @@ -38,6 +42,7 @@ impl From for Instruction { operation: Operation::CALL, a_field, b_field, + b_is_constant, b_type, c_field, ..Default::default() @@ -53,15 +58,15 @@ impl Display for Call { function_register, argument_list_index, return_type, - .. + is_recursive: _, } = self; match *return_type { TypeCode::NONE => {} TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination} = ")?, TypeCode::BYTE => write!(f, "R_BYTE_{destination} = ")?, - TypeCode::CHARACTER => write!(f, "R_CHR_{destination} = ")?, - TypeCode::FLOAT => write!(f, "R_FLT_{destination} = ")?, + TypeCode::CHARACTER => write!(f, "R_CHAR_{destination} = ")?, + TypeCode::FLOAT => write!(f, "R_FLOAT_{destination} = ")?, TypeCode::INTEGER => write!(f, "R_INT_{destination} = ")?, TypeCode::STRING => write!(f, "R_STR_{destination} = ")?, TypeCode::LIST => write!(f, "R_LIST_{destination} = ")?, @@ -69,6 +74,6 @@ impl Display for Call { _ => unreachable!(), } - write!(f, "R_FN_{function_register}({argument_list_index})") + write!(f, "R_FN_{function_register}(ARGS_{argument_list_index})") } } diff --git a/dust-lang/src/instruction/call_native.rs b/dust-lang/src/instruction/call_native.rs index fe133f2..ed3986b 100644 --- a/dust-lang/src/instruction/call_native.rs +++ b/dust-lang/src/instruction/call_native.rs @@ -7,7 +7,7 @@ use super::InstructionFields; pub struct CallNative { pub destination: u16, pub function: NativeFunction, - pub first_argument_index: u16, + pub argument_list_index: u16, } impl From for CallNative { @@ -19,7 +19,7 @@ impl From for CallNative { CallNative { destination, function, - first_argument_index, + argument_list_index: first_argument_index, } } } @@ -29,7 +29,7 @@ impl From for Instruction { let operation = Operation::CALL_NATIVE; let a_field = call_native.destination; let b_field = call_native.function as u16; - let c_field = call_native.first_argument_index; + let c_field = call_native.argument_list_index; InstructionFields { operation, @@ -47,57 +47,23 @@ impl Display for CallNative { let CallNative { destination, function, - first_argument_index, + argument_list_index, } = self; - let argument_count = function.r#type().value_parameters.len() as u16; + let return_type = function.r#type().return_type; - if function.returns_value() { - write!(f, "R{destination} = ")?; + match *return_type { + Type::None => {} + Type::Boolean => write!(f, "R_BOOL_{destination} = ")?, + Type::Byte => write!(f, "R_BYTE_{destination} = ")?, + Type::Character => write!(f, "R_CHR_{destination} = ")?, + Type::Float => write!(f, "R_FLT_{destination} = ")?, + Type::Integer => write!(f, "R_INT_{destination} = ")?, + Type::String => write!(f, "R_STR_{destination} = ")?, + Type::List(_) => write!(f, "R_LIST_{destination} = ")?, + Type::Function(_) => write!(f, "R_FN_{destination} = ")?, + _ => unreachable!(), } - write!(f, "{function}")?; - - match argument_count { - 0 => { - write!(f, "()") - } - _ => { - let arguments_end = first_argument_index + argument_count - 1; - let arguments_index_range = *first_argument_index..=arguments_end; - let function_type = function.r#type(); - let argument_types = function_type.value_parameters.iter(); - - write!(f, "(")?; - - for (index, r#type) in arguments_index_range.zip(argument_types) { - match r#type { - Type::Boolean => { - write!(f, "R_BOOL_{index}") - } - Type::Byte => { - write!(f, "R_BYTE_{index}") - } - Type::Float => { - write!(f, "R_FLOAT_{index}") - } - Type::Integer => { - write!(f, "R_INT_{index}") - } - Type::String => { - write!(f, "R_STR_{index}") - } - unsupported => { - todo!("Support for {unsupported:?} arguments") - } - }?; - - if index != arguments_end { - write!(f, ", ")?; - } - } - - write!(f, ")") - } - } + write!(f, "{function}(ARGS_{argument_list_index})") } } diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index 22e41af..b36b16a 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -183,15 +183,15 @@ impl From<&Instruction> for InstructionFields { impl Default for InstructionFields { fn default() -> Self { InstructionFields { - operation: Operation::MOVE, + operation: Operation::NO_OP, a_field: 0, b_field: 0, c_field: 0, d_field: false, b_is_constant: false, c_is_constant: false, - b_type: TypeCode::BOOLEAN, - c_type: TypeCode::BOOLEAN, + b_type: TypeCode::NONE, + c_type: TypeCode::NONE, } } } @@ -278,9 +278,9 @@ impl Instruction { pub fn as_operand(&self) -> Operand { match self.operation() { Operation::MOVE => { - let Move { operand: to, .. } = Move::from(self); + let Move { operand, .. } = Move::from(self); - Operand::Register(to.index(), to.as_type()) + operand } Operation::LOAD_ENCODED => { let LoadEncoded { @@ -362,6 +362,15 @@ impl Instruction { Operand::Register(destination, left.as_type()) } + Operation::CALL => { + let Call { + destination, + return_type, + .. + } = Call::from(*self); + + Operand::Register(destination, return_type) + } unsupported => todo!("Support {unsupported}"), } } @@ -546,24 +555,26 @@ impl Instruction { function_register: u16, argument_list_register: u16, return_type: TypeCode, + is_recursive: bool, ) -> Instruction { Instruction::from(Call { destination, function_register, argument_list_index: argument_list_register, return_type, + is_recursive, }) } pub fn call_native( destination: u16, function: NativeFunction, - first_argument_index: u16, + argument_list_index: u16, ) -> Instruction { Instruction::from(CallNative { destination, function, - first_argument_index, + argument_list_index, }) } diff --git a/dust-lang/src/native_function/io.rs b/dust-lang/src/native_function/io.rs new file mode 100644 index 0000000..3c36d3c --- /dev/null +++ b/dust-lang/src/native_function/io.rs @@ -0,0 +1,36 @@ +use std::io::{stdout, Write}; + +use crate::{instruction::CallNative, vm::Thread, Instruction, Type}; + +pub fn write_line(instruction: Instruction, thread: &mut Thread) { + let CallNative { + destination: _, + function, + argument_list_index, + } = CallNative::from(instruction); + + let current_frame = thread.current_frame(); + let current_registers = thread.current_registers(); + let arguments = current_frame.get_argument_list(argument_list_index); + let mut stdout = stdout(); + + for (argument_index, argument_type) in arguments + .0 + .iter() + .zip(function.r#type().value_parameters.iter()) + { + match argument_type { + Type::String => { + let string = current_registers + .strings + .get(*argument_index as usize) + .as_value(); + + stdout.write_all(string.as_bytes()).unwrap(); + } + _ => unreachable!(), + } + } + + stdout.write_all(b"\n").unwrap(); +} diff --git a/dust-lang/src/native_function/mod.rs b/dust-lang/src/native_function/mod.rs index fca05fc..f8e5f2f 100644 --- a/dust-lang/src/native_function/mod.rs +++ b/dust-lang/src/native_function/mod.rs @@ -2,15 +2,14 @@ //! //! Native functions are used to implement features that are not possible to implement in Dust //! itself or that are more efficient to implement in Rust. +mod io; +mod string; -use std::{ - fmt::{self, Display, Formatter}, - ops::Range, -}; +use std::fmt::{self, Display, Formatter}; use serde::{Deserialize, Serialize}; -use crate::{vm::Thread, FunctionType, Type}; +use crate::{vm::Thread, FunctionType, Instruction, Type}; macro_rules! define_native_function { ($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => { @@ -25,15 +24,10 @@ macro_rules! define_native_function { } impl NativeFunction { - pub fn call( - &self, - thread: &mut Thread, - destination: usize, - argument_range: Range, - ) { + pub fn call(&self, instruction: Instruction, thread: &mut Thread) { match self { $( - NativeFunction::$name => $function(thread, destination, argument_range), + NativeFunction::$name => $function(instruction, thread), )* } } @@ -127,18 +121,18 @@ define_native_function! { // assert::panic // ), - // // Type conversion + // Type conversion // (Parse, 4_u8, "parse", true), // (ToByte, 5_u8, "to_byte", true), // (ToFloat, 6_u8, "to_float", true), // (ToInteger, 7_u8, "to_integer", true), - // ( - // ToString, - // 8, - // "to_string", - // FunctionType::new([], [Type::Any], Type::String), - // string::to_string - // ), + ( + ToString, + 8, + "to_string", + FunctionType::new([], [Type::Any], Type::String), + string::to_string + ), // // List and string // (All, 9_u8, "all", true), @@ -209,13 +203,13 @@ define_native_function! { // io::write // ), // (WriteFile, 56_u8, "write_file", false), - // ( - // WriteLine, - // 57, - // "write_line", - // FunctionType::new([], [Type::String], Type::None), - // io::write_line - // ), + ( + WriteLine, + 57, + "write_line", + FunctionType::new([], [Type::String], Type::None), + io::write_line + ), // // Random // ( @@ -236,4 +230,4 @@ define_native_function! { ) } -fn spawn(_: &mut Thread, _: usize, _: Range) {} +fn spawn(_: Instruction, _: &mut Thread) {} diff --git a/dust-lang/src/native_function/string.rs b/dust-lang/src/native_function/string.rs new file mode 100644 index 0000000..1958ce0 --- /dev/null +++ b/dust-lang/src/native_function/string.rs @@ -0,0 +1,31 @@ +use crate::{instruction::CallNative, vm::Thread, DustString, Instruction, Type}; + +pub fn to_string(instruction: Instruction, thread: &mut Thread) { + let CallNative { + destination, + function: _, + argument_list_index, + } = CallNative::from(instruction); + + let current_frame = thread.current_frame(); + let current_registers = thread.current_registers(); + let arguments = current_frame.get_argument_list(argument_list_index); + + let string = match arguments.1[0] { + Type::Integer => { + let integer = current_registers + .integers + .get(arguments.0[0] as usize) + .as_value(); + + DustString::from(integer.to_string()) + } + _ => unreachable!(), + }; + + *thread + .current_registers_mut() + .strings + .get_mut(destination as usize) + .as_value_mut() = DustString::from(string); +} diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index 300e31d..960c38e 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -1,5 +1,4 @@ //! Virtual machine and errors -// mod action; mod thread; use std::{ @@ -13,7 +12,7 @@ pub use thread::Thread; use crossbeam_channel::bounded; use tracing::{span, Level}; -use crate::{compile, AbstractList, Chunk, DustError, DustString, Function, Value}; +use crate::{compile, AbstractList, Chunk, DustError, DustString, Function, Type, Value}; pub fn run(source: &str) -> Result, DustError> { let chunk = compile(source)?; @@ -106,6 +105,14 @@ impl CallFrame { unsafe { self.chunk.string_constants.get_unchecked(constant_index) } } } + + pub fn get_argument_list(&self, index: u16) -> &(Vec, Vec) { + if cfg!(debug_assertions) { + self.chunk.argument_lists.get(index as usize).unwrap() + } else { + unsafe { self.chunk.argument_lists.get_unchecked(index as usize) } + } + } } #[derive(Debug)] diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index d514fa0..debdbba 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -5,7 +5,7 @@ use tracing::{info, trace}; use crate::{ instruction::TypeCode, vm::{CallFrame, Pointer}, - AbstractList, Chunk, DustString, Operation, Span, Type, Value, + AbstractList, Chunk, DustString, NativeFunction, Operation, Span, Type, Value, }; use super::RegisterTable; @@ -1274,16 +1274,21 @@ impl Thread { let destination = instruction.a_field(); let function_register = instruction.b_field(); let argument_list_register = instruction.c_field(); + let is_recursive = instruction.b_is_constant(); let function = registers .functions .get(function_register as usize) .as_value(); - let function_prototype = current_frame - .chunk - .prototypes - .get(function.prototype_index as usize) - .unwrap(); + let function_prototype = if is_recursive { + ¤t_frame.chunk + } else { + current_frame + .chunk + .prototypes + .get(function.prototype_index as usize) + .unwrap() + }; let argument_list = current_frame .chunk .argument_lists @@ -1300,7 +1305,7 @@ impl Thread { .r#type .value_parameters .iter() - .zip(argument_list.iter()) + .zip(argument_list.0.iter()) { let register_index = *register_index as usize; @@ -1351,6 +1356,11 @@ impl Thread { .collect::() ); } + Operation::CALL_NATIVE => { + let function = NativeFunction::from(instruction.b_field()); + + function.call(instruction, &mut self); + } Operation::JUMP => { let offset = instruction.b_field() as usize; let is_positive = instruction.c_field() != 0; diff --git a/examples/fibonacci.ds b/examples/fibonacci.ds index 7261e19..8b83500 100644 --- a/examples/fibonacci.ds +++ b/examples/fibonacci.ds @@ -5,4 +5,4 @@ fn fib (n: int) -> int { fib(n - 1) + fib(n - 2) } -write_line(fib(25)) +write_line(to_string(fib(25)))