Add locations to AST Expressions
This commit is contained in:
parent
a2f96cc8df
commit
56281008e4
3 changed files with 320 additions and 144 deletions
|
@ -1,21 +1,85 @@
|
||||||
|
use crate::compiler::token::CodeLocation;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Expression<'source> {
|
pub enum Expression<'source> {
|
||||||
EmptyLiteral(),
|
EmptyLiteral(CodeLocation),
|
||||||
IntLiteral(u32),
|
IntLiteral(CodeLocation, u32),
|
||||||
BoolLiteral(bool),
|
BoolLiteral(CodeLocation, bool),
|
||||||
Identifier(&'source str),
|
Identifier(CodeLocation, &'source str),
|
||||||
UnaryOp(&'source str, Box<Expression<'source>>),
|
UnaryOp(CodeLocation, &'source str, Box<Expression<'source>>),
|
||||||
VarDeclaration(&'source str, Box<Expression<'source>>),
|
VarDeclaration(CodeLocation, &'source str, Box<Expression<'source>>),
|
||||||
BinaryOp(
|
BinaryOp(
|
||||||
|
CodeLocation,
|
||||||
Box<Expression<'source>>,
|
Box<Expression<'source>>,
|
||||||
&'source str,
|
&'source str,
|
||||||
Box<Expression<'source>>,
|
Box<Expression<'source>>,
|
||||||
),
|
),
|
||||||
Conditional(
|
Conditional(
|
||||||
|
CodeLocation,
|
||||||
Box<Expression<'source>>,
|
Box<Expression<'source>>,
|
||||||
Box<Expression<'source>>,
|
Box<Expression<'source>>,
|
||||||
Option<Box<Expression<'source>>>,
|
Option<Box<Expression<'source>>>,
|
||||||
),
|
),
|
||||||
FunCall(&'source str, Vec<Expression<'source>>),
|
FunCall(CodeLocation, &'source str, Vec<Expression<'source>>),
|
||||||
Block(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()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,12 @@ fn parse_expression<'source>(
|
||||||
if OPS[level].contains(&peek(pos, tokens).text) {
|
if OPS[level].contains(&peek(pos, tokens).text) {
|
||||||
let operator_token = consume_strings(pos, tokens, OPS[level]);
|
let operator_token = consume_strings(pos, tokens, OPS[level]);
|
||||||
let right = parse_expression(level, pos, tokens);
|
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 {
|
} else {
|
||||||
left
|
left
|
||||||
}
|
}
|
||||||
|
@ -70,7 +75,12 @@ fn parse_expression<'source>(
|
||||||
let operator_token = consume_strings(pos, tokens, OPS[level]);
|
let operator_token = consume_strings(pos, tokens, OPS[level]);
|
||||||
let right = parse_expression(level + 1, pos, tokens);
|
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
|
left
|
||||||
}
|
}
|
||||||
|
@ -78,7 +88,7 @@ fn parse_expression<'source>(
|
||||||
if OPS[level].contains(&peek(pos, tokens).text) {
|
if OPS[level].contains(&peek(pos, tokens).text) {
|
||||||
let operator_token = consume_strings(pos, tokens, OPS[level]);
|
let operator_token = consume_strings(pos, tokens, OPS[level]);
|
||||||
let right = parse_expression(level, pos, tokens);
|
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 {
|
} else {
|
||||||
parse_expression(level + 1, pos, tokens)
|
parse_expression(level + 1, pos, tokens)
|
||||||
}
|
}
|
||||||
|
@ -119,14 +129,14 @@ fn parse_var_declaration<'source>(
|
||||||
tokens: &[Token<'source>],
|
tokens: &[Token<'source>],
|
||||||
) -> Expression<'source> {
|
) -> Expression<'source> {
|
||||||
consume_string(pos, tokens, "var");
|
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, "=");
|
consume_string(pos, tokens, "=");
|
||||||
let value = parse_expression(0, 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> {
|
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));
|
let condition = Box::new(parse_expression(0, pos, tokens));
|
||||||
consume_string(pos, tokens, "then");
|
consume_string(pos, tokens, "then");
|
||||||
let then_expr = Box::new(parse_expression(0, pos, tokens));
|
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,
|
_ => 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> {
|
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> {
|
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();
|
let mut expressions = Vec::new();
|
||||||
loop {
|
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 the last expression of the block ended in a semicolon, empty return
|
||||||
if peek(pos, tokens).text == "}" {
|
let next_token = peek(pos, tokens);
|
||||||
expressions.push(EmptyLiteral());
|
if next_token.text == "}" {
|
||||||
|
expressions.push(EmptyLiteral(next_token.loc));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
consume_string(pos, tokens, "}");
|
consume_string(pos, tokens, "}");
|
||||||
Block(expressions)
|
Block(start.loc, expressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_function<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expression<'source> {
|
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, ")");
|
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> {
|
fn parse_int_literal<'source>(pos: &mut usize, tokens: &[Token]) -> Expression<'source> {
|
||||||
let token = consume_type(pos, tokens, TokenType::Integer);
|
let token = consume_type(pos, tokens, TokenType::Integer);
|
||||||
|
|
||||||
IntLiteral(
|
IntLiteral(
|
||||||
|
token.loc,
|
||||||
token
|
token
|
||||||
.text
|
.text
|
||||||
.parse::<u32>()
|
.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);
|
let token = consume_type(pos, tokens, TokenType::Identifier);
|
||||||
|
|
||||||
match token.text {
|
match token.text {
|
||||||
"true" => BoolLiteral(true),
|
"true" => BoolLiteral(token.loc, true),
|
||||||
"false" => BoolLiteral(false),
|
"false" => BoolLiteral(token.loc, false),
|
||||||
_ => panic!("Fatal parser error! Expected bool literal but found {token}"),
|
_ => panic!("Fatal parser error! Expected bool literal but found {token}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_identifier<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expression<'source> {
|
fn parse_identifier<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expression<'source> {
|
||||||
let token = consume_type(pos, tokens, TokenType::Identifier);
|
let token = consume_type(pos, tokens, TokenType::Identifier);
|
||||||
Identifier(token.text)
|
Identifier(token.loc, token.text)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,111 @@
|
||||||
use super::*;
|
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 {
|
macro_rules! int_ast {
|
||||||
($x:expr) => {
|
($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 {
|
macro_rules! id_ast {
|
||||||
($x:expr) => {
|
($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 {
|
macro_rules! un_ast {
|
||||||
($x:expr, $y:expr) => {
|
($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 {
|
macro_rules! bin_ast {
|
||||||
($x:expr, $y:expr, $z:expr) => {
|
($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) => {
|
($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]
|
#[test]
|
||||||
fn test_binary_op_basic() {
|
fn test_binary_op_basic() {
|
||||||
let result = parse(&tokenize("1 + 23"));
|
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"));
|
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"));
|
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"));
|
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]
|
#[test]
|
||||||
|
@ -75,22 +153,26 @@ fn test_binary_op_all_levels() {
|
||||||
let result = parse(&tokenize("1 * 2 + 3 < 4 == 5 and 6 or 7"));
|
let result = parse(&tokenize("1 * 2 + 3 < 4 == 5 and 6 or 7"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
BinaryOp(
|
bin_ast!(
|
||||||
bin_ast!(
|
bin_ast_b!(
|
||||||
bin_ast!(
|
bin_ast_b!(
|
||||||
bin_ast!(
|
bin_ast_b!(
|
||||||
bin_ast!(bin_ast!(int_ast!(1), "*", int_ast!(2)), "+", int_ast!(3)),
|
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",
|
"and",
|
||||||
int_ast!(6)
|
int_ast_b!(6)
|
||||||
),
|
),
|
||||||
"or",
|
"or",
|
||||||
int_ast!(7)
|
int_ast_b!(7)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -98,10 +180,10 @@ fn test_binary_op_all_levels() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_binary_op_identifier() {
|
fn test_binary_op_identifier() {
|
||||||
let result = parse(&tokenize("a + 1"));
|
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"));
|
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]
|
#[test]
|
||||||
|
@ -109,7 +191,11 @@ fn test_binary_op_multiple() {
|
||||||
let result = parse(&tokenize("1 + 2 - 3"));
|
let result = parse(&tokenize("1 + 2 - 3"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
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"));
|
let result = parse(&tokenize("1 + 2 * 3"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
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"));
|
let result = parse(&tokenize("1 - 2 / 3"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
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"));
|
let result = parse(&tokenize("a = 1 + 2"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
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"));
|
let result = parse(&tokenize("a = b = 1 + 2"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
BinaryOp(
|
bin_ast!(
|
||||||
id_ast!("a"),
|
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]
|
#[test]
|
||||||
fn test_unary_basic() {
|
fn test_unary_basic() {
|
||||||
let result = parse(&tokenize("not x"));
|
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"));
|
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"));
|
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"));
|
let result = parse(&tokenize("-1 + 2"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
BinaryOp(un_ast!("-", int_ast!(1)), "+", int_ast!(2))
|
bin_ast!(un_ast_b!("-", int_ast_b!(1)), "+", int_ast_b!(2))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unary_chain() {
|
fn test_unary_chain() {
|
||||||
let result = parse(&tokenize("not not x"));
|
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"));
|
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"));
|
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"));
|
let result = parse(&tokenize("--1 + 2"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
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"));
|
let result = parse(&tokenize("(1+2)*3"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
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"));
|
let result = parse(&tokenize("((1 - 2))/3"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
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"));
|
let result = parse(&tokenize("((1 + 2)*3) / 4"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
BinaryOp(
|
bin_ast!(
|
||||||
bin_ast!(bin_ast!(int_ast!(1), "+", int_ast!(2)), "*", int_ast!(3)),
|
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"));
|
let result = parse(&tokenize("if 1 + 2 then 3"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
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"));
|
let result = parse(&tokenize("if a then b + c else 1 * 2"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Conditional(
|
con_ast!(
|
||||||
id_ast!("a"),
|
id_ast_b!("a"),
|
||||||
bin_ast!(id_ast!("b"), "+", id_ast!("c")),
|
bin_ast_b!(id_ast_b!("b"), "+", id_ast_b!("c")),
|
||||||
Some(bin_ast!(int_ast!(1), "*", int_ast!(2)))
|
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"));
|
let result = parse(&tokenize("1 + if true then 2 else 3"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
BinaryOp(
|
bin_ast!(
|
||||||
int_ast!(1),
|
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"));
|
let result = parse(&tokenize("if true then if false then 1 else 2 else 3"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Conditional(
|
con_ast!(
|
||||||
bool_ast!(true),
|
bool_ast_b!(true),
|
||||||
Box::new(Conditional(
|
con_ast_b!(bool_ast_b!(false), int_ast_b!(1), Some(int_ast_b!(2))),
|
||||||
bool_ast!(false),
|
Some(int_ast_b!(3))
|
||||||
int_ast!(1),
|
|
||||||
Some(int_ast!(2))
|
|
||||||
)),
|
|
||||||
Some(int_ast!(3))
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -287,22 +405,19 @@ fn test_if_no_then() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_func_basic() {
|
fn test_func_basic() {
|
||||||
let result = parse(&tokenize("f(a, b)"));
|
let result = parse(&tokenize("f(a, b)"));
|
||||||
assert_eq!(
|
assert_eq!(result, fun_ast!("f", vec![id_ast!("a"), id_ast!("b"),]));
|
||||||
result,
|
|
||||||
FunCall("f", vec![Identifier("a"), Identifier("b"),])
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = parse(&tokenize("f(a, 1 + 2)"));
|
let result = parse(&tokenize("f(a, 1 + 2)"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
FunCall(
|
fun_ast!(
|
||||||
"f",
|
"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()"));
|
let result = parse(&tokenize("f()"));
|
||||||
assert_eq!(result, FunCall("f", vec![]));
|
assert_eq!(result, fun_ast!("f", vec![]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -310,11 +425,7 @@ fn test_func_embedded() {
|
||||||
let result = parse(&tokenize("1 + f(a)"));
|
let result = parse(&tokenize("1 + f(a)"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
BinaryOp(
|
bin_ast!(int_ast_b!(1), "+", fun_ast_b!("f", vec![id_ast!("a")]))
|
||||||
int_ast!(1),
|
|
||||||
"+",
|
|
||||||
Box::new(FunCall("f", vec![Identifier("a")]))
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,10 +434,7 @@ fn test_func_nested() {
|
||||||
let result = parse(&tokenize("f(a, g(b))"));
|
let result = parse(&tokenize("f(a, g(b))"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
FunCall(
|
fun_ast!("f", vec![id_ast!("a"), fun_ast!("g", vec![id_ast!("b")]),])
|
||||||
"f",
|
|
||||||
vec![Identifier("a"), FunCall("g", vec![Identifier("b")]),]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,19 +455,19 @@ fn test_block_basic() {
|
||||||
let result = parse(&tokenize("{ a = 1; b; }"));
|
let result = parse(&tokenize("{ a = 1; b; }"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Block(vec![
|
block_ast!(vec![
|
||||||
BinaryOp(id_ast!("a"), "=", int_ast!(1)),
|
bin_ast!(id_ast_b!("a"), "=", int_ast_b!(1)),
|
||||||
Identifier("b"),
|
id_ast!("b"),
|
||||||
EmptyLiteral()
|
empty_ast!()
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = parse(&tokenize("{ a = 1; b }"));
|
let result = parse(&tokenize("{ a = 1; b }"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Block(vec![
|
block_ast!(vec![
|
||||||
BinaryOp(id_ast!("a"), "=", int_ast!(1)),
|
bin_ast!(id_ast_b!("a"), "=", int_ast_b!(1)),
|
||||||
Identifier("b"),
|
id_ast!("b"),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -369,10 +477,10 @@ fn test_block_embedded() {
|
||||||
let result = parse(&tokenize("{ 1 + 2 } * 3"));
|
let result = parse(&tokenize("{ 1 + 2 } * 3"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
BinaryOp(
|
bin_ast!(
|
||||||
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))]),
|
||||||
"*",
|
"*",
|
||||||
int_ast!(3)
|
int_ast_b!(3)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -382,10 +490,10 @@ fn test_block_nested() {
|
||||||
let result = parse(&tokenize("{ a = { 1 + 2}}"));
|
let result = parse(&tokenize("{ a = { 1 + 2}}"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Block(vec![BinaryOp(
|
block_ast!(vec![bin_ast!(
|
||||||
id_ast!("a"),
|
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]
|
#[test]
|
||||||
fn test_var_basic() {
|
fn test_var_basic() {
|
||||||
let result = parse(&tokenize("var x = 1"));
|
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; }"));
|
let result = parse(&tokenize("{ var x = 1; x = 2; }"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Block(vec![
|
block_ast!(vec![
|
||||||
VarDeclaration("x", int_ast!(1)),
|
var_ast!("x", int_ast_b!(1)),
|
||||||
BinaryOp(id_ast!("x"), "=", int_ast!(2)),
|
bin_ast!(id_ast_b!("x"), "=", int_ast_b!(2)),
|
||||||
EmptyLiteral()
|
empty_ast!()
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -435,61 +543,53 @@ fn test_omitting_semicolons() {
|
||||||
let result = parse(&tokenize("{ { a } { b } }"));
|
let result = parse(&tokenize("{ { a } { b } }"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Block(vec![
|
block_ast!(vec![
|
||||||
Block(vec![Identifier("a")]),
|
block_ast!(vec![id_ast!("a")]),
|
||||||
Block(vec![Identifier("b")])
|
block_ast!(vec![id_ast!("b")])
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = parse(&tokenize("{ if true then { a } b }"));
|
let result = parse(&tokenize("{ if true then { a } b }"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Block(vec![
|
block_ast!(vec![
|
||||||
Conditional(
|
con_ast!(bool_ast_b!(true), block_ast_b!(vec![id_ast!("a")]), None),
|
||||||
bool_ast!(true),
|
id_ast!("b"),
|
||||||
Box::new(Block(vec![Identifier("a")])),
|
|
||||||
None
|
|
||||||
),
|
|
||||||
Identifier("b"),
|
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = parse(&tokenize("{ if true then { a }; b }"));
|
let result = parse(&tokenize("{ if true then { a }; b }"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Block(vec![
|
block_ast!(vec![
|
||||||
Conditional(
|
con_ast!(bool_ast_b!(true), block_ast_b!(vec![id_ast!("a")]), None),
|
||||||
bool_ast!(true),
|
id_ast!("b"),
|
||||||
Box::new(Block(vec![Identifier("a")])),
|
|
||||||
None
|
|
||||||
),
|
|
||||||
Identifier("b"),
|
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = parse(&tokenize("{ if true then { a } else { b } c }"));
|
let result = parse(&tokenize("{ if true then { a } else { b } c }"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Block(vec![
|
block_ast!(vec![
|
||||||
Conditional(
|
con_ast!(
|
||||||
bool_ast!(true),
|
bool_ast_b!(true),
|
||||||
Box::new(Block(vec![Identifier("a")])),
|
block_ast_b!(vec![id_ast!("a")]),
|
||||||
Some(Box::new(Block(vec![Identifier("b")])))
|
Some(block_ast_b!(vec![id_ast!("b")]))
|
||||||
),
|
),
|
||||||
Identifier("c"),
|
id_ast!("c"),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = parse(&tokenize("x = { { f(a) } { b } }"));
|
let result = parse(&tokenize("x = { { f(a) } { b } }"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
BinaryOp(
|
bin_ast!(
|
||||||
id_ast!("x"),
|
id_ast_b!("x"),
|
||||||
"=",
|
"=",
|
||||||
Box::new(Block(vec![
|
block_ast_b!(vec![
|
||||||
Block(vec![FunCall("f", vec![Identifier("a")])]),
|
block_ast!(vec![fun_ast!("f", vec![id_ast!("a")])]),
|
||||||
Block(vec![Identifier("b")]),
|
block_ast!(vec![id_ast!("b")]),
|
||||||
]))
|
])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue