Skip to content

12. The script

Contents of ATD 0.1

The script (grammar/script.lark) is the part of ATD in which a tool type's logic is written: the small algorithms the companions list and the hosts write in code (sibling naming, cycle enumeration, relationship elevation). It is not an embedded general-purpose language (FR-019): its expressions are CEL, and it adds only local variables, conditionals, loops with a stated limit, local lists and maps, functions with bounded recursion, and the actions of DISL and DESL as statements (FR-013).

12.1 Functions

fn siblingName(parent: Node, base: string) -> string recursion 8 {
  …
}

A function has a name, unique among the description's functions; typed parameters, whose types are CEL types or the description's element types; an optional result type; an optional recursion <n>; and a block of statements.

  • A function is callable from every expression position (FR-017): from CEL, by its name, in operations, hooks, constraints, derived attributes, labels and binding rules.
  • A function with a result type MUST return a value of it or fail on every path (ATD7003).
  • recursion <n> bounds how deep the function recurses, at most 64 (ATD7004); absent, the bound is 64.
  • DISL's CEL functions (functions, DISL §3.4) are functions whose block is one return.

12.2 Statements

Statement Meaning
let <name> [: <Type>] = <CEL> an immutable local
var <name> [: <Type>] = <CEL> a mutable local
<name> = <CEL> assigns a mutable local (the name and = are one token, section 3.4)
if <CEL> { … } [else if … | else { … }] a conditional
for <name> [, <name>] in <CEL> limit <n> { … } a loop over a list, a map (its keys, and with two names its keys and values) or a range
while <CEL> limit <n> { … } a loop while a condition holds
return <CEL>, and return last in a block returns from the function, or ends an operation's or hook's statements
break, continue leave or continue the nearest loop
fail <CEL> refuses the gesture with the message, a string
an action (section 12.3) a change to the model, in the gesture's transaction

A loop MUST state its limit; one without does not parse. A loop that reaches its limit stops the call with a finding of its own (ATD7101), not merely the loop: a limit is the most iterations the tool engineer expects, and reaching it is an error, distinct from a budget overrun.

12.3 Actions

The actions of DISL §9.4 and DESL §8.2 are statements, each allowed only where a model may change (section 12.7); their arguments are CEL:

Action DISL
set <path> = <CEL> (set self.text = …, or set text = … for self) set
unset <path> unset
create <Type> [{ <attr>: <CEL> … }] [in <CEL>] [after <CEL> | before <CEL>] [slot <name>] [at <CEL>] [size <CEL>] [place <name>] [as <name>] create
connect <Type> from <CEL> to <CEL> [{ … }] [as <name>] connect
delete <CEL> delete
move <CEL> [to <CEL>, <CEL>] [into <CEL>] [slot <name>] [after <CEL> | before <CEL>] move (DISL), and DESL §8.2's move
resize <CEL> to <CEL>, <CEL> resize
retype <CEL> to <Type> retype
reparent <CEL> to <CEL> [slot <name>] [after <CEL> | before <CEL>] reparent
reorder <CEL> by <CEL> | after <CEL> | before <CEL> reorder
view <CEL> { <key>: <CEL> … } view
open <CEL> open
openForm <form> for <CEL> openForm
editLabel <CEL> [{ label: <id> }] editLabel
select <CEL>, reveal <CEL> select, reveal
highlight <CEL> [{ durationMs: <n> }] highlight
notify <CEL> [severity <name>] notify
layout <name> [on <CEL>] [{ … }] layout
call <operation>(<args>) call
call plugin <name>(<args>) plugin
fail <CEL> abort
let, if, for let, if with then and else, forEach with as and do

DISL's when on an action is an if around it. A name an action binds with as is a local of the statements after it.

