1
0
Fork 0

Add locations to AST Expressions

This commit is contained in:
Vili Sinervä 2025-01-31 19:49:46 +02:00
parent a2f96cc8df
commit 56281008e4
No known key found for this signature in database
GPG key ID: DF8FEAF54EFAC996
3 changed files with 320 additions and 144 deletions

View file

@ -1,21 +1,85 @@
use crate::compiler::token::CodeLocation;
use std::fmt;
#[derive(Debug, PartialEq)]
pub enum Expression<'source> {
EmptyLiteral(),
IntLiteral(u32),
BoolLiteral(bool),
Identifier(&'source str),
UnaryOp(&'source str, Box<Expression<'source>>),
VarDeclaration(&'source str, Box<Expression<'source>>),
EmptyLiteral(CodeLocation),
IntLiteral(CodeLocation, u32),
BoolLiteral(CodeLocation, bool),
Identifier(CodeLocation, &'source str),
UnaryOp(CodeLocation, &'source str, Box<Expression<'source>>),
VarDeclaration(CodeLocation, &'source str, Box<Expression<'source>>),
BinaryOp(
CodeLocation,
Box<Expression<'source>>,
&'source str,
Box<Expression<'source>>,
),
Conditional(
CodeLocation,
Box<Expression<'source>>,
Box<Expression<'source>>,
Option<Box<Expression<'source>>>,
),
FunCall(&'source str, Vec<Expression<'source>>),
Block(Vec<Expression<'source>>),
FunCall(CodeLocation, &'source str, Vec<Expression<'source>>),
Block(CodeLocation, Vec<Expression<'source>>),
}
impl<'source> Expression<'source> {
pub fn loc(&self) -> CodeLocation {
match self {
Expression::EmptyLiteral(loc) => *loc,
Expression::IntLiteral(loc, _) => *loc,
Expression::BoolLiteral(loc, _) => *loc,
Expression::Identifier(loc, _) => *loc,
Expression::UnaryOp(loc, _, _) => *loc,
Expression::VarDeclaration(loc, _, _) => *loc,
Expression::BinaryOp(loc, _, _, _) => *loc,
Expression::Conditional(loc, _, _, _) => *loc,
Expression::FunCall(loc, _, _) => *loc,
Expression::Block(loc, _) => *loc,
}
}
fn expr_type_str(&self) -> &str {
match self {
Expression::EmptyLiteral(..) => "Empty literal",
Expression::IntLiteral(..) => "Integer literal",
Expression::BoolLiteral(..) => "Boolen literal",
Expression::Identifier(..) => "Identifier",
Expression::UnaryOp(..) => "Unary operation",
Expression::VarDeclaration(..) => "Variable declaration",
Expression::BinaryOp(..) => "Binary operation",
Expression::Conditional(..) => "Conditional",
Expression::FunCall(..) => "Function call",
Expression::Block(..) => "Block",
}
}
fn val_string(&self) -> String {
match self {
Expression::EmptyLiteral(..) => "".to_string(),
Expression::IntLiteral(_, val) => val.to_string(),
Expression::BoolLiteral(_, val) => val.to_string(),
Expression::Identifier(_, name) => name.to_string(),
Expression::UnaryOp(_, op, _) => op.to_string(),
Expression::VarDeclaration(_, name, _) => name.to_string(),
Expression::BinaryOp(_, _, op, _) => op.to_string(),
Expression::Conditional(_, condition, _, _) => format!("if {}", condition),
Expression::FunCall(_, name, args) => format!("{} with {} args", name, args.len()),
Expression::Block(_, expressions) => format!("with {} expressions", expressions.len()),
}
}
}
impl<'source> fmt::Display for Expression<'source> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {} at {}",
self.expr_type_str(),
self.val_string(),
self.loc()
)
}
}

View file

