processor
connector grammar version 33.11
model | 105 | application |
---|---|---|
interface | 20 | interface |
auto-webclient | xindi.1 | generator_settings |
generator_annotations | ||
phrases | ||
query | ||
translations | ||
parameters | ||
connector | 36.5 | processor |
variables | ||
datastore | 113 | consumed_interfaces_mapping |
provided_interface_implementation | ||
migration_mapping | ||
regular_expression_engine | ||
relational-database-bridge | 109 | database |
database_transformation | ||
sql-mirror | 109.1 | sql_mapping |
webclient | xindi.1.1 | views |
widget | ||
client bindings | ||
gui_model | ||
phrases | ||
translations | ||
settings | ||
parameters | ||
project-build-environment | 41 | wiring |
deployment |
The Standard Libraries
The connector provides a set of standard libraries. These libraries provide functionality outside the language constructs of the connector.
Calendar
The calendar library provides conversions between the connectors internal date time representation and broken down time.
define 'type' as @API choice ( 'date' 'date-time' 'time' )
define 'weekday' as @API choice (
'Monday': 1
'Tuesday': 2
'Wednesday': 3
'Thursday': 4
'Friday': 5
'Saturday': 6
'Sunday': 7
)
define 'calendar' as @API {
'year': integer
'day of year': integer
'month': integer
'day of month': integer
'week': integer
'day of week': 'weekday'
'hour': integer
'minute': integer
'second': integer
}
library
/* Converts Alan Time to broken down time.
* When information is not present in the source, it is set to zero.
*/
function 'convert'
< integer , 'calendar' >
(
$'source type': 'type'
$'timezone': optional text
)
binds: "c5e5166832c5eb9eec7481afa9c4a3c9689a1b6a"
Network
The network library provides functions to perform network request and the data structures to represent related objects.
define 'network method' as @API choice ( 'get' 'head' 'post' 'put' 'delete' )
define 'network security' as @API choice ( 'strict' 'preferred' 'none' )
define 'key value list' as @API collection unicode case folding text
define 'network request' as @API {
'method': 'network method'
'parameters': optional 'key value list'
'headers': optional 'key value list'
'content': optional text
}
define 'network response' as @API {
'status': integer
'headers': 'key value list'
'content': text
}
define 'network authentication' as @API {
'username': text
'password': text
}
define 'network message part' as @API {
'mime type': text
'mime sub type': text
'content': text
}
define 'network message' as @API {
'from': 'key value list'
'recipients': 'key value list'
'recipients extra': optional 'key value list'
'recipients hidden': optional 'key value list'
'subject': optional text
'headers': optional 'key value list'
'attachments': optional list {
'name': text
'part': 'network message part'
}
'body': 'network message part'
}
library
/* Parses and decorates text as 'network message'.
* Optionally a 'preferred mime subtype' of MIME type `text` can be provided.
* When a MIME part with this type is found, it becomes a candidate for the body.
* The MIME type `text/plain` is always considered as a candidate for the body.
*/
function 'parse network message'
< text , unsafe 'network message' >
(
$'preferred mime subtype': text
)
binds: "a9727dd7a7a001ff7ced169b93e6b209d03ffe96"
/* Performs an HTTP(S) request.
* Methods are mapped directly to their HTTP equivalent.
* When provided, 'content' is send with the request.
*/
function 'http'
< boolean , unsafe 'network response' >
(
$'server': text
$'path': text
$'authentication': optional 'network authentication'
$'request': 'network request'
)
binds: "26b4d11d4f2a785d5c80be5aec16b94ef739cb8f"
/* Performs an FTP(S) request.
* Method mapping:
* > 'get': Retrieves the content of 'path'. For this to work on directories, 'path' must end with a slash (/).
* > 'head': Retrieves the directory entries at 'path' without meta data. 'path' must be a directory, but does not need to end with a slash (/).
* > 'post': Stores 'content' in 'path'.
* > 'put': Identical to 'post'.
* > 'delete': Attempts to remove 'path'. Unlike other methods, this transmits 'path' directly to the server for interpretation.
* Only when storing data, should content be set.
*/
function 'ftp'
< boolean , unsafe optional text >
(
$'server': text
$'path': text
$'method': 'network method'
$'authentication': optional 'network authentication'
$'security': 'network security'
$'content': optional text
)
binds: "7752d005d762c5aad87720bffe634d7ca012f833"
/* Retrieves all messages matching 'criteria' with IMAP(S).
* See RFC-3501 section 6.4.4 for valid values of 'criteria'.
* All messages are parsed as if passed to `function 'parse network message'`.
*/
function 'imap'
< boolean , unsafe plural 'network message' >
(
$'server': text
$'path': text
$'criteria': text
$'authentication': optional 'network authentication'
$'security': 'network security'
$'preferred mime subtype': text
)
binds: "fdf2d49d643d45d11f8312665743db62e4cbc3b0"
/* Send a message with SMTP(S).
* When the transfer fails, this function throws an error.
*/
function 'smtp'
< boolean , unsafe boolean >
(
$'server': text
$'authentication': optional 'network authentication'
$'security': 'network security'
$'message': 'network message'
)
binds: "eee8e142b5a92d5fd361a3865a0fd1d44250e99f"
/* Register webserver handlers.
* The handler is used as the path to respond to.
* Each handler is triggered by external web requests for the path.
*/
hook 'webserver'
on 'network response'
(
$'request': 'network request'
)
binds: "3646e76f83861027942b241a95a2e91c023873bd"
Plural
The plural library provides algorithms operating on sets.
library
/* Sorts a set based on a comparison function.
*/
function 'sort'
< template plural T , plural T , plural T >
(
$'compare': lambda on boolean (
$'A': T
$'B': T
)
)
binds: "354397a819e32138d5f64e235fa01fcdd218403a"
/* Selects a single entry in a set based on a comparison function.
* This returns the entry for which `compare` results in true when the entry is `A` and false when `B` compared to all other entries in the set.
* It fails when no such entry is found.
*/
function 'select'
< template plural T , plural T , unsafe T >
(
$'compare': lambda on boolean (
$'A': T
$'B': T
)
)
binds: "7bc2180c97c2a18d32e8b9334b7027cf09ca475d"
/* Filters a set.
* This returns a new set containing only the entries for which `filter` results in true.
*/
function 'filter'
< template plural T , plural T , plural T >
(
$'filter': lambda on boolean (
$'entry': T
)
)
binds: "c34096effde804589af15914169b375ff34b2b20"
/* Divide a set in several smaller sets (buckets).
* The order of the entries is maintained.
*/
function 'split'
< template plural T , plural T , plural plural T >
(
$'bucket size': integer
)
binds: "48c1f8a920a3944a99799cac8bf202fb26dcd023"
Unicode
The unicode library provides functions to manipulate text values. These functions require UTF-8 encoded data.
define 'trim style' as @API choice ( 'leading' 'trailing' 'both' 'none' )
define 'message data' as @API {
'types': collection union (
'calendar' integer
'number' integer
'text' text
)
}
library
/* Removes all whitespace from a text value.
*/
function 'strip'
< text , text >
( )
binds: "ecc93f85ae7d8b594c2babc62e934ee7858e3fc5"
/* Removes leading and/or trailing whitespace from a text value.
*/
function 'trim'
< text , text >
(
$'style': 'trim style'
)
binds: "90f6a92fc16f3c79c5095ce69994ed3f31012470"
/* Split a text into multiple fragments.
* Empty fragments are automatically removed.
*/
function 'split'
< text , plural text >
(
$'style': 'trim style'
)
binds: "f0ac58660348a12cbcbd257438034886f34dfd1a"
/* Match a text with a Regular Expression.
* The whole input must match the pattern.
*/
function 'regex'
< text , unsafe boolean >
(
$'pattern': text
)
binds: "e6dedeaf4c3863ab8f84ee3e00e82e81a9a6689d"
/* Format a message with dynamic data.
* Pattern syntax:
* message = messageText (argument messageText)*
* argument = noneArg | simpleArg | complexArg
* complexArg = choiceArg | pluralArg | selectArg | selectordinalArg
*
* noneArg = '{' argNameOrNumber '}'
* simpleArg = '{' argNameOrNumber ',' argType [',' argStyle] '}'
* choiceArg = '{' argNameOrNumber ',' "choice" ',' choiceStyle '}'
* pluralArg = '{' argNameOrNumber ',' "plural" ',' pluralStyle '}'
* selectArg = '{' argNameOrNumber ',' "select" ',' selectStyle '}'
* selectordinalArg = '{' argNameOrNumber ',' "selectordinal" ',' pluralStyle '}'
*
* choiceStyle: see ChoiceFormat
* pluralStyle: see PluralFormat
* selectStyle: see SelectFormat
*
* argNameOrNumber = argName | argNumber
* argName = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+
* argNumber = '0' | ('1'..'9' ('0'..'9')*)
*
* argType = "number" | "date" | "time" | "spellout" | "ordinal" | "duration"
* argStyle = "short" | "medium" | "long" | "full" | "integer" | "currency" | "percent" | argStyleText | "::" argSkeletonText
*
* For more information see: https://unicode-org.github.io/icu/userguide/format_parse/
*/
function 'format'
< text , unsafe text >
(
$'data': 'message data'
$'locale': text
)
binds: "aedc593ebef3a2e8229353ba43b63c4a1617f885"
/* Parse a message with dynamic pattern.
* Pattern syntax:
* see 'format' function
* parsing does not support named arguments
*/
function 'parse'
< text , unsafe 'message data' >
(
$'pattern': text
$'locale': text
)
binds: "9e9eecdf412d4f82c04e94e8227e3b6ce69eb361"
Processor
The internal library.
Allows defining reusable types.
'library': dictionary { [ define ]
'type': [ as ] stategroup (
'schema' {
/* Define a schema type.
* Optionally the `@API` option can be used to suppress usages analysis.
*/
'analysis': stategroup (
'API' { [ @API ] }
'full' { }
)
'type': component 'schema complex type'
}
'pattern' { [ pattern ]
/* Define a pattern.
*/
'analysis': stategroup (
'API' { [ @API ] }
'full' { }
)
'rule': component 'pattern rule'
}
'locale' { [ locale ]
/* Define a custom locale.
*/
'positive sign': [ positive-sign: ] text
'negative sign': [ negative-sign: ] text
'decimal point': [ decimal-point: ] text
}
)
}
The default locale.
Allows overwriting the builtin default locale.
'custom locale': stategroup (
'yes' {
/* Use a custom locale as the default.
* All locale dependent operations without an explicit locale use this locale.
*/
'default': [ default-locale: ] component 'library selector'
}
'no' { }
)
The type of the connector.
This is chosen externally, the archetype here must follow the external choice.
'archetype': stategroup (
'scheduled provider' { [ provider ]
/* The connector is an Alan Interface Provider.
* As a provider, the connector runs the main routine based on an external schedule.
* Optionally, command handlers can be provided.
*/
/* Main routine.
* Each run of the main routine must create a snapshot of the current state.
* As a result the current state must always be a complete dataset and not an update to a previous dataset.
*/
'main routine': component 'statement'
/* Dataset initialization.
* Before a provider can process command and generate events, a dataset is required.
* When available, the last result of the main routine is used as dataset.
* Otherwise the initialization is used to determine the initial dataset.
*/
'initialization': stategroup (
'custom' { [ init ]
/* Use a custom routine to generate the initial dataset.
* This can be used to prevent additional runs of the main routine when this would exceed rate limits/query quotas.
*/
'routine': component 'statement'
}
'main' { [ init main ]
/* Use the main routine to generate the initial dataset.
* This run of the main routine is in addition to runs caused by the external schedule.
*/
}
'none' {
/* Do not generate an initial dataset.
* Received commands are ignored until the main routine is triggered.
*/
}
)
/* Command routines.
* These routines are run when the corresponding command is received.
* Commands can have multiple handlers. When the command is received, all handlers are executed in an undefined order.
* Received commands for which no handlers exist are ignored.
*/
'routines': dictionary { [ routine ]
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
/* The command the routine binds to.
* The command data is available in `$`.
*/
'command': [ on command ] reference
/* The path to the node the command is defined at.
* The path becomes the initial scope of the routine.
*/
'named type': component 'interface named path'
'type': stategroup (
'execute' {
/* Execute a statement.
* From this context Alan Interface events can be executed.
*/
'statement': [ do ] component 'statement'
}
'schedule' { [ schedule ]
/* Schedule a run of the main routine immediately after the command.
* The main routine is run, and consumers are updated, regardless of the external schedule.
* This run of the main routine is in addition to runs caused by the external schedule.
* These additional runs are scheduled regardless of the current dataset state.
*/
}
)
}
/* The hooks.
* See `Library Hooks` for more information.
*/
'hooks': set {
'hook': component 'library hook'
}
}
'consumer' { [ consumer ]
/* The connector is an Alan Interface Consumer.
* As a consumer, the connector runs routines based on the nodes touched by updates or received events.
*/
/* The context keys on which to subscribe.
* CAVEAT: The context keys are only evaluated once at startup.
*/
'context keys': [ (, ) ] dictionary {
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'value': [ = ] component 'statement'
}
/* The routines.
* Each routine is bound to something in the interface.
* The routine is triggered based on the interface binding.
*/
'routines': dictionary { [ routine ]
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'binding': [ on ] stategroup (
'event' {
/* The routine binds to an event.
* When the event is received, the routine is run.
* The event data is available in `$`.
*/
'event': [ event ] reference
}
'node' {
/* The routine binds to a node.
* When the node is touched by an update, the routine is run.
* The routine is only run after the update is fully processed.
* As a result, the routine always has a fully initialized dataset available, but is not informed which data was updated.
*/
}
)
/* The path to the node the routine binds to or the event is defined at.
* The path becomes the initial scope of the routine.
*/
'named type': component 'interface named path'
/* From this context Alan Interface events can be executed.
*/
'statement': [ do ] component 'statement'
}
/* The hooks.
* See `Library Hooks` for more information.
*/
'hooks': set {
'hook': component 'library hook'
}
}
'library' { [ library ]
/* The connector is a library.
* Currently only standard libraries are supported.
*
* A standard library consist of a set of function signatures.
* These functions are implemented by the connector runtime.
*/
'functions': dictionary { [ function ]
'signature': component 'signature'
'binds': [ binds: ] text
}
/* A standard library can expose hooks.
* Each hook is triggered by external events.
*/
'hooks': dictionary { [ hook ]
'signature': component 'signature'
'binds': [ binds: ] text
}
}
)
'library selector' {
/* Select a type from a standard or the internal library.
*/
'library': stategroup (
'dependency' {
'dependency': [, / ] reference
}
'self' { }
)
'type': reference
}
Library Hook
The standard library defines hooks. When or how these hooks are triggered is defined by the individual hooks. Providers and consumers can add lambdas to these hooks. These lambdas are executed when the hook is triggered. Some hooks only trigger a subset of the added lambdas, based on the handler name.
Lambdas added to hooks can execute side-effects based on there context. Providers can execute events. Consumers can execute commands.
'library hook' {
/* The standard library and hook to add the lambda to.
*/
'library': [ add-hook ] reference
'hook': [ :: ] reference
/* The handler name.
* Usages of this value is defined by the individual hooks.
*/
'name': text
/* The implementation to run when the hook is triggered.
* The root node of the interface data is available in `$`.
* The arguments for the library hook defined parameters become the initial scope.
*/
'instance': component 'callable instance'
'implementation': [ do ] component 'lambda implementation'
}
'locale selector' {
'locale': stategroup (
'custom' {
'selector': [ locale: ] component 'library selector'
}
'default' { }
)
}
'interface type path' {
'has step': stategroup (
'yes' {
'property': [ . ] reference
'type': stategroup (
'collection' { [ [] ] }
'choice' {
'state': [ ? ] reference
}
'node' { }
)
'tail': component 'interface type path'
}
'no' { }
)
}
Interface Named Path
As the connector does not allow stepping from a child node to the parent, the path is segmented. Each segment has a name and specifies a part of the path. This name is then assigned that node. This allows a routine to access its parents by explicitly naming them.
'interface named path' {
'segments': dictionary { [ $ ]
'has previous': stategroup = node-switch predecessor (
| node = 'yes' { 'previous' = predecessor }
| none = 'no'
)
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'steps': [ = ] component 'interface type path'
}
'has segment': stategroup = node-switch .'segments' (
| nodes = 'yes' {
'first' = first
'last' = last
}
| none = 'no'
)
}
'comparator' {
'type': stategroup (
'unicode' { [ unicode ]
/* The unicode comparator operates only on texts.
* It supports equality and relational comparisons.
*/
'case folding': stategroup (
'yes' { [ case folding ] }
'no' { }
)
}
'simple' {
/* The simple comparator behaves depending on the it operates on.
* > boolean: supports only equality comparisons
* > integer: supports equality and relational comparisons
* > text : supports only equality comparisons, 2 texts are considered equal when they are bitwise identical
* > file : supports only equality comparisons, 2 files are considered equal when both text values are identical
*/
}
)
}
'schema scalar type' {
'type': stategroup (
'boolean' { [ boolean ]
/* A boolean type can hold the value `true` or `false`.
*/
}
'integer' { [ integer ]
/* An integer can hold numbers.
* Integers can not have a fraction.
*/
'limits': stategroup (
'yes' { [ @limit: {, } ]
/* Limit the value range.
* All limits are inclusive.
*/
'lower': stategroup (
'yes' {
/* Limit the minimum value.
* The value is evaluated before the decimal import rule.
*/
'value': integer
}
'no' { }
)
'upper': [ , ] stategroup (
'yes' {
/* Limit the maximum value.
* The value is evaluated before the decimal import rule.
*/
'value': integer
}
'no' { }
)
}
'no' { }
)
'rule': component 'decimal import rule'
}
'text' { [ text ]
/* A text can hold any type of binary data.
* While a text can hold arbitrary binary data, all text operations require the data to be valid utf-8 encoded data.
* When data is encoded differently, it must first be converted.
* NOTE: The connector does not track the encoding of data.
* Passing data not encoded as utf-8 to a text operation causes the operation to fail, even if the operation is normally guaranteed.
* An encoding failure results in an implicit throw, bypassing any alternative handling present!
*/
'limits': stategroup (
'yes' { [ @limit: {, } ]
/* Limit the text length.
* All limits are inclusive.
*/
'lower': stategroup (
'yes' {
/* Limit the minimum length of the value.
* The length is measured in code-points.
*/
'length': integer
}
'no' { }
)
'upper': [ , ] stategroup (
'yes' {
/* Limit the maximum length of the value.
* The length is measured in code-points.
*/
'length': integer
}
'no' { }
)
}
'no' { }
)
}
'choice' { [ choice ]
/* A choice can hold a single value from a user defined set of values.
* A choice has a base type, this type only effects parsing and serialization.
* So a choice with an integer base type cannot be compared to an actual integer.
*/
'options': [ (, ) ] dictionary {
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'base type': stategroup (
'integer' {
'value': [ : ] integer
}
'text' { }
)
}
}
)
}
'schema complex type' {
'type': stategroup (
'any' { [ any ]
/* Differed type.
* The type of this data can not yet be determined.
* This allows other data in the same schema to define the type of this by decorating this value at a later point.
*/
}
'library' {
/* Library type.
* This is not an actual type.
* The type of this data is the referred type.
*/
'selector': component 'library selector'
}
'union' { [ union ]
/* Union type.
* The type of this data is conditional.
* Parsing is not able to determine the type, it must be explicitly give a type through a separate decorate step.
* Serialization does not include the type information and omits the value when the type was not yet determined.
*/
'types': [ (, ) ] dictionary {
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'type': component 'schema complex type'
}
}
'node' {
/* Node type.
* Parsing and serialization expects the type information (property names) to be present in the data.
*/
'node': component 'schema node type'
}
'headless' { [ headless ]
/* Node type.
* Unlike the normal node type, this expects the data to be ordered and uses the property order during parsing and serialization.
*/
'node': component 'schema node type'
}
'collection' { [ collection ]
/* Set type.
* This holds a dynamic amount of data, or no data at all (empty set).
* Every entry in the set is an `entity` with an implicit key and a value of `type`.
* A comparator is used for matching the implicit keys.
*/
'comparator': component 'comparator'
'type': component 'schema complex type'
}
'list' { [ list ]
/* Set type.
* This holds a dynamic amount of data, or no data at all (empty set).
* Every entry in the set is of `type`.
* Entries in a list have no unique identification.
*/
'type': component 'schema complex type'
}
'table' { [ table ]
/* Special set type.
* A table behaves as if it was a list of headless nodes, except for a special parsing/serialization specialization.
* The table expects the very first row in serialized from to be the header.
* This header must match all properties in the node, but can do so in any order.
* Unlike the headless node, where the property order is defined by the schema, the parsing/serialization order of all properties is defined by this header row.
*/
'node': component 'schema node type'
}
'scalar' {
'type': component 'schema scalar type'
}
)
}
'schema node type' {
/* A node is a container containing a static set of named values, the properties.
* When the document type supports it, each property itself can have a set of attributes.
* These attributes can either be a text (to be retrieved and processed later) or a filter (to reduce the possible matches in serialized form).
* A property can be marked as optional, an optional property may be omitted or have a special no value construct in serialized form.
*
* A property can be marked as protected, an protected property's data is always excluded from data dumps.
* This is to prevent the connector leaking sensitive data.
* NOTE: Using @protected is no guarantee the relevant data is never included.
* When the sensitive that is also present in unprotected data, it will still be readable in that data.
*/
'properties': [ {, } ] dictionary {
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'has attributes': stategroup (
'yes' { [ <, > ]
'attributes': dictionary {
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'type': [ : ] component 'schema scalar type'
}
'filters': dictionary { [ where ]
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'value': [ is ] component 'value promise'
}
}
'no' { }
)
'protect': [ : ] stategroup (
'yes' { [ @protected ] }
'no' { }
)
'is optional': stategroup (
'yes' { [ optional ] }
'no' { }
)
'type': component 'schema complex type'
}
}
'stack block' {
/* A stack block is a group of `let` expressions.
* With `let` expressions it is possible to store a value in a name.
* This value can be retrieved at a later point.
*
* A `let` expression is not a variable.
* Unlike a variable, a `let` expression is always given a value when it is declared.
* It also can never be assigned a different value at a later point.
*/
'values': dictionary { [ let $ ]
'has previous': stategroup = node-switch predecessor (
| node = 'yes' { 'previous' = predecessor }
| none = 'no'
)
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'type': stategroup (
'inferred' {
/* Source driven `let`.
* The result of the promise is assigned to the name.
* The type of this `let` is inferred from the promise.
*/
'value': [ = ] component 'value promise'
}
'schema' {
/* Target driven `let`.
* The type of this `let` is the type of the schema. It must be a node type.
* This executes the statement with the schema as target.
*/
'schema': [ as ] component 'schema complex type'
'statement': [ = ] component 'statement'
}
'lambda' {
/* Lambda `let`.
* The type of this `let` is a callable object.
*/
'lambda': [ = ] component 'lambda definition'
}
)
}
}
'stack selector' {
/* Navigate the stack.
* Each frame on the stack is created by a scope.
*/
'stack': stategroup (
'non-empty' {
'select': stategroup (
'this frame' {
/* Select a value in the selected frame.
*/
'value': [ $ ] reference
}
'parent frame' {
/* Select a value in the parent frame.
*/
'tail': [ ^ ] component 'stack selector'
}
)
}
)
}
'target type path step' {
'has step': stategroup (
'yes' {
'type': stategroup (
'collection' { [ * ] }
'choice' {
'state': [ ? ] reference
}
'node' {
'property': [ . ] reference
}
'command' {
'command': [ command ] reference
}
'event' {
'event': [ event ] reference
}
)
'tail': component 'target type path step'
}
'no' { }
)
}
'target type path' {
'type': stategroup (
'none' { [ none ] }
'absolute' {
'root': stategroup (
'interface' { [ interface ] }
'schema' {
'selector': component 'library selector'
}
'boolean' { [ boolean ] }
'integer' { [ integer ] }
'text' { [ text ] }
)
'steps': component 'target type path step'
}
'relative' {
'steps': component 'target type path step'
}
)
}
'type path step' {
'has step': stategroup (
'yes' {
'type': stategroup (
'collection' { [ * ] }
'indexed collection' { [ [] ] }
'choice' {
'state': [ ? ] reference
}
'node' {
'property': [ . ] reference
}
'command' {
'command': [ command ] reference
}
'event' {
'event': [ event ] reference
}
)
'tail': component 'type path step'
}
'no' { }
)
}
'type path' {
'root': stategroup (
'interface' { [ interface ] }
'schema' {
'schema': component 'schema complex type'
}
'context' { [ context ] }
)
'steps': component 'type path step'
}
'type definition' {
'type': stategroup (
'file' { [ file ] }
'optional' { [ optional ]
'sub type': component 'type definition'
}
'plural' { [ plural ]
'sub type': component 'type definition'
}
'lambda' {
'signature': [ lambda ] component 'signature'
}
'type' {
'path': component 'type path'
}
'template' { [ T ] }
)
}
'signature parameters' {
/* Parameter definition of a callable object.
*/
'parameters': [ (, ) ] dictionary { [ $ ]
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'type': [ : ] component 'type definition'
}
}
'signature promise' {
/* The signature of a callable object.
* This represents a callable object that must be called in a promise chain context.
*/
'template': [ < ] stategroup (
'yes' {
'requirement': [ template, T , ] stategroup (
'plural' { [ plural ] }
'optional' { [ optional ] }
'none' { }
)
}
'no' { }
)
/* The type of the input.
* When written inline, the input type can be inferred (implicit).
*/
'context': stategroup (
'explicit' {
'context': component 'type definition'
}
'implicit' { }
)
/* The promise guarantee of the callable object.
*/
'guarantee': [ , ] stategroup (
'yes' { }
'no' { [ unsafe ] }
)
/* The type of the output.
*/
'result': [, > ] component 'type definition'
/* The parameter definition.
*/
'parameters': component 'signature parameters'
}
'signature statement' {
/* The signature of a callable object.
* This represents a callable object that must be called as a statement.
*/
/* The target the callable object must be called on.
* When written inline, the target can be inferred.
*/
'target': stategroup (
'explicit' {
'path': [ on ] component 'target type path'
}
'inferred' { }
)
/* The parameter definition.
*/
'parameters': component 'signature parameters'
}
'signature' {
/* The signature of a callable object.
*/
'type': stategroup (
'promise' {
'signature': component 'signature promise'
}
'statement' {
'signature': component 'signature statement'
}
)
}
'callable instance' {
'template': stategroup (
'yes' { [ <, > ]
'type': component 'type definition'
}
'no' { }
)
}
'lambda implementation' {
/* The implementation of a lambda.
* Currently lambdas can only be statements.
*/
'type': stategroup (
'statement' {
'statement': component 'statement'
}
)
}
'lambda argument' {
'type': stategroup (
'optional' {
/* Set an optional parameter.
* Lambda arguments must always explicitly set or unset.
*/
'action': stategroup (
'set' { [ set ]
'argument': component 'lambda argument'
}
'unset' { [ unset ] }
)
}
'signature' { [ lambda => ]
/* Set the implementation of a lambda.
* The signature of the lambda is taken from the parameter.
*/
'lambda': component 'lambda implementation'
}
'choice' {
/* Set a choice.
* The available values of the choice are taken from the parameter.
*/
'option': [ option ] reference
}
'node' {
/* Set a node.
* This allows the creation of new data inline instead of retrieving stored data.
*/
'statement': [ new ] component 'statement'
}
'value' {
/* Set the value.
*/
'value': component 'value promise'
}
)
}
'lambda arguments' {
/* The arguments for a callable object.
* This creates a new scoped bound to the signature parameters.
* The created scope will be the initial scope of the callable object when called.
*/
'values': [ (, ) ] dictionary { [ $ ]
'argument': [ = ] component 'lambda argument'
}
}
'lambda definition' {
/* Defines a new lambda.
*/
'signature': [ lambda ] component 'signature'
'instance': component 'callable instance'
'lambda': [ => ] component 'lambda implementation'
}
'callable selector' {
'type': stategroup (
'function' {
/* Select a function from the standard library.
*/
'library': reference
'function': [ :: ] reference
'instance': component 'callable instance'
}
'recurs' { [ self ]
/* Select self.
* This represents the currently executing lambda.
* Only available in a lambda implementation.
*/
}
'new' {
/* Create and select a new lambda.
* This lambda is anonymous.
*/
'lambda': component 'lambda definition'
}
'stored' {
/* Retrieve a stored lambda.
*/
'value': component 'value promise'
}
)
}
'decimal import rule' {
/* Enable/Disable decimal point translation.
* Decimal point translation is only every applied during parsing and serialization.
* The translation is provided from the parsing point of view, during serialization the inverse is applied.
*/
'decimal point translation': stategroup (
'yes' {
'places': [ << (, ) ] component 'value promise'
}
'no' { }
)
}
'date expression' {
'year': component 'promise chain'
'style': stategroup (
'calendar' {
'month': [ - ] component 'promise chain'
'day': [ - ] component 'promise chain'
}
'week' { [ W ]
'week': component 'promise chain'
'day of week': stategroup (
'monday' { [ Monday ] }
'tuesday' { [ Tuesday ] }
'wednesday' { [ Wednesday ] }
'thursday' { [ Thursday ] }
'friday' { [ Friday ] }
'saturday' { [ Saturday ] }
'sunday' { [ Sunday ] }
)
}
'ordinal' {
'day': [ , ] component 'promise chain'
}
)
}
'time expression' {
'hour': component 'promise chain'
'minute': [ : ] component 'promise chain'
'second': [ : ] component 'promise chain'
}
'pattern rule piece' {
'type': stategroup (
'pattern' {
'capture': stategroup (
'yes' { [ $ ] }
'no' { }
)
'type': stategroup (
'decimal' { [ decimal ]
'locale': component 'locale selector'
'rule': component 'decimal import rule'
}
'text' { [ text ] }
)
'repeat': stategroup (
'yes' { [ {, } ]
'lower': stategroup (
'yes' {
'min': integer
}
'no' { }
)
'upper': [ , ] stategroup (
'yes' {
'max': integer
}
'no' { }
)
}
'no' { }
)
}
'static' {
'text': text
}
)
'has tail': stategroup (
'yes' {
'tail': component 'pattern rule piece'
}
'no' { }
)
}
'pattern rule' { [ (, ) ]
/* Describes a custom pattern.
* A pattern is an ordered set of named parts, with each part an ordered set of pieces.
* Each part must capture a single piece. Only dynamic patterns can be captured.
* A pattern must contains at least a single part.
*/
'parts': dictionary {
'has next': [ : ] stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'pieces': component 'pattern rule piece'
}
'head': reference = first
}
'format rule' {
'type': stategroup (
'dynamic' {
'padding': stategroup (
'yes' { [ (, ) ]
/* Select the justification and padding character.
* Zero padding can only be applied to decimals.
* NOTE: This selects the justification/alignment of the text value, justify right causes padding to be added to the LEFT!
*/
'justify': stategroup (
'right zero' { [ @zero ] }
'right space' { }
'left space' { [ @left ] }
)
/* Select the minimum amount of characters written.
* Longer values are not truncated.
*/
'places': component 'value promise'
}
'no' { }
)
'value': component 'promise'
'type': stategroup (
'decimal' { [ decimal ]
'locale': component 'locale selector'
'rule': component 'decimal import rule'
}
'text' { [ text ] }
)
}
'static' {
'text': text
}
)
'has tail': stategroup (
'yes' { [ , ]
'tail': component 'format rule'
}
'no' { }
)
}
'promise path' {
'step': stategroup (
'optional value' { [ get ]
/* Retrieve the value of an optional value.
* This fails when the optional value is not set.
*/
}
'entity lookup' {
/* Retrieve the value of an entry in an indexed set.
* A set is considered indexed when the entry type is `entity`.
* This fails when the provided key does not exist in the set.
* On success it results in the entity value, not the entity itself.
*/
'key': [ [, ] ] component 'promise'
}
'file fetch' {
/* Retrieve a property from a file.
*/
'field': stategroup (
'token' { [ .token ] }
'extension' { [ .extension ] }
)
}
'entity fetch' {
/* Retrieve a property from an entity.
*/
'field': stategroup (
'key' { [ .key ] }
'value' { [ .value ] }
)
}
'node fetch' {
/* Retrieve a property of a node.
* Optionally an attribute from the property can be retrieved.
*/
'property': [ . ] reference
'sub property': stategroup (
'yes' {
'attribute': [ <, > ] reference
}
'no' { }
)
}
'interface choice' {
/* Retrieve the value of a specific state.
* This fails when the choice is set to another state.
*/
'state': [ ? ] reference
}
'plural size' { [ .size ]
/* Retrieve the size of a set.
*/
}
)
}
'promise chain' {
'has step': stategroup (
'yes' {
'step': stategroup (
'path' {
'step': component 'promise path'
}
'complex' { [ => ]
'type': stategroup (
'parse' {
'as': [ parse as ] stategroup (
'JSON' { [ JSON ]
/* Parses a JSON object.
* On success it results in a document and must first be passed to a decorator before it is usable.
*/
}
'XML' { [ XML ]
/* Parses a XML document.
* On success it results in a document and must first be passed to a decorator before it is usable.
*/
}
'CSV' { [ CSV ]
/* Parses a CSV document.
* The CSV parser conforms to RFC 4180.
* On success it results in a document and must first be passed to a decorator before it is usable.
*/
}
'ISO Date Time' { [ ISODateTime ]
/* Parses an ISO-8601 date and time, however it must have at least Seconds accuracy. (source: https://en.wikipedia.org/wiki/ISO_8601)
* Combines an ISODate and ISOTime separated with T, but the Date and Time components must both be in the same format.
* Calendar dates + Time: YYYYMMDDThhmmss[.sss] or YYYY-MM-DDThh:mm:ss[.sss]
* Week dates + Time: YYYYWwwDThhmmss[.sss] or YYYY-Www-DThh:mm:ss[.sss]
* Ordinal dates + Time: YYYYDDDThhmmss[.sss] or YYYY-DDDThh:mm:ss[.sss]
* In all cases the Time component may contain a Time Zone, when omitted local time is assumed.
* On success it results in an Alan DateTime.
*/
}
'ISO Date' { [ ISODate ]
/* Parses an ISO-8601 date, however it must have Day accuracy. (source: https://en.wikipedia.org/wiki/ISO_8601)
* Calendar dates: YYYYMMDD or YYYY-MM-DD
* Week dates: YYYYWwwD or YYYY-Www-D
* Ordinal dates: YYYYDDD or YYYY-DDD
* On success it results in an Alan Date.
*/
}
'ISO Time' { [ ISOTime ]
/* Parses an ISO-8601 time, however it must have at least Seconds accuracy. (source: https://en.wikipedia.org/wiki/ISO_8601)
* Time: hhmmss[.sss] or hh:mm:ss[.sss]
* The fraction separated by a decimal point is allowed but discarded.
* Time Zone: Z or ±hh or ±hhmm or ±hh:mm
* The timezone is allowed but discarded.
* On success it results in the amount of seconds since midnight.
*/
}
'decimal' { [ decimal ]
/* Parses a decimal value.
* Decimal: [+-]d[.f]
* Any number of digits can be provided, but the supported range is limited by the implementation.
* An optional minus or plus sign is allowed before the first digit to set the sign.
* Optionally no digits can be provided before the decimal point, which implies the value of 0.
* The fraction separated by a decimal point is allowed and the amount of digits may differ from the `rule`,
* but only the digits specified by `rule` are kept with possibly additional 0 digits introduced when insufficient precision was provided.
*/
'locale': component 'locale selector'
'rule': component 'decimal import rule'
}
'lines' { [ lines ]
/* Split all lines of a text value.
* Optionally leading and/or trailing spaces can be removed from each line.
* Empty lines, after trimming, are removed.
* It always results in a list of texts, with each entry a single line.
*/
'trim spaces': stategroup (
'yes' { [ trim, spaces ]
'locations': stategroup (
'leading' { [ leading ] }
'trailing' { [ trailing ] }
'both' { }
)
}
'no' { }
)
}
'pattern' { [ pattern ]
/* Parses a custom pattern.
* On success it results in a node with all properties set to their respective captured value.
*/
'pattern': stategroup (
'library' {
'selector': component 'library selector'
}
'inline' {
'rule': component 'pattern rule'
}
)
}
)
}
'serialize' {
'as': [ serialize as ] stategroup (
'JSON' { [ JSON ] }
'XML' { [ XML ] }
'ISO Date Time' { [ ISODateTime ] }
'ISO Date' { [ ISODate ] }
'ISO Time' { [ ISOTime ] }
'decimal' { [ decimal ]
'locale': component 'locale selector'
'rule': component 'decimal import rule'
}
)
}
'decorate' {
'type': [ decorate as ] stategroup (
'source' {
/* Decorate parsed data according to a schema.
*/
'schema': component 'schema complex type'
}
'union' {
/* Decorate a union type.
* When the type is already know, this works as a type cast.
*/
'type': [ union ] reference
}
)
}
'make' { [ make ]
'type': stategroup (
'date' { [ date (, ) ]
/* Creates an Alan Date for individual components. */
'date': component 'date expression'
}
'date time' { [ date-time (, ) ]
/* Creates an Alan DateTime for individual components. */
'date': component 'date expression'
'time': [ T ] component 'time expression'
}
'time' { [ time (, ) ]
/* Creates a time in seconds for individual components. */
'time': component 'time expression'
}
)
}
'compare' {
/* Compares the current value with another value.
* Both values must be of the same type and support the comparison.
*/
'type': stategroup (
'equality' { [ is ] }
'relational' {
'operator': stategroup (
'smaller' { [ less-than ] }
'smaller equal' { [ less-than or is ] }
'greater' { [ greater-than ] }
'greater equal' { [ greater-than or is ] }
)
}
)
'comparator': component 'comparator'
'other': [ (, ) ] component 'promise'
}
'reduce' {
'merge': stategroup (
'value' {
/* Merge a set of values.
* The `for each` sub-chain is run for each value in the input set, the result must match the type required by the operator.
*/
'merge type': stategroup (
'shared' { [ shared ]
/* Reduces a set of values to a single shared value.
* Shared values are detected with an equality check.
* It succeeds only when all values pass an equality check and the set contains at least one value.
*/
'comparator': component 'comparator'
}
'unique' { [ unique ]
/* Removes all duplicate values from a set of values.
* Duplicates are detected with an equality check.
* The order of the set is maintained, duplicates are placed at the first occurrence.
*/
'comparator': component 'comparator'
}
'sum' { [ sum ]
/* Calculates the sum of a set of integers.
* It results in the sum of all values, or the additive identity when the set is empty.
*/
}
'product' { [ product ]
/* Calculates the product of a set of integers.
* It results in the produce of all values, or the multiplicative identity when the set is empty.
*/
}
'join' { [ join ]
/* Concatenates a set of texts.
* Optionally a separator can be added between each element.
* The entries are joined in the order of the set.
*/
'separator': stategroup (
'yes' {
'value': [ (, ) ] component 'promise'
}
'no' { }
)
}
'logical and' { [ and ]
/* Calculates the logical and of a set of booleans.
* It fails when the set is empty.
*/
}
'logical or' { [ or ]
/* Calculates the logical or of a set of booleans.
* It fails when the set is empty.
*/
}
)
'for each': [ (, ) ] component 'promise chain'
}
'select' { [ select ]
/* Select a single value in a set.
* The selected entry must match `true` when compared with all other entries.
* The set must contain at least one entry.
*/
'compare': [ (, ) ] component 'promise chain'
}
'filter' { [ filter ]
/* Filter a set of values.
* It results in a set only containing the entries which passed the sub-promise with `true`.
* The order of the set is maintained.
*/
'filter': [ (, ) ] component 'promise chain'
}
)
}
'partition' { [ partition ]
/* Partition a set.
* It groups all entries with the `key` together in a bucket.
* The result is a set of key-value pairs, where the key is the shared `key` and the value is a set of the entries with that `key`.
* The order of buckets is undefined.
* The order of entries within a bucket is maintained.
*/
'comparator': [ on ] component 'comparator'
'key': [ (, ) ] component 'promise chain'
}
)
}
'call' { [ => ]
/* Call a standard library function or lambda. */
'selection': [ call ] component 'callable selector'
'arguments': [ with ] component 'lambda arguments'
}
)
'tail': component 'promise chain'
}
'no' { }
)
}
Promise
A promise consists of a head
, an instruction producing a value without any input, and a chain
.
The chain of a promise is a set of ordered instructions which each take the output of the previous instruction as their input.
Each instruction in the chain, as well as the head, has a promise guarantee
. When this guarantee is yes
, the instruction will always succeed regardless of the input.
Likewise, when this guarantee is no
, the instruction may fail at runtime when the input does not meet the instruction’s requirements.
The individual instructions are separated with =>
. Instructions with sub-promises, wrap the sub-promise in parenthesis ( ... )
.
These sub-promises propagate their guarantee to the parent promise, allow the handling of possible runtime failures at the top-level promise. Although they can handle the failures on their own.
Just like sub-promises, instructions in a chain pass their guarantee to the next instruction.
At the end of a chain, when the guarantee is no
, an alternative must be provided.
An alternative is indicated with ||
. It provides an alternative promise to obtain a value when the previous promise fails at runtime.
When the end of the alternative promise chain the guarantee is again no
, an other alternative must be provided.
This pattern repeats itself until a promise is provided with a guarantee of yes
or the guarantee can be propagated to the parent.
Alternatively, when no such promise can be provided, the promise can be terminated with a throw
.
Throwing causes execution of the statements to be aborted until a try
statement is found.
All changes, if any, to the target since this try are undone an execution resumes at the corresponding catch
statement.
The promise guarantees safe execution. When an instruction fails at runtime, the remainder of the chain is aborted and the alternative is evaluated. As a result it is always safe the chain multiple instructions with a guarantee of no.
'promise' {
'head': stategroup (
/* fetch from key-value pair systems */
'variable' {
/* Retrieves a variable from the instance-data.
*/
'variable': [ var ] reference
}
'configuration' {
/* Retrieves a value from the system configuration.
*/
'key': [ conf ] text
'data type': stategroup (
'integer' { [ integer ] }
'text' { [ text ] }
)
}
/* static/hardcoded values */
'static boolean' {
/* Create a static boolean value.
*/
'value': stategroup (
'true' { [ true ] }
'false' { [ false ] }
)
}
'static integer' {
'type': stategroup (
'integer' {
/* Create a static integer value.
*/
'value': integer
}
'current time' { [ now ]
/* Retrieves the current time.
* The time indicates when the current routine was started and does not change during the execution of the routine.
* This is the time as known by the hosting server.
*/
}
)
}
'static text' {
'type': stategroup (
'text' {
/* Create a static text value.
*/
'value': text
}
'line break' { [ line-break ]
/* Create a static text value containing the line break value.
*/
}
'guid' { [ guid ]
/* Generate a new GUID.
* This GUID is a Version 4 UUID.
* The random data is obtained from a CSPRNG.
*/
'format': stategroup (
'canonical' {
/* The canonical textual representation of 8-4-4-4-12 groups of hexadecimal digits.
*/
}
'base 16' { [ base16 ]
/* Format as a big-endian base 16 encoded text.
*/
}
'base 64' { [ base64 ]
/* Format as a big-endian base 64 encoded text.
*/
}
)
}
)
}
/* fetch from execution state */
'stored' {
/* Retrieve the value of a `let` expression.
*/
'selection': component 'stack selector'
}
'context' { [ $ ]
/* Retrieve the current context (`$`).
*/
}
'captured error' { [ error ]
/* Retrieve the captured error.
* This is only valid inside a `catch` statement.
*/
}
/* compute new values */
'list constructor' { [ list ]
/* Construct a list object.
* This allows for the conversion of a static list to a dynamic list.
* The order of entries is identical to the promise order.
*/
'promises': [ (, ) ] component 'promises'
}
'list merge' {
/* This operations are identical to the equivalently named operations in promise-chains `reduce` instruction.
* Unlike the reduce operations, these operate on static set of values instead of a dynamic set.
*/
'operation': stategroup (
'sum' { [ sum ] }
'product' { [ product ] }
'concatenate' { [ concatenate ]
'separator': stategroup (
'yes' {
'value': component 'promise'
}
'no' { }
)
}
'logical and' { [ and ] }
'logical or' { [ or ] }
)
'promises': [ (, ) ] component 'promises'
}
'arithmetic' {
'operation': stategroup (
'inversion' { [ - ]
/* Invert the sign of an integer.
*/
'operand': [ (, ) ] component 'promise'
}
'division' { [ division (, ) ]
/* Divide one integer by another integer.
* When the resulting fraction, if any, is truncated.
* This fails when `denominator` is zero.
*/
'numerator': component 'promise'
'denominator': [ , ] component 'promise'
}
)
}
'logic' {
'operation': stategroup (
'negation' { [ not ]
/* Toggle the value of a boolean.
*/
'operand': [ (, ) ] component 'promise'
}
)
}
'file constructor' { [ file (, ) ]
/* Construct a file object.
*/
'token': [ token = ] component 'promise'
'extension': [ extension = ] component 'promise'
}
'format' { [ format ]
/* Generate a new text value from a format rule.
*/
'format': [ (, ) ] component 'format rule'
}
)
'chain': component 'promise chain'
'alternative': stategroup (
'yes' {
'type': [ || ] stategroup (
'value' {
/* Provide an alternative promise. */
'value': component 'promise'
}
'throw' {
/* Throw a new error. */
'message': [ throw ] text
}
)
}
'no' { }
)
}
'promises' {
'value': component 'promise'
'has tail': stategroup (
'yes' {
'tail': [ , ] component 'promises'
}
'no' { }
)
}
Value Promise
This represents a top-level promise. A value promise indicates that a promise guarantee of no cannot be propagated from the wrapped promise. The wrapped promise must always have an alternative with a guarantee of yes, or throw.
'value promise' {
'promise': component 'promise'
}
'target expression' {
'type': stategroup (
'unset' { [ unset ]
/* Explicitly set an optional value to unset.
* This allows the setting of an optional value to be dependent on some condition.
*/
}
'set' {
'type': stategroup (
'node' {
/* Target a node.
* All mandatory properties must be specified.
* Optional properties may be omitted, which is identical to setting them to `unset`.
* When a property has attributes, all attributes must also be specified.
*/
'properties': [ (, ) ] dictionary {
'attributes': stategroup (
'yes' {
'attributes': [ <, > ] dictionary {
'value': [ = ] component 'value promise'
}
}
'no' { }
)
'statement': [ = ] component 'statement'
}
}
'entry' { [ create ]
/* Create a new entry in a set.
* When the set has implicit keys, a key must be provided.
*/
'implicit key': stategroup (
'yes' {
'value': [ [, ] ] component 'value promise'
}
'no' { }
)
'target': component 'target expression'
}
'state' {
'type': stategroup (
'branch' { [ create ]
/* Set an Alan Interface `stategroup` or `union` to the specified state/type.
*/
'state': reference
'target': component 'target expression'
}
'leaf' {
/* Set a `schema scalar type` choice to the specified value.
*/
'option': [ option ] reference
}
)
}
'scalar' {
/* Assign a scalar the produced value.
*/
'value': component 'value promise'
}
)
}
)
}
'statement' {
'type': stategroup (
'block' { [ {, } ]
/* Start a new block.
* Optionally a block can introduce a new scope.
* A block allows multiple independent statements to be written in the same context.
* Multiple statements are only allowed when the current target supports multiple values.
*/
'scope': stategroup (
'yes' {
'stack block': component 'stack block'
}
'no' { }
)
'statement': component 'statements'
}
'guard' {
/* Guard a statement and provide an alternative path.
* This catches all throws found in the guarded statement.
* When a throw is caught, the changes to the target are reverted and the fallback statement is executed.
* When the guarded statement is successfully executed, the fallback statement is skipped.
*/
'guarded statement': [ try ] component 'statement'
'optional assignment': [ catch ] stategroup (
'yes' { [ as $ ] }
'no' { }
)
'fallback statement': [ => ] component 'statement'
}
'condition' {
'type': stategroup (
'boolean' {
/* Execute a conditional branch.
* The condition is evaluated and based on the result, either the `true` or `false` case is executed.
*/
'condition': [ match ] component 'value promise'
'cases': [ (, ) ] group {
'on true': [ | true => ] component 'statement'
'on false': [ | false => ] component 'statement'
}
}
'existence' {
/* Execute a conditional branch.
* The promise without a guarantee of yes is evaluated.
* Based on whether or not the promise succeeded or failed, the corresponding case is executed.
* When the promise succeeds, the result is available in `$`.
*/
'value': [ any ] component 'promise'
'cases': [ (, ) ] group {
'on value': [ | value as $ => ] component 'statement'
'on error': [ | error => ] component 'statement'
}
}
'switch' {
/* Execute a conditional branch.
* The branch is determined based on a user defined choice.
* When the choice is an Alan Interface `stategroup`, the state node is optionally available in `$`.
*/
'value': [ switch ] component 'value promise'
'cases': [ (, ) ] dictionary { [ | ]
'has next': stategroup = node-switch successor (
| node = 'yes' { 'next' = successor }
| none = 'no'
)
'optional assignment': stategroup (
'yes' { [ as $ ] }
'no' { }
)
'statement': [ => ] component 'statement'
}
}
)
}
'walk' {
/* Execute a statement once for each entry in a set.
* The entry for which the statement is executed, is available in `$`.
*/
'value': [ walk, as $ ] component 'value promise'
'statement': [ => ] component 'statement'
}
'call' {
/* Call a standard library function or lambda. */
'selection': [ call ] component 'callable selector'
'arguments': [ with ] component 'lambda arguments'
}
'log operation' { [ @log: ]
/* Write a message to the debug channel.
*/
'message': component 'value promise'
}
'no operation' { [ no-op ]
/* No action.
* This is to terminate an execution path without doing anything.
* It is only allowed when the target allows no value.
*
* This statement allows for the creation of empty sets.
* Unlike all other targets, which define no default/initial value, sets are default empty.
*/
}
'throw' {
/* Throw a error.
* This can either be a new error, or when inside a `catch` statement it can rethrow the caught error.
*/
'type': stategroup (
'captured error' { [ rethrow ] }
'new error' { [ throw ]
'message': text
}
)
}
'target' {
'target': component 'target expression'
}
'execute' {
/* Execute a Alan Interface command or event.
*/
'context': [ execute ] component 'value promise'
'type': stategroup (
'command' {
'command': [ command ] reference
}
'event' {
'event': [ event ] reference
}
)
'statement': [ with ] component 'statement'
}
)
}
'statements' {
'statement': component 'statement'
'has more statements': stategroup (
'yes' {
'statement': component 'statements'
}
'no' { }
)
}