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.
#[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>),
Assign(Box<Node<Statement>>, Box<Node<Statement>>),
// Expressions
Add(Box<Node>, Box<Node>),
// Math expressions
Add(Box<Node<Statement>>, Box<Node<Statement>>),
Subtract(Box<Node<Statement>>, Box<Node<Statement>>),
Multiply(Box<Node<Statement>>, 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>),
// Comparison expressions
Comparison(
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
Constant(Value),
@ -65,18 +75,22 @@ pub enum Statement {
impl Statement {
pub fn expected_type(&self, variables: &HashMap<Identifier, Value>) -> Option<Type> {
match self {
Statement::Add(left, _) => left.statement.expected_type(variables),
Statement::Add(left, _) => left.inner.expected_type(variables),
Statement::Assign(_, _) => None,
Statement::BuiltInFunctionCall { function, .. } => function.expected_type(),
Statement::Comparison(_, _, _) => Some(Type::Boolean),
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()
.map(|node| node.inner.expected_type(variables))
.flatten(),
Statement::Multiply(left, _) => left.inner.expected_type(variables),
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, ")")
}
Statement::Comparison(left, operator, right) => write!(f, "{left} {operator} {right}"),
Statement::Constant(value) => write!(f, "{value}"),
Statement::FunctionCall {
function,
type_arguments: type_parameters,
@ -156,6 +172,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() {
@ -166,11 +183,28 @@ 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::PropertyAccess(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(())
}
fn analyze_node(&self, node: &Node) -> Result<(), AnalyzerError> {
match &node.statement {
fn analyze_node(&self, node: &Node<Statement>) -> Result<(), AnalyzerError> {
match &node.inner {
Statement::Add(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);
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)) => {}
@ -100,7 +100,7 @@ impl<'a> Analyzer<'a> {
}
}
Statement::Assign(left, right) => {
if let Statement::Identifier(_) = &left.statement {
if let Statement::Identifier(_) = &left.inner {
// Identifier is in the correct position
} else {
return Err(AnalyzerError::ExpectedIdentifier {
@ -112,9 +112,33 @@ impl<'a> Analyzer<'a> {
self.analyze_node(right)?;
}
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::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 {
@ -139,7 +163,7 @@ impl<'a> Analyzer<'a> {
self.analyze_node(right)?;
if let Some(Type::Integer) | Some(Type::Float) =
left.statement.expected_type(self.variables)
left.inner.expected_type(self.variables)
{
} else {
return Err(AnalyzerError::ExpectedIntegerOrFloat {
@ -149,7 +173,7 @@ impl<'a> Analyzer<'a> {
}
if let Some(Type::Integer) | Some(Type::Float) =
right.statement.expected_type(self.variables)
right.inner.expected_type(self.variables)
{
} else {
return Err(AnalyzerError::ExpectedIntegerOrFloat {
@ -160,7 +184,7 @@ impl<'a> Analyzer<'a> {
}
Statement::PropertyAccess(left, right) => {
if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) =
&left.statement
&left.inner
{
// Left side is valid
} 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 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(),
@ -188,8 +212,8 @@ impl<'a> Analyzer<'a> {
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)) => {}
@ -216,13 +240,34 @@ 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,
},
ExpectedFunction {
actual: Node<Statement>,
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 {

View File

@ -167,6 +167,28 @@ impl Lexer {
(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;
@ -393,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";

View File

@ -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::ComparisonOperator, 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)
}
@ -149,12 +149,113 @@ impl<'src> Parser<'src> {
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::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, _) => {
self.next_token()?;
@ -177,39 +278,6 @@ impl<'src> Parser<'src> {
(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)
}
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()?;
@ -258,7 +326,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(),
@ -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 {
if let (Token::RightParenthesis, _) = self.current {
@ -366,6 +434,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,
@ -427,10 +496,90 @@ impl Display for ParseError {
#[cfg(test)]
mod tests {
use crate::Identifier;
use crate::{abstract_tree::ComparisonOperator, Identifier};
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]
fn subtract_negative_integers() {
let input = "-1 - -2";

View File

@ -27,8 +27,12 @@ pub enum Token<'src> {
Comma,
Dot,
Equal,
Greater,
GreaterEqual,
LeftParenthesis,
LeftSquareBrace,
Less,
LessEqual,
Minus,
Plus,
RightParenthesis,
@ -39,53 +43,61 @@ 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::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::Eof => "EOF",
Token::Identifier(_) => "identifier",
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::Dot => ".",
Token::Eof => "EOF",
Token::Equal => "=",
Token::Plus => "+",
Token::Star => "*",
Token::Float(_) => "float",
Token::Greater => ">",
Token::GreaterEqual => ">=",
Token::Identifier(_) => "identifier",
Token::Integer(_) => "integer",
Token::IsEven => "is_even",
Token::IsOdd => "is_odd",
Token::LeftParenthesis => "(",
Token::RightParenthesis => ")",
Token::LeftSquareBrace => "[",
Token::RightSquareBrace => "]",
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",
}
}
}
@ -103,26 +115,30 @@ impl<'src> PartialEq for Token<'src> {
(Token::Float(left), Token::Float(right)) => left.to_bits() == right.to_bits(),
// 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::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::Dot, Token::Dot) => true,
(Token::Eof, Token::Eof) => true,
(Token::Equal, Token::Equal) => true,
(Token::Plus, Token::Plus) => true,
(Token::Star, Token::Star) => 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::RightParenthesis, Token::RightParenthesis) => 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::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,
}
}
@ -154,8 +170,12 @@ pub enum TokenOwned {
Comma,
Dot,
Equal,
Greater,
GreaterOrEqual,
LeftParenthesis,
LeftSquareBrace,
Less,
LessOrEqual,
Minus,
Plus,
RightParenthesis,
@ -166,27 +186,31 @@ pub enum TokenOwned {
impl Display for TokenOwned {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
TokenOwned::Eof => Token::Eof.fmt(f),
TokenOwned::Identifier(text) => write!(f, "{text}"),
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::Dot => Token::Dot.fmt(f),
TokenOwned::Eof => Token::Eof.fmt(f),
TokenOwned::Equal => Token::Equal.fmt(f),
TokenOwned::Plus => Token::Plus.fmt(f),
TokenOwned::Star => Token::Star.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::RightParenthesis => Token::RightParenthesis.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::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())),
}
}
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 {
@ -636,7 +686,7 @@ impl Function {
.iter()
.last()
.unwrap()
.statement
.inner
.expected_type(variables)
}
}
@ -686,9 +736,13 @@ pub enum ValueError {
CannotAdd(Value, Value),
CannotMultiply(Value, Value),
CannotSubtract(Value, Value),
PropertyNotFound { value: Value, property: Identifier },
IndexOutOfBounds { value: Value, index: i64 },
CannotLessThan(Value, Value),
CannotLessThanOrEqual(Value, Value),
CannotGreaterThan(Value, Value),
CannotGreaterThanOrEqual(Value, Value),
ExpectedList(Value),
IndexOutOfBounds { value: Value, index: i64 },
PropertyNotFound { value: Value, property: Identifier },
}
impl Error for ValueError {}
@ -703,6 +757,12 @@ impl Display for ValueError {
ValueError::CannotSubtract(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 } => {
write!(f, "{} does not have a property named {}", value, property)
}

View File

@ -6,8 +6,8 @@ use std::{
};
use crate::{
parse, AbstractSyntaxTree, Analyzer, AnalyzerError, BuiltInFunctionError, Identifier, Node,
ParseError, Span, Statement, Value, ValueError,
abstract_tree::ComparisonOperator, parse, AbstractSyntaxTree, Analyzer, AnalyzerError,
BuiltInFunctionError, Identifier, Node, ParseError, Span, Statement, Value, ValueError,
};
pub fn run(
@ -48,10 +48,10 @@ impl Vm {
fn run_node(
&self,
node: Node,
node: Node<Statement>,
variables: &mut HashMap<Identifier, Value>,
) -> Result<Option<Value>, VmError> {
match node.statement {
match node.inner {
Statement::Add(left, right) => {
let left_span = left.position;
let left = if let Some(value) = self.run_node(*left, variables)? {
@ -79,7 +79,7 @@ impl Vm {
Ok(Some(sum))
}
Statement::Assign(left, right) => {
let identifier = if let Statement::Identifier(identifier) = &left.statement {
let identifier = if let Statement::Identifier(identifier) = &left.inner {
identifier
} else {
return Err(VmError::ExpectedIdentifier {
@ -132,6 +132,54 @@ impl Vm {
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::FunctionCall {
function: function_node,
@ -240,7 +288,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();
@ -256,7 +304,7 @@ impl Vm {
type_arguments: _,
value_arguments: value_argument_nodes,
},
) = (left_value, right.statement)
) = (left_value, right.inner)
{
let mut value_arguments = Vec::new();
@ -454,6 +502,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";