Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  tests.rs   Sprache: unbekannt

 
use std::iter;

use crate::lexer::Lexer;
use crate::parser::Parser;
use crate::{parse_script, ParseOptions};
use ast::source_atom_set::SourceAtomSet;
use ast::source_slice_list::SourceSliceList;
use ast::{arena, source_location::SourceLocation, types::*};
use bumpalo::{self, Bump};
use generated_parser::{self, AstBuilder, ParseError, Result, TerminalId};
use std::cell::RefCell;
use std::rc::Rc;

#[cfg(all(feature = "unstable", test))]
mod benchmarks {
    extern crate test;

    use std::fs::File;
    use std::io::Read;
    use test::Bencher;

    use crate::lexer::Lexer;
    use crate::parse_script;

    #[bench]
    fn bench_parse_grammar(b: &mut Bencher) {
        let mut buffer = fs::read_to_string("../vue.js").expect("reading test file");
        b.iter(|| {
            let lexer = Lexer::new(buffer.chars());
            parse_script(lexer).unwrap();
        });
    }
}

trait IntoChunks<'a> {
    type Chunks: Iterator<Item = &'a str>;
    fn into_chunks(self) -> Self::Chunks;
}

impl<'a> IntoChunks<'a> for &'a str {
    type Chunks = iter::Once<&'a str>;
    fn into_chunks(self) -> Self::Chunks {
        iter::once(self)
    }
}

impl<'a> IntoChunks<'a> for &'a Vec<&'a str> {
    type Chunks = iter::Cloned<std::slice::Iter<'a, &'a str>>;
    fn into_chunks(self) -> Self::Chunks {
        self.iter().cloned()
    }
}

// Glue all the chunks together.  XXX TODO Once the lexer supports chunks,
// we'll reimplement this to feed the code to the lexer one chunk at a time.
fn chunks_to_string<'a, T: IntoChunks<'a>>(code: T) -> String {
    let mut buf = String::new();
    for chunk in code.into_chunks() {
        buf.push_str(chunk);
    }
    buf
}

fn try_parse<'alloc, 'source, Source>(
    allocator: &'alloc Bump,
    code: Source,
) -> Result<'alloc, arena::Box<'alloc, Script<'alloc>>>
where
    Source: IntoChunks<'source>,
{
    let buf = arena::alloc_str(allocator, &chunks_to_string(code));
    let options = ParseOptions::new();
    let atoms = Rc::new(RefCell::new(SourceAtomSet::new()));
    let slices = Rc::new(RefCell::new(SourceSliceList::new()));
    parse_script(allocator, &buf, &options, atoms, slices)
}

fn assert_parses<'alloc, T: IntoChunks<'alloc>>(code: T) {
    let allocator = &Bump::new();
    try_parse(allocator, code).unwrap();
}

fn assert_error<'alloc, T: IntoChunks<'alloc>>(code: T) {
    let allocator = &Bump::new();
    assert!(match try_parse(allocator, code).map_err(|e| *e) {
        Err(ParseError::NotImplemented(_)) => panic!("expected error, got NotImplemented"),
        Err(_) => true,
        Ok(ast) => panic!("assertion failed: SUCCESS error: {:?}", ast),
    });
}

fn assert_syntax_error<'alloc, T: IntoChunks<'alloc>>(code: T) {
    let allocator = &Bump::new();
    assert!(match try_parse(allocator, code).map_err(|e| *e) {
        Err(ParseError::SyntaxError(_)) => true,
        Err(other) => panic!("unexpected error: {:?}", other),
        Ok(ast) => panic!("assertion failed: SUCCESS error: {:?}", ast),
    });
}

fn assert_not_implemented<'alloc, T: IntoChunks<'alloc>>(code: T) {
    let allocator = &Bump::new();
    assert!(match try_parse(allocator, code).map_err(|e| *e) {
        Err(ParseError::NotImplemented(_)) => true,
        Err(other) => panic!("unexpected error: {:?}", other),
        Ok(ast) => panic!("assertion failed: SUCCESS error: {:?}", ast),
    });
}

