Skip to content

BPC DSL — Language Specification ​

The Blueprint Chart format (.bpc) is a declarative text DSL for describing a chart and its scenes. It is parsed by a PEG grammar (Peggy) into an AST and converted at runtime into ChartData + ChartOptions.

The canonical grammar lives in packages/lib/src/dsl/grammar.peggy. This page is the human-readable specification.

Stability

The DSL is AST round-trip safe: parse(source) → serialize(ast) → parse(...) produces an equivalent AST. The round trip is over the AST, not the source text. Comments are attached to the AST node they document and survive serialize: the ones written above a member, the one ending a member's line, and the ones after a block's last member. A multi-line comment ending a member's line is re-emitted as a /* */ block so it still parses.

A minimal example ​

bpc
chart line {
  title = "Bitcoin surged past $90,000 in 2024"
  description = "USD, year-end closing price"
  source = "CoinGecko"
  sourceUrl = "https://www.coingecko.com"
  colors = "#f7931a"
  lineSymbols = true
  lineSymbolShowOn = "all"
  lineSymbolShape = "diamond"
  verticalNumberFormat = ",.0f"
  tooltips = true

  annotation "2021" {
    text = "All-time high cycle"
    textOffsetY = -12
    showArrow = true
  }

  data {
    "2016" = 963
    "2017" = 13880
    "2018" = 3742
    "2019" = 7194
    "2020" = 28949
    "2021" = 46306
    "2022" = 16547
    "2023" = 42258
    "2024" = 93429
  }
}

From packages/lib/src/samples/bitcoin-price.bpc

A single-series line chart with a brand color, custom symbol shape, vertical-axis number format, and one point annotation — every feature appears in the same ~30-line block.

Top-level structure ​

A BPC document is exactly one chart block:

bpc
chart <chartType> {
  theme = "blueprint-bold"
  colorPalette = "BlueprintBold"
  <properties>
  <data block>
  <series>
  <colorize / highlight / area-fill / annotation / range / note>
  <scenes>
  <transforms>
}

Where <chartType> is one of the registered chart-type identifiers (line, bar-vertical, bar-horizontal, bar-grouped, bar-stacked, bar-multi, bar-split, column-stacked, area, area-stacked, line-multi, pie, donut). The authoritative list is exported as ChartType from @blueprint-chart/lib.

Lexical grammar ​

Identifiers ​

Identifier ← [a-zA-Z_#] [a-zA-Z0-9_#-]*

Used for chart-type names, property keys, and transform names.

Strings ​

String ← '"' StringChar* '"'

Strings are double-quoted. Supported escapes: \\, \", \n, \t, \r, and \uXXXX (four hex digits, decoded to the matching code point). Any other escape sequence is a parse error.

Numbers ​

Number  ← '-'? ('.' Digit+ / Digit+ ('.' Digit*)?) ([eE] [+-]? Digit+)?
Percent ← Number '%'

Numbers accept an optional leading minus, a fractional part (including a leading-dot form such as .5), and an optional scientific-notation exponent (1e3, 2.5E-4). A trailing % marks the value as a percentage; the parser preserves isPercentage: true on the AST node so downstream code can distinguish 25 from 25%.

Comments ​

Two comment forms exist. Line comments start with // and run to end-of-line. Block comments are delimited by /* and */ and may span multiple lines. Neither carries meaning for rendering, but both are attached to the AST node they sit above, on the same line as, or at the end of the enclosing block, as leadingComments, trailingComment and trailingComments, so they survive a parse then serialize round trip.

Working with the AST ​

ts
import { parse, serialize, compactSerialize } from '@blueprint-chart/lib'

const ast = parse(source)        // BPC text → AST
const text = serialize(ast)      // AST → BPC text (pretty)
const tight = compactSerialize(ast) // AST → BPC text (compact)

For any value the grammar accepts, parse(serialize(parse(x))) is structurally equal to parse(x), and the test suite enforces it. The guarantee covers AST structure only: because comments are consumed as whitespace during parsing, serialize cannot reproduce them, so the emitted text will differ from a comment-bearing original.

Stability and versioning ​

  • parse errors throw SyntaxError with a 1-indexed location (line / column) for tooling.
  • Unknown property keys are preserved on the AST; renderers may ignore them.
  • Unknown top-level statements are a parse error — by design.
  • While Blueprint Chart is pre-1.0, the grammar may change in any release. Every DSL change is called out in the CHANGELOG under a dedicated DSL heading. From 1.0 onward, a breaking grammar change requires a major version bump.

See it in action ​

Every snippet across these DSL pages is taken verbatim from a runnable sample in packages/lib/src/samples/. The corresponding chart-type pages document the option surface and render live previews:

Feature shownSample fileChart-type page
Minimal example, point annotationbitcoin-price.bpcLine chart
Multi-series data, series meta-rowmedal-count.bpcBar multi
colorize single categoryletter-frequency.bpcBar vertical
Curved-leader annotationtemperature-anomaly.bpcLine chart
Scene-by-scene highlight tourco2-emissions-story.bpcBar horizontal
Scenes overriding chart typefarm-compass.bpcArea stacked
transform sortcoffee-production.bpcBar vertical

Next steps ​

The rest of the DSL specification is split across three chapter pages:

Released under the MIT License. Built static-first — your data never leaves the page.