UNPKG

tenko

Version:

A "pixel perfect" 100% spec compliant ES2021 JavaScript parser written in JS.

310 lines (301 loc) 13.1 kB
// All constants of the parser itself (parser.mjs) that can be inlined should go in here // At dev time many of these constants are distinguishable object references. // In a prod build these are all scrubbed to generic booleans or numbers. // This file is used as a way to decide which constants to inline. As such be careful to only make primitives `const` // because the inlining is unconditional beyond being a const in this file. import {ASSERT} from "./utils.mjs"; const VERSION_EXPONENTIATION = 7; // ES2016 const VERSION_ASYNC = 8; // ES2017 const VERSION_TRAILING_FUNC_COMMAS = 8; // ES2017 const VERSION_ASYNC_GEN = 9; // ES2018 const VERSION_OBJECTSPREAD = 9; // ES2018 const VERSION_TAGGED_TEMPLATE_BAD_ESCAPES = 9; // ES2018 const VERSION_OPTIONAL_CATCH = 10; // ES2019 const VERSION_DYNAMIC_IMPORT = 11; // ES2020 const VERSION_EXPORT_STAR_AS = 11; // ES2020 const VERSION_WHATEVER = Infinity; const IS_ASYNC = DEVONLY() ? {IS_ASYNC: 1} : true; const NOT_ASYNC = DEVONLY() ? {_ASYNC: 1} : false; const IS_ASYNC_PREFIXED = DEVONLY() ? {IS_ASYNC_PREFIXED: 1} : true; const NOT_ASYNC_PREFIXED = DEVONLY() ? {NOT_ASYNC_PREFIXED: 1} : false; const UNDEF_STATIC = DEVONLY() ? {UNDEF_STATIC: 1, get str(){ASSERT(false)}} : undefined; const UNDEF_ASYNC = DEVONLY() ? {UNDEF_ASYNC: 1, get str(){ASSERT(false)}} : undefined; const UNDEF_STAR = DEVONLY() ? {UNDEF_STAR: 1, get str(){ASSERT(false)}} : undefined; const UNDEF_GET = DEVONLY() ? {UNDEF_GET: 1, get str(){ASSERT(false)}} : undefined; const UNDEF_SET = DEVONLY() ? {UNDEF_SET: 1, get str(){ASSERT(false)}} : undefined; const IS_FUNC_DECL = DEVONLY() ? {IS_FUNC_DECL: 1} : true; const NOT_FUNC_DECL = DEVONLY() ? {NOT_FUNC_DECL: 1} : false; const IS_FUNC_EXPR = DEVONLY() ? {IS_FUNC_EXPR: 1} : true; const NOT_FUNC_EXPR = DEVONLY() ? {NOT_FUNC_EXPR: 1} : false; const IDENT_OPTIONAL = DEVONLY() ? {IDENT_OPTIONAL: 1} : true; const IDENT_REQUIRED = DEVONLY() ? {IDENT_REQUIRED: 1} : false; const PARSE_VALUE_MAYBE = DEVONLY() ? {PARSE_VALUE_MAYBE: 1} : true; const PARSE_VALUE_MUST = DEVONLY() ? {PARSE_VALUE_MUST: 1} : false; const YIELD_WITHOUT_VALUE = DEVONLY() ? {YIELD_WITHOUT_VALUE: 1} : 0; const WITH_ASSIGNABLE = DEVONLY() ? {WITH_ASSIGNABLE: 1} : 1; const WITH_NON_ASSIGNABLE = DEVONLY() ? {WITH_NON_ASSIGNABLE: 1} : 2; const IS_ARROW = DEVONLY() ? {IS_ARROW: 1} : true; const NOT_ARROW = DEVONLY() ? {NOT_ARROW: 1} : false; const FROM_STATEMENT_START = DEVONLY() ? {FROM_STATEMENT_START: 1} : 1; const FROM_FOR_HEADER = DEVONLY() ? {FROM_FOR_HEADER: 1} : 2; const FROM_EXPORT_DECL = DEVONLY() ? {FROM_EXPORT_DECL: 1} : 3; const FROM_CATCH = DEVONLY() ? {FROM_CATCH: 1} : 4; const FROM_ASYNC_ARG = DEVONLY() ? {FROM_ASYNC_ARG: 1} : 5; const FROM_OTHER_FUNC_ARG = DEVONLY() ? {FROM_OTHER_FUNC_ARG: 1} : 6; const COAL_SEEN_NEITHER = 0; const COAL_SEEN_NULLISH = 1; const COAL_SEEN_LOGICAL = 2; const ASSIGNMENT_IS_INIT = DEVONLY() ? {ASSIGNMENT_IS_INIT: 1} : true; // var foo = bar; (not to be parsed by parseBinding const ASSIGNMENT_IS_DEFAULT = DEVONLY() ? {ASSIGNMENT_IS_DEFAULT: 1} : false; // (foo = bar) => foo (parsed by parseBinding) const IS_EXPRESSION = DEVONLY() ? {IS_EXPRESSION: 1} : 1; const IS_STATEMENT = DEVONLY() ? {IS_STATEMENT: 1} : 2; const IS_NEW_ARG = DEVONLY() ? {IS_NEW_ARG: 1} : 3; const NOT_NEW_ARG = DEVONLY() ? {NOT_NEW_ARG: 1} : 4; const IS_OPTIONAL = DEVONLY() ? {IS_OPTIONAL: 1} : 1; const NOT_OPTIONAL = DEVONLY() ? {NOT_OPTIONAL: 1} : 2; const MIGHT_DESTRUCT = 0; // any kind of destructuring or lack thereof is okay const CANT_DESTRUCT = 1 << 0; // it is impossible to destructure this const DESTRUCT_ASSIGN_ONLY = 1 << 1; // the only way this can destruct is by assignment const MUST_DESTRUCT = 1 << 2; // something that is an error if it doesnt lead to destructurig like `({a=b})` const ASSIGNABLE_UNDETERMINED = 1 << 3; const NOT_ASSIGNABLE = 1 << 4; const IS_ASSIGNABLE = 1 << 5; const PIGGY_BACK_SAW_AWAIT = 1 << 6; // parsed an expression containing `await` as varname or keyword const PIGGY_BACK_SAW_YIELD = 1 << 7; // parsed an expression containing `yield` as varname or keyword const PIGGY_BACK_WAS_CONSTRUCTOR = 1 << 8; // signal having found a constructor (special case) const PIGGY_BACK_WAS_PROTO = 1 << 9; // signal that a `__proto__: x` was parsed (do detect double occurrence) const PIGGY_BACK_WAS_ARROW = 1 << 10; // signal that double proto was found on object; error in web compat outside of arrow headers const NO_SPREAD = DEVONLY() ? {NO_SPREAD: 1} : 0; const LAST_SPREAD = DEVONLY() ? {LAST_SPREAD: 1} : 1; const MID_SPREAD = DEVONLY() ? {MID_SPREAD: 1} : 2; const PARSE_INIT = DEVONLY() ? {PARSE_INIT: 1} : true; const SKIP_INIT = DEVONLY() ? {SKIP_INIT: 1} : false; const IS_EXPORT = DEVONLY() ? {IS_EXPORT: 1} : true; const NOT_EXPORT = DEVONLY() ? {NOT_EXPORT: 1} : false; const IS_QUASI_TAIL = DEVONLY() ? {IS_QUASI_TAIL: 1} : true; const NOT_QUASI_TAIL = DEVONLY() ? {NOT_QUASI_TAIL: 1} : false; const PARAM_UNDETERMINED = DEVONLY() ? {PARAM_UNDETERMINED: 1} : 0; const PARAM_WAS_SIMPLE = DEVONLY() ? {PARAM_WAS_SIMPLE: 1} : 1; const PARAM_WAS_NON_STRICT_SIMPLE = DEVONLY() ? {PARAM_WAS_NON_STRICT_SIMPLE: 1} : 2; // like a future reserved word, `(package) => {"use strict"}` const PARAM_WAS_COMPLEX = DEVONLY() ? {PARAM_WAS_COMPLEX: 1} : 3; const PARAM_WAS_COMPLEX_HAD_INIT = DEVONLY() ? {PARAM_WAS_COMPLEX_HAD_INIT: 1} : 4; const PARAMS_ALL_SIMPLE = DEVONLY() ? {PARAMS_ALL_SIMPLE: 1} : 1; const PARAMS_SOME_NONSTRICT = DEVONLY() ? {PARAMS_SOME_NONSTRICT: 1} : 2; const PARAMS_SOME_COMPLEX = DEVONLY() ? {PARAMS_SOME_COMPLEX: 1} : 3; const IS_CONSTRUCTOR = DEVONLY() ? {IS_CONSTRUCTOR: 1} : true; const NOT_CONSTRUCTOR = DEVONLY() ? {NOT_CONSTRUCTOR: 1} : false; const IS_METHOD = DEVONLY() ? {IS_METHOD: 1} : true; const NOT_METHOD = DEVONLY() ? {NOT_METHOD: 1} : false; const ASSIGN_EXPR_IS_OK = DEVONLY() ? {ASSIGN_EXPR_IS_OK: 1} : true; // fine to parse assignments, arrows, yield, ternary const ASSIGN_EXPR_IS_ERROR = DEVONLY() ? {ASSIGN_EXPR_IS_ERROR: 1} : false; // throw on actual assignments, but also arrows, yield, await, ternary const NO_ID_TO_VERIFY = DEVONLY() ? {NO_ID_TO_VERIFY: 1} : undefined; const NO_DUPE_PARAMS = DEVONLY() ? {NO_DUPE_PARAMS: 1} : 0; const DO_NOT_BIND = DEVONLY() ? {DO_NOT_BIND: 1} : null; const UNDEF_EXPORTS = DEVONLY() ? {UNDEF_EXPORTS: 1} : undefined; const FDS_ILLEGAL = DEVONLY() ? {FDS_ILLEGAL: 1} : 1; // function declaration not allowed, period const FDS_IFELSE = DEVONLY() ? {FDS_IFELSE: 2} : 2; // if-else specific webcompat exception would apply to a function declaration const FDS_LEX = DEVONLY() ? {FDS_LEX: 3} : 3; // a function declaration would be a lexical binding const FDS_VAR = DEVONLY() ? {FDS_VAR: 4} : 4; // a function declaration would be a var binding const IS_GLOBAL_TOPLEVEL = DEVONLY() ? {IS_GLOBAL_TOPLEVEL: 1} : true; const NOT_GLOBAL_TOPLEVEL = DEVONLY() ? {NOT_GLOBAL_TOPLEVEL: 1} : false; const IS_LABELLED = DEVONLY() ? {IS_LABELLED: 1} : true; const NOT_LABELLED = DEVONLY() ? {NOT_LABELLED: 1} : false; const NOT_LHSE = DEVONLY() ? {NOT_LHSE: 1} : false; // not requiring a "LeftHandExpression". This is currently only used for class `extends`. const ONLY_LHSE = DEVONLY() ? {ONLY_LHSE: 1} : true; // restrict value to conform to a "LeftHandExpression" production. const PARENT_NOT_LABEL = null; // when the parent statement was not a label statement const EMPTY_LABEL_SET = null; // The scope constants are exported so make them `let` so they don't get scrubbed entirely // TODO: also means they won't be inlined so this may require a slightly more granular system let BINDING_TYPE_NONE = DEVONLY() ? {BINDING_TYPE_NONE: 1} : 0; let BINDING_TYPE_ARG = DEVONLY() ? {BINDING_TYPE_ARG: 1} : 1; let BINDING_TYPE_VAR = DEVONLY() ? {BINDING_TYPE_VAR: 1} : 2; let BINDING_TYPE_FUNC_VAR = DEVONLY() ? {BINDING_TYPE_FUNC_VAR: 1} : 3; let BINDING_TYPE_FUNC_STMT = DEVONLY() ? {BINDING_TYPE_FUNC_STMT: 1} : 4; // A func decl inside a block or switch (for webcompat mode) let BINDING_TYPE_FUNC_LEX = DEVONLY() ? {BINDING_TYPE_FUNC_LEX: 1} : 5; let BINDING_TYPE_LET = DEVONLY() ? {BINDING_TYPE_LET: 1} : 6; let BINDING_TYPE_CONST = DEVONLY() ? {BINDING_TYPE_CONST: 1} : 7; let BINDING_TYPE_CLASS = DEVONLY() ? {BINDING_TYPE_CLASS: 1} : 8; let BINDING_TYPE_CATCH_IDENT = DEVONLY() ? {BINDING_TYPE_CATCH_IDENT: 1} : 9; let BINDING_TYPE_CATCH_OTHER = DEVONLY() ? {BINDING_TYPE_CATCH_OTHER: 1} : 10; let HAS_NO_BINDINGS = DEVONLY() ? {HAS_NO_BINDINGS: 1} : null; let SCOPE_LAYER_GLOBAL = DEVONLY() ? {SCOPE_LAYER_GLOBAL: 1} : 0; let SCOPE_LAYER_FOR_HEADER = DEVONLY() ? {SCOPE_LAYER_FOR_HEADER: 1} : 1; let SCOPE_LAYER_BLOCK = DEVONLY() ? {SCOPE_LAYER_BLOCK: 1} : 2; let SCOPE_LAYER_FUNC_PARAMS = DEVONLY() ? {SCOPE_LAYER_FUNC_PARAMS: 1} : 3; let SCOPE_LAYER_CATCH_HEAD = DEVONLY() ? {SCOPE_LAYER_CATCH_HEAD: 1} : 5; let SCOPE_LAYER_CATCH_BODY = DEVONLY() ? {SCOPE_LAYER_CATCH_BODY: 1} : 6; let SCOPE_LAYER_FINALLY = DEVONLY() ? {SCOPE_LAYER_FINALLY: 1} : 7; let SCOPE_LAYER_SWITCH = DEVONLY() ? {SCOPE_LAYER_SWITCH: 1} : 8; let SCOPE_LAYER_FUNC_ROOT = DEVONLY() ? {SCOPE_LAYER_FUNC_ROOT: 1} : 9; let SCOPE_LAYER_FUNC_BODY = DEVONLY() ? {SCOPE_LAYER_FUNC_BODY: 1} : 10; let SCOPE_LAYER_ARROW_PARAMS = DEVONLY() ? {SCOPE_LAYER_ARROW_PARAMS: 1} : 11; let SCOPE_LAYER_FAKE_BLOCK = DEVONLY() ? {SCOPE_LAYER_FAKE_BLOCK: 1} : 12; // This `DEVONLY` function is only used to set objects for enum in a dev build. In a prod build these are stripped entirely // The "dsl" for this function is assuming usage in a ternary (`DEVONLY() ? devstuff : prodstuff`). Any other use is at the mercy of the DCE of the minifier. function DEVONLY() { let dev = false; // A build will eliminate this ASSERT call. A minifier will inline the `true` and then eliminate it. Hopefully. ASSERT(dev = true); return dev; } const PIGGIES = (0 | PIGGY_BACK_SAW_AWAIT | PIGGY_BACK_SAW_YIELD | PIGGY_BACK_WAS_CONSTRUCTOR | PIGGY_BACK_WAS_PROTO | PIGGY_BACK_WAS_ARROW ); function copyPiggies(output, input) { return output | (input & PIGGIES); } function P(f, arr) { if (f & PIGGY_BACK_WAS_CONSTRUCTOR) { arr.push('PIGGY_BACK_WAS_CONSTRUCTOR'); f ^= PIGGY_BACK_WAS_CONSTRUCTOR; } if (f & PIGGY_BACK_WAS_PROTO) { arr.push('PIGGY_BACK_WAS_PROTO'); f ^= PIGGY_BACK_WAS_PROTO; } if (f & PIGGY_BACK_SAW_AWAIT) { arr.push('PIGGY_BACK_SAW_AWAIT'); f ^= PIGGY_BACK_SAW_AWAIT; } if (f & PIGGY_BACK_SAW_YIELD) { arr.push('PIGGY_BACK_SAW_YIELD'); f ^= PIGGY_BACK_SAW_YIELD; } if (f & PIGGY_BACK_WAS_ARROW) { arr.push('PIGGY_BACK_WAS_ARROW'); f ^= PIGGY_BACK_WAS_ARROW; } return f; } export { VERSION_EXPONENTIATION, VERSION_ASYNC, VERSION_TRAILING_FUNC_COMMAS, VERSION_ASYNC_GEN, VERSION_OBJECTSPREAD, VERSION_TAGGED_TEMPLATE_BAD_ESCAPES, VERSION_OPTIONAL_CATCH, VERSION_DYNAMIC_IMPORT, VERSION_EXPORT_STAR_AS, VERSION_WHATEVER, IS_ASYNC, NOT_ASYNC, IS_ASYNC_PREFIXED, NOT_ASYNC_PREFIXED, UNDEF_STATIC, UNDEF_ASYNC, UNDEF_STAR, UNDEF_GET, UNDEF_SET, IS_FUNC_DECL, NOT_FUNC_DECL, IS_FUNC_EXPR, NOT_FUNC_EXPR, IDENT_OPTIONAL, IDENT_REQUIRED, PARSE_VALUE_MAYBE, PARSE_VALUE_MUST, YIELD_WITHOUT_VALUE, WITH_ASSIGNABLE, WITH_NON_ASSIGNABLE, IS_ARROW, NOT_ARROW, FROM_STATEMENT_START, FROM_FOR_HEADER, FROM_EXPORT_DECL, FROM_CATCH, FROM_ASYNC_ARG, FROM_OTHER_FUNC_ARG, COAL_SEEN_NEITHER, COAL_SEEN_NULLISH, COAL_SEEN_LOGICAL, BINDING_TYPE_NONE, BINDING_TYPE_ARG, BINDING_TYPE_VAR, BINDING_TYPE_LET, BINDING_TYPE_CONST, BINDING_TYPE_CLASS, BINDING_TYPE_FUNC_VAR, BINDING_TYPE_FUNC_LEX, BINDING_TYPE_FUNC_STMT, BINDING_TYPE_CATCH_IDENT, BINDING_TYPE_CATCH_OTHER, HAS_NO_BINDINGS, ASSIGNMENT_IS_INIT, ASSIGNMENT_IS_DEFAULT, IS_EXPRESSION, IS_STATEMENT, IS_NEW_ARG, NOT_NEW_ARG, IS_OPTIONAL, NOT_OPTIONAL, MIGHT_DESTRUCT, CANT_DESTRUCT, DESTRUCT_ASSIGN_ONLY, MUST_DESTRUCT, ASSIGNABLE_UNDETERMINED, NOT_ASSIGNABLE, IS_ASSIGNABLE, PIGGY_BACK_SAW_AWAIT, PIGGY_BACK_SAW_YIELD, PIGGY_BACK_WAS_CONSTRUCTOR, PIGGY_BACK_WAS_PROTO, PIGGY_BACK_WAS_ARROW, NO_SPREAD, LAST_SPREAD, MID_SPREAD, PARSE_INIT, SKIP_INIT, IS_EXPORT, NOT_EXPORT, IS_QUASI_TAIL, NOT_QUASI_TAIL, PARAM_UNDETERMINED, PARAM_WAS_SIMPLE, PARAM_WAS_NON_STRICT_SIMPLE, PARAM_WAS_COMPLEX, PARAM_WAS_COMPLEX_HAD_INIT, PARAMS_ALL_SIMPLE, PARAMS_SOME_NONSTRICT, PARAMS_SOME_COMPLEX, IS_CONSTRUCTOR, NOT_CONSTRUCTOR, IS_METHOD, NOT_METHOD, ASSIGN_EXPR_IS_OK, ASSIGN_EXPR_IS_ERROR, NO_ID_TO_VERIFY, NO_DUPE_PARAMS, SCOPE_LAYER_GLOBAL, SCOPE_LAYER_FOR_HEADER, SCOPE_LAYER_BLOCK, SCOPE_LAYER_FUNC_PARAMS, SCOPE_LAYER_CATCH_HEAD, SCOPE_LAYER_CATCH_BODY, SCOPE_LAYER_FINALLY, SCOPE_LAYER_SWITCH, SCOPE_LAYER_FUNC_ROOT, SCOPE_LAYER_FUNC_BODY, SCOPE_LAYER_ARROW_PARAMS, SCOPE_LAYER_FAKE_BLOCK, DO_NOT_BIND, UNDEF_EXPORTS, FDS_ILLEGAL, FDS_IFELSE, FDS_LEX, FDS_VAR, IS_GLOBAL_TOPLEVEL, NOT_GLOBAL_TOPLEVEL, IS_LABELLED, NOT_LABELLED, NOT_LHSE, ONLY_LHSE, PARENT_NOT_LABEL, EMPTY_LABEL_SET, copyPiggies, P, };