11. Persistence plugins
Contents of FBL 0.9
Some formats are too intricate to declare: projecting Turtle triples onto cards and rows, evaluating MSBuild with its imports and wildcards, parsing SPARQL. For them a binding's reader is a persistence plugin:
{ "reader": { "plugin": "net.etalii.adp.w3c.turtle", "version": "^0.1.0" } }
The plugin is declared in the DISL specification's plugins with provides: ["persistenceFormat"] (DISL §13.1); args are validated against its declaration. Everything else in the binding is declared and done by the host exactly as for a declared reader: claims and routing, the registration, several readings, folder recognition and watching, templates, history, drift and saving. A format FBL can declare SHOULD be bound rather than read by a plugin; (FBL 0.9) a format with a context-free syntax can be declared with the grammar family (section 4.9).
A plugin implements these operations, stated as data exchanged so that each host binds them to its own plugin mechanism:
| Operation | Receives | Delivers |
|---|---|---|
read |
The body's bytes (a file body), or for a folder subject the relative path and bytes of every file the file rules select; the binding's args. |
The elements and relations: type, id, parent and slot, attribute values; for each, the source span of the entry and of every writable value (file, byte range); the findings, with source locations; and whether the body is unreadable as a whole. |
plan |
The current bytes, the last read result, and one model change (below): add, set, remove, move or retype an element or relation. |
The splices that realise it, each {operation, file, start, end, text} with an operation from section 6.1; or a refusal with the sentence the host shows. |
template |
The new body's name and the template placeholders (section 13). | The bytes of a new body. Only asked when the binding has no template.text. |
watch |
The last read result (folder subjects, optional). |
The paths the reading depends on beyond the file rules; the host watches them too. |
Model changes (FBL 0.2). The change plan receives is one of these, each an object with the change's name as its one key ($defs/ModelChange):
| Change | Fields |
|---|---|
add |
type, id (optional), attributes, parent (optional), slot (optional), position (optional) |
set |
id, attributes |
remove |
id |
move |
id, parent (null for the top level), slot (optional), position |
retype |
id, type, attributes (the values after the type's attributeMapping, DISL §9.5) |
where position ($defs/ChangePosition) is { "index": int }, { "after": id } or { "before": id }. A plugin declares which changes it plans in its DISL declaration (DISL §13.1): "plans": ["add", "set", "remove", "move", "retype"], with "add": { "position": true } when it honours a position, and "move": { "position": true } likewise. Without plans, a plugin plans add, set and remove.
- A host MUST send an add with
positionwhen the change came from an ordered placement (DISLcreatewithafterorbefore, a parent placement, a drop), and a plugin that declaresadd.positionMUST insert at that position or refuse. - A host MUST send a reorder or a re-parent as one
move, never as a remove and an add, so the plugin can splice the element's bytes unchanged; a plugin that does not declaremovegets the remove and the add instead. - A host MUST send a DISL retype as one
retype; a plugin that does not declareretypeMUST be sent a remove and an add, and the host MUST warn that identity and unbound bytes may not survive. - An index MUST be counted among the parent's children in the slot after the element was removed from its old place, so the same index means the same thing for a move within one parent and across parents.
{ "move": { "id": "1.2", "parent": "1", "position": { "index": 2 } } }
{ "retype": { "id": "1.3", "type": "Check", "attributes": {} } }
{ "add": { "type": "Check", "attributes": { "label": "New question" }, "parent": "1", "position": { "index": 1 } } }
An agent behavior model's Markdown plugin declares all five changes and positions. Move later on 1.2, the second of three children of 1, sends the move above; changing 1.3 from Do to Check sends the retype; a drop under 1 between its first and second child sends the add. Each becomes line splices that keep every other byte.
The host, not the plugin, applies splices to bytes and saves them atomically; keeps the history and undoes with inverse splices or snapshots; checks drift; watches the body and calls read again after external changes; stores view data in the registration; routes files; and shares one open body between readings. A plugin-backed body therefore behaves exactly as a declared one: byte-preserving writes, exact undo, drift refusal, several readings, folder subjects.
readMUST NOT fail on content: problems are findings, and an unreadable body is reported as such (section 7).readandplanMUST be deterministic: the same bytes and change give the same result, in every host's implementation of the plugin.- Splices from
planMUST change only the bytes the change concerns, MUST name their operation truthfully, and MUST follow the new-text rules of section 6.3 where they apply to the format. - A plugin MUST NOT write files, keep its own undo history, or store view data. A read-only binding's plugin is never asked to
plan. - A plugin MAY report findings about anything in the body, with DISL's source location and
subject; the host shows them like any other finding.
| Format | Tool types | Why a plugin |
|---|---|---|
| Turtle and N-Triples | W3C RDF, OWL, SHACL, SKOS | Terms span tokens, prefixes compress IRIs, and one IRI yields several elements; the projection is code. (FBL 0.9) Turtle's syntax is read by the grammar family in w3c-turtle-grammar.fbl, and spec 035 binds the W3C RDF type's Turtle with it. |
| SPARQL 1.1 | SPARQL query | A recursive grammar projected onto scopes; read-only. |
.sln, .slnx, MSBuild projects |
.NET dependency graph | Wildcards, imports and central package management are evaluated; read-only. |
| Ansible project folders | Ansible structure | YAML and INI inventories, task directives and role resolution; read-only. |
| Helm chart folders | Helm chart | Templates are scanned as lines, not YAML, and includes resolved; read-only. |
| Azure Pipelines template expansion | Azure DevOps pipeline | Following template: references and unwrapping ${{ }} expressions is evaluation; the rest of the file could be declared. |