Architecture¶
process-bigraph-lang defines a domain-specific language (files ending in .pblang) for describing
process bigraph composites. A .pblang file is a
tree-shaped, verifiable projection of the composite document that process-bigraph (≥ 1.8, with
bigraph-schema ≥ 1.6) runs: stores, nodes and edges with their wires, plus the static semantics that compile
away (imported or inline interface declarations, parameters, open sites, units, model headers). The design is
in the v2 design, its background, the
registry and authoring design and the plan.
The repository holds two cooperating projects:
| Component | Location | Language | Purpose |
|---|---|---|---|
| Langium DSL | dsl/ |
TypeScript | The grammar, the checker, the compiler and the decompiler; the language server, the VS Code extension, the browser editor demo and the CLI. |
| Python package | process_bigraph_lang/ |
Python | Calls the CLI (compile, decompile, check); describes Python packages and SBML models as headers; compares documents. |
Everything the language does is TypeScript. The Python package has no model of a composite: it is the entry to the CLI from Python plus the pieces that have to run in Python.
End-to-end data flow¶
dsl/ (TypeScript, Langium) process_bigraph_lang/ (Python)
┌──────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────┐
│ .pblang ──► parser ──► AST ──► checker (check.ts) ──► diagnostics │ │ v2/api.py │
│ │ │ │ compile_text / compile_file │
│ ▼ bind.ts │ │ runs cli-native check, then compile │
│ model (model.ts) ──► writer.ts ──► document│◄──┤ decompile │
│ ▲ or spec │ │ runs cli-native decompile │
│ document / spec ──► reader.ts ──┘ │ │ │
│ model ──► printer.ts ──► .pblang + assets │ │ v2/interfaces.py describe_module │
│ │ │ (imports classes) ──► manifest JSON ──┼──► headers
│ bundled with deno compile ──► dist/cli-native (copied by build.py)│ │ v2/sbml.py describe_sbml ──► JSON┼──► model-header
└──────────────────────────────────────────────────────────────────┘ │ v2/normalize.py equivalent / diff │
└─────────────────────────────────────────┘
Documents cross the CLI boundary as JSON in bigraph_schema.json_codec's encoding (numpy arrays tagged), with
tuples tagged as {"__tuple__": true, "data": [...]} by the Python side.
The DSL project (dsl/)¶
dsl/ is a Langium project. Langium generates a parser, AST types and language server
infrastructure from the grammar; the project adds its own services and reuses them in every entry point.
Grammar and language services (dsl/src/language/)¶
pblang.langiumis the grammar (language idpblang, files.pblangand.pbl). A module is a flat list of statements: imports, params, meta and options blocks, interface declarations (headers), model declarations (headers and sorts), the composite tree (store,node,process/stepedges withconfig,inputs,outputs,ports, nestedcomposites,sharedprocesses) andemitdeclarations. See the design's section 2 for the syntax and section 3 for the mapping to the document.generated/holds the AST types, grammar and module produced bynpm run langium:generate. It is gitignored and must be regenerated after every grammar change.process-bigraph-language-module.tsis the dependency-injection module;createProcessBigraphLanguageServices()is the factory every entry point calls.pblang-scope.tsexports every named node (interface and model declarations, imported names), so edges resolve interfaces and model sites resolve headers.v2/is the language:check.tsis the static checker, registered as a Langium validation (validator.ts): unknown ports, config keys, wires to undeclared stores, wire type mismatches, paths above the root, intervals on steps, unknown components, dynamic ports, untyped stores; writer analysis (additive, overwrite, step arrays); emit paths; units with unit variables (units.ts, mathjs); model sites admitted against sorts. Codes follow the registry design's section 4.headers.tsreads interface and model declarations from manifests and from the text;types.tsis the type expression language (bigraph-schema's string and dict forms, the DSL spelling, a canonical form).model.tsis the object model of a composite, the pivot of compile and decompile: ints arebigint, floatsnumber, dictsMaps in source order; numpy arrays, tuples, params, sidecar references and sites are tagged objects.json.tsis the lossless document codec;yaml.tsthe CompositeSpec form.reader.tsclassifies a document (or CompositeSpec) into the model;writer.tswrites the model back;printer.tsprints canonical text with sidecar assets for large values, byte-identical to the Python printer it replaced (format.tsreproduces Python's float andjson.dumpsspellings);bind.tsbinds the Langium AST to the model;resolve.tsis what the checker, hover and completion share (which header an edge is declared by, how a path walks the tree).hover.tsandcompletion.tsare the editor services;graph.tsthe wiring graph for viewers;modelheader.tsrendersdescribe-model's JSON.
Entry points¶
| Entry point | File | Used by |
|---|---|---|
| Language server (Node) | src/language/main.ts |
The VS Code extension |
| Language server (web worker) | src/language/main-browser.ts |
The Monaco-based browser demo (npm run dev, static/, setup*.ts) |
| VS Code extension client | src/extension/main.ts |
VS Code. Starts the language server. |
| Embeddable API | src/api.ts |
Other applications (web apps, scripts, workers): a Pblang session that checks, compiles, decompiles and graphs from strings and plain values. See "Embedding" below. |
| CLI | src/cli/main.ts, bin/cli.js |
Humans and the Python package: check [--json], compile [--to document\|spec] [--strict] [--force], decompile, headers, model-header, graph, generate (the AST as JSON). |
npm run build compiles TypeScript (with declarations) and bundles with esbuild: the extension and the language
server into out/, the CLI into one self-contained file dist/cli.mjs. npm run build:native also runs
deno compile --node-modules-dir=none on that bundle to produce a standalone dist/cli-native executable (about 75 MB,
the Deno runtime plus the bundle) that does not need Node at runtime.
Embedding in another application¶
dsl/ is also the npm package process-bigraph-lang, staged on npm by every GitHub release at the release's version by
.github/workflows/publish-npm.yml through npm's trusted publishing (OIDC, no token) and approved by a maintainer
(see dsl/README.md, "Releasing"); npm run pack writes an unreleased build's tarball to dist/. Nothing in
src/language/ or src/api.ts touches the file system, so the whole language runs in a browser. The package exports:
| Export | What it is |
|---|---|
process-bigraph-lang |
Pblang: addManifest, addHeader, check, compile (to: "document" \| "spec", strict, force, assets), decompile (text + sidecar assets by path), graph. |
process-bigraph-lang/worker |
The language server as a web worker entry (main-browser.ts): diagnostics as you type, hover and completion for an editor such as Monaco through monaco-languageclient. |
process-bigraph-lang/monarch |
The Monarch tokenizer (Monaco's "classic" highlighting). |
process-bigraph-lang/textmate |
The TextMate grammar (source.pblang), for VS Code-style highlighting. |
process-bigraph-lang/language-configuration |
Comments, brackets and auto-closing pairs. |
A viewer decompiles a run's document on the main thread with a Pblang session, shows the text in an editor fed by
the worker, and draws graph(...). An authoring tool adds the manifests of the environment the document will run in to both: the session
through addManifest or setManifests, the worker through the pblang/setManifests notification
(SET_MANIFESTS_NOTIFICATION, { manifests }), after which it re-checks every open document. It then compiles the
text and submits the document. Manifests come from describe_env.py run inside that environment
(registry and authoring, section 1.7). static/ and
src/setup*.ts are the reference wiring of Monaco to the worker (npm run dev).
The Python package (process_bigraph_lang/v2/)¶
api.pyis the public API.compile_text/compile_fileruncli-native checkand thencli-native compile, returning a{"schema", "state"}document or a CompositeSpec mapping (CompileErroron errors;strict=Truemakes warnings fatal;force=Truecompiles an existing document that relies on defaults the checker flags).decompilerunscli-native decompileand returns canonical.pblangtext with its sidecar assets. There is no Python model of a composite and no way to a runnable document that skips the checker: a Python tool that builds composites targets the DSL text.interfaces.pygenerates interface declarations from a module's step/process classes (describe_module, the publish-time step that imports the classes) as the JSON interface manifest (pbg-interfaces/1, a format shared with other tools; JSON Schema indocs/schemas/) the CLI reads;render_interfaceshas the CLI print the.pblangheader file.sbml.pydescribes an SBML model as JSON (pblang-model/0); the CLI renders the model header.check.pywrapscli-native check --json;normalize.pydefines document equivalence (equivalent,diff_documents), withtypes.pyas its type parser;langium.pylocates and runs the CLI (cli-native, ornode dsl/bin/cli.jsin a checkout).python -m process_bigraph_lang.v2 compile | decompile | check | diff | describe | describe-model.
Native binary: process_bigraph_lang/bin/¶
This directory holds the cli-native executable the package runs. build.py (also Poetry's build script) runs
npm install, npm run langium:generate and npm run build:native in dsl/, then copies dsl/dist/cli-native*
here. The wheel includes the binary, so installed users do not need Node or Deno. The wheel is therefore
platform-specific: a release builds one per platform (Linux x86_64 and arm64, macOS arm64 and Intel, Windows),
re-tagged py3-none-<platform> since the Python is pure, and publishes them to PyPI by trusted publishing
(.github/workflows/on-release-main.yml).
Any change to the grammar or TypeScript code only reaches Python after the binary is rebuilt
(python build.py, or cd dsl && npm run build:native && cp dist/cli-native ../process_bigraph_lang/bin/).
Tests¶
dsl/test/v2/: the checker's compliance suite (check.test.ts), hover and completion, the grammar over every snapshot, the model's unit tests (model.test.ts) and the round trip (roundtrip.test.ts): every corpus document prints to its committed snapshot byte for byte, every snapshot is a fixpoint through parse → bind → print, the v1 expected documents write back exactly.tests/v2/: the same round trips, equivalence under the normalizer and realized equivalence underprocess_bigraph.Composite, all through the CLI;describeof the test registry; the SBML header.tests/fixtures/data/v2/:corpus/(see its README for provenance;v1/*.jsonare the expected documents of the former v1 compiler),snapshots/(the printed text, regenerated withPBLANG_UPDATE_SNAPSHOTS=1 pytest tests/v2),interfaces/(manifests and headers, those of viva-pde-particle generated in its own environment),models/(SBML).tests/fixtures/test_registry/holds the processes the composites run.