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).
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
failon 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 onereturn.
| 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.
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).
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.
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
forover a map visits its keys in ascending order of CEL's ordering (strings by code point); - arithmetic is CEL's:
intanduintare 64-bit with CEL's overflow errors,doubleis IEEE 754 binary64, andstring(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.
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).
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.
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 ofpersistence'stypeMap(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.