Skip to content

ParseletRegistry

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:42

Dual-keyed ParseletRegistry, accepts both string token types and integer token type IDs for fast dispatch in the Parser hot path.

Providers call registerPrefix(“NUMBER”, …) with string token types. Internally, we populate both string-keyed and integer-keyed maps so Parser.parseExpression() can use token.typeId (integer) for lookup while diagnostics and error messages use token.type (string).

Performance: Integer Map.get() avoids string hashing, saving ~2-5ns per dispatch. With ~10-15 dispatches per expression, that’s ~20-75ns.

new ParseletRegistry(): ParseletRegistry;

ParseletRegistry

get infixCount(): number;

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:165

Number of registered infix parselets.

number


get prefixCount(): number;

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:162

Number of registered prefix parselets.

number

clear(): void;

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:194

void


getAllInfix(): {
category?: string;
leftBindingPower: number;
rightBindingPower: number;
tokenType: string;
}[];

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:147

Iterate all registered infix parselets for diagnostic display.

The field an InfixParselet actually declares is bindingPower. This used to read leftBindingPower and rightBindingPower, which no parselet in this codebase declares, so both reads were undefined, both fell to the ?? 0 default, and the public getParseletRegistry() reported a binding power of 0 for every one of the ~60 infix operators. A host building a precedence table from it was told * and + bind equally, and that neither binds at all.

The left/right split: bindingPower is the LEFT power, and the right is one higher. That is the standard encoding for a left-associative operator, and it is what the parser itself does, see PrecedenceParser.parseExpression()’s bp + 1 for the right operand. Every operator is reported that way, ^ included. Associativity is not something a parselet declares, it is a property of how each one calls parseExpression, so this API cannot report it without a new field on the interface. See ParseletBindingPowers for why scraping a plausible-looking one off the parselet is worse than not reporting it.

{ category?: string; leftBindingPower: number; rightBindingPower: number; tokenType: string; }[]


getAllPrefix(): {
bindingPower: number;
category?: string;
tokenType: string;
}[];

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:114

Iterate all registered prefix parselets for diagnostic display.

PrefixParselet declares no binding power, and this used to read a field of that name and report 0 for every one of them. 0 is not a neutral wrong answer: it is the value that means “not an operator, stop the expression”, so a host drawing a table from this was told none of them bind at all. A prefix parselet in this parser has no per-parselet power to report, they all bind at the prefix level and each chooses for itself at what power to parse its own operand, so that level is what is reported. A parselet that does carry its own bindingPower still wins.

{ bindingPower: number; category?: string; tokenType: string; }[]


getInfix(tokenType):
| InfixParselet
| undefined;

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:181

Get infix parselet by string token type OR integer typeId. Fast path for integer IDs (Parser hot path), fallback for strings.

ParameterType
tokenTypestring | number

| InfixParselet | undefined


getPrefix(tokenType):
| PrefixParselet
| undefined;

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:172

Get prefix parselet by string token type OR integer typeId. Fast path for integer IDs (Parser hot path), fallback for strings (diagnostics, error messages, backwards compatibility).

ParameterType
tokenTypestring | number

| PrefixParselet | undefined


hasInfix(tokenType): boolean;

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:190

ParameterType
tokenTypestring

boolean


hasPrefix(tokenType): boolean;

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:186

ParameterType
tokenTypestring

boolean


registerInfix(tokenType, parselet): void;

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:88

Register an infix parselet for tokenType. See registerPrefix for the collision-warning behavior this mirrors.

ParameterType
tokenTypestring
parseletInfixParselet

void


registerPrefix(tokenType, parselet): void;

Defined in: packages/engine/src/parser/registry/ParseletRegistry.ts:73

Register a prefix parselet for tokenType.

If another parselet is already registered for this token type, it is silently overwritten by default (Map.set() semantics), the old parselet is simply unreachable from then on, with no error. This is a real footgun for third-party packages: two packages independently choosing the same custom token type will collide with zero signal about which one “won”. Mirrors ResolverRegistry.register()‘s and ExpressionEngine.registerPackage()‘s existing “warn and replace” pattern for the same class of problem at the resolver-namespace and package-name levels.

Note: this warns about registry-level collisions only. It does NOT detect the separate case where tokenType is one of PrecedenceParser’s Tier-1 fast-path token types (NUMBER, STRING, IDENT, LPAREN, MINUS, PLUS, and the Tier-1 infix operators), those are deliberately kept registered here for introspection/diagnostics even though Tier-1 always intercepts them before this registry is consulted (see PrecedenceParser.parsePrefix()‘s docs), so warning there would misfire on that intentional, already-documented pattern.

ParameterType
tokenTypestring
parseletPrefixParselet

void