Skip to content

feat(parser): extensible parser routing + functional parser modules (0.22.0) - #172

Merged
facontidavide merged 1 commit into
mainfrom
feat/parser-extensibility-1a
Aug 9, 2026
Merged

feat(parser): extensible parser routing + functional parser modules (0.22.0)#172
facontidavide merged 1 commit into
mainfrom
feat/parser-extensibility-1a

Conversation

@facontidavide

@facontidavide facontidavide commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

SDK PR 1a of the parser-extensibility v4 architecture (spec, PlotJuggler/pj-official-plugins#272): the complete wasmer-free core, with the dual-target module ABI frozen here. PR 1b (wasmer loader + hardening) stacks on this branch; the two PRs merge back-to-back and release together as 0.22.0 (single version increment across the stack).

Surface

Area Contents
Route-aware classification pj.parser_route_claims.v1 — exact handler-table claims (scalar/object), auto-implemented by MessageParserPluginBase; delivered via get_plugin_extension, zero vtable/layout changes (sentinels green)
Functional v2 pj.parser_functional.v2 — spliced object sink (one splice/object, input-space offsets, frozen per-type eligibility table); frozen pj.parser.* error-kind constants; v1 byte-identical
Module ABI parser_module_abi.h — frozen pj_module_* export set (u64 module-space tokens, token-0 creation-error channel) + bounds-checked LE codecs for BindingInfo / parse-input / output-descriptor
Claim catalog + resolver §4 admission matrix, module-manifest ingestion, synthesized plugin claim ids, §5 selection (pin fail-closed → exact > wildcard → provenance tier → priority → identity tie-break), split per-route probe caches, selection traces
Native loader + runtime dlopen(RTLD_LOCAL|RTLD_NOW), per-handle export resolution, session never-unload; runtime with splice eligibility/bounds validation and the fault-vs-data strike tracker (3 → quarantine → recreate; repeat → disable)
Authoring kit pj_base/include/pj_base/parser_module/ — header-only, C++17, wasi-clean, -fno-exceptions capable; CdrReader/CdrFieldLocator (XCDR1 traversal plans), ProtoReader/ProtoFieldLocator, checked time normalization, canonical-wire ObjectWriter (+ splice path), PJ_FUNCTIONAL_PARSER, pj_add_parser_module()
Wasm conformance (static) Shared manifest custom-section codec + wasi-sdk 27 reactor build of the same toy module, audited at the binary-format level (export names and signatures, _initialize, no start section, exactly one manifest section) — no wasmer; gated on PJ_WASI_SDK_ROOT, wired into Linux CI

Verification

  • 75/75 Debug+ASAN (74 with the wasm gate gracefully skipped); layout sentinels, golden byte fixtures, adversarial loader/runtime fixtures
  • Keystone E2E: a kit-authored module (one C++ source) built by pj_add_parser_module, loaded blind, admitted, bound via CdrFieldLocator against a .msg schema, parsed to a host-decodable PointCloud — full-wire and zero-copy-splice variants, plus a schema-revision DECLINE
  • The same toy-module source compiles as the wasm conformance fixture — the "one source, two targets" contract is exercised, not assumed
  • Installed-package consumer test (find_package(... COMPONENTS parser_module))
  • Process: spec closed 4 adversarial review rounds before implementation; each of the 5 implementation milestones was independently reviewed and re-verified; a simplification pass ran before squashing

🤖 Generated with Claude Code

…0.22.0)

SDK core of the parser-extensibility v4 architecture (spec: pj-official-plugins
PR #272) — the complete wasmer-free surface, with the dual-target module ABI
frozen here:

- pj.parser_route_claims.v1 extension: exact handler-table route claims
  (scalar/object, exact-only match), auto-implemented by
  MessageParserPluginBase from its handler table; delivery via
  get_plugin_extension, zero layout changes
- pj.parser_functional.v2: object sink gains accept_object_spliced (one
  splice per object, input-space offsets, frozen per-type eligibility table
  in builtin_object_abi.h); frozen error-kind constants; v1 byte-identical
- parser_module_abi.h: frozen pj_module_* export ABI (u64 module-space
  tokens, token-0 creation-error channel, 512-byte error buffer) +
  bounds-checked little-endian codecs for BindingInfo / parse-input /
  output-descriptor blocks
- host claim catalog + route resolver: §4 admission matrix (priority bounds,
  wildcard/object rules, encoding registry, duplicate identity, provenance
  never from manifests), module-manifest ingestion, synthesized plugin claim
  ids (wildcard:<encoding>, handler:<encoding>:<type>), §5 selection
  (pin fail-closed → exact > wildcard → provenance tier → priority →
  identity tie-break) with split per-route probe caches and selection traces
- native module loader (dlopen RTLD_LOCAL|RTLD_NOW, per-handle export
  resolution, session never-unload) + module runtime over the codecs with
  splice eligibility/bounds validation and the fault-vs-data-error strike
  tracker (3 strikes → quarantine → recreate; repeat → session disable)
- authoring kit pj_base/include/pj_base/parser_module/: header-only,
  C++17, wasi-clean (own Status/Expected/arena; -fno-exceptions capable);
  CdrReader + CdrFieldLocator (XCDR1 traversal plans, depth caps, bounds),
  ProtoReader + ProtoFieldLocator, checked time normalization, canonical-wire
  ObjectWriter (PointCloud/Image + splice path), pj::FunctionalParser +
  PJ_FUNCTIONAL_PARSER macro, pj_add_parser_module() native target
- wasm manifest custom-section codec (shared embed/read; 1b's tooling wraps
  it) + static wasm ABI conformance: wasi-sdk 27 reactor build of the same
  toy module, binary-format audit of export names/signatures, reactor model,
  and single manifest section — no wasmer, gated on PJ_WASI_SDK_ROOT, wired
  into linux CI

Docs and the in-repo authoring skill are synchronized to this surface: a new
parser-module authoring reference, the parser-module choice rule and route-claim
semantics in the MessageParser guidance, and corrections to stale claims (wrong
plugin-base include path, two-builder ObjectWriter, builtin-type inventory).
Editing VERSION now re-runs configure, so a stale build tree can no longer stamp
a previous version into the generated version header.

Rebased onto main: the schema-aware functional-route gate from #174 now governs
the v2 advertisement as well as v1, so a mixed-model parser bound to a schema it
only implements through legacy parse() withdraws both revisions (a host prefers
v2, so leaving v2 advertised would route every message on those topics into a
parser that can only reject them).

Tests: 75/75 Debug+ASAN (74 with the wasm gate skipped), incl. layout
sentinels, golden byte fixtures, adversarial loader/runtime fixtures, and a
kit-authored module E2E (load → admit → bind → full + spliced PointCloud).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@facontidavide
facontidavide force-pushed the feat/parser-extensibility-1a branch from 3501581 to 817130e Compare August 9, 2026 14:49
@facontidavide
facontidavide merged commit aa14df3 into main Aug 9, 2026
7 checks passed
@facontidavide
facontidavide deleted the feat/parser-extensibility-1a branch August 9, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant