Begin implementing better built-in features
This commit is contained in:
parent
06f3a9b746
commit
4d7f59aee2
@ -5,7 +5,7 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Identifier, ReservedIdentifier, Type, Value};
|
use crate::{Identifier, Type, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct AbstractSyntaxTree<P> {
|
pub struct AbstractSyntaxTree<P> {
|
||||||
@ -40,6 +40,16 @@ pub enum Statement<P> {
|
|||||||
|
|
||||||
// Expressions
|
// Expressions
|
||||||
Add(Box<Node<P>>, Box<Node<P>>),
|
Add(Box<Node<P>>, Box<Node<P>>),
|
||||||
|
BuiltInFunctionCall {
|
||||||
|
function: BuiltInFunction,
|
||||||
|
type_arguments: Option<Vec<Node<P>>>,
|
||||||
|
value_arguments: Option<Vec<Node<P>>>,
|
||||||
|
},
|
||||||
|
FunctionCall {
|
||||||
|
function: Box<Node<P>>,
|
||||||
|
type_arguments: Option<Vec<Node<P>>>,
|
||||||
|
value_arguments: Option<Vec<Node<P>>>,
|
||||||
|
},
|
||||||
PropertyAccess(Box<Node<P>>, Box<Node<P>>),
|
PropertyAccess(Box<Node<P>>, Box<Node<P>>),
|
||||||
List(Vec<Node<P>>),
|
List(Vec<Node<P>>),
|
||||||
Multiply(Box<Node<P>>, Box<Node<P>>),
|
Multiply(Box<Node<P>>, Box<Node<P>>),
|
||||||
@ -47,7 +57,6 @@ pub enum Statement<P> {
|
|||||||
// Hard-coded values
|
// Hard-coded values
|
||||||
Constant(Value),
|
Constant(Value),
|
||||||
Identifier(Identifier),
|
Identifier(Identifier),
|
||||||
ReservedIdentifier(ReservedIdentifier),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> Statement<P> {
|
impl<P> Statement<P> {
|
||||||
@ -55,17 +64,15 @@ impl<P> Statement<P> {
|
|||||||
match self {
|
match self {
|
||||||
Statement::Add(left, _) => left.statement.expected_type(variables),
|
Statement::Add(left, _) => left.statement.expected_type(variables),
|
||||||
Statement::Assign(_, _) => None,
|
Statement::Assign(_, _) => None,
|
||||||
|
Statement::BuiltInFunctionCall { function, .. } => function.expected_type(),
|
||||||
Statement::Constant(value) => Some(value.r#type(variables)),
|
Statement::Constant(value) => Some(value.r#type(variables)),
|
||||||
|
Statement::FunctionCall { function, .. } => function.statement.expected_type(variables),
|
||||||
Statement::Identifier(identifier) => variables
|
Statement::Identifier(identifier) => variables
|
||||||
.get(identifier)
|
.get(identifier)
|
||||||
.map(|value| value.r#type(variables)),
|
.map(|value| value.r#type(variables)),
|
||||||
Statement::List(_) => None,
|
Statement::List(_) => None,
|
||||||
Statement::Multiply(left, _) => left.statement.expected_type(variables),
|
Statement::Multiply(left, _) => left.statement.expected_type(variables),
|
||||||
Statement::PropertyAccess(_, _) => None,
|
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<P> Display for Statement<P> {
|
|||||||
match self {
|
match self {
|
||||||
Statement::Assign(left, right) => write!(f, "{left} = {right}"),
|
Statement::Assign(left, right) => write!(f, "{left} = {right}"),
|
||||||
Statement::Add(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::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
|
||||||
Statement::List(nodes) => {
|
Statement::List(nodes) => {
|
||||||
write!(f, "[")?;
|
write!(f, "[")?;
|
||||||
@ -89,7 +166,69 @@ impl<P> Display for Statement<P> {
|
|||||||
Statement::Multiply(left, right) => write!(f, "{left} * {right}"),
|
Statement::Multiply(left, right) => write!(f, "{left} * {right}"),
|
||||||
Statement::Constant(value) => write!(f, "{value}"),
|
Statement::Constant(value) => write!(f, "{value}"),
|
||||||
Statement::Identifier(identifier) => write!(f, "{identifier}"),
|
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<Vec<Type>>,
|
||||||
|
value_arguments: Option<Vec<Value>>,
|
||||||
|
) -> Result<Value, BuiltInFunctionError> {
|
||||||
|
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<Type> {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
@ -103,7 +103,17 @@ impl<'a, P: Clone> Analyzer<'a, P> {
|
|||||||
|
|
||||||
self.analyze_node(right)?;
|
self.analyze_node(right)?;
|
||||||
}
|
}
|
||||||
|
Statement::BuiltInFunctionCall { .. } => {}
|
||||||
Statement::Constant(_) => {}
|
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(_) => {
|
Statement::Identifier(_) => {
|
||||||
return Err(AnalyzerError::UnexpectedIdentifier {
|
return Err(AnalyzerError::UnexpectedIdentifier {
|
||||||
identifier: node.clone(),
|
identifier: node.clone(),
|
||||||
@ -149,7 +159,6 @@ impl<'a, P: Clone> Analyzer<'a, P> {
|
|||||||
|
|
||||||
self.analyze_node(right)?;
|
self.analyze_node(right)?;
|
||||||
}
|
}
|
||||||
Statement::ReservedIdentifier(_) => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -158,6 +167,7 @@ impl<'a, P: Clone> Analyzer<'a, P> {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum AnalyzerError<P> {
|
pub enum AnalyzerError<P> {
|
||||||
|
ExpectedFunction { position: P },
|
||||||
ExpectedIdentifier { actual: Node<P> },
|
ExpectedIdentifier { actual: Node<P> },
|
||||||
ExpectedIntegerOrFloat { actual: Node<P> },
|
ExpectedIntegerOrFloat { actual: Node<P> },
|
||||||
UnexpectedIdentifier { identifier: Node<P> },
|
UnexpectedIdentifier { identifier: Node<P> },
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
//! - [`Lexer`], which lexes the input a token at a time
|
//! - [`Lexer`], which lexes the input a token at a time
|
||||||
use std::num::{ParseFloatError, ParseIntError};
|
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.
|
/// Lexes the input and return a vector of tokens and their positions.
|
||||||
///
|
///
|
||||||
@ -245,9 +245,9 @@ impl<'a> Lexer<'a> {
|
|||||||
let token = match string {
|
let token = match string {
|
||||||
"true" => Token::Boolean(true),
|
"true" => Token::Boolean(true),
|
||||||
"false" => Token::Boolean(false),
|
"false" => Token::Boolean(false),
|
||||||
"is_even" => Token::ReservedIdentifier(ReservedIdentifier::IsEven),
|
"is_even" => Token::IsEven,
|
||||||
"is_odd" => Token::ReservedIdentifier(ReservedIdentifier::IsOdd),
|
"is_odd" => Token::IsOdd,
|
||||||
"length" => Token::ReservedIdentifier(ReservedIdentifier::Length),
|
"length" => Token::Length,
|
||||||
_ => Token::Identifier(Identifier::new(string)),
|
_ => Token::Identifier(Identifier::new(string)),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -298,19 +298,18 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn integer_property_access() {
|
fn property_access_function_call() {
|
||||||
let input = "42.is_even";
|
let input = "42.is_even()";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex(input),
|
lex(input),
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
(Token::Integer(42), (0, 2)),
|
(Token::Integer(42), (0, 2)),
|
||||||
(Token::Dot, (2, 3)),
|
(Token::Dot, (2, 3)),
|
||||||
(
|
(Token::IsEven, (3, 10)),
|
||||||
Token::ReservedIdentifier(ReservedIdentifier::IsEven),
|
(Token::LeftParenthesis, (10, 11)),
|
||||||
(3, 10)
|
(Token::RightParenthesis, (11, 12)),
|
||||||
),
|
(Token::Eof, (12, 12)),
|
||||||
(Token::Eof, (10, 10)),
|
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -328,13 +327,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex(input),
|
lex(input),
|
||||||
Ok(vec![
|
Ok(vec![(Token::Length, (0, 6)), (Token::Eof, (6, 6)),])
|
||||||
(
|
|
||||||
Token::ReservedIdentifier(ReservedIdentifier::Length),
|
|
||||||
(0, 6)
|
|
||||||
),
|
|
||||||
(Token::Eof, (6, 6)),
|
|
||||||
])
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,13 +15,13 @@ pub mod r#type;
|
|||||||
pub mod value;
|
pub mod value;
|
||||||
pub mod vm;
|
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 analyzer::{analyze, Analyzer, AnalyzerError};
|
||||||
pub use identifier::Identifier;
|
pub use identifier::Identifier;
|
||||||
pub use lex::{lex, LexError, Lexer};
|
pub use lex::{lex, LexError, Lexer};
|
||||||
pub use parse::{parse, ParseError, Parser};
|
pub use parse::{parse, ParseError, Parser};
|
||||||
pub use r#type::Type;
|
pub use r#type::Type;
|
||||||
pub use token::{ReservedIdentifier, Token};
|
pub use token::Token;
|
||||||
pub use value::{Value, ValueError};
|
pub use value::{Value, ValueError};
|
||||||
pub use vm::{run, Vm, VmError};
|
pub use vm::{run, Vm, VmError};
|
||||||
|
|
||||||
|
@ -5,7 +5,10 @@
|
|||||||
/// - `Parser` struct, which parses the input a statement at a time
|
/// - `Parser` struct, which parses the input a statement at a time
|
||||||
use std::collections::VecDeque;
|
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.
|
/// Parses the input into an abstract syntax tree.
|
||||||
///
|
///
|
||||||
@ -206,15 +209,12 @@ impl<'src> Parser<'src> {
|
|||||||
(Token::LeftParenthesis, left_span) => {
|
(Token::LeftParenthesis, left_span) => {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
let instruction = self.parse_node(0)?;
|
let node = self.parse_node(0)?;
|
||||||
|
|
||||||
if let (Token::RightParenthesis, right_span) = self.current {
|
if let (Token::RightParenthesis, right_span) = self.current {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
Ok(Node::new(
|
Ok(Node::new(node.statement, (left_span.0, right_span.1)))
|
||||||
instruction.statement,
|
|
||||||
(left_span.0, right_span.1),
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::ExpectedClosingParenthesis {
|
Err(ParseError::ExpectedClosingParenthesis {
|
||||||
actual: self.current.0.clone(),
|
actual: self.current.0.clone(),
|
||||||
@ -225,14 +225,14 @@ impl<'src> Parser<'src> {
|
|||||||
(Token::LeftSquareBrace, left_span) => {
|
(Token::LeftSquareBrace, left_span) => {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
let mut instructions = Vec::new();
|
let mut nodes = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let (Token::RightSquareBrace, right_span) = self.current {
|
if let (Token::RightSquareBrace, right_span) = self.current {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
return Ok(Node::new(
|
return Ok(Node::new(
|
||||||
Statement::List(instructions),
|
Statement::List(nodes),
|
||||||
(left_span.0, right_span.1),
|
(left_span.0, right_span.1),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -244,7 +244,7 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(instruction) = self.parse_node(0) {
|
if let Ok(instruction) = self.parse_node(0) {
|
||||||
instructions.push(instruction);
|
nodes.push(instruction);
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedClosingSquareBrace {
|
return Err(ParseError::ExpectedClosingSquareBrace {
|
||||||
actual: self.current.0.clone(),
|
actual: self.current.0.clone(),
|
||||||
@ -253,12 +253,52 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Token::ReservedIdentifier(reserved), _) => {
|
(Token::IsEven, left_span) => {
|
||||||
self.next_token()?;
|
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(
|
Ok(Node::new(
|
||||||
Statement::ReservedIdentifier(reserved),
|
Statement::BuiltInFunctionCall {
|
||||||
self.current.1,
|
function: BuiltInFunction::IsEven,
|
||||||
|
type_arguments: None,
|
||||||
|
value_arguments: value_parameters,
|
||||||
|
},
|
||||||
|
left_span,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => Err(ParseError::UnexpectedToken(self.current.0.clone())),
|
_ => Err(ParseError::UnexpectedToken(self.current.0.clone())),
|
||||||
@ -280,6 +320,7 @@ impl<'src> Parser<'src> {
|
|||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
ExpectedClosingParenthesis { actual: Token, span: Span },
|
ExpectedClosingParenthesis { actual: Token, span: Span },
|
||||||
ExpectedClosingSquareBrace { actual: Token, span: Span },
|
ExpectedClosingSquareBrace { actual: Token, span: Span },
|
||||||
|
ExpectedOpeningParenthesis { actual: Token, span: Span },
|
||||||
LexError(LexError),
|
LexError(LexError),
|
||||||
UnexpectedToken(Token),
|
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]
|
#[test]
|
||||||
fn list_access() {
|
fn list_access() {
|
||||||
let input = "[1, 2, 3].0";
|
let input = "[1, 2, 3].0";
|
||||||
|
@ -1,44 +1,57 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::Identifier;
|
use crate::Identifier;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
|
Eof,
|
||||||
|
|
||||||
|
Identifier(Identifier),
|
||||||
|
|
||||||
|
// Hard-coded values
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
|
Float(f64),
|
||||||
|
Integer(i64),
|
||||||
|
|
||||||
|
// Keywords
|
||||||
|
IsEven,
|
||||||
|
IsOdd,
|
||||||
|
Length,
|
||||||
|
|
||||||
|
// Symbols
|
||||||
Comma,
|
Comma,
|
||||||
Dot,
|
Dot,
|
||||||
Eof,
|
|
||||||
Equal,
|
Equal,
|
||||||
Identifier(Identifier),
|
|
||||||
ReservedIdentifier(ReservedIdentifier),
|
|
||||||
Integer(i64),
|
|
||||||
Plus,
|
Plus,
|
||||||
Star,
|
Star,
|
||||||
LeftParenthesis,
|
LeftParenthesis,
|
||||||
RightParenthesis,
|
RightParenthesis,
|
||||||
LeftSquareBrace,
|
LeftSquareBrace,
|
||||||
RightSquareBrace,
|
RightSquareBrace,
|
||||||
Float(f64),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
impl Display for Token {
|
||||||
pub enum ReservedIdentifier {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
// Number properties
|
|
||||||
IsEven,
|
|
||||||
IsOdd,
|
|
||||||
|
|
||||||
// List properties
|
|
||||||
Length,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ReservedIdentifier {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
match self {
|
||||||
ReservedIdentifier::IsEven => write!(f, "is_even"),
|
Token::Eof => write!(f, "EOF"),
|
||||||
ReservedIdentifier::IsOdd => write!(f, "is_odd"),
|
Token::Identifier(identifier) => write!(f, "{identifier}"),
|
||||||
ReservedIdentifier::Length => write!(f, "length"),
|
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, "]"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ use serde::{
|
|||||||
Deserialize, Deserializer, Serialize,
|
Deserialize, Deserializer, Serialize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{identifier::Identifier, Statement, Type};
|
use crate::{identifier::Identifier, AbstractSyntaxTree, Type, Vm, VmError};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Value(Arc<ValueInner>);
|
pub struct Value(Arc<ValueInner>);
|
||||||
@ -30,6 +30,10 @@ impl Value {
|
|||||||
Value(Arc::new(ValueInner::Float(float)))
|
Value(Arc::new(ValueInner::Float(float)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn function(function: Function) -> Self {
|
||||||
|
Value(Arc::new(ValueInner::Function(function)))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn integer(integer: i64) -> Self {
|
pub fn integer(integer: i64) -> Self {
|
||||||
Value(Arc::new(ValueInner::Integer(integer)))
|
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<Value>> {
|
pub fn as_list(&self) -> Option<&Vec<Value>> {
|
||||||
if let ValueInner::List(list) = self.inner().as_ref() {
|
if let ValueInner::List(list) = self.inner().as_ref() {
|
||||||
Some(list)
|
Some(list)
|
||||||
@ -539,12 +551,39 @@ pub struct Function {
|
|||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
pub type_parameters: Option<Vec<Type>>,
|
pub type_parameters: Option<Vec<Type>>,
|
||||||
pub value_parameters: Option<Vec<(Identifier, Type)>>,
|
pub value_parameters: Option<Vec<(Identifier, Type)>>,
|
||||||
pub body: Vec<Statement<()>>,
|
pub body: AbstractSyntaxTree<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
|
pub fn call(
|
||||||
|
self,
|
||||||
|
type_arguments: Option<Vec<Type>>,
|
||||||
|
value_arguments: Option<Vec<Value>>,
|
||||||
|
variables: &HashMap<Identifier, Value>,
|
||||||
|
) -> Result<Option<Value>, 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<Identifier, Value>) -> Option<Type> {
|
pub fn return_type(&self, variables: &HashMap<Identifier, Value>) -> Option<Type> {
|
||||||
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, ") {{")?;
|
write!(f, ") {{")?;
|
||||||
|
|
||||||
for statement in &self.body {
|
for statement in &self.body.nodes {
|
||||||
write!(f, "{}", statement)?;
|
write!(f, "{}", statement)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parse, AbstractSyntaxTree, Analyzer, AnalyzerError, Identifier, Node, ParseError,
|
abstract_tree::BuiltInFunctionError, parse, AbstractSyntaxTree, Analyzer, AnalyzerError,
|
||||||
ReservedIdentifier, Span, Statement, Value, ValueError,
|
Identifier, Node, ParseError, Span, Statement, Value, ValueError,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(
|
pub fn run(
|
||||||
@ -47,9 +47,6 @@ impl<P: Copy> Vm<P> {
|
|||||||
variables: &mut HashMap<Identifier, Value>,
|
variables: &mut HashMap<Identifier, Value>,
|
||||||
) -> Result<Option<Value>, VmError<P>> {
|
) -> Result<Option<Value>, VmError<P>> {
|
||||||
match node.statement {
|
match node.statement {
|
||||||
Statement::Constant(value) => Ok(Some(value.clone())),
|
|
||||||
Statement::Identifier(_) => Ok(None),
|
|
||||||
Statement::ReservedIdentifier(_) => Ok(None),
|
|
||||||
Statement::Add(left, right) => {
|
Statement::Add(left, right) => {
|
||||||
let left_span = left.position;
|
let left_span = left.position;
|
||||||
let left = if let Some(value) = self.run_node(*left, variables)? {
|
let left = if let Some(value) = self.run_node(*left, variables)? {
|
||||||
@ -92,6 +89,84 @@ impl<P: Copy> Vm<P> {
|
|||||||
|
|
||||||
Ok(None)
|
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) => {
|
Statement::List(nodes) => {
|
||||||
let values = nodes
|
let values = nodes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -110,7 +185,7 @@ impl<P: Copy> Vm<P> {
|
|||||||
Statement::Multiply(_, _) => todo!(),
|
Statement::Multiply(_, _) => todo!(),
|
||||||
Statement::PropertyAccess(left, right) => {
|
Statement::PropertyAccess(left, right) => {
|
||||||
let left_span = left.position;
|
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
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedValue {
|
return Err(VmError::ExpectedValue {
|
||||||
@ -119,39 +194,8 @@ impl<P: Copy> Vm<P> {
|
|||||||
};
|
};
|
||||||
let right_span = right.position;
|
let right_span = right.position;
|
||||||
|
|
||||||
if let Statement::ReservedIdentifier(reserved) = &right.statement {
|
if let (Some(list), Statement::Constant(value)) =
|
||||||
match reserved {
|
(left_value.as_list(), &right.statement)
|
||||||
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(index) = value.as_integer() {
|
if let Some(index) = value.as_integer() {
|
||||||
let value = list.get(index as usize).cloned();
|
let value = list.get(index as usize).cloned();
|
||||||
@ -160,6 +204,43 @@ impl<P: Copy> Vm<P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
Err(VmError::ExpectedIdentifierOrInteger {
|
||||||
position: right_span,
|
position: right_span,
|
||||||
})
|
})
|
||||||
@ -176,11 +257,32 @@ pub enum VmError<P> {
|
|||||||
|
|
||||||
// Anaylsis Failures
|
// Anaylsis Failures
|
||||||
// These should be prevented by running the analyzer before the VM
|
// These should be prevented by running the analyzer before the VM
|
||||||
ExpectedIdentifier { position: P },
|
BuiltInFunctionCallFailed(BuiltInFunctionError),
|
||||||
ExpectedIdentifierOrInteger { position: P },
|
ExpectedIdentifier {
|
||||||
ExpectedInteger { position: P },
|
position: P,
|
||||||
ExpectedList { position: P },
|
},
|
||||||
ExpectedValue { position: P },
|
ExpectedIdentifierOrInteger {
|
||||||
|
position: P,
|
||||||
|
},
|
||||||
|
ExpectedInteger {
|
||||||
|
position: P,
|
||||||
|
},
|
||||||
|
ExpectedList {
|
||||||
|
position: P,
|
||||||
|
},
|
||||||
|
ExpectedValue {
|
||||||
|
position: P,
|
||||||
|
},
|
||||||
|
FunctionCallFailed {
|
||||||
|
error: Box<VmError<()>>,
|
||||||
|
position: P,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P> From<BuiltInFunctionError> for VmError<P> {
|
||||||
|
fn from(v: BuiltInFunctionError) -> Self {
|
||||||
|
Self::BuiltInFunctionCallFailed(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> From<AnalyzerError<P>> for VmError<P> {
|
impl<P> From<AnalyzerError<P>> for VmError<P> {
|
||||||
@ -217,7 +319,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_even() {
|
fn is_even() {
|
||||||
let input = "42.is_even";
|
let input = "42.is_even()";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut HashMap::new()),
|
||||||
@ -227,7 +329,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_odd() {
|
fn is_odd() {
|
||||||
let input = "42.is_odd";
|
let input = "42.is_odd()";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
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]
|
#[test]
|
||||||
fn list_access() {
|
fn list_access() {
|
||||||
let input = "[1, 2, 3].1";
|
let input = "[1, 2, 3].1";
|
||||||
@ -242,13 +351,6 @@ mod tests {
|
|||||||
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(2))));
|
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]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
let input = "1 + 2";
|
let input = "1 + 2";
|
||||||
|
Loading…
Reference in New Issue
Block a user