move is DISL's in a diagram and DESL's in a designer (DESL §8.2). into names the new parent, null for the document's own elements; slot its slot; after or before, at most one, a sibling there, the element going last without either. The element keeps its id, its attributes and everything it contains; a move into the element itself or into something it contains, or into a parent or slot that does not allow its type, rolls the transaction back; a move whose parent and slot are the element's own is a reorder. to <x>, <y> sets a node's position on the canvas and is admitted in diagrams only (ATD3002).

12.4 The transaction

A script changes a model only through its actions, inside the transaction of the gesture that called it (FR-016): each action adds its commands to the transaction exactly as DISL's operation actions do (DISL §14.4), every expression sees the effects of every action before it, and the transaction is one step of the history with an exact undo. A call that ends other than by returning (a fail, a CEL error, a budget or recursion overrun, a loop at its limit, or the debugger's stop) discards the whole transaction, also when it runs in a hook inside another operation's transaction, and refuses the gesture with a finding that names the function.

12.5 Determinism

A script is deterministic (FR-014):

  • its environment is CEL's, with DISL §12.4's library and §12.5's rules: no function reads a clock, randomness, a file, the network, the environment or the host; the checker refuses a call of any function that is neither in that library nor a function of the description (ATD7001);
  • a for over a map visits its keys in ascending order of CEL's ordering (strings by code point);
  • arithmetic is CEL's: int and uint are 64-bit with CEL's overflow errors, double is IEEE 754 binary64, and string(double) formats as CEL's specification says;
  • a local list or map grows only by its statements; a value is at most 1,000,000 elements or code points long, beyond which the call stops as a budget overrun.

12.6 The budget

Every call runs under a step budget (FR-015): one step per statement executed, per loop iteration, and per CEL call evaluated. The budget is per gesture, the transaction's: 100,000 steps unless the description's limits.steps sets a lower number. Recursion is at most 64 frames, or the lower recursion of a function. Exceeding the budget (ATD7102) or the recursion (ATD7103) stops the call and discards the transaction. Where there is no gesture to refuse (a label, a constraint, a derived attribute, a binding rule read when a document is opened), the position falls back as DISL §2.5's table says for an expression that fails: a label takes its default, a constraint reports that it could not be evaluated, a derived attribute or element is absent with a finding, and a binding rule's entry is unreadable (FBL §7.4).

12.7 Purity

A function whose block, or a function it calls, contains an action is impure. An expression position that only reads (a label, a constraint's when and rule, a derived attribute, a variant's when, a binding rule's when, a setting's ( CEL )) MUST NOT call an impure function, through any chain of calls (ATD7002); an operation, a hook and a fix may.

12.8 Host attributes

hostAttribute(<element>, <name>) is the value the binding read into the host attribute <name> of the element (persistence's typeMap, hostAttributes, DISL §11.2), as the binding read it, and null when it read none. DISL keeps host attributes out of CEL; a description reads them through this one function, by name, so that a rule can judge what the file wrote where the model holds nothing: a relation's end that names no element is unset in the model (DISL §4.9), while the binding can read the id it was written with a second time, into a host attribute (dependency-graph.atd's writtenEnd).

  • The function only reads, so it may be called from every expression position (section 12.7). A host attribute is document data, so the call is deterministic (section 12.5).
  • A <name> written as a string MUST be a host attribute of an entry of persistence's typeMap (ATD2001).
  • A first argument that is not an element of the document, or a second that is not a string, stops the call as a CEL error.

endId(<relation>, <end>) is the id a relation's end was read with, <end> being "source" or "target": the id as the binding read it when the end was read from the file (DISL §11.2), else the id of the element the end reaches, and null when it has neither. It reads what the file wrote for an end that the model keeps unset because it names no element (DISL §4.9), where the binding's relation rule reads its ends as references, without a host attribute for it (timeline.atd's writtenEnd).

  • The function only reads, so it may be called from every expression position (section 12.7), and it is deterministic (section 12.5).
  • A first argument that is not a relation of the document, or a second that is not "source" or "target", stops the call as a CEL error.

Referenced from