Normative description of what JsParser.Parse produces. A port must reproduce the tree shape, the scope
information and the error behavior described here.
Entry point
JsParser.Parse(source, options, cancellationToken) returns ParseResult { Program, Source, Diagnostics, HasErrors }.
It never throws for malformed input; every problem is a Diagnostic. ParseOptions:
| Option | Default | Meaning |
|---|---|---|
Goal | Script | Script, Module (always strict, top-level await) or Eval |
Strict | false | start in strict mode |
FileName | <anonymous> | name shown in reports |
MaxDepth | 512 | nesting limit; exceeding it reports depth-limit and stops |
MaxErrors | 100 | parsing stops after this many errors |
AllowReturnOutsideFunction, AllowNewTarget, AllowSuperProperty, AllowSuperCall, InClassFieldInitializer, PrivateNames | off | context for direct eval code and hosts that wrap source in a function body |
RegExpValidator | the engine's RegExpValidator.ValidateLiteral | (body, flags, bodyStart) -> IReadOnlyList<RegExpDiagnostic>: problems with source-offset spans, empty when valid; the first becomes the SyntaxError (headline = its V8 message, span = its token); null accepts all patterns |
Positions
Every node has Start and End, UTF-16 offsets into the source. SourceText.GetPosition(offset) gives the
1-based line and column; line terminators are LF, CR, CR LF, U+2028 and U+2029 (ECMA-262 12.3). The --> line
of a report is the start of the primary span.
Tree shape
- Node classes live in
RecursiveAI.OneEcma.Syntax(Ast.csis generated bytools/gen-ast.pyfrom a table in that script; edit the table, not the output). Every node hasStart,EndandKind. Expressions addIsParenthesized(there is no parenthesized-expression node); statements and declarations derive fromStatement. - One
FunctionNodecovers declarations, expressions, arrows, methods, getters/setters and generators/async variants, with flags (IsArrow,IsAsync,IsGenerator,IsMethod, ...),Id,Params,Body,Scope, andUsesArguments,UsesThis,HasDirectEval. OneClassNodecovers class declarations and expressions. - Binding and assignment targets are separate pattern nodes (object, array, assignment, rest patterns), produced by
reinterpreting the cover grammar (
a = [x, y], arrow parameters) with the early errors of ECMA-262 13.15.5 and 15.2.1. - Optional chains are a
ChainExpressionwrapper around member and call nodes (ECMA-262 13.3.9). - Module declarations (
import,export) have their own nodes;import(...)isImportCall,import.metaandnew.targetareMetaProperty. - Error recovery inserts
ErrorExpression(empty span at the error) where an operand is missing; a tree with errors is for diagnostics, not for execution. - Operator chains are flat in source but nested in the tree, so
a+b+c+...with 100k operands gives a tree 100k deep. Consumers must walk trees iteratively or check a depth counter.
Scopes
Scope analysis runs inside the parser (early errors need it) and leaves Scope objects on the tree:
Scope { Kind, Parent, Children, Bindings, ContainsDirectEval, ContainsWith, IsStrict, PrivateNames } and
Binding { Name, Kind, Span, FunctionDeclaration }. Kinds: global, module, eval, function, block, catch, for, switch, class, class field initializer and
class static block. Redeclaration, let/var clashes, duplicate parameters, labels, private names,
break/continue targets, yield/await/arguments/super/new.target context and strict-mode restrictions are
reported as early errors in V8's wording (syntax-messages.md).
Annex B: a function declaration in a block (sloppy mode) is hoisted to a var of the same name unless that would
conflict (B.3.3). The declaration carries IsAnnexBHoisted and the function scope gets an AnnexBFunctionVar
binding. Also accepted: HTML-like comments (B.1.1), legacy octal literals and escapes (sloppy only), __proto__
duplicates rules, for (var x = 1 in y) (sloppy), labelled function declarations (sloppy), if function bodies.
Error recovery and reporting
- The parser reports every syntax error it finds. After an error it inserts the missing token or skips the stuck
token (recorded in
Diagnostic.Recovery) and resynchronizes at statement boundaries and at the closing bracket. - Resynchronization: an error inside a parameter list skips to the next
,or the closing)on bracket structure; a brokenif/while/for/withhead skips to its)on the same line. The body is then parsed as a body instead of as fallout of the head. - Cooldown: a new error within 3 tokens of a syntax error, or within 12 tokens on the same line, is dropped as fallout; an end-of-input error is exempt from the same-line window (an unclosed bracket is always worth saying). An early error (not a grammar error) within two lines after a syntax error is dropped as well. After a lexical error (unterminated string, typographic quote) errors on the same and next line are dropped. Deviation: two real mistakes closer than that are reported as one.
- Hints: after parsing,
SyntaxHintsrecognizes common mistakes (Pythonelif/def/and/or/for ... in range, TypeScript annotations,ascasts andinterface, HTML entities, typographic quotes, stray closers, a bracket still open at the end of the input) and may move the primary span to the construct that is wrong and add a label andhelp. The headline is never changed. A hint never changes the source. - A regular expression literal's flags are validated by the lexer; the pattern body goes to
RegExpValidator.
Limits and hostile input
- Recursion is bounded by
MaxDepthplusRuntimeHelpers.TryEnsureSufficientExecutionStack; hitting either reports thedepth-limitdiagnostic ("Maximum nesting depth exceeded") and ends the parse. Tested with 100,000 nested(,[,{, functions, arrows, unary and ternary chains on a 256 KB thread stack. - Cancellation is polled every 256 tokens.
- Work per error is bounded by a constant window of source text, so a long file full of errors stays linear.
Documented deviations from Node
- Sloppy-mode call expressions are accepted as assignment, update and for-in/of targets (web compatibility,
Annex B test262 tests); they are a runtime
ReferenceError. - Errors closer together than the cooldown are merged (above).
- Where Node reports one error, the parser may report more after it; the first error's headline and line are
always Node's (
SyntaxMessageTests).