fn assert_illegal_character<'alloc, T: IntoChunks<'alloc>>(code: T) {
    let allocator = &Bump::new();
    assert!(match try_parse(allocator, code).map_err(|e| *e) {
        Err(ParseError::IllegalCharacter(_)) => true,
        Err(other) => panic!("unexpected error: {:?}", other),
        Ok(ast) => panic!("assertion failed: SUCCESS error: {:?}", ast),
    });
}

fn assert_error_eq<'alloc, T: IntoChunks<'alloc>>(code: T, expected: ParseError) {
    let allocator = &Bump::new();
    let result = try_parse(allocator, code);
    assert!(result.is_err());
    assert_eq!(*result.unwrap_err(), expected);
}

fn assert_incomplete<'alloc, T: IntoChunks<'alloc>>(code: T) {
    let allocator = &Bump::new();
    let result = try_parse(allocator, code);
    assert!(result.is_err());
    assert_eq!(*result.unwrap_err(), ParseError::UnexpectedEnd);
}

// Assert that `left` and `right`, when parsed as ES Modules, consist of the
// same sequence of tokens (although possibly at different offsets).
fn assert_same_tokens<'alloc>(left: &str, right: &str) {
    let allocator = &Bump::new();
    let left_atoms = Rc::new(RefCell::new(SourceAtomSet::new()));
    let left_slices = Rc::new(RefCell::new(SourceSliceList::new()));
    let right_atoms = Rc::new(RefCell::new(SourceAtomSet::new()));
    let right_slices = Rc::new(RefCell::new(SourceSliceList::new()));
    let mut left_lexer = Lexer::new(
        allocator,
        left.chars(),
        left_atoms.clone(),
        left_slices.clone(),
    );
    let mut right_lexer = Lexer::new(
        allocator,
        right.chars(),
        right_atoms.clone(),
        right_slices.clone(),
    );

    let mut left_parser = Parser::new(
        AstBuilder::new(allocator, left_atoms, left_slices),
        generated_parser::START_STATE_MODULE,
    );
    let mut right_parser = Parser::new(
        AstBuilder::new(allocator, right_atoms, right_slices),
        generated_parser::START_STATE_MODULE,
    );

    loop {
        let left_token = left_lexer
            .next(&left_parser)
            .expect("error parsing left string");
        let right_token = right_lexer
            .next(&right_parser)
            .expect("error parsing right string");
        assert_eq!(
            left_token.terminal_id, right_token.terminal_id,
            "at offset {} in {:?} / {} in {:?}",
            left_token.loc.start, left, right_token.loc.start, right,
        );
        assert_eq!(
            left_token.value, right_token.value,
            "at offsets {} / {}",
            left_token.loc.start, right_token.loc.start
        );

        if left_token.terminal_id == TerminalId::End {
            break;
        }
        left_parser.write_token(left_token).unwrap();
        right_parser.write_token(right_token).unwrap();
    }
    left_parser.close(left_lexer.offset()).unwrap();
    right_parser.close(left_lexer.offset()).unwrap();
}

fn assert_can_close_after<'alloc, T: IntoChunks<'alloc>>(code: T) {
    let allocator = &Bump::new();
    let buf = chunks_to_string(code);
    let atoms = Rc::new(RefCell::new(SourceAtomSet::new()));
    let slices = Rc::new(RefCell::new(SourceSliceList::new()));
    let mut lexer = Lexer::new(allocator, buf.chars(), atoms.clone(), slices.clone());
    let mut parser = Parser::new(
        AstBuilder::new(allocator, atoms, slices),
        generated_parser::START_STATE_SCRIPT,
    );
    loop {
        let t = lexer.next(&parser).expect("lexer error");
        if t.terminal_id == TerminalId::End {
            break;
        }
        parser.write_token(t).unwrap();
    }
    assert!(parser.can_close());
}

fn assert_same_number(code: &str, expected: f64) {
    let allocator = &Bump::new();
    let script = try_parse(allocator, code).unwrap().unbox();
    match &script.statements[0] {
        Statement::ExpressionStatement(expression) => match &**expression {
            Expression::LiteralNumericExpression(num) => {
                assert_eq!(num.value, expected, "{}", code);
            }
            _ => panic!("expected LiteralNumericExpression"),
        },
        _ => panic!("expected ExpressionStatement"),
    }
}

