2024-11-05 19:38:26 -05:00
|
|
|
//! Built-in functions that implement extended functionality.
|
|
|
|
//!
|
2024-11-29 15:48:50 -05:00
|
|
|
//! 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.
|
2024-11-10 19:28:21 -05:00
|
|
|
mod logic;
|
|
|
|
|
2024-10-30 08:02:22 -04:00
|
|
|
use std::{
|
|
|
|
fmt::{self, Display, Formatter},
|
2024-11-10 19:28:21 -05:00
|
|
|
io::{self},
|
2024-10-30 09:32:46 -04:00
|
|
|
string::{self},
|
2024-10-30 08:02:22 -04:00
|
|
|
};
|
|
|
|
|
2024-10-30 03:08:25 -04:00
|
|
|
use serde::{Deserialize, Serialize};
|
2024-12-04 13:31:02 -05:00
|
|
|
use smallvec::smallvec;
|
2024-10-30 03:08:25 -04:00
|
|
|
|
2024-11-17 20:32:53 -05:00
|
|
|
use crate::{AnnotatedError, FunctionType, Instruction, Span, Type, Value, Vm, VmError};
|
2024-10-30 08:02:22 -04:00
|
|
|
|
2024-11-05 22:28:10 -05:00
|
|
|
macro_rules! define_native_function {
|
2024-11-29 15:48:50 -05:00
|
|
|
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
2024-11-05 19:38:26 -05:00
|
|
|
/// A dust-native function.
|
|
|
|
///
|
|
|
|
/// See the [module-level documentation](index.html) for more information.
|
2024-11-02 21:24:41 -04:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
|
|
pub enum NativeFunction {
|
|
|
|
$(
|
2024-11-29 15:48:50 -05:00
|
|
|
$name = $bytes as isize,
|
2024-11-02 21:24:41 -04:00
|
|
|
)*
|
|
|
|
}
|
|
|
|
|
2024-10-30 09:11:28 -04:00
|
|
|
impl NativeFunction {
|
2024-11-10 19:28:21 -05:00
|
|
|
pub fn call(
|
|
|
|
&self,
|
|
|
|
vm: &mut Vm,
|
|
|
|
instruction: Instruction,
|
2024-11-17 20:32:53 -05:00
|
|
|
) -> Result<Option<Value>, VmError> {
|
2024-11-10 19:28:21 -05:00
|
|
|
match self {
|
|
|
|
$(
|
2024-11-15 19:18:00 -05:00
|
|
|
NativeFunction::$name => $function(vm, instruction),
|
2024-11-10 19:28:21 -05:00
|
|
|
)*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-30 09:11:28 -04:00
|
|
|
pub fn as_str(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
$(
|
|
|
|
NativeFunction::$name => $str,
|
|
|
|
)*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::should_implement_trait)]
|
|
|
|
pub fn from_str(string: &str) -> Option<Self> {
|
|
|
|
match string {
|
|
|
|
$(
|
|
|
|
$str => Some(NativeFunction::$name),
|
|
|
|
)*
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-04 19:16:19 -05:00
|
|
|
pub fn r#type(&self) -> FunctionType {
|
2024-10-30 09:11:28 -04:00
|
|
|
match self {
|
|
|
|
$(
|
2024-11-04 19:16:19 -05:00
|
|
|
NativeFunction::$name => $type,
|
2024-10-30 09:11:28 -04:00
|
|
|
)*
|
|
|
|
}
|
|
|
|
}
|
2024-11-07 00:49:36 -05:00
|
|
|
|
|
|
|
pub fn returns_value(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
$(
|
2024-12-04 13:31:02 -05:00
|
|
|
NativeFunction::$name => $type.return_type != Type::None,
|
2024-11-07 00:49:36 -05:00
|
|
|
)*
|
|
|
|
}
|
|
|
|
}
|
2024-10-30 08:02:22 -04:00
|
|
|
}
|
2024-11-03 01:30:41 -05:00
|
|
|
|
2024-12-09 07:01:07 -05:00
|
|
|
impl From<u8> for NativeFunction {
|
|
|
|
fn from(bytes: u8) -> Self {
|
2024-11-29 15:48:50 -05:00
|
|
|
match bytes {
|
2024-11-03 01:30:41 -05:00
|
|
|
$(
|
2024-11-29 15:48:50 -05:00
|
|
|
$bytes => NativeFunction::$name,
|
2024-11-03 01:30:41 -05:00
|
|
|
)*
|
|
|
|
_ => {
|
|
|
|
if cfg!(test) {
|
2024-11-29 15:48:50 -05:00
|
|
|
panic!("Invalid native function byte: {}", bytes)
|
2024-11-03 01:30:41 -05:00
|
|
|
} else {
|
|
|
|
NativeFunction::Panic
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-29 15:48:50 -05:00
|
|
|
impl From<NativeFunction> for u16 {
|
2024-11-03 01:30:41 -05:00
|
|
|
fn from(native_function: NativeFunction) -> Self {
|
|
|
|
match native_function {
|
|
|
|
$(
|
2024-11-29 15:48:50 -05:00
|
|
|
NativeFunction::$name => $bytes,
|
2024-11-03 01:30:41 -05:00
|
|
|
)*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-10-30 09:11:28 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-11-10 19:28:21 -05:00
|
|
|
impl Display for NativeFunction {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{}", self.as_str())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-05 22:28:10 -05:00
|
|
|
define_native_function! {
|
2024-11-02 21:24:41 -04:00
|
|
|
// Assertion
|
2024-11-10 19:28:21 -05:00
|
|
|
// (
|
|
|
|
// Assert,
|
|
|
|
// 0_u8,
|
|
|
|
// "assert",
|
|
|
|
// FunctionType {
|
|
|
|
// type_parameters: None,
|
|
|
|
// value_parameters: None,
|
|
|
|
// return_type: Box::new(Type::None)
|
|
|
|
// },
|
|
|
|
// assert
|
|
|
|
// ),
|
2024-11-04 19:16:19 -05:00
|
|
|
// (AssertEqual, 1_u8, "assert_equal", false),
|
|
|
|
// (AssertNotEqual, 2_u8, "assert_not_equal", false),
|
|
|
|
(
|
|
|
|
Panic,
|
2024-11-25 20:43:18 -05:00
|
|
|
3,
|
2024-11-04 19:16:19 -05:00
|
|
|
"panic",
|
|
|
|
FunctionType {
|
|
|
|
type_parameters: None,
|
|
|
|
value_parameters: None,
|
2024-12-04 13:31:02 -05:00
|
|
|
return_type: Type::None
|
2024-11-10 19:28:21 -05:00
|
|
|
},
|
|
|
|
logic::panic
|
2024-11-04 19:16:19 -05:00
|
|
|
),
|
|
|
|
|
|
|
|
// // 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,
|
2024-11-25 20:43:18 -05:00
|
|
|
8,
|
2024-11-04 19:16:19 -05:00
|
|
|
"to_string",
|
|
|
|
FunctionType {
|
|
|
|
type_parameters: None,
|
2024-12-04 13:31:02 -05:00
|
|
|
value_parameters: Some(smallvec![(0, Type::Any)]),
|
|
|
|
return_type: Type::String
|
2024-11-10 19:28:21 -05:00
|
|
|
},
|
|
|
|
logic::to_string
|
2024-11-04 19:16:19 -05:00
|
|
|
),
|
|
|
|
|
|
|
|
// // List and string
|
|
|
|
// (All, 9_u8, "all", true),
|
|
|
|
// (Any, 10_u8, "any", true),
|
|
|
|
// (Append, 11_u8, "append", false),
|
|
|
|
// (Contains, 12_u8, "contains", true),
|
|
|
|
// (Dedup, 13_u8, "dedup", false),
|
|
|
|
// (EndsWith, 14_u8, "ends_with", true),
|
|
|
|
// (Find, 15_u8, "find", true),
|
|
|
|
// (Get, 16_u8, "get", true),
|
|
|
|
// (IndexOf, 17_u8, "index_of", true),
|
|
|
|
// (Length, 18_u8, "length", true),
|
|
|
|
// (Prepend, 19_u8, "prepend", false),
|
|
|
|
// (Replace, 20_u8, "replace", false),
|
|
|
|
// (Set, 21_u8, "set", false),
|
|
|
|
// (StartsWith, 22_u8, "starts_with", true),
|
|
|
|
// (Slice, 23_u8, "slice", true),
|
|
|
|
// (Sort, 24_u8, "sort", false),
|
|
|
|
// (Split, 25_u8, "split", true),
|
|
|
|
|
|
|
|
// // List
|
|
|
|
// (Flatten, 26_u8, "flatten", false),
|
|
|
|
// (Join, 27_u8, "join", true),
|
|
|
|
// (Map, 28_u8, "map", true),
|
|
|
|
// (Reduce, 29_u8, "reduce", true),
|
|
|
|
// (Remove, 30_u8, "remove", false),
|
|
|
|
// (Reverse, 31_u8, "reverse", false),
|
|
|
|
// (Unzip, 32_u8, "unzip", true),
|
|
|
|
// (Zip, 33_u8, "zip", true),
|
|
|
|
|
|
|
|
// // String
|
|
|
|
// (Bytes, 34_u8, "bytes", true),
|
|
|
|
// (CharAt, 35_u8, "char_at", true),
|
|
|
|
// (CharCodeAt, 36_u8, "char_code_at", true),
|
|
|
|
// (Chars, 37_u8, "chars", true),
|
|
|
|
// (Format, 38_u8, "format", true),
|
|
|
|
// (Repeat, 39_u8, "repeat", true),
|
|
|
|
// (SplitAt, 40_u8, "split_at", true),
|
|
|
|
// (SplitLines, 41_u8, "split_lines", true),
|
|
|
|
// (SplitWhitespace, 42_u8, "split_whitespace", true),
|
|
|
|
// (ToLowerCase, 43_u8, "to_lower_case", true),
|
|
|
|
// (ToUpperCase, 44_u8, "to_upper_case", true),
|
|
|
|
// (Trim, 45_u8, "trim", true),
|
|
|
|
// (TrimEnd, 46_u8, "trim_end", true),
|
|
|
|
// (TrimStart, 47_u8, "trim_start", true),
|
|
|
|
|
|
|
|
// // I/O
|
|
|
|
// // Read
|
|
|
|
// (Read, 48_u8, "read", true),
|
|
|
|
// (ReadFile, 49_u8, "read_file", true),
|
|
|
|
(
|
|
|
|
ReadLine,
|
2024-11-25 20:43:18 -05:00
|
|
|
50,
|
2024-11-04 19:16:19 -05:00
|
|
|
"read_line",
|
|
|
|
FunctionType {
|
|
|
|
type_parameters: None,
|
|
|
|
value_parameters: None,
|
2024-12-04 13:31:02 -05:00
|
|
|
return_type: Type::String
|
2024-11-10 19:28:21 -05:00
|
|
|
},
|
|
|
|
logic::read_line
|
2024-11-04 19:16:19 -05:00
|
|
|
),
|
|
|
|
// (ReadTo, 51_u8, "read_to", false),
|
|
|
|
// (ReadUntil, 52_u8, "read_until", true),
|
|
|
|
// // Write
|
|
|
|
// (AppendFile, 53_u8, "append_file", false),
|
|
|
|
// (PrependFile, 54_u8, "prepend_file", false),
|
|
|
|
(
|
|
|
|
Write,
|
2024-11-25 20:43:18 -05:00
|
|
|
55,
|
2024-11-04 19:16:19 -05:00
|
|
|
"write",
|
|
|
|
FunctionType {
|
|
|
|
type_parameters: None,
|
2024-12-04 13:31:02 -05:00
|
|
|
value_parameters: Some(smallvec![(0, Type::String)]),
|
|
|
|
return_type: Type::None
|
2024-11-10 19:28:21 -05:00
|
|
|
},
|
|
|
|
logic::write
|
2024-11-04 19:16:19 -05:00
|
|
|
),
|
|
|
|
// (WriteFile, 56_u8, "write_file", false),
|
|
|
|
(
|
|
|
|
WriteLine,
|
2024-11-25 20:43:18 -05:00
|
|
|
57,
|
2024-11-04 19:16:19 -05:00
|
|
|
"write_line",
|
|
|
|
FunctionType {
|
|
|
|
type_parameters: None,
|
2024-12-04 13:31:02 -05:00
|
|
|
value_parameters: Some(smallvec![(0, Type::String)]),
|
|
|
|
return_type: Type::None
|
2024-11-10 19:28:21 -05:00
|
|
|
},
|
|
|
|
logic::write_line
|
2024-11-04 19:16:19 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// // Random
|
|
|
|
// (Random, 58_u8, "random", true),
|
|
|
|
// (RandomInRange, 59_u8, "random_in_range", true)
|
2024-10-30 09:11:28 -04:00
|
|
|
}
|
|
|
|
|
2024-10-30 09:32:46 -04:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
pub enum NativeFunctionError {
|
|
|
|
ExpectedArgumentCount {
|
|
|
|
expected: usize,
|
|
|
|
found: usize,
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
Panic {
|
|
|
|
message: Option<String>,
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
Parse {
|
|
|
|
error: string::ParseError,
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
Io {
|
|
|
|
error: io::ErrorKind,
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AnnotatedError for NativeFunctionError {
|
|
|
|
fn title() -> &'static str {
|
2024-10-30 14:48:30 -04:00
|
|
|
"Native Function Error"
|
2024-10-30 09:32:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn description(&self) -> &'static str {
|
2024-10-30 14:48:30 -04:00
|
|
|
match self {
|
|
|
|
NativeFunctionError::ExpectedArgumentCount { .. } => {
|
|
|
|
"Expected a different number of arguments"
|
|
|
|
}
|
|
|
|
NativeFunctionError::Panic { .. } => "Explicit panic",
|
|
|
|
NativeFunctionError::Parse { .. } => "Failed to parse value",
|
|
|
|
NativeFunctionError::Io { .. } => "I/O error",
|
|
|
|
}
|
2024-10-30 09:32:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn details(&self) -> Option<String> {
|
2024-10-30 14:48:30 -04:00
|
|
|
match self {
|
|
|
|
NativeFunctionError::ExpectedArgumentCount {
|
|
|
|
expected, found, ..
|
|
|
|
} => Some(format!("Expected {} arguments, found {}", expected, found)),
|
|
|
|
NativeFunctionError::Panic { message, .. } => message.clone(),
|
|
|
|
NativeFunctionError::Parse { error, .. } => Some(format!("{}", error)),
|
|
|
|
NativeFunctionError::Io { error, .. } => Some(format!("{}", error)),
|
|
|
|
}
|
2024-10-30 09:32:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn position(&self) -> Span {
|
2024-10-30 14:48:30 -04:00
|
|
|
match self {
|
|
|
|
NativeFunctionError::ExpectedArgumentCount { position, .. } => *position,
|
|
|
|
NativeFunctionError::Panic { position, .. } => *position,
|
|
|
|
NativeFunctionError::Parse { position, .. } => *position,
|
|
|
|
NativeFunctionError::Io { position, .. } => *position,
|
|
|
|
}
|
2024-10-30 09:32:46 -04:00
|
|
|
}
|
|
|
|
}
|