Add comparison statement to replace four separate statements
This commit is contained in:
parent
580b85e2d0
commit
b9081f8653
@ -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, "<="),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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";
|
||||||
|
@ -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";
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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";
|
||||||
|
Loading…
Reference in New Issue
Block a user