#[test]
fn test_asi_at_end() {
    assert_parses("3 + 4");
    assert_syntax_error("3 4");
    assert_incomplete("3 +");
    assert_incomplete("{");
    assert_incomplete("{;");
}

#[test]
fn test_asi_at_block_end() {
    assert_parses("{ doCrimes() }");
    assert_parses("function f() { ok }");
}

#[test]
fn test_asi_after_line_terminator() {
    assert_parses(
        "switch (value) {
             case 1: break
             case 2: console.log('2');
         }",
    );
    assert_syntax_error("switch (value) { case 1: break case 2: console.log('2'); }");

    // "[T]he presence or absence of single-line comments does not affect the
    // process of automatic semicolon insertion[...]."
    // <https://tc39.es/ecma262/#sec-comments>
    assert_parses("x = 1 // line break here\ny = 2");
    assert_parses("x = 1 // line break here\r\ny = 2");
    assert_parses("x = 1 /* no line break in here */ //\ny = 2");
    assert_parses("x = 1<!-- line break here\ny = 2");

    assert_syntax_error("x = 1 /* no line break in here */ y = 2");
    assert_parses("x = 1 /* line break \n there */y = 2");
}

#[test]
fn test_asi_suppressed() {
    // The specification says ASI does not happen in the production
    // EmptyStatement : `;`.
    // TODO - assert_syntax_error("if (true)");
    assert_syntax_error("{ for (;;) }");

    // ASI does not happen in for(;;) loops.
    assert_syntax_error("for ( \n ; ) {}");
    assert_syntax_error("for ( ; \n ) {}");
    assert_syntax_error("for ( \n \n ) {}");
    assert_syntax_error("for (var i = 0 \n i < 9;   i++) {}");
    assert_syntax_error("for (var i = 0;   i < 9 \n i++) {}");
    assert_syntax_error("for (i = 0     \n i < 9;   i++) {}");
    assert_syntax_error("for (i = 0;       i < 9 \n i++) {}");
    assert_syntax_error("for (const i = 0 \n i < 9;   i++) {}");

    // ASI is suppressed in the production ClassElement[Yield, Await] : `;`
    // to prevent an infinite loop of ASI. lol
    assert_syntax_error("class Fail { \n +1; }");
}

#[test]
fn test_if_else() {
    assert_parses("if (x) f();");
    assert_incomplete("if (x)");
    assert_parses("if (x) f(); else g();");
    assert_incomplete("if (x) f(); else");
    assert_parses("if (x) if (y) g(); else h();");
    assert_parses("if (x) if (y) g(); else h(); else j();");
}

#[test]
fn test_lexer_decimal() {
    assert_parses("0.");
    assert_parses(".5");
    assert_syntax_error(".");
}

