import type { Program } from "./ast.ts"; export declare class ParseError extends Error { readonly pos: number; constructor(message: string, pos: number); } /** * Raised by `Parser.parseFunctionInput()` when the source given to * `jsmql(($) => …)` is not a valid arrow function shape — `async` * arrows, `function` declarations, missing arrow operator, unbalanced * params, `return` inside a block body, etc. Distinct from `ParseError` * because the failure is in the function-shape adapter, not in jsmql's * own grammar; `validate()` maps both to `SYNTAX_ERROR`. */ export declare class FunctionInputError extends Error { readonly pos: number; constructor(message: string, pos?: number); } /** * One entry in the params destructure of a function-form arrow. * - `key` is the property looked up on the params object at call time * (the *outer* destructure key). * - `name` is the identifier used in the function body (the *inner* alias if * the user wrote `{ key: alias }`, or the same as `key` otherwise). * * Most users never alias and both fields are identical; aliasing exists so a * verbose public param name can have a short body-local synonym. */ export type ParamBinding = { key: string; name: string; }; /** * Result of `Parser.parseFunctionInput()`. The function source is split into a * parsed body (`program`) and the parameter bindings extracted from the params * slot (`bindings`, empty when the arrow has no params destructure). The two * pieces travel together because codegen needs the binding names to interpret * bare identifiers in the body as parameter references rather than unknown * idents. */ export type FunctionInputResult = { program: Program; bindings: ParamBinding[]; }; export declare const MAX_RECURSION_DEPTH = 200; export declare class Parser { private readonly lexer; private depth; constructor(src: string); parse(): Program; /** * Entry point for the function-input form (`jsmql(($) => …)`). The source * is the result of `Function.prototype.toString.call(fn)` — a full arrow * function expression. We consume the parameter list and `=>`, then dispatch * to either a block-body parser (`{ stmt; stmt; }`, the function-form mirror * of the implicit `;`-separated pipeline) or an expression-body parser (a * single jsmql expression / update op, with one optional trailing `;` allowed * as a formatter artifact — single-statement bodies do NOT flip into pipeline * mode here). * * Raises `FunctionInputError` for shape problems specific to the adapter * (`async`, `function`, missing arrow, `return` in a block body, …) and * `ParseError`/`LexError` for grammar problems inside the body itself. */ parseFunctionInput(): FunctionInputResult; /** * Parse and classify the parenthesised parameter list of the function-form * arrow. Each top-level parameter slot is one of three *shapes*: * * - **Plain identifier** (`$`, `doc`, anything) — the document-context slot. * Discarded; the body parser doesn't need the name. * - **Destructure pattern with all keys starting with `$`** (`{ $dateDiff }`, * `{ $match, $project }`) — the ops-hint slot (types-only IDE * autocomplete). Discarded; the keys don't reach codegen. * - **Destructure pattern with at least one non-`$` key** (`{ minAge }`) — * the function-form parameter bindings. Names are returned so codegen can * inline values supplied at `jsmql.compile()` call time. * * The legal slot orderings are: `()`, `(doc)`, `(ops)`, `(params)`, * `(doc, ops)`, `(params, doc)`, `(params, ops)`, `(params, doc, ops)`. * Anything else throws `FunctionInputError` with a precise message. * * Returns the binding names extracted from the params slot (in source * order). Cursor is left immediately after the closing `)`. */ private parseParameterList; /** * Parse a single parameter slot and classify it by shape. Called by * `parseParameterList`; advances the lexer past the slot. */ private parseParameterSlot; /** * Parse `{ key (: alias)? (, key)* (,)? }` and classify the slot as `ops` * (every key starts with `$`) or `params` (at least one non-`$` key). * Rejects defaults, nested destructure, rest, and mixed `$`/non-`$` keys * with the user-facing error messages from `docs/LANGUAGE.md`. */ private parseDestructureSlot; /** * Parse the body of a block-body arrow: `{ stmt (; stmt)* ;? }`. This is * structurally the same as the top-level `;`-separated pipeline form * (see `parse()`), terminated by `}` instead of EOF. A single-statement * block body without `;` returns the underlying `Expr`/`UpdateFilter` * unchanged; any `;` (including a trailing one) wraps as a `Pipeline`. * * `return` is rejected up front with a precise `FunctionInputError` so the * user gets a clear pointer to either the `;`-separated form or an * expression-body arrow, instead of the parser's generic "unknown * identifier" message. */ private parseBlockBody; /** * Parse the body of an expression-body arrow: a single jsmql statement, * with one optional trailing `;` consumed as a formatter artifact. The * trailing `;` does NOT trigger pipeline mode here — single-statement * expression bodies preserve their object-shaped output, matching the * documented contract for `jsmql(($) => …)`. */ private parseExpressionBody; /** * Raised when a `let` declaration appears at the top of an input that turns * out to be expression-mode (no `;` separator, not a bracketed pipeline). * `let` only makes sense as a pipeline statement — there's no enclosing * scope for the binding to live in otherwise. */ private throwLetOutsidePipeline; /** * Throw a precise `FunctionInputError` if the next token is the bare * identifier `return`. Called at every statement-start position inside * a block body, so a `return` token *inside* a string or expression * (where it would just be a property name like `obj.return`) doesn't * false-fire — only true statement-leading `return`s reach this check. */ private rejectReturn; /** * Parse one top-level statement: either an expression or a update op chain * (one or more comma-separated update ops sharing a stage). Stops at the * first `;` or EOF; the caller (`parse()`) handles the `;` boundary. */ private collectStatement; /** * Parse `let = `. The leading `let` token must already be the * current peek; this consumes it and the rest of the declaration. The cursor * is left at whatever follows the value expression (typically `;` or `,` in * the array-pipeline form). No re-declaration check here — that needs a * pipeline-level view and lives in codegen. */ private parseLetDecl; /** Entry when the input starts with a update op token (`delete`). */ private parseUpdateFilter; /** Entry when the first target was already parsed as an expression. */ private parseUpdateFilterFrom; /** * Entry when the first target was parsed and is followed by `++` or `--` * (postfix inc/dec). Validation must already have happened. */ private parseUpdateFilterFromPostfix; /** * After the first update op is parsed, consume any `,`-separated tail. * Stops at the first non-`,` token (typically `;` or EOF) and leaves * the cursor there for the caller to handle. */ private parseUpdateFilterRest; /** * Parse a single update op. Returns an array because a chained assignment * (`$.a = $.b = expr`) flattens to multiple `AssignExpr` nodes here, all * sharing the deepest RHS. */ private parseUpdateOp; private parseDeleteStmt; /** * Given a target already parsed and validated, expect an assignment operator * and parse the RHS. Handles right-associative chains for `=`; rejects them * for compound operators because `a += b += 1` is too easy to misread. */ private parseAssignmentChainFrom; /** * Returns the assignment operator string at the current position, or null * if the next token is not an assignment operator. */ private peekAssignOp; private isAssignOpType; private peekUpdateOpSeparator; /** * Returns "++" or "--" if the next token is an inc/dec operator, else null. * Used at both prefix (start of update op) and postfix (after a target) * positions; the caller decides which. */ private peekIncDecOp; /** * Parse a prefix `++` or `--`. The prefix vs postfix * distinction is irrelevant in MQL pipeline context — both forms compile * to the same `$set: { x: { $add|$subtract: ["$x", 1] } }` shape, since * pipeline stages don't carry "value of expression" semantics. */ private parsePrefixIncDec; /** Build the desugared AssignExpr for `target++` / `target--` / `++target` / `--target`. */ private makeIncDecUpdateOp; /** * Lookahead: do the next tokens look like `$.path[.path]* `? * Used to detect the start of a chained assignment's RHS when we're at * the right of a `=` operator. Bounded by the length of the field path * so it's cheap and never false-positive on regular RHS expressions. */ private peekIsAssignmentChainStart; /** * UpdateOp targets are restricted to field paths: a `FieldRef` (`$.x`) or * a chain of `MemberAccess` nodes rooted at one (`$.x.y.z`). Anything else * — index access, function calls, parameter refs, parenthesized expressions * containing operators — is rejected with a precise error. */ private validateUpdateTarget; private isFieldPathTarget; /** * Accept the `$out` sugar LHS shape: one or two static (dot or bracket) * accesses rooted at `DatabaseRef` (`$$$`) or `ClusterRef` (`$$$$`). The * detailed validation (right segment count, no computed bracket) lives in * codegen — at parse time we only commit to "this looks like a write * destination" so the assignment can be built and routed. */ private isOutTarget; private parseExpression; /** ternary: nullish ("?" expression ":" ternary)? — right-associative */ private parseTernary; /** nullish: or ("??" or)* — left-associative, flattened later */ private parseNullish; /** or: and ("||" and)* */ private parseOr; /** and: bitOr ("&&" bitOr)* */ private parseAnd; /** bitOr: bitXor ("|" bitXor)* */ private parseBitOr; /** bitXor: bitAnd ("^" bitAnd)* */ private parseBitXor; /** bitAnd: comparison ("&" comparison)* */ private parseBitAnd; /** * comparison: relational [ (==|!=|===|!==) relational ] * Non-chainable. Lower precedence than relational (<, <=, >, >=, in) to match JS. */ private parseComparison; private peekEqualityOp; /** * relational: additive [ (<|<=|>|>=|in) additive ] * Non-chainable. Higher precedence than equality to match JS. */ private parseRelational; private peekRelationalOp; /** additive: multiplicative ((+|-) multiplicative)* */ private parseAdditive; /** multiplicative: power ((*|/|%) power)* */ private parseMultiplicative; /** power: unary ("**" power)? — right-associative */ private parsePower; /** unary: typeof | ("!"|"-"|"~") unary | postfix */ private parseUnary; /** * postfix: primary ( "[" expression "]" | "." member | "?." member | "?.[" expression "]" )* * * Optional chaining (`?.`) produces the same AST node shape as the non-optional * counterpart, but with `optional: true`. The codegen consults this flag at every * null-unsafe consumer site (array spread, array/string method receivers, string * `$concat`, template-literal interpolations, `.length`, `Object.keys`/etc.) to * wrap the chain's result with `$ifNull(v, neutral)`, where `neutral` is `[]` * / `""` / `{}` depending on the consumer. See `chainHasOptional` / * `wrapIfNull` in src/codegen.ts and docs/specs/method-dispatch.md. */ private parsePostfix; /** Parse method call argument list: "(" [argOrLambda (, argOrLambda)*] ")" */ private parseMethodCallArgs; /** * Parse one argument in a call site. Allows: * - ...expr (spread) * - lambda forms (x => ..., (x) => ..., (x, y) => ...) * - any expression */ private parseCallArg; /** * Parse an argument that might be a lambda expression. * Checks for lambda patterns before falling back to parseExpression(). * When `allowBlockBody` is true, the lambda body may be a `{ stmt; stmt; }` * block (only set inside `$$$..find/filter(...)`); otherwise `=> {` * keeps its current meaning (object-literal start via parseObjectLiteral). */ private parseArgOrLambda; /** primary: operator_call | field_ref | literals | "(" expr ")" | array | object */ private parsePrimary; /** "(" expression ")" — also handles `(x => expr)`, the unparen-single-param lambda */ private parseGrouped; private parseOperatorCall; /** $.field — stops at first segment; postfix handles further dots */ private parseFieldRef; /** * Context-reference prefix: `$$` (collection), `$$$` (database), `$$$$` (cluster). * Returns a bare marker AST node; postfix `.name` (MemberAccess) and `[expr]` * (IndexAccess) compose via the standard primary-postfix loop. * * Sanity-guards that the next token is `.` or `[` so bare `$$$` / `$$$$` * (which are meaningless on their own) produce an actionable error rather * than a downstream surprise. `$$` (CollectionRef) is more permissive: it * is valid as the LHS of `$$ = ` (the stream-level replacement) and * as the RHS of `$$$.coll = $$` (the no-op `$out` write of the current * stream), so for CollectionRef we accept anything that isn't an * identifier-like follower. The typo case `$$foo` (no separator, an Ident * next) is still rejected so the user sees the actionable * "Expected '.' or '[]'" hint; codegen continues to gate bare * `$$` in unsupported positions via the CollectionRef branch. */ private parseContextRef; /** * An identifier-like token — a regular `Ident` or one of the reserved-word * keywords (`in`, `new`, `typeof`) we accept in identifier position. Valid * after `.` (field-path segments, member names), after `$` (operator names), * and as a `$` in object literals. */ private isIdentOrKeyword; /** * Non-consuming lookahead: is the current position the start of a parenthesized lambda? * Matches: "(" ")" "=>" | "(" Ident ")" "=>" | "(" Ident ("," Ident)* ")" "=>" */ private isLambdaStart; /** Parse "x => expr" — single unparenthesized parameter */ private parseLambdaUnparen; /** Parse "(x) => expr" or "(x, y) => expr" or "() => expr" */ private parseLambdaParen; /** * Parse the block-body of a lookup-callback lambda: `{ stmt; stmt; }`. * The block reuses the same machinery as the top-level `;`-separated * pipeline form — every statement must be a stage call, an update op * (`$.x = …`, `delete $.x`), or a `let` binding. The result is normalised * to a `Pipeline` (one stmt = single-stage pipeline, several = multi-stage). * Only callable when the lookup-callback `allowBlockBody` flag is set — * outside that, `=> {` keeps its existing object-literal interpretation. */ private parseLambdaBlockBody; /** "new Date()" / "new Date(expr)" or "new Set()" / "new Set(expr)" */ private parseNewDate; /** "Date.now()" or "Date.UTC(year, month, day, …)" — other Date.* members are not supported */ private parseDateStatic; /** "Array.isArray(x)" or "Array.from(input)" or "Array.from(input, mapFn)" */ private parseArrayStaticCall; /** "Number.isInteger(x)" / "Number.isNaN(x)" / "Number.isFinite(x)" */ private parseNumberStaticCall; /** "Math.method(args)" or "Math.PI" / "Math.E" constants */ private parseMathReference; /** "Object.method(args)" */ private parseObjectCall; /** "Number(x)" | "String(x)" | "Boolean(x)" | "parseInt(x)" | "parseFloat(x)" */ private parseTypeCast; private parseNumber; /** Parse a template literal: `chunk0${expr0}chunk1${expr1}chunk2` */ private parseTemplateLiteral; private parseArrayLiteral; private parseObjectLiteral; /** * Parse one non-spread object entry. Supports: * - Static key: `name: expr` or `"name": expr` * - Computed key: `[expr]: expr` * - Shorthand: `name` (sugar for `name: name`, valid in lambda scope) */ private parseObjectEntry; } //# sourceMappingURL=parser.d.ts.map