@ -59,7 +59,12 @@ fn parse_expression<'source>(
if OPS[level].contains(&peek(pos, tokens).text) {
let operator_token = consume_strings(pos, tokens, OPS[level]);
let right = parse_expression(level, pos, tokens);
BinaryOp(Box::new(left), operator_token.text, Box::new(right))
BinaryOp(
operator_token.loc,
Box::new(left),
operator_token.text,
Box::new(right),
)
} else {
left
}
@ -70,7 +75,12 @@ fn parse_expression<'source>(
let operator_token = consume_strings(pos, tokens, OPS[level]);
let right = parse_expression(level + 1, pos, tokens);
left = BinaryOp(Box::new(left), operator_token.text, Box::new(right));
left = BinaryOp(
operator_token.loc,
Box::new(left),
operator_token.text,
Box::new(right),
);
}
left
}
@ -78,7 +88,7 @@ fn parse_expression<'source>(
if OPS[level].contains(&peek(pos, tokens).text) {
let operator_token = consume_strings(pos, tokens, OPS[level]);
let right = parse_expression(level, pos, tokens);
UnaryOp(operator_token.text, Box::new(right))
UnaryOp(operator_token.loc, operator_token.text, Box::new(right))
} else {
parse_expression(level + 1, pos, tokens)
}
@ -119,14 +129,14 @@ fn parse_var_declaration<'source>(
tokens: &[Token<'source>],
) -> Expression<'source> {
consume_string(pos, tokens, "var");
let name = consume_type(pos, tokens, TokenType::Identifier).text;
let name_token = consume_type(pos, tokens, TokenType::Identifier);
consume_string(pos, tokens, "=");
let value = parse_expression(0, pos, tokens);
VarDeclaration(name, Box::new(value))
VarDeclaration(name_token.loc, name_token.text, Box::new(value))
}
fn parse_conditional<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expression<'source> {
consume_string(pos, tokens, "if");
let start = consume_string(pos, tokens, "if");
let condition = Box::new(parse_expression(0, pos, tokens));
consume_string(pos, tokens, "then");
let then_expr = Box::new(parse_expression(0, pos, tokens));
@ -139,7 +149,7 @@ fn parse_conditional<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Exp
_ => None,
};
Conditional(condition, then_expr, else_expr)
Conditional(start.loc, condition, then_expr, else_expr)
}
fn parse_parenthesized<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expression<'source> {
@ -150,7 +160,7 @@ fn parse_parenthesized<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> E
}
fn parse_block<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expression<'source> {
consume_string(pos, tokens, "{");
let start = consume_string(pos, tokens, "{");
let mut expressions = Vec::new();
loop {
@ -171,14 +181,15 @@ fn parse_block<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expressio
}
// If the last expression of the block ended in a semicolon, empty return
if peek(pos, tokens).text == "}" {
expressions.push(EmptyLiteral());
let next_token = peek(pos, tokens);
if next_token.text == "}" {
expressions.push(EmptyLiteral(next_token.loc));
break;
}
}
consume_string(pos, tokens, "}");
Block(expressions)
Block(start.loc, expressions)
}
fn parse_function<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expression<'source> {
@ -198,13 +209,14 @@ fn parse_function<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expres
}
}
consume_string(pos, tokens, ")");
FunCall(identifier.text, arguments)
FunCall(identifier.loc, identifier.text, arguments)
}
fn parse_int_literal<'source>(pos: &mut usize, tokens: &[Token]) -> Expression<'source> {
let token = consume_type(pos, tokens, TokenType::Integer);
IntLiteral(
token.loc,
token
.text
.parse::<u32>()
@ -216,13 +228,13 @@ fn parse_bool_literal<'source>(pos: &mut usize, tokens: &[Token]) -> Expression<
let token = consume_type(pos, tokens, TokenType::Identifier);
match token.text {
"true" => BoolLiteral(true),
"false" => BoolLiteral(false),
"true" => BoolLiteral(token.loc, true),
"false" => BoolLiteral(token.loc, false),
_ => panic!("Fatal parser error! Expected bool literal but found {token}"),
}
}
fn parse_identifier<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expression<'source> {
let token = consume_type(pos, tokens, TokenType::Identifier);
Identifier(token.text)
Identifier(token.loc, token.text)
}

