Mohamed Amine Hajji / Projects

mapbox-expr-lang

A compiler for a human-friendly language that targets Mapbox GL style expressions — hand-written lexer, parser, and static type checker, published on npm with one tiny public API: compile().

Live playground · npm · GitHub · Docs

Mapbox GL styles are driven by expressions — a JSON-array mini-language that is powerful but hard to read once it grows. Coloring roads by speed and type, for example, looks like this:

["case",
  ["all",
    [">", ["get", "speed"], 80],
    ["==", ["get", "type"], "highway"]
  ],
  "red",
  "gray"
]

After working with these daily on geospatial products, I designed a small language that reads like normal code — variables, if/elif/else, match, interpolate, step, function calls — and compiles to exactly the expression Mapbox expects. The same rule becomes:

var speed = get("speed")
var type = get("type")

if speed > 80 and type == "highway" then
  "red"
else
  "gray"

Everything is built from scratch in TypeScript, with no parser generator: a lexer with significant newlines, a recursive-descent parser that produces a typed AST, and a compiler that emits Mapbox JSON. On top sits a small, deliberately sound static type checker. It only rejects what it can prove is wrong from syntax alone — a number used as a boolean, a string in arithmetic, out-of-order interpolation stops — and defers everything else to Mapbox, so it never blocks a valid style but catches mistakes before they reach the map, with a message like 'and' requires a boolean operand, but this is a number.

Every design decision that touches real Mapbox semantics was verified against the actual style-spec evaluator rather than trusted from the docs — which surfaced several places where the documentation and the real engine disagree. The package is published on npm with CI, a formal EBNF grammar, a language guide, and a browser playground.

Highlights

Stack: TypeScript, Mapbox GL, Vitest, tsup, npm, GitHub Actions