From 71a92c078b34c1019468ab3de7fb5eea862de079 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 8 Feb 2025 05:56:49 -0500 Subject: [PATCH] Add formatting disassembly output into JSON or TOML --- Cargo.lock | 85 +++++++++++ dust-cli/Cargo.toml | 2 + dust-cli/src/main.rs | 207 +++++++++++++++----------- dust-lang/src/chunk/local.rs | 6 +- dust-lang/src/chunk/mod.rs | 30 +++- dust-lang/src/compiler/mod.rs | 102 ++++++------- dust-lang/src/instruction/mod.rs | 3 +- dust-lang/src/native_function/mod.rs | 53 +------ dust-lang/src/type.rs | 114 +++++++------- dust-lang/src/value/concrete_value.rs | 1 + dust-lang/src/value/mod.rs | 10 +- dust-lang/src/value/range_value.rs | 4 +- dust-lang/tests/math/add.rs | 25 +--- dust-lang/tests/values.rs | 75 ++-------- 14 files changed, 385 insertions(+), 332 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b599be5..0a5737a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,6 +96,15 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -328,11 +337,13 @@ dependencies = [ name = "dust-cli" version = "0.5.0" dependencies = [ + "basic-toml", "clap 4.5.20", "color-print", "dust-lang", "postcard", "serde_json", + "toml", "tracing", "tracing-subscriber", ] @@ -360,6 +371,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.10" @@ -398,6 +415,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + [[package]] name = "heapless" version = "0.7.17" @@ -427,6 +450,16 @@ dependencies = [ "libc", ] +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -792,6 +825,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -899,6 +941,40 @@ dependencies = [ "serde_json", ] +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tracing" version = "0.1.41" @@ -1237,6 +1313,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" +dependencies = [ + "memchr", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/dust-cli/Cargo.toml b/dust-cli/Cargo.toml index 2102ef1..3a3fb82 100644 --- a/dust-cli/Cargo.toml +++ b/dust-cli/Cargo.toml @@ -13,6 +13,7 @@ name = "dust" path = "src/main.rs" [dependencies] +basic-toml = "0.1.9" clap = { version = "4.5.14", features = [ "cargo", "color", @@ -24,5 +25,6 @@ color-print = "0.3.7" dust-lang = { path = "../dust-lang" } postcard = "1.0.10" serde_json = "1.0.133" +toml = "0.8.20" tracing = "0.1.41" tracing-subscriber = "0.3.19" diff --git a/dust-cli/src/main.rs b/dust-cli/src/main.rs index 10a5ca6..e7e9385 100644 --- a/dust-cli/src/main.rs +++ b/dust-cli/src/main.rs @@ -1,19 +1,19 @@ use std::{ fs::read_to_string, - io::{self, stdout, Read}, + io::{self, Read, stdout}, path::PathBuf, time::{Duration, Instant}, }; use clap::{ - builder::{styling::AnsiColor, Styles}, + Args, ColorChoice, Error, Parser, Subcommand, ValueEnum, ValueHint, + builder::{Styles, styling::AnsiColor}, crate_authors, crate_description, crate_version, error::ErrorKind, - Args, ColorChoice, Error, Parser, Subcommand, ValueHint, }; use color_print::{cformat, cstr}; use dust_lang::{CompileError, Compiler, DustError, DustString, Lexer, Span, Token, Vm}; -use tracing::{subscriber::set_global_default, Level}; +use tracing::{Level, subscriber::set_global_default}; use tracing_subscriber::FmtSubscriber; const ABOUT: &str = cstr!( @@ -158,11 +158,14 @@ enum Mode { style: bool, /// Custom program name, overrides the file name - #[arg(long)] + #[arg(short, long)] name: Option, #[command(flatten)] input: Input, + + #[arg(short, long, default_value = "cli")] + format: Format, }, /// Lex the source code and print the tokens @@ -180,6 +183,13 @@ enum Mode { }, } +#[derive(ValueEnum, Clone, Copy)] +enum Format { + Cli, + Json, + Toml, +} + fn get_source_and_file_name(input: Input) -> (String, Option) { if let Some(path) = input.file { let source = read_to_string(&path).expect("Failed to read source file"); @@ -221,88 +231,6 @@ fn main() { set_global_default(subscriber).expect("Failed to set tracing subscriber"); - if let Mode::Disassemble { style, name, input } = mode { - let (source, file_name) = get_source_and_file_name(input); - let lexer = Lexer::new(&source); - let program_name = name.or(file_name); - let mut compiler = match Compiler::new(lexer, program_name, true) { - Ok(compiler) => compiler, - Err(error) => { - handle_compile_error(error, &source); - - return; - } - }; - - match compiler.compile() { - Ok(()) => {} - Err(error) => { - handle_compile_error(error, &source); - - return; - } - } - - let chunk = compiler.finish(); - let mut stdout = stdout().lock(); - - chunk - .disassembler(&mut stdout) - .width(65) - .style(style) - .source(&source) - .disassemble() - .expect("Failed to write disassembly to stdout"); - - return; - } - - if let Mode::Tokenize { input, .. } = mode { - let (source, _) = get_source_and_file_name(input); - let mut lexer = Lexer::new(&source); - let mut next_token = || -> Option<(Token, Span, bool)> { - match lexer.next_token() { - Ok((token, position)) => Some((token, position, lexer.is_eof())), - Err(error) => { - let report = DustError::compile(CompileError::Lex(error), &source).report(); - - eprintln!("{report}"); - - None - } - } - }; - - println!("{:^66}", "Tokens"); - - for _ in 0..66 { - print!("-"); - } - - println!(); - println!("{:^21}|{:^22}|{:^22}", "Kind", "Value", "Position"); - - for _ in 0..66 { - print!("-"); - } - - println!(); - - while let Some((token, position, is_eof)) = next_token() { - if is_eof { - break; - } - - let token_kind = token.kind().to_string(); - let token = token.to_string(); - let position = position.to_string(); - - println!("{token_kind:^21}|{token:^22}|{position:^22}"); - } - - return; - } - if let Mode::Run(Run { time, no_output, @@ -352,6 +280,111 @@ fn main() { print_time("Run Time", run_time); print_time("Total Time", total_time); } + + return; + } + + if let Mode::Disassemble { + style, + name, + input, + format, + } = mode + { + let (source, file_name) = get_source_and_file_name(input); + let lexer = Lexer::new(&source); + let program_name = name.or(file_name); + let mut compiler = match Compiler::new(lexer, program_name, true) { + Ok(compiler) => compiler, + Err(error) => { + handle_compile_error(error, &source); + + return; + } + }; + + match compiler.compile() { + Ok(()) => {} + Err(error) => { + handle_compile_error(error, &source); + + return; + } + } + + let chunk = compiler.finish(); + let mut stdout = stdout().lock(); + + match format { + Format::Cli => { + chunk + .disassembler(&mut stdout) + .width(65) + .style(style) + .source(&source) + .disassemble() + .expect("Failed to write disassembly to stdout"); + } + Format::Json => { + let json = serde_json::to_string_pretty(&chunk) + .expect("Failed to serialize chunk to JSON"); + + println!("{json}"); + } + Format::Toml => { + let toml = basic_toml::to_string(&chunk) + .inspect_err(|error| println!("{:?}", error.to_string())) + .expect("Failed to serialize chunk to TOML"); + + println!("{toml}"); + } + } + + return; + } + + if let Mode::Tokenize { input, .. } = mode { + let (source, _) = get_source_and_file_name(input); + let mut lexer = Lexer::new(&source); + let mut next_token = || -> Option<(Token, Span, bool)> { + match lexer.next_token() { + Ok((token, position)) => Some((token, position, lexer.is_eof())), + Err(error) => { + let report = DustError::compile(CompileError::Lex(error), &source).report(); + + eprintln!("{report}"); + + None + } + } + }; + + println!("{:^66}", "Tokens"); + + for _ in 0..66 { + print!("-"); + } + + println!(); + println!("{:^21}|{:^22}|{:^22}", "Kind", "Value", "Position"); + + for _ in 0..66 { + print!("-"); + } + + println!(); + + while let Some((token, position, is_eof)) = next_token() { + if is_eof { + break; + } + + let token_kind = token.kind().to_string(); + let token = token.to_string(); + let position = position.to_string(); + + println!("{token_kind:^21}|{token:^22}|{position:^22}"); + } } } diff --git a/dust-lang/src/chunk/local.rs b/dust-lang/src/chunk/local.rs index 2bd15f8..bcc558d 100644 --- a/dust-lang/src/chunk/local.rs +++ b/dust-lang/src/chunk/local.rs @@ -13,12 +13,12 @@ pub struct Local { /// Index of the register where the variable's value is stored. pub register_index: u16, - /// Type of the variable's value. - pub r#type: Type, - /// Whether the local is mutable. pub is_mutable: bool, + /// Type of the variable's value. + pub r#type: Type, + /// Scope where the variable was declared. pub scope: Scope, } diff --git a/dust-lang/src/chunk/mod.rs b/dust-lang/src/chunk/mod.rs index dda8ed4..8f3c700 100644 --- a/dust-lang/src/chunk/mod.rs +++ b/dust-lang/src/chunk/mod.rs @@ -20,19 +20,20 @@ mod scope; pub use disassembler::Disassembler; pub use local::Local; pub use scope::Scope; +use serde::ser::SerializeStruct; use std::fmt::{self, Debug, Display, Formatter, Write as FmtWrite}; use std::io::Write; use std::sync::Arc; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, Serializer}; use crate::{ConcreteValue, DustString, Function, FunctionType, Instruction, Span}; /// Representation of a Dust program or function. /// /// See the [module-level documentation](index.html) for more information. -#[derive(Clone, Default, PartialOrd, Serialize, Deserialize)] +#[derive(Clone, Default, PartialOrd, Deserialize)] pub struct Chunk { pub name: Option, pub r#type: FunctionType, @@ -114,3 +115,28 @@ impl PartialEq for Chunk { && self.prototypes == other.prototypes } } + +impl Serialize for Chunk { + fn serialize(&self, serializer: S) -> Result { + let mut state = serializer.serialize_struct("Chunk", 11)?; + + state.serialize_field("name", &self.name)?; + state.serialize_field("boolean_register_count", &self.boolean_register_count)?; + state.serialize_field("byte_register_count", &self.byte_register_count)?; + state.serialize_field("character_register_count", &self.character_register_count)?; + state.serialize_field("float_register_count", &self.float_register_count)?; + state.serialize_field("integer_register_count", &self.integer_register_count)?; + state.serialize_field("string_register_count", &self.string_register_count)?; + state.serialize_field("list_register_count", &self.list_register_count)?; + state.serialize_field("function_register_count", &self.function_register_count)?; + state.serialize_field("prototype_index", &self.prototype_index)?; + state.serialize_field("instructions", &self.instructions)?; + state.serialize_field("positions", &self.positions)?; + state.serialize_field("prototypes", &self.prototypes)?; + state.serialize_field("locals", &self.locals)?; + state.serialize_field("constants", &self.constants)?; + state.serialize_field("type", &self.r#type)?; + + state.end() + } +} diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index 775a5ab..0fca7aa 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -159,11 +159,7 @@ impl<'src> Compiler<'src> { Ok(Compiler { function_name, - r#type: FunctionType { - type_parameters: Vec::with_capacity(0), - value_parameters: Vec::with_capacity(0), - return_type: Type::None, - }, + r#type: FunctionType::default(), instructions: Vec::new(), constants: Vec::new(), locals: Vec::new(), @@ -268,12 +264,14 @@ impl<'src> Compiler<'src> { self.instructions .iter() .rev() - .find_map(|(instruction, r#type, _)| { - if instruction.b_type() == TypeCode::BOOLEAN && instruction.yields_value() { - Some(instruction.a_field() + 1) - } else { - None + .find_map(|(instruction, _, _)| { + if instruction.operation() == Operation::LOAD_ENCODED + && instruction.b_type() == TypeCode::BOOLEAN + { + return Some(instruction.a_field() + 1); } + + None }) .unwrap_or(self.minimum_boolean_register) } @@ -283,15 +281,13 @@ impl<'src> Compiler<'src> { .iter() .rev() .find_map(|(instruction, _, _)| { - if instruction.operation() == Operation::LOAD_ENCODED { - if instruction.b_type() == TypeCode::BYTE { - Some(instruction.a_field() + 1) - } else { - None - } - } else { - None + if instruction.operation() == Operation::LOAD_ENCODED + && instruction.b_type() == TypeCode::BYTE + { + return Some(instruction.a_field() + 1); } + + None }) .unwrap_or(self.minimum_byte_register) } @@ -332,7 +328,9 @@ impl<'src> Compiler<'src> { .iter() .rev() .find_map(|(instruction, r#type, _)| { - if r#type == &Type::Integer { + if r#type == &Type::Integer + || (instruction.b_type() == TypeCode::INTEGER && instruction.yields_value()) + { Some(instruction.a_field() + 1) } else { None @@ -346,9 +344,8 @@ impl<'src> Compiler<'src> { .iter() .rev() .find_map(|(instruction, r#type, _)| { - if instruction.b_type() == TypeCode::STRING - && r#type == &Type::String - && instruction.yields_value() + if r#type == &Type::String + || (instruction.b_type() == TypeCode::STRING && instruction.yields_value()) { Some(instruction.a_field() + 1) } else { @@ -363,7 +360,7 @@ impl<'src> Compiler<'src> { .iter() .rev() .find_map(|(instruction, r#type, _)| { - if let Type::List(_) = r#type { + if let Type::List { .. } = r#type { if instruction.yields_value() { Some(instruction.a_field() + 1) } else { @@ -546,7 +543,7 @@ impl<'src> Compiler<'src> { /// /// If [`Self::type`] is already set, it will check if the given [Type] is compatible. fn update_return_type(&mut self, new_return_type: Type) -> Result<(), CompileError> { - if self.r#type.return_type != Type::None { + if self.r#type.return_type.as_ref() != &Type::None { self.r#type .return_type .check(&new_return_type) @@ -556,7 +553,7 @@ impl<'src> Compiler<'src> { })?; } - self.r#type.return_type = new_return_type; + *self.r#type.return_type.as_mut() = new_return_type; Ok(()) } @@ -900,7 +897,7 @@ impl<'src> Compiler<'src> { match left_type { Type::Boolean => self.next_boolean_register(), Type::Byte => self.next_byte_register(), - Type::Character => self.next_character_register(), + Type::Character => self.next_string_register(), Type::Float => self.next_float_register(), Type::Integer => self.next_integer_register(), Type::String => self.next_string_register(), @@ -1274,7 +1271,7 @@ impl<'src> Compiler<'src> { if used_boolean_registers > 1 { let close = Instruction::close( next_boolean_register, - next_boolean_register + used_boolean_registers, + self.next_boolean_register() - 2, TypeCode::BOOLEAN, ); @@ -1282,12 +1279,12 @@ impl<'src> Compiler<'src> { } } Type::Byte => { - let used_byte_registers = self.next_byte_register() - 1 - next_byte_register; + let used_byte_registers = self.next_byte_register() - next_byte_register; if used_byte_registers > 1 { let close = Instruction::close( next_byte_register, - next_byte_register + used_byte_registers, + self.next_byte_register() - 2, TypeCode::BYTE, ); @@ -1296,12 +1293,12 @@ impl<'src> Compiler<'src> { } Type::Character => { let used_character_registers = - self.next_character_register() - 1 - next_character_register; + self.next_character_register() - next_character_register; if used_character_registers > 1 { let close = Instruction::close( next_character_register, - next_character_register + used_character_registers, + self.next_character_register() - 2, TypeCode::CHARACTER, ); @@ -1309,12 +1306,12 @@ impl<'src> Compiler<'src> { } } Type::Float => { - let used_float_registers = self.next_float_register() - 1 - next_float_register; + let used_float_registers = self.next_float_register() - next_float_register; if used_float_registers > 1 { let close = Instruction::close( next_float_register, - next_float_register + used_float_registers, + self.next_float_register() - 2, TypeCode::FLOAT, ); @@ -1323,12 +1320,12 @@ impl<'src> Compiler<'src> { } Type::Integer => { let used_integer_registers = - self.next_integer_register() - 1 - next_integer_register; + self.next_integer_register() - next_integer_register; if used_integer_registers > 1 { let close = Instruction::close( next_integer_register, - next_integer_register + used_integer_registers, + self.next_integer_register() - 2, TypeCode::INTEGER, ); @@ -1336,20 +1333,19 @@ impl<'src> Compiler<'src> { } } Type::String => { - let used_string_registers = - self.next_string_register() - 1 - next_string_register; + let used_string_registers = self.next_string_register() - next_string_register; if used_string_registers > 1 { let close = Instruction::close( next_string_register, - next_string_register + used_string_registers, + self.next_string_register() - 2, TypeCode::STRING, ); self.emit_instruction(close, Type::None, self.current_position); } } - Type::List(_) => { + Type::List { .. } => { let used_list_registers = self.next_list_register() - next_list_register; if used_list_registers > 1 { @@ -1374,7 +1370,7 @@ impl<'src> Compiler<'src> { Type::Float => self.next_float_register().saturating_sub(1), Type::Integer => self.next_integer_register().saturating_sub(1), Type::String => self.next_string_register().saturating_sub(1), - Type::List(_) => self.next_list_register().saturating_sub(1), + Type::List { .. } => self.next_list_register().saturating_sub(1), _ => todo!(), }; let destination = self.next_list_register(); @@ -1595,7 +1591,7 @@ impl<'src> Compiler<'src> { } let end = self.previous_position.1; - let destination = match function.r#type().return_type { + 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(), @@ -1605,7 +1601,7 @@ impl<'src> Compiler<'src> { Type::None => 0, _ => todo!(), }; - let return_type = function.r#type().return_type; + let return_type = *function.r#type().return_type; let call_native = Instruction::from(CallNative { destination, function, @@ -1865,7 +1861,7 @@ impl<'src> Compiler<'src> { function_compiler.prototype_index = self.prototypes.len() as u16; - let mut value_parameters: Vec<(u16, Type)> = Vec::with_capacity(3); + let mut value_parameters = Vec::with_capacity(3); while !function_compiler.allow(Token::RightParenthesis)? { let is_mutable = function_compiler.allow(Token::Mut)?; @@ -1899,7 +1895,7 @@ impl<'src> Compiler<'src> { Type::String => function_compiler.next_string_register(), _ => todo!(), }; - let (_, identifier_index) = function_compiler.declare_local( + function_compiler.declare_local( parameter, local_register_index, r#type.clone(), @@ -1917,7 +1913,7 @@ impl<'src> Compiler<'src> { _ => {} } - value_parameters.push((identifier_index, r#type)); + value_parameters.push(r#type); function_compiler.allow(Token::Comma)?; } @@ -1933,11 +1929,7 @@ impl<'src> Compiler<'src> { } else { Type::None }; - let function_type = FunctionType { - type_parameters: Vec::with_capacity(0), - value_parameters, - return_type, - }; + let function_type = FunctionType::new([], value_parameters, return_type); function_compiler.r#type = function_type.clone(); @@ -1963,7 +1955,7 @@ impl<'src> Compiler<'src> { self.declare_local( identifier, destination, - Type::function(function_type.clone()), + Type::Function(function_type.clone()), false, self.current_scope, ); @@ -1973,7 +1965,7 @@ impl<'src> Compiler<'src> { self.emit_instruction( load_function, - Type::function(function_type), + Type::Function(function_type), Span(function_start, function_end), ); @@ -1995,7 +1987,7 @@ impl<'src> Compiler<'src> { if !matches!( last_instruction_type, - Type::Function(_) | Type::SelfFunction + Type::Function { .. } | Type::SelfFunction ) { return Err(CompileError::ExpectedFunction { found: self.previous_token.to_owned(), @@ -2006,8 +1998,8 @@ impl<'src> Compiler<'src> { let function_register = last_instruction.a_field(); let function_return_type = match last_instruction_type { - Type::Function(function_type) => function_type.return_type.clone(), - Type::SelfFunction => self.r#type.return_type.clone(), + Type::Function(function_type) => *function_type.return_type.clone(), + Type::SelfFunction => *self.r#type.return_type.clone(), _ => { return Err(CompileError::ExpectedFunction { found: self.previous_token.to_owned(), diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index c288de2..3f4cc67 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -597,7 +597,8 @@ impl Instruction { function.returns_value() } - Operation::EQUAL + Operation::CLOSE + | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL | Operation::TEST diff --git a/dust-lang/src/native_function/mod.rs b/dust-lang/src/native_function/mod.rs index 136539f..928d7ab 100644 --- a/dust-lang/src/native_function/mod.rs +++ b/dust-lang/src/native_function/mod.rs @@ -74,7 +74,7 @@ macro_rules! define_native_function { pub fn returns_value(&self) -> bool { match self { $( - NativeFunction::$name => $type.return_type != Type::None, + NativeFunction::$name => $type.return_type.as_ref() != &Type::None, )* } } @@ -134,11 +134,7 @@ define_native_function! { Panic, 3, "panic", - FunctionType { - type_parameters: Vec::with_capacity(0), - value_parameters: Vec::with_capacity(0), - return_type: Type::None - }, + FunctionType::new([], [], Type::None), assert::panic ), @@ -151,11 +147,7 @@ define_native_function! { ToString, 8, "to_string", - FunctionType { - type_parameters: Vec::with_capacity(0), - value_parameters: vec![(0, Type::Any)], - return_type: Type::String - }, + FunctionType::new([], [Type::Any], Type::String), string::to_string ), @@ -212,11 +204,7 @@ define_native_function! { ReadLine, 50, "read_line", - FunctionType { - type_parameters: Vec::with_capacity(0), - value_parameters: Vec::with_capacity(0), - return_type: Type::String - }, + FunctionType::new([], [], Type::String), io::read_line ), // (ReadTo, 51_u8, "read_to", false), @@ -228,11 +216,7 @@ define_native_function! { Write, 55, "write", - FunctionType { - type_parameters: Vec::with_capacity(0), - value_parameters: vec![(0, Type::String)], - return_type: Type::None - }, + FunctionType::new([], [Type::Any], Type::None), io::write ), // (WriteFile, 56_u8, "write_file", false), @@ -240,11 +224,7 @@ define_native_function! { WriteLine, 57, "write_line", - FunctionType { - type_parameters: Vec::with_capacity(0), - value_parameters: vec![(0, Type::String)], - return_type: Type::None - }, + FunctionType::new([], [Type::Any], Type::None), io::write_line ), @@ -253,11 +233,7 @@ define_native_function! { RandomInteger, 58, "random_int", - FunctionType { - type_parameters: Vec::with_capacity(0), - value_parameters: vec![(0, Type::Integer), (1, Type::Integer)], - return_type: Type::Integer - }, + FunctionType::new([], [Type::Integer, Type::Integer], Type::Integer), random::random_int ), @@ -266,20 +242,7 @@ define_native_function! { Spawn, 60, "spawn", - FunctionType { - type_parameters: Vec::with_capacity(0), - value_parameters: vec![ - ( - 0, - Type::Function(Box::new(FunctionType { - type_parameters: Vec::with_capacity(0), - value_parameters: Vec::with_capacity(0), - return_type: Type::Any - })) - ) - ], - return_type: Type::None - }, + FunctionType::new([], [ Type::function([], [], Type::Any)], Type::None), thread::spawn ) } diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index b70f320..1786456 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -10,7 +10,8 @@ use serde::{Deserialize, Serialize}; use crate::instruction::TypeCode; /// Description of a kind of value. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(tag = "Type", content = "Value")] pub enum Type { Any, Boolean, @@ -18,31 +19,31 @@ pub enum Type { Character, Enum(EnumType), Float, - Function(Box), - Generic { - identifier_index: u8, - concrete_type: Option>, - }, + Function(FunctionType), + Generic(GenericType), Integer, List(TypeCode), - Map { - pairs: Vec<(u8, Type)>, - }, + Map(Vec), + #[default] None, - Range { - r#type: Box, - }, + Range(Box), SelfFunction, String, Struct(StructType), - Tuple { - fields: Vec, - }, + Tuple(Vec), } impl Type { - pub fn function(function_type: FunctionType) -> Self { - Type::Function(Box::new(function_type)) + pub fn function>, U: Into>>( + type_parameters: T, + value_parameters: U, + return_type: Type, + ) -> Self { + Type::Function(FunctionType { + type_parameters: type_parameters.into(), + value_parameters: value_parameters.into(), + return_type: Box::new(return_type), + }) } pub fn type_code(&self) -> TypeCode { @@ -54,18 +55,18 @@ impl Type { Type::Integer => TypeCode::INTEGER, Type::None => TypeCode::NONE, Type::String => TypeCode::STRING, - Type::List(_) => TypeCode::LIST, - Type::Function(_) => TypeCode::FUNCTION, + Type::List { .. } => TypeCode::LIST, + Type::Function { .. } => TypeCode::FUNCTION, _ => todo!(), } } /// Returns a concrete type, either the type itself or the concrete type of a generic type. pub fn concrete_type(&self) -> &Type { - if let Type::Generic { + if let Type::Generic(GenericType { concrete_type: Some(concrete_type), .. - } = self + }) = self { concrete_type.concrete_type() } else { @@ -86,14 +87,14 @@ impl Type { | (Type::None, Type::None) | (Type::String { .. }, Type::String { .. }) => return Ok(()), ( - Type::Generic { + Type::Generic(GenericType { concrete_type: left, .. - }, - Type::Generic { + }), + Type::Generic(GenericType { concrete_type: right, .. - }, + }), ) => match (left, right) { (Some(left), Some(right)) => { if left.check(right).is_ok() { @@ -105,8 +106,8 @@ impl Type { } _ => {} }, - (Type::Generic { concrete_type, .. }, other) - | (other, Type::Generic { concrete_type, .. }) => { + (Type::Generic(GenericType { concrete_type, .. }), other) + | (other, Type::Generic(GenericType { concrete_type, .. })) => { if let Some(concrete_type) = concrete_type { if other == concrete_type.as_ref() { return Ok(()); @@ -133,12 +134,12 @@ impl Type { type_parameters: left_type_parameters, value_parameters: left_value_parameters, return_type: left_return, - } = left_function_type.as_ref(); + } = left_function_type; let FunctionType { type_parameters: right_type_parameters, value_parameters: right_value_parameters, return_type: right_return, - } = right_function_type.as_ref(); + } = right_function_type; if left_return != right_return || left_type_parameters != right_type_parameters @@ -152,7 +153,7 @@ impl Type { return Ok(()); } - (Type::Range { r#type: left_type }, Type::Range { r#type: right_type }) => { + (Type::Range(left_type), Type::Range(right_type)) => { if left_type == right_type { return Ok(()); } @@ -177,25 +178,24 @@ impl Display for Type { Type::Enum(EnumType { name, .. }) => write!(f, "{name}"), Type::Float => write!(f, "float"), Type::Function(function_type) => write!(f, "{function_type}"), - Type::Generic { concrete_type, .. } => { + Type::Generic(GenericType { concrete_type, .. }) => { match concrete_type.clone().map(|r#box| *r#box) { - Some(Type::Generic { - identifier_index: identifier, - .. - }) => write!(f, "{identifier}"), + Some(Type::Generic(GenericType { + identifier_index, .. + })) => write!(f, "C_{identifier_index}"), Some(concrete_type) => write!(f, "implied to be {concrete_type}"), None => write!(f, "unknown"), } } Type::Integer => write!(f, "int"), Type::List(item_type) => write!(f, "[{item_type}]"), - Type::Map { pairs } => { + Type::Map(pairs) => { write!(f, "map ")?; write!(f, "{{")?; - for (index, (key, value)) in pairs.iter().enumerate() { - write!(f, "{key}: {value}")?; + for (index, r#type) in pairs.iter().enumerate() { + write!(f, "???: {type}")?; if index != pairs.len() - 1 { write!(f, ", ")?; @@ -205,11 +205,11 @@ impl Display for Type { write!(f, "}}") } Type::None => write!(f, "none"), - Type::Range { r#type } => write!(f, "{type} range"), + Type::Range(r#type) => write!(f, "{type} range"), Type::SelfFunction => write!(f, "self"), Type::String => write!(f, "str"), Type::Struct(struct_type) => write!(f, "{struct_type}"), - Type::Tuple { fields } => { + Type::Tuple(fields) => { write!(f, "(")?; for (index, r#type) in fields.iter().enumerate() { @@ -244,13 +244,13 @@ impl Ord for Type { (Type::Character, Type::Character) => Ordering::Equal, (Type::Character, _) => Ordering::Greater, (Type::Enum(left_enum), Type::Enum(right_enum)) => left_enum.cmp(right_enum), - (Type::Enum(_), _) => Ordering::Greater, + (Type::Enum { .. }, _) => Ordering::Greater, (Type::Float, Type::Float) => Ordering::Equal, (Type::Float, _) => Ordering::Greater, (Type::Function(left_function), Type::Function(right_function)) => { left_function.cmp(right_function) } - (Type::Function(_), _) => Ordering::Greater, + (Type::Function { .. }, _) => Ordering::Greater, (Type::Generic { .. }, Type::Generic { .. }) => Ordering::Equal, (Type::Generic { .. }, _) => Ordering::Greater, (Type::Integer, Type::Integer) => Ordering::Equal, @@ -259,15 +259,13 @@ impl Ord for Type { left_item_type.cmp(right_item_type) } (Type::List { .. }, _) => Ordering::Greater, - (Type::Map { pairs: left_pairs }, Type::Map { pairs: right_pairs }) => { + (Type::Map(left_pairs), Type::Map(right_pairs)) => { left_pairs.iter().cmp(right_pairs.iter()) } (Type::Map { .. }, _) => Ordering::Greater, (Type::None, Type::None) => Ordering::Equal, (Type::None, _) => Ordering::Greater, - (Type::Range { r#type: left_type }, Type::Range { r#type: right_type }) => { - left_type.cmp(right_type) - } + (Type::Range(left_type), Type::Range(right_type)) => left_type.cmp(right_type), (Type::Range { .. }, _) => Ordering::Greater, (Type::SelfFunction, Type::SelfFunction) => Ordering::Equal, (Type::SelfFunction, _) => Ordering::Greater, @@ -276,9 +274,9 @@ impl Ord for Type { (Type::Struct(left_struct), Type::Struct(right_struct)) => { left_struct.cmp(right_struct) } - (Type::Struct(_), _) => Ordering::Greater, + (Type::Struct { .. }, _) => Ordering::Greater, - (Type::Tuple { fields: left }, Type::Tuple { fields: right }) => left.cmp(right), + (Type::Tuple(left), Type::Tuple(right)) => left.cmp(right), (Type::Tuple { .. }, _) => Ordering::Greater, } } @@ -287,12 +285,12 @@ impl Ord for Type { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct FunctionType { pub type_parameters: Vec, - pub value_parameters: Vec<(u16, Type)>, - pub return_type: Type, + pub value_parameters: Vec, + pub return_type: Box, } impl FunctionType { - pub fn new>, U: Into>>( + pub fn new>, U: Into>>( type_parameters: T, value_parameters: U, return_type: Type, @@ -300,7 +298,7 @@ impl FunctionType { FunctionType { type_parameters: type_parameters.into(), value_parameters: value_parameters.into(), - return_type, + return_type: Box::new(return_type), } } } @@ -310,7 +308,7 @@ impl Default for FunctionType { FunctionType { type_parameters: Vec::new(), value_parameters: Vec::new(), - return_type: Type::None, + return_type: Box::new(Type::None), } } } @@ -336,7 +334,7 @@ impl Display for FunctionType { write!(f, "(")?; if !self.value_parameters.is_empty() { - for (index, (_, r#type)) in self.value_parameters.iter().enumerate() { + for (index, r#type) in self.value_parameters.iter().enumerate() { if index > 0 { write!(f, ", ")?; } @@ -347,7 +345,7 @@ impl Display for FunctionType { write!(f, ")")?; - if self.return_type != Type::None { + if self.return_type.as_ref() != &Type::None { write!(f, " -> {}", self.return_type)?; } @@ -491,6 +489,12 @@ impl Display for EnumType { } } +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct GenericType { + pub identifier_index: u8, + pub concrete_type: Option>, +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct TypeConflict { pub expected: Type, diff --git a/dust-lang/src/value/concrete_value.rs b/dust-lang/src/value/concrete_value.rs index 235c876..6d5d22d 100644 --- a/dust-lang/src/value/concrete_value.rs +++ b/dust-lang/src/value/concrete_value.rs @@ -11,6 +11,7 @@ use super::RangeValue; pub type DustString = SmartString; #[derive(Debug, PartialEq, PartialOrd, Serialize, Deserialize)] +#[serde(tag = "type", content = "value")] pub enum ConcreteValue { Boolean(bool), Byte(u8), diff --git a/dust-lang/src/value/mod.rs b/dust-lang/src/value/mod.rs index e47d4f1..010b9c1 100644 --- a/dust-lang/src/value/mod.rs +++ b/dust-lang/src/value/mod.rs @@ -50,6 +50,14 @@ impl Value { Value::Concrete(ConcreteValue::String(string.into())) } + pub fn as_concrete(&self) -> Option<&ConcreteValue> { + if let Value::Concrete(concrete_value) = self { + Some(concrete_value) + } else { + None + } + } + pub fn as_boolean(&self) -> Option { if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self { Some(*boolean) @@ -118,7 +126,7 @@ impl Value { match self { Value::Concrete(concrete_value) => concrete_value.r#type(), Value::AbstractList(AbstractList { item_type, .. }) => Type::List(*item_type), - Value::Function(Function { r#type, .. }) => Type::Function(Box::new(r#type.clone())), + Value::Function(Function { r#type, .. }) => Type::Function(r#type.clone()), } } diff --git a/dust-lang/src/value/range_value.rs b/dust-lang/src/value/range_value.rs index eb15135..a60d362 100644 --- a/dust-lang/src/value/range_value.rs +++ b/dust-lang/src/value/range_value.rs @@ -32,9 +32,7 @@ impl RangeValue { } }; - Type::Range { - r#type: Box::new(inner_type), - } + Type::Range(Box::new(inner_type)) } } diff --git a/dust-lang/tests/math/add.rs b/dust-lang/tests/math/add.rs index ab228f7..cc7dca3 100644 --- a/dust-lang/tests/math/add.rs +++ b/dust-lang/tests/math/add.rs @@ -7,10 +7,7 @@ use dust_lang::{ fn add_bytes() { let source = "0x28 + 0x02"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::Byte, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::Byte), instructions: vec![ Instruction::load_encoded(0, 40, TypeCode::BYTE, false), Instruction::load_encoded(1, 2, TypeCode::BYTE, false), @@ -34,10 +31,7 @@ fn add_bytes() { fn add_characters() { let source = "'a' + 'b'"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::String, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::String), instructions: vec![ Instruction::add( 0, @@ -60,10 +54,7 @@ fn add_characters() { fn add_floats() { let source = "2.40 + 40.02"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::Float, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::Float), instructions: vec![ Instruction::add( 0, @@ -86,10 +77,7 @@ fn add_floats() { fn add_integers() { let source = "40 + 2"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::Integer, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::Integer), instructions: vec![ Instruction::add( 0, @@ -112,10 +100,7 @@ fn add_integers() { fn add_strings() { let source = "\"Hello, \" + \"World!\""; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::String, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::String), instructions: vec![ Instruction::add( 0, diff --git a/dust-lang/tests/values.rs b/dust-lang/tests/values.rs index 6368839..14fc2d6 100644 --- a/dust-lang/tests/values.rs +++ b/dust-lang/tests/values.rs @@ -7,10 +7,7 @@ use dust_lang::{ fn load_boolean_true() { let source = "true"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::Boolean, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::Boolean), instructions: vec![ Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), Instruction::r#return(true, 0, TypeCode::BOOLEAN), @@ -28,10 +25,7 @@ fn load_boolean_true() { fn load_boolean_false() { let source = "false"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::Boolean, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::Boolean), instructions: vec![ Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), Instruction::r#return(true, 0, TypeCode::BOOLEAN), @@ -49,10 +43,7 @@ fn load_boolean_false() { fn load_byte() { let source = "0x2a"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::Byte, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::Byte), instructions: vec![ Instruction::load_encoded(0, 0x2a, TypeCode::BYTE, false), Instruction::r#return(true, 0, TypeCode::BYTE), @@ -70,10 +61,7 @@ fn load_byte() { fn load_character() { let source = "'a'"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::Character, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::Character), instructions: vec![ Instruction::load_constant(0, 0, TypeCode::CHARACTER, false), Instruction::r#return(true, 0, TypeCode::CHARACTER), @@ -92,10 +80,7 @@ fn load_character() { fn load_float() { let source = "42.42"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::Float, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::Float), instructions: vec![ Instruction::load_constant(0, 0, TypeCode::FLOAT, false), Instruction::r#return(true, 0, TypeCode::FLOAT), @@ -114,10 +99,7 @@ fn load_float() { fn load_integer() { let source = "42"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::Integer, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::Integer), instructions: vec![ Instruction::load_constant(0, 0, TypeCode::INTEGER, false), Instruction::r#return(true, 0, TypeCode::INTEGER), @@ -136,10 +118,7 @@ fn load_integer() { fn load_string() { let source = "\"Hello, World!\""; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::String, - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::String), instructions: vec![ Instruction::load_constant(0, 0, TypeCode::STRING, false), Instruction::r#return(true, 0, TypeCode::STRING), @@ -158,10 +137,7 @@ fn load_string() { fn load_boolean_list() { let source = "[true, false]"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::List(TypeCode::BOOLEAN), - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::List(TypeCode::BOOLEAN)), instructions: vec![ Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false), @@ -184,10 +160,7 @@ fn load_boolean_list() { fn load_byte_list() { let source = "[0x2a, 0x42]"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::List(TypeCode::BYTE), - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::List(TypeCode::BYTE)), instructions: vec![ Instruction::load_encoded(0, 0x2a, TypeCode::BYTE, false), Instruction::load_encoded(1, 0x42, TypeCode::BYTE, false), @@ -210,10 +183,7 @@ fn load_byte_list() { fn load_character_list() { let source = "['a', 'b']"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::List(TypeCode::CHARACTER), - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::List(TypeCode::CHARACTER)), instructions: vec![ Instruction::load_constant(0, 0, TypeCode::CHARACTER, false), Instruction::load_constant(1, 1, TypeCode::CHARACTER, false), @@ -237,10 +207,7 @@ fn load_character_list() { fn load_float_list() { let source = "[42.42, 24.24]"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::List(TypeCode::FLOAT), - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::List(TypeCode::FLOAT)), instructions: vec![ Instruction::load_constant(0, 0, TypeCode::FLOAT, false), Instruction::load_constant(1, 1, TypeCode::FLOAT, false), @@ -264,10 +231,7 @@ fn load_float_list() { fn load_integer_list() { let source = "[1, 2, 3]"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::List(TypeCode::INTEGER), - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::List(TypeCode::INTEGER)), instructions: vec![ Instruction::load_constant(0, 0, TypeCode::INTEGER, false), Instruction::load_constant(1, 1, TypeCode::INTEGER, false), @@ -297,10 +261,7 @@ fn load_integer_list() { fn load_string_list() { let source = "[\"Hello\", \"World\"]"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::List(TypeCode::STRING), - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::List(TypeCode::STRING)), instructions: vec![ Instruction::load_constant(0, 0, TypeCode::STRING, false), Instruction::load_constant(1, 1, TypeCode::STRING, false), @@ -327,10 +288,7 @@ fn load_string_list() { fn load_nested_list() { let source = "[[1, 2], [3, 4]]"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::List(TypeCode::LIST), - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::List(TypeCode::LIST)), instructions: vec![ Instruction::load_constant(0, 0, TypeCode::INTEGER, false), Instruction::load_constant(1, 1, TypeCode::INTEGER, false), @@ -372,10 +330,7 @@ fn load_nested_list() { fn load_deeply_nested_list() { let source = "[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]"; let chunk = Chunk { - r#type: FunctionType { - return_type: Type::List(TypeCode::LIST), - ..FunctionType::default() - }, + r#type: FunctionType::new([], [], Type::List(TypeCode::LIST)), instructions: vec![ Instruction::load_constant(0, 0, TypeCode::INTEGER, false), Instruction::load_constant(1, 1, TypeCode::INTEGER, false),