1
0

Add formatting disassembly output into JSON or TOML

This commit is contained in:
Jeff 2025-02-08 05:56:49 -05:00
parent e387579a81
commit 71a92c078b
14 changed files with 385 additions and 332 deletions

85
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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<DustString>,
#[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<DustString>) {
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}");
}
}
}

View File

@ -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,
}

View File

@ -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<DustString>,
pub r#type: FunctionType,
@ -114,3 +115,28 @@ impl PartialEq for Chunk {
&& self.prototypes == other.prototypes
}
}
impl Serialize for Chunk {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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()
}
}

View File

@ -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(),

View File

@ -597,7 +597,8 @@ impl Instruction {
function.returns_value()
}
Operation::EQUAL
Operation::CLOSE
| Operation::EQUAL
| Operation::LESS
| Operation::LESS_EQUAL
| Operation::TEST

View File

@ -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
)
}

View File

@ -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<FunctionType>),
Generic {
identifier_index: u8,
concrete_type: Option<Box<Type>>,
},
Function(FunctionType),
Generic(GenericType),
Integer,
List(TypeCode),
Map {
pairs: Vec<(u8, Type)>,
},
Map(Vec<Type>),
#[default]
None,
Range {
r#type: Box<Type>,
},
Range(Box<Type>),
SelfFunction,
String,
Struct(StructType),
Tuple {
fields: Vec<Type>,
},
Tuple(Vec<Type>),
}
impl Type {
pub fn function(function_type: FunctionType) -> Self {
Type::Function(Box::new(function_type))
pub fn function<T: Into<Vec<u16>>, U: Into<Vec<Type>>>(
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<u16>,
pub value_parameters: Vec<(u16, Type)>,
pub return_type: Type,
pub value_parameters: Vec<Type>,
pub return_type: Box<Type>,
}
impl FunctionType {
pub fn new<T: Into<Vec<u16>>, U: Into<Vec<(u16, Type)>>>(
pub fn new<T: Into<Vec<u16>>, U: Into<Vec<Type>>>(
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<Box<Type>>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct TypeConflict {
pub expected: Type,

View File

@ -11,6 +11,7 @@ use super::RangeValue;
pub type DustString = SmartString<LazyCompact>;
#[derive(Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum ConcreteValue {
Boolean(bool),
Byte(u8),

View File

@ -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<bool> {
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()),
}
}

View File

@ -32,9 +32,7 @@ impl RangeValue {
}
};
Type::Range {
r#type: Box::new(inner_type),
}
Type::Range(Box::new(inner_type))
}
}

View File

@ -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,

View File

@ -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),