Browse Source

Parser tests (#42)

* First parser tests

* Added more parser tests

* Fixed formatting issue

* Added bitwise operators, removed debugging statement
pull/53/head
Iban Eguia 5 years ago committed by Jason Williams
parent
commit
14672dd340
  1. 427
      src/lib/syntax/parser.rs
  2. 9
      tests/js/test.js

427
src/lib/syntax/parser.rs

@ -757,3 +757,430 @@ impl Parser {
self.expect(TokenData::Punctuator(p), routine) self.expect(TokenData::Punctuator(p), routine)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::syntax::{
ast::expr::{Expr, ExprDef},
lexer::Lexer,
};
fn check_parser(js: &str, expr: &[Expr]) {
let mut lexer = Lexer::new(js);
lexer.lex().unwrap();
assert_eq!(
Parser::new(lexer.tokens).parse_all().unwrap(),
Expr::new(ExprDef::BlockExpr(expr.into()))
);
}
fn check_invalid(js: &str) {
let mut lexer = Lexer::new(js);
lexer.lex().unwrap();
assert!(Parser::new(lexer.tokens).parse_all().is_err());
}
#[test]
fn check_string() {
use crate::syntax::ast::constant::Const;
// Check empty string
check_parser(
"\"\"",
&[Expr::new(ExprDef::ConstExpr(Const::String(String::new())))],
);
// Check non-empty string
check_parser(
"\"hello\"",
&[Expr::new(ExprDef::ConstExpr(Const::String(String::from(
"hello",
))))],
);
}
#[test]
fn check_array() {
use crate::syntax::ast::constant::Const;
// Check empty array
check_parser("[]", &[Expr::new(ExprDef::ArrayDeclExpr(vec![]))]);
// Check array with empty slot
// FIXME: This does not work, it should ignore the comma:
// <https://tc39.es/ecma262/#prod-ArrayAssignmentPattern>
// check_parser("[,]", &[Expr::new(ExprDef::ArrayDeclExpr(vec![]))]);
// Check numeric array
check_parser(
"[1, 2, 3]",
&[Expr::new(ExprDef::ArrayDeclExpr(vec![
Expr::new(ExprDef::ConstExpr(Const::Num(1.0))),
Expr::new(ExprDef::ConstExpr(Const::Num(2.0))),
Expr::new(ExprDef::ConstExpr(Const::Num(3.0))),
]))],
);
// Check numeric array with trailing comma
// FIXME: This does not work, it should ignore the trailing comma:
// <https://tc39.es/ecma262/#prod-ArrayAssignmentPattern>
// check_parser("[1, 2, 3,]", &[Expr::new(ExprDef::ArrayDeclExpr(vec![
// Expr::new(ExprDef::ConstExpr(Const::Num(1.0))),
// Expr::new(ExprDef::ConstExpr(Const::Num(2.0))),
// Expr::new(ExprDef::ConstExpr(Const::Num(3.0))),
// ]))]);
// Check combined array
check_parser(
"[1, \"a\", 2]",
&[Expr::new(ExprDef::ArrayDeclExpr(vec![
Expr::new(ExprDef::ConstExpr(Const::Num(1.0))),
Expr::new(ExprDef::ConstExpr(Const::String(String::from("a")))),
Expr::new(ExprDef::ConstExpr(Const::Num(2.0))),
]))],
);
// Check combined array with empty string
check_parser(
"[1, \"\", 2]",
&[Expr::new(ExprDef::ArrayDeclExpr(vec![
Expr::new(ExprDef::ConstExpr(Const::Num(1.0))),
Expr::new(ExprDef::ConstExpr(Const::String(String::new()))),
Expr::new(ExprDef::ConstExpr(Const::Num(2.0))),
]))],
);
}
#[test]
fn check_declarations() {
use crate::syntax::ast::constant::Const;
// Check `var` declaration
check_parser(
"var a = 5;",
&[Expr::new(ExprDef::VarDeclExpr(vec![(
String::from("a"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))),
)]))],
);
// Check `var` declaration with no spaces
check_parser(
"var a=5;",
&[Expr::new(ExprDef::VarDeclExpr(vec![(
String::from("a"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))),
)]))],
);
// Check empty `var` declaration
check_parser(
"var a;",
&[Expr::new(ExprDef::VarDeclExpr(vec![(
String::from("a"),
None,
)]))],
);
// Check multiple `var` declaration
check_parser(
"var a = 5, b, c = 6;",
&[Expr::new(ExprDef::VarDeclExpr(vec![
(
String::from("a"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))),
),
(String::from("b"), None),
(
String::from("c"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(6.0)))),
),
]))],
);
// Check `let` declaration
check_parser(
"let a = 5;",
&[Expr::new(ExprDef::LetDeclExpr(vec![(
String::from("a"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))),
)]))],
);
// Check `let` declaration with no spaces
check_parser(
"let a=5;",
&[Expr::new(ExprDef::LetDeclExpr(vec![(
String::from("a"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))),
)]))],
);
// Check empty `let` declaration
check_parser(
"let a;",
&[Expr::new(ExprDef::LetDeclExpr(vec![(
String::from("a"),
None,
)]))],
);
// Check multiple `let` declaration
check_parser(
"let a = 5, b, c = 6;",
&[Expr::new(ExprDef::LetDeclExpr(vec![
(
String::from("a"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))),
),
(String::from("b"), None),
(
String::from("c"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(6.0)))),
),
]))],
);
// Check `const` declaration
check_parser(
"const a = 5;",
&[Expr::new(ExprDef::ConstDeclExpr(vec![(
String::from("a"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))),
)]))],
);
// Check `const` declaration with no spaces
check_parser(
"const a=5;",
&[Expr::new(ExprDef::ConstDeclExpr(vec![(
String::from("a"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))),
)]))],
);
// Check empty `const` declaration
// FIXME: This does not work, it should fail to parse an unitialized const declaration:
// <https://tc39.es/ecma262/#sec-let-and-const-declarations-static-semantics-early-errors>
// check_invalid("const a;");
// Check multiple `const` declaration
check_parser(
"const a = 5, b, c = 6;",
&[Expr::new(ExprDef::ConstDeclExpr(vec![
(
String::from("a"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(5.0)))),
),
(String::from("b"), None),
(
String::from("c"),
Some(Expr::new(ExprDef::ConstExpr(Const::Num(6.0)))),
),
]))],
);
}
#[test]
fn check_operations() {
use crate::syntax::ast::{constant::Const, op::BinOp};
fn create_bin_op(op: BinOp, exp1: Expr, exp2: Expr) -> Expr {
Expr::new(ExprDef::BinOpExpr(op, Box::new(exp1), Box::new(exp2)))
}
// Check numeric operations
check_parser(
"a + b",
&[create_bin_op(
BinOp::Num(NumOp::Add),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a+1",
&[create_bin_op(
BinOp::Num(NumOp::Add),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::ConstExpr(Const::Num(1.0))),
)],
);
check_parser(
"a - b",
&[create_bin_op(
BinOp::Num(NumOp::Sub),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a-1",
&[create_bin_op(
BinOp::Num(NumOp::Sub),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::ConstExpr(Const::Num(1.0))),
)],
);
check_parser(
"a / b",
&[create_bin_op(
BinOp::Num(NumOp::Div),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a/2",
&[create_bin_op(
BinOp::Num(NumOp::Div),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::ConstExpr(Const::Num(2.0))),
)],
);
check_parser(
"a * b",
&[create_bin_op(
BinOp::Num(NumOp::Mul),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a*2",
&[create_bin_op(
BinOp::Num(NumOp::Mul),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::ConstExpr(Const::Num(2.0))),
)],
);
check_parser(
"a % b",
&[create_bin_op(
BinOp::Num(NumOp::Mod),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a%2",
&[create_bin_op(
BinOp::Num(NumOp::Mod),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::ConstExpr(Const::Num(2.0))),
)],
);
// Check complex numeric operations
check_parser(
"a + d*(b-3)+1",
&[create_bin_op(
BinOp::Num(NumOp::Add),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
create_bin_op(
BinOp::Num(NumOp::Add),
// FIXME: shouldn't the last addition be on the right?
Expr::new(ExprDef::ConstExpr(Const::Num(1.0))),
create_bin_op(
BinOp::Num(NumOp::Mul),
Expr::new(ExprDef::LocalExpr(String::from("d"))),
create_bin_op(
BinOp::Num(NumOp::Sub),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
Expr::new(ExprDef::ConstExpr(Const::Num(3.0))),
),
),
),
)],
);
// Check bitwise operations
check_parser(
"a & b",
&[create_bin_op(
BinOp::Bit(BitOp::And),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a&b",
&[create_bin_op(
BinOp::Bit(BitOp::And),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a | b",
&[create_bin_op(
BinOp::Bit(BitOp::Or),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a|b",
&[create_bin_op(
BinOp::Bit(BitOp::Or),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a ^ b",
&[create_bin_op(
BinOp::Bit(BitOp::Xor),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a^b",
&[create_bin_op(
BinOp::Bit(BitOp::Xor),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a << b",
&[create_bin_op(
BinOp::Bit(BitOp::Shl),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a<<b",
&[create_bin_op(
BinOp::Bit(BitOp::Shl),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a >> b",
&[create_bin_op(
BinOp::Bit(BitOp::Shr),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
check_parser(
"a>>b",
&[create_bin_op(
BinOp::Bit(BitOp::Shr),
Expr::new(ExprDef::LocalExpr(String::from("a"))),
Expr::new(ExprDef::LocalExpr(String::from("b"))),
)],
);
}
}

9
tests/js/test.js

@ -1,2 +1,7 @@
let a = "hello world"; let a = 5;
a; let b = 6;
let c = 8;
let d = 5;
let res = a + d * (b - 3) + 1;
res;

Loading…
Cancel
Save