From 4d7f59aee21754530d41c8e5876d6c0f019d1b5d Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 7 Aug 2024 18:24:25 -0400 Subject: [PATCH] Begin implementing better built-in features --- dust-lang/src/abstract_tree.rs | 153 ++++++++++++++++++++++-- dust-lang/src/analyzer.rs | 12 +- dust-lang/src/lex.rs | 29 ++--- dust-lang/src/lib.rs | 4 +- dust-lang/src/parse.rs | 91 +++++++++++++-- dust-lang/src/token.rs | 57 +++++---- dust-lang/src/value.rs | 47 +++++++- dust-lang/src/vm.rs | 208 ++++++++++++++++++++++++--------- 8 files changed, 482 insertions(+), 119 deletions(-) diff --git a/dust-lang/src/abstract_tree.rs b/dust-lang/src/abstract_tree.rs index a0729b2..b86683d 100644 --- a/dust-lang/src/abstract_tree.rs +++ b/dust-lang/src/abstract_tree.rs @@ -5,7 +5,7 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::{Identifier, ReservedIdentifier, Type, Value}; +use crate::{Identifier, Type, Value}; #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct AbstractSyntaxTree

{ @@ -40,6 +40,16 @@ pub enum Statement

{ // Expressions Add(Box>, Box>), + BuiltInFunctionCall { + function: BuiltInFunction, + type_arguments: Option>>, + value_arguments: Option>>, + }, + FunctionCall { + function: Box>, + type_arguments: Option>>, + value_arguments: Option>>, + }, PropertyAccess(Box>, Box>), List(Vec>), Multiply(Box>, Box>), @@ -47,7 +57,6 @@ pub enum Statement

{ // Hard-coded values Constant(Value), Identifier(Identifier), - ReservedIdentifier(ReservedIdentifier), } impl

Statement

{ @@ -55,17 +64,15 @@ impl

Statement

{ match self { Statement::Add(left, _) => left.statement.expected_type(variables), Statement::Assign(_, _) => None, + Statement::BuiltInFunctionCall { function, .. } => function.expected_type(), Statement::Constant(value) => Some(value.r#type(variables)), + Statement::FunctionCall { function, .. } => function.statement.expected_type(variables), Statement::Identifier(identifier) => variables .get(identifier) .map(|value| value.r#type(variables)), Statement::List(_) => None, Statement::Multiply(left, _) => left.statement.expected_type(variables), Statement::PropertyAccess(_, _) => None, - Statement::ReservedIdentifier(reserved) => match reserved { - ReservedIdentifier::IsEven | ReservedIdentifier::IsOdd => Some(Type::Boolean), - ReservedIdentifier::Length => Some(Type::Integer), - }, } } } @@ -75,6 +82,76 @@ impl

Display for Statement

{ match self { Statement::Assign(left, right) => write!(f, "{left} = {right}"), Statement::Add(left, right) => write!(f, "{left} + {right}"), + Statement::BuiltInFunctionCall { + function, + type_arguments: type_parameters, + value_arguments: value_parameters, + } => { + write!(f, "{function}")?; + + if let Some(type_parameters) = type_parameters { + write!(f, "<")?; + + for (i, type_parameter) in type_parameters.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + + write!(f, "{type_parameter}")?; + } + + write!(f, ">")?; + } + + write!(f, "(")?; + + if let Some(value_parameters) = value_parameters { + for (i, value_parameter) in value_parameters.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + + write!(f, "{value_parameter}")?; + } + } + + write!(f, ")") + } + Statement::FunctionCall { + function, + type_arguments: type_parameters, + value_arguments: value_parameters, + } => { + write!(f, "{function}")?; + + if let Some(type_parameters) = type_parameters { + write!(f, "<")?; + + for (i, type_parameter) in type_parameters.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + + write!(f, "{type_parameter}")?; + } + + write!(f, ">")?; + } + + write!(f, "(")?; + + if let Some(value_parameters) = value_parameters { + for (i, value_parameter) in value_parameters.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + + write!(f, "{value_parameter}")?; + } + } + + write!(f, ")") + } Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"), Statement::List(nodes) => { write!(f, "[")?; @@ -89,7 +166,69 @@ impl

Display for Statement

{ Statement::Multiply(left, right) => write!(f, "{left} * {right}"), Statement::Constant(value) => write!(f, "{value}"), Statement::Identifier(identifier) => write!(f, "{identifier}"), - Statement::ReservedIdentifier(identifier) => write!(f, "{identifier}"), } } } + +#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum BuiltInFunction { + IsEven, + IsOdd, + Length, +} + +impl BuiltInFunction { + pub fn name(&self) -> &'static str { + match self { + BuiltInFunction::IsEven => "is_even", + BuiltInFunction::IsOdd => "is_odd", + BuiltInFunction::Length => "length", + } + } + + pub fn call( + &self, + _type_arguments: Option>, + value_arguments: Option>, + ) -> Result { + match self { + BuiltInFunction::IsEven => { + if let Some(value_arguments) = value_arguments { + if value_arguments.len() == 1 { + if let Some(integer) = value_arguments[0].as_integer() { + Ok(Value::boolean(integer % 2 == 0)) + } else { + Err(BuiltInFunctionError::ExpectedInteger) + } + } else { + Err(BuiltInFunctionError::WrongNumberOfValueArguments) + } + } else { + Err(BuiltInFunctionError::WrongNumberOfValueArguments) + } + } + BuiltInFunction::IsOdd => todo!(), + BuiltInFunction::Length => todo!(), + } + } + + pub fn expected_type(&self) -> Option { + match self { + BuiltInFunction::IsEven => Some(Type::Boolean), + BuiltInFunction::IsOdd => Some(Type::Boolean), + BuiltInFunction::Length => Some(Type::Integer), + } + } +} + +impl Display for BuiltInFunction { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.name()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum BuiltInFunctionError { + ExpectedInteger, + WrongNumberOfValueArguments, +} diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index d583943..ec9485c 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -103,7 +103,17 @@ impl<'a, P: Clone> Analyzer<'a, P> { self.analyze_node(right)?; } + Statement::BuiltInFunctionCall { .. } => {} Statement::Constant(_) => {} + Statement::FunctionCall { function, .. } => { + if let Statement::Identifier(_) = &function.statement { + // Function is in the correct position + } else { + return Err(AnalyzerError::ExpectedIdentifier { + actual: function.as_ref().clone(), + }); + } + } Statement::Identifier(_) => { return Err(AnalyzerError::UnexpectedIdentifier { identifier: node.clone(), @@ -149,7 +159,6 @@ impl<'a, P: Clone> Analyzer<'a, P> { self.analyze_node(right)?; } - Statement::ReservedIdentifier(_) => {} } Ok(()) @@ -158,6 +167,7 @@ impl<'a, P: Clone> Analyzer<'a, P> { #[derive(Clone, Debug, PartialEq)] pub enum AnalyzerError

{ + ExpectedFunction { position: P }, ExpectedIdentifier { actual: Node

}, ExpectedIntegerOrFloat { actual: Node

}, UnexpectedIdentifier { identifier: Node

}, diff --git a/dust-lang/src/lex.rs b/dust-lang/src/lex.rs index aa88607..2ec2f94 100644 --- a/dust-lang/src/lex.rs +++ b/dust-lang/src/lex.rs @@ -5,7 +5,7 @@ //! - [`Lexer`], which lexes the input a token at a time use std::num::{ParseFloatError, ParseIntError}; -use crate::{Identifier, ReservedIdentifier, Span, Token}; +use crate::{Identifier, Span, Token}; /// Lexes the input and return a vector of tokens and their positions. /// @@ -245,9 +245,9 @@ impl<'a> Lexer<'a> { let token = match string { "true" => Token::Boolean(true), "false" => Token::Boolean(false), - "is_even" => Token::ReservedIdentifier(ReservedIdentifier::IsEven), - "is_odd" => Token::ReservedIdentifier(ReservedIdentifier::IsOdd), - "length" => Token::ReservedIdentifier(ReservedIdentifier::Length), + "is_even" => Token::IsEven, + "is_odd" => Token::IsOdd, + "length" => Token::Length, _ => Token::Identifier(Identifier::new(string)), }; @@ -298,19 +298,18 @@ mod tests { } #[test] - fn integer_property_access() { - let input = "42.is_even"; + fn property_access_function_call() { + let input = "42.is_even()"; assert_eq!( lex(input), Ok(vec![ (Token::Integer(42), (0, 2)), (Token::Dot, (2, 3)), - ( - Token::ReservedIdentifier(ReservedIdentifier::IsEven), - (3, 10) - ), - (Token::Eof, (10, 10)), + (Token::IsEven, (3, 10)), + (Token::LeftParenthesis, (10, 11)), + (Token::RightParenthesis, (11, 12)), + (Token::Eof, (12, 12)), ]) ) } @@ -328,13 +327,7 @@ mod tests { assert_eq!( lex(input), - Ok(vec![ - ( - Token::ReservedIdentifier(ReservedIdentifier::Length), - (0, 6) - ), - (Token::Eof, (6, 6)), - ]) + Ok(vec![(Token::Length, (0, 6)), (Token::Eof, (6, 6)),]) ) } diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 97f61f7..5007c1f 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -15,13 +15,13 @@ pub mod r#type; pub mod value; pub mod vm; -pub use abstract_tree::{AbstractSyntaxTree, Node, Statement}; +pub use abstract_tree::{AbstractSyntaxTree, BuiltInFunction, Node, Statement}; pub use analyzer::{analyze, Analyzer, AnalyzerError}; pub use identifier::Identifier; pub use lex::{lex, LexError, Lexer}; pub use parse::{parse, ParseError, Parser}; pub use r#type::Type; -pub use token::{ReservedIdentifier, Token}; +pub use token::Token; pub use value::{Value, ValueError}; pub use vm::{run, Vm, VmError}; diff --git a/dust-lang/src/parse.rs b/dust-lang/src/parse.rs index 89a4823..bb1844e 100644 --- a/dust-lang/src/parse.rs +++ b/dust-lang/src/parse.rs @@ -5,7 +5,10 @@ /// - `Parser` struct, which parses the input a statement at a time use std::collections::VecDeque; -use crate::{AbstractSyntaxTree, LexError, Lexer, Node, Span, Statement, Token, Value}; +use crate::{ + abstract_tree::BuiltInFunction, AbstractSyntaxTree, LexError, Lexer, Node, Span, Statement, + Token, Value, +}; /// Parses the input into an abstract syntax tree. /// @@ -206,15 +209,12 @@ impl<'src> Parser<'src> { (Token::LeftParenthesis, left_span) => { self.next_token()?; - let instruction = self.parse_node(0)?; + let node = self.parse_node(0)?; if let (Token::RightParenthesis, right_span) = self.current { self.next_token()?; - Ok(Node::new( - instruction.statement, - (left_span.0, right_span.1), - )) + Ok(Node::new(node.statement, (left_span.0, right_span.1))) } else { Err(ParseError::ExpectedClosingParenthesis { actual: self.current.0.clone(), @@ -225,14 +225,14 @@ impl<'src> Parser<'src> { (Token::LeftSquareBrace, left_span) => { self.next_token()?; - let mut instructions = Vec::new(); + let mut nodes = Vec::new(); loop { if let (Token::RightSquareBrace, right_span) = self.current { self.next_token()?; return Ok(Node::new( - Statement::List(instructions), + Statement::List(nodes), (left_span.0, right_span.1), )); } @@ -244,7 +244,7 @@ impl<'src> Parser<'src> { } if let Ok(instruction) = self.parse_node(0) { - instructions.push(instruction); + nodes.push(instruction); } else { return Err(ParseError::ExpectedClosingSquareBrace { actual: self.current.0.clone(), @@ -253,12 +253,52 @@ impl<'src> Parser<'src> { } } } - (Token::ReservedIdentifier(reserved), _) => { + (Token::IsEven, left_span) => { self.next_token()?; + let mut value_parameters = None; + + if let (Token::LeftParenthesis, _) = self.current { + self.next_token()?; + + value_parameters = Some(vec![self.parse_node(0)?]); + + loop { + self.next_token()?; + + if let (Token::RightParenthesis, _) = self.current { + break; + } + + if let (Token::Comma, _) = self.current { + self.next_token()?; + } + + value_parameters.as_mut().unwrap().push(self.parse_node(0)?); + } + } else { + return Err(ParseError::ExpectedOpeningParenthesis { + actual: self.current.0.clone(), + span: self.current.1, + }); + } + + if let (Token::RightParenthesis, _) = self.current { + self.next_token()?; + } else { + return Err(ParseError::ExpectedClosingParenthesis { + actual: self.current.0.clone(), + span: self.current.1, + }); + } + Ok(Node::new( - Statement::ReservedIdentifier(reserved), - self.current.1, + Statement::BuiltInFunctionCall { + function: BuiltInFunction::IsEven, + type_arguments: None, + value_arguments: value_parameters, + }, + left_span, )) } _ => Err(ParseError::UnexpectedToken(self.current.0.clone())), @@ -280,6 +320,7 @@ impl<'src> Parser<'src> { pub enum ParseError { ExpectedClosingParenthesis { actual: Token, span: Span }, ExpectedClosingSquareBrace { actual: Token, span: Span }, + ExpectedOpeningParenthesis { actual: Token, span: Span }, LexError(LexError), UnexpectedToken(Token), } @@ -308,6 +349,32 @@ mod tests { ); } + #[test] + fn property_access_function_call() { + let input = "42.is_even()"; + + assert_eq!( + parse(input), + Ok(AbstractSyntaxTree { + nodes: [Node::new( + Statement::PropertyAccess( + Box::new(Node::new(Statement::Constant(Value::integer(42)), (0, 2))), + Box::new(Node::new( + Statement::BuiltInFunctionCall { + function: BuiltInFunction::IsEven, + type_arguments: None, + value_arguments: None + }, + (3, 10) + )), + ), + (0, 10), + )] + .into() + }) + ); + } + #[test] fn list_access() { let input = "[1, 2, 3].0"; diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index 4725300..5b44181 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -1,44 +1,57 @@ -use std::fmt::Display; +use std::fmt::{self, Display, Formatter}; use serde::{Deserialize, Serialize}; use crate::Identifier; -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub enum Token { + Eof, + + Identifier(Identifier), + + // Hard-coded values Boolean(bool), + Float(f64), + Integer(i64), + + // Keywords + IsEven, + IsOdd, + Length, + + // Symbols Comma, Dot, - Eof, Equal, - Identifier(Identifier), - ReservedIdentifier(ReservedIdentifier), - Integer(i64), Plus, Star, LeftParenthesis, RightParenthesis, LeftSquareBrace, RightSquareBrace, - Float(f64), } -#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub enum ReservedIdentifier { - // Number properties - IsEven, - IsOdd, - - // List properties - Length, -} - -impl Display for ReservedIdentifier { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl Display for Token { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - ReservedIdentifier::IsEven => write!(f, "is_even"), - ReservedIdentifier::IsOdd => write!(f, "is_odd"), - ReservedIdentifier::Length => write!(f, "length"), + Token::Eof => write!(f, "EOF"), + Token::Identifier(identifier) => write!(f, "{identifier}"), + Token::Boolean(boolean) => write!(f, "{boolean}"), + Token::Float(float) => write!(f, "{float}"), + Token::Integer(integer) => write!(f, "{integer}"), + Token::IsEven => write!(f, "is_even"), + Token::IsOdd => write!(f, "is_odd"), + Token::Length => write!(f, "length"), + Token::Comma => write!(f, ","), + Token::Dot => write!(f, "."), + Token::Equal => write!(f, "="), + Token::Plus => write!(f, "+"), + Token::Star => write!(f, "*"), + Token::LeftParenthesis => write!(f, "("), + Token::RightParenthesis => write!(f, ")"), + Token::LeftSquareBrace => write!(f, "["), + Token::RightSquareBrace => write!(f, "]"), } } } diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 21083bd..c9a1899 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -12,7 +12,7 @@ use serde::{ Deserialize, Deserializer, Serialize, }; -use crate::{identifier::Identifier, Statement, Type}; +use crate::{identifier::Identifier, AbstractSyntaxTree, Type, Vm, VmError}; #[derive(Clone, Debug, PartialEq)] pub struct Value(Arc); @@ -30,6 +30,10 @@ impl Value { Value(Arc::new(ValueInner::Float(float))) } + pub fn function(function: Function) -> Self { + Value(Arc::new(ValueInner::Function(function))) + } + pub fn integer(integer: i64) -> Self { Value(Arc::new(ValueInner::Integer(integer))) } @@ -62,6 +66,14 @@ impl Value { } } + pub fn as_function(&self) -> Option<&Function> { + if let ValueInner::Function(function) = self.0.as_ref() { + Some(function) + } else { + None + } + } + pub fn as_list(&self) -> Option<&Vec> { if let ValueInner::List(list) = self.inner().as_ref() { Some(list) @@ -539,12 +551,39 @@ pub struct Function { pub name: Identifier, pub type_parameters: Option>, pub value_parameters: Option>, - pub body: Vec>, + pub body: AbstractSyntaxTree<()>, } impl Function { + pub fn call( + self, + type_arguments: Option>, + value_arguments: Option>, + variables: &HashMap, + ) -> Result, VmError<()>> { + let mut new_variables = variables.clone(); + + if let (Some(value_parameters), Some(value_arguments)) = + (self.value_parameters, value_arguments) + { + for ((identifier, _), value) in value_parameters.into_iter().zip(value_arguments) { + new_variables.insert(identifier, value); + } + } + + let mut vm = Vm::new(self.body); + + vm.run(&mut new_variables) + } + pub fn return_type(&self, variables: &HashMap) -> Option { - self.body.last().unwrap().expected_type(variables) + self.body + .nodes + .iter() + .last() + .unwrap() + .statement + .expected_type(variables) } } @@ -580,7 +619,7 @@ impl Display for Function { write!(f, ") {{")?; - for statement in &self.body { + for statement in &self.body.nodes { write!(f, "{}", statement)?; } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index c62e274..8bf82e2 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use crate::{ - parse, AbstractSyntaxTree, Analyzer, AnalyzerError, Identifier, Node, ParseError, - ReservedIdentifier, Span, Statement, Value, ValueError, + abstract_tree::BuiltInFunctionError, parse, AbstractSyntaxTree, Analyzer, AnalyzerError, + Identifier, Node, ParseError, Span, Statement, Value, ValueError, }; pub fn run( @@ -47,9 +47,6 @@ impl Vm

{ variables: &mut HashMap, ) -> Result, VmError

> { match node.statement { - Statement::Constant(value) => Ok(Some(value.clone())), - Statement::Identifier(_) => Ok(None), - Statement::ReservedIdentifier(_) => Ok(None), Statement::Add(left, right) => { let left_span = left.position; let left = if let Some(value) = self.run_node(*left, variables)? { @@ -92,6 +89,84 @@ impl Vm

{ Ok(None) } + Statement::BuiltInFunctionCall { + function, + type_arguments: _, + value_arguments: value_nodes, + } => { + let mut values = if let Some(nodes) = value_nodes { + let mut values = Vec::new(); + + for node in nodes { + let position = node.position; + let value = if let Some(value) = self.run_node(node, variables)? { + value + } else { + return Err(VmError::ExpectedValue { position }); + }; + + values.push(value); + } + + Some(values) + } else { + None + }; + let function_call_return = function.call(None, values)?; + + Ok(Some(function_call_return)) + } + Statement::Constant(value) => Ok(Some(value.clone())), + Statement::FunctionCall { + function: function_node, + type_arguments: type_parameter_nodes, + value_arguments: value_parameter_nodes, + } => { + let function_position = function_node.position; + let function_value = + if let Some(value) = self.run_node(*function_node, variables)? { + value + } else { + return Err(VmError::ExpectedValue { + position: function_position, + }); + }; + let function = if let Some(function) = function_value.as_function() { + function + } else { + return Err(VmError::AnaylyzerError(AnalyzerError::ExpectedFunction { + position: function_position, + })); + }; + + let value_parameters = if let Some(value_nodes) = value_parameter_nodes { + let mut value_parameters = Vec::new(); + + for node in value_nodes { + let position = node.position; + let value = if let Some(value) = self.run_node(node, variables)? { + value + } else { + return Err(VmError::ExpectedValue { position }); + }; + + value_parameters.push(value); + } + + Some(value_parameters) + } else { + None + }; + + Ok(function + .clone() + .call(None, value_parameters, variables) + .map_err(|error| VmError::FunctionCallFailed { + error: Box::new(error), + position: function_position, + })?) + } + Statement::Identifier(_) => Ok(None), Statement::List(nodes) => { let values = nodes .into_iter() @@ -110,7 +185,7 @@ impl Vm

{ Statement::Multiply(_, _) => todo!(), Statement::PropertyAccess(left, right) => { let left_span = left.position; - let left = if let Some(value) = self.run_node(*left, variables)? { + let left_value = if let Some(value) = self.run_node(*left, variables)? { value } else { return Err(VmError::ExpectedValue { @@ -119,39 +194,8 @@ impl Vm

{ }; let right_span = right.position; - if let Statement::ReservedIdentifier(reserved) = &right.statement { - match reserved { - ReservedIdentifier::IsEven => { - if let Some(integer) = left.as_integer() { - return Ok(Some(Value::boolean(integer % 2 == 0))); - } else { - return Err(VmError::ExpectedInteger { - position: right_span, - }); - } - } - ReservedIdentifier::IsOdd => { - if let Some(integer) = left.as_integer() { - return Ok(Some(Value::boolean(integer % 2 != 0))); - } else { - return Err(VmError::ExpectedInteger { - position: right_span, - }); - } - } - ReservedIdentifier::Length => { - if let Some(list) = left.as_list() { - return Ok(Some(Value::integer(list.len() as i64))); - } else { - return Err(VmError::ExpectedList { - position: right_span, - }); - } - } - } - } - - if let (Some(list), Statement::Constant(value)) = (left.as_list(), &right.statement) + if let (Some(list), Statement::Constant(value)) = + (left_value.as_list(), &right.statement) { if let Some(index) = value.as_integer() { let value = list.get(index as usize).cloned(); @@ -160,6 +204,43 @@ impl Vm

{ } } + if let ( + value, + Statement::BuiltInFunctionCall { + function, + type_arguments, + value_arguments: mut value_argument_nodes, + }, + ) = (left_value, right.statement) + { + if let Some(mut nodes) = value_argument_nodes.take() { + nodes.insert(0, Node::new(Statement::Constant(value), right_span)); + } + + let value_arguments = if let Some(value_nodes) = value_argument_nodes { + let mut value_arguments = Vec::new(); + + for node in value_nodes { + let position = node.position; + let value = if let Some(value) = self.run_node(node, variables)? { + value + } else { + return Err(VmError::ExpectedValue { position }); + }; + + value_arguments.push(value); + } + + Some(value_arguments) + } else { + None + }; + + let function_call_return = function.call(None, value_arguments)?; + + return Ok(Some(function_call_return)); + } + Err(VmError::ExpectedIdentifierOrInteger { position: right_span, }) @@ -176,11 +257,32 @@ pub enum VmError

{ // Anaylsis Failures // These should be prevented by running the analyzer before the VM - ExpectedIdentifier { position: P }, - ExpectedIdentifierOrInteger { position: P }, - ExpectedInteger { position: P }, - ExpectedList { position: P }, - ExpectedValue { position: P }, + BuiltInFunctionCallFailed(BuiltInFunctionError), + ExpectedIdentifier { + position: P, + }, + ExpectedIdentifierOrInteger { + position: P, + }, + ExpectedInteger { + position: P, + }, + ExpectedList { + position: P, + }, + ExpectedValue { + position: P, + }, + FunctionCallFailed { + error: Box>, + position: P, + }, +} + +impl

From for VmError

{ + fn from(v: BuiltInFunctionError) -> Self { + Self::BuiltInFunctionCallFailed(v) + } } impl

From> for VmError

{ @@ -217,7 +319,7 @@ mod tests { #[test] fn is_even() { - let input = "42.is_even"; + let input = "42.is_even()"; assert_eq!( run(input, &mut HashMap::new()), @@ -227,7 +329,7 @@ mod tests { #[test] fn is_odd() { - let input = "42.is_odd"; + let input = "42.is_odd()"; assert_eq!( run(input, &mut HashMap::new()), @@ -235,6 +337,13 @@ mod tests { ); } + #[test] + fn length() { + let input = "[1, 2, 3].length()"; + + assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(3)))); + } + #[test] fn list_access() { let input = "[1, 2, 3].1"; @@ -242,13 +351,6 @@ mod tests { assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(2)))); } - #[test] - fn property_access() { - let input = "[1, 2, 3].length"; - - assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(3)))); - } - #[test] fn add() { let input = "1 + 2";