From 1947d66be59640756582e5f1f58d6940a388f17d Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 2 Nov 2024 21:24:41 -0400 Subject: [PATCH] Overhaul implicit returns and add lots of new native functions --- dust-lang/src/native_function.rs | 391 ++++++++++++---------------- dust-lang/src/parser.rs | 119 +++------ dust-lang/tests/control_flow.rs | 16 +- dust-lang/tests/expressions.rs | 134 ---------- dust-lang/tests/functions.rs | 135 ++++++++++ dust-lang/tests/native_functions.rs | 2 +- 6 files changed, 346 insertions(+), 451 deletions(-) create mode 100644 dust-lang/tests/functions.rs diff --git a/dust-lang/src/native_function.rs b/dust-lang/src/native_function.rs index bdc946f..a6baa44 100644 --- a/dust-lang/src/native_function.rs +++ b/dust-lang/src/native_function.rs @@ -8,129 +8,88 @@ use serde::{Deserialize, Serialize}; 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 -const PARSE: u8 = 0b0000_0001; -const TO_BYTE: u8 = 0b0000_0010; -const TO_FLOAT: u8 = 0b0000_0011; -const TO_INTEGER: u8 = 0b0000_0100; -const TO_STRING: u8 = 0b0000_0101; +const PARSE: u8 = 0b0000_0100; +const TO_BYTE: u8 = 0b0000_0101; +const TO_FLOAT: u8 = 0b0000_0110; +const TO_INTEGER: u8 = 0b0000_0111; +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 -const ALL: u8 = 0b0000_0110; -const ANY: u8 = 0b0000_0111; -const APPEND: u8 = 0b0000_1000; -const CONTAINS: u8 = 0b0000_1001; -const FIND: u8 = 0b0000_1010; -const FLATTEN: u8 = 0b0000_1011; -const GET: u8 = 0b0000_1100; -const INDEX_OF: u8 = 0b0000_1101; -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; +const FLATTEN: u8 = 0b0001_1010; +const JOIN: u8 = 0b0001_1011; +const MAP: u8 = 0b0001_1100; +const REDUCE: u8 = 0b0001_1101; +const REMOVE: u8 = 0b0001_1110; +const REVERSE: u8 = 0b0001_1111; +const UNZIP: u8 = 0b0010_0000; +const ZIP: u8 = 0b0010_0001; // 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; +const BYTES: u8 = 0b0010_0010; +const CHAR_AT: u8 = 0b0010_0011; +const CHAR_CODE_AT: u8 = 0b0010_0100; +const CHARS: u8 = 0b0010_0101; +const FORMAT: u8 = 0b0010_0110; +const REPEAT: u8 = 0b0010_0111; +const SPLIT_AT: u8 = 0b0010_1000; +const SPLIT_LINES: u8 = 0b0010_1001; +const SPLIT_WHITESPACE: u8 = 0b0010_1010; +const TO_LOWER_CASE: u8 = 0b0010_1011; +const TO_UPPER_CASE: u8 = 0b0010_1100; +const TRIM: u8 = 0b0010_1101; +const TRIM_END: u8 = 0b0010_1110; +const TRIM_START: u8 = 0b0010_1111; // I/O -const READ_LINE: u8 = 0b0010_1111; +const READ_LINE: u8 = 0b0011_0000; 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)] -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 - 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, -} +// 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 { ($(($name:ident, $str:expr, $returns_value:expr)),*) => { + #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] + pub enum NativeFunction { + $( + $name, + )* + } + impl NativeFunction { pub fn as_str(&self) -> &'static str { 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! { - (Panic, "panic", false), + // Assertion + (Assert, "assert", false), + (AssertEq, "assert_eq", false), + (AssertNe, "assert_ne", false), + (Panic, "panic", true), // Type conversion (Parse, "parse", true), @@ -172,45 +134,45 @@ impl_from_str_for_native_function! { (ToInteger, "to_integer", true), (ToString, "to_string", true), - // List + // List and string (All, "all", true), (Any, "any", true), - (Append, "append", true), + (Append, "append", false), (Contains, "contains", true), + (Dedup, "dedup", false), + (EndsWith, "ends_with", true), (Find, "find", true), - (Flatten, "flatten", true), (Get, "get", true), (IndexOf, "index_of", true), - (Join, "join", true), (Length, "length", true), - (Map, "map", true), - (Prepend, "prepend", true), - (Reduce, "reduce", true), - (Remove, "remove", true), - (Reverse, "reverse", true), - (Set, "set", true), + (Prepend, "prepend", false), + (Replace, "replace", false), + (Set, "set", false), + (StartsWith, "starts_with", true), (Slice, "slice", true), - (Sort, "sort", true), + (Sort, "sort", false), (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), (Zip, "zip", true), // String + (Bytes, "bytes", true), (CharAt, "char_at", true), (CharCodeAt, "char_code_at", true), (Chars, "chars", true), - (EndsWith, "ends_with", true), (Format, "format", true), - (Includes, "includes", true), - (Match, "match", true), - (PadEnd, "pad_end", true), - (PadStart, "pad_start", true), (Repeat, "repeat", true), - (Replace, "replace", true), (SplitAt, "split_at", true), (SplitLines, "split_lines", true), (SplitWhitespace, "split_whitespace", true), - (StartsWith, "starts_with", true), (ToLowerCase, "to_lower_case", true), (ToUpperCase, "to_upper_case", true), (Trim, "trim", true), @@ -220,7 +182,17 @@ impl_from_str_for_native_function! { // I/O (ReadLine, "read_line", true), (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 { @@ -276,51 +248,6 @@ impl NativeFunction { 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 NativeFunction::ReadLine => { let mut buffer = String::new(); @@ -391,6 +318,7 @@ impl NativeFunction { None } + _ => todo!(), }; Ok(return_value) @@ -400,64 +328,64 @@ impl NativeFunction { impl From for NativeFunction { fn from(byte: u8) -> Self { match byte { - PANIC => NativeFunction::Panic, - - // Type conversion - 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, + ALL => NativeFunction::Assert, + ASSERT_EQ => NativeFunction::AssertEq, + ASSERT_NE => NativeFunction::AssertNe, APPEND => NativeFunction::Append, + ANY => NativeFunction::Any, + BYTES => NativeFunction::Bytes, + CHAR_AT => NativeFunction::CharAt, + CHAR_CODE_AT => NativeFunction::CharCodeAt, + CHARS => NativeFunction::Chars, CONTAINS => NativeFunction::Contains, + DEDUP => NativeFunction::Dedup, + ENDS_WITH => NativeFunction::EndsWith, FIND => NativeFunction::Find, FLATTEN => NativeFunction::Flatten, + FORMAT => NativeFunction::Format, GET => NativeFunction::Get, INDEX_OF => NativeFunction::IndexOf, JOIN => NativeFunction::Join, LENGTH => NativeFunction::Length, MAP => NativeFunction::Map, + PANIC => NativeFunction::Panic, + PARSE => NativeFunction::Parse, 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, REMOVE => NativeFunction::Remove, + REPEAT => NativeFunction::Repeat, + REPLACE => NativeFunction::Replace, REVERSE => NativeFunction::Reverse, SET => NativeFunction::Set, SLICE => NativeFunction::Slice, SORT => NativeFunction::Sort, 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_LINES => NativeFunction::SplitLines, SPLIT_WHITESPACE => NativeFunction::SplitWhitespace, STARTS_WITH => NativeFunction::StartsWith, + TO_BYTE => NativeFunction::ToByte, + TO_FLOAT => NativeFunction::ToFloat, + TO_INTEGER => NativeFunction::ToInteger, TO_LOWER_CASE => NativeFunction::ToLowerCase, + TO_STRING => NativeFunction::ToString, TO_UPPER_CASE => NativeFunction::ToUpperCase, TRIM => NativeFunction::Trim, TRIM_END => NativeFunction::TrimEnd, TRIM_START => NativeFunction::TrimStart, - - // I/O - READ_LINE => NativeFunction::ReadLine, + UNZIP => NativeFunction::Unzip, WRITE => NativeFunction::Write, WRITE_LINE => NativeFunction::WriteLine, + ZIP => NativeFunction::Zip, _ => { if cfg!(test) { panic!("Invalid native function byte: {}", byte) @@ -472,56 +400,65 @@ impl From for NativeFunction { impl From for u8 { fn from(native_function: NativeFunction) -> Self { 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::Any => ANY, 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::Dedup => DEDUP, + NativeFunction::EndsWith => ENDS_WITH, NativeFunction::Find => FIND, NativeFunction::Flatten => FLATTEN, + NativeFunction::Format => FORMAT, NativeFunction::Get => GET, NativeFunction::IndexOf => INDEX_OF, NativeFunction::Join => JOIN, NativeFunction::Length => LENGTH, NativeFunction::Map => MAP, + NativeFunction::Panic => PANIC, + NativeFunction::Parse => PARSE, 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::Remove => REMOVE, + NativeFunction::Repeat => REPEAT, + NativeFunction::Replace => REPLACE, NativeFunction::Reverse => REVERSE, NativeFunction::Set => SET, NativeFunction::Slice => SLICE, NativeFunction::Sort => SORT, 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::SplitLines => SPLIT_LINES, NativeFunction::SplitWhitespace => SPLIT_WHITESPACE, NativeFunction::StartsWith => STARTS_WITH, + NativeFunction::ToByte => TO_BYTE, + NativeFunction::ToFloat => TO_FLOAT, + NativeFunction::ToInteger => TO_INTEGER, NativeFunction::ToLowerCase => TO_LOWER_CASE, + NativeFunction::ToString => TO_STRING, NativeFunction::ToUpperCase => TO_UPPER_CASE, NativeFunction::Trim => TRIM, NativeFunction::TrimEnd => TRIM_END, NativeFunction::TrimStart => TRIM_START, - NativeFunction::ReadLine => READ_LINE, - NativeFunction::WriteLine => WRITE_LINE, + NativeFunction::Unzip => UNZIP, NativeFunction::Write => WRITE, + NativeFunction::WriteLine => WRITE_LINE, + NativeFunction::Zip => ZIP, } } } diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 7d17414..6443137 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -17,19 +17,9 @@ pub fn parse(source: &str) -> Result { let lexer = Lexer::new(source); let mut parser = Parser::new(lexer).map_err(|error| DustError::Parse { error, source })?; - loop { - parser - .parse_statement(Allowed { - assignment: true, - explicit_return: false, - implicit_return: true, - }) - .map_err(|error| DustError::Parse { error, source })?; - - if parser.is_eof() { - break; - } - } + parser + .parse_top_level() + .map_err(|error| DustError::Parse { error, source })?; Ok(parser.finish()) } @@ -996,9 +986,10 @@ impl<'src> Parser<'src> { .chunk .instructions() .iter() + .rev() .find_map(|(instruction, _)| { - if !matches!(instruction.operation(), Operation::Jump) { - Some(true) + if !matches!(instruction.operation(), Operation::Jump | Operation::Move) { + Some(instruction.yields_value()) } else { None } @@ -1010,6 +1001,7 @@ impl<'src> Parser<'src> { let if_last_register = self.next_register().saturating_sub(1); self.parse_else(allowed, block_allowed)?; + self.optimize_statement(); let else_last_register = self.next_register().saturating_sub(1); let else_end = self.chunk.len(); @@ -1022,19 +1014,7 @@ impl<'src> Parser<'src> { ); } - self.current_is_expression = if_block_is_expression - && self - .chunk - .instructions() - .iter() - .find_map(|(instruction, _)| { - if !matches!(instruction.operation(), Operation::Jump) { - Some(true) - } else { - None - } - }) - .unwrap_or(false); + self.current_is_expression = if_block_is_expression && self.current_is_expression; if jump_distance == 1 { if let Some(skippable) = self.get_last_jumpable_mut() { @@ -1081,12 +1061,6 @@ impl<'src> Parser<'src> { self.current_position, )?; } - - self.current_is_expression = self - .chunk - .instructions() - .last() - .map_or(false, |(instruction, _)| instruction.yields_value()); } else { return Err(ParseError::ExpectedTokenMultiple { 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(()) } @@ -1195,11 +1161,29 @@ impl<'src> Parser<'src> { 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> { self.parse(Precedence::None, allowed)?; - if allowed.implicit_return { - self.parse_implicit_return()?; + if self.allow(Token::Semicolon)? { + self.current_is_expression = false; } Ok(()) @@ -1259,30 +1243,15 @@ impl<'src> Parser<'src> { } 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 returned = self - .chunk - .instructions() - .last() - .map(|(instruction, _)| matches!(instruction.operation(), Operation::Return)) - .unwrap_or(false); - if end_of_statement && !has_semicolon && !returned { - self.emit_instruction(Instruction::r#return(true), self.current_position); - } else if self.is_eof() { + if has_semicolon { 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(()) @@ -1426,16 +1395,7 @@ impl<'src> Parser<'src> { }; function_parser.expect(Token::LeftCurlyBrace)?; - - while function_parser.current_token != Token::RightCurlyBrace { - function_parser.parse_statement(Allowed { - assignment: true, - explicit_return: true, - implicit_return: true, - })?; - } - - function_parser.advance()?; + function_parser.parse_top_level()?; self.previous_token = function_parser.previous_token; self.previous_position = function_parser.previous_position; @@ -1531,13 +1491,6 @@ impl<'src> Parser<'src> { 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> { if self.current_token.is_expression() { Ok(()) @@ -1874,7 +1827,7 @@ impl From<&Token<'_>> for ParseRule<'_> { precedence: Precedence::None, }, Token::Semicolon => ParseRule { - prefix: Some(Parser::parse_semicolon), + prefix: Some(Parser::expect_expression), infix: None, precedence: Precedence::None, }, diff --git a/dust-lang/tests/control_flow.rs b/dust-lang/tests/control_flow.rs index 02fe324..3a41ce7 100644 --- a/dust-lang/tests/control_flow.rs +++ b/dust-lang/tests/control_flow.rs @@ -99,9 +99,9 @@ fn if_else_assigment() { Instruction::call_native(9, NativeFunction::Panic, 0), Span(121, 128) ), - (Instruction::r#move(8, 4), Span(138, 139)), - (Instruction::define_local(8, 0, false), Span(13, 14)), - (Instruction::get_local(9, 0), Span(148, 149)), + (Instruction::r#move(9, 4), Span(138, 139)), + (Instruction::define_local(9, 0, false), Span(13, 14)), + (Instruction::get_local(10, 0), Span(148, 149)), (Instruction::r#return(true), Span(149, 149)), ], vec![ @@ -156,7 +156,7 @@ fn if_else_complex() { (Instruction::load_constant(6, 8, false), Span(80, 81)), (Instruction::load_constant(7, 9, false), Span(83, 84)), (Instruction::r#move(7, 3), Span(95, 95)), - (Instruction::r#return(true), Span(95, 95)), + (Instruction::r#return(false), Span(95, 95)), ], vec![ Value::integer(1), @@ -172,7 +172,9 @@ fn if_else_complex() { ], vec![] )) - ) + ); + + assert_eq!(run(source), Ok(None)); } // #[test] @@ -265,7 +267,8 @@ fn if_else_false() { Instruction::call_native(0, NativeFunction::Panic, 0), 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)), ], 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), Span(24, 31) ), + (Instruction::r#move(1, 0), Span(33, 33)), (Instruction::r#return(true), Span(33, 33)) ], vec![Value::integer(1), Value::integer(1), Value::integer(42)], diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs index 942b44d..5ea5c03 100644 --- a/dust-lang/tests/expressions.rs +++ b/dust-lang/tests/expressions.rs @@ -227,140 +227,6 @@ fn empty_list() { 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] fn list() { let source = "[1, 2, 3]"; diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs new file mode 100644 index 0000000..0953b3e --- /dev/null +++ b/dust-lang/tests/functions.rs @@ -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)))); +} diff --git a/dust-lang/tests/native_functions.rs b/dust-lang/tests/native_functions.rs index 7a0aa56..655d662 100644 --- a/dust-lang/tests/native_functions.rs +++ b/dust-lang/tests/native_functions.rs @@ -15,7 +15,7 @@ fn panic() { Instruction::call_native(2, NativeFunction::Panic, 2), 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![]