Skip to content

Writing a package

Every feature in the engine is a package, including arithmetic. Writing one is the supported way to add syntax.

A package is a plain object. Every field is optional, so you declare only what you need.

import type { IEnginePackage } from "solve-engine";
export const myPackage: IEnginePackage = {
name: "my-package",
engineVersion: "^1.0.0",
};

name must be unique. engineVersion is a semantic version range checked at registration, so a package built against an incompatible engine is refused with a clear message rather than failing mysteriously later.

FieldPurpose
lexerVocabularyKeywords, operators and units the tokeniser should recognise
prefixParseletsParsing rules for tokens that begin an expression
infixParseletsParsing rules for tokens that combine expressions
pluginFunctionsFunctions the virtual machine can call
normalizerRulesToken-stream rewrites, including phrase fusion
asConvertersTargets for the as conversion form
variableSourcesProviders of variable values
asyncResolversExternal data sources
tokenCategoriesHighlighting categories for new tokens
completionItemsEditor completion candidates
import { ExpressionEngine } from "solve-engine";
const engine = new ExpressionEngine("en", false, undefined, undefined, [
...BUILTIN_PACKAGES,
myPackage,
]);

Order matters. Arithmetic registers first so its operators are in place before anything builds on them.

The hardest part of writing a package is not the code, it is picking syntax that does not collide with ordinary prose. Read trigger words before claiming a bare English word. The short version: prefer a multi-word phrase, and prefer requiring a parenthesis, over claiming a common noun as a keyword.