Skip to content

Parsers

This guide explains how to create custom parsers for BeDoc. Parsers read source files and extract documentation into a structured object that a formatter can render.

A parser is an action: a JavaScript file that default-exports a class with a static meta block and a setup pipeline.

A BeDoc parser has three parts:

  1. Meta — declares the language it reads and points at its contract. (required)
  2. Contract — an external Terms file describing the object the parser provides. (required)
  3. Pipeline — the steps that turn raw text into that object. (required)

Hooks are supported automatically at every named step. (optional, external)

import { ActionBuilder, ACTIVITY } from "@gesslar/actioneer"
export default class LuaParser {
static meta = Object.freeze({
kind: "parser",
input: "lua", // matched against --language lua
terms: "ref://./bedoc-lua-parser.yaml",
})
// `setup` configures the pipeline. Step names become hook anchor points.
setup = builder => builder
.do("Extract blocks", this.#extractBlocks)
.do("Process functions", ACTIVITY.SPLIT,
ctx => ctx, // splitter: hand each block onward
ctx => ctx, // rejoiner: collect the results
new ActionBuilder()
.do("Extract signature", this.#extractSignature)
.do("Extract description", this.#extractDescription)
.do("Extract tags", this.#extractTags),
)
.done(this.#finally)
// The first step receives the raw file content as `ctx`.
#extractBlocks = ctx => {
const blocks = []
for(const line of ctx.split(/\r?\n/)) {
// …find doc blocks, push structured chunks…
}
return blocks
}
#extractSignature = ctx => ctx
#extractDescription = ctx => ctx
#extractTags = ctx => ctx
// The final step returns the object promised by the contract.
#finally = ctx => ({ functions: ctx })
}

The first pipeline step receives the file’s text as its context. Each step returns the context passed to the next. The .done() step returns the structured result — the shape your contract provides.

A parser declares what it produces in an external Terms file referenced by meta.terms (ref:// is resolved relative to the action file). A parser’s contract uses provides::

bedoc-lua-parser.yaml
# yaml-language-server: $schema=https://schema.gesslar.dev/bedoc/v1/bedoc-action.json
$schema: https://schema.gesslar.dev/bedoc/v1/bedoc-action.json
provides:
type: object
required:
- functions
properties:
functions:
type: array
items:
type: object
required: [name, signature]
properties:
name: { type: string }
signature:
type: object
properties:
name: { type: string }
parameters:
type: array
items: { type: string }
description:
type: array
items: { type: string }

See the Contracts guide for the full structure.

Every named pipeline step is a hook anchor. A user can wrap a step named "Extract tags" with before$extractTags / after$extractTags handlers (the hook name is the camelCase of the step name) to mutate the context as it flows through. See the Hooks guide.

Real parsers (LPC and Lua) live in the BeDoc repository under examples/node_modules_test.

  1. Keep steps small and named. Each named step is a hook point and a unit of testability.
  2. Honour your contract. The object you .done() with must match what your Terms file provides:.
  3. Use ACTIVITY.SPLIT for per-item work (e.g. per-function) so it runs concurrently.
  4. Reset state deliberately if your steps carry parsing state.

Mock mode — point BeDoc at a directory of mock actions:

Terminal window
bedoc --mock ./mock_dir -l mylang -f markdown -i "test/*.ml" -o test/docs

Direct file — use your parser file without packaging it:

Terminal window
bedoc --parser ./my-parser.js --format markdown -i "test/*.ml" -o test/docs
# Or the short form
bedoc -p ./my-parser.js -f markdown -i "test/*.ml" -o test/docs

Package your parser following BeDoc’s naming and structure conventions, and list the action file(s) under bedoc.actions:

{
"name": "bedoc-mylang-parser",
"version": "1.0.0",
"type": "module",
"description": "MyLang parser for BeDoc",
"bedoc": {
"actions": ["./bedoc-mylang-parser.js"]
}
}

This lets BeDoc automatically discover and load your parser when it’s installed.