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
Markdown
#
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.