Skip to content

This site describes solve-engine as it is on main: 2.43.0, which npm does not have yet. npm installs 2.40.0, so a page may show an answer that version does not give yet.

ParseletRegistry

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

ParseletRegistry, which accepts both string token types and integer token type IDs, the integer form being the fast dispatch in the Parser hot path.

Providers call registerPrefix(“NUMBER”, …) with string token types. Parser.parseExpression() looks a parselet up by token.typeId (integer), while diagnostics and error messages use token.type (string).

One map per kind, keyed by the integer ID. A name and its ID are one to one (the process-wide table in lexer/Token.ts hands each name one ID for good), so a lookup by name translates the name and reads the same map, and getAllPrefix translates back. This used to keep a second, string-keyed copy of each map as well. The built-in packages register more than 256 prefix parselets, past the size at which a Map doubles its table, so the copy cost every engine about 14KB for prefix parselets alone and answered nothing the integer map does not.

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:177

Number of registered infix parselets.

number


get prefixCount(): number;

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

Number of registered prefix parselets.

number

clear(): void;

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

Remove every registered parselet.

void


getAllInfix(): {
associativity: "left" | "right";
category?: string;
leftBindingPower: number;
rightBindingPower: number;
tokenType: string;
}[];

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

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. An operator that declares rightAssociative (^) is reported with the right power one BELOW the left, which is how it parses its right operand (see parseRightOperand), and associativity says which in words.

{ associativity: "left" | "right"; category?: string; leftBindingPower: number; rightBindingPower: number; tokenType: string; }[]


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

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

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:195

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:186

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). A name no token type was ever registered under has no parselet, and asking does not register it.

ParameterType
tokenTypestring | number

| PrefixParselet | undefined


hasInfix(tokenType): boolean;

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

Whether an infix parselet is registered for the token type tokenType.

ParameterType
tokenTypestring

boolean


hasPrefix(tokenType): boolean;

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

Whether a prefix parselet is registered for the token type tokenType.

ParameterType
tokenTypestring

boolean


registerInfix(tokenType, parselet): void;

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

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:95

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