Compare commits
No commits in common. "28c65b07156ce805f24687f7e7f667a50cdbef3b" and "37e3e1116d5141f4810e2814b859b4e4d446e9c2" have entirely different histories.
28c65b0715
...
37e3e1116d
@ -66,28 +66,6 @@ pub enum Statement {
|
|||||||
body: Box<Node<Statement>>,
|
body: Box<Node<Statement>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Control flow
|
|
||||||
If {
|
|
||||||
condition: Box<Node<Statement>>,
|
|
||||||
body: Box<Node<Statement>>,
|
|
||||||
},
|
|
||||||
IfElse {
|
|
||||||
condition: Box<Node<Statement>>,
|
|
||||||
if_body: Box<Node<Statement>>,
|
|
||||||
else_body: Box<Node<Statement>>,
|
|
||||||
},
|
|
||||||
IfElseIf {
|
|
||||||
condition: Box<Node<Statement>>,
|
|
||||||
if_body: Box<Node<Statement>>,
|
|
||||||
else_ifs: Vec<(Node<Statement>, Node<Statement>)>,
|
|
||||||
},
|
|
||||||
IfElseIfElse {
|
|
||||||
condition: Box<Node<Statement>>,
|
|
||||||
if_body: Box<Node<Statement>>,
|
|
||||||
else_ifs: Vec<(Node<Statement>, Node<Statement>)>,
|
|
||||||
else_body: Box<Node<Statement>>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Identifier expression
|
// Identifier expression
|
||||||
Identifier(Identifier),
|
Identifier(Identifier),
|
||||||
|
|
||||||
@ -107,7 +85,11 @@ impl Statement {
|
|||||||
pub fn expected_type(&self, context: &Context) -> Option<Type> {
|
pub fn expected_type(&self, context: &Context) -> Option<Type> {
|
||||||
match self {
|
match self {
|
||||||
Statement::Block(nodes) => nodes.last().unwrap().inner.expected_type(context),
|
Statement::Block(nodes) => nodes.last().unwrap().inner.expected_type(context),
|
||||||
Statement::BinaryOperation { left, operator, .. } => match operator.inner {
|
Statement::BinaryOperation {
|
||||||
|
left,
|
||||||
|
operator,
|
||||||
|
right,
|
||||||
|
} => match operator.inner {
|
||||||
BinaryOperator::Add
|
BinaryOperator::Add
|
||||||
| BinaryOperator::Divide
|
| BinaryOperator::Divide
|
||||||
| BinaryOperator::Modulo
|
| BinaryOperator::Modulo
|
||||||
@ -128,10 +110,6 @@ impl Statement {
|
|||||||
Statement::Constant(value) => Some(value.r#type(context)),
|
Statement::Constant(value) => Some(value.r#type(context)),
|
||||||
Statement::FunctionCall { function, .. } => function.inner.expected_type(context),
|
Statement::FunctionCall { function, .. } => function.inner.expected_type(context),
|
||||||
Statement::Identifier(identifier) => context.get_type(identifier).cloned(),
|
Statement::Identifier(identifier) => context.get_type(identifier).cloned(),
|
||||||
Statement::If { .. } => None,
|
|
||||||
Statement::IfElse { if_body, .. } => if_body.inner.expected_type(context),
|
|
||||||
Statement::IfElseIf { .. } => None,
|
|
||||||
Statement::IfElseIfElse { if_body, .. } => if_body.inner.expected_type(context),
|
|
||||||
Statement::List(nodes) => {
|
Statement::List(nodes) => {
|
||||||
let item_type = nodes.first().unwrap().inner.expected_type(context)?;
|
let item_type = nodes.first().unwrap().inner.expected_type(context)?;
|
||||||
|
|
||||||
@ -253,43 +231,6 @@ impl Display for Statement {
|
|||||||
write!(f, ")")
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
Statement::Identifier(identifier) => write!(f, "{identifier}"),
|
Statement::Identifier(identifier) => write!(f, "{identifier}"),
|
||||||
Statement::If { condition, body } => {
|
|
||||||
write!(f, "if {condition} {body}")
|
|
||||||
}
|
|
||||||
Statement::IfElse {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_body,
|
|
||||||
} => {
|
|
||||||
write!(f, "if {condition} {if_body} else {else_body}")
|
|
||||||
}
|
|
||||||
Statement::IfElseIf {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_ifs,
|
|
||||||
} => {
|
|
||||||
write!(f, "if {condition} {if_body}")?;
|
|
||||||
|
|
||||||
for (condition, body) in else_ifs {
|
|
||||||
write!(f, " else if {condition} {body}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Statement::IfElseIfElse {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_ifs,
|
|
||||||
else_body,
|
|
||||||
} => {
|
|
||||||
write!(f, "if {condition} {if_body}")?;
|
|
||||||
|
|
||||||
for (condition, body) in else_ifs {
|
|
||||||
write!(f, " else if {condition} {body}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, " else {else_body}")
|
|
||||||
}
|
|
||||||
Statement::List(nodes) => {
|
Statement::List(nodes) => {
|
||||||
write!(f, "[")?;
|
write!(f, "[")?;
|
||||||
|
|
||||||
|
@ -168,108 +168,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::If { condition, body } => {
|
|
||||||
self.analyze_node(condition)?;
|
|
||||||
|
|
||||||
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
|
|
||||||
// Condition is valid
|
|
||||||
} else {
|
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
|
||||||
actual: condition.as_ref().clone(),
|
|
||||||
position: condition.position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.analyze_node(body)?;
|
|
||||||
}
|
|
||||||
Statement::IfElse {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_body,
|
|
||||||
} => {
|
|
||||||
self.analyze_node(condition)?;
|
|
||||||
|
|
||||||
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
|
|
||||||
// Condition is valid
|
|
||||||
} else {
|
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
|
||||||
actual: condition.as_ref().clone(),
|
|
||||||
position: condition.position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.analyze_node(if_body)?;
|
|
||||||
self.analyze_node(else_body)?;
|
|
||||||
}
|
|
||||||
Statement::IfElseIf {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_ifs,
|
|
||||||
} => {
|
|
||||||
self.analyze_node(condition)?;
|
|
||||||
|
|
||||||
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
|
|
||||||
// Condition is valid
|
|
||||||
} else {
|
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
|
||||||
actual: condition.as_ref().clone(),
|
|
||||||
position: condition.position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.analyze_node(if_body)?;
|
|
||||||
|
|
||||||
for (condition, body) in else_ifs {
|
|
||||||
self.analyze_node(condition)?;
|
|
||||||
|
|
||||||
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
|
|
||||||
// Condition is valid
|
|
||||||
} else {
|
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
|
||||||
actual: condition.clone(),
|
|
||||||
position: condition.position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.analyze_node(body)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Statement::IfElseIfElse {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_ifs,
|
|
||||||
else_body,
|
|
||||||
} => {
|
|
||||||
self.analyze_node(condition)?;
|
|
||||||
|
|
||||||
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
|
|
||||||
// Condition is valid
|
|
||||||
} else {
|
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
|
||||||
actual: condition.as_ref().clone(),
|
|
||||||
position: condition.position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.analyze_node(if_body)?;
|
|
||||||
|
|
||||||
for (condition, body) in else_ifs {
|
|
||||||
self.analyze_node(condition)?;
|
|
||||||
|
|
||||||
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
|
|
||||||
// Condition is valid
|
|
||||||
} else {
|
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
|
||||||
actual: condition.clone(),
|
|
||||||
position: condition.position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.analyze_node(body)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.analyze_node(else_body)?;
|
|
||||||
}
|
|
||||||
Statement::List(statements) => {
|
Statement::List(statements) => {
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
self.analyze_node(statement)?;
|
self.analyze_node(statement)?;
|
||||||
|
@ -395,16 +395,14 @@ impl Lexer {
|
|||||||
|
|
||||||
let string = &source[start_pos..self.position];
|
let string = &source[start_pos..self.position];
|
||||||
let token = match string {
|
let token = match string {
|
||||||
"Infinity" => Token::Float("Infinity"),
|
"true" => Token::Boolean("true"),
|
||||||
"NaN" => Token::Float("NaN"),
|
|
||||||
"else" => Token::Else,
|
|
||||||
"false" => Token::Boolean("false"),
|
"false" => Token::Boolean("false"),
|
||||||
"if" => Token::If,
|
"Infinity" => Token::Float("Infinity"),
|
||||||
"is_even" => Token::IsEven,
|
"is_even" => Token::IsEven,
|
||||||
"is_odd" => Token::IsOdd,
|
"is_odd" => Token::IsOdd,
|
||||||
"length" => Token::Length,
|
"length" => Token::Length,
|
||||||
|
"NaN" => Token::Float("NaN"),
|
||||||
"read_line" => Token::ReadLine,
|
"read_line" => Token::ReadLine,
|
||||||
"true" => Token::Boolean("true"),
|
|
||||||
"while" => Token::While,
|
"while" => Token::While,
|
||||||
"write_line" => Token::WriteLine,
|
"write_line" => Token::WriteLine,
|
||||||
_ => Token::Identifier(string),
|
_ => Token::Identifier(string),
|
||||||
@ -478,31 +476,6 @@ impl Display for LexError {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else() {
|
|
||||||
let input = "if x < 10 { x + 1 } else { x }";
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
lex(input),
|
|
||||||
Ok(vec![
|
|
||||||
(Token::If, (0, 2)),
|
|
||||||
(Token::Identifier("x"), (3, 4)),
|
|
||||||
(Token::Less, (5, 6)),
|
|
||||||
(Token::Integer("10"), (7, 9)),
|
|
||||||
(Token::LeftCurlyBrace, (10, 11)),
|
|
||||||
(Token::Identifier("x"), (12, 13)),
|
|
||||||
(Token::Plus, (14, 15)),
|
|
||||||
(Token::Integer("1"), (16, 17)),
|
|
||||||
(Token::RightCurlyBrace, (18, 19)),
|
|
||||||
(Token::Else, (20, 24)),
|
|
||||||
(Token::LeftCurlyBrace, (25, 26)),
|
|
||||||
(Token::Identifier("x"), (27, 28)),
|
|
||||||
(Token::RightCurlyBrace, (29, 30)),
|
|
||||||
(Token::Eof, (30, 30)),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn while_loop() {
|
fn while_loop() {
|
||||||
let input = "while x < 10 { x += 1 }";
|
let input = "while x < 10 { x += 1 }";
|
||||||
|
@ -202,96 +202,6 @@ impl<'src> Parser<'src> {
|
|||||||
position,
|
position,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
(Token::If, position) => {
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
let condition = Box::new(self.parse_statement(0)?);
|
|
||||||
let if_body = Box::new(self.parse_statement(0)?);
|
|
||||||
|
|
||||||
if let Statement::Block(_) = if_body.inner {
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::ExpectedBlock { actual: *if_body });
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Token::Else = self.current.0 {
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
if let Token::If = self.current.0 {
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
let first_else_if = (self.parse_statement(0)?, self.parse_statement(0)?);
|
|
||||||
let mut else_ifs = vec![first_else_if];
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if let Token::Else = self.current.0 {
|
|
||||||
self.next_token()?;
|
|
||||||
} else {
|
|
||||||
return Ok(Node::new(
|
|
||||||
Statement::IfElseIf {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_ifs,
|
|
||||||
},
|
|
||||||
position,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Token::If = self.current.0 {
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
let else_if = (self.parse_statement(0)?, self.parse_statement(0)?);
|
|
||||||
|
|
||||||
else_ifs.push(else_if);
|
|
||||||
} else {
|
|
||||||
let else_body = Box::new(self.parse_statement(0)?);
|
|
||||||
let else_end = else_body.position.1;
|
|
||||||
|
|
||||||
if let Statement::Block(_) = else_body.inner {
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::ExpectedBlock { actual: *else_body });
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(Node::new(
|
|
||||||
Statement::IfElseIfElse {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_ifs,
|
|
||||||
else_body,
|
|
||||||
},
|
|
||||||
(position.0, else_end),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let else_body = Box::new(self.parse_statement(0)?);
|
|
||||||
let else_end = else_body.position.1;
|
|
||||||
|
|
||||||
if let Statement::Block(_) = else_body.inner {
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::ExpectedBlock { actual: *else_body });
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Node::new(
|
|
||||||
Statement::IfElse {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_body,
|
|
||||||
},
|
|
||||||
(position.0, else_end),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let if_end = if_body.position.1;
|
|
||||||
|
|
||||||
Ok(Node::new(
|
|
||||||
Statement::If {
|
|
||||||
condition,
|
|
||||||
body: if_body,
|
|
||||||
},
|
|
||||||
(position.0, if_end),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Token::String(string), position) => {
|
(Token::String(string), position) => {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
@ -337,57 +247,23 @@ impl<'src> Parser<'src> {
|
|||||||
statement.get_or_insert_with(|| Statement::Block(Vec::new()))
|
statement.get_or_insert_with(|| Statement::Block(Vec::new()))
|
||||||
{
|
{
|
||||||
block.push(next_node);
|
block.push(next_node);
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
// If the next node is an assignment, this might be a map
|
||||||
|
} else if let Statement::BinaryOperation {
|
||||||
// If the new statement is already a map, expect the next node to be an
|
left,
|
||||||
// assignment
|
operator:
|
||||||
if statement
|
Node {
|
||||||
|
inner: BinaryOperator::Assign,
|
||||||
|
position: operator_position,
|
||||||
|
},
|
||||||
|
right,
|
||||||
|
} = next_node.inner
|
||||||
|
{
|
||||||
|
// If the current token is a comma, or the new statement is already a map
|
||||||
|
if self.current.0 == Token::Comma
|
||||||
|
|| statement
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|statement| matches!(statement, Statement::Map(_)))
|
.is_some_and(|statement| matches!(statement, Statement::Map(_)))
|
||||||
{
|
|
||||||
if let Statement::BinaryOperation {
|
|
||||||
left,
|
|
||||||
operator:
|
|
||||||
Node {
|
|
||||||
inner: BinaryOperator::Assign,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
right,
|
|
||||||
} = next_node.inner
|
|
||||||
{
|
|
||||||
if let Statement::Map(map_properties) =
|
|
||||||
statement.get_or_insert_with(|| Statement::Map(Vec::new()))
|
|
||||||
{
|
|
||||||
map_properties.push((*left, *right));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Token::Comma = self.current.0 {
|
|
||||||
self.next_token()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::ExpectedAssignment { actual: next_node });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the next node is an assignment, this might be a map
|
|
||||||
if let Statement::BinaryOperation {
|
|
||||||
left,
|
|
||||||
operator:
|
|
||||||
Node {
|
|
||||||
inner: BinaryOperator::Assign,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
right,
|
|
||||||
} = next_node.inner
|
|
||||||
{
|
|
||||||
// If the current token is a comma or closing brace
|
|
||||||
if self.current.0 == Token::Comma
|
|
||||||
|| self.current.0 == Token::RightCurlyBrace
|
|
||||||
{
|
{
|
||||||
// The new statement is a map
|
// The new statement is a map
|
||||||
if let Statement::Map(map_properties) =
|
if let Statement::Map(map_properties) =
|
||||||
@ -406,11 +282,14 @@ impl<'src> Parser<'src> {
|
|||||||
if let Statement::Block(statements) =
|
if let Statement::Block(statements) =
|
||||||
statement.get_or_insert_with(|| Statement::Block(Vec::new()))
|
statement.get_or_insert_with(|| Statement::Block(Vec::new()))
|
||||||
{
|
{
|
||||||
// Add the assignment statement to the block
|
// Add the statement to the block
|
||||||
statements.push(Node::new(
|
statements.push(Node::new(
|
||||||
Statement::BinaryOperation {
|
Statement::BinaryOperation {
|
||||||
left,
|
left,
|
||||||
operator: Node::new(BinaryOperator::Assign, left_position),
|
operator: Node::new(
|
||||||
|
BinaryOperator::Assign,
|
||||||
|
operator_position,
|
||||||
|
),
|
||||||
right,
|
right,
|
||||||
},
|
},
|
||||||
next_node.position,
|
next_node.position,
|
||||||
@ -686,12 +565,6 @@ pub enum ParseError {
|
|||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
LexError(LexError),
|
LexError(LexError),
|
||||||
ExpectedAssignment {
|
|
||||||
actual: Node<Statement>,
|
|
||||||
},
|
|
||||||
ExpectedBlock {
|
|
||||||
actual: Node<Statement>,
|
|
||||||
},
|
|
||||||
ExpectedIdentifier {
|
ExpectedIdentifier {
|
||||||
actual: TokenOwned,
|
actual: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -725,8 +598,6 @@ impl ParseError {
|
|||||||
pub fn position(&self) -> Span {
|
pub fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
ParseError::BooleanError { position, .. } => *position,
|
ParseError::BooleanError { position, .. } => *position,
|
||||||
ParseError::ExpectedAssignment { actual } => actual.position,
|
|
||||||
ParseError::ExpectedBlock { actual } => actual.position,
|
|
||||||
ParseError::ExpectedIdentifier { position, .. } => *position,
|
ParseError::ExpectedIdentifier { position, .. } => *position,
|
||||||
ParseError::ExpectedToken { position, .. } => *position,
|
ParseError::ExpectedToken { position, .. } => *position,
|
||||||
ParseError::FloatError { position, .. } => *position,
|
ParseError::FloatError { position, .. } => *position,
|
||||||
@ -750,8 +621,6 @@ impl Display for ParseError {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::BooleanError { error, .. } => write!(f, "{}", error),
|
Self::BooleanError { error, .. } => write!(f, "{}", error),
|
||||||
Self::ExpectedAssignment { .. } => write!(f, "Expected assignment"),
|
|
||||||
Self::ExpectedBlock { .. } => write!(f, "Expected block"),
|
|
||||||
Self::ExpectedIdentifier { actual, .. } => {
|
Self::ExpectedIdentifier { actual, .. } => {
|
||||||
write!(f, "Expected identifier, found {actual}")
|
write!(f, "Expected identifier, found {actual}")
|
||||||
}
|
}
|
||||||
@ -772,142 +641,6 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn r#if() {
|
|
||||||
let input = "if x { y }";
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
parse(input),
|
|
||||||
Ok(AbstractSyntaxTree {
|
|
||||||
nodes: [Node::new(
|
|
||||||
Statement::If {
|
|
||||||
condition: Box::new(Node::new(
|
|
||||||
Statement::Identifier(Identifier::new("x")),
|
|
||||||
(3, 4)
|
|
||||||
)),
|
|
||||||
body: Box::new(Node::new(
|
|
||||||
Statement::Block(vec![Node::new(
|
|
||||||
Statement::Identifier(Identifier::new("y")),
|
|
||||||
(7, 8)
|
|
||||||
)]),
|
|
||||||
(5, 10)
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
(0, 10)
|
|
||||||
)]
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else() {
|
|
||||||
let input = "if x { y } else { z }";
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
parse(input),
|
|
||||||
Ok(AbstractSyntaxTree {
|
|
||||||
nodes: [Node::new(
|
|
||||||
Statement::IfElse {
|
|
||||||
condition: Box::new(Node::new(
|
|
||||||
Statement::Identifier(Identifier::new("x")),
|
|
||||||
(3, 4)
|
|
||||||
)),
|
|
||||||
if_body: Box::new(Node::new(
|
|
||||||
Statement::Block(vec![Node::new(
|
|
||||||
Statement::Identifier(Identifier::new("y")),
|
|
||||||
(7, 8)
|
|
||||||
)]),
|
|
||||||
(5, 10)
|
|
||||||
)),
|
|
||||||
else_body: Box::new(Node::new(
|
|
||||||
Statement::Block(vec![Node::new(
|
|
||||||
Statement::Identifier(Identifier::new("z")),
|
|
||||||
(18, 19)
|
|
||||||
)]),
|
|
||||||
(16, 21)
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
(0, 21)
|
|
||||||
)]
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else_if_else() {
|
|
||||||
let input = "if x { y } else if z { a } else { b }";
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
parse(input),
|
|
||||||
Ok(AbstractSyntaxTree {
|
|
||||||
nodes: [Node::new(
|
|
||||||
Statement::IfElseIfElse {
|
|
||||||
condition: Box::new(Node::new(
|
|
||||||
Statement::Identifier(Identifier::new("x")),
|
|
||||||
(3, 4)
|
|
||||||
)),
|
|
||||||
if_body: Box::new(Node::new(
|
|
||||||
Statement::Block(vec![Node::new(
|
|
||||||
Statement::Identifier(Identifier::new("y")),
|
|
||||||
(7, 8)
|
|
||||||
)]),
|
|
||||||
(5, 10)
|
|
||||||
)),
|
|
||||||
else_ifs: vec![(
|
|
||||||
Node::new(Statement::Identifier(Identifier::new("z")), (19, 20)),
|
|
||||||
Node::new(
|
|
||||||
Statement::Block(vec![Node::new(
|
|
||||||
Statement::Identifier(Identifier::new("a")),
|
|
||||||
(23, 24)
|
|
||||||
)]),
|
|
||||||
(21, 26)
|
|
||||||
),
|
|
||||||
)],
|
|
||||||
else_body: Box::new(Node::new(
|
|
||||||
Statement::Block(vec![Node::new(
|
|
||||||
Statement::Identifier(Identifier::new("b")),
|
|
||||||
(34, 35)
|
|
||||||
)]),
|
|
||||||
(32, 37)
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
(0, 37)
|
|
||||||
)]
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn malformed_map() {
|
|
||||||
let input = "{ x = 1, y = 2, z = 3; }";
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
parse(input),
|
|
||||||
Err(ParseError::ExpectedAssignment {
|
|
||||||
actual: Node::new(
|
|
||||||
Statement::Nil(Box::new(Node::new(
|
|
||||||
Statement::BinaryOperation {
|
|
||||||
left: Box::new(Node::new(
|
|
||||||
Statement::Identifier(Identifier::new("z")),
|
|
||||||
(16, 17)
|
|
||||||
)),
|
|
||||||
operator: Node::new(BinaryOperator::Assign, (18, 19)),
|
|
||||||
right: Box::new(Node::new(
|
|
||||||
Statement::Constant(Value::integer(3)),
|
|
||||||
(20, 21)
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
(16, 21)
|
|
||||||
))),
|
|
||||||
(16, 24)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn while_loop() {
|
fn while_loop() {
|
||||||
let input = "while x < 10 { x += 1 }";
|
let input = "while x < 10 { x += 1 }";
|
||||||
|
@ -17,8 +17,6 @@ pub enum Token<'src> {
|
|||||||
String(&'src str),
|
String(&'src str),
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
Else,
|
|
||||||
If,
|
|
||||||
IsEven,
|
IsEven,
|
||||||
IsOdd,
|
IsOdd,
|
||||||
Length,
|
Length,
|
||||||
@ -61,14 +59,12 @@ impl<'src> Token<'src> {
|
|||||||
Token::DoubleAmpersand => TokenOwned::DoubleAmpersand,
|
Token::DoubleAmpersand => TokenOwned::DoubleAmpersand,
|
||||||
Token::DoubleEqual => TokenOwned::DoubleEqual,
|
Token::DoubleEqual => TokenOwned::DoubleEqual,
|
||||||
Token::DoublePipe => TokenOwned::DoublePipe,
|
Token::DoublePipe => TokenOwned::DoublePipe,
|
||||||
Token::Else => TokenOwned::Else,
|
|
||||||
Token::Eof => TokenOwned::Eof,
|
Token::Eof => TokenOwned::Eof,
|
||||||
Token::Equal => TokenOwned::Equal,
|
Token::Equal => TokenOwned::Equal,
|
||||||
Token::Float(float) => TokenOwned::Float(float.to_string()),
|
Token::Float(float) => TokenOwned::Float(float.to_string()),
|
||||||
Token::Greater => TokenOwned::Greater,
|
Token::Greater => TokenOwned::Greater,
|
||||||
Token::GreaterEqual => TokenOwned::GreaterOrEqual,
|
Token::GreaterEqual => TokenOwned::GreaterOrEqual,
|
||||||
Token::Identifier(text) => TokenOwned::Identifier(text.to_string()),
|
Token::Identifier(text) => TokenOwned::Identifier(text.to_string()),
|
||||||
Token::If => TokenOwned::If,
|
|
||||||
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
|
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
|
||||||
Token::IsEven => TokenOwned::IsEven,
|
Token::IsEven => TokenOwned::IsEven,
|
||||||
Token::IsOdd => TokenOwned::IsOdd,
|
Token::IsOdd => TokenOwned::IsOdd,
|
||||||
@ -100,20 +96,16 @@ impl<'src> Token<'src> {
|
|||||||
Token::Boolean(boolean_text) => boolean_text,
|
Token::Boolean(boolean_text) => boolean_text,
|
||||||
Token::Identifier(text) => text,
|
Token::Identifier(text) => text,
|
||||||
Token::Integer(integer_text) => integer_text,
|
Token::Integer(integer_text) => integer_text,
|
||||||
Token::String(text) => text,
|
|
||||||
|
|
||||||
Token::Comma => ",",
|
Token::Comma => ",",
|
||||||
Token::Dot => ".",
|
Token::Dot => ".",
|
||||||
Token::DoubleAmpersand => "&&",
|
Token::DoubleAmpersand => "&&",
|
||||||
Token::DoubleEqual => "==",
|
Token::DoubleEqual => "==",
|
||||||
Token::DoublePipe => "||",
|
Token::DoublePipe => "||",
|
||||||
Token::Else => "else",
|
|
||||||
Token::Eof => "EOF",
|
Token::Eof => "EOF",
|
||||||
Token::Equal => "=",
|
Token::Equal => "=",
|
||||||
Token::Float(_) => "float",
|
Token::Float(_) => "float",
|
||||||
Token::Greater => ">",
|
Token::Greater => ">",
|
||||||
Token::GreaterEqual => ">=",
|
Token::GreaterEqual => ">=",
|
||||||
Token::If => "if",
|
|
||||||
Token::IsEven => "is_even",
|
Token::IsEven => "is_even",
|
||||||
Token::IsOdd => "is_odd",
|
Token::IsOdd => "is_odd",
|
||||||
Token::LeftCurlyBrace => "{",
|
Token::LeftCurlyBrace => "{",
|
||||||
@ -132,6 +124,7 @@ impl<'src> Token<'src> {
|
|||||||
Token::RightSquareBrace => "]",
|
Token::RightSquareBrace => "]",
|
||||||
Token::Semicolon => ";",
|
Token::Semicolon => ";",
|
||||||
Token::Star => "*",
|
Token::Star => "*",
|
||||||
|
Token::String(_) => "string",
|
||||||
Token::Slash => "/",
|
Token::Slash => "/",
|
||||||
Token::While => "while",
|
Token::While => "while",
|
||||||
Token::WriteLine => "write_line",
|
Token::WriteLine => "write_line",
|
||||||
@ -188,14 +181,12 @@ impl<'src> PartialEq for Token<'src> {
|
|||||||
(Token::DoubleAmpersand, Token::DoubleAmpersand) => true,
|
(Token::DoubleAmpersand, Token::DoubleAmpersand) => true,
|
||||||
(Token::DoubleEqual, Token::DoubleEqual) => true,
|
(Token::DoubleEqual, Token::DoubleEqual) => true,
|
||||||
(Token::DoublePipe, Token::DoublePipe) => true,
|
(Token::DoublePipe, Token::DoublePipe) => true,
|
||||||
(Token::Else, Token::Else) => true,
|
|
||||||
(Token::Eof, Token::Eof) => true,
|
(Token::Eof, Token::Eof) => true,
|
||||||
(Token::Equal, Token::Equal) => true,
|
(Token::Equal, Token::Equal) => true,
|
||||||
(Token::Float(left), Token::Float(right)) => left == right,
|
(Token::Float(left), Token::Float(right)) => left == right,
|
||||||
(Token::Greater, Token::Greater) => true,
|
(Token::Greater, Token::Greater) => true,
|
||||||
(Token::GreaterEqual, Token::GreaterEqual) => true,
|
(Token::GreaterEqual, Token::GreaterEqual) => true,
|
||||||
(Token::Identifier(left), Token::Identifier(right)) => left == right,
|
(Token::Identifier(left), Token::Identifier(right)) => left == right,
|
||||||
(Token::If, Token::If) => true,
|
|
||||||
(Token::Integer(left), Token::Integer(right)) => left == right,
|
(Token::Integer(left), Token::Integer(right)) => left == right,
|
||||||
(Token::IsEven, Token::IsEven) => true,
|
(Token::IsEven, Token::IsEven) => true,
|
||||||
(Token::IsOdd, Token::IsOdd) => true,
|
(Token::IsOdd, Token::IsOdd) => true,
|
||||||
@ -240,8 +231,6 @@ pub enum TokenOwned {
|
|||||||
String(String),
|
String(String),
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
Else,
|
|
||||||
If,
|
|
||||||
IsEven,
|
IsEven,
|
||||||
IsOdd,
|
IsOdd,
|
||||||
Length,
|
Length,
|
||||||
@ -284,14 +273,12 @@ impl Display for TokenOwned {
|
|||||||
TokenOwned::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
TokenOwned::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
||||||
TokenOwned::DoubleEqual => Token::DoubleEqual.fmt(f),
|
TokenOwned::DoubleEqual => Token::DoubleEqual.fmt(f),
|
||||||
TokenOwned::DoublePipe => Token::DoublePipe.fmt(f),
|
TokenOwned::DoublePipe => Token::DoublePipe.fmt(f),
|
||||||
TokenOwned::Else => write!(f, "else"),
|
|
||||||
TokenOwned::Eof => Token::Eof.fmt(f),
|
TokenOwned::Eof => Token::Eof.fmt(f),
|
||||||
TokenOwned::Equal => Token::Equal.fmt(f),
|
TokenOwned::Equal => Token::Equal.fmt(f),
|
||||||
TokenOwned::Float(float) => write!(f, "{float}"),
|
TokenOwned::Float(float) => write!(f, "{float}"),
|
||||||
TokenOwned::Greater => Token::Greater.fmt(f),
|
TokenOwned::Greater => Token::Greater.fmt(f),
|
||||||
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
||||||
TokenOwned::Identifier(text) => write!(f, "{text}"),
|
TokenOwned::Identifier(text) => write!(f, "{text}"),
|
||||||
TokenOwned::If => write!(f, "if"),
|
|
||||||
TokenOwned::Integer(integer) => write!(f, "{integer}"),
|
TokenOwned::Integer(integer) => write!(f, "{integer}"),
|
||||||
TokenOwned::IsEven => Token::IsEven.fmt(f),
|
TokenOwned::IsEven => Token::IsEven.fmt(f),
|
||||||
TokenOwned::IsOdd => Token::IsOdd.fmt(f),
|
TokenOwned::IsOdd => Token::IsOdd.fmt(f),
|
||||||
|
@ -245,154 +245,6 @@ impl Vm {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::If { condition, body } => {
|
|
||||||
let condition_position = condition.position;
|
|
||||||
let condition_value = if let Some(value) = self.run_node(*condition, context)? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedValue {
|
|
||||||
position: condition_position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
let condition = if let Some(condition) = condition_value.as_boolean() {
|
|
||||||
condition
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedBoolean {
|
|
||||||
position: condition_position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if condition {
|
|
||||||
self.run_node(*body, context)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
Statement::IfElse {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_body,
|
|
||||||
} => {
|
|
||||||
let condition_position = condition.position;
|
|
||||||
let condition_value = if let Some(value) = self.run_node(*condition, context)? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedValue {
|
|
||||||
position: condition_position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(condition) = condition_value.as_boolean() {
|
|
||||||
if condition {
|
|
||||||
self.run_node(*if_body, context)
|
|
||||||
} else {
|
|
||||||
self.run_node(*else_body, context)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(VmError::ExpectedBoolean {
|
|
||||||
position: condition_position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Statement::IfElseIf {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_ifs,
|
|
||||||
} => {
|
|
||||||
let condition_position = condition.position;
|
|
||||||
let condition_value = if let Some(value) = self.run_node(*condition, context)? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedValue {
|
|
||||||
position: condition_position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(condition) = condition_value.as_boolean() {
|
|
||||||
if condition {
|
|
||||||
self.run_node(*if_body, context)
|
|
||||||
} else {
|
|
||||||
for (condition, body) in else_ifs {
|
|
||||||
let condition_position = condition.position;
|
|
||||||
let condition_value =
|
|
||||||
if let Some(value) = self.run_node(condition, context)? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedValue {
|
|
||||||
position: condition_position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
let condition = if let Some(condition) = condition_value.as_boolean() {
|
|
||||||
condition
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedBoolean {
|
|
||||||
position: condition_position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if condition {
|
|
||||||
self.run_node(body, context)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(VmError::ExpectedBoolean {
|
|
||||||
position: condition_position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Statement::IfElseIfElse {
|
|
||||||
condition,
|
|
||||||
if_body,
|
|
||||||
else_ifs,
|
|
||||||
else_body,
|
|
||||||
} => {
|
|
||||||
let condition_position = condition.position;
|
|
||||||
let condition_value = if let Some(value) = self.run_node(*condition, context)? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedValue {
|
|
||||||
position: condition_position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(condition) = condition_value.as_boolean() {
|
|
||||||
if condition {
|
|
||||||
self.run_node(*if_body, context)
|
|
||||||
} else {
|
|
||||||
for (condition, body) in else_ifs {
|
|
||||||
let condition_position = condition.position;
|
|
||||||
let condition_value =
|
|
||||||
if let Some(value) = self.run_node(condition, context)? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedValue {
|
|
||||||
position: condition_position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
let condition = if let Some(condition) = condition_value.as_boolean() {
|
|
||||||
condition
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedBoolean {
|
|
||||||
position: condition_position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if condition {
|
|
||||||
return self.run_node(body, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.run_node(*else_body, context)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(VmError::ExpectedBoolean {
|
|
||||||
position: condition_position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Statement::List(nodes) => {
|
Statement::List(nodes) => {
|
||||||
let values = nodes
|
let values = nodes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -660,34 +512,6 @@ impl Display for VmError {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn r#if() {
|
|
||||||
let input = "if true { 1 }";
|
|
||||||
|
|
||||||
assert_eq!(run(input, &mut Context::new()), Ok(None));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else() {
|
|
||||||
let input = "if false { 1 } else { 2 }";
|
|
||||||
|
|
||||||
assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(2))));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else_if() {
|
|
||||||
let input = "if false { 1 } else if true { 2 }";
|
|
||||||
|
|
||||||
assert_eq!(run(input, &mut Context::new()), Ok(None));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else_if_else() {
|
|
||||||
let input = "if false { 1 } else if false { 2 } else { 3 }";
|
|
||||||
|
|
||||||
assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(3))));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn while_loop() {
|
fn while_loop() {
|
||||||
let input = "x = 0; while x < 5 { x += 1; } x";
|
let input = "x = 0; while x < 5 { x += 1; } x";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user