#[test]
fn test_numbers() {
    assert_same_number("0", 0.0);
    assert_same_number("1", 1.0);
    assert_same_number("10", 10.0);

    assert_error_eq("0a", ParseError::IllegalCharacter('a'));
    assert_error_eq("1a", ParseError::IllegalCharacter('a'));

    assert_error_eq("1.0a", ParseError::IllegalCharacter('a'));
    assert_error_eq(".0a", ParseError::IllegalCharacter('a'));
    assert_error_eq("1.a", ParseError::IllegalCharacter('a'));

    assert_same_number("1.0", 1.0);
    assert_same_number("1.", 1.0);
    assert_same_number("0.", 0.0);

    assert_same_number("1.0e0", 1.0);
    assert_same_number("1.e0", 1.0);
    assert_same_number(".0e0", 0.0);

    assert_same_number("1.0e+0", 1.0);
    assert_same_number("1.e+0", 1.0);
    assert_same_number(".0e+0", 0.0);

    assert_same_number("1.0e-0", 1.0);
    assert_same_number("1.e-0", 1.0);
    assert_same_number(".0e-0", 0.0);

    assert_error_eq("1.0e", ParseError::UnexpectedEnd);
    assert_error_eq("1.e", ParseError::UnexpectedEnd);
    assert_error_eq(".0e", ParseError::UnexpectedEnd);

    assert_error_eq("1.0e+", ParseError::UnexpectedEnd);
    assert_error_eq("1.0e-", ParseError::UnexpectedEnd);
    assert_error_eq(".0e+", ParseError::UnexpectedEnd);
    assert_error_eq(".0e-", ParseError::UnexpectedEnd);

    assert_same_number("1.0E0", 1.0);
    assert_same_number("1.E0", 1.0);
    assert_same_number(".0E0", 0.0);

    assert_same_number("1.0E+0", 1.0);
    assert_same_number("1.E+0", 1.0);
    assert_same_number(".0E+0", 0.0);

    assert_same_number("1.0E-0", 1.0);
    assert_same_number("1.E-0", 1.0);
    assert_same_number(".0E-0", 0.0);

    assert_error_eq("1.0E", ParseError::UnexpectedEnd);
    assert_error_eq("1.E", ParseError::UnexpectedEnd);
    assert_error_eq(".0E", ParseError::UnexpectedEnd);

    assert_error_eq("1.0E+", ParseError::UnexpectedEnd);
    assert_error_eq("1.0E-", ParseError::UnexpectedEnd);
    assert_error_eq(".0E+", ParseError::UnexpectedEnd);
    assert_error_eq(".0E-", ParseError::UnexpectedEnd);

    assert_same_number(".0", 0.0);
    assert_parses("");

    assert_same_number("0b0", 0.0);

    assert_same_number("0b1", 1.0);
    assert_same_number("0B01", 1.0);
    assert_error_eq("0b", ParseError::UnexpectedEnd);
    assert_error_eq("0b ", ParseError::IllegalCharacter(' '));
    assert_error_eq("0b2", ParseError::IllegalCharacter('2'));

    assert_same_number("0o0", 0.0);
    assert_same_number("0o7", 7.0);
    assert_same_number("0O01234567", 0o01234567 as f64);
    assert_error_eq("0o", ParseError::UnexpectedEnd);
    assert_error_eq("0o ", ParseError::IllegalCharacter(' '));
    assert_error_eq("0o8", ParseError::IllegalCharacter('8'));

    assert_same_number("0x0", 0.0);
    assert_same_number("0xf", 15.0);
    assert_not_implemented("0X0123456789abcdef");
    assert_not_implemented("0X0123456789ABCDEF");
    assert_error_eq("0x", ParseError::UnexpectedEnd);
    assert_error_eq("0x ", ParseError::IllegalCharacter(' '));
    assert_error_eq("0xg", ParseError::IllegalCharacter('g'));

    assert_parses("1..x");

    assert_same_number("1_1", 11.0);
    assert_same_number("0b1_1", 3.0);
    assert_same_number("0o1_1", 9.0);
    assert_same_number("0x1_1", 17.0);

    assert_same_number("1_1.1_1", 11.11);
    assert_same_number("1_1.1_1e+1_1", 11.11e11);

    assert_error_eq("1_", ParseError::UnexpectedEnd);
    assert_error_eq("1._1", ParseError::IllegalCharacter('_'));
    assert_error_eq("1.1_", ParseError::UnexpectedEnd);
    assert_error_eq("1.1e1_", ParseError::UnexpectedEnd);
    assert_error_eq("1.1e_1", ParseError::IllegalCharacter('_'));
}

