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 and string
const ALL: u8 = 0b0000_1001;
const ANY: u8 = 0b0000_1010;
const APPEND: u8 = 0b0000_1011;
const CONTAINS: u8 = 0b0000_1100;
const DEDUP: u8 = 0b0000_1101;
const ENDS_WITH: u8 = 0b0000_1110;
const FIND: u8 = 0b0000_1111;
const GET: u8 = 0b0001_0000;
const INDEX_OF: u8 = 0b0001_0001;
const LENGTH: u8 = 0b0001_0010;
const PREPEND: u8 = 0b0001_0011;
const REPLACE: u8 = 0b0001_0100;
const SET: u8 = 0b0001_0101;
const STARTS_WITH: u8 = 0b0001_0110;
const SLICE: u8 = 0b0001_0111;
const SORT: u8 = 0b0001_1000;
const SPLIT: u8 = 0b0001_1001;
// List // List
const ALL: u8 = 0b0000_0110; const FLATTEN: u8 = 0b0001_1010;
const ANY: u8 = 0b0000_0111; const JOIN: u8 = 0b0001_1011;
const APPEND: u8 = 0b0000_1000; const MAP: u8 = 0b0001_1100;
const CONTAINS: u8 = 0b0000_1001; const REDUCE: u8 = 0b0001_1101;
const FIND: u8 = 0b0000_1010; const REMOVE: u8 = 0b0001_1110;
const FLATTEN: u8 = 0b0000_1011; const REVERSE: u8 = 0b0001_1111;
const GET: u8 = 0b0000_1100; const UNZIP: u8 = 0b0010_0000;
const INDEX_OF: u8 = 0b0000_1101; const ZIP: u8 = 0b0010_0001;
const JOIN: u8 = 0b0000_1110;
const LENGTH: u8 = 0b0000_1111;
const MAP: u8 = 0b0001_0000;
const PREPEND: u8 = 0b0001_0001;
const REDUCE: u8 = 0b0001_0010;
const REMOVE: u8 = 0b0001_0011;
const REVERSE: u8 = 0b0001_0100;
const SET: u8 = 0b0001_0101;
const SLICE: u8 = 0b0001_0110;
const SORT: u8 = 0b0001_0111;
const SPLIT: u8 = 0b0001_1000;
const UNZIP: u8 = 0b0001_1001;
const ZIP: u8 = 0b0001_1010;
// String // String
const CHAR_AT: u8 = 0b0001_1011; const BYTES: u8 = 0b0010_0010;
const CHAR_CODE_AT: u8 = 0b0001_1100; const CHAR_AT: u8 = 0b0010_0011;
const CHARS: u8 = 0b0001_1101; const CHAR_CODE_AT: u8 = 0b0010_0100;
const ENDS_WITH: u8 = 0b0001_1110; const CHARS: u8 = 0b0010_0101;
const FORMAT: u8 = 0b0001_1111; const FORMAT: u8 = 0b0010_0110;
const INCLUDES: u8 = 0b0010_0000; const REPEAT: u8 = 0b0010_0111;
const MATCH: u8 = 0b0010_0001; const SPLIT_AT: u8 = 0b0010_1000;
const PAD_END: u8 = 0b0010_0010; const SPLIT_LINES: u8 = 0b0010_1001;
const PAD_START: u8 = 0b0010_0011; const SPLIT_WHITESPACE: u8 = 0b0010_1010;
const REPEAT: u8 = 0b0010_0100; const TO_LOWER_CASE: u8 = 0b0010_1011;
const REPLACE: u8 = 0b0010_0101; const TO_UPPER_CASE: u8 = 0b0010_1100;
const SPLIT_AT: u8 = 0b0010_0110; const TRIM: u8 = 0b0010_1101;
const SPLIT_LINES: u8 = 0b0010_0111; const TRIM_END: u8 = 0b0010_1110;
const SPLIT_WHITESPACE: u8 = 0b0010_1000; const TRIM_START: u8 = 0b0010_1111;
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 // I/O
const READ_LINE: u8 = 0b0010_1111; const READ_LINE: u8 = 0b0011_0000;
const WRITE_LINE: u8 = 0b0011_0001; const WRITE_LINE: u8 = 0b0011_0001;
const WRITE: u8 = 0b0011_0000; const WRITE: u8 = 0b0011_0010;
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] // Random
pub enum NativeFunction { const RANDOM: u8 = 0b0011_0011;
Panic = PANIC as isize, const RANDOM_BYTE: u8 = 0b0011_0100;
const RANDOM_BYTES: u8 = 0b0011_0101;
// Type conversion const RANDOM_CHAR: u8 = 0b0011_0110;
Parse = PARSE as isize, const RANDOM_FLOAT: u8 = 0b0011_0111;
ToByte = TO_BYTE as isize, const RANDOM_INTEGER: u8 = 0b0011_1000;
ToFloat = TO_FLOAT as isize, const RANDOM_RANGE: u8 = 0b0011_1001;
ToInteger = TO_INTEGER as isize, const RANDOM_STRING: u8 = 0b0011_1010;
ToString = TO_STRING as isize,
// List
All = ALL as isize,
Any = ANY as isize,
Append = APPEND as isize,
Contains = CONTAINS as isize,
Find = FIND as isize,
Flatten = FLATTEN as isize,
Get = GET as isize,
IndexOf = INDEX_OF as isize,
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
CharAt = CHAR_AT as isize,
CharCodeAt = CHAR_CODE_AT as isize,
Chars = CHARS as isize,
EndsWith = ENDS_WITH as isize,
Format = FORMAT as isize,
Includes = INCLUDES as isize,
Match = MATCH as isize,
PadEnd = PAD_END as isize,
PadStart = PAD_START as isize,
Repeat = REPEAT as isize,
Replace = REPLACE as isize,
SplitAt = SPLIT_AT as isize,
SplitLines = SPLIT_LINES as isize,
SplitWhitespace = SPLIT_WHITESPACE as isize,
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
ReadLine = READ_LINE as isize,
WriteLine = WRITE_LINE as isize,
Write = WRITE as isize,
}
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![]