Skip to content
Contents of FBL 0.1

5.1 Element rules

An element rule turns entries into elements of one node type:

Property Type Description
name Name Required. Referenced by parent, cascade, reference.to and within.
type TypeRef Required. The DISL node type produced.
at Selector yaml, json, xml: where the entries are.
line Regex lines, blocks: the statement. Exactly one of at and line.
within, opens, caseInsensitive blocks (section 4.7); caseInsensitive also for lines.
files Name[] Folder subjects: the file rules whose files this rule reads (section 10).
when Expression The entry is matched only when this holds.
id IdBinding Where the element's id is stored (section 5.3).
parent {rules, slot} Containment (section 5.5).
attributes map attribute → AttributeBinding Section 5.2.
insert Insert How a new element is written (section 6.2). Absent: adding is refused.
remove Remove How an element is removed (section 6.2). Absent: removing is refused.
undo "inverse" (default), "snapshot" Section 7.1.
readOnly bool or LocalizedText Section 3.4.

When several rules match the same entry, the first in the binding's order (elements before relations) whose when holds takes it; an entry becomes at most one element or relation. Rules of the same node type are how a type is written in several shapes (the timeline's Period and Moment are one kind of entry, told apart by has(entry.end)).

5.2 Slots and attribute bindings

A slot is exactly one place a value is read from and written to:

Slot Families The value
{key} yaml, json The value of that member of the entry's mapping; with child, of the mapping the relative selector child reaches.
{attribute} xml The value of that attribute of the element, or of the element child selects.
{text: true} xml The text of the element, or of the element child selects.
{group} lines, blocks The named group; with word (and flag), a word of it (section 4.6).
{parent: a} all The value of attribute a's slot in the enclosing entry the rule's parent names, or the nearest enclosing matched entry. Read-only unless that entry's slot is writable.
{capture: c} yaml, json, xml The key a {c} selector segment matched; writing it renames the key.
{value: cel} all Computed; read-only.

An attribute binding is a slot bound to one DISL attribute of the rule's type, with these options:

Property Values Meaning
empty "remove", "keep", "refuse" Setting the attribute to its empty value (empty string, empty list, null) removes the slot (remove-key), writes the empty value (keep, the default for strings), or is refused.
absent map family → "insert", "refuse" Setting a value when the slot is absent from the entry: insert it (insert-key, the default) or refuse, per family.
default value The value read when the slot is absent.
number "shortest", {decimals} How a number is written (section 6.3).
time "keep-precision" A written date or date-time keeps the precision of the value it replaces (section 6.3).
style plain, double, single, literal, flow yaml: the style of a newly written value, when the value it replaces gives none (section 6.3).
reference {to, by} The value names an entry of the rules to by the value of their attribute by (section 5.7).
map map wire value → model value Translates values both ways. Several wire values may map to one model value; writing uses the first key in document order of the map whose value matches, unless the value being replaced already maps to it, in which case it is kept.
override Slot A second slot that wins on read when present and is removed (remove-key) when the attribute is written.
content "text", "html-paragraphs" xml text slots (section 4.5).
create {emit, place} xml: how a missing child element holding the value is written: emit with {value} as the placeholder, placed first, last or {before: name} among the element's children. Emptying the value removes the child with the line span it has (remove-key).
readOnly bool or LocalizedText Section 3.4.

An attribute of the node type that no binding names is not stored in the body: it takes its DISL default, or is derived as DISL says. A DISL attribute marked fixed (DISL §4.3) MUST NOT be bound.

5.3 Ids

How ids are made, derived and whether they are ephemeral is DISL's (persistence.ids, DISL §11.5). FBL says only where an id is stored, with the rule's id:

  • {"from": Slot}: the id is the value of the slot. It is read from it, and written to it when the element is created (with a new id DISL's strategy generates, once, when the gesture happens, so a redo re-creates the element under the same id). A rename of the id is a replace-value plus the reference rewrites of section 5.7.
  • {"sidecar": {"key": cel}}: the id is kept in the registration's identities block (section 8.6), keyed by the value of key, for formats whose entries have no id of their own (a Wardley map's components are named, not identified).
  • No id: the id is the one DISL derives (strategy: "derived"), from the element's attributes and, through self.location(), its place in the file.

Ids that DISL marks ephemeral (an id that changes when the file is edited, such as one built from a line number) are never stored: not in a registration's layout (section 8.5), not in a sidecar. A missing or duplicate id is not a reason to refuse the body: DISL's ids.missing and its findings std.missingId and std.duplicateId decide, and an entry is always addressable by its place.

5.4 Relation rules

A relation rule is an element rule with two more slots, source and target, each REQUIRED. Their values reference the ends by id: an end whose value names no element is a dangling reference, reported as fbl.dangling-reference on the entry, and the relation is not created (DISL relations always have both ends). A relation whose ends are its enclosing entries takes them with {parent: …} (a Databricks dependency's target is the task whose depends_on it is in).

5.5 Containment

parent: {rules, slot} makes the element a child, in DISL containment slot slot, of the nearest enclosing entry matched by one of rules: the nearest ancestor node in the tree families and xml, the nearest enclosing block in blocks. A rule without parent produces top-level elements. Moving an element to another parent is a remove-entry in the old parent and an insert-entry in the new one, in one edit, keeping the entry's own bytes.

5.6 The header

header names a version or identity mark: a root key with a value (yaml, json), or a line expression the first statement or the root start tag must match (lines, blocks, xml). A body without the mark, or with another value, is read, and the difference is reported as fbl.header-mismatch (a warning). With required: true, a body without the mark is unreadable (section 7.5) instead. The header is never written except by the template.

5.7 References and rename

A slot is a reference when its attribute binding has reference, or when it is a relation's source or target (which reference ids). When the referenced value changes (the by attribute of an entry of the to rules, or an id stored with from), every reference to the old value is rewritten in the same edit, one rewrite-reference splice each. A reference inside a word or a list value is rewritten in place, word by word or item by item. A rename to a value another entry of the same rules already has is refused.

A group holding several references (a causal loop's members) is split into words, each a reference.

Referenced from