#[test]
fn test_numbers_large() {
    assert_same_number("4294967295", 4294967295.0);
    assert_same_number("4294967296", 4294967296.0);
    assert_same_number("4294967297", 4294967297.0);

    assert_same_number("9007199254740991", 9007199254740991.0);
    assert_same_number("9007199254740992", 9007199254740992.0);
    assert_same_number("9007199254740993", 9007199254740992.0);

    assert_same_number("18446744073709553664", 18446744073709552000.0);
    assert_same_number("18446744073709553665", 18446744073709556000.0);

    assert_same_number("0b11111111111111111111111111111111", 4294967295.0);
    assert_same_number("0b100000000000000000000000000000000", 4294967296.0);
    assert_same_number("0b100000000000000000000000000000001", 4294967297.0);

    assert_same_number(
        "0b11111111111111111111111111111111111111111111111111111",
        9007199254740991.0,
    );
    assert_not_implemented("0b100000000000000000000000000000000000000000000000000000");

    assert_same_number("0o77777777777777777", 2251799813685247.0);
    assert_not_implemented("0o100000000000000000");

    assert_same_number("0xfffffffffffff", 4503599627370495.0);
    assert_not_implemented("0x10000000000000");

    assert_same_number("4.9406564584124654417656879286822e-324", 5e-324);
}

#[test]
fn test_bigint() {
    assert_not_implemented("0n");
    /*
        assert_parses("0n");
        assert_parses("1n");
        assert_parses("10n");

        assert_error_eq("0na", ParseError::IllegalCharacter('a'));
        assert_error_eq("1na", ParseError::IllegalCharacter('a'));

        assert_error_eq("1.0n", ParseError::IllegalCharacter('n'));
        assert_error_eq(".0n", ParseError::IllegalCharacter('n'));
        assert_error_eq("1.n", ParseError::IllegalCharacter('n'));

        assert_error_eq("1e0n", ParseError::IllegalCharacter('n'));
        assert_error_eq("1e+0n", ParseError::IllegalCharacter('n'));
        assert_error_eq("1e-0n", ParseError::IllegalCharacter('n'));
        assert_error_eq("1E0n", ParseError::IllegalCharacter('n'));
        assert_error_eq("1E+0n", ParseError::IllegalCharacter('n'));
        assert_error_eq("1E-0n", ParseError::IllegalCharacter('n'));

        assert_parses("0b0n");

        assert_parses("0b1n");
        assert_parses("0B01n");
        assert_error_eq("0bn", ParseError::IllegalCharacter('n'));

        assert_parses("0o0n");
        assert_parses("0o7n");
        assert_parses("0O01234567n");
        assert_error_eq("0on", ParseError::IllegalCharacter('n'));

        assert_parses("0x0n");
        assert_parses("0xfn");
        assert_parses("0X0123456789abcdefn");
        assert_parses("0X0123456789ABCDEFn");
        assert_error_eq("0xn", ParseError::IllegalCharacter('n'));

        assert_parses("1_1n");
        assert_parses("0b1_1n");
        assert_parses("0o1_1n");
        assert_parses("0x1_1n");

        assert_error_eq("1_1.1_1n", ParseError::IllegalCharacter('n'));
        assert_error_eq("1_1.1_1e1_1n", ParseError::IllegalCharacter('n'));

        assert_error_eq("1_n", ParseError::IllegalCharacter('n'));
        assert_error_eq("1.1_n", ParseError::IllegalCharacter('n'));
        assert_error_eq("1.1e1_n", ParseError::IllegalCharacter('n'));
    */
}

#[test]
fn test_arrow() {
    assert_parses("x => x");
    assert_parses("f = x => x;");
    assert_parses("(x, y) => [y, x]");
    assert_parses("f = (x, y) => {}");
    assert_syntax_error("(x, y) => {x: x, y: y}");
}

#[test]
fn test_illegal_character() {
    assert_illegal_character("\0");
    assert_illegal_character("—x;");
    assert_illegal_character("const ONE_THIRD = 1 ÷ 3;");
}

#[test]
fn test_identifier() {
    // U+00B7 MIDDLE DOT is an IdentifierPart.
    assert_parses("_·_ = {_·_:'·_·'};");

    // <ZWJ> and <ZWNJ> match IdentifierPart but not IdentifierStart.
    assert_parses("var x\u{200c};"); // <ZWNJ>
    assert_parses("_\u{200d}();"); // <ZWJ>
    assert_parses("_\u{200d}__();"); // <ZWJ>
    assert_parses("_\u{200d}\u{200c}();"); // <ZWJ>
    assert_illegal_character("var \u{200c};"); // <ZWNJ>
    assert_illegal_character("x = \u{200d};"); // <ZWJ>

    // Other_ID_Start for backward compat.
    assert_parses("\u{309B}();");
    assert_parses("\u{309C}();");
    assert_parses("_\u{309B}();");
    assert_parses("_\u{309C}();");

    // Non-BMP.
    assert_parses("\u{10000}();");
    assert_parses("_\u{10000}();");
    assert_illegal_character("\u{1000c}();");
    assert_illegal_character("_\u{1000c}();");
}

