1
0

Add comparison statement to replace four separate statements

This commit is contained in:
Jeff 2024-08-09 03:00:48 -04:00
parent 580b85e2d0
commit b9081f8653
7 changed files with 626 additions and 164 deletions

View File

@ -11,51 +11,61 @@ use crate::{BuiltInFunction, Identifier, Span, Type, Value};
/// In-memory representation of a Dust program. /// In-memory representation of a Dust program.
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct AbstractSyntaxTree { pub struct AbstractSyntaxTree {
pub nodes: VecDeque<Node>, pub nodes: VecDeque<Node<Statement>>,
} }
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Node { pub struct Node<T> {
pub statement: Statement, pub inner: T,
pub position: Span, pub position: Span,
} }
impl Node { impl<T> Node<T> {
pub fn new(operation: Statement, position: Span) -> Self { pub fn new(inner: T, position: Span) -> Self {
Self { Self { inner, position }
statement: operation,
position,
}
} }
} }
impl Display for Node { impl<T: Display> Display for Node<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { 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)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Statement { pub enum Statement {
// Top-level statements // Top-level statements
Assign(Box<Node>, Box<Node>), Assign(Box<Node<Statement>>, Box<Node<Statement>>),
// Expressions // Math expressions
Add(Box<Node>, Box<Node>), Add(Box<Node<Statement>>, Box<Node<Statement>>),
Subtract(Box<Node<Statement>>, Box<Node<Statement>>),
Multiply(Box<Node<Statement>>, Box<Node<Statement>>),
// Function calls
BuiltInFunctionCall { BuiltInFunctionCall {
function: BuiltInFunction, function: BuiltInFunction,
type_arguments: Option<Vec<Node>>, type_arguments: Option<Vec<Node<Statement>>>,
value_arguments: Option<Vec<Node>>, value_arguments: Option<Vec<Node<Statement>>>,
}, },
FunctionCall { FunctionCall {
function: Box<Node>, function: Box<Node<Statement>>,
type_arguments: Option<Vec<Node>>, type_arguments: Option<Vec<Node<Statement>>>,
value_arguments: Option<Vec<Node>>, value_arguments: Option<Vec<Node<Statement>>>,
}, },
PropertyAccess(Box<Node>, Box<Node>),
Subtract(Box<Node>, Box<Node>), // Comparison expressions
List(Vec<Node>), Comparison(
Multiply(Box<Node>, Box<Node>), Box<Node<Statement>>,
Node<ComparisonOperator>,
Box<Node<Statement>>,
),
// Property access
PropertyAccess(Box<Node<Statement>>, Box<Node<Statement>>),
// Value collections
List(Vec<Node<Statement>>),
// Hard-coded values // Hard-coded values
Constant(Value), Constant(Value),
@ -65,18 +75,22 @@ pub enum Statement {
impl Statement { impl Statement {
pub fn expected_type(&self, variables: &HashMap<Identifier, Value>) -> Option<Type> { pub fn expected_type(&self, variables: &HashMap<Identifier, Value>) -> Option<Type> {
match self { match self {
Statement::Add(left, _) => left.statement.expected_type(variables), Statement::Add(left, _) => left.inner.expected_type(variables),
Statement::Assign(_, _) => None, Statement::Assign(_, _) => None,
Statement::BuiltInFunctionCall { function, .. } => function.expected_type(), Statement::BuiltInFunctionCall { function, .. } => function.expected_type(),
Statement::Comparison(_, _, _) => Some(Type::Boolean),
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::FunctionCall { function, .. } => function.inner.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(nodes) => nodes
Statement::Multiply(left, _) => left.statement.expected_type(variables), .first()
.map(|node| node.inner.expected_type(variables))
.flatten(),
Statement::Multiply(left, _) => left.inner.expected_type(variables),
Statement::PropertyAccess(_, _) => None, Statement::PropertyAccess(_, _) => None,
Statement::Subtract(left, _) => left.statement.expected_type(variables), Statement::Subtract(left, _) => left.inner.expected_type(variables),
} }
} }
} }
@ -121,6 +135,8 @@ impl Display for Statement {
write!(f, ")") write!(f, ")")
} }
Statement::Comparison(left, operator, right) => write!(f, "{left} {operator} {right}"),
Statement::Constant(value) => write!(f, "{value}"),
Statement::FunctionCall { Statement::FunctionCall {
function, function,
type_arguments: type_parameters, type_arguments: type_parameters,
@ -156,6 +172,7 @@ impl Display for Statement {
write!(f, ")") write!(f, ")")
} }
Statement::Identifier(identifier) => write!(f, "{identifier}"),
Statement::List(nodes) => { Statement::List(nodes) => {
write!(f, "[")?; write!(f, "[")?;
for (i, node) in nodes.iter().enumerate() { for (i, node) in nodes.iter().enumerate() {
@ -166,11 +183,28 @@ impl Display for Statement {
} }
write!(f, "]") write!(f, "]")
} }
Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
Statement::Multiply(left, right) => write!(f, "{left} * {right}"), Statement::Multiply(left, right) => write!(f, "{left} * {right}"),
Statement::Constant(value) => write!(f, "{value}"), Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
Statement::Identifier(identifier) => write!(f, "{identifier}"),
Statement::Subtract(left, right) => write!(f, "{left} - {right}"), Statement::Subtract(left, right) => write!(f, "{left} - {right}"),
} }
} }
} }
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum ComparisonOperator {
GreaterThan,
GreaterThanOrEqual,
LessThan,
LessThanOrEqual,
}
impl Display for ComparisonOperator {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
ComparisonOperator::GreaterThan => write!(f, ">"),
ComparisonOperator::GreaterThanOrEqual => write!(f, ">="),
ComparisonOperator::LessThan => write!(f, "<"),
ComparisonOperator::LessThanOrEqual => write!(f, "<="),
}
}
}