View file

@ -1,33 +1,111 @@
use super::*;
use crate::compiler::tokenizer::tokenize;
use crate::compiler::{token::CodeLocation, tokenizer::tokenize};
macro_rules! bool_ast {
($x:expr) => {
BoolLiteral(CodeLocation::new(usize::MAX, usize::MAX), $x)
};
}
macro_rules! bool_ast_b {
($x:expr) => {
Box::new(bool_ast!($x))
};
}
macro_rules! int_ast {
($x:expr) => {
Box::new(IntLiteral($x))
IntLiteral(CodeLocation::new(usize::MAX, usize::MAX), $x)
};
}
macro_rules! int_ast_b {
($x:expr) => {
Box::new(int_ast!($x))
};
}
macro_rules! id_ast {
($x:expr) => {
Box::new(Identifier($x))
Identifier(CodeLocation::new(usize::MAX, usize::MAX), $x)
};
}
macro_rules! id_ast_b {
($x:expr) => {
Box::new(id_ast!($x))
};
}
macro_rules! un_ast {
($x:expr, $y:expr) => {
Box::new(UnaryOp($x, $y))
UnaryOp(CodeLocation::new(usize::MAX, usize::MAX), $x, $y)
};
}
macro_rules! un_ast_b {
($x:expr, $y:expr) => {
Box::new(un_ast!($x, $y))
};
}
macro_rules! bin_ast {
($x:expr, $y:expr, $z:expr) => {
Box::new(BinaryOp($x, $y, $z))
BinaryOp(CodeLocation::new(usize::MAX, usize::MAX), $x, $y, $z)
};
}
macro_rules! bool_ast {
macro_rules! bin_ast_b {
($x:expr, $y:expr, $z:expr) => {
Box::new(bin_ast!($x, $y, $z))
};
}
macro_rules! con_ast {
($x:expr, $y:expr, $z:expr) => {
Conditional(CodeLocation::new(usize::MAX, usize::MAX), $x, $y, $z)
};
}
macro_rules! con_ast_b {
($x:expr, $y:expr, $z:expr) => {
Box::new(con_ast!($x, $y, $z))
};
}
macro_rules! fun_ast {
($x:expr, $y:expr) => {
FunCall(CodeLocation::new(usize::MAX, usize::MAX), $x, $y)
};
}
macro_rules! fun_ast_b {
($x:expr, $y:expr) => {
Box::new(fun_ast!($x, $y))
};
}
macro_rules! block_ast {
($x:expr) => {
Box::new(BoolLiteral($x))
Block(CodeLocation::new(usize::MAX, usize::MAX), $x)
};
}
macro_rules! block_ast_b {
($x:expr) => {
Box::new(block_ast!($x))
};
}
macro_rules! empty_ast {
() => {
EmptyLiteral(CodeLocation::new(usize::MAX, usize::MAX))
};
}
macro_rules! var_ast {
($x:expr, $y:expr) => {
VarDeclaration(CodeLocation::new(usize::MAX, usize::MAX), $x, $y)
};
}
@ -58,16 +136,16 @@ fn test_invalid_end() {
#[test]
fn test_binary_op_basic() {
let result = parse(&tokenize("1 + 23"));
assert_eq!(result, BinaryOp(int_ast!(1), "+", int_ast!(23)));
assert_eq!(result, bin_ast!(int_ast_b!(1), "+", int_ast_b!(23)));
let result = parse(&tokenize("4 - 56"));
assert_eq!(result, BinaryOp(int_ast!(4), "-", int_ast!(56)));
assert_eq!(result, bin_ast!(int_ast_b!(4), "-", int_ast_b!(56)));
let result = parse(&tokenize("1 * 2"));
assert_eq!(result, BinaryOp(int_ast!(1), "*", int_ast!(2)));
assert_eq!(result, bin_ast!(int_ast_b!(1), "*", int_ast_b!(2)));
let result = parse(&tokenize("1 / 2"));
assert_eq!(result, BinaryOp(int_ast!(1), "/", int_ast!(2)));
assert_eq!(result, bin_ast!(int_ast_b!(1), "/", int_ast_b!(2)));
}
#[test]
@ -75,22 +153,26 @@ fn test_binary_op_all_levels() {
let result = parse(&tokenize("1 * 2 + 3 < 4 == 5 and 6 or 7"));
assert_eq!(
result,
BinaryOp(
bin_ast!(
bin_ast!(
bin_ast!(
bin_ast!(bin_ast!(int_ast!(1), "*", int_ast!(2)), "+", int_ast!(3)),
bin_ast!(
bin_ast_b!(
bin_ast_b!(
bin_ast_b!(
bin_ast_b!(
bin_ast_b!(int_ast_b!(1), "*", int_ast_b!(2)),
"+",
int_ast_b!(3)
),
"<",
int_ast!(4)
int_ast_b!(4)
),
"==",
int_ast!(5)
int_ast_b!(5)
),
"and",
int_ast!(6)
int_ast_b!(6)
),
"or",
int_ast!(7)
int_ast_b!(7)
)
);
}
@ -98,10 +180,10 @@ fn test_binary_op_all_levels() {
#[test]
fn test_binary_op_identifier() {
let result = parse(&tokenize("a + 1"));
assert_eq!(result, BinaryOp(id_ast!("a"), "+", int_ast!(1)));
assert_eq!(result, bin_ast!(id_ast_b!("a"), "+", int_ast_b!(1)));
let result = parse(&tokenize("1 - a"));
assert_eq!(result, BinaryOp(int_ast!(1), "-", id_ast!("a")));
assert_eq!(result, bin_ast!(int_ast_b!(1), "-", id_ast_b!("a")));
}
#[test]
@ -109,7 +191,11 @@ fn test_binary_op_multiple() {
let result = parse(&tokenize("1 + 2 - 3"));
assert_eq!(
result,
BinaryOp(bin_ast!(int_ast!(1), "+", int_ast!(2)), "-", int_ast!(3))
bin_ast!(
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)),
"-",
int_ast_b!(3)
)
);
}
@ -118,13 +204,21 @@ fn test_binary_op_precedence() {
let result = parse(&tokenize("1 + 2 * 3"));
assert_eq!(
result,
BinaryOp(int_ast!(1), "+", bin_ast!(int_ast!(2), "*", int_ast!(3)),)
bin_ast!(
int_ast_b!(1),
"+",
bin_ast_b!(int_ast_b!(2), "*", int_ast_b!(3))
)
);
let result = parse(&tokenize("1 - 2 / 3"));
assert_eq!(
result,
BinaryOp(int_ast!(1), "-", bin_ast!(int_ast!(2), "/", int_ast!(3)),)
bin_ast!(
int_ast_b!(1),
"-",
bin_ast_b!(int_ast_b!(2), "/", int_ast_b!(3))
)
);
}
@ -133,7 +227,11 @@ fn test_assignment_basic() {
let result = parse(&tokenize("a = 1 + 2"));
assert_eq!(
result,
BinaryOp(id_ast!("a"), "=", bin_ast!(int_ast!(1), "+", int_ast!(2)))
bin_ast!(
id_ast_b!("a"),
"=",
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2))
)
);
}
@ -142,10 +240,14 @@ fn test_assignment_chain() {
let result = parse(&tokenize("a = b = 1 + 2"));
assert_eq!(
result,
BinaryOp(
id_ast!("a"),
bin_ast!(
id_ast_b!("a"),
"=",
bin_ast!(id_ast!("b"), "=", bin_ast!(int_ast!(1), "+", int_ast!(2)))
bin_ast_b!(
id_ast_b!("b"),
"=",
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2))
)
)
);
}
@ -159,36 +261,40 @@ fn test_assignment_invalid() {
#[test]
fn test_unary_basic() {
let result = parse(&tokenize("not x"));
assert_eq!(result, UnaryOp("not", id_ast!("x")));
assert_eq!(result, un_ast!("not", id_ast_b!("x")));
let result = parse(&tokenize("-x"));
assert_eq!(result, UnaryOp("-", id_ast!("x")));
assert_eq!(result, un_ast!("-", id_ast_b!("x")));
let result = parse(&tokenize("-1"));
assert_eq!(result, UnaryOp("-", int_ast!(1)));
assert_eq!(result, un_ast!("-", int_ast_b!(1)));
let result = parse(&tokenize("-1 + 2"));
assert_eq!(
result,
BinaryOp(un_ast!("-", int_ast!(1)), "+", int_ast!(2))
bin_ast!(un_ast_b!("-", int_ast_b!(1)), "+", int_ast_b!(2))
);
}
#[test]
fn test_unary_chain() {
let result = parse(&tokenize("not not x"));
assert_eq!(result, UnaryOp("not", un_ast!("not", id_ast!("x"))));
assert_eq!(result, un_ast!("not", un_ast_b!("not", id_ast_b!("x"))));
let result = parse(&tokenize("--x"));
assert_eq!(result, UnaryOp("-", un_ast!("-", id_ast!("x"))));
assert_eq!(result, un_ast!("-", un_ast_b!("-", id_ast_b!("x"))));
let result = parse(&tokenize("--1"));
assert_eq!(result, UnaryOp("-", un_ast!("-", int_ast!(1))));
assert_eq!(result, un_ast!("-", un_ast_b!("-", int_ast_b!(1))));
let result = parse(&tokenize("--1 + 2"));
assert_eq!(
result,
BinaryOp(un_ast!("-", un_ast!("-", int_ast!(1))), "+", int_ast!(2))
bin_ast!(
un_ast_b!("-", un_ast_b!("-", int_ast_b!(1))),
"+",
int_ast_b!(2)
)
);
}
@ -197,7 +303,11 @@ fn test_parenthesized() {
let result = parse(&tokenize("(1+2)*3"));
assert_eq!(
result,
BinaryOp(bin_ast!(int_ast!(1), "+", int_ast!(2)), "*", int_ast!(3),)
bin_ast!(
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)),
"*",
int_ast_b!(3)
)
);
}
@ -206,16 +316,24 @@ fn test_parenthesized_nested() {
let result = parse(&tokenize("((1 - 2))/3"));
assert_eq!(
result,
BinaryOp(bin_ast!(int_ast!(1), "-", int_ast!(2)), "/", int_ast!(3),)
bin_ast!(
bin_ast_b!(int_ast_b!(1), "-", int_ast_b!(2)),
"/",
int_ast_b!(3)
)
);
let result = parse(&tokenize("((1 + 2)*3) / 4"));
assert_eq!(
result,
BinaryOp(
bin_ast!(bin_ast!(int_ast!(1), "+", int_ast!(2)), "*", int_ast!(3)),
bin_ast!(
bin_ast_b!(
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)),
"*",
int_ast_b!(3)
),
"/",
int_ast!(4)
int_ast_b!(4)
)
);
}
@ -231,7 +349,11 @@ fn test_if_then() {
let result = parse(&tokenize("if 1 + 2 then 3"));
assert_eq!(
result,
Conditional(bin_ast!(int_ast!(1), "+", int_ast!(2)), int_ast!(3), None,)
con_ast!(
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)),
int_ast_b!(3),
None
)
);
}
@ -240,10 +362,10 @@ fn test_if_then_else() {
let result = parse(&tokenize("if a then b + c else 1 * 2"));
assert_eq!(
result,
Conditional(
id_ast!("a"),
bin_ast!(id_ast!("b"), "+", id_ast!("c")),
Some(bin_ast!(int_ast!(1), "*", int_ast!(2)))
con_ast!(
id_ast_b!("a"),
bin_ast_b!(id_ast_b!("b"), "+", id_ast_b!("c")),
Some(bin_ast_b!(int_ast_b!(1), "*", int_ast_b!(2)))
)
);
}
@ -253,10 +375,10 @@ fn test_if_then_else_embedded() {
let result = parse(&tokenize("1 + if true then 2 else 3"));
assert_eq!(
result,
BinaryOp(
int_ast!(1),
bin_ast!(
int_ast_b!(1),
"+",
Box::new(Conditional(bool_ast!(true), int_ast!(2), Some(int_ast!(3))))
con_ast_b!(bool_ast_b!(true), int_ast_b!(2), Some(int_ast_b!(3)))
)
);
}
@ -266,14 +388,10 @@ fn test_if_then_else_nested() {
let result = parse(&tokenize("if true then if false then 1 else 2 else 3"));
assert_eq!(
result,
Conditional(
bool_ast!(true),
Box::new(Conditional(
bool_ast!(false),
int_ast!(1),
Some(int_ast!(2))
)),
Some(int_ast!(3))
con_ast!(
bool_ast_b!(true),
con_ast_b!(bool_ast_b!(false), int_ast_b!(1), Some(int_ast_b!(2))),
Some(int_ast_b!(3))
)
);
}
@ -287,22 +405,19 @@ fn test_if_no_then() {
#[test]
fn test_func_basic() {
let result = parse(&tokenize("f(a, b)"));
assert_eq!(
result,
FunCall("f", vec![Identifier("a"), Identifier("b"),])
);
assert_eq!(result, fun_ast!("f", vec![id_ast!("a"), id_ast!("b"),]));
let result = parse(&tokenize("f(a, 1 + 2)"));
assert_eq!(
result,
FunCall(
fun_ast!(
"f",
vec![Identifier("a"), BinaryOp(int_ast!(1), "+", int_ast!(2),),]
vec![id_ast!("a"), bin_ast!(int_ast_b!(1), "+", int_ast_b!(2)),]
)
);
let result = parse(&tokenize("f()"));
assert_eq!(result, FunCall("f", vec![]));
assert_eq!(result, fun_ast!("f", vec![]));
}
#[test]
@ -310,11 +425,7 @@ fn test_func_embedded() {
let result = parse(&tokenize("1 + f(a)"));
assert_eq!(
result,
BinaryOp(
int_ast!(1),
"+",
Box::new(FunCall("f", vec![Identifier("a")]))
)
bin_ast!(int_ast_b!(1), "+", fun_ast_b!("f", vec![id_ast!("a")]))
);
}
@ -323,10 +434,7 @@ fn test_func_nested() {
let result = parse(&tokenize("f(a, g(b))"));
assert_eq!(
result,
FunCall(
"f",
vec![Identifier("a"), FunCall("g", vec![Identifier("b")]),]
)
fun_ast!("f", vec![id_ast!("a"), fun_ast!("g", vec![id_ast!("b")]),])
);
}
@ -347,19 +455,19 @@ fn test_block_basic() {
let result = parse(&tokenize("{ a = 1; b; }"));
assert_eq!(
result,
Block(vec![
BinaryOp(id_ast!("a"), "=", int_ast!(1)),
Identifier("b"),
EmptyLiteral()
block_ast!(vec![
bin_ast!(id_ast_b!("a"), "=", int_ast_b!(1)),
id_ast!("b"),
empty_ast!()
])
);
let result = parse(&tokenize("{ a = 1; b }"));
assert_eq!(
result,
Block(vec![
BinaryOp(id_ast!("a"), "=", int_ast!(1)),
Identifier("b"),
block_ast!(vec![
bin_ast!(id_ast_b!("a"), "=", int_ast_b!(1)),
id_ast!("b"),
])
);
}
@ -369,10 +477,10 @@ fn test_block_embedded() {
let result = parse(&tokenize("{ 1 + 2 } * 3"));
assert_eq!(
result,
BinaryOp(
Box::new(Block(vec![BinaryOp(int_ast!(1), "+", int_ast!(2))])),
bin_ast!(
block_ast_b!(vec![bin_ast!(int_ast_b!(1), "+", int_ast_b!(2))]),
"*",
int_ast!(3)
int_ast_b!(3)
)
);
}
@ -382,10 +490,10 @@ fn test_block_nested() {
let result = parse(&tokenize("{ a = { 1 + 2}}"));
assert_eq!(
result,
Block(vec![BinaryOp(
id_ast!("a"),
block_ast!(vec![bin_ast!(
id_ast_b!("a"),
"=",
Box::new(Block(vec![BinaryOp(int_ast!(1), "+", int_ast!(2))])),
block_ast_b!(vec![bin_ast!(int_ast_b!(1), "+", int_ast_b!(2))])
)])
);
}
@ -405,15 +513,15 @@ fn test_block_missing_semicolon() {
#[test]
fn test_var_basic() {
let result = parse(&tokenize("var x = 1"));
assert_eq!(result, VarDeclaration("x", int_ast!(1)));
assert_eq!(result, var_ast!("x", int_ast_b!(1)));
let result = parse(&tokenize("{ var x = 1; x = 2; }"));
assert_eq!(
result,
Block(vec![
VarDeclaration("x", int_ast!(1)),
BinaryOp(id_ast!("x"), "=", int_ast!(2)),
EmptyLiteral()
block_ast!(vec![
var_ast!("x", int_ast_b!(1)),
bin_ast!(id_ast_b!("x"), "=", int_ast_b!(2)),
empty_ast!()
])
);
}
@ -435,61 +543,53 @@ fn test_omitting_semicolons() {
let result = parse(&tokenize("{ { a } { b } }"));
assert_eq!(
result,
Block(vec![
Block(vec![Identifier("a")]),
Block(vec![Identifier("b")])
block_ast!(vec![
block_ast!(vec![id_ast!("a")]),
block_ast!(vec![id_ast!("b")])
])
);
let result = parse(&tokenize("{ if true then { a } b }"));
assert_eq!(
result,
Block(vec![
Conditional(
bool_ast!(true),
Box::new(Block(vec![Identifier("a")])),
None
),
Identifier("b"),
block_ast!(vec![
con_ast!(bool_ast_b!(true), block_ast_b!(vec![id_ast!("a")]), None),
id_ast!("b"),
])
);
let result = parse(&tokenize("{ if true then { a }; b }"));
assert_eq!(
result,
Block(vec![
Conditional(
bool_ast!(true),
Box::new(Block(vec![Identifier("a")])),
None
),
Identifier("b"),
block_ast!(vec![
con_ast!(bool_ast_b!(true), block_ast_b!(vec![id_ast!("a")]), None),
id_ast!("b"),
])
);
let result = parse(&tokenize("{ if true then { a } else { b } c }"));
assert_eq!(
result,
Block(vec![
Conditional(
bool_ast!(true),
Box::new(Block(vec![Identifier("a")])),
Some(Box::new(Block(vec![Identifier("b")])))
block_ast!(vec![
con_ast!(
bool_ast_b!(true),
block_ast_b!(vec![id_ast!("a")]),
Some(block_ast_b!(vec![id_ast!("b")]))
),
Identifier("c"),
id_ast!("c"),
])
);
let result = parse(&tokenize("x = { { f(a) } { b } }"));
assert_eq!(
result,
BinaryOp(
id_ast!("x"),
bin_ast!(
id_ast_b!("x"),
"=",
Box::new(Block(vec![
Block(vec![FunCall("f", vec![Identifier("a")])]),
Block(vec![Identifier("b")]),
]))
block_ast_b!(vec![
block_ast!(vec![fun_ast!("f", vec![id_ast!("a")])]),
block_ast!(vec![id_ast!("b")]),
])
)
);
}