#[test]
fn test_regexp() {
    assert_parses(r"/\w/");
    assert_parses("/[A-Z]/");
    assert_parses("/[//]/");
    assert_parses("/a*a/");
    assert_parses("/**//x*/");
    assert_same_tokens("/**//x*/", "/x*/");
    assert_parses("{} /x/");
    assert_parses("of / 2");
}

#[test]
fn test_html_comments() {
    assert_same_tokens("x<!--y;", "x");
    assert_same_tokens("x<!-y;", "x < ! - y ;");
    assert_same_tokens("x<!y", "x < ! y");

    assert_same_tokens("--> hello world\nok", "ok");
    assert_same_tokens("/* ignore */ --> also ignore\nok", "ok");
    assert_same_tokens("/* ignore *//**/--> also ignore\nok", "ok");
    assert_same_tokens("x-->y\nz", "x -- > y\nz");
}

#[test]
fn test_incomplete_comments() {
    assert_error("/*");
    assert_error("/* hello world");
    assert_error("/* hello world *");

    assert_parses(&vec!["/* hello\n", " world */"]);
    assert_parses(&vec!["// oawfeoiawj", "ioawefoawjie"]);
    assert_parses(&vec!["// oawfeoiawj", "ioawefoawjie\n ok();"]);
    assert_parses(&vec!["// oawfeoiawj", "ioawefoawjie", "jiowaeawojefiw"]);
    assert_parses(&vec![
        "// oawfeoiawj",
        "ioawefoawjie",
        "jiowaeawojefiw\n ok();",
    ]);
}

#[test]
fn test_strings() {
    assert_parses("f(\"\",\"\")");
    assert_parses("f(\"\")");
    assert_parses("(\"\")");
    assert_parses("f('','')");
    assert_parses("f('')");
    assert_parses("('')");
}

#[test]
fn test_awkward_chunks() {
    assert_parses(&vec!["const", "ructor.length = 1;"]);
    assert_parses(&vec!["const", " x = 1;"]);

    // Try feeding one character at a time to the parser.
    let chars: Vec<&str> = "function f() { ok(); }".split("").collect();
    assert_parses(&chars);

    // XXX TODO
    //assertEqual(
    //    self.parse(&vec!["/xyzzy/", "g;"]),
    //    ('Script',
    //     ('ScriptBody',
    //      ('StatementList 0',
    //       ('ExpressionStatement',
    //        ('PrimaryExpression 10', '/xyzzy/g'))))));

    let allocator = &Bump::new();
    let actual = try_parse(allocator, &vec!["x/", "=2;"]).unwrap();
    let atoms = Rc::new(RefCell::new(SourceAtomSet::new()));
    let expected = Script {
        directives: arena::Vec::new_in(allocator),
        statements: bumpalo::vec![
            in allocator;
            Statement::ExpressionStatement(arena::alloc(
                allocator,
                Expression::CompoundAssignmentExpression {
                    operator: CompoundAssignmentOperator::Div {
                        loc: SourceLocation::new(1, 3),
                    },
                    binding: SimpleAssignmentTarget::AssignmentTargetIdentifier(
                        AssignmentTargetIdentifier {
                            name: Identifier {
                                value: atoms.borrow_mut().insert("x"),
                                loc: SourceLocation::new(0, 1),
                            },
                            loc: SourceLocation::new(0, 1),
                        },
                    ),
                    expression: arena::alloc(
                        allocator,
                        Expression::LiteralNumericExpression(NumericLiteral {
                            value: 2.0,
                            loc: SourceLocation::new(3, 4),
                        }),
                    ),
                    loc: SourceLocation::new(0, 4),
                },
            ))
        ],
        loc: SourceLocation::new(0, 4),
    };
    assert_eq!(format!("{:?}", actual), format!("{:?}", expected));
}