View File

@ -72,14 +72,14 @@ impl<'a> Analyzer<'a> {
Ok(()) Ok(())
} }
fn analyze_node(&self, node: &Node) -> Result<(), AnalyzerError> { fn analyze_node(&self, node: &Node<Statement>) -> Result<(), AnalyzerError> {
match &node.statement { match &node.inner {
Statement::Add(left, right) => { Statement::Add(left, right) => {
self.analyze_node(left)?; self.analyze_node(left)?;
self.analyze_node(right)?; self.analyze_node(right)?;
let left_type = left.statement.expected_type(self.variables); let left_type = left.inner.expected_type(self.variables);
let right_type = right.statement.expected_type(self.variables); let right_type = right.inner.expected_type(self.variables);
match (left_type, right_type) { match (left_type, right_type) {
(Some(Type::Integer), Some(Type::Integer)) => {} (Some(Type::Integer), Some(Type::Integer)) => {}
@ -100,7 +100,7 @@ impl<'a> Analyzer<'a> {
} }
} }
Statement::Assign(left, right) => { Statement::Assign(left, right) => {
if let Statement::Identifier(_) = &left.statement { if let Statement::Identifier(_) = &left.inner {
// Identifier is in the correct position // Identifier is in the correct position
} else { } else {
return Err(AnalyzerError::ExpectedIdentifier { return Err(AnalyzerError::ExpectedIdentifier {
@ -112,9 +112,33 @@ impl<'a> Analyzer<'a> {
self.analyze_node(right)?; self.analyze_node(right)?;
} }
Statement::BuiltInFunctionCall { .. } => {} Statement::BuiltInFunctionCall { .. } => {}
Statement::Comparison(left, _, right) => {
self.analyze_node(left)?;
self.analyze_node(right)?;
if let Some(Type::Integer) | Some(Type::Float) =
left.inner.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.inner.expected_type(self.variables)
{
} else {
return Err(AnalyzerError::ExpectedIntegerOrFloat {
actual: right.as_ref().clone(),
position: right.position,
});
}
}
Statement::Constant(_) => {} Statement::Constant(_) => {}
Statement::FunctionCall { function, .. } => { Statement::FunctionCall { function, .. } => {
if let Statement::Identifier(_) = &function.statement { if let Statement::Identifier(_) = &function.inner {
// Function is in the correct position // Function is in the correct position
} else { } else {
return Err(AnalyzerError::ExpectedIdentifier { return Err(AnalyzerError::ExpectedIdentifier {
@ -139,7 +163,7 @@ impl<'a> Analyzer<'a> {
self.analyze_node(right)?; self.analyze_node(right)?;
if let Some(Type::Integer) | Some(Type::Float) = if let Some(Type::Integer) | Some(Type::Float) =
left.statement.expected_type(self.variables) left.inner.expected_type(self.variables)
{ {
} else { } else {
return Err(AnalyzerError::ExpectedIntegerOrFloat { return Err(AnalyzerError::ExpectedIntegerOrFloat {
@ -149,7 +173,7 @@ impl<'a> Analyzer<'a> {
} }
if let Some(Type::Integer) | Some(Type::Float) = if let Some(Type::Integer) | Some(Type::Float) =
right.statement.expected_type(self.variables) right.inner.expected_type(self.variables)
{ {
} else { } else {
return Err(AnalyzerError::ExpectedIntegerOrFloat { return Err(AnalyzerError::ExpectedIntegerOrFloat {
@ -160,7 +184,7 @@ impl<'a> Analyzer<'a> {
} }
Statement::PropertyAccess(left, right) => { Statement::PropertyAccess(left, right) => {
if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) = if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) =
&left.statement &left.inner
{ {
// Left side is valid // Left side is valid
} else { } else {
@ -170,9 +194,9 @@ impl<'a> Analyzer<'a> {
}); });
} }
if let Statement::BuiltInFunctionCall { function, .. } = &right.statement { if let Statement::BuiltInFunctionCall { function, .. } = &right.inner {
if function == &BuiltInFunction::IsEven || function == &BuiltInFunction::IsOdd { 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 { } else {
return Err(AnalyzerError::ExpectedIntegerOrFloat { return Err(AnalyzerError::ExpectedIntegerOrFloat {
actual: left.as_ref().clone(), actual: left.as_ref().clone(),
@ -188,8 +212,8 @@ impl<'a> Analyzer<'a> {
self.analyze_node(left)?; self.analyze_node(left)?;
self.analyze_node(right)?; self.analyze_node(right)?;
let left_type = left.statement.expected_type(self.variables); let left_type = left.inner.expected_type(self.variables);
let right_type = right.statement.expected_type(self.variables); let right_type = right.inner.expected_type(self.variables);
match (left_type, right_type) { match (left_type, right_type) {
(Some(Type::Integer), Some(Type::Integer)) => {} (Some(Type::Integer), Some(Type::Integer)) => {}
@ -216,13 +240,34 @@ impl<'a> Analyzer<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum AnalyzerError { pub enum AnalyzerError {
ExpectedBoolean { actual: Node, position: Span }, ExpectedBoolean {
ExpectedFunction { actual: Node, position: Span }, actual: Node<Statement>,
ExpectedIdentifier { actual: Node, position: Span }, position: Span,
ExpectedIdentifierOrValue { actual: Node, position: Span }, },
ExpectedIntegerOrFloat { actual: Node, position: Span }, ExpectedFunction {
ExpectedIntegerFloatOrString { actual: Node, position: Span }, actual: Node<Statement>,
UnexpectedIdentifier { identifier: Node, position: Span }, position: Span,
},
ExpectedIdentifier {
actual: Node<Statement>,
position: Span,
},
ExpectedIdentifierOrValue {
actual: Node<Statement>,
position: Span,
},
ExpectedIntegerOrFloat {
actual: Node<Statement>,
position: Span,
},
ExpectedIntegerFloatOrString {
actual: Node<Statement>,
position: Span,
},
UnexpectedIdentifier {
identifier: Node<Statement>,
position: Span,
},
} }
impl AnalyzerError { impl AnalyzerError {

View File

@ -167,6 +167,28 @@ impl Lexer {
(Token::Dot, (self.position - 1, self.position)) (Token::Dot, (self.position - 1, self.position))
} }
'>' => {
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; self.position += 1;
@ -393,6 +415,46 @@ impl From<ParseIntError> for LexError {
mod tests { mod tests {
use super::*; 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] #[test]
fn infinity() { fn infinity() {
let input = "Infinity"; let input = "Infinity";

View File

@ -10,8 +10,8 @@ use std::{
}; };
use crate::{ use crate::{
built_in_function::BuiltInFunction, token::TokenOwned, AbstractSyntaxTree, Identifier, abstract_tree::ComparisonOperator, built_in_function::BuiltInFunction, token::TokenOwned,
LexError, Lexer, Node, Span, Statement, Token, Value, AbstractSyntaxTree, Identifier, LexError, Lexer, Node, Span, Statement, Token, Value,
}; };
/// Parses the input into an abstract syntax tree. /// Parses the input into an abstract syntax tree.
@ -27,13 +27,13 @@ use crate::{
/// Ok(AbstractSyntaxTree { /// Ok(AbstractSyntaxTree {
/// nodes: [ /// nodes: [
/// Node { /// Node {
/// statement: Statement::Assign( /// inner: Statement::Assign(
/// Box::new(Node { /// Box::new(Node {
/// statement: Statement::Identifier("x".into()), /// inner: Statement::Identifier("x".into()),
/// position: (0, 1), /// position: (0, 1),
/// }), /// }),
/// Box::new(Node { /// Box::new(Node {
/// statement: Statement::Constant(Value::integer(42)), /// inner: Statement::Constant(Value::integer(42)),
/// position: (4, 6), /// position: (4, 6),
/// }) /// })
/// ), /// ),
@ -84,15 +84,15 @@ pub fn parse(input: &str) -> Result<AbstractSyntaxTree, ParseError> {
/// ///
/// assert_eq!( /// assert_eq!(
/// nodes, /// nodes,
/// Into::<VecDeque<Node>>::into([ /// Into::<VecDeque<Node<Statement>>>::into([
/// Node { /// Node {
/// statement: Statement::Assign( /// inner: Statement::Assign(
/// Box::new(Node { /// Box::new(Node {
/// statement: Statement::Identifier("x".into()), /// inner: Statement::Identifier("x".into()),
/// position: (0, 1), /// position: (0, 1),
/// }), /// }),
/// Box::new(Node { /// Box::new(Node {
/// statement: Statement::Constant(Value::integer(42)), /// inner: Statement::Constant(Value::integer(42)),
/// position: (4, 6), /// 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) self.parse_node(0)
} }
@ -149,12 +149,113 @@ impl<'src> Parser<'src> {
Ok(()) 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_node = self.parse_primary()?;
let left_start = left_node.position.0; let left_start = left_node.position.0;
if precedence < self.current_precedence() { if precedence < self.current_precedence() {
match &self.current { match &self.current {
(Token::Dot, _) => {
self.next_token()?;
let right_node = self.parse_node(self.current_precedence())?;
let right_end = right_node.position.1;
return Ok(Node::new(
Statement::PropertyAccess(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::Greater, _) => {
let operator_position = 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::Comparison(
Box::new(left_node),
Node::new(ComparisonOperator::GreaterThan, operator_position),
Box::new(right_node),
),
(left_start, right_end),
));
}
(Token::GreaterEqual, _) => {
let operator_position = 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::Comparison(
Box::new(left_node),
Node::new(ComparisonOperator::GreaterThanOrEqual, operator_position),
Box::new(right_node),
),
(left_start, right_end),
));
}
(Token::Less, _) => {
let operator_position = 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::Comparison(
Box::new(left_node),
Node::new(ComparisonOperator::LessThan, operator_position),
Box::new(right_node),
),
(left_start, right_end),
));
}
(Token::LessEqual, _) => {
let operator_position = 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::Comparison(
Box::new(left_node),
Node::new(ComparisonOperator::LessThanOrEqual, operator_position),
Box::new(right_node),
),
(left_start, right_end),
));
}
(Token::Minus, _) => {
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)),
(left_start, right_end),
));
}
(Token::Plus, _) => { (Token::Plus, _) => {
self.next_token()?; self.next_token()?;
@ -177,39 +278,6 @@ impl<'src> Parser<'src> {
(left_start, right_end), (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()?;
let right_node = self.parse_node(self.current_precedence())?;
let right_end = right_node.position.1;
return Ok(Node::new(
Statement::PropertyAccess(Box::new(left_node), Box::new(right_node)),
(left_start, right_end),
));
}
(Token::Minus, _) => {
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)),
(left_start, right_end),
));
}
_ => {} _ => {}
} }
} }
@ -217,7 +285,7 @@ impl<'src> Parser<'src> {
Ok(left_node) Ok(left_node)
} }
fn parse_primary(&mut self) -> Result<Node, ParseError> { fn parse_primary(&mut self) -> Result<Node<Statement>, ParseError> {
match self.current { match self.current {
(Token::Boolean(boolean), span) => { (Token::Boolean(boolean), span) => {
self.next_token()?; self.next_token()?;
@ -258,7 +326,7 @@ impl<'src> Parser<'src> {
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(node.statement, (left_span.0, right_span.1))) Ok(Node::new(node.inner, (left_span.0, right_span.1)))
} else { } else {
Err(ParseError::ExpectedClosingParenthesis { Err(ParseError::ExpectedClosingParenthesis {
actual: self.current.0.to_owned(), actual: self.current.0.to_owned(),
@ -321,7 +389,7 @@ impl<'src> Parser<'src> {
}); });
} }
let mut value_arguments: Option<Vec<Node>> = None; let mut value_arguments: Option<Vec<Node<Statement>>> = None;
loop { loop {
if let (Token::RightParenthesis, _) = self.current { if let (Token::RightParenthesis, _) = self.current {
@ -366,6 +434,7 @@ impl<'src> Parser<'src> {
fn current_precedence(&self) -> u8 { fn current_precedence(&self) -> u8 {
match self.current.0 { match self.current.0 {
Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5,
Token::Dot => 4, Token::Dot => 4,
Token::Equal => 3, Token::Equal => 3,
Token::Star => 2, Token::Star => 2,
@ -427,10 +496,90 @@ impl Display for ParseError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::Identifier; use crate::{abstract_tree::ComparisonOperator, Identifier};
use super::*; use super::*;
#[test]
fn less_than() {
let input = "1 < 2";
assert_eq!(
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node::new(
Statement::Comparison(
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
Node::new(ComparisonOperator::LessThan, (2, 3)),
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::Comparison(
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
Node::new(ComparisonOperator::LessThanOrEqual, (2, 4)),
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::Comparison(
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
Node::new(ComparisonOperator::GreaterThanOrEqual, (2, 4)),
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::Comparison(
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
Node::new(ComparisonOperator::GreaterThan, (2, 3)),
Box::new(Node::new(Statement::Constant(Value::integer(2)), (4, 5)))
),
(0, 5)
)]
.into()
})
);
}
#[test] #[test]
fn subtract_negative_integers() { fn subtract_negative_integers() {
let input = "-1 - -2"; let input = "-1 - -2";

View File

@ -27,8 +27,12 @@ pub enum Token<'src> {
Comma, Comma,
Dot, Dot,
Equal, Equal,
Greater,
GreaterEqual,
LeftParenthesis, LeftParenthesis,
LeftSquareBrace, LeftSquareBrace,
Less,
LessEqual,
Minus, Minus,
Plus, Plus,
RightParenthesis, RightParenthesis,
@ -39,53 +43,61 @@ pub enum Token<'src> {
impl<'src> Token<'src> { impl<'src> Token<'src> {
pub fn to_owned(&self) -> TokenOwned { pub fn to_owned(&self) -> TokenOwned {
match self { match self {
Token::Eof => TokenOwned::Eof,
Token::Identifier(text) => TokenOwned::Identifier(text.to_string()),
Token::Boolean(boolean) => TokenOwned::Boolean(*boolean), 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::Comma => TokenOwned::Comma,
Token::Dot => TokenOwned::Dot, Token::Dot => TokenOwned::Dot,
Token::Eof => TokenOwned::Eof,
Token::Equal => TokenOwned::Equal, Token::Equal => TokenOwned::Equal,
Token::Plus => TokenOwned::Plus, Token::Float(float) => TokenOwned::Float(*float),
Token::Star => TokenOwned::Star, 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::LeftParenthesis => TokenOwned::LeftParenthesis,
Token::RightParenthesis => TokenOwned::RightParenthesis,
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace, 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::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 { pub fn as_str(&self) -> &'static str {
match self { match self {
Token::Eof => "EOF",
Token::Identifier(_) => "identifier",
Token::Boolean(_) => "boolean", Token::Boolean(_) => "boolean",
Token::Float(_) => "float",
Token::Integer(_) => "integer",
Token::String(_) => "string",
Token::IsEven => "is_even",
Token::IsOdd => "is_odd",
Token::Length => "length",
Token::ReadLine => "read_line",
Token::WriteLine => "write_line",
Token::Comma => ",", Token::Comma => ",",
Token::Dot => ".", Token::Dot => ".",
Token::Eof => "EOF",
Token::Equal => "=", Token::Equal => "=",
Token::Plus => "+", Token::Float(_) => "float",
Token::Star => "*", Token::Greater => ">",
Token::GreaterEqual => ">=",
Token::Identifier(_) => "identifier",
Token::Integer(_) => "integer",
Token::IsEven => "is_even",
Token::IsOdd => "is_odd",
Token::LeftParenthesis => "(", Token::LeftParenthesis => "(",
Token::RightParenthesis => ")",
Token::LeftSquareBrace => "[", Token::LeftSquareBrace => "[",
Token::RightSquareBrace => "]", Token::Length => "length",
Token::Less => "<",
Token::LessEqual => "<=",
Token::Minus => "-", Token::Minus => "-",
Token::Plus => "+",
Token::ReadLine => "read_line",
Token::RightParenthesis => ")",
Token::RightSquareBrace => "]",
Token::Star => "*",
Token::String(_) => "string",
Token::WriteLine => "write_line",
} }
} }
} }
@ -103,26 +115,30 @@ impl<'src> PartialEq for Token<'src> {
(Token::Float(left), Token::Float(right)) => left.to_bits() == right.to_bits(), (Token::Float(left), Token::Float(right)) => left.to_bits() == right.to_bits(),
// Compare all other variants normally. // Compare all other variants normally.
(Token::Eof, Token::Eof) => true,
(Token::Identifier(left), Token::Identifier(right)) => left == right,
(Token::Boolean(left), Token::Boolean(right)) => left == right, (Token::Boolean(left), Token::Boolean(right)) => left == right,
(Token::Integer(left), Token::Integer(right)) => left == right,
(Token::String(left), Token::String(right)) => left == right,
(Token::IsEven, Token::IsEven) => true,
(Token::IsOdd, Token::IsOdd) => true,
(Token::Length, Token::Length) => true,
(Token::ReadLine, Token::ReadLine) => true,
(Token::WriteLine, Token::WriteLine) => true,
(Token::Comma, Token::Comma) => true, (Token::Comma, Token::Comma) => true,
(Token::Dot, Token::Dot) => true, (Token::Dot, Token::Dot) => true,
(Token::Eof, Token::Eof) => true,
(Token::Equal, Token::Equal) => true, (Token::Equal, Token::Equal) => true,
(Token::Plus, Token::Plus) => true, (Token::Greater, Token::Greater) => true,
(Token::Star, Token::Star) => 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::LeftParenthesis, Token::LeftParenthesis) => true,
(Token::RightParenthesis, Token::RightParenthesis) => true,
(Token::LeftSquareBrace, Token::LeftSquareBrace) => true, (Token::LeftSquareBrace, Token::LeftSquareBrace) => true,
(Token::RightSquareBrace, Token::RightSquareBrace) => true, (Token::Length, Token::Length) => true,
(Token::Less, Token::Less) => true,
(Token::LessEqual, Token::LessEqual) => true,
(Token::Minus, Token::Minus) => 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, _ => false,
} }
} }
@ -154,8 +170,12 @@ pub enum TokenOwned {
Comma, Comma,
Dot, Dot,
Equal, Equal,
Greater,
GreaterOrEqual,
LeftParenthesis, LeftParenthesis,
LeftSquareBrace, LeftSquareBrace,
Less,
LessOrEqual,
Minus, Minus,
Plus, Plus,
RightParenthesis, RightParenthesis,
@ -166,27 +186,31 @@ pub enum TokenOwned {
impl Display for TokenOwned { impl Display for TokenOwned {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
TokenOwned::Eof => Token::Eof.fmt(f),
TokenOwned::Identifier(text) => write!(f, "{text}"),
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"), TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
TokenOwned::Float(float) => write!(f, "{float}"),
TokenOwned::Integer(integer) => write!(f, "{integer}"),
TokenOwned::String(string) => write!(f, "{string}"),
TokenOwned::IsEven => Token::IsEven.fmt(f),
TokenOwned::IsOdd => Token::IsOdd.fmt(f),
TokenOwned::Length => Token::Length.fmt(f),
TokenOwned::ReadLine => Token::ReadLine.fmt(f),
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
TokenOwned::Comma => Token::Comma.fmt(f), TokenOwned::Comma => Token::Comma.fmt(f),
TokenOwned::Dot => Token::Dot.fmt(f), TokenOwned::Dot => Token::Dot.fmt(f),
TokenOwned::Eof => Token::Eof.fmt(f),
TokenOwned::Equal => Token::Equal.fmt(f), TokenOwned::Equal => Token::Equal.fmt(f),
TokenOwned::Plus => Token::Plus.fmt(f), TokenOwned::Float(float) => write!(f, "{float}"),
TokenOwned::Star => Token::Star.fmt(f), 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::LeftParenthesis => Token::LeftParenthesis.fmt(f),
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),
TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f), TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
TokenOwned::RightSquareBrace => Token::RightSquareBrace.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::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::WriteLine => Token::WriteLine.fmt(f),
} }
} }
} }

View File

@ -157,6 +157,56 @@ impl Value {
_ => Err(ValueError::CannotMultiply(self.clone(), other.clone())), _ => 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(),
)),
}
}
} }
impl Display for Value { impl Display for Value {
@ -636,7 +686,7 @@ impl Function {
.iter() .iter()
.last() .last()
.unwrap() .unwrap()
.statement .inner
.expected_type(variables) .expected_type(variables)
} }
} }
@ -686,9 +736,13 @@ pub enum ValueError {
CannotAdd(Value, Value), CannotAdd(Value, Value),
CannotMultiply(Value, Value), CannotMultiply(Value, Value),
CannotSubtract(Value, Value), CannotSubtract(Value, Value),
PropertyNotFound { value: Value, property: Identifier }, CannotLessThan(Value, Value),
IndexOutOfBounds { value: Value, index: i64 }, CannotLessThanOrEqual(Value, Value),
CannotGreaterThan(Value, Value),
CannotGreaterThanOrEqual(Value, Value),
ExpectedList(Value), ExpectedList(Value),
IndexOutOfBounds { value: Value, index: i64 },
PropertyNotFound { value: Value, property: Identifier },
} }
impl Error for ValueError {} impl Error for ValueError {}
@ -703,6 +757,12 @@ impl Display for ValueError {
ValueError::CannotSubtract(left, right) => { ValueError::CannotSubtract(left, right) => {
write!(f, "Cannot subtract {} and {}", left, right) write!(f, "Cannot subtract {} and {}", left, right)
} }
ValueError::CannotLessThan(left, right)
| ValueError::CannotLessThanOrEqual(left, right)
| ValueError::CannotGreaterThan(left, right)
| ValueError::CannotGreaterThanOrEqual(left, right) => {
write!(f, "Cannot compare {} and {}", left, right)
}
ValueError::PropertyNotFound { value, property } => { ValueError::PropertyNotFound { value, property } => {
write!(f, "{} does not have a property named {}", value, property) write!(f, "{} does not have a property named {}", value, property)
} }

View File

@ -6,8 +6,8 @@ use std::{
}; };
use crate::{ use crate::{
parse, AbstractSyntaxTree, Analyzer, AnalyzerError, BuiltInFunctionError, Identifier, Node, abstract_tree::ComparisonOperator, parse, AbstractSyntaxTree, Analyzer, AnalyzerError,
ParseError, Span, Statement, Value, ValueError, BuiltInFunctionError, Identifier, Node, ParseError, Span, Statement, Value, ValueError,
}; };
pub fn run( pub fn run(
@ -48,10 +48,10 @@ impl Vm {
fn run_node( fn run_node(
&self, &self,
node: Node, node: Node<Statement>,
variables: &mut HashMap<Identifier, Value>, variables: &mut HashMap<Identifier, Value>,
) -> Result<Option<Value>, VmError> { ) -> Result<Option<Value>, VmError> {
match node.statement { match node.inner {
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)? {
@ -79,7 +79,7 @@ impl Vm {
Ok(Some(sum)) Ok(Some(sum))
} }
Statement::Assign(left, right) => { Statement::Assign(left, right) => {
let identifier = if let Statement::Identifier(identifier) = &left.statement { let identifier = if let Statement::Identifier(identifier) = &left.inner {
identifier identifier
} else { } else {
return Err(VmError::ExpectedIdentifier { return Err(VmError::ExpectedIdentifier {
@ -132,6 +132,54 @@ impl Vm {
Ok(function_call_return) Ok(function_call_return)
} }
Statement::Comparison(left, operator, 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 comparison = match operator.inner {
ComparisonOperator::GreaterThan => {
left.greater_than(&right)
.map_err(|value_error| VmError::ValueError {
error: value_error,
position: (left_span.0, right_span.1),
})?
}
ComparisonOperator::GreaterThanOrEqual => left
.greater_than_or_equal(&right)
.map_err(|value_error| VmError::ValueError {
error: value_error,
position: (left_span.0, right_span.1),
})?,
ComparisonOperator::LessThan => {
left.less_than(&right)
.map_err(|value_error| VmError::ValueError {
error: value_error,
position: (left_span.0, right_span.1),
})?
}
ComparisonOperator::LessThanOrEqual => left
.less_than_or_equal(&right)
.map_err(|value_error| VmError::ValueError {
error: value_error,
position: (left_span.0, right_span.1),
})?,
};
Ok(Some(comparison))
}
Statement::Constant(value) => Ok(Some(value.clone())), Statement::Constant(value) => Ok(Some(value.clone())),
Statement::FunctionCall { Statement::FunctionCall {
function: function_node, function: function_node,
@ -240,7 +288,7 @@ impl Vm {
let right_span = right.position; let right_span = right.position;
if let (Some(list), Statement::Constant(value)) = 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() { if let Some(index) = value.as_integer() {
let value = list.get(index as usize).cloned(); let value = list.get(index as usize).cloned();
@ -256,7 +304,7 @@ impl Vm {
type_arguments: _, type_arguments: _,
value_arguments: value_argument_nodes, value_arguments: value_argument_nodes,
}, },
) = (left_value, right.statement) ) = (left_value, right.inner)
{ {
let mut value_arguments = Vec::new(); let mut value_arguments = Vec::new();
@ -454,6 +502,46 @@ impl Display for VmError {
mod tests { mod tests {
use super::*; 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] #[test]
fn integer_saturating_add() { fn integer_saturating_add() {
let input = "9223372036854775807 + 1"; let input = "9223372036854775807 + 1";