1
0

Overhaul implicit returns and add lots of new native functions

This commit is contained in:
Jeff 2024-11-02 21:24:41 -04:00
parent ae6d3d7a82
commit 1947d66be5
6 changed files with 346 additions and 451 deletions

View File

@ -8,129 +8,88 @@ use serde::{Deserialize, Serialize};
use crate::{AnnotatedError, Instruction, Primitive, Span, Value, Vm, VmError}; use crate::{AnnotatedError, Instruction, Primitive, Span, Value, Vm, VmError};
const PANIC: u8 = 0b0000_0000; // Assertio
const ASSERT: u8 = 0b0000_0000;
const ASSERT_EQ: u8 = 0b0000_0001;
const ASSERT_NE: u8 = 0b0000_0010;
const PANIC: u8 = 0b0000_0011;
// Type conversion // Type conversion
const PARSE: u8 = 0b0000_0001; const PARSE: u8 = 0b0000_0100;
const TO_BYTE: u8 = 0b0000_0010; const TO_BYTE: u8 = 0b0000_0101;
const TO_FLOAT: u8 = 0b0000_0011; const TO_FLOAT: u8 = 0b0000_0110;
const TO_INTEGER: u8 = 0b0000_0100; const TO_INTEGER: u8 = 0b0000_0111;
const TO_STRING: u8 = 0b0000_0101; const TO_STRING: u8 = 0b0000_1000;
// List // List and string
const ALL: u8 = 0b0000_0110; const ALL: u8 = 0b0000_1001;
const ANY: u8 = 0b0000_0111; const ANY: u8 = 0b0000_1010;
const APPEND: u8 = 0b0000_1000; const APPEND: u8 = 0b0000_1011;
const CONTAINS: u8 = 0b0000_1001; const CONTAINS: u8 = 0b0000_1100;
const FIND: u8 = 0b0000_1010; const DEDUP: u8 = 0b0000_1101;
const FLATTEN: u8 = 0b0000_1011; const ENDS_WITH: u8 = 0b0000_1110;
const GET: u8 = 0b0000_1100; const FIND: u8 = 0b0000_1111;
const INDEX_OF: u8 = 0b0000_1101; const GET: u8 = 0b0001_0000;
const JOIN: u8 = 0b0000_1110; const INDEX_OF: u8 = 0b0001_0001;
const LENGTH: u8 = 0b0000_1111; const LENGTH: u8 = 0b0001_0010;
const MAP: u8 = 0b0001_0000; const PREPEND: u8 = 0b0001_0011;
const PREPEND: u8 = 0b0001_0001; const REPLACE: u8 = 0b0001_0100;
const REDUCE: u8 = 0b0001_0010;
const REMOVE: u8 = 0b0001_0011;
const REVERSE: u8 = 0b0001_0100;
const SET: u8 = 0b0001_0101; const SET: u8 = 0b0001_0101;
const SLICE: u8 = 0b0001_0110; const STARTS_WITH: u8 = 0b0001_0110;
const SORT: u8 = 0b0001_0111; const SLICE: u8 = 0b0001_0111;
const SPLIT: u8 = 0b0001_1000; const SORT: u8 = 0b0001_1000;
const UNZIP: u8 = 0b0001_1001; const SPLIT: u8 = 0b0001_1001;
const ZIP: u8 = 0b0001_1010;
// String
const CHAR_AT: u8 = 0b0001_1011;
const CHAR_CODE_AT: u8 = 0b0001_1100;
const CHARS: u8 = 0b0001_1101;
const ENDS_WITH: u8 = 0b0001_1110;
const FORMAT: u8 = 0b0001_1111;
const INCLUDES: u8 = 0b0010_0000;
const MATCH: u8 = 0b0010_0001;
const PAD_END: u8 = 0b0010_0010;
const PAD_START: u8 = 0b0010_0011;
const REPEAT: u8 = 0b0010_0100;
const REPLACE: u8 = 0b0010_0101;
const SPLIT_AT: u8 = 0b0010_0110;
const SPLIT_LINES: u8 = 0b0010_0111;
const SPLIT_WHITESPACE: u8 = 0b0010_1000;
const STARTS_WITH: u8 = 0b0010_1001;
const TO_LOWER_CASE: u8 = 0b0010_1010;
const TO_UPPER_CASE: u8 = 0b0010_1011;
const TRIM: u8 = 0b0010_1100;
const TRIM_END: u8 = 0b0010_1101;
const TRIM_START: u8 = 0b0010_1110;
// I/O
const READ_LINE: u8 = 0b0010_1111;
const WRITE_LINE: u8 = 0b0011_0001;
const WRITE: u8 = 0b0011_0000;
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum NativeFunction {
Panic = PANIC as isize,
// Type conversion
Parse = PARSE as isize,
ToByte = TO_BYTE as isize,
ToFloat = TO_FLOAT as isize,
ToInteger = TO_INTEGER as isize,
ToString = TO_STRING as isize,
// List // List
All = ALL as isize, const FLATTEN: u8 = 0b0001_1010;
Any = ANY as isize, const JOIN: u8 = 0b0001_1011;
Append = APPEND as isize, const MAP: u8 = 0b0001_1100;
Contains = CONTAINS as isize, const REDUCE: u8 = 0b0001_1101;
Find = FIND as isize, const REMOVE: u8 = 0b0001_1110;
Flatten = FLATTEN as isize, const REVERSE: u8 = 0b0001_1111;
Get = GET as isize, const UNZIP: u8 = 0b0010_0000;
IndexOf = INDEX_OF as isize, const ZIP: u8 = 0b0010_0001;
Join = JOIN as isize,
Length = LENGTH as isize,
Map = MAP as isize,
Prepend = PREPEND as isize,
Reduce = REDUCE as isize,
Remove = REMOVE as isize,
Reverse = REVERSE as isize,
Set = SET as isize,
Slice = SLICE as isize,
Sort = SORT as isize,
Split = SPLIT as isize,
Unzip = UNZIP as isize,
Zip = ZIP as isize,
// String // String
CharAt = CHAR_AT as isize, const BYTES: u8 = 0b0010_0010;
CharCodeAt = CHAR_CODE_AT as isize, const CHAR_AT: u8 = 0b0010_0011;
Chars = CHARS as isize, const CHAR_CODE_AT: u8 = 0b0010_0100;
EndsWith = ENDS_WITH as isize, const CHARS: u8 = 0b0010_0101;
Format = FORMAT as isize, const FORMAT: u8 = 0b0010_0110;
Includes = INCLUDES as isize, const REPEAT: u8 = 0b0010_0111;
Match = MATCH as isize, const SPLIT_AT: u8 = 0b0010_1000;
PadEnd = PAD_END as isize, const SPLIT_LINES: u8 = 0b0010_1001;
PadStart = PAD_START as isize, const SPLIT_WHITESPACE: u8 = 0b0010_1010;
Repeat = REPEAT as isize, const TO_LOWER_CASE: u8 = 0b0010_1011;
Replace = REPLACE as isize, const TO_UPPER_CASE: u8 = 0b0010_1100;
SplitAt = SPLIT_AT as isize, const TRIM: u8 = 0b0010_1101;
SplitLines = SPLIT_LINES as isize, const TRIM_END: u8 = 0b0010_1110;
SplitWhitespace = SPLIT_WHITESPACE as isize, const TRIM_START: u8 = 0b0010_1111;
StartsWith = STARTS_WITH as isize,
ToLowerCase = TO_LOWER_CASE as isize,
ToUpperCase = TO_UPPER_CASE as isize,
Trim = TRIM as isize,
TrimEnd = TRIM_END as isize,
TrimStart = TRIM_START as isize,
// I/O // I/O
ReadLine = READ_LINE as isize, const READ_LINE: u8 = 0b0011_0000;
WriteLine = WRITE_LINE as isize, const WRITE_LINE: u8 = 0b0011_0001;
Write = WRITE as isize, const WRITE: u8 = 0b0011_0010;
}
// Random
const RANDOM: u8 = 0b0011_0011;
const RANDOM_BYTE: u8 = 0b0011_0100;
const RANDOM_BYTES: u8 = 0b0011_0101;
const RANDOM_CHAR: u8 = 0b0011_0110;
const RANDOM_FLOAT: u8 = 0b0011_0111;
const RANDOM_INTEGER: u8 = 0b0011_1000;
const RANDOM_RANGE: u8 = 0b0011_1001;
const RANDOM_STRING: u8 = 0b0011_1010;
macro_rules! impl_from_str_for_native_function { macro_rules! impl_from_str_for_native_function {
($(($name:ident, $str:expr, $returns_value:expr)),*) => { ($(($name:ident, $str:expr, $returns_value:expr)),*) => {
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum NativeFunction {
$(
$name,
)*
}
impl NativeFunction { impl NativeFunction {
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match self { match self {
@ -161,9 +120,12 @@ macro_rules! impl_from_str_for_native_function {
}; };
} }
// Use the macro to implement From<&str> for NativeFunction
impl_from_str_for_native_function! { impl_from_str_for_native_function! {
(Panic, "panic", false), // Assertion
(Assert, "assert", false),
(AssertEq, "assert_eq", false),
(AssertNe, "assert_ne", false),
(Panic, "panic", true),
// Type conversion // Type conversion
(Parse, "parse", true), (Parse, "parse", true),
@ -172,45 +134,45 @@ impl_from_str_for_native_function! {
(ToInteger, "to_integer", true), (ToInteger, "to_integer", true),
(ToString, "to_string", true), (ToString, "to_string", true),
// List // List and string
(All, "all", true), (All, "all", true),
(Any, "any", true), (Any, "any", true),
(Append, "append", true), (Append, "append", false),
(Contains, "contains", true), (Contains, "contains", true),
(Dedup, "dedup", false),
(EndsWith, "ends_with", true),
(Find, "find", true), (Find, "find", true),
(Flatten, "flatten", true),
(Get, "get", true), (Get, "get", true),
(IndexOf, "index_of", true), (IndexOf, "index_of", true),
(Join, "join", true),
(Length, "length", true), (Length, "length", true),
(Map, "map", true), (Prepend, "prepend", false),
(Prepend, "prepend", true), (Replace, "replace", false),
(Reduce, "reduce", true), (Set, "set", false),
(Remove, "remove", true), (StartsWith, "starts_with", true),
(Reverse, "reverse", true),
(Set, "set", true),
(Slice, "slice", true), (Slice, "slice", true),
(Sort, "sort", true), (Sort, "sort", false),
(Split, "split", true), (Split, "split", true),
// List
(Flatten, "flatten", true),
(Join, "join", true),
(Map, "map", true),
(Reduce, "reduce", true),
(Remove, "remove", false),
(Reverse, "reverse", false),
(Unzip, "unzip", true), (Unzip, "unzip", true),
(Zip, "zip", true), (Zip, "zip", true),
// String // String
(Bytes, "bytes", true),
(CharAt, "char_at", true), (CharAt, "char_at", true),
(CharCodeAt, "char_code_at", true), (CharCodeAt, "char_code_at", true),
(Chars, "chars", true), (Chars, "chars", true),
(EndsWith, "ends_with", true),
(Format, "format", true), (Format, "format", true),
(Includes, "includes", true),
(Match, "match", true),
(PadEnd, "pad_end", true),
(PadStart, "pad_start", true),
(Repeat, "repeat", true), (Repeat, "repeat", true),
(Replace, "replace", true),
(SplitAt, "split_at", true), (SplitAt, "split_at", true),
(SplitLines, "split_lines", true), (SplitLines, "split_lines", true),
(SplitWhitespace, "split_whitespace", true), (SplitWhitespace, "split_whitespace", true),
(StartsWith, "starts_with", true),
(ToLowerCase, "to_lower_case", true), (ToLowerCase, "to_lower_case", true),
(ToUpperCase, "to_upper_case", true), (ToUpperCase, "to_upper_case", true),
(Trim, "trim", true), (Trim, "trim", true),
@ -220,7 +182,17 @@ impl_from_str_for_native_function! {
// I/O // I/O
(ReadLine, "read_line", true), (ReadLine, "read_line", true),
(WriteLine, "write_line", false), (WriteLine, "write_line", false),
(Write, "write", false) (Write, "write", false),
// Random
(Random, "random", true),
(RandomByte, "random_byte", true),
(RandomBytes, "random_bytes", true),
(RandomChar, "random_char", true),
(RandomFloat, "random_float", true),
(RandomInteger, "random_integer", true),
(RandomRange, "random_range", true),
(RandomString, "random_string", true)
} }
impl NativeFunction { impl NativeFunction {
@ -276,51 +248,6 @@ impl NativeFunction {
Some(Value::Primitive(Primitive::String(string))) Some(Value::Primitive(Primitive::String(string)))
} }
// List
NativeFunction::All => todo!(),
NativeFunction::Any => todo!(),
NativeFunction::Append => todo!(),
NativeFunction::Contains => todo!(),
NativeFunction::Find => todo!(),
NativeFunction::Flatten => todo!(),
NativeFunction::Get => todo!(),
NativeFunction::IndexOf => todo!(),
NativeFunction::Join => todo!(),
NativeFunction::Length => todo!(),
NativeFunction::Map => todo!(),
NativeFunction::Prepend => todo!(),
NativeFunction::Reduce => todo!(),
NativeFunction::Remove => todo!(),
NativeFunction::Reverse => todo!(),
NativeFunction::Set => todo!(),
NativeFunction::Slice => todo!(),
NativeFunction::Sort => todo!(),
NativeFunction::Split => todo!(),
NativeFunction::Unzip => todo!(),
NativeFunction::Zip => todo!(),
// String
NativeFunction::CharAt => todo!(),
NativeFunction::CharCodeAt => todo!(),
NativeFunction::Chars => todo!(),
NativeFunction::EndsWith => todo!(),
NativeFunction::Format => todo!(),
NativeFunction::Includes => todo!(),
NativeFunction::Match => todo!(),
NativeFunction::PadEnd => todo!(),
NativeFunction::PadStart => todo!(),
NativeFunction::Repeat => todo!(),
NativeFunction::Replace => todo!(),
NativeFunction::SplitAt => todo!(),
NativeFunction::SplitLines => todo!(),
NativeFunction::SplitWhitespace => todo!(),
NativeFunction::StartsWith => todo!(),
NativeFunction::ToLowerCase => todo!(),
NativeFunction::ToUpperCase => todo!(),
NativeFunction::Trim => todo!(),
NativeFunction::TrimEnd => todo!(),
NativeFunction::TrimStart => todo!(),
// I/O // I/O
NativeFunction::ReadLine => { NativeFunction::ReadLine => {
let mut buffer = String::new(); let mut buffer = String::new();
@ -391,6 +318,7 @@ impl NativeFunction {
None None
} }
_ => todo!(),
}; };
Ok(return_value) Ok(return_value)
@ -400,64 +328,64 @@ impl NativeFunction {
impl From<u8> for NativeFunction { impl From<u8> for NativeFunction {
fn from(byte: u8) -> Self { fn from(byte: u8) -> Self {
match byte { match byte {
PANIC => NativeFunction::Panic, ALL => NativeFunction::Assert,
ASSERT_EQ => NativeFunction::AssertEq,
// Type conversion ASSERT_NE => NativeFunction::AssertNe,
PARSE => NativeFunction::Parse,
TO_BYTE => NativeFunction::ToByte,
TO_FLOAT => NativeFunction::ToFloat,
TO_INTEGER => NativeFunction::ToInteger,
TO_STRING => NativeFunction::ToString,
// List
ALL => NativeFunction::All,
ANY => NativeFunction::Any,
APPEND => NativeFunction::Append, APPEND => NativeFunction::Append,
ANY => NativeFunction::Any,
BYTES => NativeFunction::Bytes,
CHAR_AT => NativeFunction::CharAt,
CHAR_CODE_AT => NativeFunction::CharCodeAt,
CHARS => NativeFunction::Chars,
CONTAINS => NativeFunction::Contains, CONTAINS => NativeFunction::Contains,
DEDUP => NativeFunction::Dedup,
ENDS_WITH => NativeFunction::EndsWith,
FIND => NativeFunction::Find, FIND => NativeFunction::Find,
FLATTEN => NativeFunction::Flatten, FLATTEN => NativeFunction::Flatten,
FORMAT => NativeFunction::Format,
GET => NativeFunction::Get, GET => NativeFunction::Get,
INDEX_OF => NativeFunction::IndexOf, INDEX_OF => NativeFunction::IndexOf,
JOIN => NativeFunction::Join, JOIN => NativeFunction::Join,
LENGTH => NativeFunction::Length, LENGTH => NativeFunction::Length,
MAP => NativeFunction::Map, MAP => NativeFunction::Map,
PANIC => NativeFunction::Panic,
PARSE => NativeFunction::Parse,
PREPEND => NativeFunction::Prepend, PREPEND => NativeFunction::Prepend,
RANDOM => NativeFunction::Random,
RANDOM_BYTE => NativeFunction::RandomByte,
RANDOM_BYTES => NativeFunction::RandomBytes,
RANDOM_CHAR => NativeFunction::RandomChar,
RANDOM_FLOAT => NativeFunction::RandomFloat,
RANDOM_INTEGER => NativeFunction::RandomInteger,
RANDOM_RANGE => NativeFunction::RandomRange,
RANDOM_STRING => NativeFunction::RandomString,
READ_LINE => NativeFunction::ReadLine,
REDUCE => NativeFunction::Reduce, REDUCE => NativeFunction::Reduce,
REMOVE => NativeFunction::Remove, REMOVE => NativeFunction::Remove,
REPEAT => NativeFunction::Repeat,
REPLACE => NativeFunction::Replace,
REVERSE => NativeFunction::Reverse, REVERSE => NativeFunction::Reverse,
SET => NativeFunction::Set, SET => NativeFunction::Set,
SLICE => NativeFunction::Slice, SLICE => NativeFunction::Slice,
SORT => NativeFunction::Sort, SORT => NativeFunction::Sort,
SPLIT => NativeFunction::Split, SPLIT => NativeFunction::Split,
UNZIP => NativeFunction::Unzip,
ZIP => NativeFunction::Zip,
// String
CHAR_AT => NativeFunction::CharAt,
CHAR_CODE_AT => NativeFunction::CharCodeAt,
CHARS => NativeFunction::Chars,
ENDS_WITH => NativeFunction::EndsWith,
FORMAT => NativeFunction::Format,
INCLUDES => NativeFunction::Includes,
MATCH => NativeFunction::Match,
PAD_END => NativeFunction::PadEnd,
PAD_START => NativeFunction::PadStart,
REPEAT => NativeFunction::Repeat,
REPLACE => NativeFunction::Replace,
SPLIT_AT => NativeFunction::SplitAt, SPLIT_AT => NativeFunction::SplitAt,
SPLIT_LINES => NativeFunction::SplitLines, SPLIT_LINES => NativeFunction::SplitLines,
SPLIT_WHITESPACE => NativeFunction::SplitWhitespace, SPLIT_WHITESPACE => NativeFunction::SplitWhitespace,
STARTS_WITH => NativeFunction::StartsWith, STARTS_WITH => NativeFunction::StartsWith,
TO_BYTE => NativeFunction::ToByte,
TO_FLOAT => NativeFunction::ToFloat,
TO_INTEGER => NativeFunction::ToInteger,
TO_LOWER_CASE => NativeFunction::ToLowerCase, TO_LOWER_CASE => NativeFunction::ToLowerCase,
TO_STRING => NativeFunction::ToString,
TO_UPPER_CASE => NativeFunction::ToUpperCase, TO_UPPER_CASE => NativeFunction::ToUpperCase,
TRIM => NativeFunction::Trim, TRIM => NativeFunction::Trim,
TRIM_END => NativeFunction::TrimEnd, TRIM_END => NativeFunction::TrimEnd,
TRIM_START => NativeFunction::TrimStart, TRIM_START => NativeFunction::TrimStart,
UNZIP => NativeFunction::Unzip,
// I/O
READ_LINE => NativeFunction::ReadLine,
WRITE => NativeFunction::Write, WRITE => NativeFunction::Write,
WRITE_LINE => NativeFunction::WriteLine, WRITE_LINE => NativeFunction::WriteLine,
ZIP => NativeFunction::Zip,
_ => { _ => {
if cfg!(test) { if cfg!(test) {
panic!("Invalid native function byte: {}", byte) panic!("Invalid native function byte: {}", byte)
@ -472,56 +400,65 @@ impl From<u8> for NativeFunction {
impl From<NativeFunction> for u8 { impl From<NativeFunction> for u8 {
fn from(native_function: NativeFunction) -> Self { fn from(native_function: NativeFunction) -> Self {
match native_function { match native_function {
NativeFunction::Panic => PANIC,
NativeFunction::Parse => PARSE,
NativeFunction::ToByte => TO_BYTE,
NativeFunction::ToFloat => TO_FLOAT,
NativeFunction::ToInteger => TO_INTEGER,
NativeFunction::ToString => TO_STRING,
NativeFunction::All => ALL, NativeFunction::All => ALL,
NativeFunction::Any => ANY, NativeFunction::Any => ANY,
NativeFunction::Append => APPEND, NativeFunction::Append => APPEND,
NativeFunction::Assert => ASSERT,
NativeFunction::AssertEq => ASSERT_EQ,
NativeFunction::AssertNe => ASSERT_NE,
NativeFunction::Bytes => BYTES,
NativeFunction::CharAt => CHAR_AT,
NativeFunction::CharCodeAt => CHAR_CODE_AT,
NativeFunction::Chars => CHARS,
NativeFunction::Contains => CONTAINS, NativeFunction::Contains => CONTAINS,
NativeFunction::Dedup => DEDUP,
NativeFunction::EndsWith => ENDS_WITH,
NativeFunction::Find => FIND, NativeFunction::Find => FIND,
NativeFunction::Flatten => FLATTEN, NativeFunction::Flatten => FLATTEN,
NativeFunction::Format => FORMAT,
NativeFunction::Get => GET, NativeFunction::Get => GET,
NativeFunction::IndexOf => INDEX_OF, NativeFunction::IndexOf => INDEX_OF,
NativeFunction::Join => JOIN, NativeFunction::Join => JOIN,
NativeFunction::Length => LENGTH, NativeFunction::Length => LENGTH,
NativeFunction::Map => MAP, NativeFunction::Map => MAP,
NativeFunction::Panic => PANIC,
NativeFunction::Parse => PARSE,
NativeFunction::Prepend => PREPEND, NativeFunction::Prepend => PREPEND,
NativeFunction::Random => RANDOM,
NativeFunction::RandomByte => RANDOM_BYTE,
NativeFunction::RandomBytes => RANDOM_BYTES,
NativeFunction::RandomChar => RANDOM_CHAR,
NativeFunction::RandomFloat => RANDOM_FLOAT,
NativeFunction::RandomInteger => RANDOM_INTEGER,
NativeFunction::RandomRange => RANDOM_RANGE,
NativeFunction::RandomString => RANDOM_STRING,
NativeFunction::ReadLine => READ_LINE,
NativeFunction::Reduce => REDUCE, NativeFunction::Reduce => REDUCE,
NativeFunction::Remove => REMOVE, NativeFunction::Remove => REMOVE,
NativeFunction::Repeat => REPEAT,
NativeFunction::Replace => REPLACE,
NativeFunction::Reverse => REVERSE, NativeFunction::Reverse => REVERSE,
NativeFunction::Set => SET, NativeFunction::Set => SET,
NativeFunction::Slice => SLICE, NativeFunction::Slice => SLICE,
NativeFunction::Sort => SORT, NativeFunction::Sort => SORT,
NativeFunction::Split => SPLIT, NativeFunction::Split => SPLIT,
NativeFunction::Unzip => UNZIP,
NativeFunction::Zip => ZIP,
NativeFunction::CharAt => CHAR_AT,
NativeFunction::CharCodeAt => CHAR_CODE_AT,
NativeFunction::Chars => CHARS,
NativeFunction::EndsWith => ENDS_WITH,
NativeFunction::Format => FORMAT,
NativeFunction::Includes => INCLUDES,
NativeFunction::Match => MATCH,
NativeFunction::PadEnd => PAD_END,
NativeFunction::PadStart => PAD_START,
NativeFunction::Repeat => REPEAT,
NativeFunction::Replace => REPLACE,
NativeFunction::SplitAt => SPLIT_AT, NativeFunction::SplitAt => SPLIT_AT,
NativeFunction::SplitLines => SPLIT_LINES, NativeFunction::SplitLines => SPLIT_LINES,
NativeFunction::SplitWhitespace => SPLIT_WHITESPACE, NativeFunction::SplitWhitespace => SPLIT_WHITESPACE,
NativeFunction::StartsWith => STARTS_WITH, NativeFunction::StartsWith => STARTS_WITH,
NativeFunction::ToByte => TO_BYTE,
NativeFunction::ToFloat => TO_FLOAT,
NativeFunction::ToInteger => TO_INTEGER,
NativeFunction::ToLowerCase => TO_LOWER_CASE, NativeFunction::ToLowerCase => TO_LOWER_CASE,
NativeFunction::ToString => TO_STRING,
NativeFunction::ToUpperCase => TO_UPPER_CASE, NativeFunction::ToUpperCase => TO_UPPER_CASE,
NativeFunction::Trim => TRIM, NativeFunction::Trim => TRIM,
NativeFunction::TrimEnd => TRIM_END, NativeFunction::TrimEnd => TRIM_END,
NativeFunction::TrimStart => TRIM_START, NativeFunction::TrimStart => TRIM_START,
NativeFunction::ReadLine => READ_LINE, NativeFunction::Unzip => UNZIP,
NativeFunction::WriteLine => WRITE_LINE,
NativeFunction::Write => WRITE, NativeFunction::Write => WRITE,
NativeFunction::WriteLine => WRITE_LINE,
NativeFunction::Zip => ZIP,
} }
} }
} }

View File

@ -17,20 +17,10 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
let lexer = Lexer::new(source); let lexer = Lexer::new(source);
let mut parser = Parser::new(lexer).map_err(|error| DustError::Parse { error, source })?; let mut parser = Parser::new(lexer).map_err(|error| DustError::Parse { error, source })?;
loop {
parser parser
.parse_statement(Allowed { .parse_top_level()
assignment: true,
explicit_return: false,
implicit_return: true,
})
.map_err(|error| DustError::Parse { error, source })?; .map_err(|error| DustError::Parse { error, source })?;
if parser.is_eof() {
break;
}
}
Ok(parser.finish()) Ok(parser.finish())
} }
@ -996,9 +986,10 @@ impl<'src> Parser<'src> {
.chunk .chunk
.instructions() .instructions()
.iter() .iter()
.rev()
.find_map(|(instruction, _)| { .find_map(|(instruction, _)| {
if !matches!(instruction.operation(), Operation::Jump) { if !matches!(instruction.operation(), Operation::Jump | Operation::Move) {
Some(true) Some(instruction.yields_value())
} else { } else {
None None
} }
@ -1010,6 +1001,7 @@ impl<'src> Parser<'src> {
let if_last_register = self.next_register().saturating_sub(1); let if_last_register = self.next_register().saturating_sub(1);
self.parse_else(allowed, block_allowed)?; self.parse_else(allowed, block_allowed)?;
self.optimize_statement();
let else_last_register = self.next_register().saturating_sub(1); let else_last_register = self.next_register().saturating_sub(1);
let else_end = self.chunk.len(); let else_end = self.chunk.len();
@ -1022,19 +1014,7 @@ impl<'src> Parser<'src> {
); );
} }
self.current_is_expression = if_block_is_expression self.current_is_expression = if_block_is_expression && self.current_is_expression;
&& self
.chunk
.instructions()
.iter()
.find_map(|(instruction, _)| {
if !matches!(instruction.operation(), Operation::Jump) {
Some(true)
} else {
None
}
})
.unwrap_or(false);
if jump_distance == 1 { if jump_distance == 1 {
if let Some(skippable) = self.get_last_jumpable_mut() { if let Some(skippable) = self.get_last_jumpable_mut() {
@ -1081,12 +1061,6 @@ impl<'src> Parser<'src> {
self.current_position, self.current_position,
)?; )?;
} }
self.current_is_expression = self
.chunk
.instructions()
.last()
.map_or(false, |(instruction, _)| instruction.yields_value());
} else { } else {
return Err(ParseError::ExpectedTokenMultiple { return Err(ParseError::ExpectedTokenMultiple {
expected: &[TokenKind::If, TokenKind::LeftCurlyBrace], expected: &[TokenKind::If, TokenKind::LeftCurlyBrace],
@ -1095,14 +1069,6 @@ impl<'src> Parser<'src> {
}); });
} }
self.current_is_expression = self
.chunk
.instructions()
.last()
.map_or(false, |(instruction, _)| instruction.yields_value());
self.optimize_statement();
Ok(()) Ok(())
} }
@ -1195,11 +1161,29 @@ impl<'src> Parser<'src> {
Ok(()) Ok(())
} }
fn parse_top_level(&mut self) -> Result<(), ParseError> {
loop {
self.parse_statement(Allowed {
assignment: true,
explicit_return: false,
implicit_return: true,
})?;
if self.is_eof() || self.allow(Token::RightCurlyBrace)? {
self.parse_implicit_return()?;
break;
}
}
Ok(())
}
fn parse_statement(&mut self, allowed: Allowed) -> Result<(), ParseError> { fn parse_statement(&mut self, allowed: Allowed) -> Result<(), ParseError> {
self.parse(Precedence::None, allowed)?; self.parse(Precedence::None, allowed)?;
if allowed.implicit_return { if self.allow(Token::Semicolon)? {
self.parse_implicit_return()?; self.current_is_expression = false;
} }
Ok(()) Ok(())
@ -1259,30 +1243,15 @@ impl<'src> Parser<'src> {
} }
fn parse_implicit_return(&mut self) -> Result<(), ParseError> { fn parse_implicit_return(&mut self) -> Result<(), ParseError> {
if !self.current_is_expression {
if self.is_eof() {
self.emit_instruction(Instruction::r#return(false), self.current_position);
}
return Ok(());
}
let end_of_statement = matches!(
self.current_token,
Token::Eof | Token::RightCurlyBrace | Token::Semicolon
);
let has_semicolon = self.allow(Token::Semicolon)?; let has_semicolon = self.allow(Token::Semicolon)?;
let returned = self
.chunk
.instructions()
.last()
.map(|(instruction, _)| matches!(instruction.operation(), Operation::Return))
.unwrap_or(false);
if end_of_statement && !has_semicolon && !returned { if has_semicolon {
self.emit_instruction(Instruction::r#return(true), self.current_position);
} else if self.is_eof() {
self.emit_instruction(Instruction::r#return(false), self.current_position); self.emit_instruction(Instruction::r#return(false), self.current_position);
} else {
self.emit_instruction(
Instruction::r#return(self.current_is_expression),
self.current_position,
);
} }
Ok(()) Ok(())
@ -1426,16 +1395,7 @@ impl<'src> Parser<'src> {
}; };
function_parser.expect(Token::LeftCurlyBrace)?; function_parser.expect(Token::LeftCurlyBrace)?;
function_parser.parse_top_level()?;
while function_parser.current_token != Token::RightCurlyBrace {
function_parser.parse_statement(Allowed {
assignment: true,
explicit_return: true,
implicit_return: true,
})?;
}
function_parser.advance()?;
self.previous_token = function_parser.previous_token; self.previous_token = function_parser.previous_token;
self.previous_position = function_parser.previous_position; self.previous_position = function_parser.previous_position;
@ -1531,13 +1491,6 @@ impl<'src> Parser<'src> {
Ok(()) Ok(())
} }
fn parse_semicolon(&mut self, _: Allowed) -> Result<(), ParseError> {
self.current_is_expression = false;
self.optimize_statement();
self.advance()
}
fn expect_expression(&mut self, _: Allowed) -> Result<(), ParseError> { fn expect_expression(&mut self, _: Allowed) -> Result<(), ParseError> {
if self.current_token.is_expression() { if self.current_token.is_expression() {
Ok(()) Ok(())
@ -1874,7 +1827,7 @@ impl From<&Token<'_>> for ParseRule<'_> {
precedence: Precedence::None, precedence: Precedence::None,
}, },
Token::Semicolon => ParseRule { Token::Semicolon => ParseRule {
prefix: Some(Parser::parse_semicolon), prefix: Some(Parser::expect_expression),
infix: None, infix: None,
precedence: Precedence::None, precedence: Precedence::None,
}, },

View File

@ -99,9 +99,9 @@ fn if_else_assigment() {
Instruction::call_native(9, NativeFunction::Panic, 0), Instruction::call_native(9, NativeFunction::Panic, 0),
Span(121, 128) Span(121, 128)
), ),
(Instruction::r#move(8, 4), Span(138, 139)), (Instruction::r#move(9, 4), Span(138, 139)),
(Instruction::define_local(8, 0, false), Span(13, 14)), (Instruction::define_local(9, 0, false), Span(13, 14)),
(Instruction::get_local(9, 0), Span(148, 149)), (Instruction::get_local(10, 0), Span(148, 149)),
(Instruction::r#return(true), Span(149, 149)), (Instruction::r#return(true), Span(149, 149)),
], ],
vec![ vec![
@ -156,7 +156,7 @@ fn if_else_complex() {
(Instruction::load_constant(6, 8, false), Span(80, 81)), (Instruction::load_constant(6, 8, false), Span(80, 81)),
(Instruction::load_constant(7, 9, false), Span(83, 84)), (Instruction::load_constant(7, 9, false), Span(83, 84)),
(Instruction::r#move(7, 3), Span(95, 95)), (Instruction::r#move(7, 3), Span(95, 95)),
(Instruction::r#return(true), Span(95, 95)), (Instruction::r#return(false), Span(95, 95)),
], ],
vec![ vec![
Value::integer(1), Value::integer(1),
@ -172,7 +172,9 @@ fn if_else_complex() {
], ],
vec![] vec![]
)) ))
) );
assert_eq!(run(source), Ok(None));
} }
// #[test] // #[test]
@ -265,7 +267,8 @@ fn if_else_false() {
Instruction::call_native(0, NativeFunction::Panic, 0), Instruction::call_native(0, NativeFunction::Panic, 0),
Span(12, 19) Span(12, 19)
), ),
(Instruction::load_constant(0, 2, true), Span(29, 31)), (Instruction::load_constant(1, 2, true), Span(29, 31)),
(Instruction::r#move(1, 0), Span(33, 33)),
(Instruction::r#return(true), Span(33, 33)), (Instruction::r#return(true), Span(33, 33)),
], ],
vec![Value::integer(1), Value::integer(2), Value::integer(42)], vec![Value::integer(1), Value::integer(2), Value::integer(42)],
@ -297,6 +300,7 @@ fn if_else_true() {
Instruction::call_native(1, NativeFunction::Panic, 0), Instruction::call_native(1, NativeFunction::Panic, 0),
Span(24, 31) Span(24, 31)
), ),
(Instruction::r#move(1, 0), Span(33, 33)),
(Instruction::r#return(true), Span(33, 33)) (Instruction::r#return(true), Span(33, 33))
], ],
vec![Value::integer(1), Value::integer(1), Value::integer(42)], vec![Value::integer(1), Value::integer(1), Value::integer(42)],

View File

@ -227,140 +227,6 @@ fn empty_list() {
assert_eq!(run(source), Ok(Some(Value::list(0, 0, Type::Any)))); assert_eq!(run(source), Ok(Some(Value::list(0, 0, Type::Any))));
} }
#[test]
fn function() {
let source = "fn(a: int, b: int) -> int { a + b }";
assert_eq!(
run(source),
Ok(Some(Value::function(
Chunk::with_data(
None,
vec![
(Instruction::add(2, 0, 1), Span(30, 31)),
(Instruction::r#return(true), Span(34, 35)),
],
vec![],
vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
]
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)),
}
)))
);
}
#[test]
fn function_declaration() {
let source = "fn add (a: int, b: int) -> int { a + b }";
assert_eq!(
parse(source),
Ok(Chunk::with_data(
None,
vec![
(Instruction::load_constant(0, 0, false), Span(0, 40)),
(Instruction::define_local(0, 0, false), Span(3, 6)),
(Instruction::r#return(false), Span(40, 40))
],
vec![Value::function(
Chunk::with_data(
None,
vec![
(Instruction::add(2, 0, 1), Span(35, 36)),
(Instruction::r#return(true), Span(39, 40)),
],
vec![],
vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
]
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)),
},
)],
vec![Local::new(
Identifier::new("add"),
Some(Type::Function(FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)),
})),
false,
0,
0
),],
)),
);
assert_eq!(run(source), Ok(None));
}
#[test]
fn function_call() {
let source = "fn(a: int, b: int) -> int { a + b }(1, 2)";
assert_eq!(
parse(source),
Ok(Chunk::with_data(
None,
vec![
(Instruction::load_constant(0, 0, false), Span(0, 36)),
(Instruction::load_constant(1, 1, false), Span(36, 37)),
(Instruction::load_constant(2, 2, false), Span(39, 40)),
(Instruction::call(3, 0, 2), Span(35, 41)),
(Instruction::r#return(true), Span(41, 41)),
],
vec![
Value::function(
Chunk::with_data(
None,
vec![
(Instruction::add(2, 0, 1), Span(30, 31)),
(Instruction::r#return(true), Span(34, 35)),
],
vec![],
vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
]
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)),
}
),
Value::integer(1),
Value::integer(2)
],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(Value::integer(3))));
}
#[test] #[test]
fn list() { fn list() {
let source = "[1, 2, 3]"; let source = "[1, 2, 3]";

View File

@ -0,0 +1,135 @@
use dust_lang::*;
#[test]
fn function() {
let source = "fn(a: int, b: int) -> int { a + b }";
assert_eq!(
run(source),
Ok(Some(Value::function(
Chunk::with_data(
None,
vec![
(Instruction::add(2, 0, 1), Span(30, 31)),
(Instruction::r#return(true), Span(35, 35)),
],
vec![],
vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
]
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)),
}
)))
);
}
#[test]
fn function_declaration() {
let source = "fn add (a: int, b: int) -> int { a + b }";
assert_eq!(
parse(source),
Ok(Chunk::with_data(
None,
vec![
(Instruction::load_constant(0, 0, false), Span(0, 40)),
(Instruction::define_local(0, 0, false), Span(3, 6)),
(Instruction::r#return(false), Span(40, 40))
],
vec![Value::function(
Chunk::with_data(
None,
vec![
(Instruction::add(2, 0, 1), Span(35, 36)),
(Instruction::r#return(true), Span(40, 40)),
],
vec![],
vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
]
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)),
},
)],
vec![Local::new(
Identifier::new("add"),
Some(Type::Function(FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)),
})),
false,
0,
0
),],
)),
);
assert_eq!(run(source), Ok(None));
}
#[test]
fn function_call() {
let source = "fn(a: int, b: int) -> int { a + b }(1, 2)";
assert_eq!(
parse(source),
Ok(Chunk::with_data(
None,
vec![
(Instruction::load_constant(0, 0, false), Span(0, 36)),
(Instruction::load_constant(1, 1, false), Span(36, 37)),
(Instruction::load_constant(2, 2, false), Span(39, 40)),
(Instruction::call(3, 0, 2), Span(35, 41)),
(Instruction::r#return(true), Span(41, 41)),
],
vec![
Value::function(
Chunk::with_data(
None,
vec![
(Instruction::add(2, 0, 1), Span(30, 31)),
(Instruction::r#return(true), Span(35, 36)),
],
vec![],
vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
]
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)),
}
),
Value::integer(1),
Value::integer(2)
],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(Value::integer(3))));
}

View File

@ -15,7 +15,7 @@ fn panic() {
Instruction::call_native(2, NativeFunction::Panic, 2), Instruction::call_native(2, NativeFunction::Panic, 2),
Span(0, 27) Span(0, 27)
), ),
(Instruction::r#return(false), Span(27, 27)) (Instruction::r#return(true), Span(27, 27))
], ],
vec![Value::string("Goodbye world!"), Value::integer(42)], vec![Value::string("Goodbye world!"), Value::integer(42)],
vec![] vec![]