@xnoxs/flux-lang 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,1620 @@
1
- function map(arr, fn) { return arr.map(fn); } function join(arr, sep) { return arr.join(sep != null ? sep : ','); } "use strict"; const { T } = require("./lexer"); function _a(_b, _c) { const line = (_c?.line ?? "?"); const col = (_c?.col ?? "?"); const err = new Error(`[Parser ${line}:${col}] ${_b}`); err.name = "ParseError"; err.tok = _c; return err; } class Parser { constructor(tokens, pos) { this.tokens = tokens; this.pos = pos; } peek(_d = 0) { return (this.tokens[(this.pos + _d)] ?? { type: "EOF", value: null, line: 0, col: 0 }); } check(_e) { return (this.peek().type == _e); } at(_f, _g = null, _h = null, _i = null, _j = null, _k = null) { const t = this.peek().type; return ((((((t == _f) || ((_g != null) && (t == _g))) || ((_h != null) && (t == _h))) || ((_i != null) && (t == _i))) || ((_j != null) && (t == _j))) || ((_k != null) && (t == _k))); } eat(_e) { const _c = this.peek(); if ((_c.type != _e)) { throw _a(`Expected '${_e}', got '${_c.type}' (${JSON.stringify(_c.value)})`, _c); } this.pos = (this.pos + 1); return _c; } maybe(_e) { if (this.check(_e)) { this.pos = (this.pos + 1); return true; } return false; } skip() { const t = this.tokens[this.pos]; this.pos = (this.pos + 1); return t; } skipNewlines() { while (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); } } err(_b) { throw _a(_b, this.peek()); } parseBlock() { this.eat(T.COLON); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); this.eat(T.INDENT); const body = this.parseStmtList(); this.maybe(T.DEDENT); return body; } const stmt = this.parseOneStmt(); return [stmt]; } parseStmtList() { const stmts = []; while ((!this.check(T.DEDENT) && !this.check(T.EOF))) { this.skipNewlines(); if ((this.check(T.DEDENT) || this.check(T.EOF))) { break; } stmts.push(this.parseOneStmt()); } return stmts; } parse() { this.skipNewlines(); const body = []; while (!this.check(T.EOF)) { this.skipNewlines(); if (this.check(T.EOF)) { break; } body.push(this.parseOneStmt()); } return { type: "Program", body }; } parseOneStmt() { const _c = this.peek(); if (((_c.type == T.VAR) || (_c.type == T.VAL))) { return this.parseVarDecl(); } if ((_c.type == T.FN)) { return this.parseFnDecl(false); } if ((_c.type == T.ASYNC)) { return this.parseAsyncFn(); } if ((_c.type == T.CLASS)) { return this.parseClassDecl(); } if ((_c.type == T.DECLARE)) { return this.parseDeclareDecl(); } if ((_c.type == T.IF)) { return this.parseIf(); } if ((_c.type == T.FOR)) { return this.parseFor(); } if ((_c.type == T.WHILE)) { return this.parseWhile(); } if ((_c.type == T.DO)) { return this.parseDoWhile(); } if ((_c.type == T.MATCH)) { return this.parseMatch(); } if ((_c.type == T.RETURN)) { return this.parseReturn(); } if ((_c.type == T.BREAK)) { this.skip(); this.skipNewlines(); return { type: "BreakStmt", loc: _c }; } if ((_c.type == T.CONTINUE)) { this.skip(); this.skipNewlines(); return { type: "ContinueStmt", loc: _c }; } if ((_c.type == T.IMPORT)) { return this.parseImport(); } if ((_c.type == T.EXPORT)) { return this.parseExport(); } if ((_c.type == T.TRY)) { return this.parseTryCatch(); } if ((_c.type == T.THROW)) { return this.parseThrow(); } if ((_c.type == T.TYPE)) { return this.parseTypeDecl(); } if ((_c.type == T.INTERFACE)) { return this.parseInterfaceDecl(); } if ((_c.type == T.ENUM)) { return this.parseEnumDecl(); } return this.parseExprStmt(); } parseVarDecl() { const kind = ((this.peek().type == T.VAR) ? "var" : "val"); const loc = this.skip(); if (this.check(T.LBRACE)) { const pattern = this.parseObjectDestructurePattern(); this.eat(T.EQ); const init = this.parseExpr(); this.skipNewlines(); return { type: "DestructureDecl", kind, patternType: "object", pattern, init, loc }; } if (this.check(T.LBRACKET)) { const pattern = this.parseArrayDestructurePattern(); this.eat(T.EQ); const init = this.parseExpr(); this.skipNewlines(); return { type: "DestructureDecl", kind, patternType: "array", pattern, init, loc }; } const name = this.eat(T.IDENT).value; let typeAnn = null; if (this.maybe(T.COLON)) { typeAnn = this.parseTypeAnn(); } let init = null; if (this.maybe(T.EQ)) { init = this.parseExpr(); } this.skipNewlines(); return { type: "VarDecl", kind, name, typeAnn, init, loc }; } parseObjectDestructurePattern() { this.eat(T.LBRACE); const props = []; while ((!this.check(T.RBRACE) && !this.check(T.EOF))) { let rest = false; if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); rest = true; } const key = this.eat(T.IDENT).value; if (rest) { props.push({ key, alias: key, rest: true }); break; } let alias = key; if (this.maybe(T.COLON)) { alias = this.eat(T.IDENT).value; } let defaultVal = null; if (this.maybe(T.EQ)) { defaultVal = this.parseExpr(); } props.push({ key, alias, defaultVal, rest: false }); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACE); return props; } parseArrayDestructurePattern() { this.eat(T.LBRACKET); const items = []; while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) { if (this.check(T.COMMA)) { items.push(null); this.pos = (this.pos + 1); continue; } let rest = false; if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); rest = true; } const name = this.eat(T.IDENT).value; let defaultVal = null; if (this.maybe(T.EQ)) { defaultVal = this.parseExpr(); } items.push({ name, defaultVal, rest }); if (rest) { break; } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACKET); return items; } parseTypeAnn() { let name = this.parseIntersectionType(); while (this.check(T.PIPEB)) { this.pos = (this.pos + 1); name = ((name + " | ") + this.parseIntersectionType()); } return name; } parseIntersectionType() { let name = this.parseSingleType(); while (this.check(T.AMPERSAND)) { this.pos = (this.pos + 1); name = ((name + " & ") + this.parseSingleType()); } return name; } parseSingleType() { const _c = this.peek(); if ((_c.type == T.LBRACKET)) { this.pos = (this.pos + 1); const parts = []; while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) { parts.push(this.parseTypeAnn()); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACKET); return (("[" + parts.join(", ")) + "]"); } if ((_c.type == T.LBRACE)) { this.pos = (this.pos + 1); const pairs = []; while ((!this.check(T.RBRACE) && !this.check(T.EOF))) { if (this.check(T.LBRACKET)) { this.pos = (this.pos + 1); const iname = this.eat(T.IDENT).value; this.eat(T.COLON); const iktype = this.parseTypeAnn(); this.eat(T.RBRACKET); this.eat(T.COLON); const ivtype = this.parseTypeAnn(); pairs.push(`[${iname}: ${iktype}]: ${ivtype}`); } else { const key = (this.at(T.IDENT, T.STRING) ? this.skip().value : this.eat(T.IDENT).value); let optional = false; if (this.check(T.QUESTION)) { this.pos = (this.pos + 1); optional = true; } this.eat(T.COLON); const valType = this.parseTypeAnn(); const optStr = (optional ? "?" : ""); pairs.push(`${key}${optStr}: ${valType}`); } this.maybe(T.COMMA); } this.eat(T.RBRACE); return (("{ " + pairs.join(", ")) + " }"); } if ((_c.type == T.LPAREN)) { this.pos = (this.pos + 1); const inner = this.parseTypeAnn(); this.eat(T.RPAREN); return (("(" + inner) + ")"); } if (((_c.type == T.IDENT) && (_c.value == "keyof"))) { this.pos = (this.pos + 1); return ("keyof " + this.parseSingleType()); } if ((_c.type == T.TYPEOF)) { this.pos = (this.pos + 1); return ("typeof " + this.eat(T.IDENT).value); } if ((_c.type == T.READONLY)) { this.pos = (this.pos + 1); return ("readonly " + this.parseSingleType()); } if (((_c.type == T.IDENT) && (_c.value == "infer"))) { this.pos = (this.pos + 1); return ("infer " + this.eat(T.IDENT).value); } if ((_c.type == T.FN)) { this.pos = (this.pos + 1); const paramTypes = []; if (this.check(T.LPAREN)) { this.pos = (this.pos + 1); while ((!this.check(T.RPAREN) && !this.check(T.EOF))) { let pt = ""; if ((this.check(T.IDENT) && (this.peek(1).type == T.COLON))) { this.pos = (this.pos + 2); pt = this.parseTypeAnn(); } else { pt = this.parseTypeAnn(); } paramTypes.push(pt); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RPAREN); } let retType = "Void"; if (this.check(T.ARROW)) { this.pos = (this.pos + 1); retType = this.parseTypeAnn(); } return ((("fn(" + paramTypes.join(", ")) + ") -> ") + retType); } let name = ""; if ((((_c.type == T.IDENT) || (_c.type == T.CONST)) || (_c.type == T.TYPE))) { name = this.skip().value; } else { name = this.eat(T.IDENT).value; } if (this.check(T.EXTENDS)) { this.pos = (this.pos + 1); const constraint = this.parseSingleType(); this.eat(T.QUESTION); const thenType = this.parseTypeAnn(); this.eat(T.COLON); const elseType = this.parseTypeAnn(); return `${name} extends ${constraint} ? ${thenType} : ${elseType}`; } if (this.check(T.LT)) { this.pos = (this.pos + 1); const params = [this.parseTypeAnn()]; while (this.maybe(T.COMMA)) { params.push(this.parseTypeAnn()); } this.eat(T.GT); name = (((name + "<") + params.join(", ")) + ">"); } while ((this.check(T.LBRACKET) && (this.peek(1).type == T.RBRACKET))) { this.pos = (this.pos + 2); name = (name + "[]"); } if ((this.check(T.QUESTION) && (this.peek(1).type != T.DOT))) { this.pos = (this.pos + 1); name = (name + "?"); } return name; } isTypeAnnBeforeColon() { const saved = this.pos; try { const tok0 = this.peek(); if ((((tok0.type == T.LBRACKET) || (tok0.type == T.LBRACE)) || (tok0.type == T.LPAREN))) { let depth = 0; let i = 0; while ((this.tokens[(this.pos + i)] && (this.tokens[(this.pos + i)].type != T.EOF))) { const tt = this.tokens[(this.pos + i)].type; if (((((tt == T.LBRACKET) || (tt == T.LBRACE)) || (tt == T.LPAREN)) || (tt == T.LT))) { depth = (depth + 1); } if (((((tt == T.RBRACKET) || (tt == T.RBRACE)) || (tt == T.RPAREN)) || (tt == T.GT))) { depth = (depth - 1); } i = (i + 1); if ((depth == 0)) { break; } } if ((this.tokens[(this.pos + i)] && (this.tokens[(this.pos + i)].type == T.QUESTION))) { i = (i + 1); } const nextType = this.tokens[(this.pos + i)]?.type; this.pos = saved; return (nextType == T.COLON); } if (((((tok0.type != T.IDENT) && (tok0.type != T.TYPEOF)) && (tok0.type != T.READONLY)) && (tok0.type != T.FN))) { this.pos = saved; return false; } this.parseTypeAnn(); const result = (this.check(T.NEWLINE) || this.check(T.EOF)); this.pos = saved; return result; } catch (_j) { this.pos = saved; return false; } } isColonReturnType() { const saved = this.pos; try { this.pos = (this.pos + 1); if (!this.at(T.IDENT, T.LBRACKET, T.LBRACE, T.LPAREN, T.FN, T.READONLY, T.TYPEOF)) { this.pos = saved; return false; } this.parseTypeAnn(); const result = (this.check(T.NEWLINE) || this.check(T.EOF)); this.pos = saved; return result; } catch (_j) { this.pos = saved; return false; } } parseTypeDecl() { const loc = this.eat(T.TYPE); const name = this.eat(T.IDENT).value; const typeParams = []; if (this.check(T.LT)) { this.pos = (this.pos + 1); while ((!this.check(T.GT) && !this.check(T.EOF))) { typeParams.push(this.eat(T.IDENT).value); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.GT); } this.eat(T.EQ); const variants = []; let running = true; while (running) { const vname = this.eat(T.IDENT).value; const fields = []; const fieldTypes = { }; if (this.check(T.LPAREN)) { this.eat(T.LPAREN); while ((!this.check(T.RPAREN) && !this.check(T.EOF))) { const ftok = this.peek(); let fname = ""; if ((ftok.type == T.IDENT)) { fname = this.skip().value; } else { fname = this.skip().value; } if (this.maybe(T.COLON)) { fieldTypes[fname] = this.parseTypeAnn(); } fields.push(fname); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RPAREN); } variants.push({ name: vname, fields, fieldTypes }); if (!this.check(T.PIPEB)) { running = false; } else { this.pos = (this.pos + 1); } } this.skipNewlines(); return { type: "TypeDecl", name, variants, loc }; } parseInterfaceDecl() { const loc = this.eat(T.INTERFACE); const name = this.eat(T.IDENT).value; const typeParams = []; if (this.check(T.LT)) { this.pos = (this.pos + 1); while ((!this.check(T.GT) && !this.check(T.EOF))) { typeParams.push(this.eat(T.IDENT).value); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.GT); } const superInterfaces = []; if (this.maybe(T.EXTENDS)) { superInterfaces.push(this.eat(T.IDENT).value); while (this.maybe(T.COMMA)) { superInterfaces.push(this.eat(T.IDENT).value); } } this.eat(T.COLON); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); } this.eat(T.INDENT); const members = []; while ((!this.check(T.DEDENT) && !this.check(T.EOF))) { this.skipNewlines(); if ((this.check(T.DEDENT) || this.check(T.EOF))) { break; } const mods = this.parseAccessModifiers(); if (this.check(T.FN)) { this.eat(T.FN); const mname = this.eat(T.IDENT).value; const params = this.parseParamList(); let retType = null; if (this.maybe(T.ARROW)) { retType = this.parseTypeAnn(); } this.skipNewlines(); members.push({ kind: "method", name: mname, params, retType, modifiers: mods, isAsync: false }); } else if (this.check(T.IDENT)) { const fname = this.eat(T.IDENT).value; let optional = false; if (this.check(T.QUESTION)) { this.pos = (this.pos + 1); optional = true; } this.eat(T.COLON); const ftype = this.parseTypeAnn(); this.skipNewlines(); members.push({ kind: "field", name: fname, typeAnn: ftype, optional, modifiers: mods }); } else { this.skip(); } } this.maybe(T.DEDENT); return { type: "InterfaceDecl", name, typeParams, superInterfaces, members, loc }; } parseEnumDecl() { const loc = this.eat(T.ENUM); const name = this.eat(T.IDENT).value; this.eat(T.COLON); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); this.eat(T.INDENT); } const members = []; let autoIndex = 0; while ((!this.check(T.DEDENT) && !this.check(T.EOF))) { this.skipNewlines(); if ((this.check(T.DEDENT) || this.check(T.EOF))) { break; } const mname = this.eat(T.IDENT).value; let value = { type: "NumberLit", value: autoIndex }; if (this.maybe(T.EQ)) { const parsed = this.parseExpr(); value = parsed; if ((parsed.type == "NumberLit")) { autoIndex = parsed.value; } } else { value = { type: "NumberLit", value: autoIndex }; } autoIndex = (autoIndex + 1); this.skipNewlines(); members.push({ name: mname, value }); } this.maybe(T.DEDENT); return { type: "EnumDecl", name, members, loc }; } parseIf() { const loc = this.eat(T.IF); const cond = this.parseExpr(); const then = this.parseBlock(); const elseifs = []; let else_ = null; this.skipNewlines(); while (this.check(T.ELSE)) { this.pos = (this.pos + 1); if (this.check(T.IF)) { this.pos = (this.pos + 1); const eic = this.parseExpr(); const eib = this.parseBlock(); elseifs.push({ cond: eic, body: eib }); this.skipNewlines(); } else { else_ = this.parseBlock(); break; } } return { type: "IfStmt", cond, then, elseifs, else_, loc }; } parseFor() { const loc = this.eat(T.FOR); let isAwait = false; if (this.check(T.AWAIT)) { this.pos = (this.pos + 1); isAwait = true; } const varName = this.eat(T.IDENT).value; this.eat(T.IN); const iter = this.parseExpr(); const body = this.parseBlock(); return { type: "ForInStmt", var: varName, iter, body, isAwait, loc }; } parseWhile() { const loc = this.eat(T.WHILE); const cond = this.parseExpr(); const body = this.parseBlock(); return { type: "WhileStmt", cond, body, loc }; } parseDoWhile() { const loc = this.eat(T.DO); const body = this.parseBlock(); this.skipNewlines(); this.eat(T.WHILE); const cond = this.parseExpr(); this.skipNewlines(); return { type: "DoWhileStmt", body, cond, loc }; } parseMatch() { const loc = this.eat(T.MATCH); const subject = this.parseExpr(); this.eat(T.COLON); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); } this.eat(T.INDENT); const arms = []; while ((!this.check(T.DEDENT) && !this.check(T.EOF))) { this.skipNewlines(); if ((this.check(T.DEDENT) || this.check(T.EOF))) { break; } this.eat(T.WHEN); const pattern = this.parsePattern(); let guard = null; if (this.check(T.IF)) { this.pos = (this.pos + 1); guard = this.parseExpr(); } if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const expr = this.parseExpr(); this.skipNewlines(); arms.push({ pattern, guard, body: [{ type: "ExprStmt", expr }], inline: true }); } else if (this.check(T.COLON)) { const isInline = (this.peek(1).type != T.NEWLINE); const body = this.parseBlock(); const inlineArm = ((isInline && (body.length == 1)) && (body[0].type == "ExprStmt")); arms.push({ pattern, guard, body, inline: inlineArm }); } } this.maybe(T.DEDENT); return { type: "MatchStmt", subject, arms, loc }; } parsePattern() { if (this.check(T.WILDCARD)) { this.skip(); return { type: "WildcardPat" }; } if ((this.check(T.IDENT) && (this.peek(1).type == T.LPAREN))) { const vname = this.eat(T.IDENT).value; this.eat(T.LPAREN); const bindings = []; while ((!this.check(T.RPAREN) && !this.check(T.EOF))) { if (this.check(T.WILDCARD)) { bindings.push("_"); this.pos = (this.pos + 1); } else { bindings.push(this.eat(T.IDENT).value); } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RPAREN); return { type: "VariantPat", variant: vname, bindings }; } let left = this.parsePrimary(); while (this.check(T.DOT)) { this.pos = (this.pos + 1); const prop = this.skip().value; left = { type: "MemberExpr", obj: left, prop }; } if (this.check(T.DOTDOT)) { this.pos = (this.pos + 1); const right = this.parsePrimary(); return { type: "RangePat", start: left, end: right }; } return { type: "LiteralPat", value: left }; } parseReturn() { const loc = this.eat(T.RETURN); let value = null; if (!this.at(T.NEWLINE, T.EOF, T.DEDENT)) { value = this.parseExpr(); } this.skipNewlines(); return { type: "ReturnStmt", value, loc }; } parseTryCatch() { const loc = this.eat(T.TRY); const tryBody = this.parseBlock(); let catchParam = null; let catchBody = null; let finallyBody = null; this.skipNewlines(); if (this.check(T.CATCH)) { this.pos = (this.pos + 1); if (this.check(T.LPAREN)) { this.pos = (this.pos + 1); catchParam = this.eat(T.IDENT).value; if (this.maybe(T.COLON)) { this.parseTypeAnn(); } this.eat(T.RPAREN); } catchBody = this.parseBlock(); this.skipNewlines(); } if (this.check(T.FINALLY)) { this.pos = (this.pos + 1); finallyBody = this.parseBlock(); } return { type: "TryCatchStmt", tryBody, catchParam, catchBody, finallyBody, loc }; } parseThrow() { const loc = this.eat(T.THROW); const value = this.parseExpr(); this.skipNewlines(); return { type: "ThrowStmt", value, loc }; } parseImport() { this.eat(T.IMPORT); if (this.check(T.STAR)) { this.pos = (this.pos + 1); this.eat(T.AS); const namespaceName = this.eat(T.IDENT).value; this.eat(T.FROM); const source = this.eat(T.STRING).value; this.skipNewlines(); return { type: "ImportDecl", names: [], defaultName: null, namespaceName, source }; } if (this.check(T.IDENT)) { const defaultName = this.eat(T.IDENT).value; this.eat(T.FROM); const source = this.eat(T.STRING).value; this.skipNewlines(); return { type: "ImportDecl", names: [], defaultName, source }; } const names = []; if (this.maybe(T.LBRACE)) { while ((!this.check(T.RBRACE) && !this.check(T.EOF))) { const name = this.eat(T.IDENT).value; let alias = name; if (this.check(T.AS)) { this.pos = (this.pos + 1); alias = this.eat(T.IDENT).value; } names.push({ name, alias }); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACE); } this.eat(T.FROM); const source = this.eat(T.STRING).value; this.skipNewlines(); return { type: "ImportDecl", names, defaultName: null, source }; } parseExport() { this.eat(T.EXPORT); if (this.check(T.DEFAULT)) { this.pos = (this.pos + 1); if (this.check(T.ASYNC)) { this.pos = (this.pos + 1); const decl = this.parseFnDecl(true); return { type: "ExportDecl", isDefault: true, decl }; } if (this.check(T.FN)) { const decl = this.parseFnDecl(false); return { type: "ExportDecl", isDefault: true, decl }; } const value = this.parseExpr(); this.skipNewlines(); return { type: "ExportDecl", isDefault: true, decl: value }; } if (this.check(T.ASYNC)) { this.pos = (this.pos + 1); if (!this.check(T.FN)) { this.err("Expected fn after async"); } const decl = this.parseFnDecl(true); return { type: "ExportDecl", isDefault: false, decl }; } const decl = this.parseOneStmt(); return { type: "ExportDecl", isDefault: false, decl }; } parseFnDecl(_l = false) { const loc = this.eat(T.FN); let name = null; if ((((((((this.check(T.IDENT) || this.check(T.NEW)) || this.check(T.FROM)) || this.check(T.AS)) || this.check(T.DEFAULT)) || this.check(T.IS)) || this.check(T.IN)) || this.check(T.TYPE))) { name = this.skip().value; } const params = this.parseParamList(); let retType = null; if (this.check(T.ARROW)) { this.pos = (this.pos + 1); if (this.isTypeAnnBeforeColon()) { retType = this.parseTypeAnn(); const body = this.parseBlock(); return { type: "FnDecl", name, params, retType, body, inline: false, async: _l, loc }; } const body = this.parseExpr(); this.skipNewlines(); return { type: "FnDecl", name, params, retType: null, body, inline: true, async: _l, loc }; } if (this.check(T.COLON)) { if (this.isColonReturnType()) { this.pos = (this.pos + 1); retType = this.parseTypeAnn(); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); } if (this.check(T.INDENT)) { this.pos = (this.pos + 1); const body = this.parseStmtList(); this.maybe(T.DEDENT); return { type: "FnDecl", name, params, retType, body, inline: false, async: _l, loc }; } const stmt = this.parseOneStmt(); return { type: "FnDecl", name, params, retType, body: [stmt], inline: false, async: _l, loc }; } const body = this.parseBlock(); return { type: "FnDecl", name, params, retType: null, body, inline: false, async: _l, loc }; } this.err("Expected -> or : after function signature"); } parseAsyncFn() { this.eat(T.ASYNC); if (!this.check(T.FN)) { this.err("Expected fn after async"); } return this.parseFnDecl(true); } parseDeclareDecl() { const loc = this.eat(T.DECLARE); const _c = this.peek(); if (((_c.type == T.FN) || (_c.type == T.ASYNC))) { const _l = (_c.type == T.ASYNC); this.skip(); if (_l) { this.eat(T.FN); } const name = (this.check(T.IDENT) ? this.skip().value : null); const params = this.parseParamList(); let retType = null; if (this.check(T.ARROW)) { this.pos = (this.pos + 1); retType = this.parseTypeAnn(); } else if (this.check(T.COLON)) { this.pos = (this.pos + 1); retType = this.parseTypeAnn(); } this.skipNewlines(); const decl = { type: "FnDecl", name, params, retType, body: [], inline: false, async: _l, loc: _c }; return { type: "DeclareDecl", decl, loc }; } if (((_c.type == T.VAL) || (_c.type == T.VAR))) { const kind = ((_c.type == T.VAR) ? "var" : "val"); this.skip(); const name = this.eat(T.IDENT).value; let typeAnn = null; if (this.maybe(T.COLON)) { typeAnn = this.parseTypeAnn(); } this.skipNewlines(); const decl = { type: "VarDecl", kind, name, typeAnn, init: null, loc: _c }; return { type: "DeclareDecl", decl, loc }; } if ((_c.type == T.CLASS)) { this.skip(); const name = this.eat(T.IDENT).value; this.skipNewlines(); const decl = { type: "ClassDecl", name, fields: [], methods: [], loc: _c }; return { type: "DeclareDecl", decl, loc }; } this.err("Expected fn, async fn, val, var, or class after declare"); } parseParamList() { this.eat(T.LPAREN); const params = []; while ((!this.check(T.RPAREN) && !this.check(T.EOF))) { let rest = false; if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); rest = true; } const name = this.eat(T.IDENT).value; let optional = false; let typeAnn = null; if ((!rest && this.check(T.QUESTION))) { this.pos = (this.pos + 1); optional = true; } if ((!rest && this.maybe(T.COLON))) { typeAnn = this.parseTypeAnn(); } let defaultVal = null; if ((!rest && this.maybe(T.EQ))) { defaultVal = this.parseExpr(); } params.push({ name, typeAnn, optional, defaultVal, rest }); if (rest) { break; } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RPAREN); return params; } parseAccessModifiers() { const mods = new Set([]); let running = true; while (running) { const t = this.peek().type; if ((((((((t == T.PRIVATE) || (t == T.PUBLIC)) || (t == T.PROTECTED)) || (t == T.READONLY)) || (t == T.STATIC)) || (t == T.ABSTRACT)) || (t == T.OVERRIDE))) { mods.add(this.skip().value); } else { running = false; } } return mods; } parseClassDecl() { const loc = this.eat(T.CLASS); const name = this.eat(T.IDENT).value; let superClass = null; const interfaces = []; const typeParams = []; if (this.check(T.LT)) { this.pos = (this.pos + 1); typeParams.push(this.eat(T.IDENT).value); while (this.maybe(T.COMMA)) { typeParams.push(this.eat(T.IDENT).value); } this.eat(T.GT); } if (this.maybe(T.EXTENDS)) { superClass = this.eat(T.IDENT).value; } if (this.maybe(T.IMPLEMENTS)) { interfaces.push(this.eat(T.IDENT).value); while (this.maybe(T.COMMA)) { interfaces.push(this.eat(T.IDENT).value); } } this.eat(T.COLON); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); this.eat(T.INDENT); } const fields = []; const methods = []; while ((!this.check(T.DEDENT) && !this.check(T.EOF))) { this.skipNewlines(); if ((this.check(T.DEDENT) || this.check(T.EOF))) { break; } const mods = this.parseAccessModifiers(); if (this.check(T.FN)) { const m = this.parseFnDecl(false); m.modifiers = mods; methods.push(m); } else if (this.check(T.ASYNC)) { const m = this.parseAsyncFn(); m.modifiers = mods; methods.push(m); } else if ((this.check(T.STATIC) && (this.peek(1).type == T.FN))) { this.skip(); const m = this.parseFnDecl(false); m.modifiers = mods; m.modifiers.add("static"); methods.push(m); } else if (this.check(T.IDENT)) { const fname = this.eat(T.IDENT).value; let optional = false; if (this.check(T.QUESTION)) { this.pos = (this.pos + 1); optional = true; } this.eat(T.COLON); const ftype = this.parseTypeAnn(); this.skipNewlines(); fields.push({ name: fname, typeAnn: ftype, optional, modifiers: mods }); } else { this.skip(); } } this.maybe(T.DEDENT); return { type: "ClassDecl", name, typeParams, superClass, interfaces, fields, methods, loc }; } parseExprStmt() { const expr = this.parseExpr(); this.skipNewlines(); return { type: "ExprStmt", expr }; } parseExpr() { return this.parsePipe(); } parsePipe() { let left = this.parseAssign(); let running = true; while (running) { if (this.check(T.PIPE)) { this.pos = (this.pos + 1); const right = this.parseAssign(); left = { type: "PipeExpr", left, right }; } else if (this.check(T.NEWLINE)) { let i = 1; while ((this.peek(i).type == T.NEWLINE)) { i = (i + 1); } if ((this.peek(i).type == T.PIPE)) { while (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); } this.pos = (this.pos + 1); const right = this.parseAssign(); left = { type: "PipeExpr", left, right }; } else { running = false; } } else { running = false; } } return left; } parseAssign() { const left = this.parseTernary(); const t = this.peek().type; let op = ""; if ((t == T.EQ)) { op = "="; } else if ((t == T.PLUSEQ)) { op = "+="; } else if ((t == T.MINUSEQ)) { op = "-="; } else if ((t == T.STAREQ)) { op = "*="; } else if ((t == T.SLASHEQ)) { op = "/="; } else if ((t == T.PERCENTEQ)) { op = "%="; } if (op) { this.pos = (this.pos + 1); return { type: "AssignExpr", target: left, op, value: this.parseAssign() }; } return left; } parseTernary() { const cond = this.parseNullish(); if (this.maybe(T.QUESTION)) { const then = this.parseNullish(); this.eat(T.COLON); const else_ = this.parseTernary(); return { type: "TernaryExpr", cond, then, else_ }; } return cond; } parseNullish() { let l = this.parseOr(); while (this.check(T.NULLISH)) { this.pos = (this.pos + 1); const r = this.parseOr(); l = { type: "BinaryExpr", op: "??", left: l, right: r }; } return l; } parseOr() { let l = this.parseAnd(); while ((this.check(T.OR) || this.check(T.OROR))) { this.pos = (this.pos + 1); const r = this.parseAnd(); l = { type: "BinaryExpr", op: "||", left: l, right: r }; } return l; } parseAnd() { let l = this.parseBitOr(); while ((this.check(T.AND) || this.check(T.ANDAND))) { this.pos = (this.pos + 1); const r = this.parseBitOr(); l = { type: "BinaryExpr", op: "&&", left: l, right: r }; } return l; } parseBitOr() { let l = this.parseBitXor(); while (this.check(T.PIPEB)) { this.pos = (this.pos + 1); const r = this.parseBitXor(); l = { type: "BinaryExpr", op: "|", left: l, right: r }; } return l; } parseBitXor() { let l = this.parseBitAnd(); while (this.check(T.CARET)) { this.pos = (this.pos + 1); const r = this.parseBitAnd(); l = { type: "BinaryExpr", op: "^", left: l, right: r }; } return l; } parseBitAnd() { let l = this.parseEq(); while (this.check(T.AMPERSAND)) { this.pos = (this.pos + 1); const r = this.parseEq(); l = { type: "BinaryExpr", op: "&", left: l, right: r }; } return l; } parseEq() { let l = this.parseRel(); while (this.at(T.EQEQ, T.NEQ, T.EQEQEQ, T.NEQEQ)) { const op = this.skip().value; const r = this.parseRel(); l = { type: "BinaryExpr", op, left: l, right: r }; } return l; } parseRel() { let l = this.parseShift(); while ((this.at(T.LT, T.LTE, T.GT, T.GTE) || this.check(T.IN))) { const op = this.skip().value; const r = this.parseShift(); l = { type: "BinaryExpr", op, left: l, right: r }; } return l; } parseShift() { let l = this.parseRange(); while (this.at(T.LSHIFT, T.RSHIFT)) { const op = this.skip().value; const r = this.parseRange(); l = { type: "BinaryExpr", op, left: l, right: r }; } return l; } parseRange() { const l = this.parseAdd(); if (this.check(T.DOTDOT)) { this.pos = (this.pos + 1); const r = this.parseAdd(); return { type: "RangeExpr", start: l, end: r }; } return l; } parseAdd() { let l = this.parseMul(); while (this.at(T.PLUS, T.MINUS)) { const op = this.skip().value; const r = this.parseMul(); l = { type: "BinaryExpr", op, left: l, right: r }; } return l; } parseMul() { let l = this.parsePow(); while (this.at(T.STAR, T.SLASH, T.PERCENT)) { const op = this.skip().value; const r = this.parsePow(); l = { type: "BinaryExpr", op, left: l, right: r }; } return l; } parsePow() { const l = this.parseUnary(); if (this.check(T.STARSTAR)) { this.pos = (this.pos + 1); return { type: "BinaryExpr", op: "**", left: l, right: this.parsePow() }; } return l; } parseUnary() { if (this.check(T.MINUS)) { this.pos = (this.pos + 1); return { type: "UnaryExpr", op: "-", operand: this.parseUnary() }; } if (this.check(T.NOT)) { this.pos = (this.pos + 1); return { type: "UnaryExpr", op: "!", operand: this.parseUnary() }; } if (this.check(T.BANG)) { this.pos = (this.pos + 1); return { type: "UnaryExpr", op: "!", operand: this.parseUnary() }; } if (this.check(T.TILDE)) { this.pos = (this.pos + 1); return { type: "UnaryExpr", op: "~", operand: this.parseUnary() }; } if (this.check(T.PLUSPLUS)) { this.pos = (this.pos + 1); return { type: "UpdateExpr", op: "++", prefix: true, operand: this.parseUnary() }; } if (this.check(T.MINUSMINUS)) { this.pos = (this.pos + 1); return { type: "UpdateExpr", op: "--", prefix: true, operand: this.parseUnary() }; } if (this.check(T.AWAIT)) { this.pos = (this.pos + 1); return { type: "AwaitExpr", operand: this.parseUnary() }; } if (this.check(T.TYPEOF)) { this.pos = (this.pos + 1); return { type: "TypeofExpr", operand: this.parseUnary() }; } return this.parseLambdaOrPostfix(); } parseLambdaOrPostfix() { const saved = this.pos; if ((this.check(T.IDENT) || this.check(T.WILDCARD))) { const name = (this.skip().value ?? "_"); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const body = this.parseExpr(); return { type: "LambdaExpr", params: [{ name }], body }; } this.pos = saved; } return this.parsePostfix(); } parsePostfix() { let expr = this.parsePrimary(); let running = true; while (running) { if (this.check(T.PLUSPLUS)) { this.pos = (this.pos + 1); expr = { type: "UpdateExpr", op: "++", prefix: false, operand: expr }; } else if (this.check(T.MINUSMINUS)) { this.pos = (this.pos + 1); expr = { type: "UpdateExpr", op: "--", prefix: false, operand: expr }; } else if (this.check(T.DOT)) { this.pos = (this.pos + 1); const propTok = this.peek(); const prop = this.skip().value; expr = { type: "MemberExpr", obj: expr, prop }; } else if (this.check(T.QUESTIONDOT)) { this.pos = (this.pos + 1); if (this.check(T.LPAREN)) { const args = this.parseArgList(); expr = { type: "OptCallExpr", callee: expr, args }; } else if (this.check(T.LBRACKET)) { this.pos = (this.pos + 1); const idx = this.parseExpr(); this.eat(T.RBRACKET); expr = { type: "OptIndexExpr", obj: expr, idx }; } else { const prop = this.skip().value; expr = { type: "OptMemberExpr", obj: expr, prop }; } } else if (this.check(T.INSTANCEOF)) { this.pos = (this.pos + 1); const right = this.eat(T.IDENT).value; expr = { type: "BinaryExpr", op: "instanceof", left: expr, right: { type: "Identifier", name: right } }; } else if (this.check(T.AS)) { this.pos = (this.pos + 1); if (this.check(T.CONST)) { this.pos = (this.pos + 1); expr = { type: "AsConstExpr", expr }; } else { const castType = this.parseTypeAnn(); expr = { type: "CastExpr", expr, castType }; } } else if (this.check(T.SATISFIES)) { this.pos = (this.pos + 1); const satType = this.parseTypeAnn(); expr = { type: "SatisfiesExpr", expr, satType }; } else if (this.check(T.IS)) { this.pos = (this.pos + 1); const isType = this.parseTypeAnn(); expr = { type: "IsExpr", expr, isType }; } else if (this.check(T.BANG)) { this.pos = (this.pos + 1); expr = { type: "NonNullExpr", expr }; } else if (this.check(T.LBRACKET)) { this.pos = (this.pos + 1); const idx = this.parseExpr(); this.eat(T.RBRACKET); expr = { type: "IndexExpr", obj: expr, idx }; } else if (this.check(T.LPAREN)) { const args = this.parseArgList(); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const body = this.parseExpr(); const lambdaParams = args.map((_f) => { name: ((_f.type == "Identifier") ? _f.name : "_") }); return { type: "LambdaExpr", params: lambdaParams, body }; } expr = { type: "CallExpr", callee: expr, args }; } else { running = false; } } return expr; } parseArgList() { this.eat(T.LPAREN); const args = []; while ((!this.check(T.RPAREN) && !this.check(T.EOF))) { if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); args.push({ type: "SpreadExpr", expr: this.parseExpr() }); } else { args.push(this.parseExpr()); } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RPAREN); return args; } parsePrimary() { const _c = this.peek(); if ((_c.type == T.NUMBER)) { this.pos = (this.pos + 1); return { type: "NumberLit", value: _c.value, loc: _c }; } if ((_c.type == T.BOOL)) { this.pos = (this.pos + 1); return { type: "BoolLit", value: _c.value, loc: _c }; } if ((_c.type == T.NULL)) { this.pos = (this.pos + 1); return { type: "NullLit", loc: _c }; } if ((_c.type == T.SELF)) { this.pos = (this.pos + 1); return { type: "SelfExpr", loc: _c }; } if ((_c.type == T.WILDCARD)) { this.pos = (this.pos + 1); return { type: "Identifier", name: "_", loc: _c }; } if ((_c.type == T.IDENT)) { this.pos = (this.pos + 1); return { type: "Identifier", name: _c.value, loc: _c }; } if ((_c.type == T.STRING)) { this.pos = (this.pos + 1); if ((_c.value && _c.value.template)) { return { type: "TemplateLit", parts: _c.value.parts, loc: _c }; } return { type: "StringLit", value: _c.value, loc: _c }; } if ((_c.type == T.REGEX)) { this.pos = (this.pos + 1); return { type: "RegexLit", value: _c.value, loc: _c }; } if ((_c.type == T.NEW)) { this.pos = (this.pos + 1); const callee = this.eat(T.IDENT).value; const args = this.parseArgList(); return { type: "NewExpr", callee, args }; } if ((_c.type == T.MATCH)) { return this.parseMatch(); } if ((_c.type == T.FN)) { this.pos = (this.pos + 1); const params = this.parseParamList(); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); if (this.isTypeAnnBeforeColon()) { const retType = this.parseTypeAnn(); const body = this.parseBlock(); return { type: "FnDecl", name: null, params, retType, body, inline: false, async: false, loc: _c }; } const body = this.parseExpr(); return { type: "FnDecl", name: null, params, retType: null, body, inline: true, async: false, loc: _c }; } if (this.check(T.COLON)) { const body = this.parseBlock(); return { type: "FnDecl", name: null, params, retType: null, body, inline: false, async: false, loc: _c }; } this.err("Expected -> or : after anonymous fn"); } if ((_c.type == T.LPAREN)) { this.pos = (this.pos + 1); if (this.check(T.RPAREN)) { this.pos = (this.pos + 1); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const body = this.parseExpr(); return { type: "LambdaExpr", params: [], body }; } return { type: "NullLit" }; } const first = this.parseExpr(); if (this.check(T.RPAREN)) { this.pos = (this.pos + 1); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const body = this.parseExpr(); const pname = ((first.type == "Identifier") ? first.name : "_"); return { type: "LambdaExpr", params: [{ name: pname }], body }; } return first; } const items = [first]; while (this.maybe(T.COMMA)) { items.push(this.parseExpr()); } this.eat(T.RPAREN); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const body = this.parseExpr(); const lambdaParams = items.map((_m) => { name: ((_m.type == "Identifier") ? _m.name : "_") }); return { type: "LambdaExpr", params: lambdaParams, body }; } return items[0]; } if ((_c.type == T.LBRACKET)) { this.pos = (this.pos + 1); const items = []; while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) { if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); items.push({ type: "SpreadExpr", expr: this.parseExpr() }); } else { items.push(this.parseExpr()); } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACKET); return { type: "ArrayExpr", items }; } if ((_c.type == T.LBRACE)) { this.pos = (this.pos + 1); const pairs = []; while ((!this.check(T.RBRACE) && !this.check(T.EOF))) { if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); pairs.push({ spread: true, value: this.parseExpr() }); if (!this.maybe(T.COMMA)) { break; } continue; } const key = (this.check(T.STRING) ? this.eat(T.STRING).value : (this.check(T.IDENT) ? this.skip().value : this.skip().value)); if (!this.check(T.COLON)) { pairs.push({ key, value: { type: "Identifier", name: key } }); } else { this.eat(T.COLON); const v = this.parseExpr(); pairs.push({ key, value: v }); } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACE); return { type: "ObjectExpr", pairs }; } this.err(`Unexpected token: ${_c.type} (${JSON.stringify(_c.value)})`); } } module.exports.Parser = Parser; function makeParser(_n) { return new Parser(_n, 0); } module.exports.makeParser = makeParser;
1
+ // ── Flux stdlib ──
2
+
3
+ function map(arr, fn) { return arr.map(fn); }
4
+
5
+ function join(arr, sep) { return arr.join(sep != null ? sep : ','); }
6
+ // ── end stdlib ──
7
+
8
+ // Generated by Flux Transpiler v3.2.0
9
+ "use strict";
10
+
11
+ const { T } = require("./lexer");
12
+ function makeParseError(msg, tok) {
13
+ const line = (tok?.line ?? "?");
14
+ const col = (tok?.col ?? "?");
15
+ const err = new Error(`[Parser ${line}:${col}] ${msg}`);
16
+ err.name = "ParseError";
17
+ err.tok = tok;
18
+ return err;
19
+ }
20
+ class Parser {
21
+ constructor(tokens, pos) {
22
+ this.tokens = tokens;
23
+ this.pos = pos;
24
+ }
25
+
26
+ peek(n = 0) {
27
+ return (this.tokens[(this.pos + n)] ?? { type: "EOF", value: null, line: 0, col: 0 });
28
+ }
29
+
30
+ check(typ) {
31
+ return (this.peek().type == typ);
32
+ }
33
+
34
+ at(a, b = null, c = null, d = null, e = null, f = null) {
35
+ const t = this.peek().type;
36
+ return ((((((t == a) || ((b != null) && (t == b))) || ((c != null) && (t == c))) || ((d != null) && (t == d))) || ((e != null) && (t == e))) || ((f != null) && (t == f)));
37
+ }
38
+
39
+ eat(typ) {
40
+ const tok = this.peek();
41
+ if ((tok.type != typ)) {
42
+ throw makeParseError(`Expected '${typ}', got '${tok.type}' (${JSON.stringify(tok.value)})`, tok);
43
+ }
44
+ this.pos = (this.pos + 1);
45
+ return tok;
46
+ }
47
+
48
+ maybe(typ) {
49
+ if (this.check(typ)) {
50
+ this.pos = (this.pos + 1);
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+
56
+ skip() {
57
+ const t = this.tokens[this.pos];
58
+ this.pos = (this.pos + 1);
59
+ return t;
60
+ }
61
+
62
+ skipNewlines() {
63
+ while (this.check(T.NEWLINE)) {
64
+ this.pos = (this.pos + 1);
65
+ }
66
+ }
67
+
68
+ err(msg) {
69
+ throw makeParseError(msg, this.peek());
70
+ }
71
+
72
+ parseBlock() {
73
+ this.eat(T.COLON);
74
+ if (this.check(T.NEWLINE)) {
75
+ this.pos = (this.pos + 1);
76
+ this.eat(T.INDENT);
77
+ const body = this.parseStmtList();
78
+ this.maybe(T.DEDENT);
79
+ return body;
80
+ }
81
+ const stmt = this.parseOneStmt();
82
+ return [stmt];
83
+ }
84
+
85
+ parseStmtList() {
86
+ const stmts = [];
87
+ while ((!this.check(T.DEDENT) && !this.check(T.EOF))) {
88
+ this.skipNewlines();
89
+ if ((this.check(T.DEDENT) || this.check(T.EOF))) {
90
+ break;
91
+ }
92
+ stmts.push(this.parseOneStmt());
93
+ }
94
+ return stmts;
95
+ }
96
+
97
+ parse() {
98
+ this.skipNewlines();
99
+ const body = [];
100
+ while (!this.check(T.EOF)) {
101
+ this.skipNewlines();
102
+ if (this.check(T.EOF)) {
103
+ break;
104
+ }
105
+ body.push(this.parseOneStmt());
106
+ }
107
+ return { type: "Program", body };
108
+ }
109
+
110
+ parseOneStmt() {
111
+ const tok = this.peek();
112
+ if (((tok.type == T.VAR) || (tok.type == T.VAL))) {
113
+ return this.parseVarDecl();
114
+ }
115
+ if ((tok.type == T.FN)) {
116
+ return this.parseFnDecl(false);
117
+ }
118
+ if ((tok.type == T.ASYNC)) {
119
+ return this.parseAsyncFn();
120
+ }
121
+ if ((tok.type == T.CLASS)) {
122
+ return this.parseClassDecl();
123
+ }
124
+ if ((tok.type == T.DECLARE)) {
125
+ return this.parseDeclareDecl();
126
+ }
127
+ if ((tok.type == T.IF)) {
128
+ return this.parseIf();
129
+ }
130
+ if ((tok.type == T.FOR)) {
131
+ return this.parseFor();
132
+ }
133
+ if ((tok.type == T.WHILE)) {
134
+ return this.parseWhile();
135
+ }
136
+ if ((tok.type == T.DO)) {
137
+ return this.parseDoWhile();
138
+ }
139
+ if ((tok.type == T.MATCH)) {
140
+ return this.parseMatch();
141
+ }
142
+ if ((tok.type == T.RETURN)) {
143
+ return this.parseReturn();
144
+ }
145
+ if ((tok.type == T.BREAK)) {
146
+ this.skip();
147
+ this.skipNewlines();
148
+ return { type: "BreakStmt", loc: tok };
149
+ }
150
+ if ((tok.type == T.CONTINUE)) {
151
+ this.skip();
152
+ this.skipNewlines();
153
+ return { type: "ContinueStmt", loc: tok };
154
+ }
155
+ if ((tok.type == T.IMPORT)) {
156
+ return this.parseImport();
157
+ }
158
+ if ((tok.type == T.EXPORT)) {
159
+ return this.parseExport();
160
+ }
161
+ if ((tok.type == T.TRY)) {
162
+ return this.parseTryCatch();
163
+ }
164
+ if ((tok.type == T.THROW)) {
165
+ return this.parseThrow();
166
+ }
167
+ if ((tok.type == T.TYPE)) {
168
+ return this.parseTypeDecl();
169
+ }
170
+ if ((tok.type == T.INTERFACE)) {
171
+ return this.parseInterfaceDecl();
172
+ }
173
+ if ((tok.type == T.ENUM)) {
174
+ return this.parseEnumDecl();
175
+ }
176
+ return this.parseExprStmt();
177
+ }
178
+
179
+ parseVarDecl() {
180
+ const kind = ((this.peek().type == T.VAR) ? "var" : "val");
181
+ const loc = this.skip();
182
+ if (this.check(T.LBRACE)) {
183
+ const pattern = this.parseObjectDestructurePattern();
184
+ this.eat(T.EQ);
185
+ const init = this.parseExpr();
186
+ this.skipNewlines();
187
+ return { type: "DestructureDecl", kind, patternType: "object", pattern, init, loc };
188
+ }
189
+ if (this.check(T.LBRACKET)) {
190
+ const pattern = this.parseArrayDestructurePattern();
191
+ this.eat(T.EQ);
192
+ const init = this.parseExpr();
193
+ this.skipNewlines();
194
+ return { type: "DestructureDecl", kind, patternType: "array", pattern, init, loc };
195
+ }
196
+ const name = this.eat(T.IDENT).value;
197
+ let typeAnn = null;
198
+ if (this.maybe(T.COLON)) {
199
+ typeAnn = this.parseTypeAnn();
200
+ }
201
+ let init = null;
202
+ if (this.maybe(T.EQ)) {
203
+ init = this.parseExpr();
204
+ }
205
+ this.skipNewlines();
206
+ return { type: "VarDecl", kind, name, typeAnn, init, loc };
207
+ }
208
+
209
+ parseObjectDestructurePattern() {
210
+ this.eat(T.LBRACE);
211
+ const props = [];
212
+ while ((!this.check(T.RBRACE) && !this.check(T.EOF))) {
213
+ let rest = false;
214
+ if (this.check(T.DOTDOTDOT)) {
215
+ this.pos = (this.pos + 1);
216
+ rest = true;
217
+ }
218
+ const key = this.eat(T.IDENT).value;
219
+ if (rest) {
220
+ props.push({ key, alias: key, rest: true });
221
+ break;
222
+ }
223
+ let alias = key;
224
+ if (this.maybe(T.COLON)) {
225
+ alias = this.eat(T.IDENT).value;
226
+ }
227
+ let defaultVal = null;
228
+ if (this.maybe(T.EQ)) {
229
+ defaultVal = this.parseExpr();
230
+ }
231
+ props.push({ key, alias, defaultVal, rest: false });
232
+ if (!this.maybe(T.COMMA)) {
233
+ break;
234
+ }
235
+ }
236
+ this.eat(T.RBRACE);
237
+ return props;
238
+ }
239
+
240
+ parseArrayDestructurePattern() {
241
+ this.eat(T.LBRACKET);
242
+ const items = [];
243
+ while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) {
244
+ if (this.check(T.COMMA)) {
245
+ items.push(null);
246
+ this.pos = (this.pos + 1);
247
+ continue;
248
+ }
249
+ let rest = false;
250
+ if (this.check(T.DOTDOTDOT)) {
251
+ this.pos = (this.pos + 1);
252
+ rest = true;
253
+ }
254
+ const name = this.eat(T.IDENT).value;
255
+ let defaultVal = null;
256
+ if (this.maybe(T.EQ)) {
257
+ defaultVal = this.parseExpr();
258
+ }
259
+ items.push({ name, defaultVal, rest });
260
+ if (rest) {
261
+ break;
262
+ }
263
+ if (!this.maybe(T.COMMA)) {
264
+ break;
265
+ }
266
+ }
267
+ this.eat(T.RBRACKET);
268
+ return items;
269
+ }
270
+
271
+ parseTypeAnn() {
272
+ let name = this.parseIntersectionType();
273
+ while (this.check(T.PIPEB)) {
274
+ this.pos = (this.pos + 1);
275
+ name = ((name + " | ") + this.parseIntersectionType());
276
+ }
277
+ return name;
278
+ }
279
+
280
+ parseIntersectionType() {
281
+ let name = this.parseSingleType();
282
+ while (this.check(T.AMPERSAND)) {
283
+ this.pos = (this.pos + 1);
284
+ name = ((name + " & ") + this.parseSingleType());
285
+ }
286
+ return name;
287
+ }
288
+
289
+ parseSingleType() {
290
+ const tok = this.peek();
291
+ if ((tok.type == T.LBRACKET)) {
292
+ this.pos = (this.pos + 1);
293
+ const parts = [];
294
+ while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) {
295
+ parts.push(this.parseTypeAnn());
296
+ if (!this.maybe(T.COMMA)) {
297
+ break;
298
+ }
299
+ }
300
+ this.eat(T.RBRACKET);
301
+ return (("[" + parts.join(", ")) + "]");
302
+ }
303
+ if ((tok.type == T.LBRACE)) {
304
+ this.pos = (this.pos + 1);
305
+ const pairs = [];
306
+ while ((!this.check(T.RBRACE) && !this.check(T.EOF))) {
307
+ if (this.check(T.LBRACKET)) {
308
+ this.pos = (this.pos + 1);
309
+ const iname = this.eat(T.IDENT).value;
310
+ this.eat(T.COLON);
311
+ const iktype = this.parseTypeAnn();
312
+ this.eat(T.RBRACKET);
313
+ this.eat(T.COLON);
314
+ const ivtype = this.parseTypeAnn();
315
+ pairs.push(`[${iname}: ${iktype}]: ${ivtype}`);
316
+ }
317
+ else {
318
+ const key = (this.at(T.IDENT, T.STRING) ? this.skip().value : this.eat(T.IDENT).value);
319
+ let optional = false;
320
+ if (this.check(T.QUESTION)) {
321
+ this.pos = (this.pos + 1);
322
+ optional = true;
323
+ }
324
+ this.eat(T.COLON);
325
+ const valType = this.parseTypeAnn();
326
+ const optStr = (optional ? "?" : "");
327
+ pairs.push(`${key}${optStr}: ${valType}`);
328
+ }
329
+ this.maybe(T.COMMA);
330
+ }
331
+ this.eat(T.RBRACE);
332
+ return (("{ " + pairs.join(", ")) + " }");
333
+ }
334
+ if ((tok.type == T.LPAREN)) {
335
+ this.pos = (this.pos + 1);
336
+ const inner = this.parseTypeAnn();
337
+ this.eat(T.RPAREN);
338
+ return (("(" + inner) + ")");
339
+ }
340
+ if (((tok.type == T.IDENT) && (tok.value == "keyof"))) {
341
+ this.pos = (this.pos + 1);
342
+ return ("keyof " + this.parseSingleType());
343
+ }
344
+ if ((tok.type == T.TYPEOF)) {
345
+ this.pos = (this.pos + 1);
346
+ return ("typeof " + this.eat(T.IDENT).value);
347
+ }
348
+ if ((tok.type == T.READONLY)) {
349
+ this.pos = (this.pos + 1);
350
+ return ("readonly " + this.parseSingleType());
351
+ }
352
+ if (((tok.type == T.IDENT) && (tok.value == "infer"))) {
353
+ this.pos = (this.pos + 1);
354
+ return ("infer " + this.eat(T.IDENT).value);
355
+ }
356
+ if ((tok.type == T.FN)) {
357
+ this.pos = (this.pos + 1);
358
+ const paramTypes = [];
359
+ if (this.check(T.LPAREN)) {
360
+ this.pos = (this.pos + 1);
361
+ while ((!this.check(T.RPAREN) && !this.check(T.EOF))) {
362
+ let pt = "";
363
+ if ((this.check(T.IDENT) && (this.peek(1).type == T.COLON))) {
364
+ this.pos = (this.pos + 2);
365
+ pt = this.parseTypeAnn();
366
+ }
367
+ else {
368
+ pt = this.parseTypeAnn();
369
+ }
370
+ paramTypes.push(pt);
371
+ if (!this.maybe(T.COMMA)) {
372
+ break;
373
+ }
374
+ }
375
+ this.eat(T.RPAREN);
376
+ }
377
+ let retType = "Void";
378
+ if (this.check(T.ARROW)) {
379
+ this.pos = (this.pos + 1);
380
+ retType = this.parseTypeAnn();
381
+ }
382
+ return ((("fn(" + paramTypes.join(", ")) + ") -> ") + retType);
383
+ }
384
+ let name = "";
385
+ if ((((tok.type == T.IDENT) || (tok.type == T.CONST)) || (tok.type == T.TYPE))) {
386
+ name = this.skip().value;
387
+ }
388
+ else {
389
+ name = this.eat(T.IDENT).value;
390
+ }
391
+ if (this.check(T.EXTENDS)) {
392
+ this.pos = (this.pos + 1);
393
+ const constraint = this.parseSingleType();
394
+ this.eat(T.QUESTION);
395
+ const thenType = this.parseTypeAnn();
396
+ this.eat(T.COLON);
397
+ const elseType = this.parseTypeAnn();
398
+ return `${name} extends ${constraint} ? ${thenType} : ${elseType}`;
399
+ }
400
+ if (this.check(T.LT)) {
401
+ this.pos = (this.pos + 1);
402
+ const params = [this.parseTypeAnn()];
403
+ while (this.maybe(T.COMMA)) {
404
+ params.push(this.parseTypeAnn());
405
+ }
406
+ this.eat(T.GT);
407
+ name = (((name + "<") + params.join(", ")) + ">");
408
+ }
409
+ while ((this.check(T.LBRACKET) && (this.peek(1).type == T.RBRACKET))) {
410
+ this.pos = (this.pos + 2);
411
+ name = (name + "[]");
412
+ }
413
+ if ((this.check(T.QUESTION) && (this.peek(1).type != T.DOT))) {
414
+ this.pos = (this.pos + 1);
415
+ name = (name + "?");
416
+ }
417
+ return name;
418
+ }
419
+
420
+ isTypeAnnBeforeColon() {
421
+ const saved = this.pos;
422
+ try {
423
+ const tok0 = this.peek();
424
+ if ((((tok0.type == T.LBRACKET) || (tok0.type == T.LBRACE)) || (tok0.type == T.LPAREN))) {
425
+ let depth = 0;
426
+ let i = 0;
427
+ while ((this.tokens[(this.pos + i)] && (this.tokens[(this.pos + i)].type != T.EOF))) {
428
+ const tt = this.tokens[(this.pos + i)].type;
429
+ if (((((tt == T.LBRACKET) || (tt == T.LBRACE)) || (tt == T.LPAREN)) || (tt == T.LT))) {
430
+ depth = (depth + 1);
431
+ }
432
+ if (((((tt == T.RBRACKET) || (tt == T.RBRACE)) || (tt == T.RPAREN)) || (tt == T.GT))) {
433
+ depth = (depth - 1);
434
+ }
435
+ i = (i + 1);
436
+ if ((depth == 0)) {
437
+ break;
438
+ }
439
+ }
440
+ if ((this.tokens[(this.pos + i)] && (this.tokens[(this.pos + i)].type == T.QUESTION))) {
441
+ i = (i + 1);
442
+ }
443
+ const nextType = this.tokens[(this.pos + i)]?.type;
444
+ this.pos = saved;
445
+ return (nextType == T.COLON);
446
+ }
447
+ if (((((tok0.type != T.IDENT) && (tok0.type != T.TYPEOF)) && (tok0.type != T.READONLY)) && (tok0.type != T.FN))) {
448
+ this.pos = saved;
449
+ return false;
450
+ }
451
+ this.parseTypeAnn();
452
+ const result = (this.check(T.NEWLINE) || this.check(T.EOF));
453
+ this.pos = saved;
454
+ return result;
455
+ }
456
+ catch (e) {
457
+ this.pos = saved;
458
+ return false;
459
+ }
460
+ }
461
+
462
+ isColonReturnType() {
463
+ const saved = this.pos;
464
+ try {
465
+ this.pos = (this.pos + 1);
466
+ if (!this.at(T.IDENT, T.LBRACKET, T.LBRACE, T.LPAREN, T.FN, T.READONLY, T.TYPEOF)) {
467
+ this.pos = saved;
468
+ return false;
469
+ }
470
+ this.parseTypeAnn();
471
+ const result = (this.check(T.NEWLINE) || this.check(T.EOF));
472
+ this.pos = saved;
473
+ return result;
474
+ }
475
+ catch (e) {
476
+ this.pos = saved;
477
+ return false;
478
+ }
479
+ }
480
+
481
+ parseTypeDecl() {
482
+ const loc = this.eat(T.TYPE);
483
+ const name = this.eat(T.IDENT).value;
484
+ const typeParams = [];
485
+ if (this.check(T.LT)) {
486
+ this.pos = (this.pos + 1);
487
+ while ((!this.check(T.GT) && !this.check(T.EOF))) {
488
+ typeParams.push(this.eat(T.IDENT).value);
489
+ if (!this.maybe(T.COMMA)) {
490
+ break;
491
+ }
492
+ }
493
+ this.eat(T.GT);
494
+ }
495
+ this.eat(T.EQ);
496
+ const variants = [];
497
+ let running = true;
498
+ while (running) {
499
+ const vname = this.eat(T.IDENT).value;
500
+ const fields = [];
501
+ const fieldTypes = { };
502
+ if (this.check(T.LPAREN)) {
503
+ this.eat(T.LPAREN);
504
+ while ((!this.check(T.RPAREN) && !this.check(T.EOF))) {
505
+ const ftok = this.peek();
506
+ let fname = "";
507
+ if ((ftok.type == T.IDENT)) {
508
+ fname = this.skip().value;
509
+ }
510
+ else {
511
+ fname = this.skip().value;
512
+ }
513
+ if (this.maybe(T.COLON)) {
514
+ fieldTypes[fname] = this.parseTypeAnn();
515
+ }
516
+ fields.push(fname);
517
+ if (!this.maybe(T.COMMA)) {
518
+ break;
519
+ }
520
+ }
521
+ this.eat(T.RPAREN);
522
+ }
523
+ variants.push({ name: vname, fields, fieldTypes });
524
+ if (!this.check(T.PIPEB)) {
525
+ running = false;
526
+ }
527
+ else {
528
+ this.pos = (this.pos + 1);
529
+ }
530
+ }
531
+ this.skipNewlines();
532
+ return { type: "TypeDecl", name, variants, loc };
533
+ }
534
+
535
+ parseInterfaceDecl() {
536
+ const loc = this.eat(T.INTERFACE);
537
+ const name = this.eat(T.IDENT).value;
538
+ const typeParams = [];
539
+ if (this.check(T.LT)) {
540
+ this.pos = (this.pos + 1);
541
+ while ((!this.check(T.GT) && !this.check(T.EOF))) {
542
+ typeParams.push(this.eat(T.IDENT).value);
543
+ if (!this.maybe(T.COMMA)) {
544
+ break;
545
+ }
546
+ }
547
+ this.eat(T.GT);
548
+ }
549
+ const superInterfaces = [];
550
+ if (this.maybe(T.EXTENDS)) {
551
+ superInterfaces.push(this.eat(T.IDENT).value);
552
+ while (this.maybe(T.COMMA)) {
553
+ superInterfaces.push(this.eat(T.IDENT).value);
554
+ }
555
+ }
556
+ this.eat(T.COLON);
557
+ if (this.check(T.NEWLINE)) {
558
+ this.pos = (this.pos + 1);
559
+ }
560
+ this.eat(T.INDENT);
561
+ const members = [];
562
+ while ((!this.check(T.DEDENT) && !this.check(T.EOF))) {
563
+ this.skipNewlines();
564
+ if ((this.check(T.DEDENT) || this.check(T.EOF))) {
565
+ break;
566
+ }
567
+ const mods = this.parseAccessModifiers();
568
+ if (this.check(T.FN)) {
569
+ this.eat(T.FN);
570
+ const mname = this.eat(T.IDENT).value;
571
+ const params = this.parseParamList();
572
+ let retType = null;
573
+ if (this.maybe(T.ARROW)) {
574
+ retType = this.parseTypeAnn();
575
+ }
576
+ this.skipNewlines();
577
+ members.push({ kind: "method", name: mname, params, retType, modifiers: mods, isAsync: false });
578
+ }
579
+ else if (this.check(T.IDENT)) {
580
+ const fname = this.eat(T.IDENT).value;
581
+ let optional = false;
582
+ if (this.check(T.QUESTION)) {
583
+ this.pos = (this.pos + 1);
584
+ optional = true;
585
+ }
586
+ this.eat(T.COLON);
587
+ const ftype = this.parseTypeAnn();
588
+ this.skipNewlines();
589
+ members.push({ kind: "field", name: fname, typeAnn: ftype, optional, modifiers: mods });
590
+ }
591
+ else {
592
+ this.skip();
593
+ }
594
+ }
595
+ this.maybe(T.DEDENT);
596
+ return { type: "InterfaceDecl", name, typeParams, superInterfaces, members, loc };
597
+ }
598
+
599
+ parseEnumDecl() {
600
+ const loc = this.eat(T.ENUM);
601
+ const name = this.eat(T.IDENT).value;
602
+ this.eat(T.COLON);
603
+ if (this.check(T.NEWLINE)) {
604
+ this.pos = (this.pos + 1);
605
+ this.eat(T.INDENT);
606
+ }
607
+ const members = [];
608
+ let autoIndex = 0;
609
+ while ((!this.check(T.DEDENT) && !this.check(T.EOF))) {
610
+ this.skipNewlines();
611
+ if ((this.check(T.DEDENT) || this.check(T.EOF))) {
612
+ break;
613
+ }
614
+ const mname = this.eat(T.IDENT).value;
615
+ let value = { type: "NumberLit", value: autoIndex };
616
+ if (this.maybe(T.EQ)) {
617
+ const parsed = this.parseExpr();
618
+ value = parsed;
619
+ if ((parsed.type == "NumberLit")) {
620
+ autoIndex = parsed.value;
621
+ }
622
+ }
623
+ else {
624
+ value = { type: "NumberLit", value: autoIndex };
625
+ }
626
+ autoIndex = (autoIndex + 1);
627
+ this.skipNewlines();
628
+ members.push({ name: mname, value });
629
+ }
630
+ this.maybe(T.DEDENT);
631
+ return { type: "EnumDecl", name, members, loc };
632
+ }
633
+
634
+ parseIf() {
635
+ const loc = this.eat(T.IF);
636
+ const cond = this.parseExpr();
637
+ const then = this.parseBlock();
638
+ const elseifs = [];
639
+ let else_ = null;
640
+ this.skipNewlines();
641
+ while (this.check(T.ELSE)) {
642
+ this.pos = (this.pos + 1);
643
+ if (this.check(T.IF)) {
644
+ this.pos = (this.pos + 1);
645
+ const eic = this.parseExpr();
646
+ const eib = this.parseBlock();
647
+ elseifs.push({ cond: eic, body: eib });
648
+ this.skipNewlines();
649
+ }
650
+ else {
651
+ else_ = this.parseBlock();
652
+ break;
653
+ }
654
+ }
655
+ return { type: "IfStmt", cond, then, elseifs, else_, loc };
656
+ }
657
+
658
+ parseFor() {
659
+ const loc = this.eat(T.FOR);
660
+ let isAwait = false;
661
+ if (this.check(T.AWAIT)) {
662
+ this.pos = (this.pos + 1);
663
+ isAwait = true;
664
+ }
665
+ const varName = this.eat(T.IDENT).value;
666
+ this.eat(T.IN);
667
+ const iter = this.parseExpr();
668
+ const body = this.parseBlock();
669
+ return { type: "ForInStmt", var: varName, iter, body, isAwait, loc };
670
+ }
671
+
672
+ parseWhile() {
673
+ const loc = this.eat(T.WHILE);
674
+ const cond = this.parseExpr();
675
+ const body = this.parseBlock();
676
+ return { type: "WhileStmt", cond, body, loc };
677
+ }
678
+
679
+ parseDoWhile() {
680
+ const loc = this.eat(T.DO);
681
+ const body = this.parseBlock();
682
+ this.skipNewlines();
683
+ this.eat(T.WHILE);
684
+ const cond = this.parseExpr();
685
+ this.skipNewlines();
686
+ return { type: "DoWhileStmt", body, cond, loc };
687
+ }
688
+
689
+ parseMatch() {
690
+ const loc = this.eat(T.MATCH);
691
+ const subject = this.parseExpr();
692
+ this.eat(T.COLON);
693
+ if (this.check(T.NEWLINE)) {
694
+ this.pos = (this.pos + 1);
695
+ }
696
+ this.eat(T.INDENT);
697
+ const arms = [];
698
+ while ((!this.check(T.DEDENT) && !this.check(T.EOF))) {
699
+ this.skipNewlines();
700
+ if ((this.check(T.DEDENT) || this.check(T.EOF))) {
701
+ break;
702
+ }
703
+ this.eat(T.WHEN);
704
+ const pattern = this.parsePattern();
705
+ let guard = null;
706
+ if (this.check(T.IF)) {
707
+ this.pos = (this.pos + 1);
708
+ guard = this.parseExpr();
709
+ }
710
+ if (this.check(T.ARROW)) {
711
+ this.pos = (this.pos + 1);
712
+ const expr = this.parseExpr();
713
+ this.skipNewlines();
714
+ arms.push({ pattern, guard, body: [{ type: "ExprStmt", expr }], inline: true });
715
+ }
716
+ else if (this.check(T.COLON)) {
717
+ const isInline = (this.peek(1).type != T.NEWLINE);
718
+ const body = this.parseBlock();
719
+ const inlineArm = ((isInline && (body.length == 1)) && (body[0].type == "ExprStmt"));
720
+ arms.push({ pattern, guard, body, inline: inlineArm });
721
+ }
722
+ }
723
+ this.maybe(T.DEDENT);
724
+ return { type: "MatchStmt", subject, arms, loc };
725
+ }
726
+
727
+ parsePattern() {
728
+ if (this.check(T.WILDCARD)) {
729
+ this.skip();
730
+ return { type: "WildcardPat" };
731
+ }
732
+ if ((this.check(T.IDENT) && (this.peek(1).type == T.LPAREN))) {
733
+ const vname = this.eat(T.IDENT).value;
734
+ this.eat(T.LPAREN);
735
+ const bindings = [];
736
+ while ((!this.check(T.RPAREN) && !this.check(T.EOF))) {
737
+ if (this.check(T.WILDCARD)) {
738
+ bindings.push("_");
739
+ this.pos = (this.pos + 1);
740
+ }
741
+ else {
742
+ bindings.push(this.eat(T.IDENT).value);
743
+ }
744
+ if (!this.maybe(T.COMMA)) {
745
+ break;
746
+ }
747
+ }
748
+ this.eat(T.RPAREN);
749
+ return { type: "VariantPat", variant: vname, bindings };
750
+ }
751
+ let left = this.parsePrimary();
752
+ while (this.check(T.DOT)) {
753
+ this.pos = (this.pos + 1);
754
+ const prop = this.skip().value;
755
+ left = { type: "MemberExpr", obj: left, prop };
756
+ }
757
+ if (this.check(T.DOTDOT)) {
758
+ this.pos = (this.pos + 1);
759
+ const right = this.parsePrimary();
760
+ return { type: "RangePat", start: left, end: right };
761
+ }
762
+ return { type: "LiteralPat", value: left };
763
+ }
764
+
765
+ parseReturn() {
766
+ const loc = this.eat(T.RETURN);
767
+ let value = null;
768
+ if (!this.at(T.NEWLINE, T.EOF, T.DEDENT)) {
769
+ value = this.parseExpr();
770
+ }
771
+ this.skipNewlines();
772
+ return { type: "ReturnStmt", value, loc };
773
+ }
774
+
775
+ parseTryCatch() {
776
+ const loc = this.eat(T.TRY);
777
+ const tryBody = this.parseBlock();
778
+ let catchParam = null;
779
+ let catchBody = null;
780
+ let finallyBody = null;
781
+ this.skipNewlines();
782
+ if (this.check(T.CATCH)) {
783
+ this.pos = (this.pos + 1);
784
+ if (this.check(T.LPAREN)) {
785
+ this.pos = (this.pos + 1);
786
+ catchParam = this.eat(T.IDENT).value;
787
+ if (this.maybe(T.COLON)) {
788
+ this.parseTypeAnn();
789
+ }
790
+ this.eat(T.RPAREN);
791
+ }
792
+ catchBody = this.parseBlock();
793
+ this.skipNewlines();
794
+ }
795
+ if (this.check(T.FINALLY)) {
796
+ this.pos = (this.pos + 1);
797
+ finallyBody = this.parseBlock();
798
+ }
799
+ return { type: "TryCatchStmt", tryBody, catchParam, catchBody, finallyBody, loc };
800
+ }
801
+
802
+ parseThrow() {
803
+ const loc = this.eat(T.THROW);
804
+ const value = this.parseExpr();
805
+ this.skipNewlines();
806
+ return { type: "ThrowStmt", value, loc };
807
+ }
808
+
809
+ parseImport() {
810
+ this.eat(T.IMPORT);
811
+ if (this.check(T.STAR)) {
812
+ this.pos = (this.pos + 1);
813
+ this.eat(T.AS);
814
+ const namespaceName = this.eat(T.IDENT).value;
815
+ this.eat(T.FROM);
816
+ const source = this.eat(T.STRING).value;
817
+ this.skipNewlines();
818
+ return { type: "ImportDecl", names: [], defaultName: null, namespaceName, source };
819
+ }
820
+ if (this.check(T.IDENT)) {
821
+ const defaultName = this.eat(T.IDENT).value;
822
+ this.eat(T.FROM);
823
+ const source = this.eat(T.STRING).value;
824
+ this.skipNewlines();
825
+ return { type: "ImportDecl", names: [], defaultName, source };
826
+ }
827
+ const names = [];
828
+ if (this.maybe(T.LBRACE)) {
829
+ while ((!this.check(T.RBRACE) && !this.check(T.EOF))) {
830
+ const name = this.eat(T.IDENT).value;
831
+ let alias = name;
832
+ if (this.check(T.AS)) {
833
+ this.pos = (this.pos + 1);
834
+ alias = this.eat(T.IDENT).value;
835
+ }
836
+ names.push({ name, alias });
837
+ if (!this.maybe(T.COMMA)) {
838
+ break;
839
+ }
840
+ }
841
+ this.eat(T.RBRACE);
842
+ }
843
+ this.eat(T.FROM);
844
+ const source = this.eat(T.STRING).value;
845
+ this.skipNewlines();
846
+ return { type: "ImportDecl", names, defaultName: null, source };
847
+ }
848
+
849
+ parseExport() {
850
+ this.eat(T.EXPORT);
851
+ if (this.check(T.DEFAULT)) {
852
+ this.pos = (this.pos + 1);
853
+ if (this.check(T.ASYNC)) {
854
+ this.pos = (this.pos + 1);
855
+ const decl = this.parseFnDecl(true);
856
+ return { type: "ExportDecl", isDefault: true, decl };
857
+ }
858
+ if (this.check(T.FN)) {
859
+ const decl = this.parseFnDecl(false);
860
+ return { type: "ExportDecl", isDefault: true, decl };
861
+ }
862
+ const value = this.parseExpr();
863
+ this.skipNewlines();
864
+ return { type: "ExportDecl", isDefault: true, decl: value };
865
+ }
866
+ if (this.check(T.ASYNC)) {
867
+ this.pos = (this.pos + 1);
868
+ if (!this.check(T.FN)) {
869
+ this.err("Expected fn after async");
870
+ }
871
+ const decl = this.parseFnDecl(true);
872
+ return { type: "ExportDecl", isDefault: false, decl };
873
+ }
874
+ const decl = this.parseOneStmt();
875
+ return { type: "ExportDecl", isDefault: false, decl };
876
+ }
877
+
878
+ parseFnDecl(isAsync = false) {
879
+ const loc = this.eat(T.FN);
880
+ let name = null;
881
+ if ((((((((this.check(T.IDENT) || this.check(T.NEW)) || this.check(T.FROM)) || this.check(T.AS)) || this.check(T.DEFAULT)) || this.check(T.IS)) || this.check(T.IN)) || this.check(T.TYPE))) {
882
+ name = this.skip().value;
883
+ }
884
+ const params = this.parseParamList();
885
+ let retType = null;
886
+ if (this.check(T.ARROW)) {
887
+ this.pos = (this.pos + 1);
888
+ if (this.isTypeAnnBeforeColon()) {
889
+ retType = this.parseTypeAnn();
890
+ const body = this.parseBlock();
891
+ return { type: "FnDecl", name, params, retType, body, inline: false, async: isAsync, loc };
892
+ }
893
+ const body = this.parseExpr();
894
+ this.skipNewlines();
895
+ return { type: "FnDecl", name, params, retType: null, body, inline: true, async: isAsync, loc };
896
+ }
897
+ if (this.check(T.COLON)) {
898
+ if (this.isColonReturnType()) {
899
+ this.pos = (this.pos + 1);
900
+ retType = this.parseTypeAnn();
901
+ if (this.check(T.NEWLINE)) {
902
+ this.pos = (this.pos + 1);
903
+ }
904
+ if (this.check(T.INDENT)) {
905
+ this.pos = (this.pos + 1);
906
+ const body = this.parseStmtList();
907
+ this.maybe(T.DEDENT);
908
+ return { type: "FnDecl", name, params, retType, body, inline: false, async: isAsync, loc };
909
+ }
910
+ const stmt = this.parseOneStmt();
911
+ return { type: "FnDecl", name, params, retType, body: [stmt], inline: false, async: isAsync, loc };
912
+ }
913
+ const body = this.parseBlock();
914
+ return { type: "FnDecl", name, params, retType: null, body, inline: false, async: isAsync, loc };
915
+ }
916
+ this.err("Expected -> or : after function signature");
917
+ }
918
+
919
+ parseAsyncFn() {
920
+ this.eat(T.ASYNC);
921
+ if (!this.check(T.FN)) {
922
+ this.err("Expected fn after async");
923
+ }
924
+ return this.parseFnDecl(true);
925
+ }
926
+
927
+ parseDeclareDecl() {
928
+ const loc = this.eat(T.DECLARE);
929
+ const tok = this.peek();
930
+ if (((tok.type == T.FN) || (tok.type == T.ASYNC))) {
931
+ const isAsync = (tok.type == T.ASYNC);
932
+ this.skip();
933
+ if (isAsync) {
934
+ this.eat(T.FN);
935
+ }
936
+ const name = (this.check(T.IDENT) ? this.skip().value : null);
937
+ const params = this.parseParamList();
938
+ let retType = null;
939
+ if (this.check(T.ARROW)) {
940
+ this.pos = (this.pos + 1);
941
+ retType = this.parseTypeAnn();
942
+ }
943
+ else if (this.check(T.COLON)) {
944
+ this.pos = (this.pos + 1);
945
+ retType = this.parseTypeAnn();
946
+ }
947
+ this.skipNewlines();
948
+ const decl = { type: "FnDecl", name, params, retType, body: [], inline: false, async: isAsync, loc: tok };
949
+ return { type: "DeclareDecl", decl, loc };
950
+ }
951
+ if (((tok.type == T.VAL) || (tok.type == T.VAR))) {
952
+ const kind = ((tok.type == T.VAR) ? "var" : "val");
953
+ this.skip();
954
+ const name = this.eat(T.IDENT).value;
955
+ let typeAnn = null;
956
+ if (this.maybe(T.COLON)) {
957
+ typeAnn = this.parseTypeAnn();
958
+ }
959
+ this.skipNewlines();
960
+ const decl = { type: "VarDecl", kind, name, typeAnn, init: null, loc: tok };
961
+ return { type: "DeclareDecl", decl, loc };
962
+ }
963
+ if ((tok.type == T.CLASS)) {
964
+ this.skip();
965
+ const name = this.eat(T.IDENT).value;
966
+ this.skipNewlines();
967
+ const decl = { type: "ClassDecl", name, fields: [], methods: [], loc: tok };
968
+ return { type: "DeclareDecl", decl, loc };
969
+ }
970
+ this.err("Expected fn, async fn, val, var, or class after declare");
971
+ }
972
+
973
+ parseParamList() {
974
+ this.eat(T.LPAREN);
975
+ const params = [];
976
+ while ((!this.check(T.RPAREN) && !this.check(T.EOF))) {
977
+ let rest = false;
978
+ if (this.check(T.DOTDOTDOT)) {
979
+ this.pos = (this.pos + 1);
980
+ rest = true;
981
+ }
982
+ const name = this.eat(T.IDENT).value;
983
+ let optional = false;
984
+ let typeAnn = null;
985
+ if ((!rest && this.check(T.QUESTION))) {
986
+ this.pos = (this.pos + 1);
987
+ optional = true;
988
+ }
989
+ if ((!rest && this.maybe(T.COLON))) {
990
+ typeAnn = this.parseTypeAnn();
991
+ }
992
+ let defaultVal = null;
993
+ if ((!rest && this.maybe(T.EQ))) {
994
+ defaultVal = this.parseExpr();
995
+ }
996
+ params.push({ name, typeAnn, optional, defaultVal, rest });
997
+ if (rest) {
998
+ break;
999
+ }
1000
+ if (!this.maybe(T.COMMA)) {
1001
+ break;
1002
+ }
1003
+ }
1004
+ this.eat(T.RPAREN);
1005
+ return params;
1006
+ }
1007
+
1008
+ parseAccessModifiers() {
1009
+ const mods = new Set([]);
1010
+ let running = true;
1011
+ while (running) {
1012
+ const t = this.peek().type;
1013
+ if ((((((((t == T.PRIVATE) || (t == T.PUBLIC)) || (t == T.PROTECTED)) || (t == T.READONLY)) || (t == T.STATIC)) || (t == T.ABSTRACT)) || (t == T.OVERRIDE))) {
1014
+ mods.add(this.skip().value);
1015
+ }
1016
+ else {
1017
+ running = false;
1018
+ }
1019
+ }
1020
+ return mods;
1021
+ }
1022
+
1023
+ parseClassDecl() {
1024
+ const loc = this.eat(T.CLASS);
1025
+ const name = this.eat(T.IDENT).value;
1026
+ let superClass = null;
1027
+ const interfaces = [];
1028
+ const typeParams = [];
1029
+ if (this.check(T.LT)) {
1030
+ this.pos = (this.pos + 1);
1031
+ typeParams.push(this.eat(T.IDENT).value);
1032
+ while (this.maybe(T.COMMA)) {
1033
+ typeParams.push(this.eat(T.IDENT).value);
1034
+ }
1035
+ this.eat(T.GT);
1036
+ }
1037
+ if (this.maybe(T.EXTENDS)) {
1038
+ superClass = this.eat(T.IDENT).value;
1039
+ }
1040
+ if (this.maybe(T.IMPLEMENTS)) {
1041
+ interfaces.push(this.eat(T.IDENT).value);
1042
+ while (this.maybe(T.COMMA)) {
1043
+ interfaces.push(this.eat(T.IDENT).value);
1044
+ }
1045
+ }
1046
+ this.eat(T.COLON);
1047
+ if (this.check(T.NEWLINE)) {
1048
+ this.pos = (this.pos + 1);
1049
+ this.eat(T.INDENT);
1050
+ }
1051
+ const fields = [];
1052
+ const methods = [];
1053
+ while ((!this.check(T.DEDENT) && !this.check(T.EOF))) {
1054
+ this.skipNewlines();
1055
+ if ((this.check(T.DEDENT) || this.check(T.EOF))) {
1056
+ break;
1057
+ }
1058
+ const mods = this.parseAccessModifiers();
1059
+ if (this.check(T.FN)) {
1060
+ const m = this.parseFnDecl(false);
1061
+ m.modifiers = mods;
1062
+ methods.push(m);
1063
+ }
1064
+ else if (this.check(T.ASYNC)) {
1065
+ const m = this.parseAsyncFn();
1066
+ m.modifiers = mods;
1067
+ methods.push(m);
1068
+ }
1069
+ else if ((this.check(T.STATIC) && (this.peek(1).type == T.FN))) {
1070
+ this.skip();
1071
+ const m = this.parseFnDecl(false);
1072
+ m.modifiers = mods;
1073
+ m.modifiers.add("static");
1074
+ methods.push(m);
1075
+ }
1076
+ else if (this.check(T.IDENT)) {
1077
+ const fname = this.eat(T.IDENT).value;
1078
+ let optional = false;
1079
+ if (this.check(T.QUESTION)) {
1080
+ this.pos = (this.pos + 1);
1081
+ optional = true;
1082
+ }
1083
+ this.eat(T.COLON);
1084
+ const ftype = this.parseTypeAnn();
1085
+ this.skipNewlines();
1086
+ fields.push({ name: fname, typeAnn: ftype, optional, modifiers: mods });
1087
+ }
1088
+ else {
1089
+ this.skip();
1090
+ }
1091
+ }
1092
+ this.maybe(T.DEDENT);
1093
+ return { type: "ClassDecl", name, typeParams, superClass, interfaces, fields, methods, loc };
1094
+ }
1095
+
1096
+ parseExprStmt() {
1097
+ const expr = this.parseExpr();
1098
+ this.skipNewlines();
1099
+ return { type: "ExprStmt", expr };
1100
+ }
1101
+
1102
+ parseExpr() {
1103
+ return this.parsePipe();
1104
+ }
1105
+
1106
+ parsePipe() {
1107
+ let left = this.parseAssign();
1108
+ let running = true;
1109
+ while (running) {
1110
+ if (this.check(T.PIPE)) {
1111
+ this.pos = (this.pos + 1);
1112
+ const right = this.parseAssign();
1113
+ left = { type: "PipeExpr", left, right };
1114
+ }
1115
+ else if (this.check(T.NEWLINE)) {
1116
+ let i = 1;
1117
+ while ((this.peek(i).type == T.NEWLINE)) {
1118
+ i = (i + 1);
1119
+ }
1120
+ if ((this.peek(i).type == T.PIPE)) {
1121
+ while (this.check(T.NEWLINE)) {
1122
+ this.pos = (this.pos + 1);
1123
+ }
1124
+ this.pos = (this.pos + 1);
1125
+ const right = this.parseAssign();
1126
+ left = { type: "PipeExpr", left, right };
1127
+ }
1128
+ else {
1129
+ running = false;
1130
+ }
1131
+ }
1132
+ else {
1133
+ running = false;
1134
+ }
1135
+ }
1136
+ return left;
1137
+ }
1138
+
1139
+ parseAssign() {
1140
+ const left = this.parseTernary();
1141
+ const t = this.peek().type;
1142
+ let op = "";
1143
+ if ((t == T.EQ)) {
1144
+ op = "=";
1145
+ }
1146
+ else if ((t == T.PLUSEQ)) {
1147
+ op = "+=";
1148
+ }
1149
+ else if ((t == T.MINUSEQ)) {
1150
+ op = "-=";
1151
+ }
1152
+ else if ((t == T.STAREQ)) {
1153
+ op = "*=";
1154
+ }
1155
+ else if ((t == T.SLASHEQ)) {
1156
+ op = "/=";
1157
+ }
1158
+ else if ((t == T.PERCENTEQ)) {
1159
+ op = "%=";
1160
+ }
1161
+ if (op) {
1162
+ this.pos = (this.pos + 1);
1163
+ return { type: "AssignExpr", target: left, op, value: this.parseAssign() };
1164
+ }
1165
+ return left;
1166
+ }
1167
+
1168
+ parseTernary() {
1169
+ const cond = this.parseNullish();
1170
+ if (this.maybe(T.QUESTION)) {
1171
+ const then = this.parseNullish();
1172
+ this.eat(T.COLON);
1173
+ const else_ = this.parseTernary();
1174
+ return { type: "TernaryExpr", cond, then, else_ };
1175
+ }
1176
+ return cond;
1177
+ }
1178
+
1179
+ parseNullish() {
1180
+ let l = this.parseOr();
1181
+ while (this.check(T.NULLISH)) {
1182
+ this.pos = (this.pos + 1);
1183
+ const r = this.parseOr();
1184
+ l = { type: "BinaryExpr", op: "??", left: l, right: r };
1185
+ }
1186
+ return l;
1187
+ }
1188
+
1189
+ parseOr() {
1190
+ let l = this.parseAnd();
1191
+ while ((this.check(T.OR) || this.check(T.OROR))) {
1192
+ this.pos = (this.pos + 1);
1193
+ const r = this.parseAnd();
1194
+ l = { type: "BinaryExpr", op: "||", left: l, right: r };
1195
+ }
1196
+ return l;
1197
+ }
1198
+
1199
+ parseAnd() {
1200
+ let l = this.parseBitOr();
1201
+ while ((this.check(T.AND) || this.check(T.ANDAND))) {
1202
+ this.pos = (this.pos + 1);
1203
+ const r = this.parseBitOr();
1204
+ l = { type: "BinaryExpr", op: "&&", left: l, right: r };
1205
+ }
1206
+ return l;
1207
+ }
1208
+
1209
+ parseBitOr() {
1210
+ let l = this.parseBitXor();
1211
+ while (this.check(T.PIPEB)) {
1212
+ this.pos = (this.pos + 1);
1213
+ const r = this.parseBitXor();
1214
+ l = { type: "BinaryExpr", op: "|", left: l, right: r };
1215
+ }
1216
+ return l;
1217
+ }
1218
+
1219
+ parseBitXor() {
1220
+ let l = this.parseBitAnd();
1221
+ while (this.check(T.CARET)) {
1222
+ this.pos = (this.pos + 1);
1223
+ const r = this.parseBitAnd();
1224
+ l = { type: "BinaryExpr", op: "^", left: l, right: r };
1225
+ }
1226
+ return l;
1227
+ }
1228
+
1229
+ parseBitAnd() {
1230
+ let l = this.parseEq();
1231
+ while (this.check(T.AMPERSAND)) {
1232
+ this.pos = (this.pos + 1);
1233
+ const r = this.parseEq();
1234
+ l = { type: "BinaryExpr", op: "&", left: l, right: r };
1235
+ }
1236
+ return l;
1237
+ }
1238
+
1239
+ parseEq() {
1240
+ let l = this.parseRel();
1241
+ while (this.at(T.EQEQ, T.NEQ, T.EQEQEQ, T.NEQEQ)) {
1242
+ const op = this.skip().value;
1243
+ const r = this.parseRel();
1244
+ l = { type: "BinaryExpr", op, left: l, right: r };
1245
+ }
1246
+ return l;
1247
+ }
1248
+
1249
+ parseRel() {
1250
+ let l = this.parseShift();
1251
+ while ((this.at(T.LT, T.LTE, T.GT, T.GTE) || this.check(T.IN))) {
1252
+ const op = this.skip().value;
1253
+ const r = this.parseShift();
1254
+ l = { type: "BinaryExpr", op, left: l, right: r };
1255
+ }
1256
+ return l;
1257
+ }
1258
+
1259
+ parseShift() {
1260
+ let l = this.parseRange();
1261
+ while (this.at(T.LSHIFT, T.RSHIFT)) {
1262
+ const op = this.skip().value;
1263
+ const r = this.parseRange();
1264
+ l = { type: "BinaryExpr", op, left: l, right: r };
1265
+ }
1266
+ return l;
1267
+ }
1268
+
1269
+ parseRange() {
1270
+ const l = this.parseAdd();
1271
+ if (this.check(T.DOTDOT)) {
1272
+ this.pos = (this.pos + 1);
1273
+ const r = this.parseAdd();
1274
+ return { type: "RangeExpr", start: l, end: r };
1275
+ }
1276
+ return l;
1277
+ }
1278
+
1279
+ parseAdd() {
1280
+ let l = this.parseMul();
1281
+ while (this.at(T.PLUS, T.MINUS)) {
1282
+ const op = this.skip().value;
1283
+ const r = this.parseMul();
1284
+ l = { type: "BinaryExpr", op, left: l, right: r };
1285
+ }
1286
+ return l;
1287
+ }
1288
+
1289
+ parseMul() {
1290
+ let l = this.parsePow();
1291
+ while (this.at(T.STAR, T.SLASH, T.PERCENT)) {
1292
+ const op = this.skip().value;
1293
+ const r = this.parsePow();
1294
+ l = { type: "BinaryExpr", op, left: l, right: r };
1295
+ }
1296
+ return l;
1297
+ }
1298
+
1299
+ parsePow() {
1300
+ const l = this.parseUnary();
1301
+ if (this.check(T.STARSTAR)) {
1302
+ this.pos = (this.pos + 1);
1303
+ return { type: "BinaryExpr", op: "**", left: l, right: this.parsePow() };
1304
+ }
1305
+ return l;
1306
+ }
1307
+
1308
+ parseUnary() {
1309
+ if (this.check(T.MINUS)) {
1310
+ this.pos = (this.pos + 1);
1311
+ return { type: "UnaryExpr", op: "-", operand: this.parseUnary() };
1312
+ }
1313
+ if (this.check(T.NOT)) {
1314
+ this.pos = (this.pos + 1);
1315
+ return { type: "UnaryExpr", op: "!", operand: this.parseUnary() };
1316
+ }
1317
+ if (this.check(T.BANG)) {
1318
+ this.pos = (this.pos + 1);
1319
+ return { type: "UnaryExpr", op: "!", operand: this.parseUnary() };
1320
+ }
1321
+ if (this.check(T.TILDE)) {
1322
+ this.pos = (this.pos + 1);
1323
+ return { type: "UnaryExpr", op: "~", operand: this.parseUnary() };
1324
+ }
1325
+ if (this.check(T.PLUSPLUS)) {
1326
+ this.pos = (this.pos + 1);
1327
+ return { type: "UpdateExpr", op: "++", prefix: true, operand: this.parseUnary() };
1328
+ }
1329
+ if (this.check(T.MINUSMINUS)) {
1330
+ this.pos = (this.pos + 1);
1331
+ return { type: "UpdateExpr", op: "--", prefix: true, operand: this.parseUnary() };
1332
+ }
1333
+ if (this.check(T.AWAIT)) {
1334
+ this.pos = (this.pos + 1);
1335
+ return { type: "AwaitExpr", operand: this.parseUnary() };
1336
+ }
1337
+ if (this.check(T.TYPEOF)) {
1338
+ this.pos = (this.pos + 1);
1339
+ return { type: "TypeofExpr", operand: this.parseUnary() };
1340
+ }
1341
+ return this.parseLambdaOrPostfix();
1342
+ }
1343
+
1344
+ parseLambdaOrPostfix() {
1345
+ const saved = this.pos;
1346
+ if ((this.check(T.IDENT) || this.check(T.WILDCARD))) {
1347
+ const name = (this.skip().value ?? "_");
1348
+ if (this.check(T.ARROW)) {
1349
+ this.pos = (this.pos + 1);
1350
+ const body = this.parseExpr();
1351
+ return { type: "LambdaExpr", params: [{ name }], body };
1352
+ }
1353
+ this.pos = saved;
1354
+ }
1355
+ return this.parsePostfix();
1356
+ }
1357
+
1358
+ parsePostfix() {
1359
+ let expr = this.parsePrimary();
1360
+ let running = true;
1361
+ while (running) {
1362
+ if (this.check(T.PLUSPLUS)) {
1363
+ this.pos = (this.pos + 1);
1364
+ expr = { type: "UpdateExpr", op: "++", prefix: false, operand: expr };
1365
+ }
1366
+ else if (this.check(T.MINUSMINUS)) {
1367
+ this.pos = (this.pos + 1);
1368
+ expr = { type: "UpdateExpr", op: "--", prefix: false, operand: expr };
1369
+ }
1370
+ else if (this.check(T.DOT)) {
1371
+ this.pos = (this.pos + 1);
1372
+ const propTok = this.peek();
1373
+ const prop = this.skip().value;
1374
+ expr = { type: "MemberExpr", obj: expr, prop };
1375
+ }
1376
+ else if (this.check(T.QUESTIONDOT)) {
1377
+ this.pos = (this.pos + 1);
1378
+ if (this.check(T.LPAREN)) {
1379
+ const args = this.parseArgList();
1380
+ expr = { type: "OptCallExpr", callee: expr, args };
1381
+ }
1382
+ else if (this.check(T.LBRACKET)) {
1383
+ this.pos = (this.pos + 1);
1384
+ const idx = this.parseExpr();
1385
+ this.eat(T.RBRACKET);
1386
+ expr = { type: "OptIndexExpr", obj: expr, idx };
1387
+ }
1388
+ else {
1389
+ const prop = this.skip().value;
1390
+ expr = { type: "OptMemberExpr", obj: expr, prop };
1391
+ }
1392
+ }
1393
+ else if (this.check(T.INSTANCEOF)) {
1394
+ this.pos = (this.pos + 1);
1395
+ const right = this.eat(T.IDENT).value;
1396
+ expr = { type: "BinaryExpr", op: "instanceof", left: expr, right: { type: "Identifier", name: right } };
1397
+ }
1398
+ else if (this.check(T.AS)) {
1399
+ this.pos = (this.pos + 1);
1400
+ if (this.check(T.CONST)) {
1401
+ this.pos = (this.pos + 1);
1402
+ expr = { type: "AsConstExpr", expr };
1403
+ }
1404
+ else {
1405
+ const castType = this.parseTypeAnn();
1406
+ expr = { type: "CastExpr", expr, castType };
1407
+ }
1408
+ }
1409
+ else if (this.check(T.SATISFIES)) {
1410
+ this.pos = (this.pos + 1);
1411
+ const satType = this.parseTypeAnn();
1412
+ expr = { type: "SatisfiesExpr", expr, satType };
1413
+ }
1414
+ else if (this.check(T.IS)) {
1415
+ this.pos = (this.pos + 1);
1416
+ const isType = this.parseTypeAnn();
1417
+ expr = { type: "IsExpr", expr, isType };
1418
+ }
1419
+ else if (this.check(T.BANG)) {
1420
+ this.pos = (this.pos + 1);
1421
+ expr = { type: "NonNullExpr", expr };
1422
+ }
1423
+ else if (this.check(T.LBRACKET)) {
1424
+ this.pos = (this.pos + 1);
1425
+ const idx = this.parseExpr();
1426
+ this.eat(T.RBRACKET);
1427
+ expr = { type: "IndexExpr", obj: expr, idx };
1428
+ }
1429
+ else if (this.check(T.LPAREN)) {
1430
+ const args = this.parseArgList();
1431
+ if (this.check(T.ARROW)) {
1432
+ this.pos = (this.pos + 1);
1433
+ const body = this.parseExpr();
1434
+ const lambdaParams = args.map((a) => { name: ((a.type == "Identifier") ? a.name : "_") });
1435
+ return { type: "LambdaExpr", params: lambdaParams, body };
1436
+ }
1437
+ expr = { type: "CallExpr", callee: expr, args };
1438
+ }
1439
+ else {
1440
+ running = false;
1441
+ }
1442
+ }
1443
+ return expr;
1444
+ }
1445
+
1446
+ parseArgList() {
1447
+ this.eat(T.LPAREN);
1448
+ const args = [];
1449
+ while ((!this.check(T.RPAREN) && !this.check(T.EOF))) {
1450
+ if (this.check(T.DOTDOTDOT)) {
1451
+ this.pos = (this.pos + 1);
1452
+ args.push({ type: "SpreadExpr", expr: this.parseExpr() });
1453
+ }
1454
+ else {
1455
+ args.push(this.parseExpr());
1456
+ }
1457
+ if (!this.maybe(T.COMMA)) {
1458
+ break;
1459
+ }
1460
+ }
1461
+ this.eat(T.RPAREN);
1462
+ return args;
1463
+ }
1464
+
1465
+ parsePrimary() {
1466
+ const tok = this.peek();
1467
+ if ((tok.type == T.NUMBER)) {
1468
+ this.pos = (this.pos + 1);
1469
+ return { type: "NumberLit", value: tok.value, loc: tok };
1470
+ }
1471
+ if ((tok.type == T.BOOL)) {
1472
+ this.pos = (this.pos + 1);
1473
+ return { type: "BoolLit", value: tok.value, loc: tok };
1474
+ }
1475
+ if ((tok.type == T.NULL)) {
1476
+ this.pos = (this.pos + 1);
1477
+ return { type: "NullLit", loc: tok };
1478
+ }
1479
+ if ((tok.type == T.SELF)) {
1480
+ this.pos = (this.pos + 1);
1481
+ return { type: "SelfExpr", loc: tok };
1482
+ }
1483
+ if ((tok.type == T.WILDCARD)) {
1484
+ this.pos = (this.pos + 1);
1485
+ return { type: "Identifier", name: "_", loc: tok };
1486
+ }
1487
+ if ((tok.type == T.IDENT)) {
1488
+ this.pos = (this.pos + 1);
1489
+ return { type: "Identifier", name: tok.value, loc: tok };
1490
+ }
1491
+ if ((tok.type == T.STRING)) {
1492
+ this.pos = (this.pos + 1);
1493
+ if ((tok.value && tok.value.template)) {
1494
+ return { type: "TemplateLit", parts: tok.value.parts, loc: tok };
1495
+ }
1496
+ return { type: "StringLit", value: tok.value, loc: tok };
1497
+ }
1498
+ if ((tok.type == T.REGEX)) {
1499
+ this.pos = (this.pos + 1);
1500
+ return { type: "RegexLit", value: tok.value, loc: tok };
1501
+ }
1502
+ if ((tok.type == T.NEW)) {
1503
+ this.pos = (this.pos + 1);
1504
+ const callee = this.eat(T.IDENT).value;
1505
+ const args = this.parseArgList();
1506
+ return { type: "NewExpr", callee, args };
1507
+ }
1508
+ if ((tok.type == T.MATCH)) {
1509
+ return this.parseMatch();
1510
+ }
1511
+ if ((tok.type == T.FN)) {
1512
+ this.pos = (this.pos + 1);
1513
+ const params = this.parseParamList();
1514
+ if (this.check(T.ARROW)) {
1515
+ this.pos = (this.pos + 1);
1516
+ if (this.isTypeAnnBeforeColon()) {
1517
+ const retType = this.parseTypeAnn();
1518
+ const body = this.parseBlock();
1519
+ return { type: "FnDecl", name: null, params, retType, body, inline: false, async: false, loc: tok };
1520
+ }
1521
+ const body = this.parseExpr();
1522
+ return { type: "FnDecl", name: null, params, retType: null, body, inline: true, async: false, loc: tok };
1523
+ }
1524
+ if (this.check(T.COLON)) {
1525
+ const body = this.parseBlock();
1526
+ return { type: "FnDecl", name: null, params, retType: null, body, inline: false, async: false, loc: tok };
1527
+ }
1528
+ this.err("Expected -> or : after anonymous fn");
1529
+ }
1530
+ if ((tok.type == T.LPAREN)) {
1531
+ this.pos = (this.pos + 1);
1532
+ if (this.check(T.RPAREN)) {
1533
+ this.pos = (this.pos + 1);
1534
+ if (this.check(T.ARROW)) {
1535
+ this.pos = (this.pos + 1);
1536
+ const body = this.parseExpr();
1537
+ return { type: "LambdaExpr", params: [], body };
1538
+ }
1539
+ return { type: "NullLit" };
1540
+ }
1541
+ const first = this.parseExpr();
1542
+ if (this.check(T.RPAREN)) {
1543
+ this.pos = (this.pos + 1);
1544
+ if (this.check(T.ARROW)) {
1545
+ this.pos = (this.pos + 1);
1546
+ const body = this.parseExpr();
1547
+ const pname = ((first.type == "Identifier") ? first.name : "_");
1548
+ return { type: "LambdaExpr", params: [{ name: pname }], body };
1549
+ }
1550
+ return first;
1551
+ }
1552
+ const items = [first];
1553
+ while (this.maybe(T.COMMA)) {
1554
+ items.push(this.parseExpr());
1555
+ }
1556
+ this.eat(T.RPAREN);
1557
+ if (this.check(T.ARROW)) {
1558
+ this.pos = (this.pos + 1);
1559
+ const body = this.parseExpr();
1560
+ const lambdaParams = items.map((i) => { name: ((i.type == "Identifier") ? i.name : "_") });
1561
+ return { type: "LambdaExpr", params: lambdaParams, body };
1562
+ }
1563
+ return items[0];
1564
+ }
1565
+ if ((tok.type == T.LBRACKET)) {
1566
+ this.pos = (this.pos + 1);
1567
+ const items = [];
1568
+ while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) {
1569
+ if (this.check(T.DOTDOTDOT)) {
1570
+ this.pos = (this.pos + 1);
1571
+ items.push({ type: "SpreadExpr", expr: this.parseExpr() });
1572
+ }
1573
+ else {
1574
+ items.push(this.parseExpr());
1575
+ }
1576
+ if (!this.maybe(T.COMMA)) {
1577
+ break;
1578
+ }
1579
+ }
1580
+ this.eat(T.RBRACKET);
1581
+ return { type: "ArrayExpr", items };
1582
+ }
1583
+ if ((tok.type == T.LBRACE)) {
1584
+ this.pos = (this.pos + 1);
1585
+ const pairs = [];
1586
+ while ((!this.check(T.RBRACE) && !this.check(T.EOF))) {
1587
+ if (this.check(T.DOTDOTDOT)) {
1588
+ this.pos = (this.pos + 1);
1589
+ pairs.push({ spread: true, value: this.parseExpr() });
1590
+ if (!this.maybe(T.COMMA)) {
1591
+ break;
1592
+ }
1593
+ continue;
1594
+ }
1595
+ const key = (this.check(T.STRING) ? this.eat(T.STRING).value : (this.check(T.IDENT) ? this.skip().value : this.skip().value));
1596
+ if (!this.check(T.COLON)) {
1597
+ pairs.push({ key, value: { type: "Identifier", name: key } });
1598
+ }
1599
+ else {
1600
+ this.eat(T.COLON);
1601
+ const v = this.parseExpr();
1602
+ pairs.push({ key, value: v });
1603
+ }
1604
+ if (!this.maybe(T.COMMA)) {
1605
+ break;
1606
+ }
1607
+ }
1608
+ this.eat(T.RBRACE);
1609
+ return { type: "ObjectExpr", pairs };
1610
+ }
1611
+ this.err(`Unexpected token: ${tok.type} (${JSON.stringify(tok.value)})`);
1612
+ }
1613
+
1614
+ }
1615
+
1616
+ module.exports.Parser = Parser;
1617
+ function makeParser(tokens) {
1618
+ return new Parser(tokens, 0);
1619
+ }
1620
+ module.exports.makeParser = makeParser;