|
|
Dap 0.1.6 Language Quick Tour
Dap is developed to provide an easy and powerful means for complex and extensive DOM rebuilds with dinamically resolved dependencies.
Dap language is rather "functional", than "imperative". That means, dap rules do not specify imperatively for each click event: "update Node1, Node2 and another dozen of nodes; hide Node765432 and show P elements inside DIV elements, having class .NodesToShow, slowly". You needn't provide all the wirings to make the nodes interacting with each other. Instead, dap rules simply specify the node generation rule, like: "This node is built using variables A, B, X and may update variables A, X, Y, Z". And all dependencies are resolved by dap engine automatically. When any of A, or B, or X changed, the node rebuilds automatically. When the node changes any of A, X, Y, Z — all the dependent nodes are updated — automatically, too. No any additional wirings needed.
Such a "functional" way of document definition may appear pretty much different from what web-developers are used to. It might seem complicated and cumbersome when reading syntax specification, but when it comes to actual coding — it turns out to be quite easy and handy.
I'd recommend that you see the 'Hello, world' tutorial before getting into dap syntax details. That would help you keep track of what is it all about.
Rules and Phases
All dap rules consist of steps, steps consist of tokens, tokens consist of parts. Very simple:
rule ::=step{; step}
step ::=token{ token}
token::=[field][$status][:converter{,converter}][@alias][=value]
That is, steps within a rule are separated by a semicolon+space; header and tokens within a step — by a single space; multiple converters are separated by commas. All token parts are grammatically optional. Empty strings are valid names, but in many cases they are reserved and have a special meaning.
A dap-enabled node may have none, any or both of the following rules, corresponding to node life phases:
-
Data or Down phase: d-rule
When a dap node is populated, its content is generated using its d-rule, and according to its status status context and data context. Execution of d-rule of a node may change any particular entries of its contexts, and theese changes will be observable by the nodes's descendants and after-siblings.
-
User or Up phase: u-rule
When a dap node is activated (usually, by a user-related event), its own u-rule and u-rules of its ancestors are executed, starting from the node and bubbling up. Changes made to status entries are automatically reflected in all dependent nodes, initiating rebuilds for the nodes who's content does depend on the changes made. Changes in data entries do not initiate updates, yet their updated values are used in rebuilds initiated by status updates.
Steps and Tokens
Each rule consists of one or more steps, wich are executed consequently on the rule run. A step consists of tokens. The first token of a step is the step's head; the rest are data tokens (or simply tokens). Data tokens define data involved in the step execution; step head specifies actions to perform on that data.
Zero or more data tokens may participate in execution of each step. A token is a structure, that defines a datapiece passed to a mapper. Data tokens are not just identifiers, but execution units wich provide some simple data manipulations, such as data binding, assignments and conversions. Token may reference a data entry, a status entry, or specify a literal value. Token also specifies conversion chain for the datapiece, and an alias, under wich the datapiece is fed to the mapper.
Each token may contain:
- data entry reference
- status reference
- conversion chain
- alias
- literal value
Token parts are separated by their respective prefixes: $ (dollar sign) — for status entry reference, : (colon) — for conversion chain, @ (at sign) — for alias, = (equal sign) — for value. Data entry reference, if present, is not prefixed, but is placed as first part of a token. All token parts are optional, but their order is mandatory. Full token looks like:
data$status:converter1,converterN@alias=value
and its meaning is:
- read data entry from the node's data context, or take literal value if data entry is not specified or not present in the data context.
- store it as a status entry in the node's status context
- sequentially apply all the converters,
- provide the executed token with an alias.
Omitting any token parts modifies token behavior in a straightforward manner:
- if no converts specified, datapiece is fed as is
- if neither data entry nor literal value specified, the status entry's own value is taken
- if no status entry specified, none is changed
- if no alias provided, the result is named after the entry (or left anonymous, if none of those are specified).
A shortened form is allowed, in which status entry name is omitted, while the $ prefix is present, like: entry$. This form is equivalent to entry$entry (read value from a particular data entry and store it in a status entry with the same name). This shorthand is very common in dap, it helps keeping the code cleaner and encourages reasonable entry naming.
Setting and getting status entries
As can be seen from the token structure description above, a status variable's value can be set (by specifying a literal value or a source data entry) and can be read (by omitting both literal value and data entry reference).
Setting a status entry has different semantics for up and down phases:
-
at the down-phase, a new status entry is introduced in the node's scope of status context; should a one with the same name already exist in outer scopes, it is hid from the current scope
-
at the up-phase, the referenced entry is first searched in the context, then, if not found, created new.
Reading a status entry always assumes that it is present in the node's status context (has been already introduced — in the node's ancestors' d-rules, or in the current rule earlier), otherwise rule fails.
Converters
Converters allow to... yes, convert, the result of token execution. Converters only change the result, not the source of the value. This changed value is fed to the mapper, but not written back to the entry it was obtained from.
Dap core provides the following converters:
-
esc, usc
escape/unescape(string)
-
+, -
absolute/negate value(number)
-
?, !
test if value(any type) present/missing
-
+?, -?, 0?
test if value(number) is positive/negative/zero
-
#get, #cfg, #dat, #lib
converters of this group "convert" the value(url) into data received from that url
-
csv
"Comma-separated values". Converts x1,x2,x3-like string into a single-column rowset.
-
nvp
"Name-value pairs". Converts x1:y1;xN:yN-like string into a multi-column (x,y) rowset. Rows need not be "pairs", any number of colon-separated columns is allowed.
Custom converters may be defined in extension libraries.
Step head
Step head is the first token of a step. Step head itself doesn't fetch datapieces; it defines what to do with the rest of the token list. It has the same structure as an ordinary token, except that its data part specifies a dap mapper, and its value part specifies a token list flattener.
Flatteners
A flattener may be applied to the token list to convert the whole list into a single datum, wich is fed to the mapper instead of individual tokens of the step. Alias for the flattened datum is specified by the head's alias part.
The most basic dap core flatteners are:
-
concat
Concatenates values of all tokens into a single string
-
space
Concatenates values by a single space
-
url
Builds an URLEncoded query string. Anonymous tokens are appended unencoded.
-
?, !
ANY(returns first non-empty value from the token list) / LACK (returns true if at least one token is false/empty)
In conjunction with ! converter, these flatteners become NONE / ALL respectively
-
eq, asc, dsc
Check respectively for: equality, monotonous ascend or monotonous descend of the token list Equality check may be performed upon any data, whereas ascend and descend checks — only upon numbers.
-
Examples
!=concat =abra @foo=cada @bar=bra abracadabra
!=space =abra @foo=cada @bar=bra abra cada bra
!=url =abra @foo=cada @bar=bra =hmaputra abra&foo=cada&bar=brahmaputra
!=? @empty= @foo=cada @bar=bra cada
?=eq =abra @foo=cad @bar=abra false
?=eq =abra @bar=abra true
?=asc =-1 =7 =12 =13 true
?=asc =-1 =7 =15 =13 false
?=dsc =-1 =-7 =-12 =-13 true
Custom flatteners can be defined in external libraries.
Mappers
Mappers to dap are what functions to javascript are. They take "arguments" and perform "actions". Dap mappers are unary — they deal with only one token at a time. Multiple tokens in a step are executed as a "for each token" sequence.
Since all mappers are unary, the following signature may be used for their description:
mapper @alias=value
Each token comes to a mapper as an @alias=value datapiece. Alias is specified by token's @alias part (or an entry name, or anonymous), and value is the result of token execution; it is also subject to conversions, if specified. If the token's alias is empty (like in =value, or data@, or data$status@:convert=value, etc.), the token is said anonymous. In some mappers, anonymous tokens have specialized meaning over named ones.
The basic dap core mappers are:
-
! @alias=value
Writes the value to the output node. Aliases are ignored.
Examples
d="! $prefix root $suffix"
writes $prefix, root, $suffix one by one, all as separate text nodes, though they will usually look concatenated in viewport
d="!=concat $prefix root $suffix"
writes a single text node, containing a concatenation of $prefix, root, $suffix.
d="!=space firstname lastname"
writes a text node, as firstname and lastname concatenated by space
d="! myTemplates.hello firstname"
writes the hello template from myTemplates library and the firstname datafield.
-
? @alias=value
Conditional gate: allows execution of the rest of the rule only if value matches the alias (for anonymous tokens: if value is present); otherwise, rule terminates.
Examples
d="? $page@catalog"
allows further execution of the rule if $page's value is 'catalog'
d="?=? $public@ name@ login@ email@"
allows if any of: $public status entry, or name, login, email data entries are present. (The ANY aggregation is performed by the ? flattener)
d="? $foo@"
allows if $foo is present
d="? $foo:!@"
allows if $foo is absent (the ! convertor turns absent to present (true) and vice versa)
-
attr @alias=value
Sets the node's attribute.
Examples
d="attr @id=myNode"
sets the node's id attribute to 'myNode'
d="attr@src=url =/images/ $imageId@ =.jpg"
sets src attribute to the calculated URL
-
ui @alias=value
UI element. Assigns an event listener to the node. Event is specified by alias (or 'click' if anonymous), a handler is specified by value (or default handler, if value is absent). A reduced — tokenless — form assigns click event listener and default handler.
Examples
d="ui"
adds 'click' event listener with default handler (launch u-phase on click)
d="ui @change @blur"
adds 'change' and 'blur' events listeners with the default handler
d="ui @mouseover=myOwnHandler @dblclick"
hangs myOwnHandler on the node's 'mouseover' event, and default handler on 'dblclick' event
-
* @alias=value
Populates a rowset: executes the rest of the rule for each row in the value (rowset). If the token is fed anonymous, datafield names are as provided in the rowset; if aliases provided, datafields are respectively (re)named.
Examples
d="* @color,hex:nvp=red:F00;green:0F0;blue:00F"
populates a rowset of primary RGB colors, each row containing color and hex datafields
d=" $colors=yellow:FF0;cyan:0FF;magenta:F0F; * $colors@color,hex"
populates a rowset of complementary RGB colors from a variable
d="* :#dat=/myData.asp"
populates a rowset from a dataset, wich is, in turn, loaded from /myData.asp
d="*:#dat=url =/myData.asp? @style=verbose"
populates a rowset from /myData.asp?&style=verbose
Entry-modifying mappers
Entry-modifying mappers are used to modify target's fields. The target may be a status entry, or the node's scope of data context.
-
The empty mapper. This mapper is likely the most frequently used one. That is why its name is the shortest possible — empty.
$status-entry @alias=value
Empty mapper, accompanied by a status entry reference sets (or updates) that entry's field specified by the alias.
@ @alias=value
Empty mapper, accompanied by an alias sets (or updates) a data entry in the node's up-closest data scope.
Examples
d="@grossweight=+ peas water can"
creates a data entry as sum (the + flattener) of other datafields.
u="$person@fullname=space firstname lastname"
adds to the $person status entry a new field — fullname, combined from the firstname and lastname datafields
If neither entry reference, nor alias specified in the step head (the mapper looks like a leading whitespace), empty mapper turns into pure zen and does nothing beyond execution of its token list.
<single whitespace>@alias=value
Pure execution of token list is usual for introduction of new entries, or forcing dependencies on particular status entries, or performing some token-centric activity.
Examples
d=" $foo=0 $bar="
introduces new status entries: $foo initiated with 0, and empty $bar
d=" price$"
introduces a new status entry $price, and initiates it from price data entry
d=" wholesale$price=0 discount$=0"
introduces two status entries: $price and $discount; $price is initiated from wholesale data entry or 0 if wholesale not found in the node's data context; $discount is initiated from discount data entry, or 0.
d=" $user $shop"
fictively "uses" $user and $shop status entries in d-rule, forcing the node to update when any of them change
u=" item$ price$"
on up-phase, variables $item and $price are updated from respective datafields; notice: both tokens are written in shortened form
-
%$target @alas1,alias2,alias3=value
Split. Takes a value, split by one of value-to-array converters, and maps its elements onto the target's fields specified by alias.
Examples
d="% grossweight:bar@peas,water,can"
If grossweight is equal to, say '120|180|100', it is split (bar converter splits a string by '|'symbol) into three data entries: peas='120', water='180', can='100'. Grossweight also remains.
u=$person@fullname=space firstname lastname"
adds to the $person entry a new field fullname, combined from the firstname and lastname datafields
-
-$target @alias=value
Toggles the target's field specified by alias. If the field is empty it is set to value (or true, if value not provided), otherwise set to empty.
Examples
d="-$expand"
toggles the value of $expand
d="- @email=missing"
sets the data entry email to 'missing', otherwise clears it (might be handy for "Sorry, email missing" alerts).
-
+$target @alias=value
Confirms the target's fields specified by aliases. If a particular field is not empty it is set to value (or true, if value not provided), otherwise remains empty.
Examples
d="+ @email=present"
sets email to 'present' — if it is present
u="+$contact @email=present"
sets non-empty email field of $contact status entry to 'present'
-
~$target =value1 =value2 =valueN
Cycles the target through the token list values plus empty value. Say, if at the moment target is equal to value1 — it is set to value2, if equal to valueN — set to empty, if empty — set to value1. A closed loop. If target's value doesn't match any value in the list, it is not changed.
Examples
u="~$page =home =portfolio =blogs =about"
on every run, picks for $page next value in the list. Beware, after 'about' goes empty. It must be acceptable for $page, too.
u="~$display firstname lastname job-title"
on every run, picks next data entry from the list.
Custom mappers can be defined in external libraries.
See also:
|