Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774// Type recognizer: scans a TypeScript type (or type parameter list, type// argument list, heritage list, object type body) and reports where it ends.// It never emits; the parser blanks the range afterwards. Every function// takes the current lexer token as its first token and leaves the lexer on// the first token after the construct, with `typeEnd` at the end of the last// token consumed. A false return means either a syntax mismatch or that the// lexer ran out of input (`more` is then set); callers check `more`.import { scan, more, tt, ts, te, tk, tb, tnl, rescanTemplate, rescanLess, reseek, tokenIn, T_EOF, T_NAME, T_NUM, T_STR, T_TEMPLATE, T_TEMPLATE_HEAD, T_TEMPLATE_MID, T_TEMPLATE_TAIL, T_LBRACE, T_RBRACE, T_LPAREN, T_RPAREN, T_LBRACK, T_RBRACK, T_DOT, T_QDOT, T_ELLIPSIS, T_SEMI, T_COMMA, T_COLON, T_QUESTION, T_ARROW, T_BANG, T_LT, T_SHL, T_GT, T_EQ, T_PLUS, T_MINUS, T_STAR, T_BAR, T_AMP, T_PRIVATE,} from './lex.zena';import { K_EXTENDS, K_KEYOF, K_UNIQUE, K_READONLY, K_INFER, K_TYPEOF, K_NEW, K_ABSTRACT, K_IMPORT, K_THIS, K_FUNCTION, K_IS, K_ASSERTS, K_IN, K_OUT, K_AS, K_VOID, K_NULL, K_TRUE, K_FALSE, K_FIRST_CONTEXTUAL, K_IMPLEMENTS, KS_STMT_MOD, keywordIn, keywordInHi, KS_KEYWORD_TYPE, KS_KEYWORD_TYPE_HI, KS_TYPE_START, KS_TYPE_MEMBER_MOD, TS_TYPE_START, TS_PROPERTY_NAME, TS_OPEN, TS_CLOSE, TS_BINDING_START, TS_PARAM_START, TS_STR_NUM_TPL, TS_JSDOC_NULLABLE_END, TS_PARAM_FOLLOW, TS_MEMBER_NAME, KS_NO_DOT_TYPE,} from './keywords.zena';
export var typeEnd: i32 = 0;var noCond: i32 = 0;
// The branch keeps this out of line: zena inlines a straight-line body this small at every call site, and// there are about 100 (516 bytes in build/erase.wasm).function nx(): boolean { typeEnd = te; scan(); if (more != 0) return false; return true;}
function is(t: i32): boolean { return tt == t;}
function kw(k: i32): boolean { return tt == T_NAME && tk == k;}
function eat(t: i32): boolean { if (tt != t) return false; return nx();}
// Keyword types that take no type arguments (TypeScript parses them as keyword nodes).function isKeywordType(k: i32): boolean { return keywordIn(k, KS_KEYWORD_TYPE) || keywordInHi(k, KS_KEYWORD_TYPE_HI);}
function isPropertyNameStart(): boolean { return tokenIn(TS_PROPERTY_NAME);}
// Type: union-or-higher, then an optional conditional tail.export function scanType(): boolean { if (!scanUnion()) return false; if (noCond == 0 && kw(K_EXTENDS) && tnl == 0) { if (!nx()) return false; noCond = 1; let ok = scanUnion(); noCond = 0; if (!ok) return false; if (!eat(T_QUESTION)) return false; if (!scanType()) return false; if (!eat(T_COLON)) return false; return scanType(); } return true;}
// A type with conditionals re-allowed (inside brackets, parens, braces and type arguments).function scanNested(): boolean { let saved = noCond; noCond = 0; let ok = scanType(); noCond = saved; return ok;}
function scanUnion(): boolean { if (is(T_BAR)) { if (!nx()) return false; } if (!scanIntersection()) return false; while (is(T_BAR)) { if (!nx()) return false; if (!scanIntersection()) return false; } return true;}
function scanIntersection(): boolean { if (is(T_AMP)) { if (!nx()) return false; } if (!scanOperator()) return false; while (is(T_AMP)) { if (!nx()) return false; if (!scanOperator()) return false; } return true;}
function scanOperator(): boolean { if (tt == T_NAME) { if (tk == K_KEYOF || tk == K_UNIQUE || tk == K_READONLY) { if (!nx()) return false; return scanOperator(); } if (tk == K_INFER) { if (!nx()) return false; if (tt != T_NAME) return false; if (!nx()) return false; // `infer U extends C` keeps the constraint unless a `?` follows it where a conditional may start, // in which case the `extends` belongs to the enclosing conditional type. if (kw(K_EXTENDS) && tnl == 0) { let m = tb; if (!nx()) return false; let saved = noCond; noCond = 1; let ok = scanUnion(); noCond = saved; if (!ok) return false; if (noCond == 0 && tt == T_QUESTION) { reseek(m); return more == 0; } } return true; } } return scanPostfix();}
function scanPostfix(): boolean { let saved = noCond; noCond = 0; let ok = scanPrimary(); noCond = saved; if (!ok) return false; while (tnl == 0) { if (is(T_BANG)) { if (!nx()) return false; continue; } if (is(T_QUESTION)) { // JSDoc `T?`, unless a type follows and this is a conditional's `?`. let m = tb; scan(); if (more == 1) return false; let cond = isTypeStart(); reseek(m); if (more == 1 || cond) return more == 0; if (!nx()) return false; continue; } if (!is(T_LBRACK)) break; if (!nx()) return false; if (is(T_RBRACK)) { if (!nx()) return false; } else { if (!scanNested()) return false; if (!eat(T_RBRACK)) return false; } } return true;}
// TypeScript's isStartOfType, without its lookaheads for `(` and `-`.function isTypeStart(): boolean { if (tt == T_NAME) { let k = tk; return k == 0 || k >= K_FIRST_CONTEXTUAL || keywordIn(k, KS_TYPE_START); } return tokenIn(TS_TYPE_START);}
// `A.B.C`, each segment any name.function scanEntityName(): boolean { if (tt != T_NAME) return false; if (!nx()) return false; return scanQualifiers();}
// `.B.C` after a name; JSDoc's `Array.<T>` stops at the `<`.function scanQualifiers(): boolean { while (is(T_DOT)) { if (!nx()) return false; if (is(T_LT)) return true; if (tt != T_NAME && tt != T_PRIVATE) return false; if (!nx()) return false; } return true;}
export function scanTypeArgs(): boolean { if (is(T_SHL)) { rescanLess(); if (more == 1) return false; } if (!eat(T_LT)) return false; if (is(T_GT)) return nx(); while (true) { if (!scanNested()) return false; if (is(T_COMMA)) { if (!nx()) return false; if (is(T_GT)) break; continue; } break; } return eat(T_GT);}
function scanOptionalTypeArgs(): boolean { if ((is(T_LT) || is(T_SHL)) && tnl == 0) return scanTypeArgs(); return true;}
function scanPrimary(): boolean { if (tt == T_NAME) { let k = tk; if (k == K_TYPEOF) { if (!nx()) return false; if (kw(K_IMPORT)) { if (!scanImportType()) return false; } else if (tt == T_NAME || tt == T_PRIVATE) { if (!scanEntityName()) return false; } else { return false; } return scanOptionalTypeArgs(); } if (k == K_IMPORT) return scanImportType(); if (k == K_ASSERTS) { // `asserts x [is T]` parses in any type position (an error outside a return type). let m = tb; if (!nx()) return false; if (tt == T_NAME && tnl == 0) { if (!nx()) return false; if (!kw(K_IS) || tnl == 1) return true; if (!nx()) return false; return scanType(); } reseek(m); if (more == 1) return false; } if (k == K_FUNCTION) { // JSDoc `function(A, B): R` (an error in TypeScript files). if (!nx()) return false; if (!is(T_LPAREN)) return scanQualifiers(); if (!skipPattern()) return false; if (!is(T_COLON)) return true; if (!nx()) return false; return scanType(); } if (k == K_NEW) return scanFunctionType(); if (k == K_ABSTRACT) { if (!nx()) return false; if (!kw(K_NEW)) return false; return scanFunctionType(); } if (isKeywordType(k)) { if (!nx()) return false; // TypeScript's parseKeywordAndNoDot: `string.X` is a type reference. if (!is(T_DOT) || keywordIn(k, KS_NO_DOT_TYPE)) return true; if (!scanQualifiers()) return false; return scanOptionalTypeArgs(); } if (!scanEntityName()) return false; // TypeScript takes `this is T` in any type position (a grammar error outside a return type). if (k == K_THIS && kw(K_IS) && tnl == 0) { if (!nx()) return false; return scanType(); } return scanOptionalTypeArgs(); } if (tokenIn(TS_STR_NUM_TPL)) return nx(); if (tt == T_MINUS) { if (!nx()) return false; if (tt != T_NUM) return false; return nx(); } if (tt == T_TEMPLATE_HEAD) return scanTemplateType(); if (tt == T_LPAREN) return scanParenOrFunction(); if (tt == T_LT) return scanFunctionType(); if (tt == T_LBRACK) return scanTuple(); if (tt == T_LBRACE) return scanObjectType(); // JSDoc types (errors in TypeScript files): `*`, `?`, `?T`, `!T`. if (tt == T_STAR) return nx(); if (tt == T_QUESTION) { if (!nx()) return false; if (tokenIn(TS_JSDOC_NULLABLE_END)) return true; return scanType(); } if (tt == T_BANG) { if (!nx()) return false; return scanPrimary(); } return false;}
function scanImportType(): boolean { if (!nx()) return false; if (!eat(T_LPAREN)) return false; if (!scanNested()) return false; if (is(T_COMMA)) { if (!nx()) return false; if (is(T_LBRACE)) { if (!scanObjectType()) return false; } if (is(T_COMMA)) { if (!nx()) return false; } } if (!eat(T_RPAREN)) return false; while (is(T_DOT)) { if (!nx()) return false; if (tt != T_NAME) return false; if (!nx()) return false; } return scanOptionalTypeArgs();}
function scanTemplateType(): boolean { if (!nx()) return false; while (true) { if (!scanNested()) return false; if (tt != T_RBRACE) return false; rescanTemplate(); if (more == 1) return false; if (tt == T_TEMPLATE_TAIL) return nx(); if (tt != T_TEMPLATE_MID) return false; if (!nx()) return false; }}
// `<T>(a: A) => R`, `new (a: A) => R`, `abstract new () => R`.function scanFunctionType(): boolean { if (kw(K_NEW)) { if (!nx()) return false; } if (is(T_LT)) { if (!scanTypeParams()) return false; } if (!is(T_LPAREN)) return false; if (!scanParams()) return false; if (!eat(T_ARROW)) return false; return scanReturnType();}
// Parenthesized type or function type, decided the way TypeScript does (isUnambiguouslyStartOfFunctionType).function scanParenOrFunction(): boolean { if (isFunctionTypeStart()) { if (!scanParams()) return false; if (!eat(T_ARROW)) return false; return scanReturnType(); } if (!nx()) return false; if (!scanNested()) return false; return eat(T_RPAREN);}
// At `(`: `()`, `(...`, `(name:`/`,`/`?`/`=`, `(name) =>`, or a binding pattern followed by one of those.function isFunctionTypeStart(): boolean { let m = tb; scan(); var r = false; if (more == 0) { // TypeScript's skipParameterStart: modifiers, then a name or binding pattern. while (tt == T_NAME && isParamModifier(tk)) { let m2 = tb; scan(); if (more == 1) return false; if (!(tokenIn(TS_BINDING_START))) { reseek(m2); break; } } if (tt == T_RPAREN || tt == T_ELLIPSIS) { r = true; } else { var skipped = false; if (tt == T_NAME && (tk == 0 || tk >= K_FIRST_CONTEXTUAL || tk == K_THIS)) { scan(); skipped = more == 0; } else if (tt == T_LBRACK || tt == T_LBRACE) { skipped = skipPattern(); } if (skipped) { if (tokenIn(TS_PARAM_FOLLOW)) { r = true; } else if (tt == T_RPAREN) { scan(); r = more == 0 && tt == T_ARROW; } } } } if (more == 1) return false; reseek(m); return more == 0 && r;}
// Skips a bracketed binding pattern from its opener; the lexer ends on the token after it.function skipPattern(): boolean { var depth = 0; while (true) { if (tokenIn(TS_OPEN)) depth = depth + 1; if (tokenIn(TS_CLOSE)) depth = depth - 1; typeEnd = te; scan(); if (more == 1) return false; if (depth == 0) return true; // An unclosed pattern at the end of the input: the lexer stays on T_EOF. if (tt == T_TEMPLATE_HEAD || tt == T_EOF) return false; }}
// TypeScript's isModifierKind, which parseModifiers takes before a parameter or type parameter.function isParamModifier(k: i32): boolean { return keywordIn(k, KS_STMT_MOD) || k == K_IN || k == K_OUT;}
// `(` parameters `)`: modifiers (errors in a type, but parsed), `...`, a name or binding pattern, `?`, `: T`, and an// initializer (also an error), which is an expression.function scanParams(): boolean { if (!eat(T_LPAREN)) return false; while (!is(T_RPAREN)) { while (tt == T_NAME && isParamModifier(tk)) { let m = tb; if (!nx()) return false; if (!(tokenIn(TS_PARAM_START))) { reseek(m); break; } } if (is(T_ELLIPSIS)) { if (!nx()) return false; } if (tt == T_LBRACK || tt == T_LBRACE) { if (!skipPattern()) return false; } else if (!scanNested()) { return false; } if (is(T_QUESTION)) { if (!nx()) return false; } if (is(T_COLON)) { if (!nx()) return false; if (!scanNested()) return false; } if (is(T_EQ)) { if (!nx()) return false; while (!is(T_COMMA) && !is(T_RPAREN)) { if (tokenIn(TS_OPEN)) { if (!skipPattern()) return false; } else if (tt == T_EOF || tt == T_RBRACK || tt == T_RBRACE || tt == T_TEMPLATE_HEAD) { return false; } else if (!nx()) { return false; } } } if (is(T_COMMA)) { if (!nx()) return false; continue; } if (!is(T_RPAREN)) return false; } return nx();}
// Return type: `asserts x is T`, `asserts this`, `x is T`, `this is T`, or a type.export function scanReturnType(): boolean { if (kw(K_ASSERTS) && tnl == 0) { let save = ts; if (!nx()) return false; if (tt == T_NAME && tnl == 0) { if (!nx()) return false; if (kw(K_IS) && tnl == 0) { if (!nx()) return false; return scanType(); } return true; } // `asserts` on its own is a type reference named asserts. return scanTypeAfterName(); } if (tt == T_NAME) { if (!scanUnion()) return false; if (kw(K_IS) && tnl == 0) { if (!nx()) return false; return scanType(); } return scanConditionalTail(); } return scanType();}
// After scanUnion consumed the first part of a type, finish a possible conditional.function scanConditionalTail(): boolean { if (noCond == 0 && kw(K_EXTENDS) && tnl == 0) { if (!nx()) return false; noCond = 1; let ok = scanUnion(); noCond = 0; if (!ok) return false; if (!eat(T_QUESTION)) return false; if (!scanType()) return false; if (!eat(T_COLON)) return false; return scanType(); } return true;}
function scanTypeAfterName(): boolean { // The name was consumed already; continue as a reference with possible type args, then the rest. while (is(T_DOT)) { if (!nx()) return false; if (tt != T_NAME) return false; if (!nx()) return false; } if (!scanOptionalTypeArgs()) return false; while (is(T_LBRACK) && tnl == 0) { if (!nx()) return false; if (!is(T_RBRACK)) { if (!scanNested()) return false; } if (!eat(T_RBRACK)) return false; } while (is(T_BAR) || is(T_AMP)) { if (!nx()) return false; if (!scanOperator()) return false; } return scanConditionalTail();}
function scanTuple(): boolean { if (!eat(T_LBRACK)) return false; while (!is(T_RBRACK)) { if (is(T_ELLIPSIS)) { if (!nx()) return false; } if (!scanNested()) return false; if (is(T_QUESTION)) { if (!nx()) return false; } if (is(T_COLON)) { // `name: T` or `name?: T` member; TypeScript parses `name: ...T` and `name: T?` too (errors). if (!nx()) return false; if (is(T_ELLIPSIS)) { if (!nx()) return false; } if (!scanNested()) return false; if (is(T_QUESTION)) { if (!nx()) return false; } } if (is(T_COMMA)) { if (!nx()) return false; continue; } if (!is(T_RBRACK)) return false; } return nx();}
// `<` params `>`: `[const] [in] [out] Name [extends T] [= T]`.export function scanTypeParams(): boolean { if (!eat(T_LT)) return false; while (!is(T_GT)) { // TypeScript's parseModifiers(permitConst): `const`, `in`, `out`, and the other modifiers (errors). while (tt == T_NAME && isParamModifier(tk)) { let m = tb; if (!nx()) return false; // `<in>` or `<out>` as the parameter's own name. if (tt != T_NAME || tnl == 1 || kw(K_EXTENDS)) { reseek(m); if (more == 1) return false; break; } } if (tt != T_NAME) return false; if (!nx()) return false; if (kw(K_EXTENDS)) { if (!nx()) return false; if (!scanNested()) return false; } if (is(T_EQ)) { if (!nx()) return false; if (!scanNested()) return false; } if (is(T_COMMA)) { if (!nx()) return false; continue; } if (!is(T_GT)) return false; } return nx();}
// Comma-separated heritage elements, after `implements` or `interface X extends`: TypeScript parses each as a// left-hand-side expression (calls and `?.` included, both errors) with type arguments.export function scanTypeList(): boolean { while (true) { if (tt == T_LPAREN || tt == T_LBRACK) { if (!skipPattern()) return false; } else if (!scanEntityName()) { return false; } while (true) { if (is(T_DOT) || is(T_QDOT)) { if (!nx()) return false; if (tt != T_NAME && tt != T_PRIVATE && !is(T_LPAREN) && !is(T_LBRACK)) return false; if (tt == T_NAME || tt == T_PRIVATE) { if (!nx()) return false; } } else if (is(T_LPAREN) || is(T_LBRACK)) { if (!skipPattern()) return false; } else if (is(T_BANG)) { if (!nx()) return false; } else if ((is(T_LT) || is(T_SHL)) && tnl == 0) { if (!scanTypeArgs()) return false; } else { break; } } if (is(T_COMMA)) { if (!nx()) return false; // A trailing comma (an error) before the body or the next clause. if (is(T_LBRACE) || kw(K_EXTENDS) || kw(K_IMPLEMENTS)) return true; continue; } return true; }}
function scanMemberSeparator(): boolean { if (is(T_SEMI) || is(T_COMMA)) return nx(); return true;}
// `{` members `}`, including mapped types.export function scanObjectType(): boolean { if (!eat(T_LBRACE)) return false; while (!is(T_RBRACE)) { if (!scanTypeMember()) return false; if (!scanMemberSeparator()) return false; } return nx();}
function scanTypeMember(): boolean { // A rest element inside a binding pattern that is being scanned as an object type. if (is(T_ELLIPSIS)) { if (!nx()) return false; if (tt != T_NAME) return false; return nx(); } // Mapped type modifiers: `+readonly`, `-readonly`. if (is(T_PLUS) || is(T_MINUS)) { if (!nx()) return false; if (!kw(K_READONLY)) return false; if (!nx()) return false; return scanIndexOrMapped(); } // Modifiers: readonly, accessibility, static, declare, accessor, get, set. Each only when a name follows. while (tt == T_NAME) { let k = tk; let isMod = keywordIn(k, KS_TYPE_MEMBER_MOD); if (!isMod) break; let save = ts; if (!nx()) return false; if (!(isPropertyNameStart() && tnl == 0)) { // The keyword was the member's own name; its suffix follows. return scanMemberAfterName(); } } if (is(T_LBRACK)) return scanIndexOrMapped(); if (is(T_LPAREN) || is(T_LT)) return scanSignatureRest(); if (kw(K_NEW)) { if (!nx()) return false; if (is(T_LPAREN) || is(T_LT)) return scanSignatureRest(); return scanMemberAfterName(); } if (tokenIn(TS_MEMBER_NAME)) { if (!nx()) return false; return scanMemberAfterName(); } return false;}
// After a member name: `?`, then `(`/`<` signature or `: T`, or nothing. TypeScript also parses an accessor's// body and a property's initializer here (errors).function scanMemberAfterName(): boolean { if (is(T_QUESTION)) { if (!nx()) return false; } if (is(T_LPAREN) || is(T_LT)) { if (!scanSignatureRest()) return false; if (is(T_LBRACE)) return skipPattern(); return true; } if (is(T_COLON)) { if (!nx()) return false; if (!scanNested()) return false; } if (is(T_EQ)) { if (!nx()) return false; var first = true; while (!is(T_SEMI) && !is(T_COMMA) && !is(T_RBRACE) && !is(T_EOF) && (first || tnl == 0)) { first = false; if (tokenIn(TS_OPEN)) { if (!skipPattern()) return false; } else if (tt == T_TEMPLATE_HEAD || tt == T_RBRACK || tt == T_RPAREN) { return false; } else if (!nx()) { return false; } } } return true;}
// Signature from `<` or `(`: type params, params, optional `: T`.function scanSignatureRest(): boolean { if (is(T_LT)) { if (!scanTypeParams()) return false; } if (!scanParams()) return false; if (is(T_COLON)) { if (!nx()) return false; return scanReturnType(); } return true;}
// `[` ... : index signature `[k: T]: U`, mapped type `[K in T as U]: V`, or computed name `[a.b]`; anything else// TypeScript parses there (odd index parameters, a computed name's expression) is skipped as a balanced run.function scanIndexOrMapped(): boolean { let m = tb; if (scanIndexForm()) return true; if (more == 1) return false; reseek(m); if (more == 1 || !skipPattern()) return false; return scanMemberAfterName();}
function scanIndexForm(): boolean { if (!eat(T_LBRACK)) return false; if (tt == T_NAME) { if (!nx()) return false; if (is(T_COLON)) { if (!nx()) return false; if (!scanNested()) return false; if (!eat(T_RBRACK)) return false; return scanMemberAfterName(); } if (kw(K_IN)) { if (!nx()) return false; if (!scanNested()) return false; if (kw(K_AS)) { if (!nx()) return false; if (!scanNested()) return false; } if (!eat(T_RBRACK)) return false; if (is(T_PLUS) || is(T_MINUS)) { if (!nx()) return false; } if (is(T_QUESTION)) { if (!nx()) return false; } if (is(T_COLON)) { if (!nx()) return false; if (!scanNested()) return false; } return true; } // computed name `[Symbol.iterator]` while (is(T_DOT)) { if (!nx()) return false; if (tt != T_NAME) return false; if (!nx()) return false; } if (!eat(T_RBRACK)) return false; return scanMemberAfterName(); } if (tokenIn(TS_STR_NUM_TPL)) { if (!nx()) return false; if (!eat(T_RBRACK)) return false; return scanMemberAfterName(); } return false;}