#[test]
fn test_can_close() {
    let empty: Vec<&str> = vec![];
    assert_can_close_after(&empty);
    assert_can_close_after("");
    assert_can_close_after("2 + 2;\n");
    assert_can_close_after("// seems ok\n");
}

#[test]
fn test_regex() {
    assert_parses("/x/");
    assert_parses("x = /x/");
    assert_parses("x = /x/g");

    // FIXME: Unexpected flag
    // assert_parses("x = /x/wow_flags_can_be_$$anything$$");
    assert_not_implemented("x = /x/wow_flags_can_be_$$anything$$");

    // TODO: Should the lexer running out of input throw an incomplete error, or a lexer error?
    assert_error_eq("/x", ParseError::UnterminatedRegExp);
    assert_incomplete("x = //"); // comment
    assert_error_eq("x = /*/", ParseError::UnterminatedMultiLineComment); /*/ comment */
    assert_error_eq("x =/= 2", ParseError::UnterminatedRegExp);
    assert_parses("x /= 2");
    assert_parses("x = /[]/");
    assert_parses("x = /[^x]/");
    assert_parses("x = /+=351*/");
    assert_parses("x = /^\\s*function (\\w+)/;");
    assert_parses("const regexp = /this is fine: [/] dont @ me/;");
}

#[test]
fn test_arrow_parameters() {
    assert_error_eq(
        "({a:a, ...b, c:c}) => {}",
        ParseError::ObjectPatternWithNonFinalRest,
    );
    assert_error_eq(
        "(a, [...zero, one]) => {}",
        ParseError::ArrayPatternWithNonFinalRest,
    );
    assert_error_eq(
        "(a, {items: [...zero, one]}) => {}",
        ParseError::ArrayPatternWithNonFinalRest,
    );
}

#[test]
fn test_invalid_assignment_targets() {
    assert_syntax_error("2 + 2 = x;");
    assert_error_eq("(2 + 2) = x;", ParseError::InvalidAssignmentTarget);
    assert_error_eq("++-x;", ParseError::InvalidAssignmentTarget);
    assert_error_eq("(x && y)--;", ParseError::InvalidAssignmentTarget);
}

#[test]
fn test_can_close_with_asi() {
    assert_can_close_after("2 + 2\n");
}

#[test]
fn test_conditional_keywords() {
    // property names
    assert_parses("const obj = {if: 3, function: 4};");
    assert_parses("const obj = {true: 1, false: 0, null: NaN};");
    assert_parses("assert(obj.if == 3);");
    assert_parses("assert(obj.true + obj.false + obj.null == NaN);");

    // method names
    assert_parses(
        "
        class C {
            if() {}
            function() {}
        }
        ",
    );

    // FIXME: let (multitoken lookahead):
    assert_not_implemented("let a = 1;");
    /*
    // let as identifier
    assert_parses("var let = [new Date];");
    // let as keyword, then identifier
    assert_parses("let v = let;");
    // `let .` -> ExpressionStatement
    assert_parses("let.length;");
    // `let [` -> LexicalDeclaration
    assert_syntax_error("let[0].getYear();");
     */

    assert_parses(
        "
        var of = [1, 2, 3];
        for (of of of) console.log(of);  // logs 1, 2, 3
        ",
    );

    // Not implemented:
    // assert_parses("var of, let, private, target;");

    assert_parses("class X { get y() {} }");

    // Not implemented:
    // assert_parses("async: { break async; }");

    assert_parses("var get = { get get() {}, set get(v) {}, set: 3 };");

    // Not implemented (requires hack; grammar is not LR(1)):
    // assert_parses("for (async of => {};;) {}");
    // assert_parses("for (async of []) {}");
}

