Compare commits
5 Commits
c1b71ffccc
...
d0dba35285
Author | SHA1 | Date | |
---|---|---|---|
d0dba35285 | |||
8c8fde94ce | |||
b9081f8653 | |||
580b85e2d0 | |||
83018ec5ec |
@ -11,72 +11,82 @@ use crate::{BuiltInFunction, Identifier, Span, Type, Value};
|
||||
/// In-memory representation of a Dust program.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct AbstractSyntaxTree {
|
||||
pub nodes: VecDeque<Node>,
|
||||
pub nodes: VecDeque<Node<Statement>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Node {
|
||||
pub statement: Statement,
|
||||
pub struct Node<T> {
|
||||
pub inner: T,
|
||||
pub position: Span,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new(operation: Statement, position: Span) -> Self {
|
||||
Self {
|
||||
statement: operation,
|
||||
position,
|
||||
}
|
||||
impl<T> Node<T> {
|
||||
pub fn new(inner: T, position: Span) -> Self {
|
||||
Self { inner, position }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Node {
|
||||
impl<T: Display> Display for Node<T> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.statement)
|
||||
write!(f, "{}", self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum Statement {
|
||||
// Top-level statements
|
||||
Assign(Box<Node>, Box<Node>),
|
||||
Assignment {
|
||||
identifier: Identifier,
|
||||
value_node: Box<Node<Statement>>,
|
||||
},
|
||||
|
||||
// Expressions
|
||||
Add(Box<Node>, Box<Node>),
|
||||
// Logic, math and comparison expressions
|
||||
BinaryOperation {
|
||||
left: Box<Node<Statement>>,
|
||||
operator: Node<BinaryOperator>,
|
||||
right: Box<Node<Statement>>,
|
||||
},
|
||||
|
||||
// Function calls
|
||||
BuiltInFunctionCall {
|
||||
function: BuiltInFunction,
|
||||
type_arguments: Option<Vec<Node>>,
|
||||
value_arguments: Option<Vec<Node>>,
|
||||
type_arguments: Option<Vec<Node<Statement>>>,
|
||||
value_arguments: Option<Vec<Node<Statement>>>,
|
||||
},
|
||||
FunctionCall {
|
||||
function: Box<Node>,
|
||||
type_arguments: Option<Vec<Node>>,
|
||||
value_arguments: Option<Vec<Node>>,
|
||||
function: Box<Node<Statement>>,
|
||||
type_arguments: Option<Vec<Node<Statement>>>,
|
||||
value_arguments: Option<Vec<Node<Statement>>>,
|
||||
},
|
||||
PropertyAccess(Box<Node>, Box<Node>),
|
||||
Subtract(Box<Node>, Box<Node>),
|
||||
List(Vec<Node>),
|
||||
Multiply(Box<Node>, Box<Node>),
|
||||
|
||||
// Property access expression
|
||||
PropertyAccess(Box<Node<Statement>>, Box<Node<Statement>>),
|
||||
|
||||
// Identifier expression
|
||||
Identifier(Identifier),
|
||||
|
||||
// Value collection expressions
|
||||
List(Vec<Node<Statement>>),
|
||||
|
||||
// Hard-coded values
|
||||
Constant(Value),
|
||||
Identifier(Identifier),
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
pub fn expected_type(&self, variables: &HashMap<Identifier, Value>) -> Option<Type> {
|
||||
match self {
|
||||
Statement::Add(left, _) => left.statement.expected_type(variables),
|
||||
Statement::Assign(_, _) => None,
|
||||
Statement::BuiltInFunctionCall { function, .. } => function.expected_type(),
|
||||
Statement::Assignment { .. } => None,
|
||||
Statement::BinaryOperation { left, .. } => left.inner.expected_type(variables),
|
||||
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
|
||||
Statement::Constant(value) => Some(value.r#type(variables)),
|
||||
Statement::FunctionCall { function, .. } => function.statement.expected_type(variables),
|
||||
Statement::FunctionCall { function, .. } => function.inner.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::List(nodes) => nodes
|
||||
.first()
|
||||
.and_then(|node| node.inner.expected_type(variables)),
|
||||
Statement::PropertyAccess(_, _) => None,
|
||||
Statement::Subtract(left, _) => left.statement.expected_type(variables),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -84,8 +94,19 @@ impl Statement {
|
||||
impl Display for Statement {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Statement::Assign(left, right) => write!(f, "{left} = {right}"),
|
||||
Statement::Add(left, right) => write!(f, "{left} + {right}"),
|
||||
Statement::Assignment {
|
||||
identifier,
|
||||
value_node: value,
|
||||
} => {
|
||||
write!(f, "{identifier} = {value}")
|
||||
}
|
||||
Statement::BinaryOperation {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
} => {
|
||||
write!(f, "{left} {operator} {right}")
|
||||
}
|
||||
Statement::BuiltInFunctionCall {
|
||||
function,
|
||||
type_arguments: type_parameters,
|
||||
@ -121,6 +142,7 @@ impl Display for Statement {
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
Statement::Constant(value) => write!(f, "{value}"),
|
||||
Statement::FunctionCall {
|
||||
function,
|
||||
type_arguments: type_parameters,
|
||||
@ -156,6 +178,7 @@ impl Display for Statement {
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
Statement::Identifier(identifier) => write!(f, "{identifier}"),
|
||||
Statement::List(nodes) => {
|
||||
write!(f, "[")?;
|
||||
for (i, node) in nodes.iter().enumerate() {
|
||||
@ -167,10 +190,42 @@ impl Display for Statement {
|
||||
write!(f, "]")
|
||||
}
|
||||
Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
|
||||
Statement::Multiply(left, right) => write!(f, "{left} * {right}"),
|
||||
Statement::Constant(value) => write!(f, "{value}"),
|
||||
Statement::Identifier(identifier) => write!(f, "{identifier}"),
|
||||
Statement::Subtract(left, right) => write!(f, "{left} - {right}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum BinaryOperator {
|
||||
// Math
|
||||
Add,
|
||||
Divide,
|
||||
Multiply,
|
||||
Subtract,
|
||||
|
||||
// Comparison
|
||||
Greater,
|
||||
GreaterOrEqual,
|
||||
Less,
|
||||
LessOrEqual,
|
||||
|
||||
// Logic
|
||||
And,
|
||||
Or,
|
||||
}
|
||||
|
||||
impl Display for BinaryOperator {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
BinaryOperator::Add => write!(f, "+"),
|
||||
BinaryOperator::And => write!(f, "&&"),
|
||||
BinaryOperator::Divide => write!(f, "/"),
|
||||
BinaryOperator::Greater => write!(f, ">"),
|
||||
BinaryOperator::GreaterOrEqual => write!(f, ">="),
|
||||
BinaryOperator::Less => write!(f, "<"),
|
||||
BinaryOperator::LessOrEqual => write!(f, "<="),
|
||||
BinaryOperator::Multiply => write!(f, "*"),
|
||||
BinaryOperator::Or => write!(f, "||"),
|
||||
BinaryOperator::Subtract => write!(f, "-"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,10 @@ use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
};
|
||||
|
||||
use crate::{AbstractSyntaxTree, BuiltInFunction, Identifier, Node, Span, Statement, Type, Value};
|
||||
use crate::{
|
||||
abstract_tree::BinaryOperator, AbstractSyntaxTree, BuiltInFunction, Identifier, Node, Span,
|
||||
Statement, Type, Value,
|
||||
};
|
||||
|
||||
/// Analyzes the abstract syntax tree for errors.
|
||||
///
|
||||
@ -72,49 +75,73 @@ impl<'a> Analyzer<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn analyze_node(&self, node: &Node) -> Result<(), AnalyzerError> {
|
||||
match &node.statement {
|
||||
Statement::Add(left, right) => {
|
||||
fn analyze_node(&self, node: &Node<Statement>) -> Result<(), AnalyzerError> {
|
||||
match &node.inner {
|
||||
Statement::Assignment {
|
||||
value_node: value, ..
|
||||
} => {
|
||||
if let None = value.inner.expected_type(self.variables) {
|
||||
return Err(AnalyzerError::ExpectedValue {
|
||||
actual: value.as_ref().clone(),
|
||||
position: value.position,
|
||||
});
|
||||
}
|
||||
}
|
||||
Statement::BinaryOperation {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
} => {
|
||||
self.analyze_node(left)?;
|
||||
self.analyze_node(right)?;
|
||||
|
||||
let left_type = left.statement.expected_type(self.variables);
|
||||
let right_type = right.statement.expected_type(self.variables);
|
||||
let left_type = left.inner.expected_type(self.variables);
|
||||
let right_type = right.inner.expected_type(self.variables);
|
||||
|
||||
match (left_type, right_type) {
|
||||
(Some(Type::Integer), Some(Type::Integer)) => {}
|
||||
(Some(Type::Float), Some(Type::Float)) => {}
|
||||
(Some(Type::String), Some(Type::String)) => {}
|
||||
(Some(Type::Integer), _) | (Some(Type::Float), _) | (Some(Type::String), _) => {
|
||||
return Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||
actual: right.as_ref().clone(),
|
||||
position: right.position,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
return Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
if let BinaryOperator::Add
|
||||
| BinaryOperator::Subtract
|
||||
| BinaryOperator::Multiply
|
||||
| BinaryOperator::Divide
|
||||
| BinaryOperator::Greater
|
||||
| BinaryOperator::GreaterOrEqual
|
||||
| BinaryOperator::Less
|
||||
| BinaryOperator::LessOrEqual = operator.inner
|
||||
{
|
||||
match (left_type, right_type) {
|
||||
(Some(Type::Integer), Some(Type::Integer)) => {}
|
||||
(Some(Type::Float), Some(Type::Float)) => {}
|
||||
(Some(Type::String), Some(Type::String)) => {}
|
||||
(Some(Type::Integer), _) => {
|
||||
return Err(AnalyzerError::ExpectedInteger {
|
||||
actual: right.as_ref().clone(),
|
||||
position: right.position,
|
||||
});
|
||||
}
|
||||
(Some(Type::Float), _) => {
|
||||
return Err(AnalyzerError::ExpectedFloat {
|
||||
actual: right.as_ref().clone(),
|
||||
position: right.position,
|
||||
});
|
||||
}
|
||||
(Some(Type::String), _) => {
|
||||
return Err(AnalyzerError::ExpectedString {
|
||||
actual: right.as_ref().clone(),
|
||||
position: right.position,
|
||||
});
|
||||
}
|
||||
(_, _) => {
|
||||
return Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||
actual: right.as_ref().clone(),
|
||||
position: right.position,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::Assign(left, right) => {
|
||||
if let Statement::Identifier(_) = &left.statement {
|
||||
// Identifier is in the correct position
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIdentifier {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
}
|
||||
|
||||
self.analyze_node(right)?;
|
||||
}
|
||||
Statement::BuiltInFunctionCall { .. } => {}
|
||||
Statement::Constant(_) => {}
|
||||
Statement::FunctionCall { function, .. } => {
|
||||
if let Statement::Identifier(_) = &function.statement {
|
||||
if let Statement::Identifier(_) = &function.inner {
|
||||
// Function is in the correct position
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIdentifier {
|
||||
@ -134,45 +161,21 @@ impl<'a> Analyzer<'a> {
|
||||
self.analyze_node(statement)?;
|
||||
}
|
||||
}
|
||||
Statement::Multiply(left, right) => {
|
||||
self.analyze_node(left)?;
|
||||
self.analyze_node(right)?;
|
||||
|
||||
if let Some(Type::Integer) | Some(Type::Float) =
|
||||
left.statement.expected_type(self.variables)
|
||||
{
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(Type::Integer) | Some(Type::Float) =
|
||||
right.statement.expected_type(self.variables)
|
||||
{
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: right.as_ref().clone(),
|
||||
position: right.position,
|
||||
});
|
||||
}
|
||||
}
|
||||
Statement::PropertyAccess(left, right) => {
|
||||
if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) =
|
||||
&left.statement
|
||||
&left.inner
|
||||
{
|
||||
// Left side is valid
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIdentifierOrValue {
|
||||
return Err(AnalyzerError::ExpectedValue {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
}
|
||||
|
||||
if let Statement::BuiltInFunctionCall { function, .. } = &right.statement {
|
||||
if let Statement::BuiltInFunctionCall { function, .. } = &right.inner {
|
||||
if function == &BuiltInFunction::IsEven || function == &BuiltInFunction::IsOdd {
|
||||
if let Some(Type::Integer) = left.statement.expected_type(self.variables) {
|
||||
if let Some(Type::Integer) = left.inner.expected_type(self.variables) {
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: left.as_ref().clone(),
|
||||
@ -184,30 +187,6 @@ impl<'a> Analyzer<'a> {
|
||||
|
||||
self.analyze_node(right)?;
|
||||
}
|
||||
Statement::Subtract(left, right) => {
|
||||
self.analyze_node(left)?;
|
||||
self.analyze_node(right)?;
|
||||
|
||||
let left_type = left.statement.expected_type(self.variables);
|
||||
let right_type = right.statement.expected_type(self.variables);
|
||||
|
||||
match (left_type, right_type) {
|
||||
(Some(Type::Integer), Some(Type::Integer)) => {}
|
||||
(Some(Type::Float), Some(Type::Float)) => {}
|
||||
(Some(Type::Integer), _) | (Some(Type::Float), _) => {
|
||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: right.as_ref().clone(),
|
||||
position: right.position,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -216,13 +195,74 @@ impl<'a> Analyzer<'a> {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum AnalyzerError {
|
||||
ExpectedBoolean { actual: Node, position: Span },
|
||||
ExpectedFunction { actual: Node, position: Span },
|
||||
ExpectedIdentifier { actual: Node, position: Span },
|
||||
ExpectedIdentifierOrValue { actual: Node, position: Span },
|
||||
ExpectedIntegerOrFloat { actual: Node, position: Span },
|
||||
ExpectedIntegerFloatOrString { actual: Node, position: Span },
|
||||
UnexpectedIdentifier { identifier: Node, position: Span },
|
||||
ExpectedBoolean {
|
||||
actual: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedFloat {
|
||||
actual: Node<Statement>,
|
||||
position: (usize, usize),
|
||||
},
|
||||
ExpectedFunction {
|
||||
actual: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedIdentifier {
|
||||
actual: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedInteger {
|
||||
actual: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedIntegerOrFloat {
|
||||
actual: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedIntegerFloatOrString {
|
||||
actual: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedSameType {
|
||||
left: Node<Statement>,
|
||||
right: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedString {
|
||||
actual: Node<Statement>,
|
||||
position: (usize, usize),
|
||||
},
|
||||
ExpectedValue {
|
||||
actual: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
UnexpectedIdentifier {
|
||||
identifier: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
UnexectedString {
|
||||
actual: Node<Statement>,
|
||||
position: (usize, usize),
|
||||
},
|
||||
}
|
||||
|
||||
impl AnalyzerError {
|
||||
pub fn position(&self) -> Span {
|
||||
match self {
|
||||
AnalyzerError::ExpectedBoolean { position, .. } => *position,
|
||||
AnalyzerError::ExpectedFloat { position, .. } => *position,
|
||||
AnalyzerError::ExpectedFunction { position, .. } => *position,
|
||||
AnalyzerError::ExpectedIdentifier { position, .. } => *position,
|
||||
AnalyzerError::ExpectedValue { position, .. } => *position,
|
||||
AnalyzerError::ExpectedInteger { position, .. } => *position,
|
||||
AnalyzerError::ExpectedIntegerOrFloat { position, .. } => *position,
|
||||
AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => *position,
|
||||
AnalyzerError::ExpectedSameType { position, .. } => *position,
|
||||
AnalyzerError::ExpectedString { position, .. } => *position,
|
||||
AnalyzerError::UnexpectedIdentifier { position, .. } => *position,
|
||||
AnalyzerError::UnexectedString { position, .. } => *position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for AnalyzerError {}
|
||||
@ -236,11 +276,14 @@ impl Display for AnalyzerError {
|
||||
AnalyzerError::ExpectedFunction { actual, .. } => {
|
||||
write!(f, "Expected function, found {}", actual)
|
||||
}
|
||||
AnalyzerError::ExpectedFloat { actual, .. } => {
|
||||
write!(f, "Expected float, found {}", actual)
|
||||
}
|
||||
AnalyzerError::ExpectedIdentifier { actual, .. } => {
|
||||
write!(f, "Expected identifier, found {}", actual)
|
||||
}
|
||||
AnalyzerError::ExpectedIdentifierOrValue { actual, .. } => {
|
||||
write!(f, "Expected identifier or value, found {}", actual)
|
||||
AnalyzerError::ExpectedInteger { actual, .. } => {
|
||||
write!(f, "Expected integer, found {}", actual)
|
||||
}
|
||||
AnalyzerError::ExpectedIntegerOrFloat { actual, .. } => {
|
||||
write!(f, "Expected integer or float, found {}", actual)
|
||||
@ -248,9 +291,21 @@ impl Display for AnalyzerError {
|
||||
AnalyzerError::ExpectedIntegerFloatOrString { actual, .. } => {
|
||||
write!(f, "Expected integer, float, or string, found {}", actual)
|
||||
}
|
||||
AnalyzerError::ExpectedSameType { left, right, .. } => {
|
||||
write!(f, "Expected same type, found {} and {}", left, right)
|
||||
}
|
||||
AnalyzerError::ExpectedString { actual, .. } => {
|
||||
write!(f, "Expected string, found {}", actual)
|
||||
}
|
||||
AnalyzerError::ExpectedValue { actual, .. } => {
|
||||
write!(f, "Expected value, found {}", actual)
|
||||
}
|
||||
AnalyzerError::UnexpectedIdentifier { identifier, .. } => {
|
||||
write!(f, "Unexpected identifier {}", identifier)
|
||||
}
|
||||
AnalyzerError::UnexectedString { actual, .. } => {
|
||||
write!(f, "Unexpected string {}", actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -262,13 +317,14 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn add_expects_same_types() {
|
||||
fn float_plus_integer() {
|
||||
let abstract_tree = AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Add(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
Box::new(Node::new(Statement::Constant(Value::float(1.0)), (1, 2))),
|
||||
),
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(Statement::Constant(Value::float(1.0)), (0, 1))),
|
||||
operator: Node::new(BinaryOperator::Add, (1, 2)),
|
||||
right: Box::new(Node::new(Statement::Constant(Value::integer(1)), (3, 4))),
|
||||
},
|
||||
(0, 2),
|
||||
)]
|
||||
.into(),
|
||||
@ -278,21 +334,22 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||
actual: Node::new(Statement::Constant(Value::float(1.0)), (1, 2)),
|
||||
position: (1, 2)
|
||||
Err(AnalyzerError::ExpectedFloat {
|
||||
actual: Node::new(Statement::Constant(Value::integer(1)), (3, 4)),
|
||||
position: (3, 4)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_expects_integer_float_or_string() {
|
||||
fn integer_plus_boolean() {
|
||||
let abstract_tree = AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Add(
|
||||
Box::new(Node::new(Statement::Constant(Value::boolean(true)), (0, 1))),
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(1)), (1, 2))),
|
||||
),
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
operator: Node::new(BinaryOperator::Add, (1, 2)),
|
||||
right: Box::new(Node::new(Statement::Constant(Value::boolean(true)), (3, 4))),
|
||||
},
|
||||
(0, 2),
|
||||
)]
|
||||
.into(),
|
||||
@ -302,9 +359,9 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||
actual: Node::new(Statement::Constant(Value::boolean(true)), (0, 1)),
|
||||
position: (0, 1)
|
||||
Err(AnalyzerError::ExpectedInteger {
|
||||
actual: Node::new(Statement::Constant(Value::boolean(true)), (3, 4)),
|
||||
position: (3, 4)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -370,57 +427,6 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiply_expect_integer_or_float() {
|
||||
let abstract_tree = AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Multiply(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
Box::new(Node::new(
|
||||
Statement::Constant(Value::boolean(false)),
|
||||
(1, 2),
|
||||
)),
|
||||
),
|
||||
(0, 2),
|
||||
)]
|
||||
.into(),
|
||||
};
|
||||
let variables = HashMap::new();
|
||||
let analyzer = Analyzer::new(&abstract_tree, &variables);
|
||||
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: Node::new(Statement::Constant(Value::boolean(false)), (1, 2)),
|
||||
position: (1, 2)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assignment_expect_identifier() {
|
||||
let abstract_tree = AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Assign(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(2)), (1, 2))),
|
||||
),
|
||||
(0, 2),
|
||||
)]
|
||||
.into(),
|
||||
};
|
||||
let variables = HashMap::new();
|
||||
let analyzer = Analyzer::new(&abstract_tree, &variables);
|
||||
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::ExpectedIdentifier {
|
||||
actual: Node::new(Statement::Constant(Value::integer(1)), (0, 1)),
|
||||
position: (0, 1)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unexpected_identifier() {
|
||||
let abstract_tree = AbstractSyntaxTree {
|
||||
|
@ -113,7 +113,7 @@ impl BuiltInFunction {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expected_type(&self) -> Option<Type> {
|
||||
pub fn expected_return_type(&self) -> Option<Type> {
|
||||
match self {
|
||||
BuiltInFunction::IsEven => Some(Type::Boolean),
|
||||
BuiltInFunction::IsOdd => Some(Type::Boolean),
|
||||
|
@ -2,7 +2,7 @@
|
||||
use annotate_snippets::{Level, Renderer, Snippet};
|
||||
use std::{error::Error, fmt::Display};
|
||||
|
||||
use crate::{AnalyzerError, VmError};
|
||||
use crate::VmError;
|
||||
|
||||
/// An error that occurred during the execution of the Dust language and its
|
||||
/// corresponding source code.
|
||||
@ -22,30 +22,10 @@ impl<'src> DustError<'src> {
|
||||
VmError::AnaylyzerError(_) => "Analyzer error",
|
||||
VmError::ParseError(_) => "Parse error",
|
||||
VmError::ValueError { .. } => "Value error",
|
||||
VmError::BuiltInFunctionCallError(_) => "Runtime error",
|
||||
VmError::BuiltInFunctionError { .. } => "Runtime error",
|
||||
_ => "Analysis Failure",
|
||||
};
|
||||
let span = match &self.vm_error {
|
||||
VmError::AnaylyzerError(analyzer_error) => match analyzer_error {
|
||||
AnalyzerError::ExpectedBoolean { position, .. } => position,
|
||||
AnalyzerError::ExpectedFunction { position, .. } => position,
|
||||
AnalyzerError::ExpectedIdentifier { position, .. } => position,
|
||||
AnalyzerError::ExpectedIdentifierOrValue { position, .. } => position,
|
||||
AnalyzerError::ExpectedIntegerOrFloat { position, .. } => position,
|
||||
AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => position,
|
||||
AnalyzerError::UnexpectedIdentifier { position, .. } => position,
|
||||
},
|
||||
VmError::ParseError(parse_error) => &parse_error.position(),
|
||||
VmError::ValueError { position, .. } => position,
|
||||
VmError::BuiltInFunctionCallError(_) => todo!(),
|
||||
VmError::ExpectedIdentifier { position } => position,
|
||||
VmError::ExpectedIdentifierOrInteger { position } => position,
|
||||
VmError::ExpectedInteger { position } => position,
|
||||
VmError::ExpectedFunction { position, .. } => position,
|
||||
VmError::ExpectedList { position } => position,
|
||||
VmError::ExpectedValue { position } => position,
|
||||
VmError::UndefinedIdentifier { position, .. } => position,
|
||||
};
|
||||
let span = self.vm_error.position();
|
||||
let label = self.vm_error.to_string();
|
||||
let message = Level::Error.title(title).snippet(
|
||||
Snippet::source(self.source).annotation(Level::Info.span(span.0..span.1).label(&label)),
|
||||
|
@ -20,15 +20,17 @@ pub struct Identifier(Arc<String>);
|
||||
|
||||
impl Identifier {
|
||||
pub fn new<T: ToString>(text: T) -> Self {
|
||||
let cache = identifier_cache();
|
||||
let cache = identifier_cache().read().unwrap();
|
||||
|
||||
let new = Identifier(Arc::new(text.to_string()));
|
||||
|
||||
if let Some(identifier) = cache.read().unwrap().get(&new).cloned() {
|
||||
return identifier;
|
||||
if cache.contains(&new) {
|
||||
return new;
|
||||
}
|
||||
|
||||
cache.write().unwrap().insert(new.clone());
|
||||
drop(cache);
|
||||
|
||||
identifier_cache().write().unwrap().insert(new.clone());
|
||||
|
||||
new
|
||||
}
|
||||
|
@ -108,12 +108,14 @@ impl Lexer {
|
||||
self.lex_number(source)?
|
||||
} else if "-Infinity" == self.peek_chars(source, 9) {
|
||||
self.position += 9;
|
||||
|
||||
(
|
||||
Token::Float(f64::NEG_INFINITY),
|
||||
(self.position - 9, self.position),
|
||||
)
|
||||
} else {
|
||||
self.position += 1;
|
||||
|
||||
(Token::Minus, (self.position - 1, self.position))
|
||||
}
|
||||
}
|
||||
@ -122,41 +124,76 @@ impl Lexer {
|
||||
'\'' => self.lex_string('\'', source)?,
|
||||
'+' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::Plus, (self.position - 1, self.position))
|
||||
}
|
||||
'*' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::Star, (self.position - 1, self.position))
|
||||
}
|
||||
'(' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::LeftParenthesis, (self.position - 1, self.position))
|
||||
}
|
||||
')' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::RightParenthesis, (self.position - 1, self.position))
|
||||
}
|
||||
'=' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::Equal, (self.position - 1, self.position))
|
||||
}
|
||||
'[' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::LeftSquareBrace, (self.position - 1, self.position))
|
||||
}
|
||||
']' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::RightSquareBrace, (self.position - 1, self.position))
|
||||
}
|
||||
',' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::Comma, (self.position - 1, self.position))
|
||||
}
|
||||
'.' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::Dot, (self.position - 1, self.position))
|
||||
}
|
||||
_ => return Err(LexError::UnexpectedCharacter(c)),
|
||||
'>' => {
|
||||
if let Some('=') = self.peek_second_char(source) {
|
||||
self.position += 2;
|
||||
|
||||
(Token::GreaterEqual, (self.position - 2, self.position))
|
||||
} else {
|
||||
self.position += 1;
|
||||
|
||||
(Token::Greater, (self.position - 1, self.position))
|
||||
}
|
||||
}
|
||||
'<' => {
|
||||
if let Some('=') = self.peek_second_char(source) {
|
||||
self.position += 2;
|
||||
|
||||
(Token::LessEqual, (self.position - 2, self.position))
|
||||
} else {
|
||||
self.position += 1;
|
||||
|
||||
(Token::Less, (self.position - 1, self.position))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.position += 1;
|
||||
|
||||
return Err(LexError::UnexpectedCharacter(c));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(Token::Eof, (self.position, self.position))
|
||||
@ -167,10 +204,13 @@ impl Lexer {
|
||||
|
||||
/// Progress to the next character.
|
||||
fn next_char(&mut self, source: &str) -> Option<char> {
|
||||
source[self.position..].chars().next().map(|c| {
|
||||
if let Some(c) = source[self.position..].chars().next() {
|
||||
self.position += c.len_utf8();
|
||||
c
|
||||
})
|
||||
|
||||
Some(c)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Skip whitespace characters.
|
||||
@ -375,6 +415,46 @@ impl From<ParseIntError> for LexError {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn greater_than() {
|
||||
let input = ">";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![(Token::Greater, (0, 1)), (Token::Eof, (1, 1))])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_than_or_equal() {
|
||||
let input = ">=";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![(Token::GreaterEqual, (0, 2)), (Token::Eof, (2, 2))])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_than() {
|
||||
let input = "<";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![(Token::Less, (0, 1)), (Token::Eof, (1, 1))])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_than_or_equal() {
|
||||
let input = "<=";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![(Token::LessEqual, (0, 2)), (Token::Eof, (2, 2))])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infinity() {
|
||||
let input = "Infinity";
|
||||
|
@ -10,8 +10,8 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
built_in_function::BuiltInFunction, token::TokenOwned, AbstractSyntaxTree, Identifier,
|
||||
LexError, Lexer, Node, Span, Statement, Token, Value,
|
||||
abstract_tree::BinaryOperator, built_in_function::BuiltInFunction, token::TokenOwned,
|
||||
AbstractSyntaxTree, Identifier, LexError, Lexer, Node, Span, Statement, Token, Value,
|
||||
};
|
||||
|
||||
/// Parses the input into an abstract syntax tree.
|
||||
@ -27,13 +27,13 @@ use crate::{
|
||||
/// Ok(AbstractSyntaxTree {
|
||||
/// nodes: [
|
||||
/// Node {
|
||||
/// statement: Statement::Assign(
|
||||
/// inner: Statement::Assign(
|
||||
/// Box::new(Node {
|
||||
/// statement: Statement::Identifier("x".into()),
|
||||
/// inner: Statement::Identifier("x".into()),
|
||||
/// position: (0, 1),
|
||||
/// }),
|
||||
/// Box::new(Node {
|
||||
/// statement: Statement::Constant(Value::integer(42)),
|
||||
/// inner: Statement::Constant(Value::integer(42)),
|
||||
/// position: (4, 6),
|
||||
/// })
|
||||
/// ),
|
||||
@ -84,15 +84,15 @@ pub fn parse(input: &str) -> Result<AbstractSyntaxTree, ParseError> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// nodes,
|
||||
/// Into::<VecDeque<Node>>::into([
|
||||
/// Into::<VecDeque<Node<Statement>>>::into([
|
||||
/// Node {
|
||||
/// statement: Statement::Assign(
|
||||
/// inner: Statement::Assign(
|
||||
/// Box::new(Node {
|
||||
/// statement: Statement::Identifier("x".into()),
|
||||
/// inner: Statement::Identifier("x".into()),
|
||||
/// position: (0, 1),
|
||||
/// }),
|
||||
/// Box::new(Node {
|
||||
/// statement: Statement::Constant(Value::integer(42)),
|
||||
/// inner: Statement::Constant(Value::integer(42)),
|
||||
/// position: (4, 6),
|
||||
/// })
|
||||
/// ),
|
||||
@ -119,7 +119,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> Result<Node, ParseError> {
|
||||
pub fn parse(&mut self) -> Result<Node<Statement>, ParseError> {
|
||||
self.parse_node(0)
|
||||
}
|
||||
|
||||
@ -128,56 +128,33 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
fn next_token(&mut self) -> Result<(), ParseError> {
|
||||
self.current =
|
||||
self.lexer
|
||||
.next_token(self.source)
|
||||
.map_err(|lex_error| ParseError::LexError {
|
||||
let next = self.lexer.next_token(self.source);
|
||||
|
||||
self.current = match next {
|
||||
Ok((token, position)) => (token, position),
|
||||
Err(lex_error) => {
|
||||
let position = {
|
||||
self.next_token()?;
|
||||
|
||||
self.current.1
|
||||
};
|
||||
|
||||
return Err(ParseError::LexError {
|
||||
error: lex_error,
|
||||
position: self.current.1,
|
||||
})?;
|
||||
position,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_node(&mut self, precedence: u8) -> Result<Node, ParseError> {
|
||||
fn parse_node(&mut self, precedence: u8) -> Result<Node<Statement>, ParseError> {
|
||||
let left_node = self.parse_primary()?;
|
||||
let left_start = left_node.position.0;
|
||||
|
||||
if precedence < self.current_precedence() {
|
||||
match &self.current {
|
||||
(Token::Plus, _) => {
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::Add(Box::new(left_node), Box::new(right_node)),
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Star, _) => {
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::Multiply(Box::new(left_node), Box::new(right_node)),
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Equal, _) => {
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::Assign(Box::new(left_node), Box::new(right_node)),
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Dot, _) => {
|
||||
self.next_token()?;
|
||||
|
||||
@ -189,14 +166,144 @@ impl<'src> Parser<'src> {
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Minus, _) => {
|
||||
(Token::Equal, _) => {
|
||||
self.next_token()?;
|
||||
|
||||
let identifier = if let Statement::Identifier(identifier) = left_node.inner {
|
||||
identifier
|
||||
} else {
|
||||
return Err(ParseError::ExpectedIdentifier {
|
||||
actual: left_node.inner,
|
||||
position: left_node.position,
|
||||
});
|
||||
};
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::Assignment {
|
||||
identifier,
|
||||
value_node: Box::new(right_node),
|
||||
},
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Greater, _) => {
|
||||
let operator = Node::new(BinaryOperator::Greater, self.current.1);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::Subtract(Box::new(left_node), Box::new(right_node)),
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(left_node),
|
||||
operator,
|
||||
right: Box::new(right_node),
|
||||
},
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::GreaterEqual, _) => {
|
||||
let operator = Node::new(BinaryOperator::GreaterOrEqual, self.current.1);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(left_node),
|
||||
operator,
|
||||
right: Box::new(right_node),
|
||||
},
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Less, _) => {
|
||||
let operator = Node::new(BinaryOperator::Less, self.current.1);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(left_node),
|
||||
operator,
|
||||
right: Box::new(right_node),
|
||||
},
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::LessEqual, _) => {
|
||||
let operator = Node::new(BinaryOperator::LessOrEqual, self.current.1);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(left_node),
|
||||
operator,
|
||||
right: Box::new(right_node),
|
||||
},
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Minus, _) => {
|
||||
let operator = Node::new(BinaryOperator::Subtract, self.current.1);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(left_node),
|
||||
operator,
|
||||
right: Box::new(right_node),
|
||||
},
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Plus, _) => {
|
||||
let operator = Node::new(BinaryOperator::Add, self.current.1);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(left_node),
|
||||
operator,
|
||||
right: Box::new(right_node),
|
||||
},
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Star, _) => {
|
||||
let operator = Node::new(BinaryOperator::Multiply, self.current.1);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(left_node),
|
||||
operator,
|
||||
right: Box::new(right_node),
|
||||
},
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
@ -207,7 +314,7 @@ impl<'src> Parser<'src> {
|
||||
Ok(left_node)
|
||||
}
|
||||
|
||||
fn parse_primary(&mut self) -> Result<Node, ParseError> {
|
||||
fn parse_primary(&mut self) -> Result<Node<Statement>, ParseError> {
|
||||
match self.current {
|
||||
(Token::Boolean(boolean), span) => {
|
||||
self.next_token()?;
|
||||
@ -248,7 +355,7 @@ impl<'src> Parser<'src> {
|
||||
if let (Token::RightParenthesis, right_span) = self.current {
|
||||
self.next_token()?;
|
||||
|
||||
Ok(Node::new(node.statement, (left_span.0, right_span.1)))
|
||||
Ok(Node::new(node.inner, (left_span.0, right_span.1)))
|
||||
} else {
|
||||
Err(ParseError::ExpectedClosingParenthesis {
|
||||
actual: self.current.0.to_owned(),
|
||||
@ -311,7 +418,7 @@ impl<'src> Parser<'src> {
|
||||
});
|
||||
}
|
||||
|
||||
let mut value_arguments: Option<Vec<Node>> = None;
|
||||
let mut value_arguments: Option<Vec<Node<Statement>>> = None;
|
||||
|
||||
loop {
|
||||
if let (Token::RightParenthesis, _) = self.current {
|
||||
@ -356,6 +463,7 @@ impl<'src> Parser<'src> {
|
||||
|
||||
fn current_precedence(&self) -> u8 {
|
||||
match self.current.0 {
|
||||
Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5,
|
||||
Token::Dot => 4,
|
||||
Token::Equal => 3,
|
||||
Token::Star => 2,
|
||||
@ -368,12 +476,31 @@ impl<'src> Parser<'src> {
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ParseError {
|
||||
LexError { error: LexError, position: Span },
|
||||
LexError {
|
||||
error: LexError,
|
||||
position: Span,
|
||||
},
|
||||
|
||||
ExpectedClosingParenthesis { actual: TokenOwned, position: Span },
|
||||
ExpectedClosingSquareBrace { actual: TokenOwned, position: Span },
|
||||
ExpectedOpeningParenthesis { actual: TokenOwned, position: Span },
|
||||
UnexpectedToken { actual: TokenOwned, position: Span },
|
||||
ExpectedClosingParenthesis {
|
||||
actual: TokenOwned,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedClosingSquareBrace {
|
||||
actual: TokenOwned,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedIdentifier {
|
||||
actual: Statement,
|
||||
position: (usize, usize),
|
||||
},
|
||||
ExpectedOpeningParenthesis {
|
||||
actual: TokenOwned,
|
||||
position: Span,
|
||||
},
|
||||
UnexpectedToken {
|
||||
actual: TokenOwned,
|
||||
position: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
@ -382,6 +509,7 @@ impl ParseError {
|
||||
Self::LexError { position, .. } => *position,
|
||||
Self::ExpectedClosingParenthesis { position, .. } => *position,
|
||||
Self::ExpectedClosingSquareBrace { position, .. } => *position,
|
||||
Self::ExpectedIdentifier { position, .. } => *position,
|
||||
Self::ExpectedOpeningParenthesis { position, .. } => *position,
|
||||
Self::UnexpectedToken { position, .. } => *position,
|
||||
}
|
||||
@ -407,6 +535,9 @@ impl Display for ParseError {
|
||||
Self::ExpectedClosingSquareBrace { actual, .. } => {
|
||||
write!(f, "Expected closing square brace, found {actual}",)
|
||||
}
|
||||
Self::ExpectedIdentifier { actual, .. } => {
|
||||
write!(f, "Expected identifier, found {actual}")
|
||||
}
|
||||
Self::ExpectedOpeningParenthesis { actual, .. } => {
|
||||
write!(f, "Expected opening parenthesis, found {actual}",)
|
||||
}
|
||||
@ -417,10 +548,90 @@ impl Display for ParseError {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Identifier;
|
||||
use crate::{abstract_tree::BinaryOperator, Identifier};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn less_than() {
|
||||
let input = "1 < 2";
|
||||
|
||||
assert_eq!(
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
operator: Node::new(BinaryOperator::Less, (2, 3)),
|
||||
right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (4, 5))),
|
||||
},
|
||||
(0, 5)
|
||||
)]
|
||||
.into()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_than_or_equal() {
|
||||
let input = "1 <= 2";
|
||||
|
||||
assert_eq!(
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
operator: Node::new(BinaryOperator::LessOrEqual, (2, 4)),
|
||||
right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (5, 6))),
|
||||
},
|
||||
(0, 6)
|
||||
)]
|
||||
.into()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_than_or_equal() {
|
||||
let input = "1 >= 2";
|
||||
|
||||
assert_eq!(
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
operator: Node::new(BinaryOperator::GreaterOrEqual, (2, 4)),
|
||||
right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (5, 6))),
|
||||
},
|
||||
(0, 6)
|
||||
)]
|
||||
.into()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_than() {
|
||||
let input = "1 > 2";
|
||||
|
||||
assert_eq!(
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
operator: Node::new(BinaryOperator::Greater, (2, 3)),
|
||||
right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (4, 5))),
|
||||
},
|
||||
(0, 5)
|
||||
)]
|
||||
.into()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtract_negative_integers() {
|
||||
let input = "-1 - -2";
|
||||
@ -429,10 +640,11 @@ mod tests {
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Subtract(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(-1)), (0, 2))),
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(-2)), (5, 7)))
|
||||
),
|
||||
Statement::BinaryOperation {
|
||||
left: Node::new(Statement::Constant(Value::integer(-1)), (0, 2)).into(),
|
||||
operator: Node::new(BinaryOperator::Subtract, (3, 4)),
|
||||
right: Node::new(Statement::Constant(Value::integer(-2)), (5, 7)).into()
|
||||
},
|
||||
(0, 7)
|
||||
)]
|
||||
.into()
|
||||
@ -448,16 +660,17 @@ mod tests {
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Add(
|
||||
Box::new(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(
|
||||
Statement::Constant(Value::string("Hello, ")),
|
||||
(0, 9)
|
||||
)),
|
||||
Box::new(Node::new(
|
||||
operator: Node::new(BinaryOperator::Add, (10, 11)),
|
||||
right: Box::new(Node::new(
|
||||
Statement::Constant(Value::string("World!")),
|
||||
(12, 20)
|
||||
))
|
||||
),
|
||||
},
|
||||
(0, 20)
|
||||
)]
|
||||
.into()
|
||||
@ -581,33 +794,42 @@ mod tests {
|
||||
Statement::List(vec![
|
||||
Node::new(Statement::Constant(Value::integer(1)), (1, 2)),
|
||||
Node::new(
|
||||
Statement::Add(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(1)), (4, 5))),
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(1)), (8, 9))),
|
||||
),
|
||||
(4, 9),
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(1)),
|
||||
(4, 5)
|
||||
)),
|
||||
operator: Node::new(BinaryOperator::Add, (6, 7)),
|
||||
right: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(1)),
|
||||
(8, 9)
|
||||
))
|
||||
},
|
||||
(4, 9)
|
||||
),
|
||||
Node::new(
|
||||
Statement::Add(
|
||||
Box::new(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(2)),
|
||||
(11, 12)
|
||||
)),
|
||||
Box::new(Node::new(
|
||||
Statement::Multiply(
|
||||
Box::new(Node::new(
|
||||
operator: Node::new(BinaryOperator::Add, (13, 14)),
|
||||
right: Box::new(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(4)),
|
||||
(16, 17)
|
||||
)),
|
||||
Box::new(Node::new(
|
||||
operator: Node::new(BinaryOperator::Multiply, (18, 19)),
|
||||
right: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(10)),
|
||||
(20, 22)
|
||||
)),
|
||||
),
|
||||
(15, 23),
|
||||
),),
|
||||
),
|
||||
(11, 23),
|
||||
))
|
||||
},
|
||||
(15, 23)
|
||||
))
|
||||
},
|
||||
(11, 23)
|
||||
),
|
||||
]),
|
||||
(0, 24),
|
||||
@ -668,10 +890,11 @@ mod tests {
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Add(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(2)), (4, 5))),
|
||||
),
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
operator: Node::new(BinaryOperator::Add, (2, 3)),
|
||||
right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (4, 5)),)
|
||||
},
|
||||
(0, 5),
|
||||
)]
|
||||
.into()
|
||||
@ -687,10 +910,11 @@ mod tests {
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Multiply(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(2)), (4, 5))),
|
||||
),
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
operator: Node::new(BinaryOperator::Multiply, (2, 3)),
|
||||
right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (4, 5)),)
|
||||
},
|
||||
(0, 5),
|
||||
)]
|
||||
.into()
|
||||
@ -706,16 +930,24 @@ mod tests {
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Add(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
Box::new(Node::new(
|
||||
Statement::Multiply(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(2)), (4, 5))),
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(3)), (8, 9))),
|
||||
),
|
||||
(4, 9),
|
||||
)),
|
||||
),
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
|
||||
operator: Node::new(BinaryOperator::Add, (2, 3)),
|
||||
right: Box::new(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(2)),
|
||||
(4, 5)
|
||||
)),
|
||||
operator: Node::new(BinaryOperator::Multiply, (6, 7)),
|
||||
right: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(3)),
|
||||
(8, 9)
|
||||
),)
|
||||
},
|
||||
(4, 9)
|
||||
),)
|
||||
},
|
||||
(0, 9),
|
||||
)]
|
||||
.into()
|
||||
@ -731,31 +963,33 @@ mod tests {
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Assign(
|
||||
Box::new(Node::new(
|
||||
Statement::Identifier(Identifier::new("a")),
|
||||
(0, 1)
|
||||
)),
|
||||
Box::new(Node::new(
|
||||
Statement::Add(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(1)), (4, 5))),
|
||||
Box::new(Node::new(
|
||||
Statement::Multiply(
|
||||
Box::new(Node::new(
|
||||
Statement::Assignment {
|
||||
identifier: Identifier::new("a"),
|
||||
value_node: Box::new(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(1)),
|
||||
(4, 5)
|
||||
)),
|
||||
operator: Node::new(BinaryOperator::Add, (6, 7)),
|
||||
right: Box::new(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(2)),
|
||||
(8, 9)
|
||||
)),
|
||||
Box::new(Node::new(
|
||||
operator: Node::new(BinaryOperator::Multiply, (10, 11)),
|
||||
right: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(3)),
|
||||
(12, 13)
|
||||
)),
|
||||
),
|
||||
(8, 13),
|
||||
)),
|
||||
),
|
||||
(4, 13),
|
||||
)),
|
||||
),
|
||||
),)
|
||||
},
|
||||
(8, 13)
|
||||
),)
|
||||
},
|
||||
(4, 13)
|
||||
),)
|
||||
},
|
||||
(0, 13),
|
||||
)]
|
||||
.into()
|
||||
|
@ -26,9 +26,15 @@ pub enum Token<'src> {
|
||||
// Symbols
|
||||
Comma,
|
||||
Dot,
|
||||
DoubleAmpersand,
|
||||
DoublePipe,
|
||||
Equal,
|
||||
Greater,
|
||||
GreaterEqual,
|
||||
LeftParenthesis,
|
||||
LeftSquareBrace,
|
||||
Less,
|
||||
LessEqual,
|
||||
Minus,
|
||||
Plus,
|
||||
RightParenthesis,
|
||||
@ -39,66 +45,107 @@ pub enum Token<'src> {
|
||||
impl<'src> Token<'src> {
|
||||
pub fn to_owned(&self) -> TokenOwned {
|
||||
match self {
|
||||
Token::Eof => TokenOwned::Eof,
|
||||
Token::Identifier(text) => TokenOwned::Identifier(text.to_string()),
|
||||
Token::Boolean(boolean) => TokenOwned::Boolean(*boolean),
|
||||
Token::Float(float) => TokenOwned::Float(*float),
|
||||
Token::Integer(integer) => TokenOwned::Integer(*integer),
|
||||
Token::String(text) => TokenOwned::String(text.to_string()),
|
||||
Token::IsEven => TokenOwned::IsEven,
|
||||
Token::IsOdd => TokenOwned::IsOdd,
|
||||
Token::Length => TokenOwned::Length,
|
||||
Token::ReadLine => TokenOwned::ReadLine,
|
||||
Token::WriteLine => TokenOwned::WriteLine,
|
||||
Token::Comma => TokenOwned::Comma,
|
||||
Token::Dot => TokenOwned::Dot,
|
||||
Token::DoubleAmpersand => TokenOwned::DoubleAmpersand,
|
||||
Token::DoublePipe => TokenOwned::DoublePipe,
|
||||
Token::Eof => TokenOwned::Eof,
|
||||
Token::Equal => TokenOwned::Equal,
|
||||
Token::Plus => TokenOwned::Plus,
|
||||
Token::Star => TokenOwned::Star,
|
||||
Token::Float(float) => TokenOwned::Float(*float),
|
||||
Token::Greater => TokenOwned::Greater,
|
||||
Token::GreaterEqual => TokenOwned::GreaterOrEqual,
|
||||
Token::Identifier(text) => TokenOwned::Identifier(text.to_string()),
|
||||
Token::Integer(integer) => TokenOwned::Integer(*integer),
|
||||
Token::IsEven => TokenOwned::IsEven,
|
||||
Token::IsOdd => TokenOwned::IsOdd,
|
||||
Token::LeftParenthesis => TokenOwned::LeftParenthesis,
|
||||
Token::RightParenthesis => TokenOwned::RightParenthesis,
|
||||
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace,
|
||||
Token::RightSquareBrace => TokenOwned::RightSquareBrace,
|
||||
Token::Length => TokenOwned::Length,
|
||||
Token::Less => TokenOwned::Less,
|
||||
Token::LessEqual => TokenOwned::LessOrEqual,
|
||||
Token::Minus => TokenOwned::Minus,
|
||||
Token::Plus => TokenOwned::Plus,
|
||||
Token::ReadLine => TokenOwned::ReadLine,
|
||||
Token::RightParenthesis => TokenOwned::RightParenthesis,
|
||||
Token::RightSquareBrace => TokenOwned::RightSquareBrace,
|
||||
Token::Star => TokenOwned::Star,
|
||||
Token::String(text) => TokenOwned::String(text.to_string()),
|
||||
Token::WriteLine => TokenOwned::WriteLine,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Token::Boolean(_) => "boolean",
|
||||
Token::Comma => ",",
|
||||
Token::Dot => ".",
|
||||
Token::DoubleAmpersand => "&&",
|
||||
Token::DoublePipe => "||",
|
||||
Token::Eof => "EOF",
|
||||
Token::Equal => "=",
|
||||
Token::Float(_) => "float",
|
||||
Token::Greater => ">",
|
||||
Token::GreaterEqual => ">=",
|
||||
Token::Identifier(_) => "identifier",
|
||||
Token::Integer(_) => "integer",
|
||||
Token::IsEven => "is_even",
|
||||
Token::IsOdd => "is_odd",
|
||||
Token::LeftParenthesis => "(",
|
||||
Token::LeftSquareBrace => "[",
|
||||
Token::Length => "length",
|
||||
Token::Less => "<",
|
||||
Token::LessEqual => "<=",
|
||||
Token::Minus => "-",
|
||||
Token::Plus => "+",
|
||||
Token::ReadLine => "read_line",
|
||||
Token::RightParenthesis => ")",
|
||||
Token::RightSquareBrace => "]",
|
||||
Token::Star => "*",
|
||||
Token::String(_) => "string",
|
||||
Token::WriteLine => "write_line",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> Display for Token<'src> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Token::Eof => write!(f, "EOF"),
|
||||
Token::Identifier(text) => write!(f, "{text}"),
|
||||
Token::Boolean(boolean) => write!(f, "{boolean}"),
|
||||
Token::Float(float) => write!(f, "{float}"),
|
||||
Token::Integer(integer) => write!(f, "{integer}"),
|
||||
Token::String(string) => write!(f, "{string}"),
|
||||
Token::IsEven => write!(f, "is_even"),
|
||||
Token::IsOdd => write!(f, "is_odd"),
|
||||
Token::Length => write!(f, "length"),
|
||||
Token::ReadLine => write!(f, "read_line"),
|
||||
Token::WriteLine => write!(f, "write_line"),
|
||||
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, "]"),
|
||||
Token::Minus => write!(f, "-"),
|
||||
}
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> PartialEq for Token<'src> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
// Floats are compared by their bit representation.
|
||||
(Token::Float(left), Token::Float(right)) => left.to_bits() == right.to_bits(),
|
||||
_ => {
|
||||
matches!(self, other)
|
||||
}
|
||||
|
||||
// Compare all other variants normally.
|
||||
(Token::Boolean(left), Token::Boolean(right)) => left == right,
|
||||
(Token::Comma, Token::Comma) => true,
|
||||
(Token::Dot, Token::Dot) => true,
|
||||
(Token::Eof, Token::Eof) => true,
|
||||
(Token::Equal, Token::Equal) => true,
|
||||
(Token::Greater, Token::Greater) => true,
|
||||
(Token::GreaterEqual, Token::GreaterEqual) => true,
|
||||
(Token::Identifier(left), Token::Identifier(right)) => left == right,
|
||||
(Token::Integer(left), Token::Integer(right)) => left == right,
|
||||
(Token::IsEven, Token::IsEven) => true,
|
||||
(Token::IsOdd, Token::IsOdd) => true,
|
||||
(Token::LeftParenthesis, Token::LeftParenthesis) => true,
|
||||
(Token::LeftSquareBrace, Token::LeftSquareBrace) => true,
|
||||
(Token::Length, Token::Length) => true,
|
||||
(Token::Less, Token::Less) => true,
|
||||
(Token::LessEqual, Token::LessEqual) => true,
|
||||
(Token::Minus, Token::Minus) => true,
|
||||
(Token::Plus, Token::Plus) => true,
|
||||
(Token::ReadLine, Token::ReadLine) => true,
|
||||
(Token::RightParenthesis, Token::RightParenthesis) => true,
|
||||
(Token::RightSquareBrace, Token::RightSquareBrace) => true,
|
||||
(Token::Star, Token::Star) => true,
|
||||
(Token::String(left), Token::String(right)) => left == right,
|
||||
(Token::WriteLine, Token::WriteLine) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -128,9 +175,15 @@ pub enum TokenOwned {
|
||||
// Symbols
|
||||
Comma,
|
||||
Dot,
|
||||
DoubleAmpersand,
|
||||
DoublePipe,
|
||||
Equal,
|
||||
Greater,
|
||||
GreaterOrEqual,
|
||||
LeftParenthesis,
|
||||
LeftSquareBrace,
|
||||
Less,
|
||||
LessOrEqual,
|
||||
Minus,
|
||||
Plus,
|
||||
RightParenthesis,
|
||||
@ -141,27 +194,33 @@ pub enum TokenOwned {
|
||||
impl Display for TokenOwned {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TokenOwned::Eof => write!(f, "EOF"),
|
||||
TokenOwned::Identifier(text) => write!(f, "{text}"),
|
||||
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
|
||||
TokenOwned::Comma => Token::Comma.fmt(f),
|
||||
TokenOwned::Dot => Token::Dot.fmt(f),
|
||||
TokenOwned::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
||||
TokenOwned::DoublePipe => Token::DoublePipe.fmt(f),
|
||||
TokenOwned::Eof => Token::Eof.fmt(f),
|
||||
TokenOwned::Equal => Token::Equal.fmt(f),
|
||||
TokenOwned::Float(float) => write!(f, "{float}"),
|
||||
TokenOwned::Greater => Token::Greater.fmt(f),
|
||||
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
||||
TokenOwned::Identifier(text) => write!(f, "{text}"),
|
||||
TokenOwned::Integer(integer) => write!(f, "{integer}"),
|
||||
TokenOwned::IsEven => Token::IsEven.fmt(f),
|
||||
TokenOwned::IsOdd => Token::IsOdd.fmt(f),
|
||||
TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f),
|
||||
TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
|
||||
TokenOwned::Length => Token::Length.fmt(f),
|
||||
TokenOwned::Less => Token::Less.fmt(f),
|
||||
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
|
||||
TokenOwned::Minus => Token::Minus.fmt(f),
|
||||
TokenOwned::Plus => Token::Plus.fmt(f),
|
||||
TokenOwned::ReadLine => Token::ReadLine.fmt(f),
|
||||
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),
|
||||
TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
||||
TokenOwned::Star => Token::Star.fmt(f),
|
||||
TokenOwned::String(string) => write!(f, "{string}"),
|
||||
TokenOwned::IsEven => write!(f, "is_even"),
|
||||
TokenOwned::IsOdd => write!(f, "is_odd"),
|
||||
TokenOwned::Length => write!(f, "length"),
|
||||
TokenOwned::ReadLine => write!(f, "read_line"),
|
||||
TokenOwned::WriteLine => write!(f, "write_line"),
|
||||
TokenOwned::Comma => write!(f, ","),
|
||||
TokenOwned::Dot => write!(f, "."),
|
||||
TokenOwned::Equal => write!(f, "="),
|
||||
TokenOwned::Plus => write!(f, "+"),
|
||||
TokenOwned::Star => write!(f, "*"),
|
||||
TokenOwned::LeftParenthesis => write!(f, "("),
|
||||
TokenOwned::RightParenthesis => write!(f, ")"),
|
||||
TokenOwned::LeftSquareBrace => write!(f, "["),
|
||||
TokenOwned::RightSquareBrace => write!(f, "]"),
|
||||
TokenOwned::Minus => write!(f, "-"),
|
||||
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,6 +157,74 @@ impl Value {
|
||||
_ => Err(ValueError::CannotMultiply(self.clone(), other.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_than(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::boolean(left < right)),
|
||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
||||
Ok(Value::boolean(left < right))
|
||||
}
|
||||
_ => Err(ValueError::CannotLessThan(self.clone(), other.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_than_or_equal(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Float(left), ValueInner::Float(right)) => {
|
||||
Ok(Value::boolean(left <= right))
|
||||
}
|
||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
||||
Ok(Value::boolean(left <= right))
|
||||
}
|
||||
_ => Err(ValueError::CannotLessThanOrEqual(
|
||||
self.clone(),
|
||||
other.clone(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn greater_than(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::boolean(left > right)),
|
||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
||||
Ok(Value::boolean(left > right))
|
||||
}
|
||||
_ => Err(ValueError::CannotGreaterThan(self.clone(), other.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn greater_than_or_equal(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Float(left), ValueInner::Float(right)) => {
|
||||
Ok(Value::boolean(left >= right))
|
||||
}
|
||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
||||
Ok(Value::boolean(left >= right))
|
||||
}
|
||||
_ => Err(ValueError::CannotGreaterThanOrEqual(
|
||||
self.clone(),
|
||||
other.clone(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn and(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Boolean(left), ValueInner::Boolean(right)) => {
|
||||
Ok(Value::boolean(*left && *right))
|
||||
}
|
||||
_ => Err(ValueError::CannotAnd(self.clone(), other.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Boolean(left), ValueInner::Boolean(right)) => {
|
||||
Ok(Value::boolean(*left || *right))
|
||||
}
|
||||
_ => Err(ValueError::CannotOr(self.clone(), other.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
@ -636,7 +704,7 @@ impl Function {
|
||||
.iter()
|
||||
.last()
|
||||
.unwrap()
|
||||
.statement
|
||||
.inner
|
||||
.expected_type(variables)
|
||||
}
|
||||
}
|
||||
@ -684,11 +752,16 @@ impl Display for Function {
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ValueError {
|
||||
CannotAdd(Value, Value),
|
||||
CannotAnd(Value, Value),
|
||||
CannotGreaterThan(Value, Value),
|
||||
CannotGreaterThanOrEqual(Value, Value),
|
||||
CannotLessThan(Value, Value),
|
||||
CannotLessThanOrEqual(Value, Value),
|
||||
CannotMultiply(Value, Value),
|
||||
CannotSubtract(Value, Value),
|
||||
PropertyNotFound { value: Value, property: Identifier },
|
||||
IndexOutOfBounds { value: Value, index: i64 },
|
||||
CannotOr(Value, Value),
|
||||
ExpectedList(Value),
|
||||
IndexOutOfBounds { value: Value, index: i64 },
|
||||
}
|
||||
|
||||
impl Error for ValueError {}
|
||||
@ -697,14 +770,29 @@ impl Display for ValueError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ValueError::CannotAdd(left, right) => write!(f, "Cannot add {} and {}", left, right),
|
||||
ValueError::CannotAnd(left, right) => write!(
|
||||
f,
|
||||
"Cannot use logical and operation on {} and {}",
|
||||
left, right
|
||||
),
|
||||
ValueError::CannotMultiply(left, right) => {
|
||||
write!(f, "Cannot multiply {} and {}", left, right)
|
||||
}
|
||||
ValueError::CannotSubtract(left, right) => {
|
||||
write!(f, "Cannot subtract {} and {}", left, right)
|
||||
}
|
||||
ValueError::PropertyNotFound { value, property } => {
|
||||
write!(f, "{} does not have a property named {}", value, property)
|
||||
ValueError::CannotLessThan(left, right)
|
||||
| ValueError::CannotLessThanOrEqual(left, right)
|
||||
| ValueError::CannotGreaterThan(left, right)
|
||||
| ValueError::CannotGreaterThanOrEqual(left, right) => {
|
||||
write!(f, "Cannot compare {} and {}", left, right)
|
||||
}
|
||||
ValueError::CannotOr(left, right) => {
|
||||
write!(
|
||||
f,
|
||||
"Cannot use logical or operation on {} and {}",
|
||||
left, right
|
||||
)
|
||||
}
|
||||
ValueError::IndexOutOfBounds { value, index } => {
|
||||
write!(f, "{} does not have an index of {}", value, index)
|
||||
|
@ -6,8 +6,8 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
parse, AbstractSyntaxTree, Analyzer, AnalyzerError, BuiltInFunctionError, Identifier, Node,
|
||||
ParseError, Span, Statement, Value, ValueError,
|
||||
abstract_tree::BinaryOperator, parse, AbstractSyntaxTree, Analyzer, AnalyzerError,
|
||||
BuiltInFunctionError, Identifier, Node, ParseError, Span, Statement, Value, ValueError,
|
||||
};
|
||||
|
||||
pub fn run(
|
||||
@ -48,57 +48,71 @@ impl Vm {
|
||||
|
||||
fn run_node(
|
||||
&self,
|
||||
node: Node,
|
||||
node: Node<Statement>,
|
||||
variables: &mut HashMap<Identifier, Value>,
|
||||
) -> Result<Option<Value>, VmError> {
|
||||
match node.statement {
|
||||
Statement::Add(left, right) => {
|
||||
let left_span = left.position;
|
||||
let left = if let Some(value) = self.run_node(*left, variables)? {
|
||||
match node.inner {
|
||||
Statement::Assignment {
|
||||
identifier,
|
||||
value_node,
|
||||
} => {
|
||||
let value_node_position = value_node.position;
|
||||
let value = if let Some(value) = self.run_node(*value_node, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: left_span,
|
||||
});
|
||||
};
|
||||
let right_span = right.position;
|
||||
let right = if let Some(value) = self.run_node(*right, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: right_span,
|
||||
});
|
||||
};
|
||||
let sum = left
|
||||
.add(&right)
|
||||
.map_err(|value_error| VmError::ValueError {
|
||||
error: value_error,
|
||||
position: (left_span.0, right_span.1),
|
||||
})?;
|
||||
|
||||
Ok(Some(sum))
|
||||
}
|
||||
Statement::Assign(left, right) => {
|
||||
let identifier = if let Statement::Identifier(identifier) = &left.statement {
|
||||
identifier
|
||||
} else {
|
||||
return Err(VmError::ExpectedIdentifier {
|
||||
position: left.position,
|
||||
});
|
||||
};
|
||||
let right_span = right.position;
|
||||
let value = if let Some(value) = self.run_node(*right, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: right_span,
|
||||
position: value_node_position,
|
||||
});
|
||||
};
|
||||
|
||||
variables.insert(identifier.clone(), value);
|
||||
variables.insert(identifier, value);
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
Statement::BinaryOperation {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
} => {
|
||||
let left_position = left.position;
|
||||
let left_value = if let Some(value) = self.run_node(*left, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: left_position,
|
||||
});
|
||||
};
|
||||
|
||||
let right_position = right.position;
|
||||
let right_value = if let Some(value) = self.run_node(*right, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: right_position,
|
||||
});
|
||||
};
|
||||
|
||||
let result = match operator.inner {
|
||||
BinaryOperator::Add => left_value.add(&right_value),
|
||||
BinaryOperator::And => left_value.and(&right_value),
|
||||
BinaryOperator::Divide => todo!(),
|
||||
BinaryOperator::Greater => left_value.greater_than(&right_value),
|
||||
BinaryOperator::GreaterOrEqual => {
|
||||
left_value.greater_than_or_equal(&right_value)
|
||||
}
|
||||
BinaryOperator::Less => left_value.less_than(&right_value),
|
||||
BinaryOperator::LessOrEqual => left_value.less_than_or_equal(&right_value),
|
||||
BinaryOperator::Multiply => left_value.multiply(&right_value),
|
||||
BinaryOperator::Or => left_value.or(&right_value),
|
||||
BinaryOperator::Subtract => left_value.subtract(&right_value),
|
||||
}
|
||||
.map_err(|value_error| VmError::ValueError {
|
||||
error: value_error,
|
||||
position: node.position,
|
||||
})?;
|
||||
|
||||
Ok(Some(result))
|
||||
}
|
||||
Statement::BuiltInFunctionCall {
|
||||
function,
|
||||
type_arguments: _,
|
||||
@ -122,7 +136,13 @@ impl Vm {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let function_call_return = function.call(None, values)?;
|
||||
let function_call_return =
|
||||
function
|
||||
.call(None, values)
|
||||
.map_err(|built_in_function_error| VmError::BuiltInFunctionError {
|
||||
error: built_in_function_error,
|
||||
position: node.position,
|
||||
})?;
|
||||
|
||||
Ok(function_call_return)
|
||||
}
|
||||
@ -196,32 +216,6 @@ impl Vm {
|
||||
|
||||
Ok(Some(Value::list(values)))
|
||||
}
|
||||
Statement::Multiply(left, right) => {
|
||||
let left_span = left.position;
|
||||
let left = if let Some(value) = self.run_node(*left, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: left_span,
|
||||
});
|
||||
};
|
||||
let right_span = right.position;
|
||||
let right = if let Some(value) = self.run_node(*right, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: right_span,
|
||||
});
|
||||
};
|
||||
let product = left
|
||||
.multiply(&right)
|
||||
.map_err(|value_error| VmError::ValueError {
|
||||
error: value_error,
|
||||
position: (left_span.0, right_span.1),
|
||||
})?;
|
||||
|
||||
Ok(Some(product))
|
||||
}
|
||||
Statement::PropertyAccess(left, right) => {
|
||||
let left_span = left.position;
|
||||
let left_value = if let Some(value) = self.run_node(*left, variables)? {
|
||||
@ -234,7 +228,7 @@ impl Vm {
|
||||
let right_span = right.position;
|
||||
|
||||
if let (Some(list), Statement::Constant(value)) =
|
||||
(left_value.as_list(), &right.statement)
|
||||
(left_value.as_list(), &right.inner)
|
||||
{
|
||||
if let Some(index) = value.as_integer() {
|
||||
let value = list.get(index as usize).cloned();
|
||||
@ -250,7 +244,7 @@ impl Vm {
|
||||
type_arguments: _,
|
||||
value_arguments: value_argument_nodes,
|
||||
},
|
||||
) = (left_value, right.statement)
|
||||
) = (left_value, right.inner)
|
||||
{
|
||||
let mut value_arguments = Vec::new();
|
||||
|
||||
@ -269,7 +263,12 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
let function_call_return = function.call(None, Some(value_arguments))?;
|
||||
let function_call_return = function.call(None, Some(value_arguments)).map_err(
|
||||
|built_in_function_error| VmError::BuiltInFunctionError {
|
||||
error: built_in_function_error,
|
||||
position: right_span,
|
||||
},
|
||||
)?;
|
||||
|
||||
return Ok(function_call_return);
|
||||
}
|
||||
@ -278,32 +277,6 @@ impl Vm {
|
||||
position: right_span,
|
||||
})
|
||||
}
|
||||
Statement::Subtract(left, right) => {
|
||||
let left_span = left.position;
|
||||
let left = if let Some(value) = self.run_node(*left, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: left_span,
|
||||
});
|
||||
};
|
||||
let right_span = right.position;
|
||||
let right = if let Some(value) = self.run_node(*right, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: right_span,
|
||||
});
|
||||
};
|
||||
let difference =
|
||||
left.subtract(&right)
|
||||
.map_err(|value_error| VmError::ValueError {
|
||||
error: value_error,
|
||||
position: (left_span.0, right_span.1),
|
||||
})?;
|
||||
|
||||
Ok(Some(difference))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -319,7 +292,10 @@ pub enum VmError {
|
||||
|
||||
// Anaylsis Failures
|
||||
// These should be prevented by running the analyzer before the VM
|
||||
BuiltInFunctionCallError(BuiltInFunctionError),
|
||||
BuiltInFunctionError {
|
||||
error: BuiltInFunctionError,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedIdentifier {
|
||||
position: Span,
|
||||
},
|
||||
@ -345,9 +321,21 @@ pub enum VmError {
|
||||
},
|
||||
}
|
||||
|
||||
impl From<BuiltInFunctionError> for VmError {
|
||||
fn from(v: BuiltInFunctionError) -> Self {
|
||||
Self::BuiltInFunctionCallError(v)
|
||||
impl VmError {
|
||||
pub fn position(&self) -> Span {
|
||||
match self {
|
||||
Self::AnaylyzerError(analyzer_error) => analyzer_error.position(),
|
||||
Self::ParseError(parse_error) => parse_error.position(),
|
||||
Self::ValueError { position, .. } => *position,
|
||||
Self::BuiltInFunctionError { position, .. } => *position,
|
||||
Self::ExpectedIdentifier { position } => *position,
|
||||
Self::ExpectedIdentifierOrInteger { position } => *position,
|
||||
Self::ExpectedInteger { position } => *position,
|
||||
Self::ExpectedFunction { position, .. } => *position,
|
||||
Self::ExpectedList { position } => *position,
|
||||
Self::ExpectedValue { position } => *position,
|
||||
Self::UndefinedIdentifier { position, .. } => *position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,9 +357,7 @@ impl Error for VmError {
|
||||
Self::AnaylyzerError(analyzer_error) => Some(analyzer_error),
|
||||
Self::ParseError(parse_error) => Some(parse_error),
|
||||
Self::ValueError { error, .. } => Some(error),
|
||||
Self::BuiltInFunctionCallError(built_in_function_error) => {
|
||||
Some(built_in_function_error)
|
||||
}
|
||||
Self::BuiltInFunctionError { error, .. } => Some(error),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -383,8 +369,8 @@ impl Display for VmError {
|
||||
Self::AnaylyzerError(analyzer_error) => write!(f, "{}", analyzer_error),
|
||||
Self::ParseError(parse_error) => write!(f, "{}", parse_error),
|
||||
Self::ValueError { error, .. } => write!(f, "{}", error),
|
||||
Self::BuiltInFunctionCallError(built_in_function_error) => {
|
||||
write!(f, "{}", built_in_function_error)
|
||||
Self::BuiltInFunctionError { error, .. } => {
|
||||
write!(f, "{}", error)
|
||||
}
|
||||
Self::ExpectedFunction { actual, position } => {
|
||||
write!(
|
||||
@ -430,6 +416,46 @@ impl Display for VmError {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn less_than() {
|
||||
let input = "2 < 3";
|
||||
|
||||
assert_eq!(
|
||||
run(input, &mut HashMap::new()),
|
||||
Ok(Some(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_than_or_equal() {
|
||||
let input = "42 <= 42";
|
||||
|
||||
assert_eq!(
|
||||
run(input, &mut HashMap::new()),
|
||||
Ok(Some(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_than() {
|
||||
let input = "2 > 3";
|
||||
|
||||
assert_eq!(
|
||||
run(input, &mut HashMap::new()),
|
||||
Ok(Some(Value::boolean(false)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_than_or_equal() {
|
||||
let input = "42 >= 42";
|
||||
|
||||
assert_eq!(
|
||||
run(input, &mut HashMap::new()),
|
||||
Ok(Some(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn integer_saturating_add() {
|
||||
let input = "9223372036854775807 + 1";
|
||||
|
Loading…
Reference in New Issue
Block a user