package parser import ( "errors" "math/big" "regexp" "strings" "testing" "apigo.cc/ai/ai/goja/ast" "apigo.cc/ai/ai/goja/file" "apigo.cc/ai/ai/goja/token" "apigo.cc/ai/ai/goja/unistring" ) func firstErr(err error) error { switch err := err.(type) { case ErrorList: return err[0] } return err } var matchBeforeAfterSeparator = regexp.MustCompile(`(?m)^[ \t]*---$`) func testParse(src string) (parser *_parser, program *ast.Program, err error) { defer func() { if tmp := recover(); tmp != nil { switch tmp := tmp.(type) { case string: if strings.HasPrefix(tmp, "SyntaxError:") { parser = nil program = nil err = errors.New(tmp) return } } panic(tmp) } }() parser = newParser("", src) program, err = parser.parse() return } func TestParseFile(t *testing.T) { tt(t, func() { _, err := ParseFile(nil, "", `/abc/`, 0) is(err, nil) _, err = ParseFile(nil, "", `/(?!def)abc/`, IgnoreRegExpErrors) is(err, nil) _, err = ParseFile(nil, "", `/(?!def)abc/; return`, IgnoreRegExpErrors) is(err, "(anonymous): Line 1:15 Illegal return statement") }) } func TestParseFunction(t *testing.T) { tt(t, func() { test := func(prm, bdy string, expect interface{}) *ast.FunctionLiteral { function, err := ParseFunction(prm, bdy) is(firstErr(err), expect) return function } test("a, b,c,d", "", nil) test("a, b;,c,d", "", "(anonymous): Line 1:15 Unexpected token ;") test("this", "", "(anonymous): Line 1:11 Unexpected token this") test("a, b, c, null", "", "(anonymous): Line 1:20 Unexpected token null") test("a, b,c,d", "return;", nil) test("a, b,c,d", "break;", "(anonymous): Line 2:1 Illegal break statement") test("a, b,c,d", "{}", nil) }) } func TestParserErr(t *testing.T) { tt(t, func() { test := func(input string, expect interface{}) (*ast.Program, *_parser) { parser := newParser("", input) program, err := parser.parse() is(firstErr(err), expect) return program, parser } test("", nil) program, parser := test(` var abc; break; do { } while(true); `, "(anonymous): Line 3:9 Illegal break statement") { stmt := program.Body[1].(*ast.BadStatement) is(parser.position(stmt.From).Column, 9) is(parser.position(stmt.To).Column, 16) is(parser.slice(stmt.From, stmt.To), "break; ") } s := string([]byte{0x22, 0x25, 0x21, 0x63, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3d, 0x25, 0x63, 0x25, 0x9c, 0x29, 0x25, 0x21, 0x5c, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3d, 0x5c, 0xe2, 0x80, 0xa9, 0x29, 0x78, 0x39, 0x63, 0x22}) test(s, `(anonymous): Line 1:16 Invalid UTF-8 character`) test("{", "(anonymous): Line 1:2 Unexpected end of input") test("}", "(anonymous): Line 1:1 Unexpected token }") test("3ea", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3in", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3in []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3e", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3e+", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3e-", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3x", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3x0", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("0x", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("09", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("018", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("01.0", "(anonymous): Line 1:3 Unexpected number") test(".0.9", "(anonymous): Line 1:3 Unexpected number") test("0o3e1", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("01a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("0x3in[]", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\"Hello\nWorld\"", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\u203f = 10", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("x\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("x\\\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("x\\u005c", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("x\\u002a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("x\\\\u002a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("/\n", "(anonymous): Line 1:1 Invalid regular expression: missing /") test("0 = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("func() = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("(1 + 1) = 2", "(anonymous): Line 1:2 Invalid left-hand side in assignment") test("1++", "(anonymous): Line 1:2 Invalid left-hand side in assignment") test("1--", "(anonymous): Line 1:2 Invalid left-hand side in assignment") test("--1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("for((1 + 1) in abc) def();", "(anonymous): Line 1:1 Invalid left-hand side in for-in or for-of") test("[", "(anonymous): Line 1:2 Unexpected end of input") test("[,", "(anonymous): Line 1:3 Unexpected end of input") test("1 + {", "(anonymous): Line 1:6 Unexpected end of input") test("1 + { abc:abc", "(anonymous): Line 1:14 Unexpected end of input") test("1 + { abc:abc,", "(anonymous): Line 1:15 Unexpected end of input") test("var abc = /\n/", "(anonymous): Line 1:11 Invalid regular expression: missing /") test("var abc = \"\n", "(anonymous): Line 1:11 Unexpected token ILLEGAL") test("var if = 0", "(anonymous): Line 1:5 Unexpected token if") test("abc + 0 = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("+abc = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("1 + (", "(anonymous): Line 1:6 Unexpected end of input") test("\n\n\n{", "(anonymous): Line 4:2 Unexpected end of input") test("\n/* Some multiline\ncomment */\n)", "(anonymous): Line 4:1 Unexpected token )") test("+1 ** 2", "(anonymous): Line 1:4 Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence") test("typeof 1 ** 2", "(anonymous): Line 1:10 Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence") // TODO //{ set 1 } //{ get 2 } //({ set: s(if) { } }) //({ set s(.) { } }) //({ set: s() { } }) //({ set: s(a, b) { } }) //({ get: g(d) { } }) //({ get i() { }, i: 42 }) //({ i: 42, get i() { } }) //({ set i(x) { }, i: 42 }) //({ i: 42, set i(x) { } }) //({ get i() { }, get i() { } }) //({ set i(x) { }, set i(x) { } }) test("function abc(if) {}", "(anonymous): Line 1:14 Unexpected token if") test("function abc(true) {}", "(anonymous): Line 1:14 Unexpected token true") test("function abc(false) {}", "(anonymous): Line 1:14 Unexpected token false") test("function abc(null) {}", "(anonymous): Line 1:14 Unexpected token null") test("function null() {}", "(anonymous): Line 1:10 Unexpected token null") test("function true() {}", "(anonymous): Line 1:10 Unexpected token true") test("function false() {}", "(anonymous): Line 1:10 Unexpected token false") test("function if() {}", "(anonymous): Line 1:10 Unexpected token if") test("a b;", "(anonymous): Line 1:3 Unexpected identifier") test("if.a", "(anonymous): Line 1:3 Unexpected token .") test("a if", "(anonymous): Line 1:3 Unexpected token if") test("a class", "(anonymous): Line 1:3 Unexpected token class") test("break\n", "(anonymous): Line 1:1 Illegal break statement") test("break 1;", "(anonymous): Line 1:7 Unexpected number") test("for (;;) { break 1; }", "(anonymous): Line 1:18 Unexpected number") test("continue\n", "(anonymous): Line 1:1 Illegal continue statement") test("continue 1;", "(anonymous): Line 1:10 Unexpected number") test("for (;;) { continue 1; }", "(anonymous): Line 1:21 Unexpected number") test("throw", "(anonymous): Line 1:1 Unexpected end of input") test("throw;", "(anonymous): Line 1:6 Unexpected token ;") test("throw \n", "(anonymous): Line 1:1 Unexpected end of input") test("for (var abc, def in {});", "(anonymous): Line 1:19 Unexpected token in") test("for ((abc in {});;);", nil) test("for ((abc in {}));", "(anonymous): Line 1:17 Unexpected token )") test("for (+abc in {});", "(anonymous): Line 1:1 Invalid left-hand side in for-in or for-of") test("if (false)", "(anonymous): Line 1:11 Unexpected end of input") test("if (false) abc(); else", "(anonymous): Line 1:23 Unexpected end of input") test("do", "(anonymous): Line 1:3 Unexpected end of input") test("while (false)", "(anonymous): Line 1:14 Unexpected end of input") test("for (;;)", "(anonymous): Line 1:9 Unexpected end of input") test("with (abc)", "(anonymous): Line 1:11 Unexpected end of input") test("try {}", "(anonymous): Line 1:1 Missing catch or finally after try") test("try {} catch () {}", "(anonymous): Line 1:15 Unexpected token )") test("\u203f = 1", "(anonymous): Line 1:1 Unexpected token ILLEGAL") // TODO // const x = 12, y; // const x, y = 12; // const x; // if(true) let a = 1; // if(true) const a = 1; test(`new abc()."def"`, "(anonymous): Line 1:11 Unexpected string") test("/*", "(anonymous): Line 1:3 Unexpected end of input") test("/**", "(anonymous): Line 1:4 Unexpected end of input") test("/*\n\n\n", "(anonymous): Line 4:1 Unexpected end of input") test("/*\n\n\n*", "(anonymous): Line 4:2 Unexpected end of input") test("/*abc", "(anonymous): Line 1:6 Unexpected end of input") test("/*abc *", "(anonymous): Line 1:9 Unexpected end of input") test("\n]", "(anonymous): Line 2:1 Unexpected token ]") test("\r\n]", "(anonymous): Line 2:1 Unexpected token ]") test("\n\r]", "(anonymous): Line 3:1 Unexpected token ]") test("//\r\n]", "(anonymous): Line 2:1 Unexpected token ]") test("//\n\r]", "(anonymous): Line 3:1 Unexpected token ]") test("/abc\\\n/", "(anonymous): Line 1:1 Invalid regular expression: missing /") test("//\r \n]", "(anonymous): Line 3:1 Unexpected token ]") test("/*\r\n*/]", "(anonymous): Line 2:3 Unexpected token ]") test("/*\r \n*/]", "(anonymous): Line 3:3 Unexpected token ]") test("\\\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\\u005c", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\\abc", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\\u0000", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\\u200c = []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\\u200D = []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test(`"\`, "(anonymous): Line 1:1 Unexpected token ILLEGAL") test(`"\u`, "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("return", "(anonymous): Line 1:1 Illegal return statement") test("continue", "(anonymous): Line 1:1 Illegal continue statement") test("break", "(anonymous): Line 1:1 Illegal break statement") test("switch (abc) { default: continue; }", "(anonymous): Line 1:25 Illegal continue statement") test("do { abc } *", "(anonymous): Line 1:12 Unexpected token *") test("while (true) { break abc; }", "(anonymous): Line 1:16 Undefined label 'abc'") test("while (true) { continue abc; }", "(anonymous): Line 1:16 Undefined label 'abc'") test("abc: while (true) { (function(){ break abc; }); }", "(anonymous): Line 1:34 Undefined label 'abc'") test("abc: while (true) { (function(){ abc: break abc; }); }", nil) test("abc: while (true) { (function(){ continue abc; }); }", "(anonymous): Line 1:34 Undefined label 'abc'") test(`abc: if (0) break abc; else {}`, nil) test(`abc: if (0) { break abc; } else {}`, nil) test(`abc: if (0) { break abc } else {}`, nil) test("abc: while (true) { abc: while (true) {} }", "(anonymous): Line 1:21 Label 'abc' already exists") test(`if(0) { do { } while(0) } else { do { } while(0) }`, nil) test(`if(0) do { } while(0); else do { } while(0)`, nil) test("_: _: while (true) {]", "(anonymous): Line 1:4 Label '_' already exists") test("_:\n_:\nwhile (true) {]", "(anonymous): Line 2:1 Label '_' already exists") test("_:\n _:\nwhile (true) {]", "(anonymous): Line 2:4 Label '_' already exists") test("function(){}", "(anonymous): Line 1:9 Unexpected token (") test("\n/*/", "(anonymous): Line 2:4 Unexpected end of input") test("/*/.source", "(anonymous): Line 1:11 Unexpected end of input") test("var class", "(anonymous): Line 1:5 Unexpected token class") test("var if", "(anonymous): Line 1:5 Unexpected token if") test("object Object", "(anonymous): Line 1:8 Unexpected identifier") test("[object Object]", "(anonymous): Line 1:9 Unexpected identifier") test("\\u0xyz", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test(`for (var abc, def in {}) {}`, "(anonymous): Line 1:19 Unexpected token in") test(`for (abc, def in {}) {}`, "(anonymous): Line 1:1 Invalid left-hand side in for-in or for-of") test(`for (var abc=def, ghi=("abc" in {}); true;) {}`, nil) { // Semicolon insertion test("this\nif (1);", nil) test("while (1) { break\nif (1); }", nil) test("throw\nif (1);", "(anonymous): Line 1:1 Illegal newline after throw") test("(function(){ return\nif (1); })", nil) test("while (1) { continue\nif (1); }", nil) test("debugger\nif (1);", nil) } { // Reserved words test("class", "(anonymous): Line 1:6 Unexpected end of input") test("abc.class = 1", nil) test("var class;", "(anonymous): Line 1:5 Unexpected token class") test("const", "(anonymous): Line 1:6 Unexpected end of input") test("abc.const = 1", nil) test("var const;", "(anonymous): Line 1:5 Unexpected token const") test("enum", "(anonymous): Line 1:1 Unexpected reserved word") test("abc.enum = 1", nil) test("var enum;", "(anonymous): Line 1:5 Unexpected reserved word") test("export", "(anonymous): Line 1:1 Unexpected reserved word") test("abc.export = 1", nil) test("var export;", "(anonymous): Line 1:5 Unexpected reserved word") test("extends", "(anonymous): Line 1:1 Unexpected token extends") test("abc.extends = 1", nil) test("var extends;", "(anonymous): Line 1:5 Unexpected token extends") test("import", "(anonymous): Line 1:1 Unexpected reserved word") test("abc.import = 1", nil) test("var import;", "(anonymous): Line 1:5 Unexpected reserved word") test("super", "(anonymous): Line 1:1 'super' keyword unexpected here") test("abc.super = 1", nil) test("var super;", "(anonymous): Line 1:5 Unexpected token super") test(` obj = { aaa: 1 bbb: "string" };`, "(anonymous): Line 4:6 Unexpected identifier") test("{}", nil) test("{a: 1}", nil) test("{a: 1,}", "(anonymous): Line 1:7 Unexpected token }") test("{a: 1, b: 2}", "(anonymous): Line 1:9 Unexpected token :") test("{a: 1, b: 2,}", "(anonymous): Line 1:9 Unexpected token :") test(`let f = () => new import('');`, "(anonymous): Line 1:19 Unexpected reserved word") } { // Reserved words (strict) test(`implements`, nil) test(`abc.implements = 1`, nil) test(`var implements;`, nil) test(`interface`, nil) test(`abc.interface = 1`, nil) test(`var interface;`, nil) test(`let`, nil) test(`abc.let = 1`, nil) test(`var let;`, nil) test(`package`, nil) test(`abc.package = 1`, nil) test(`var package;`, nil) test(`private`, nil) test(`abc.private = 1`, nil) test(`var private;`, nil) test(`protected`, nil) test(`abc.protected = 1`, nil) test(`var protected;`, nil) test(`public`, nil) test(`abc.public = 1`, nil) test(`var public;`, nil) test(`static`, nil) test(`abc.static = 1`, nil) test(`var static;`, nil) test(`yield`, nil) test(`abc.yield = 1`, nil) test(`var yield;`, nil) } test(`0, { get a(param = null) {} };`, "(anonymous): Line 1:11 Getter must not have any formal parameters.") test(`let{f(`, "(anonymous): Line 1:7 Unexpected end of input") test("`", "(anonymous): Line 1:2 Unexpected end of input") test(" `", "(anonymous): Line 1:3 Unexpected end of input") test("` ", "(anonymous): Line 1:3 Unexpected end of input") test(`var{..(`, "(anonymous): Line 1:7 Unexpected token ILLEGAL") test(`var{get..(`, "(anonymous): Line 1:10 Unexpected token ILLEGAL") test(`var{set..(`, "(anonymous): Line 1:10 Unexpected token ILLEGAL") test(`(0 ?? 0 || true)`, "(anonymous): Line 1:9 Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses") test(`(a || b ?? c)`, "(anonymous): Line 1:9 Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses") test(`2 ?? 2 && 3 + 3`, "(anonymous): Line 1:3 Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses") test(` class C { st\u0061tic m() {} } `, "(anonymous): Line 3:25 Unexpected identifier") }) } func TestParser(t *testing.T) { tt(t, func() { test := func(source string, chk interface{}) *ast.Program { _, program, err := testParse(source) is(firstErr(err), chk) return program } test(`new (() => {});`, nil) test(` abc -- [] `, "(anonymous): Line 3:13 Invalid left-hand side in assignment") test(` abc-- [] `, nil) test("1\n[]\n", "(anonymous): Line 2:2 Unexpected token ]") test(` function abc() { } abc() `, nil) test("", nil) test("//", nil) test("/* */", nil) test("/** **/", nil) test("/*****/", nil) test("/*", "(anonymous): Line 1:3 Unexpected end of input") test("#", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("/**/#", "(anonymous): Line 1:5 Unexpected token ILLEGAL") test("new +", "(anonymous): Line 1:5 Unexpected token +") program := test(";", nil) is(len(program.Body), 1) is(program.Body[0].(*ast.EmptyStatement).Semicolon, file.Idx(1)) program = test(";;", nil) is(len(program.Body), 2) is(program.Body[0].(*ast.EmptyStatement).Semicolon, file.Idx(1)) is(program.Body[1].(*ast.EmptyStatement).Semicolon, file.Idx(2)) program = test("1.2", nil) is(len(program.Body), 1) is(program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.NumberLiteral).Literal, "1.2") program = test("/* */1.2", nil) is(len(program.Body), 1) is(program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.NumberLiteral).Literal, "1.2") program = test("\n", nil) is(len(program.Body), 0) test(` if (0) { abc = 0 } else abc = 0 `, nil) test("if (0) abc = 0 else abc = 0", "(anonymous): Line 1:16 Unexpected token else") test(` if (0) { abc = 0 } else abc = 0 `, nil) test(` if (0) { abc = 1 } else { } `, nil) test(` do { } while (true) `, nil) test(` try { } finally { } `, nil) test(` try { } catch (abc) { } finally { } `, nil) test(` try { } catch (abc) { } finally { } `, nil) test(`try {} catch (abc) {} finally {}`, nil) test("try {} catch {}", nil) test(` do { do { } while (0) } while (0) `, nil) test(` (function(){ try { if ( 1 ) { return 1 } return 0 } finally { } })() `, nil) test("abc = ''\ndef", nil) test("abc = 1\ndef", nil) test("abc = Math\ndef", nil) test(`"\'"`, nil) test(` abc = function(){ } abc = 0 `, nil) test("abc.null = 0", nil) test("0x41", nil) test(`"\d"`, nil) test(`(function(){return this})`, nil) test(` Object.defineProperty(Array.prototype, "0", { value: 100, writable: false, configurable: true }); abc = [101]; abc.hasOwnProperty("0") && abc[0] === 101; `, nil) test(`new abc()`, nil) test(`new {}`, nil) test(` limit = 4 result = 0 while (limit) { limit = limit - 1 if (limit) { } else { break } result = result + 1 } `, nil) test(` while (0) { if (0) { continue } } `, nil) test("var \u0061\u0062\u0063 = 0", nil) // 7_3_1 test("var test7_3_1\nabc = 66;", nil) test("var test7_3_1\u2028abc = 66;", nil) // 7_3_3 test("//\u2028 =;", "(anonymous): Line 2:2 Unexpected token =") // 7_3_10 test("var abc = \u2029;", "(anonymous): Line 2:1 Unexpected token ;") test("var abc = \\u2029;", "(anonymous): Line 1:11 Unexpected token ILLEGAL") test("var \\u0061\\u0062\\u0063 = 0;", nil) test("'", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("'\nstr\ning\n'", "(anonymous): Line 1:1 Unexpected token ILLEGAL") // S7.6_A4.3_T1 test(`var $\u0030 = 0;`, nil) // S7.6.1.1_A1.1 test(`switch = 1`, "(anonymous): Line 1:8 Unexpected token =") // S7.8.3_A2.1_T1 test(`.0 === 0.0`, nil) // 7.8.5-1 test("var regExp = /\\\rn/;", "(anonymous): Line 1:14 Invalid regular expression: missing /") // S7.8.5_A1.1_T2 test("var regExp = /=/;", nil) // S7.8.5_A1.2_T1 test("/*/", "(anonymous): Line 1:4 Unexpected end of input") // Sbp_7.9_A9_T3 test(` do { ; } while (false) true `, nil) // S7.9_A10_T10 test(` {a:1 } 3 `, nil) test(` abc ++def `, nil) // S7.9_A5.2_T1 test(` for(false;false ) { break; } `, "(anonymous): Line 3:13 Unexpected token )") // S7.9_A9_T8 test(` do {}; while (false) `, "(anonymous): Line 2:18 Unexpected token ;") // S8.4_A5 test(` "x\0y" `, nil) // S9.3.1_A6_T1 test(` 10e10000 `, nil) // 10.4.2-1-5 test(` "abc\ def" `, nil) test("'\\\n'", nil) test("'\\\r\n'", nil) //// 11.13.1-1-1 test("42 = 42;", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("s &^= 42;", "(anonymous): Line 1:4 Unexpected token ^=") // S11.13.2_A4.2_T1.3 test(` abc /= "1" `, nil) // 12.1-1 test(` try{};catch(){} `, "(anonymous): Line 2:13 Missing catch or finally after try") // 12.1-3 test(` try{};finally{} `, "(anonymous): Line 2:13 Missing catch or finally after try") // S12.6.3_A11.1_T3 test(` while (true) { break abc; } `, "(anonymous): Line 3:17 Undefined label 'abc'") // S15.3_A2_T1 test(`var x / = 1;`, "(anonymous): Line 1:7 Unexpected token /") test(` function abc() { if (0) return; else { } } `, nil) test("//\u2028 var =;", "(anonymous): Line 2:6 Unexpected token =") test(` throw {} `, "(anonymous): Line 2:13 Illegal newline after throw") // S7.6.1.1_A1.11 test(` function = 1 `, "(anonymous): Line 2:22 Unexpected token =") // S7.8.3_A1.2_T1 test(`0e1`, nil) test("abc = 1; abc\n++", "(anonymous): Line 2:3 Unexpected end of input") // --- test("({ get abc() {} })", nil) test(`for (abc.def in {}) {}`, nil) test(`while (true) { break }`, nil) test(`while (true) { continue }`, nil) test(`abc=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?)|(.{0,2}\/{1}))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/,def=/^(?:(\w+:)\/{2})|(.{0,2}\/{1})?([/.]*?(?:[^?]+)?\/?)?$/`, nil) test(`(function() { try {} catch (err) {} finally {} return })`, nil) test(`0xde0b6b3a7640080.toFixed(0)`, nil) test(`/[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/`, nil) test(`/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]/`, nil) test("var abc = 1;\ufeff", nil) test("\ufeff/* var abc = 1; */", nil) test(`if (-0x8000000000000000<=abc&&abc<=0x8000000000000000) {}`, nil) test(`(function(){debugger;return this;})`, nil) test(` `, nil) test(` var abc = "" debugger `, nil) test(` var abc = /\[\]$/ debugger `, nil) test(` var abc = 1 / 2 debugger `, nil) test("'ё\\\u2029'", nil) test(`[a, b] = [1, 2]`, nil) test(`({"a b": {}} = {})`, nil) test(`ref = (a, b = 39,) => { };`, nil) test(`(a,) => {}`, nil) test(`2 ?? (2 && 3) + 3`, nil) test(`(2 ?? 2) && 3 + 3`, nil) program = test(`a ?? b ?? c`, nil) is(len(program.Body), 1) is(program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right.(*ast.Identifier).Name, "c") program = test(` class C { a b #c m() { return this.#c; } } `, nil) is(len(program.Body), 1) { program := test(`(-2)**53`, nil) st := program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression) is(st.Operator, token.EXPONENT) left := st.Left.(*ast.UnaryExpression) is(left.Operator, token.MINUS) op1 := left.Operand.(*ast.NumberLiteral) is(op1.Literal, "2") right := st.Right.(*ast.NumberLiteral) is(right.Literal, "53") } }) } func TestParseDestruct(t *testing.T) { parser := newParser("", `({a: (a.b), ...spread,} = {})`) prg, err := parser.parse() if err != nil { t.Fatal(err) } _ = prg } func Test_parseStringLiteral(t *testing.T) { tt(t, func() { test := func(have string, want unistring.String) { parser := newParser("", have) parser.read() parser.read() _, res, err := parser.scanString(0, true) is(err, "") is(res, want) } test(`""`, "") test(`/=/`, "=") test("'1(\\\\d+)'", "1(\\d+)") test("'\\u2029'", "\u2029") test("'abc\\uFFFFabc'", "abc\uFFFFabc") test("'[First line \\\nSecond line \\\n Third line\\\n. ]'", "[First line Second line Third line. ]") test("'\\u007a\\x79\\u000a\\x78'", "zy\nx") // S7.8.4_A4.2_T3 test("'\\a'", "a") test("'\u0410'", "\u0410") // S7.8.4_A5.1_T1 test("'\\0'", "\u0000") // S8.4_A5 test("'\u0000'", "\u0000") // 15.5.4.20 test("\"'abc'\\\n'def'\"", "'abc''def'") // 15.5.4.20-4-1 test("\"'abc'\\\r\n'def'\"", "'abc''def'") // Octal test("'\\0'", "\000") test("'\\00'", "\000") test("'\\000'", "\000") test("'\\09'", "\0009") test("'\\009'", "\0009") test("'\\0009'", "\0009") test("'\\1'", "\001") test("'\\01'", "\001") test("'\\001'", "\001") test("'\\0011'", "\0011") test("'\\1abc'", "\001abc") test("'\\\u4e16'", "\u4e16") // err test = func(have string, want unistring.String) { parser := newParser("", have) parser.read() parser.read() _, res, err := parser.scanString(0, true) is(err, want) is(res, "") } test(`"\u"`, `invalid escape: \u: len("") != 4`) test(`"\u0"`, `invalid escape: \u: len("0") != 4`) test(`"\u00"`, `invalid escape: \u: len("00") != 4`) test(`"\u000"`, `invalid escape: \u: len("000") != 4`) test(`"\x"`, `invalid escape: \x: len("") != 2`) test(`"\x0"`, `invalid escape: \x: len("0") != 2`) }) } func Test_parseNumberLiteral(t *testing.T) { tt(t, func() { test := func(input string, expect interface{}) { result, err := parseNumberLiteral(input) is(err, nil) is(result, expect) } test("0", 0) test("0x8000000000000000", float64(9.223372036854776e+18)) test("1n", big.NewInt(1)) test("-1n", big.NewInt(-1)) test("0x23n", big.NewInt(35)) test("0xabcdef01n", big.NewInt(2882400001)) var n big.Int n.SetString("0xabcdef0123456789abcdef0123", 0) test("0xabcdef0123456789abcdef0123n", &n) }) } func TestPosition(t *testing.T) { tt(t, func() { parser := newParser("", "// Lorem ipsum") // Out of range, idx0 (error condition) is(parser.slice(0, 1), "") is(parser.slice(0, 10), "") // Out of range, idx1 (error condition) is(parser.slice(1, 128), "") is(parser.str[0:0], "") is(parser.slice(1, 1), "") is(parser.str[0:1], "/") is(parser.slice(1, 2), "/") is(parser.str[0:14], "// Lorem ipsum") is(parser.slice(1, 15), "// Lorem ipsum") parser = newParser("", "(function(){ return 0; })") program, err := parser.parse() is(err, nil) var node ast.Node node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral) is(node.Idx0(), file.Idx(2)) is(node.Idx1(), file.Idx(25)) is(parser.slice(node.Idx0(), node.Idx1()), "function(){ return 0; }") is(parser.slice(node.Idx0(), node.Idx1()+1), "function(){ return 0; })") is(parser.slice(node.Idx0(), node.Idx1()+2), "") is(node.(*ast.FunctionLiteral).Source, "function(){ return 0; }") node = program is(node.Idx0(), file.Idx(2)) is(node.Idx1(), file.Idx(25)) is(parser.slice(node.Idx0(), node.Idx1()), "function(){ return 0; }") parser = newParser("", "(function(){ return abc; })") program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral) is(node.(*ast.FunctionLiteral).Source, "function(){ return abc; }") node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.List[0].(*ast.ReturnStatement) is(parser.slice(node.Idx0(), node.Idx1()), "return abc") parser = newParser("", "(function(){ return; })") program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.List[0].(*ast.ReturnStatement) is(parser.slice(node.Idx0(), node.Idx1()), "return") parser = newParser("", "true ? 1 : 2") program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ConditionalExpression) is(parser.slice(node.Idx0(), node.Idx1()), "true ? 1 : 2") parser = newParser("", "a++") program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.UnaryExpression) is(parser.slice(node.Idx0(), node.Idx1()), "a++") parser = newParser("", "++a") program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.UnaryExpression) is(parser.slice(node.Idx0(), node.Idx1()), "++a") parser = newParser("", "xyz: for (i = 0; i < 10; i++) { if (i == 5) continue xyz; }") program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.LabelledStatement) is(parser.slice(node.Idx0(), node.Idx1()), "xyz: for (i = 0; i < 10; i++) { if (i == 5) continue xyz; }") node = program.Body[0].(*ast.LabelledStatement).Statement.(*ast.ForStatement) is(parser.slice(node.Idx0(), node.Idx1()), "for (i = 0; i < 10; i++) { if (i == 5) continue xyz; }") block := program.Body[0].(*ast.LabelledStatement).Statement.(*ast.ForStatement).Body.(*ast.BlockStatement) node = block.List[0].(*ast.IfStatement).Consequent.(*ast.BranchStatement) is(parser.slice(node.Idx0(), node.Idx1()), "continue xyz") parser = newParser("", "for (p in o) { break; }") program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.ForInStatement) is(parser.slice(node.Idx0(), node.Idx1()), "for (p in o) { break; }") node = program.Body[0].(*ast.ForInStatement).Body.(*ast.BlockStatement).List[0].(*ast.BranchStatement) is(parser.slice(node.Idx0(), node.Idx1()), "break") parser = newParser("", "while (i < 10) { i++; }") program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.WhileStatement) is(parser.slice(node.Idx0(), node.Idx1()), "while (i < 10) { i++; }") parser = newParser("", "do { i++; } while (i < 10 )") program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.DoWhileStatement) is(parser.slice(node.Idx0(), node.Idx1()), "do { i++; } while (i < 10 )") parser = newParser("", "with (1) {}") program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.WithStatement) is(parser.slice(node.Idx0(), node.Idx1()), "with (1) {}") parser = newParser("", `switch (a) { case 1: x--; case 2: default: x++; }`) program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.SwitchStatement) is(parser.slice(node.Idx0(), node.Idx1()), `switch (a) { case 1: x--; case 2: default: x++; }`) }) } func TestExtractSourceMapLine(t *testing.T) { tt(t, func() { is(extractSourceMapLine(""), "") is(extractSourceMapLine("\n"), "") is(extractSourceMapLine(" "), "") is(extractSourceMapLine("1\n2\n3\n4\n"), "") src := `"use strict"; var x = {}; //# sourceMappingURL=delme.js.map` modSrc := `(function(exports, require, module) {` + src + ` })` is(extractSourceMapLine(modSrc), "//# sourceMappingURL=delme.js.map") is(extractSourceMapLine(modSrc+"\n\n\n\n"), "//# sourceMappingURL=delme.js.map") }) } func TestSourceMapOptions(t *testing.T) { tt(t, func() { count := 0 requestedPath := "" loader := func(p string) ([]byte, error) { count++ requestedPath = p return nil, nil } src := `"use strict"; var x = {}; //# sourceMappingURL=delme.js.map` _, err := ParseFile(nil, "delme.js", src, 0, WithSourceMapLoader(loader)) is(err, nil) is(count, 1) is(requestedPath, "delme.js.map") count = 0 _, err = ParseFile(nil, "", src, 0, WithSourceMapLoader(loader)) is(err, nil) is(count, 1) is(requestedPath, "delme.js.map") count = 0 _, err = ParseFile(nil, "delme.js", src, 0, WithDisableSourceMaps) is(err, nil) is(count, 0) _, err = ParseFile(nil, "/home/user/src/delme.js", src, 0, WithSourceMapLoader(loader)) is(err, nil) is(count, 1) is(requestedPath, "/home/user/src/delme.js.map") count = 0 _, err = ParseFile(nil, "https://site.com/delme.js", src, 0, WithSourceMapLoader(loader)) is(err, nil) is(count, 1) is(requestedPath, "https://site.com/delme.js.map") }) } func TestParseTemplateCharacters(t *testing.T) { parser := newParser("", "`test\\\r\\\n${a}`") parser.next() if parser.token != token.BACKTICK { t.Fatalf("Token: %s", parser.token) } checkParseTemplateChars := func(expectedLiteral string, expectedParsed unistring.String, expectedFinished, expectParseErr, expectErr bool) { literal, parsed, finished, parseErr, err := parser.parseTemplateCharacters() if err != "" != expectErr { t.Fatal(err) } if literal != expectedLiteral { t.Fatalf("Literal: %q", literal) } if parsed != expectedParsed { t.Fatalf("Parsed: %q", parsed) } if finished != expectedFinished { t.Fatal(finished) } if parseErr != "" != expectParseErr { t.Fatalf("parseErr: %v", parseErr) } } checkParseTemplateChars("test\\\n\\\n", "test", false, false, false) parser.next() parser.expect(token.IDENTIFIER) if len(parser.errors) > 0 { t.Fatal(parser.errors) } if parser.token != token.RIGHT_BRACE { t.Fatal("Expected }") } if len(parser.errors) > 0 { t.Fatal(parser.errors) } checkParseTemplateChars("", "", true, false, false) if parser.chr != -1 { t.Fatal("Expected EOF") } } func TestParseTemplateLiteral(t *testing.T) { parser := newParser("", "f()\n`test${a}`") prg, err := parser.parse() if err != nil { t.Fatal(err) } if st, ok := prg.Body[0].(*ast.ExpressionStatement); ok { if expr, ok := st.Expression.(*ast.TemplateLiteral); ok { if expr.Tag == nil { t.Fatal("tag is nil") } if idx0 := expr.Tag.Idx0(); idx0 != 1 { t.Fatalf("Tag.Idx0(): %d", idx0) } if expr.OpenQuote != 5 { t.Fatalf("OpenQuote: %d", expr.OpenQuote) } if expr.CloseQuote != 14 { t.Fatalf("CloseQuote: %d", expr.CloseQuote) } if l := len(expr.Elements); l != 2 { t.Fatalf("len elements: %d", l) } if l := len(expr.Expressions); l != 1 { t.Fatalf("len expressions: %d", l) } } else { t.Fatal(st) } } else { t.Fatal(prg.Body[0]) } } func TestParseTemplateLiteralWithTail(t *testing.T) { parser := newParser("", "f()\n`test${a}tail` ") prg, err := parser.parse() if err != nil { t.Fatal(err) } if st, ok := prg.Body[0].(*ast.ExpressionStatement); ok { if expr, ok := st.Expression.(*ast.TemplateLiteral); ok { if expr.CloseQuote != 18 { t.Fatalf("CloseQuote: %d", expr.CloseQuote) } } else { t.Fatal(st) } } else { t.Fatal(prg.Body[0]) } }