#[test]
fn test_async_arrows() {
    // FIXME: async (multiple lookahead)
    assert_not_implemented("const a = async a => 1;");
    /*
    assert_parses("let f = async arg => body;");
    assert_parses("f = async (a1, a2) => {};");
    assert_parses("f = async (a1 = b + c, ...a2) => {};");

    assert_error_eq("f = async (a, b + c) => {};", ParseError::InvalidParameter);
    assert_error_eq(
        "f = async (...a1, a2) => {};",
        ParseError::ArrowParametersWithNonFinalRest,
    );
    assert_error_eq("obj.async() => {}", ParseError::ArrowHeadInvalid);
    */

    assert_error_eq("foo(a, b) => {}", ParseError::ArrowHeadInvalid);
}

#[test]
fn test_binary() {
    assert_parses("1 == 2");
    assert_parses("1 != 2");
    assert_parses("1 === 2");
    assert_parses("1 !== 2");
    assert_parses("1 < 2");
    assert_parses("1 <= 2");
    assert_parses("1 > 2");
    assert_parses("1 >= 2");
    assert_parses("1 in 2");
    assert_parses("1 instanceof 2");
    assert_parses("1 << 2");
    assert_parses("1 >> 2");
    assert_parses("1 >>> 2");
    assert_parses("1 + 2");
    assert_parses("1 - 2");
    assert_parses("1 * 2");
    assert_parses("1 / 2");
    assert_parses("1 % 2");
    assert_parses("1 ** 2");
    assert_parses("1 , 2");
    assert_parses("1 || 2");
    assert_parses("1 && 2");
    assert_parses("1 | 2");
    assert_parses("1 ^ 2");
    assert_parses("1 & 2");
}

#[test]
fn test_coalesce() {
    assert_parses("const f = options.prop ?? 0;");
    assert_syntax_error("if (options.prop ?? 0 || options.prop > 1000) {}");
}

#[test]
fn test_no_line_terminator_here() {
    // Parse `code` as a Script and compute some function of the resulting AST.
    fn parse_then<F, R>(code: &str, f: F) -> R
    where
        F: FnOnce(&Script) -> R,
    {
        let allocator = &Bump::new();
        match try_parse(allocator, code) {
            Err(err) => {
                panic!("Failed to parse code {:?}: {}", code, err);
            }
            Ok(script) => f(&*script),
        }
    }

    // Parse `code` as a Script and return the number of top-level
    // StatementListItems.
    fn count_items(code: &str) -> usize {
        parse_then(code, |script| script.statements.len())
    }

    // Without a newline, labelled `break` in loop.  But a line break changes
    // the meaning -- then it's a plain `break` statement, followed by
    // ExpressionStatement `LOOP;`
    assert_eq!(count_items("LOOP: while (true) break LOOP;"), 1);
    assert_eq!(count_items("LOOP: while (true) break \n LOOP;"), 2);

    // The same, but for `continue`.
    assert_eq!(count_items("LOOP: while (true) continue LOOP;"), 1);
    assert_eq!(count_items("LOOP: while (true) continue \n LOOP;"), 2);

    // Parse `code` as a Script, expected to contain a single function
    // declaration, and return the number of statements in the function body.
    fn count_statements_in_function(code: &str) -> usize {
        parse_then(code, |script| {
            assert_eq!(
                script.statements.len(),
                1,
                "expected function declaration, got {:?}",
                script
            );
            match &script.statements[0] {
                Statement::FunctionDeclaration(func) => func.body.statements.len(),
                _ => panic!("expected function declaration, got {:?}", script),
            }
        })
    }

    assert_eq!(
        count_statements_in_function("function f() { return x; }"),
        1
    );
    assert_eq!(
        count_statements_in_function("function f() { return\n x; }"),
        2
    );

    assert_parses("x++");
    assert_incomplete("x\n++");

    assert_parses("throw fit;");
    assert_syntax_error("throw\nfit;");

    // Alternative ways of spelling LineTerminator
    assert_syntax_error("throw//\nfit;");
    assert_syntax_error("throw/*\n*/fit;");
    assert_syntax_error("throw\rfit;");
    assert_syntax_error("throw\r\nfit;");
}

[ Dauer der Verarbeitung: 0.33 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge