UNPKG

sai-language

Version:

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

1,312 lines (865 loc) 41 kB
# This file is automatically generated. _Do not edit._ Unknown Heading ### BREAK - _exit a loop or switch case_ - constructs - ^break To exit a loop, iterator or switch case before its natural end, use the **break** keyword. break As in: ply collection unless FileExists(collection) break or .. switch choice case 'exit' if exiting break set exiting true ... or ... count 5 debug it if it=2 debug 'Nevermind...' break > 1 > 2 > Nevermind… ### CONTINUE - _shortcut to the next iteration of a loop_ - constructs - ^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. continue An example: count 10 unless key % 2 continue debug key > 1 > 3 > 5 > 7 > 9 ### COUNT - _numerically controlled looping_ - constructs - ^count In this most basic kind of loop, a block of code is executed a certain number of times with a counter keeping track of iteration count. More complexity is available if needed. count [expr1 (, expr2, ...)] (AS var1, var2, ...) [indent code block] else [indent code block] Specifically designed as an integer iterator for list elements, **count** always counts 1 at a time (unless a **step** value 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. 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]) **Count** uses the pronoun **counter** for the value. count 10 debug counter .. counts from 0 to 9 count 5, 10 ... numbers 5 to 9 countdown 10 as i ... numbers from 9 to 0 countdown 25, 15 ... numbers 24 to 15 count 0, 50, 10 ... 0, 10, 20, 30, 40 countdown 45, 0, -10 ... 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). You can use __break__ to exit out of the loop prematurely, and __continue__ to shortcut to the start. See the documentation on these terms for more. ### COUNTDOWN - _numerically controlled looping in reverse_ - constructs - ^countdown Very similar to __count__ except that __countdown__ is designed to operate with a negative step. That is, the first value will be higher than the last value. countdown [expr1 (, expr2, ...)] (AS var1, var2, ...) [indent code block] else [indent code block] See the entry for __count__ for details. ### DOUNTIL - _until variant that always executes the loop once_ - constructs - ^dountil Functions just like the __until__ loop, except that the block of code is always executed once _first_, before the test. dountil [expr] (AS var1, var2...) [indent code block] I could duplicate text here or just refer you to __dowhile__ and __while__. Which would you like? ### DOWHILE - _while variant that always executes the loop once_ - constructs - ^dowhile Functions just like the __while__ loop, except that the block of code is always executed once _first_, before the test. dowhile [expr] (AS var1, var2...) [indent code block] 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. You can use __break__ to exit out of the loop prematurely, and __continue__ to shortcut to the start. See the documentation on these terms for more. See __while__ for more. ### EACH - _object attribute enumeration_ - constructs - ^each **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. each [expr] USING [function reference] each [expr] (AS var1, var2...) [indent code block] else [indent code block] Example: 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 You can use __break__ to exit out of the loop prematurely, and __continue__ to shortcut to the start. See the documentation on these terms for more. ### EVERY - _general-purpose collection iterator_ - constructs - ^every **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). every [expr] USING [function reference] every [expr] (AS var1, var2...) [indent code block] else [indent code block] __Every__ 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__) You can use __break__ to exit out of the loop prematurely, and __continue__ to shortcut to the start. See the documentation on these terms for more. ### EXISTS - _conditional flow control_ - constructs - ^exists If the expression is _not_ __undefined__, the code directly after the **exists** is executed. Otherwise, perform additional tests if specified by any **elsif**, **elsexists** or **elsunless** clauses, or failing those, perform the code after the optional **else** clause. exists [expr] (AS var1, var2...) [indent code block] elsif [expr] (AS var1, var2...) [indent code block] elsunless [expr] (AS var1, var2...) [indent code block] elsexists [expr] (AS var1, var2...) [indent code block] else [indent code block] See __if__ for more details on th if/exists/unless constructions. The only thing that does _not_ exist are things that are __undefined__. ( the following information should be in the EXISTS keyword ) However, there is an annoying trick. You can set a variable's value to __undefined__, in which case the variable technically exists, it is just the _value_ of the variable that is undefined. debug newvar > undefined debug newvar.attribute // throws an error, can't look up an attribute on an undefined value set newvar blank debug newvar > {} debug newvar.attribute > undefined set newvar.attribute 'a value' debug newvar.attribute > a value set newvar.attribute undefined debug newvar.attribute > undefined debug newvar > { 'attribute': undefined } If you want to differentiate between an attribute that is not there and one that is there but holds the __undefined__ value, use `.hasOwnProperty` as follows debug newvar.attribute > undefined debug newvar.hasOwnProperty('attribute') > true delete newvar.attribute debug newvar.attribute > undefined debug newvar.hasOwnProperty('attribute') > false ### IF - _conditional flow control_ - constructs - ^if If the expression is _truthy_, the code directly after the **if** is executed. Otherwise, perform additional tests if specified by any **elsif**, **elsexists** or **elsunless** clauses, or failing those, perform the code after the optional **else** clause. if [expr] (AS var1, var2...) [indent code block] elsif [expr] (AS var1, var2...) [indent code block] elsunless [expr] (AS var1, var2...) [indent code block] elsexists [expr] (AS var1, var2...) [indent code block] else [indent code block] What is _truthy_? Any value which is not _falsy_. See [http://www.sitepoint.com/javascript-truthy-falsy/][1] for specifics; excerpted here. _Falsy_ values are: `false`, `0` (zero), `""` (empty string), `null`, `undefined`, and `NaN` (not-a-number). Everything else is _truthy_. The value used in the test of the **if** is available in the _pronoun_ **trial**, which can be reassigned using an **as** clause. if names[pk] debug trial // prints the contents of names[pk] if names[pk] as row debug row // prints the contents of names[pk] N.B. that __trial__ holds the value of the entire condition, not any sub-elements. if key is 'esc' debug 'the value of trial is: ' + trial // this prints 'true', not 'esc' #### else Specifies a block of code that will be executed if the condition leading to the previous block fails. Used with conditional statements **if/unless/exists**. if b debug 'b is truthy' else debug 'b is falsy' #### elsif _clause_ Combines __else__ and __if__. If the test succeeds, the tested value will be placed in the __trial__ pronoun. if b debug 'b is truthy: ' + trial // trial holds value of b elsif c debug 'b is falsy, but c is truthy: ' + trial // trial now holds c else debug 'neither b nor c is truthy.' #### elsexists Combines __else__ and __exists__. If the test succeeds, the tested value will be placed in the __it__ pronoun. exists a debug "a exists: " + it // it holds the value of a elsexists b debug "a does not exist, but b does: " + it // it holds the value of b else debug "neither a nor b exists." ### elsunless _clause_ Combines __else__ and __unless__. if a debug "a is truthy: " + trial elsunless b debug 'b is falsy! And there is no pronoun for this clause.' else debug 'a is falsy and b is truthy. No pronoun because else dosent' get one.' ### INDEX - _custom order iterator_ - constructs - ^index __Index__ iterates through an array or object sequentially using a separately provided collection of keys. The index is stepped through one row at a time, and the value of the object with that index is presented in the enclosed block of code. index [expr1 (, expr2, ...)] USING [function reference] index [expr1 (, expr2, ...)] (AS var1, var2, ...) [indent code block] else [indent code block] You provide a collection, and the index, separated by a comma. index [collection], [index] In pseudo-code, this happens: ply index as key with collectionkey as it [ code ] A real example: set fruit to traits a Apple, b Banana, c Cherry index fruit, list b, c, a, c debug it > Banana > Cherry > Apple > Cherry It is relatively easy to synthesize the function of __index__ using a normal iterator and __with__, nevertheless it is such a common pattern it makes sense to abstract it slightly for better clarity. __Index__ can accept an iterator as the index, in which case it will be stepped through until it is exhausted (or you __break__ out of the loop). If the index requests a key that does not exist in the collection, __undefined__ is presented to the block. In other words, missing values are neither skipped nor thrown as an error. #### index using __Index__ also offers the __using__ variant, where a function is called rather than a block of code executed. set tally blank set Tally to task as name inc tally ame set fruit to traits a Apple, b Banana, c Cherry index fruit, list b, c, a, c Tally it debug tally > { Banana: 1, Cherry: 2, Apple: 1 } You can use __break__ to exit out of the loop prematurely, and __continue__ to shortcut to the start. See the documentation on these terms for more. ### ITERATE - _sequentially examine values from a generator/iterator_ - constructs - ^iterate The **iterate** statement steps through each value in an _iterable expression_. This can be an object with fields/traits, an array/list with numbered elements, or a passive iterator/generator that **yield**s values for iteration. iterate [expr] USING [function reference] iterate [expr] (AS var1, var2...) [indent code block] else [indent code block] What’s an iterable expression? It’s an object that **yield**s values for iteration, or an object that on demand (via call to **[Symbol.iterator]** produces such an expression. (In ES6, native collection types based on **Array**, **Map**, and **Set** support lazy iteration.) This construct steps through each result of an _iterable expression_, passing it through a block of code via the **it** pronoun. If an **else** clause is present, that code is executed only if there is no iteration. Even though generators don't provide a key to match the value, when using a generator **iterate** nevertheless provides **key**, giving you the row number of each value returned. set seen to new ~Set seen.add 'horse' seen.add 'pig' seen.add 'horse' iterate seen debug '${key} - ${it}' > 0 - horse > 1 - pig #### iterate ... using This variation calls the given function with two parameters, the iteration value and the current row number: set fruit list Apple, Banana, Citron, Durian set SeeFunctionParameters to task debug $$ iterate (fruit iterate) using SeeFunctionParameters > { '0': 'Apple', '1': 0 } > { '0': 'Banana', '1': 1 } > { '0': 'Citron', '1': 2 } > { '0': 'Durian', '1': 3 } You can use __break__ to exit out of the loop prematurely, and __continue__ to shortcut to the start. See the documentation on these terms for more. ### PLY - _iterate over an array-like collection_ - constructs - ^ply The **ply** iterator sequentially steps through all elements in an _array-like_ collection; it works on any object that has a **length** trait. It accesses numeric traits from 0 to **length**-1, sending each trait value to the code block or function. If **length** is 0, the **else** clause is invoked instead. ply [expr] USING [function reference] ply [expr] (AS var1, var2...) [indent code block] else [indent code block] The trait value is captured in the **it** pronoun, and the trait number is captured as **key**. ply fruit debug '${key}: ${it}' else debug 'There is no fruit.' > 0: Apple > 1: Banana > 2: Citron #### ply ... using **Ply** can also call out to a function with the **ply using** variation. set handler to task given value, field, collection debug `${field}: ${value} ply fruit using handler The function is called for each element of the array with three parameters: - element value (__it__) - element index # (__key__) - a reference to the array itself You can use __break__ to exit out of the loop prematurely, and __continue__ to shortcut to the start. See the documentation on these terms for more. ### PROMISING - _asynchronous flow control for Promises_ - constructs - ^promising The __promising__ construct lends clarity to asynchronous operations using `promise`-based functions. promising [promise] resolve/reject [parameters] - or - promising [code that is turned into a promise] resolve/reject (parameters) - or - promising // with nothing following ... followed by one or more of ... then [promise] - or - then (promise parameters) [code that is turned into a promise] resolve/reject (parameters) catch [promise] - or - catch (promise parameters) [code that is turned into a promise] resolve/reject (parameters) finally [promise] - or - finally (promise parameters) [code that is turned into a promise] resolve/reject (parameters) any [array of promises] all [array of promises] rejected (expression) resolved (expression) finalize In one sense __promising__ is just a bit of syntactical sugar for using `chain` to connect `then` and `catch` etc in a series of chained method calls. And indeed, that's what it does behind the scenes. But simplifying syntax is not a bad thing, especially if it eases working with a more hat can be quite confusing. And Promises are certainly that. In order to make this construct work, you need to know what each clause requires, not only syntactically, but from a code point of view. We'll take each clause one by one. Most clauses in __promising__ are either a single line, which accept either a value that will be passed on to the next clause immediately, or an active promise (a _thenable_) that will be resolved before the next clause; or are multi-line, accepting a block of code that it will wrap in a Promise, as if it were the body of a function. First things first: the __promising__ clause must be the first clause in the construct, and you can only use it once. (You can start with either form of the __promising__ clause.) You can mix and match the other clauses in any order once you've started the __promising__. The final clause in the construct must be either __catch__ or __finally__ to ensure that errors are properly caught. However, you can use either of these multiple times in the same construct, in order to control execution flow between error handlers and the normal flow of code. #### promising promising [a value or a promise] - or - promising [code block that will be wrapped as a Promise] __Promising__ starts the clause. It expects an active Promise -- an object with `.then` and `.catch` ready for use. If it isn't a promise, it's just a value that will be passed forward. In this example, `db.listDatabases` is a function, and we invoke it with __from__ so it will return an active promise. promising !db.listDatabases In this form, the function you are promising must have been declared as a __promise__, or been created in or wrapped in a _Promise_ wrapper by a library or helper. In multi-line form, you're crafting the body of a __promise__ function, and you'll use __reject__ and __resolve__ to pass along asynchronous results to the next clause of the construct. promising https.get url, task set body '' $setEncoding 'utf8' $on 'data', task set body + $ $on 'end', task resolve body $on 'error', task reject $ Of course you could just take that bunch of code and wrap it in a __promise__ yourself, perhaps as part of some global object. But more likely you'd just find a package on npm that offers promisified fetchers ai Because you're smart. ##### promising with no block or parameter If you have an array of promises that you'd like to use with __all__ or __any__ then just start the structure with __promising__ with no parameter or block: promising all promiseArray catch debug "uh oh" #### then then [value or promise] - or - then (optional [GIVEN parameter list]) [code block that will be wrapped as a Promise] The __then__ clause continues the chain of asynchronous execution. Whatever you __resolve__ in the previous clause is given to the next __then__ clause as one (or more) parameters. (You can use the pronoun __$__ to refer to the first parameter passed without giving it a name.) then !db.dropDatabase dbName Which, by the way, does the exact same thing as this: then resolve !db.dropDatabase dbName The block takes the same form and acts the same way as the one in __promising__. Here's a fun note: when coding these _block form_ clauses, there is an automatic __resolve__ applied at the end of the code block, so execution carries onwards without you having to be explicit about it. So you can do this: promising debug "First this" then debug "Then this" reject "Ninja!" then debug "But not this." catch debug $ Which prints: First this Then this Ninja! Notice how `Ninja!` is transferred by passing the message to the __catch__ clause via __reject__. And also note the __$__ pronoun. The __catch__ clause could have been written either of these ways too. catch given msg debug msg catch debug $ #### catch catch [value or promise] - or - catch (optional [GIVEN parameter list]) [code block that will be wrapped as a Promise] As you'd expect __catch__ works just like the other line or block form clauses, except that it is only executed if a previous clause __rejects__ something. As illustrated in the above text! If there is a __then__ or __finally__ clause after __catch__ in the construct, it will be executed, unless you __reject__ again. #### finally finally [value or promise] - or - finally (optional [GIVEN parameter list]) [code block that will be wrapped as a Promise] This clause captures both main and error code paths in the Promise pseudo-thread, thus it is always executed regardless of whether the previous clause ended with a __reject__ or __resolve__. Support for __finally__ is dependent on whether the Promises library you are using supports it; SAI does not shim it for you. As of this writing, `Promise.finally` has not been ratified for inclusion in ES6, and node.js does not support it. #### all all [expression yielding an array of promises] - or - all The __all__ clause, along with __any__ is different than the other clauses. __All__ is a simple way of waiting for an array of promises to _all_ be resolved. It takes a single parameter -- an array -- and waits for each Promise in the array to be resolved. It uses the `Promise.all` helper to do this. The stand-alone version of all, that takes no expression, assumes the array has been sent to it from the __resolve__ in the previous clause. The following two lines are identical: all // all (implicit) all $ // all (explicit) The __all__ clause is great to use when you need to fetch multiple resources all at once and need to know when they're done. The following two line snippet fetches a list of databases (asynchronously) and then passes the resulting list __thru__ a function that creates multiple requests for more information about each database. The result is an array of promises that are used as a parameter to __all__. promising db.listDatabases all $ thru db.get(it, '/') __All__ resolves either with a list of the __resolve__ values of every promise in the array, or a _single_ value that is the _first_ rejection of any of those promises. In other words, __all__ will wait to __resolve__ until all of it's work done, but the instant something breaks, it __rejects__ everything and gives up. #### any any [expression yielding an array of promises] - or - any The __any__ clause is very similar to __all__, except that it either __rejects__ or __resolves__ based on the first event in all of the promises in its array. So while __all__ will wait to resolve for everything to be finished, __any__ will resolve when the first Promise it is monitoring does so. Like __all__, however, it __rejects__ on the first Promise to do so. #### rejected (only within a __promise__ function) rejected [expression] - or - rejected So you might be wondering how do you pass the results of a __promising__ construct to a Promise that contains it. After all, you cannot __resolve__ or __reject__ an outer promise from an inner one -- the rules of scoping forbid it. In this case, the last three clauses help you out. __Rejecting__ is internally equivalent to a __catch__ clause, in that any error conditions are caught by it. However, it then marks the Promise _containing_ the __promising__ construct as rejected. The bare word version of __rejected__ sends the error value it received to the containing Promise, while the version with an expression allows you to customise it. #### resolved (only within a __promise__ function) resolved [expression] - or - resolved Similarlly, __resolved__ will resolve a containing Promise with either an expression or a passed-on value. #### finalized (only within a __promise__ function) finalized And, most useful, the __finalized__ clause will take the current state of the __promising__ clause it is a part of and transfer it to the containing promise, kind of like a `return` statement. Finalized doesn't take a value because it passes on whatever value the last promise state has. If you need to override your return values you'll need to do so with rejected and/or resolved. ### STATE - _asynchronous finite state machine_ - constructs - ^state The __state__ construct creates an asynchronous state machine (or finite automata) that can be used for many different kinds of processes, from making requests or managing other machines, to controlling and responding to near-realtime events. state [expr] (AS var1, var2...) [indent code block] state [variable] Each declared __state__ is given a name (the value), and has an associated block of code. Each block is a function, with optional parameters. These functions are invoked with the __goto__ verb. The first parameter of __goto__ is the name of the state; any additional parameters are passed in to the state function. state [value1] (given [parameter list]) ... code ... goto [value] (, [parameters for next state]) ... code ... state [value2] (given [parameter list]) goto etc... ... repeats for as many states as supplied The uppermost defined __state__ in the construct is the starting state by convention, and it is invoked first. When you __goto__ a new state, you'll just be scheduling the next state to occur next. You must `return` if you want its execution to stop immediately. If you do not __goto__ another state, the machine ends (unless a callback into to a state happens asynchronously). State machine context is preserved until there is no more opportunity for a callback. *Note*: The __state__ construct runs asynchronously. Synchronous code execution passes the state machine definition instantly, and the first state executes on the next event loop tick via node's `setImmediate`. Each time you __goto__ a new state, that state is also scheduled asynchronously. While the event loop is quite fast (perhaps 100k state transitions per second on a laptop), you will want to avoid using the __state__ construct for applications that require high performance and/or that do not benefit greatly from asynchronous response. In these cases, a `while` loop around a `switch` statement is a good place to start. Context is preserved with the state machine, including the _current_ (this) object. You have access to all member variables/methods of the object instance that invoked the __state__ construct. Variables you `set` within any state are available to all states. However, `local` variables are restricted to the state they're defined in. (Examine the generated Javascript code for clarity.) Here is a simple example of a state machine, a Turing machine that adds two unary (bitcount) numbers starting at the read head, extending right and separated by a single 0 digit. set Readout to task // display Turing tape and current head position debug tape | into '' sum + ((key is head) ?? '[${it}]' :: ' ${it} ') state 'start' // turing machine to add two numbers, in this case 3 & 4. set tape list 0, 1, 1, 1, 0, 1, 1, 1, 1, 0 set head 1 goto 'a' state 'a' Readout if tapehead // seek end of first number set tapehead 1 inc head goto 'a' else // set bit between numbers set tapehead 1 inc head goto 'b' state 'b' Readout if tapehead // seek end of second number set tapehead 1 inc head goto 'b' else set tapehead 0 // rewind to end of second number dec head goto 'c' state 'c' Readout if tapehead // clear bit at end of second number set tapehead 0 dec head goto 'd' else debug "Should not be able to get here." goto 'halt' state 'd' Readout if tapehead // seek back to the beginning of the result set tapehead 1 dec head goto 'd' else set tapehead 0 // return to first set bit and halt inc head goto 'halt' state 'halt' Readout debug "Run complete." debug "State machine started." // appears first because STATE is asynchronous The output is: State machine started. 0 [1] 1 1 0 1 1 1 1 0 0 1 [1] 1 0 1 1 1 1 0 0 1 1 [1] 0 1 1 1 1 0 0 1 1 1 [0] 1 1 1 1 0 0 1 1 1 1 [1] 1 1 1 0 0 1 1 1 1 1 [1] 1 1 0 0 1 1 1 1 1 1 [1] 1 0 0 1 1 1 1 1 1 1 [1] 0 0 1 1 1 1 1 1 1 1 [0] 0 1 1 1 1 1 1 1 [1] 0 0 1 1 1 1 1 1 [1] 0 0 0 1 1 1 1 1 [1] 1 0 0 0 1 1 1 1 [1] 1 1 0 0 0 1 1 1 [1] 1 1 1 0 0 0 1 1 [1] 1 1 1 1 0 0 0 1 [1] 1 1 1 1 1 0 0 0 [1] 1 1 1 1 1 1 0 0 [0] 1 1 1 1 1 1 1 0 0 0 [1] 1 1 1 1 1 1 0 0 Run complete. There are many easier ways of programming a Turing machine without hardcoding everything like this. This one does show off the __state__ construct fairly nicely, however. #### goto Within the __state__ construct, the variable `goto` is defined as a function that schedules execution of the next state. (This trickery should be essentially invisible, but just in case you need to know how, this is how.) ##### Parameters with __goto__ __goto__ will pass any provide parameters on to the state function: state 'start' debug "You're lost in a cave!" dountil maze\pos is 'empty' set pos !Rand size goto 'move', pos // we're passing the new position here as POS state 'move' given newpos // and it is recevied here as NEWPOS inc moves set position !Valid newpos debug " move ${moves} ... now in room ${position}" goto maze\position #### then Similarly, __then__ is a function that _creates a function_ that immediately jumps to the next state rather than rescheduling another state transition. In other words, __then__ is a callback creator. Use __then__ like this: state 'empty' ply array position-1, position+1 as adjacent each features as dsc, feat if feat is maze[from Valid adjacent] debug dsc Timer 800, !then 'step' state 'step' goto 'move', position + !Step This example does some housekeeping, delays for 800ms, _then_ the timer callback jumps to the next state. ##### Parameters with __then__ Because __then__ creates a callback function, there are _two_ opportunities for arguments to be passed to the next state. 1. When __then__ is executed to create the callback. 2. When the callback is executed some later time. In the above example, Timer 800, !then 'step' This is the first opportunity for parameters to be passed. To make it clear, let's add one: ... Timer 800, !then 'step', !Step state 'step' given stepValue goto 'move', position + stepValue Parameters added at callback creation time are resolved immediately, even though they're not used until later. These parameters are invisibly bound into the callback function that is created. In the above example, `stepValue` is calculated 800ms before the state change to `step` occurs; this step value waiting in the callback defined when the `Timer` function is invoked. The second opportunity is when the callback is actually made. In our example, let's say the timer wishes to pass the real time it was resolved. And we want that data. But we also want our `stepValue`, which was passed in earlier. Fortunately, we can have both pieces of data. The callback function that __then__ creates will append any arguments it receives _as a callback_ to the arguments bound to it _on callback creation_. So we now have two parameters to our `step` state. state 'step' given stepValue, resolvedTime debug "Timer resolved at ${resolvedTime}" goto 'move', position + stepValue Just to make it clear: `stepValue` was calculated and passed into the callback generator when we started the timer, and `resolvedTime` was calculated and passed into the callback when the timer resolved. The callback concatentated them into one set of arguments for the call to the `step` state. If you unroll this, it looks something like this: then 'step', stepValue .. becomes .. callback invoking 'step' with stepValue .. which _later_ is invoked as .. callback resolvedTime .. which becomes .. invoke 'step' with stepValue, resolvedTime This works with any number of arguments provided at callback creation and resolution. Just be sure you know how many of each there are when you pick them up in the _state handler_. I hope that's clear. And useful! ### SWITCH - _choice-based conditional_ - constructs - ^switch Choose among alternatives based on expression equality. switch [expr] (AS var1, var2...) case [expr1 (, expr2, ...)] [indent code block] else [indent code block] The expression under evaluation is available to all codepaths as **trial** (which can be renamed with the **as** clause). Unlike the native Javascript `switch` these __case__ clauses do not fall through; the break is implicit. (Though you can break out early.) switch ~System.IO.Keypress() as key case 'n', 'N' Move 0,-1 case 's', 'S' Move 0,1 case 'e', 'E' Move 1,0 case 'w', 'W' Move -1,0 case ' ' Jump case '?' Help else Emit 'Key [${key}] isn't used; type ? for help.' The compiler will insist on an __else__ clause as a matter of good programming hygeine. ### THROW - _generate an exception_ - constructs - ^throw Trigger exception handling. throw [expr1 (, expr2, ...)] Example: try throw new Error 'Oh no!' catch return error.message You should probably look at the Javascript documentation. ### TRY - _handle exceptional errors_ - constructs - ^try Exception handling construct. try [indent code block] catch (AS var1, var2...) [indent code block] finally [indent code block] In addition to the standard Javascript stuff, SAI's "try" uses the __error__ pronoun within the __catch__ clause to expose the caught error, as seen in the example below. set file to new File 'output.log' try file.Open file.Write 'Hey, it's a log' catch return error.message finally file.Close For details, see the Javacript exception documentation. ### UNLESS - _conditional flow control_ - constructs - ^unless If the expression is _falsy_, the code directly after the **unless** is executed. Otherwise, perform additional tests if specified by any **elsif**, **elsexists** or **elsunless** clauses, or failing those, perform the code after the optional **else** clause. unless [expr] (AS var1, var2...) [indent code block] elsif [expr] (AS var1, var2...) [indent code block] elsunless [expr] (AS var1, var2...) [indent code block] elsexists [expr] (AS var1, var2...) [indent code block] else [indent code block] See __if__ for more details on th if/exists/unless constructions. Values that trigger an __unless__ clause are those that are _falsy_, that is: `false`, `null`, `undefined`, `0`, `NaN`, `''` (empty string). Objects are generally _truthy_, including empty arrays and blank traits. Another JS trap, the following is false: debug NaN is NaN > false The only way to check for NaN is to use isNan: debug isNaN NaN > true ### UNTIL - _loop over a block of code while an expression is false_ - constructs - ^until Executes the code block repeatedly, as long as the expression is false. This is essentially a **while** statement with the test inverted. until [expr] (AS var1, var2...) [indent code block] The basic **until** variation performs the test first, so there is a chance the code will not execute. The **dountil** variation executes the block first, then performs the test. until true debug 'You will never see this.' dountil true debug 'You will see this once.' **Until** _does not_ make its test value available for use as **it**. Because the code block only executes when the test value is _falsy_, there’s really no point. You can use __break__ to exit out of the loop prematurely, and __continue__ to shortcut to the start. See the documentation on these terms for more. ### WHILE - _loop over a block of code while an expression is true_ - constructs - ^while Executes the code block repeatedly, as long as the expression is true. while [expr] (AS var1, var2...) [indent code block] The basic **while** variation performs the test first, so there is a chance the code will not execute. The **dowhile** variation executes the block first, then performs the test. while false debug 'You will never see this.' dowhile false debug 'You will see this once.' **While** makes its value available for use as **it**, as below. while file.NextLine() @story.push it **Dowhile** _does not_ use **it**, because the expression is not evaluated until after the first pass through the code, thus the first **it** result would always be **undefined**. You can use __break__ to exit out of the loop prematurely, and __continue__ to shortcut to the start. See the documentation on these terms for more. ### WITH - _block-level pronoun assignment_ - constructs - ^with Allows the use of the **it** pronoun (and the `.` scoping prefix) within an arbitrary block of code. with [expr] (AS var1, var2...) [indent code block] For example: with customer set label to ''' ${.name} ${.address1} ${.address2} ${.city} ${.region} ${.postcode} Another example, this time using an assigned pronoun: with category\document\page as p set precis: title p.title authors join!p.authors ', ' summary p.abstract ? p.summary Though that is much the same as this: local p category\document\page set precis: title p.title authors join!p.authors ', ' summary p.abstract ? p.summary __WITH__ can make things tidier, but mind what your pronouns are.