UNPKG

sai-language

Version:

An object-oriented language designed to afford code comprehension and maintenance. Transpiles in-place to Javascript.

1,827 lines (1,097 loc) 106 kB
## Keyword Reference SAI keyword reference. #### Legend .. - elided code ... - repeating pattern A/B/C - alternatives ( .. ) - optional syntax or component [block] - an indented block of code [expr] - any expression/value [function] - a function value [identifier] - a variable or method name lexpr / rexpr - an expression on the left/right side of an operator #### Keyword Types **Clause** - a component of a larger construct **Comprehension** - a special class of operator intended for use with collections. **Declaration** - describes or defines behaviour **Literal** - declares a value or collection of values **Pronoun** - an identifier that holds context-specific data **Operator** - a process that produces a new, likely modified value while leaving the original value(s) unchanged **Scoping Prefix** - symbol used at the beginning of a variable to specifiy context. **Statement** - a unit of code, occuping one or more lines, that performs some useful action **Verb** - a function reference that may be used as a statement or in an expression as a computation returning a value #### Sample Data Examples in this document use the following data: set fruit to list Apple, Banana, Citron set friends to: :name 'Sara', age 23, #cat, province 'ON' :name 'John', age 19, #cat, #dog, province 'ON' :name 'Ellie', age 22, province 'QC' :name 'Marshal', age 21, #dog, province 'ON' :name 'Doug', age 18, province 'ON' :name 'Ann', age 23, #cat, province 'QC' :name 'Harry', age 31, province 'QC' :name 'Jenna', age 28, #dog, province 'ON' set width to 10 height to 5 angle to 45o ### ! _operator_ .. ! [identifier] ([parameters]) (;) **!** (bang) invokes the following identifier as a function call that returns a value, passing in any parameters that follow. The following are synonymous: set record !cursor.FetchRow set record from cursor.FetchRow set record to cursor.FetchRow() set cursor !db.Query 'select * from names' set cursor to db.Query('select * from names') Neither **!** nor parenthesis are needed when the verb begins the line as in the `cursor.Close` statement below. set cursor from db.Query 'select * from names' set records from cursor.FetchAll cursor.Close If you wish to use **!** in a continuing expression, close the parameter clause with a semicolon (like you'd close a structural literal). The following two examples are equivalent. set @x ~Math.sin($angle) * $magnitude set @x !~Math.sin $angle; * $magnitude set @x from ~Math.sin $angle; * $magnitude At some point __from__ may be deprecated. ### != (not equal) _operator_ .. [expr] != [expr] Compares two values for value equality; returns **true** if the values appear to be the same, **false** otherwise. This is implemented with Javascript’s != operator and the behaviour is identical. Contrast with **isnt**. ### ' and " (string) _literal_ .. 'Type your string literal here.' .. 'Use a backslash to escape \' and \" and \\ in your strings.' .. "Double quote is semantically identical to single quote." Declare a single line delimited literal string. String literals support embedded expressions (a.k.a. string compositions) using the EcmaScript 6 syntax: `${expr}`. Any normal expression can be evaluated and will be concatenated in-place. Use backslash as an escape for string delimiters `\'` and `\"`, dollar signs before braces `\${`, and the standard control characters `\n \r \t \b \f`. debug 'This here\'s ${'a'} test using \${} composition.' > This here's is a test using ${} composition. ### ''' (multi-line string) _literal_ .. ''' Now you can type as much text as you want, remembering that leading and trailing whitespace will be stripped, though newlines will be preserved. So it's perfect for markup. The string goes on as long as the indenting does. And you can include ${'compo'+'sitions'} as well. That’s three single quotes in a row, then an indented block. Declare a multi-line string ideal for embedding longer passages of markup. Blank lines are preserved, but indenting is not (this is a bug). ### 'd _syntax_ .. [verb]'d [value] (parameters) An alternate way of writing: .. from [value].[verb] (parameters) This encourages a more conversational style of coding. ### 's _syntax_ .. [value]'s [attribute] [object]'s verb Equivalent to the use of `.` when accessing named attributes. ### \# (hashtag) _literal_ / _comprehension_ Declares or searches for a _hashtag_. #### \#hashtag literal .. traits #[tagname] .. fields #[tagname] When defining fields or traits, using a hash in front of a trait _name_ will automatically assign a value of **true** to that trait. The following two definitions are identical: set row1 to: name 'John', age 19, #cat, #dog set row2 to: name 'John', age 19, cat true, dog true #### \#hashtag comprehension .. [collection] #[tagname] // require field to be truthy .. [collection] !#[tagname] // require field to be falsy When used as a comprehension, the hashtag creates a simple filter that returns only records that have a _truthy_ value for the named field. The following two comprehensions are identical: set results1 to friends #cat set results2 to friends has .cat A negated hashtag creates a filter that returns only records that have a _falsy_ value for the named field. Not having that field at all is also a match. These comprehensions are identical: set results1 to friends !#dog set results2 to friends has not .dog Hashtag comprehensions can be chained inline, and the filtering is done efficiently. set results3 to friends #cat !#dog ### $ (parameter) _argument scoping prefix_ .. $ .. $[attribute] When alone, **$** returns the first parameter a function was called with. set ShowParameter to task debug $ ShowParameter 'Bianca' > Bianca When followed immediately by an attribute name, that attribute of the first parameter in a function. (It is not necessary to include the dot.) set ShowAttribute to task debug $name ShowAttribute friends[0] > Sara Even if you use **as** to name your parameters, **$** continues to refer to the first parameter. set ShowParameters to task given p1, p2 debug $ debug p1 debug p2 ShowParameters 'First', 'Second' > First > First > Second ### $$ (arguments) _pronoun_ .. $$ Equivalent to Javascript’s `arguments` and can be used the same way. ### $\{ } (string compose) _modifier_ .. 'String ${ [expr] } continues.' .. "String ${ [expr] } continues." .. `String ${ [expr] } continues. .. ''' String ${ [expr] } continues. Allows the insertion of any arbitrary expression in the middle of a string literal. As shown above, this is supported in all four string literal formats. set FriendSummary to task return 'Name: ${$name}, age: ${$age}' ply friends limit 3 as friend debug 'Subject ${key+1}: ${FriendSummary(friend)}' > Subject 1: Sara, age 23 > Subject 2: John, age 19 > Subject 3: Ellie, age 22 Use `\${` to represent `${` in a string literal. debug 'But I really need to include \${} in my output!' > But I really need to include ${} in my output! ### % (modulo) _operator_ .. [expr] % [expr] Calculates the mathematical **modulus**; returns the remainder of the left expression divided by the right. debug 5 % 2 debug 6.2 % 1 > 1 > 0.2 ### \* (multiply) _operator_ .. [expr] * [expr] Multiplies the two expressions. debug 2 * 3 > 6 ### \*\* (exponent) _operator_ .. [lexpr] ** [rexpr] Calculates an exponent; equivalent to `Math.pow([lexpr],[rexpr])`. debug 2 ** 3 > 8 ### + (plus) _operator_ .. [expr] + [expr] Adds two numbers OR concatenates two strings. If both expressions are numbers, adds them. Otherwise converts them both to strings and concatenates. debug 1+2 debug '1'+2 debug 1+'2' debug '1'+'2' > 3 > 12 > 12 > 12 ### - (minus) _operator_ .. [lexpr] - [rexpr] .. - [expr] Used as a binary operator, subtracts the right expression from the left. Used as a unary operator, negates the expression (subtracts it from 0). debug 3 - 5 debug 3 + -5 > -2 > -2 ### . (attribute) _syntax_ .. [expression].[attribute] .. .[attribute] .. .[numeric array index] [value].[attribute] .[attribute] .[numeric array index] Looks up the trait named _attribute_ in the expression. When used without a leading value, references the named attribute in the **it** pronoun. set friend to friends.0 debug friend.name with friend debug it.name debug .name // prints 'Sara' three times Named attributes can also be referenced with `'s` because why not. ### .. (caboose) _style_ You can use the two-dot caboose to stitch a *single line* child code block onto the end of a statement that requires it. Don't use the caboose unless it will help make your code clearer. The following are functionally identical: if error return -1 if error .. return -1 if error then return -1 See also __then__. ### ... (continue) _style_ You can use the three-dot continue syntax to insert line breaks, or multiple child code blocks, into a single statement. This is not considered good practice (period), as most overly complex statements should be broken down into multiple statements (period), just like a run-on sentence should be clarified by a good editor. But should you really need it, it is there. The three dot continuation must start at the same indent as the line it continues. debug traits name Sally age 32 province QC ... update traits #cat province ON > { name: 'Sally', age: 32, province: 'ON', cat: true } Or even: debug 1 ... + ... 2 ... + ... 3 > 6 Syntactically, the continue is equivalent to a whitespace character with a bonus ability to reset level of indent. ### / (divide) _operator_ .. [lexpr] / [rexpr] Divides the left expression by the right expression. debug 22 / 7 debug 355 / 113 > 3.142857142857143 > 3.1415929203539825 ### // (comment) _syntax_ // any text goes here, is ignored through the end of line. Format of comments in SAI source files. ### : (declare structure) _literal_ / _clause_ To define an array, single line: .. : [expr] (, [expr], [expr], ...) (;) Multiple line variant: .. : [expr] ( , [expr], ... ) ( [expr], [expr], ... [expr] ... ) To define fields: .. : [name] [expr], [name] [expr], [name] [expr], ... (;) Multiple line variation: .. : [name] [expr] (, [name] [expr], ...) ( [name] [expr], [name] [expr], ... [name] [expr] ... ) The **:** structure definition parser determines whether to create an array or fields by whether or not a field name is included before the first expression. debug :1,2,3 > [ 1, 2, 3 ] debug :a 1, b 2, c 3 > { a: 1, b: 2, c: 3 } ### ; (end structure) _syntax_ .. [structure definition] (;) Optional, closes the current structure definition when needed for clarity. Only needed when there is additional code on the same line that might mistakenly bind too tightly to the final value in the structure. debug list 3, 2, 1 by it debug array 3, 2, 1 by it debug array 3, 2, 1; by it > [ 1, 2, 3 ] > [ 3, 2, 1 ] // undesired result > [ 1, 2, 3 ] In the example above, the **list** literal sorts properly because **list** elements are not allowed to be mathematical expressions, so the parser can correctly bind `by it` to the entire list. However, the first **array** literal _doesn’t_ sort correctly because array elements can be expressions, and `by it` is binding to the final term in the literal. Thus, the definition is parsed as `array (3), (2), (1 by it)`. One can force **list** to parse incorrectly by using the **=** list element expression evaluation flag, but that’s ridiculous. debug list =3, =2, =1 by it // don't write code like this debug list =3, =2, =1; by it // just don't > [ 3, 2, 1 ] > [ 1, 2, 3 ] The semicolon can also close parameter lists if using the **from** form of function invocation. The following examples are identical: set x to ~Math.sin(angle) * magnitude set x from ~Math.sin angle; * magnitude ### \< (less than) _operator_ .. [lexpr] < [rexpr] Evaluates **true** if lexpr is numerically or lexically lower than rexpr, **false** otherwise. debug 1 < 1 // false debug 1 < 2 // true debug 'a' < 'b' // true debug 'a' < 'B' // false, case matters If you want a case insensitive comparison, you must ensure both expressions are of the same case. ### \<\< (left shift) _operator_ .. [expr] << [bitcount] Convert the expression into a 32 bit signed integer and perform a binary left shift *bitcount* times. debug 4 << 1 // 8 debug -8 << 2 // -32 debug 2.8 << 0 // 2 ### \<= (less or equal) _operator_ .. [lexpr] <= [rexpr] Evaluates to **true** if lexpr is numerically or lexically lower or equal to rexpr, **false** otherwise. ### \<=\> (compare) _operator_ .. [lexpr] <=> [rexpr] Evaluates to **-1** if lexpr is lower than rexpr, **1** if it is greater, and **0** if they are equal. debug 1 <=> 0 // returns 1 debug 1 <=> 1 // returns 0 debug 1 <=> 2 // returns -1 debug 'b' <=> 'a' // returns 1 debug 'b' <=> 'b' // returns 0 debug 'b' <=> 'c' // returns -1 ### = (equal) _operator_ .. [expr] = [expr] Evaluates to **true** if the two expressions are equivalent. This compiles directly to Javascript’s `==` operator and has the same occasionally bizarre side effects. Basically, don’t use = except for comparing numeric, boolean or string values. Use **is** or **isnt** to compare objects or object types, except when dealing with **NaN** in which case the only reliable way to test for its existence is to use **isNaN**. That’s Javascript for you. ### \> (greater than) _operator_ .. [lexpr] > [rexpr] Evaluates to **true** if lexpr is numerically or lexically greater than rexpr, **false** otherwise. ### \>= (greater or equal) _operator_ .. [lexpr] <= [rexpr] Evaluates to **true** if lexpr is numerically or lexically greater or equal to rexpr, **false** otherwise. ### \>\> (signed right shift) _operator_ .. [expr] >> [bitcount] Convert the expression to a 32 bit signed integer, then shift it right bitcount times, maintaining the sign of the expression. debug 8 >> 1 // 4 debug -32 >> 2 // -8 ### \>\>\> (unsigned right shift) _operator_ .. [expr] >> [bitcount] Convert the expression to a 32 bit *unsigned* integer, then shift it right bitcount times. Zeros are added from the left. debug 8 >>> 1 // 4 debug -32 >>> 2 // 1073741816 ### ? (safe fetch) _operator_ .. ? [expr] Adds extra type-checking to an expression ensuring that it will not throw an exception if roots are undefined; instead just returning **undefined**. set a to undefined debug a.a // throws an exception debug ?a.a > undefined ### ?\< (minimum) _operator_ .. [lexpr] ?< [rexpr] Evaluates to whichever expression is numerically or lexically lower. debug 1 ?< 2 // returns 1 debug 2 ?< 1 // returns 1 ### ?\> (maximum) _operator_ .. [lexpr] ?> [rexpr] Evaluates to whichever expression is numerically or lexically higher. debug 1 ?> 2 // returns 2 debug 2 ?> 1 // returns 2 ### ?? :: (if/this/that) _operator_ .. [expr] ?? [expr1] :: [expr2] Evaluates to expr1 if the left expression is _truthy_, otherwise evaluates to expr2. debug true ?? 'True' :: 'False' // prints 'True' debug false ?? 'True' :: 'False' // prints 'False' ### [ ] (lookup) _syntax_ .. [value] '[' [expr] ']' Performs an indirect lookup of a trait/element in the given value. The following debug statements print identical results. set field to 'name' set friend to friends[0] debug friend.name debug friend['name'] debug friend['na'+'me'] debug friend[field] // prints 'Sara' four times. ### @ (object) _this scoping prefix_ Ugly. Synonym for `me` and `my`, which see. ### @@ _scoping prefix_ Used inside called functions to refer to the "this" object in the scope of the call, rather than the scope of the function definition. ### ~ _global scoping prefix_ .. ~GlobalIdentifier Use `~` in front of an identifier to access an object defined globally, e.g. by the runtime environment. You must use `~` in each of the following examples: set validated to ~parseInt(raw) set rounded to ~Math.round(float) ~process.exit 1 set attendees to new ~Set set func to new ~Function(arg1, src) You do not need to use `~` on file-level pseudo-globals defined with `reference`; it is only required for functions/values/objects that originate outside SAI. This leads to the following shortcut which I don't recommend because it's unclear where Math comes from. Not that I can stop you. reference: Math ~Math set rounded to Math.floor(float) ### \\ _lookup syntax_ .. [var]\[value] A cleaner way of doing indirect lookups when only using a single literal or variable. The following debug statements print identical results. set field to 'name' with friends.0 debug it.name debug .name debug it['name'] debug it\'name' debug it[field] debug it\field // prints 'Sara' six times. The `\` lookup syntax can only be used with simple values; that is, single variable names or literals. If you need to use an expression, you should use the `[expr]` form of lookup. ### \` (full line string) _literal_ .. `text Signifies the start of a string literal that will occupy the rest of the source code line. debug `It's nice not to have to worry about "delimiters." > It's nice not to have to worry about "delimiters." ### and _operator_ .. [expr] and [expr] Logical and operator. If the left expression evaluates to **falsy**, returns it, otherwise returns the right expression. debug 1 and 0 // prints '0' debug 1 and false // prints 'false' debug 0 and 1 // prints '0' debug false and 1 // prints 'false' debug 1 and 1 // prints '1' debug true and 'Fred' > Fred ### andb _operator_ .. [expr] andb [expr] Bitwise and operator. Converts, if possible, both left and right expressions into 32 bit signed integers, and performs a bitwise AND operation on them. debug 5 andb 11 > 1 // 5 in binary: 00000000000000000000000000000101 // 11 in binary: 00000000000000000000000000001011 // 1 in binary: 00000000000000000000000000000001 ### array _literal_ .. array [expr], [expr], ... (;) .. array [expr], [expr], ... [expr] ... Used to specify the creation of a plain array of mathematical values; e.g. the result of a series of expressions. As opposed to **list**, which is a plain array of bare literals. In general, the **colon** structure constructor will figure out what you want, but when you want to be specific about creating an array of expressions, use **array**. Compare with **list**, **fields** and **traits**. Arrays may be specified on one line: debug array 1+1, 2*3, 'Fred' > [ 2, 6, ‘Fred’ ] Or multiple lines in the form of an indented block: debug array width * ~Math.cos(angle) height * ~Math.sin(angle) > [ 7.0710678118654755, 3.5355339059327373 ] Or a combination of both: debug array 1, 1, 2, 5 14, 42, 132, 429 1430, 4862 When using an array literal in an expression that might make the end of the array a matter of question, use a **semicolon** to close the array literal: debug array 5, 3, 2, 7, 4; has it%2 > [ 5, 3, 7 ] Or enclose the array in parenthesis: debug (array 4, 3, 2, 1) by it > [ 1, 2, 3, 4 ] Arrays can be nested by use of either parenthesis or semicolons, or by using multiple levels of indent. Note that commas separate expressions on one line but are not included at the end of a line. debug array array 1, 2, 3;, array 4, 5, 6 array 7, 8, 9 > [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ] ### as _clause_ As declares named locally scoped variables within a code block based on values passed into the block. (In general, **as** is optional because one can usually access such passed-in values through pronouns.) **As** is used in conjunction with the following SAI keywords: set, task, process, promise, switch, catch, if, exists, with, iterate, each, ply, count, by, thru, audit, gather, has, set, observe, and within parenthesis as the “parenthetic as”. #### as with parameters Note that using __as__ to describe function parameters is now deprecated, please use __given__ instead. set [ident] (as [$ var]) // dynamic trait .. task (as [parameter1], [parameter2], ...) .. process (as [parameter1], [parameter2], ...) .. promise (as [parameter1], [parameter2], ...) An example of parameter naming: set tally to blank set AddRow to task given item, quantity set tally[item] to (self default 0) + quantity AddRow 'socks', 3 AddRow 'pants', 6 debug tally > { socks: 3, pants: 6 } #### as value reclaimation switch [expr] (as [trial var]) catch (as [error var]) if [expression] (as [trial var]) exists [expression] (as [it var]) with [expression] (as [it var]) An example of **if** value reclamation with **as**: if @keydown as charcode switch charcode case 27 @Escape case 32 @Continue #### as with iterators iterate [expr] (as [it var]) each [expr] (as [it var] (, [key var] ) ) ply [expr] (as [it ident] (, [key var] ) ) count [expr] (as [key var]) An example, renaming **it** and **key** in an each iterator: each friends first as value, field debug `${field}: ${value} > name: Sara > age: 23 > cat: true > province: ON #### as with comprehensions [expr] by (as [a var], [b var] ) [expr] thru (as [it var] (, [key var] ) ) [expr] audit (as [it ident] (, [key var] ) ) [expr] into [expr] (as [sum var] (, [it var] (, [key var] ) ) ) [expr] filter (as [it var] (, [key var] ) ) [expr] set (as [it var]) [expr] observe (as [it var]) An example, renaming **it** and **key** in the block handler of a **thru** comprehension: debug friends thru as friend, index return `${index}) ${friend.name} lives in ${friend.province} > [ '0) Sara lives in ON', '1) Jon lives in QC', '2) Ellie lives in QC', '3) Marshal lives in ON', '4) Doug lives in ON', '5) Ann lives in QC', '6) Harry lives in QC', '7) Jenna lives in ON' ] #### as parenthetic .. ( [expr] as [var] ) The parenthetic **as** assigns the value of the parenthesised expression to a named identifier. The assignment happens as soon as the parethesis is evaluated, so you can use the identifier in the same expression as the parenthetical, as long as the parenthetical is evaluated first. _This is not a good style of coding_. set six to (1+2 as three)+three debug array three, six > [ 3, 6 ] ### assert _verb_ assert [expr], [expr] If the first expression is _falsy_, throw an exception with the second expression as a message. assert everythingIsAwesome, 'I have some bad news...' Error: SAI: failed assertion: I have some bad news... ### bind _operator_ .. bind [function reference] The __bind__ operator _binds_ a function reference to the current object context. This is useful when you need to pass in a member function to a callback, and need to ensure the execution context (specifically, the _this_ object) isn't lost. Why is this important? Object car instance: plate '876 ACET' CheckPlate task DB.lookupPlate plate, PlateResults PlateResults task given status debug 'Status for ${plate}: ${status}' In the above function definition of `CheckPlate` the `DB.lookupPlate` function is an asynchronous call with a plate number and a callback function. As written, when the callback is invoked, `PlateResults` will have been unbound from its object; it is a function reference with no context. The code will produce this result: Looking up 876 ACET Status for undefined: Valid Although the test was successful, we have _lost context_ on the callback because `PlateResults` is passed in as an unbound function. Referring to the member trait `plate` within an unbound context produces an undefined result. In order to make this function reference work, we need to bind it to the current object with the __bind__ operator, as follows: DB.lookupPlate plate, bind PlateResults Now when we run the asynchronous function, it works as expected, because the callback has its context; e.g. it knows where it is going (or, more accurately, where it is returning to). Looking up 876 ACET Status for 876 ACET: Valid Note that __bind__ is only necessary when passing references to object member functions. Functions you define in-place won't have this issue, as they create their own context. This version of __bind__ only binds functions to the current object. If you want to bind a function on some other object, you'd write code something like this, making use of the Function object's bind ability: ... from anObject.anyFunction.bind anObject ### blank _literal_ .. blank The keyword **blank** creates a plain object with no traits. It is the SAI equivalent of Javascript’s `{}`. set player to blank set player.age to 21 debug player > { age: 21 } ### break _statement_ [loop/iterator] [code] break switch case [expr] break default break To exit a loop, iterator or switch case before its natural end, use the **break** keyword. count 5 debug it if it=2 debug 'Nevermind...' break > 1 > 2 > Nevermind… ### case _clause_ switch [trial expr] ( as [trial ident] ) case [match expr] (, [match expr] (, ...) ) [code] case [match expr] [code] default [code] ... Use one or more **case** clauses within a **switch** statement to specify one or more matching expressions to be tested against with the trial expression. switch keystroke case 'n', 'N' @move: x 0, y -1 case 'e', 'E' @move: x 1, y 0 case 'w', 'W' @move: x -1, y 0 case 's', 'S' @move: x 0, y 1 case ' ' @skipturn default @display 'Use n/e/s/w to move, space to pause.' Unlike Javascript, execution within a **case** statement does not fall through to the next case, so you don’t need to add **break** at the end of a code block. ### catch _clause_ try [code] catch ( as [error ident] ) [code] finally [code] The **catch** clause heads a section of code that executes if and only if any code within the **try** clause throws an exception. The exception thrown is caught and placed in the **error** pronoun (or the variable named in the **as** clause), as follows: try throw new ~Error 'This is an error message!' catch return error.message Alternately: try throw new ~Error 'This is another error message!' catch as e return e.message For details on how try/catch/finally exceptions work, see the Jasacript exception documentation. ### chain _operator / verb_ #### chain as an operator .. chain [expr] [comprehension/method] [comprehension/method] ... The **chain** clause allows you to compose (string together) a series of operations that will each be applied in turn to a value, object, collection or iterator. **Chain** is another way of writing `value.method().method().method().method()` that offers cleaner code and more possibilities. You start with an object, then apply a sequence of verbs to it. Each verb is a new link in the chain. set mirror to task return chain $ split '' reverse join '' debug mirror('A man, a plan, a canal, Panama!') > !amanaP ,lanac a ,nalp a ,nam A In the previous case, each verb was a trait (a method) of the string itself. You can also chain comprehensions: debug chain friends #cat by .name thru 'My friend ${.name} likes cats.' > [ 'My friend Ann likes cats.', > 'My friend Jon likes cats.', > 'My friend Sara likes cats.' ] And because one of the comprehensions is **set**, you can actually chain any function at all. set double to task return chain empty concat $ concat $ debug chain fruit set double(it) > [ 'Apple', 'Banana', 'Citron', 'Apple', 'Banana', 'Citron' ] Functions you use in **chain** typically return a value; this is used as the object to pass to the next link in the chain. However, some methods and functions don't return a value, instead modifying their context in-place. If a function returns **undefined**, **chain** will reuse the previous object for the next call. _Be careful: some built-in methods return unexpected values that can foil **chain**. For example, **Array.push** is very irritating for this; you cannot chain **.push** because it returns the new array length instead of either the array or **undefined**._ #### chain as a verb You can also use `chain` as a verb, that is, at the beginning of a line. This is useful when the end result of the chain isn't useful but the intermediate results are, which can often be the case when parsing data. set population empty chain friends audit inc population[.province] to (self default 0) + 1 debug population > [ ON: 4, QC: 4 ] Arguably it might be better to code this particular example with a discrete iterator statement like `ply`, but not every use case is as trivial as this example. _Note: If you use chain as a verb in this way, and the final result of the chain is a generator, it will be drained automatically._ Otherwise, the code within the chain may never be executed, because without a drain, generators do nothing. ### continue _statement_ [iterator/loop] [code] continue In a loop or otherwise iterating block of code, **continue** short-cuts the remaining code in the block, causing the loop to continue its next iteration immediately. count 10 unless key % 2 continue debug key > 1 > 3 > 5 > 7 > 9 ### contract _declaration_ contract: [task/trait name] [task/trait name] ... When defining an object, **contract** is used to specify tasks or traits that _children_ inheriting from this object are intended to implement. Inheriting from an object that has contracts, and then not providing implementations for those contracts, will result in a SAI exception. object fruit contract: Consume object apple inherit: fruit Consume task debug 'You ate an apple.' object pear inherit: fruit > exception thrown: “SAI: Contractually required task ‘Consume’ does not exist in object ‘pear’.” ### copy _operator_ .. copy [expr] Use the **copy** unary operator to create a _shallow copy_ of the expression it precedes. You sometimes need this because ordinarily SAI and Javascript assign objects, rather than copies of object values. set plate to fruit // i want some fruit plate.push 'Ice cream' // and also dessert debug fruit // but now fruit has changed > [ 'Apple', 'Banana', 'Citron', 'Ice Cream' ] fruit.pop // get rid of the ice cream debug fruit > [ 'Apple', 'Banana', 'Citron' ] set plate to copy fruit plate.push 'Ice cream' debug fruit > [ 'Apple', 'Banana', 'Citron' ] debug plate // there we go > [ 'Apple', 'Banana', 'Citron', 'Ice Cream' ] Note that **copy** only copies enumerable values. ### count _statement_ / _comprehension_ #### count as a looping control statement count [high expr] ( as [counter ident] ) [block] ( else [none block] ) // variants count [low expr], [high expr] ( as [counter ident] ) count [low expr], [high expr], [step amount expr] ( as [counter ident] ) countdown [high expr] ( as [counter ident] ) countdown [high expr], [low expr] ( as [counter ident]) countdown [high expr], [low expr], [step expr] ( as [counter ident]) Specifically designed as an integer iterator for list elements, **count** always counts 1 at a time (unless a **step** clause is provided), and its highest value is always the step value less than the high value provided (both when counting up and down). To reiterate, **count** _never_ outputs the high value. **Count** uses the pronoun **counter** for the value. count 10 debug counter // prints numbers from 0 to 9 count 5, 10 debug counter // prints numbers 5 to 9 countdown 10 as i debug i // prints numbers from 9 to 0 countdown 25, 15 // prints numbers 24 to 15 count 0, 50, 10 // prints 0, 10, 20, 30, 40 countdown 45, 0, -10 // prints 35, 25, 15, 5 When using a step with **count down**, be sure that the step expression is negative, otherwise an infinite loop will result. count aList.length debug 'List element ${counter} is ${aList[counter]}' else debug 'The list is empty.' The optional **else** clause is executed instead of the main block if the **count** range length computes to 0 (or less). ### counter _pronoun_ .. counter A partner to **count**, **counter** is a _pronoun_, populated by the **count** iterator, if no other variable is specified in an **as** clause. Like all pronouns, **counter** is only visible in attached expressions or code blocks, not in any functions that may be called within those expressions. A complete list of **counter** enabled events: count [expr] // key: the number being counted An example: ply friends debug key // prints: 0 1 2 3 4 5 6 7 each friends[0] debug key // prints: name age cat province Similar to **it** in nested contexts, you are unable to access “outer” values of **counter** within inner contexts unless you assign them to a name other than **counter** using the **as** clause. count 3 count 3 debug counter // only the inner loop's 'counter' is visible here. // prints 0 1 2 0 1 2 0 1 2 count 3 as i count 3 debug i+','+counter // outer loop's 'counter' now available here as 'i' // prints 0,0 0,1 0,2 1,0 1,1 1,2 2,0 2,1 2,2 count 3 count 3 as j debug counter+','+j // no, this doesn't work, you still can't access the outer 'counter' // 'counter' always has the value of the innermost context // prints 0,0 1,1 2,2 0,0 1,1 2,2 0,0 1,1 2,2 ### create _operator_ .. create [expr] [parameters] Creates a SAI object by name. Will attempt to find the object’s source by using the `SAI.config.Loader` function, which defaults to `SAI.GetSourceFromPaths`, which tries to find a file named `[expr].sai` in the provided paths. reference: Tally 'Tally ^1.0.0' .. set inventory to create Tally The example follows best practices of placing object names in a **reference** section, aliasing versioned names into literals. See also __new__, which is a direct link to Javascript's `new`, and __singleton__, which works like __create__ except instantiates the object only once in the scope of the runtime library; subsequent calls will return that same object. ### debug _adhoc verb_ debug [expr] Prints the value of the expression to the console. Compiles directly to `console.log([expr])`. ### dec _statement_ dec [var] Decrements (reduces by 1) the value in the given variable. set a to 2 dec a debug a > 1 ### default _operator_ #### default in expressions ... [lexpr] default [rexpr] If and only if the left expression evaluates to `undefined`, returns the right expression, otherwise returns the left expression. Like **or** except instead of checking for _truthy_ tests to see if a value is available. Most useful in expressions where you’re not sure if a variable is initialized. set magnitude to undefined angle to 60o debug (magnitude default 1) * ~Math.cos(angle) > 0.5 #### default in **set** statements set [lexpr] default [rexpr] If the left expression is `undefined` then evaluate the right expression and assign it to the left expression. For example: set calculationCache[key] default ExpensiveCalculation(key) This checks to see if the calculation cache has a value for the given key, and if not, calculates the value and assigns it to the cache. ### delete _statement_ / _operator_ delete [variable] set [variable] delete set [variable] delete [attribute(s)] ... [expr] delete [attribute(s)] When called on a variable or traits, undefines the named variable or attribute(s) of a variable. This just calls the underlying Javascript version of **delete**, with all the same rules and caveats. The only clever addition is the ability to delete multiple attributes at once by supplying a collection: set obj to: a 1, b 2, c 3, d 4, e 5, f 6 set obj delete 'a' debug obj > { b: 2, c: 3, d: 4, e: 5, f: 6 } set obj delete: 'c', 'f' debug obj > { b: 2, d: 4, e: 5 } set obj delete: b 'B is for Bonobo.', d 'D is for Dixie cups.' debug obj > { e: 5 } When **delete** operates on an array, it returns a copy of the array with the elements specified removed. debug (list 1,2,3,4,5) delete 1 > 1, 3, 4, 5 debug (list 1,2,3,4,5) delete (list 1,2) > 1, 3, 5 Elements are removed in first->last order, so sequence does matter. debug (list 1,2,3,4,5) delete (list 2,1) > 1, 4, 5 ### do _modifier_ dowhile [expr] [code] dountil [expr] [code] Alters a **while** or **until** statement so that the test is performed after the code block, not before. This guarantees the code block will be executed at least once. Check out these wacky examples. dountil true debug 'Hello, World!' dowhile false debug 'Hello, World!' Note there's no space in there. It's __dowhile__ and __dountil__. Makes parsing easier when it's one word. ### down _modifier_ count down [start] count down [start] to [stop] Used only with **count**; signifies the count is to proceed by _decrementing_ the key. The first key value is the start value minus one, and the last key value is the stop value, or zero. count down 5 debug key > 4 > 3 > 2 > 1 > 0 **Down** can seem freaky because you never get the value you begin with, and it seems to be even freakier when you're using a step value other than 1. count down 15 step -4 debug key > 11 > 7 > 3 Here's an important safety tip, illustrated above: if you're using **down** with **step**, be sure the step value is negative. Otherwise the loop will never terminate. See also: **count** and **step**. ### each _statement_ each [collection] ( as [it var] ( , [key var] ) [code block] ( else [code] ) each [collection] using [function] **Each** uses Javascript's `for-in` loop construct to iterate through an object's enumerable properties. It steps through each element of the collection, in arbitrary order, setting **it** and **key** variables, and executing the code block for each one. If the collection is empty, executes the (optional) **else** block instead. set friend to friends first each friend debug `${key}: ${it} else debug `You have no friends. > name: Jon > age: 19 > cat: true > dog: true > province: QC Recall that you can rename **it** and **key** with the **as** clause: each friend as value, field debug `${field}: ${value} #### each ... using **Each** can also call out to a function with the **each using** variation. set handler to task given value, field, collection debug `${field}: ${value} each friend using handler The function is called for each attribute of the collection with three parameters: - attribute value (e.g. __it__) - attribute name (e.g. __key__) - reference to the collection itself ### else _clause_ if [expr] / unless [expr] / exists [expr] / each [expr] / ply [expr] / iterate [expr] / count [expr] [block] else [block] Specifies a block of code that will be executed if the condition leading to the previous block fails. Can be used with conditional statements **if/unless/exists** as well as iterating loops **each/ply/iterate/count**. if b debug 'b is truthy' else debug 'b is falsy' each obj debug '${key}: ${it}' else debug 'No traits.' ### elsexists _clause_ if ... elsexists [expr] (as [identifier]) [block] Combines __else__ and __exists__. ### elsif _clause_ if ... elsif [expr] (as [identifier]) [block] Combines __else__ and __if__. ### elsunless _clause_ if ... elsunless [expr] [block] Combines __else__ and __unless__. ### empty _literal_ .. empty A literal value indicating an empty list/array. The equivalent of Javascript’s `[]`, **empty** creates an Array with no elements. ### enum _modifier_ fields [identifier] enum, ( [identifier] enum, ... ) When declaring a set of fields, specifies a value 1 higher than the value of the previous definition. If there is no previous definition, 1. debug fields a enum, b enum, c 10, d enum > { a: 1, b: 2, c:10, d: 11} ### Error _global object_ Refers directly to the native Javascript error object, as in: throw new Error "message" This has been added strictly as a convenience. ### error _pronoun_ .. error Valid only within the **catch** exception handler clause of a **try/catch/finally** construct. Receives the exception thrown. try noFunction catch debug error > [TypeError: undefined is not a function] You can override this behaviour by using an **as** clause with the **catch** statement: try noFunction catch as e debug e > [TypeError: undefined is not a function] ### every _statement_ every [collection] ( as [it var] ( , [key var] ) [code block] ( else [code] ) every [collection] using [function] **Every** is a general-purpose iterator that can handle any collection -- array, object or iterable -- or a even single value (which case it executes the block once). It steps through each element of the collection, in order if possible (array, iterable), in random order otherwise (objects), setting **it** and **key** pronouns, and executing the code block. The **key** pronoun will be the index number for arrays, the attribute name for objects, or a row counter for iterables. It will be `0` for the single pass granted a single value. If the collection is empty, executes the (optional) **else** block instead. set friend to friends first every friend debug `${key}: ${it} else debug `You have no friends. > name: Jon > age: 19 > cat: true > dog: true > province: QC Recall that you can rename **it** and **key** with the **as** clause: every friend as value, fieldname debug `${fieldname}: ${value} _Note:_ there is a slight performance penalty associated with using __every__ instead of a more specific iterator, as SAI must wrap the collection in a thin iteration in order to generically handle all cases. It is, however, very useful when you are not sure what kind of collection you'll be working with. #### every ... using **Every** can also call out to a function with the **every ... using** variation. set handler to task given value, field debug `${field}: ${value} every friend using handler The function is called for each attribute of the collection with two parameters: - attribute value (e.g. __it__) - attribute name (e.g. __key__) ### exists exists [expr] ( as [it ident] ) [code] else [code] Executes the indented code if the expression is a defined value, e.g. if it is not **undefined**. The tested value will be available as **it** within the scope of the code block. (This is different from the _truthy_ testing **if**, which uses **trial**.) **Exists** is quite handy when working with caches and lookups. exists @cache[$key] return it else set @cache[$key] from @ExpensiveCalculation $key return @cache[$key] Though an even slicker way of dealing with that particular idiom is to use **default**: set @cache[$key] default from @ExpensiveCalculation $key return @cache[$key] ### expects _clause_ / _operator_ ... task/promise/process expects [rules definition] .. [expr] expects [rules object] Used to check whether a function’s named parameters (or any arbitrary object) has certain traits, and optionally if those traits are of a particular type. #### expects clause When used as the clause in a **task**, **promise** or **process** function definition. **expects** adds run-time parameter checking to the function, passing the first argument (the one that holds named parameters) through an *expects check* based on the rules that follow. If any expectations are not met, a runtime error is thrown. Names appearing in **expects** clauses, and thus being used to check parameters, _must_ be preceeded by **$**. This is intended as a reminder that *named parameters are always referenced with the $ scoping operator*. #### expects operator Expects compares an object againt a set of rules and returns a list of rules that are **violated**. Thus, a successful test of expectations is an empty array. An array with one or more elements contains a list of the rules that were broken. #### expects rules Rules for **expects** take the form of a set of traits. Their names correspond to names of traits that must be found in the expression under test. The value, if not _true_, is interpreted as an object type; the trait must be of that type (or a child of that type). Any violations to the rules are returned in a list; that list being made up of plain objects with the following fields: .trait - name expected .expects - type expected .found - type found Here are some examples, finally: AddStudent task expects $name string, $age number @students.push copy $ .. @AddStudent name 'Sally', age '12' > Error: SAI: parameter exception in AddStudent > age should be number, but it's string Performing this same test in an ad-hoc fashion using the **expects** operator would look like this: set newStudent to: name 'Sally', age '12' set studentRules to: name 'string', age 'number' debug newStudent expects studentRules > [ { trait: 'age', expects: 'number', found: 'string' } ] N.B. the **expects** clause rule format is identical to that of a single-line **traits** definition, with the additional requirement of a **$** before every trait name. ### false _literal_ .. false The boolean value **false**. ### finally try [block] catch ( as [error ident]) [block] finally [block] An exception handling clause identifying a block of code that is executed whether or not an exception occurs. (Even executes if you re-**throw** an exception within the **catch** clause.) For details, see the Jasacript exception documentation. ### fields _literal_ .. fields [key] [expr], [key] [expr], ... (;) .. fields [key] [expr], [key] [expr], ... [key] [expr] ... Used to specify the creation of a plain object with a set of key/value pairs. (Contrast with **traits**.) In general, the **colon** structure constructor will figure out what you want, but when you want to be specific about creating a plain object with of keyed values & expressions, use **fields**. Compare with **list**, **array** and **traits**. The **key** is an identifying word or other string, specified without quotes (although quotes may be used if desired/necessary). If a **\\\\\\\\\\\\#** preceeds the key (a hashtag), the key will be assigned a value of **true**. The **expr** is any valid literal, variable, object or expression. Fields may be specified on one line: fields name 'Sera', class 'Rogue', level 21, #focus > { class: 'Rogue', level: 21, name: 'Sera', focus: true } Or multiple lines in the form of an indented block: fields x width * ~Math.cos(angle) y height * ~Math.sin(angle) > { x: 0.5, y: 0.866 } Or a combination of both: fields str 16, dex 34, mag 14 con 42, wil 21, cun 45 > { str: 16, dex: 34, mag: 14, con: 42, wil: 21, cun: 45 } When using a fields literal in an expression that might make the end of the literal a matter of question, use a **semicolon** to close the literal, or enclose it in parenthesis: fields x 5, y 4, z 3; thru it*2 (fields x 5, y 4, z 3) thru it *2 > { x: 10, y: 8, z: 6 } Fields initializers can be nested by use of either parenthesis or semicolons, or by using multiple levels of indent. ### from _operator_ .. from [identifier] ([parameters]) (;) **From** invokes the identifier as a function call that returns a value, passing in any parameters that follow. It is the preferred syntax for function calls that include indents, as it is difficult to close a parenthesis. The following pairs are synonymous: set record from cursor.FetchRow set record to cursor.FetchRow() set cursor from db.Query 'select * from names' set cursor to db.Query('select * from names') **From** is the encouraged form for **set** statements because it allows a more natural reading of source code. __From__ indicates that the identifier that follows will be used as a verb and returning a value. Neither **From** nor parenthesis are needed when the verb begins the line as in the `cursor.Close` statement below. set cursor from db.Query 'select * from names' set records from cursor.FetchAll cursor.Close If you wish to use **from** in a continuing expression, close the parameter clause with a semicolon (like you'd close a structural literal). The following two examples are equivalent. set @x to ~Math.sin($angle) * $magnitude set @x from ~Math.sin $angle; * $magnitude ### get _declaration_ object [name] [version] [ident] get [code] return [value] ( set ( given [ident] ) [code] Declares a _getter_ for a dynamic object trait; that is, a trait that has a value that is only calculated upon request. object Vector2 1.0.0 magnitude get // dynamic getter for 'magnitude' trait return from ~Math.sqrt (@x * @x) + (@y *