UNPKG

sai-language

Version:

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

1,753 lines (1,117 loc) 51.1 kB
# This file is automatically generated. _Do not edit._ Unknown Heading ### $$ - _function arguments pseudo-array_ - keywords - ^$$ Equivalent to Javascript’s `arguments` and can be used the same way. .. $$ Best to refer to good documentation on this elsewhere. ### INFINITY - _constant representing infinity_ - keywords - ^Infinity Represents an infinitely large value. Generally obtained by attempting to evaluate an expression with a divisor of zero. debug 1/0 > Infinity ### Iterator - _name-of-the-Iterator method_ - keywords - ^Iterator When creating an object you want to provide an invisible iterator for, use this keyword as the name of the process. Iterator process (as [parameter list]) [block] This will be a little gritty, because ES6 is gritty. When you name an object process **Iterator** (note the specific uppercase initial), the function won't be assigned to the object trait `object.Iterator`. Instead, it will be assigned to `object[Symbol.iterator]`. The bad news is that this means you can't specifically refer to that trait by name in SAI without using the convoluted approach of `object[~Symbol.iterator]` (because **Symbol** is a global and globals must use the global scoping prefix). The good news is this means you probably won't ever *have to* refer to it by name, because all of SAI's comprehensions, as well as the **iterate** statement, and ES6's `for-of` loop, all implicitly check for the existence of the `Symbol.iterator` function and will use it if present. object Tally 1.0.0 instance: bag empty Count task set bag[$] to (self default 0) + 1 Iterator process each bag yield: key, it set inventory to create 'Tally' inventory.Count 'apple' inventory.Count 'banana' inventory.Count 'apple' iterate inventory debug it > [ 'apple', 2 ] > [ 'banana', 1 ] In the example, when we **iterate** over `inventory`, a check is made to see if the object to iterate has an **Iterator** process, which in this case it does, so it is called, and then iteration takes place over the result. See the **iterate** construct for more on this. Note that this sleight-of-hand doesn't apply to an object **task** (as opposed to **process**) named "Iterator"; in fact SAI will not let you use that name for an object task, because doing so would break expectations of what an iterator is supposed to do (e.g. yield things). Gah, the cruft, it burns. ### NaN - _not-a-number_ - keywords - ^NaN **NaN** (capitalization matters) means Not a Number. .. NaN **NaN** is returned from some library calls on failure to convert a value into a number. (The **number** operator returns 0 instead.) debug ~parseInt('The one ring.') > NaN debug number 'The one ring.' > 0 The _only_ way to test for **NaN** is to use the **isNaN** operator. debug NaN = ~parseInt('The one ring.') > false // ! debug NaN is ~parseInt('The one ring.') > false // !!!! debug isNan ~parseInt('The one ring.') > true ### ARRAY - _expression-oriented list literal_ - keywords - ^array 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. .. array [expr], [expr], ... (;) .. array [expr], [expr], ... [expr] ... 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 - _pronoun renaming clause_ - keywords - ^as 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.) .. [pipe operator] as var1, var2, etc... .. [construct] as var1, var2, etc... .. ( [expression] as var 1) **As** can be used in conjunction with almost any SAI function that creates pronouns. You use __as__ to give those pronouns specific names, rather than the generic ones. Named pronouns are generally ephemeral. // incomplete list 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 Every construct that can accept __as__ pronoun renaming will indicate this in the documentation. #### as with iterators // incomplete list iterate [expr] (as [it var]) each [expr] (as [it var] (, [key var] ) ) ply [expr] (as [it ident] (, [key var] ) ) count [expr] (as [counter 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 // incomplete list .. | by (as [a var], [b var] ) .. | thru (as [it var] (, [key var] ) ) .. | audit (as [it ident] (, [key var] ) ) .. | into [expr] (as [sum var] (, [it var] (, [key 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 ] ### BLANK - _an object with no traits_ - keywords - ^blank The keyword **blank** creates a plain object with no traits. .. blank It is the SAI equivalent of Javascript’s `{}`. set player to blank set player.age to 21 debug player > { age: 21 } Contrast __blank__ with __empty__. A whiteboard can be blank; having nothing written on it. You would not usually say it was empty. While an array or a list is usually thought of as empty, rather than blank. ### CHAIN - _verb chain initiation_ - keywords - ^chain summary .. chain [expr] // in an expression [comprehension/method] [comprehension/method] ... chain [expr] // as a verb [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 pipe operators: debug chain friends | has .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 pipers 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. ### CONTRACT - _requirements for child objects_ - keywords - ^contract When defining an object, **contract** is used to specify tasks or traits that _children_ inheriting from this object are intended to implement. contract: [task/trait name] [task/trait name] ... 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’.” ### DEC - _decrement verb_ - keywords - ^dec Decrements (reduces by 1) the value in the given variable. dec [var] Example: set a to 2 dec a debug a > 1 ### DELETE - _a verb that deletes variables_ - keywords - ^delete summary delete [variable] When called on a variable or traits, undefines the named variable or attribute(s) of a variable. delete thisUnwantedValue delete goodObject.unwantedAttribute This just calls the underlying Javascript version of **delete**, with all the same rules and caveats. #### delete pipe operator The __delete__ pipe operator has advanced functionality, which see. ### EMPTY - _an array with no items_ - keywords - ^empty A literal value indicating an empty list/array. .. empty The equivalent of Javascript’s `[]`, **empty** creates an Array with no elements. debug empty debug empty.length > [] > 0 ### ENUM - _literal number enumeration_ - keywords - ^enum A convenience tool for initializing objects with sequential values. 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} ### EXISTS - _check for undefined_ - keywords - ^exists Returns true if the following expression is _not_ `undefined`. .. exists [expr] These three comparisons test the same thing and have the same effect: if undefined isnt var ... if exists var // unary operator ... exists var // flow control construct ... The unary operator can be used in expressions, e.g. unless exists .name and exists .age return: error "Name and age are required." ### EXPECTS - _runtime paramater type validation_ - keywords - ^expects 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. ... task/promise/process expects [rules definition] 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 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 in a function call results in an error being thrown describing the problem. Here's an example: 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 Expects adds a small amount of overhead at runtime. My thinking is that it would be good for integration testing but during a production build the checks would be commented out, like one might do with assertions. This would be a nice option to add to the SAI compiler someday. ### FALSE - _boolean value false_ - keywords - ^false This is the `false` value. .. false The opposite of __true__. ### FIELDS - _structure literal_ - keywords - ^fields Used to specify the creation of a plain object with a set of key/value pairs. (Contrast with **traits**.) .. fields [key] [expr], [key] [expr], ... (;) .. fields [key] [expr], [key] [expr], ... [key] [expr] ... 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 // no comma at end of line 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. ### GET - _dynamic object attribute_ - keywords - ^get Declares a _getter_ for a dynamic object trait; that is, a trait that has a value that is only calculated upon request. object [name] [version] [ident] get [code] return [value] ( set ( given [ident] ) [code] A brief example: object Vector2 1.0.0 magnitude get // dynamic getter for 'magnitude' trait return Math.sqrt(x*x + y*y) set given m // dynamic setter for 'magnitude' trait set a to angle set x to m * Math.cos(a) set y to m * Math.sin(a) You can have a getter without a setter, and a setter without a getter. documentation get return ''' Lots of information about this object. tally set given token inc _tally oken ### GIVEN - _parameter declaration and static values_ - keywords - ^given Use __given__ to name the parameters to a function, and to define immutable traits. .. given p1, p2, p3 ... - or - [ object declaration ] given [definition of values] To declare parameters: set [ident] (given [$ var]) // dynamic trait .. task (given [parameter1], [parameter2], ...) .. process (given [parameter1], [parameter2], ...) .. promise (given [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 } #### given object constants Defines immutable traits when declaring an object. These traits are assigned to the object prototype itself and locked/frozen; they cannot be changed and yet are available in every instance of an object. Givens are useful for static data. object Apple instance: varietal 'unknown' given: species 'M. domestica' Describe task return 'undefined var undefined .. set apple to create 'Apple' set apple.varietal to 'Macintosh' debug !apple.Describe > M. domestica var Macintosh **Given** traits can only be changed/overridden through inheritance. object Crabapple inherit: Apple given: species 'M. coronaria' .. set specimen to create 'Crabapple' debug !specimen.Describe > 'M. coronaria var unknown' ### INC - _value increment_ - keywords - ^inc Increments (adds 1 to) the given variable. inc [var] As in: set a to 1 inc a debug a > 2 The __inc__ statement has special behaviour around values that are _undefined_ or evaulate to _falsy_; when you increment one of these values, it becomes `1`. For example: set a to undefined inc a debug a > 1 This is useful when tallying with an array, e.g. the following construct automatically initializes array elements the way the standard `++` operator does not, such that the `tally` object will contain a log of word usage. set tally blank every word inc tallyit Note that __dec__ does not have similar initialization functionality. ### INHERIT - _class heritage specification_ - keywords - ^inherit Specify inheritance during object definition. inherit: [classname] ... An object can have multiple parents at each level of inheritance, as in the following example. object Licensed instance: licenseTag false .. object Passengers instance: paxCount 0 contract: paxMaximum .. object Automobile inherit: Licensed, Passengers .. object Sedan inherit: Automobile instance: paxMaximum 6 .. set myCar to create 'Sedan' debug myCar > { licenseTag: false, paxCount: 0, paxMaximum: 6 } debug myCar.isa > Sedan debug myCar.isof > { Licensed: { version: '0.0.0-unspecified', isa: 'Licensed', load: './sample/Keywords/Licensed.sai' }, Passengers: { version: '0.0.0-unspecified', isa: 'Passengers', load: './sample/Keywords/Passengers.sai' }, Automobile: { version: '0.0.0-unspecified', isa: 'Automobile', load: './sample/Keywords/Automobile.sai', inherit: [ 'Licensed', 'Passengers' ] }, Sedan: { version: '0.0.0-unspecified', isa: 'Sedan', load: './sample/Keywords/Sedan.sai', inherit: [ 'Automobile' ] } } Note the use of **contract** in the _Passengers_ object; this requires any child object to offer a `paxMaximum` trait. Contracts can be fulfilled by class functions (**task**/**process**/**promise**), class traits (**given**) or **instance** traits. ### INSTANCE - _object instance variable declaration_ - keywords - ^instance Define initial values for an object’s traits. instance: [varname] [initial value] ... When an object is created, instance traits are assigned the given values before the object’s **Instantiate** task is called. object Sock 1.0.0 instance: colour 'Brown' pattern 'Argyle' size 'M' kind 'dress' Instantiate task debug @ select list colour, pattern, size, kind .... set aSock to create 'Sock' > { colour: 'Brown', pattern: 'Argyle', size: 'M', kind: 'dress' } Instance traits, when declared with __instance__ or __given__, do not need to have their scope indicated with `@` when used in their own object. Said another way, declared instance variables are automatically scoped to the object. This is similar to the way that __reference__ values don't need to use the `~` global scoping prefix. The following are equivalent: instance: x 0 y 0 ... set x 34 set @x 34 // this is not necessary When referring to another object's instance variables, you will need to properly scope them: CopyPoint given point set x to point.x set y to point.y Instance variables cannot be initialized with functions or "complex" expressions. The compiler will warn you about it. Nevertheless, you should still declare all instance variables, even if you must start them with `undefined`. That's because the compiler looks at the instance variables you've declared to know what your instance variables are. object AppleCrate instance: sku '448893003' label ~GetLabel(.sku) /// this won't work, instead: instance: sku '448893003' label undefined Instantiate task set label ~GetLabel(.sku) ### .isa - _object name attribute_ - keywords - ^.isa All prototyped SAI objects have an **isa** trait that identifies the object name. .. [object].isa Further details about the object and its inheritance are found in the **isof** trait. object Fruit 1.0.0 Instantiate task debug 'I am a ${isa}' set a to create 'Fruit' > I am a Fruit object Pear 1.0.0 inherit: Fruit set b to create 'Pear' > I am a Pear ### LIST - _literal-based array construction_ - keywords - ^list Used to specify the creation of a plain array of literal values. .. list [term], [term], ... (;) .. list [term], [term], ... [term] ... As opposed to **array**, which is an array of mathematical expressions. The following are acceptable terms: true or false number literal string literal quoted with ' or " or ` or ''' unquoted bare string literal all characters available except } , ) ; | [cr] (note the comma, that will trip you up; comma separates terms) an equal sign (=) followed by an expression a nested definition starting with :, list, array, traits, or fields Lists may be specified on one line: debug list Apple, Banana, Citron > [ 'Apple', 'Banana', 'Citron' ] Or multiple lines in the form of an indented block, or a combination: debug list Vladimir, Estragon Pozzo, Lucky, The Boy > [ 'Vladimir', 'Estragon', 'Pozzo', 'Lucky', 'The Boy' ] When using a list 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 list Vash, Spike, Jack, Cat thru '${it} the cat' > { 'Vash', 'Spike', 'Jack', 'Cat the cat' ] // undesired result debug list Vash, Spike, Jack, Cat; thru '${it} the cat' // with semicolon > [ 'Vash the cat', 'Spike the cat', 'Jack the cat', 'Cat the cat' ] Or enclose the array in parenthesis: debug (list Vash, Spike, Jack, Cat).length > 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. ### LOCAL - _locally scoped variable declaration_ - keywords - ^local Create a local variable that exists only within the current level of indent (or deeper). Uses Javascript's `let` declaration. (And this is the only construct in SAI that does so.) local [varname] to [expr] Syntax identical to **set** however the variable created is limited to the current block in scope. (Normally, variables are scoped to the object method they’re used in.) **Local** causes the generated Javascript to declare variables with the `let` keyword. Javascript's scoping around global/local variables can be tricky and has caught even experienced programmers multiple times. **Local** allows you to do dumb things like this: set a 1 if a is 1 local a 2 debug a debug a > 2 > 1 In general, SAI is designed such that you should only very rarely need __local__ variables. ### MAIN - _indicates object should be instantiated at runtime_ - keywords - ^main When using the compiler, native Javascript files of objects created with **main** will automatically instantiate a single copy of the object when the file is required. object [objectname] main [objectversion] In other words, when the `.js` file is required, in addition to defining a prototype, an object is also instantiated (which causes any **Instantiate** task to run, thus conceivably launching a program). Main also sets a flag in the **isof** property of the object, indicating it is the main object. Have a look at the `bin` folder for `runner.sai` which uses the __main__ keyword to indicate it is a program that should be run rather than just an object prototype. ### NOP - _no-operation statement_ - keywords - ^nop No operation; no data; do nothing. nop This statement (or one like it) is necessary as a syntactical placeholder when a white-space indent is expected but you do not wish to put code in it. if x>26 nop elsif x>13 set x - 13 elsif x>0 set x + 13 ### NULL - _a value that is no value_ - keywords - ^null An empty value. .. null Null is: - _falsy_ - not `undefined` I don’t have any example code for this. ### OBJECT - _object declaration_ - keywords - ^object Begins the definition of an object. object [identifier] (main) [version] ... Only one object definition is permitted per file. In an object definition, the following sections are supported. See each keyword for more details. Also review the _Defining an Object_ document. reference: [globally defined references] object [identifier] (main) [version] inherit: [list of objects to inherit from] contract: [list of traits that child objects must provide] given: [declaration of immutable object traits] instance: [declaration of initial trait values for each instance] get [trait name to implement dynamically] [code] set given [value] [code] [name] task/process/promise [code that implements this task/process/promise] When a SAI object is created, the **Instantiate** task is executed on that obejct, allowing you to perform instance-level initialization code. Notice the caps; by convention SAI methods are capitalized, while attributes are lowercase. If the **main** clause is included, the object is marked to be instantiated automatically when a compiled `.js` version of the object is required. See **main** for slightly more detail. ### ORPHAN - _rescoping this_ - keywords - ^orphan **Orphan** causes the __@__ scoping prefix within the function to refer to the context of the function call, instead of the context of the function definition. orphan You probably want to use this in combination with **local** variable definitions. For clarifications and example code on this, see **task**. To do this on a case-by-case basis, use the __@@__ scoping prefix (which see). ### PROCESS - _generative function definition_ - keywords - ^process Creates a function that is expected to **yield** one or more values. [identifier] process ( as [parameter list] ) [yielding code block] .. process ( as [parameter list] ) [yielding code block] This is an ES6 feature. **Process** does exactly the same thing as **task** except the generated code uses `function*` instead of `function`. If you're not familiar with generators/yielding, here is a very simplified overview. set Candidates to process count 1 to 100 as x count 1 to 100 as y if 1<x and x<y and x+y<100 yield: x x, y y, s x+y, p x*y The Candidates process, when invoked, runs all of the code in the process up until the first **yield**, then returns, handing you a process object, here stored in the variable `iter`: set iter Candidates() That object is an _iterator_; it is a stateful representation of a `Candidates` process. Each time you call a process, you get a new iterator. Once you have an iterator, you can _iterate_ through it. Each iteration returns the next value _yielded_ by the process. Iteration takes place by calling the **.next** task on the iterator. debug iter.next() > { value: { x: 2, y: 3, s: 5, p: 6 }, done: false } You can see that **.next** returns a simple object with two fields, **.value** and **.done**. (In our iterator, the value is the first qualified candidate represented by a simple object with four fields: x, y, s and p.) **.value** holds whatever was **yield**ed. **.done** is a boolean flag that indicates whether the iteration is complete; that is, whether another call to **.next** would result in a newly generated value. Each time you call **.next** you will get a new value. debug iter.next() debug iter.next() debug iter.next() > { value: { x: 2, y: 4, s: 6, p: 8 }, done: false } > { value: { x: 2, y: 5, s: 7, p: 10 }, done: false } > { value: { x: 2, y: 6, s: 8, p: 12 }, done: false } Here is a bit of code that will keep calling **.next** until the **.done** flag is set, saving the result in a four item list. set cache to empty // empty array dountil result.done // check .done at the end of each loop set result iter.next() // next iteration to result cache.push result // save result in cache debug cache | limit -4 // print last four rows > [ { value: { x: 48, y: 50, s: 98, p: 2400 }, done: false }, { value: { x: 48, y: 51, s: 99, p: 2448 }, done: false }, { value: { x: 49, y: 50, s: 99, p: 2450 }, done: false }, { value: undefined, done: true } ] Once the **.done** flag is set, the iterator is said to be _exhausted_. Notice that **.value** is *undefined* when the **.done** flag is set. Processes, generators and iterators are very powerful tools because they allow computations to be *lazy*, that is, for data to be generated only as it is needed, thus vastly reducing memory requirements. (The Python language is very useful for scientific computing because it is a primarily lazy language because large data sets can be manipulated with modest resources.) SAI has many affordances for processes and iterators, including native support with all comprehensions. As a very simple example, all of the above code could be replaced with this simple expression: debug Candidates() | limit -4 > [ { x: 48, y: 49, s: 97, p: 2352 }, { x: 48, y: 50, s: 98, p: 2400 }, { x: 48, y: 51, s: 99, p: 2448 }, { x: 49, y: 50, s: 99, p: 2450 } ] (Notice how the limit comprehension itself deals silently with **.next** and **.done**, and automatically gives you just a list with only the **.value** of each iteration.) ### PROMISE - _declare a Promise-producing function_ - keywords - ^promise Wraps the code block in a Promise-like function shell. object [identifier] promise ( as [parameter list] ) [code block] .. promise ( as [parameter list] ) [code block] Along with **resolve** and **reject**, forms a convenient bit of syntactic sugar for making Promise-like functionality. Here is some sample code: set willIGetNewPhone promise given isMomHappy if isMomHappy resolve: brand 'Wangdoodle' colour 'paisley' else reject new ~Error 'Mom is not happy.' showOff promise given phone with phone debug 'Hey friend, I have a new ${.colour} ${.brand} phone' askMom task given happiness chain willIGetNewPhone(happiness) then showOff catch promise given e debug '${e} -- No phone for you.' askMom true askMom false > Hey friend, I have a new paisley Wangdoodle phone > Error: Mom is not happy. -- No phone for you. The way **promise** works is best explained by just showing you what happens when you use it. We wrap the code block in a Promise constructor, as follows: set doThing to promise given url HeyServer url, task given request, result if result.success resolve result else reject result .. debug doThing.toString() > function (p) { > return new Promise(function($_resolve, $_reject) { > var $url = p; > $HeyServer($url, function(p, $result) { > var $request = p; > if (($56 = ($result.success))) { > $_resolve($result); > } else { > $_reject($result); > } > }); > }); > } So basically it throws a **Promise** wrapper around the function and allows you to use **resolve** and **reject** as statements. (The $56 in there is housekeeping for the **trial** pronoun; V8 optimizes unused assignments like this away.) ### REFERENCE - _declare global values_ - keywords - ^reference Import/declare global variables at the top of a **.SAI** source file. reference: [name] [expr] ... This is the only way to make global variables; inside a reference declaration. The syntax is the same as a **fields** structure definition, with name/value pairs separated by commas or newlines. reference: MIN 0, MAX 100 express require('express') Vector2D 'Vector2D ^1.0.0' count to MAX debug key .. set @app express() .. set origin to create Vector2D 0, 0 .. Note: you cannot assign to reference variables or re-use them as locals. The following lines would produce compile errors given the above references: local Vector2D set MIN -50 ### REJECT - _mark a Promise as having failed_ - keywords - ^reject Call the failed potential outcome for a function declared with **promise**. reject [expr1 (, expr2, ...)] reject This does not implicity end execution, however, the way **return** does. For an example, see **promise**. ### RESOLVE - _mark a Promise as succeeding_ - keywords - ^resolve Call the successful potential outcome for a function declared with **promise**. resolve [expr1 (, expr2, ...)] resolve Doesn't implicitly end execution the way **return** does, so make sure you mind the code path. For an example, see **promise**. ### RETURN - _exit a function, returning a value maybe_ - keywords - ^return Return a value to the caller of a **task**. return [expr1 (, expr2, ...)] return Works just like Javascript. set multiply to task given a, b return a*b debug multiply(2, 4) > 8 ### SET - _variable assignment_ - keywords - ^set Set is a busy keyword used in many situations where a result is calculated and then stored. see below for syntax reference #### set dynamic trait declaration [identifier] set ( as [ident] ) [code block] ( get [code block] ) Declare a _setter_ for a dynamic object trait. Cannot be used inside a function body. This example object implements *Distance*, storing the value normalized to meters. You can get and set the value in centimeters and miles through the use of dynamic traits. object Distance 1.0.0 instance: meters 0 centimeters set given val set meters to val/100 get return meters * 100 miles set given val set meters to val * 1609.344 get return meters / 1609.344 And in use: set trip to create 'Distance' set trip.meters to 500 debug trip.meters // > 500 debug trip.centimeters // > 50000 debug trip.miles // > 0.31068559611867 set trip.miles to 10 debug trip.meters // > 16093.44 #### set value assignment set [ident] to [expr] set [ident] [expr] // the TO keyword is generally optional but it's a good idea to use it set [ident] ![function] ( [parameters] ) set [ident] from [function] ( [parameters] ) set [ident] [operator] [expr] set [ident] [unary operator] Assign a value to an identifier. That's right, you don't use the equals sign for assignment. Ugh, whose idea was that anyway? set a to 2 set a from Math.pow self, 2 set a Math.sqrt(a) set a * 4 set a - debug a > -8 #### set multiple array value assignment If you separate multiple identifiers with commas on the left, the assignment will assign each array element on the right, starting at 0, to each identifier. As example: set a, b, c to: 1, 3, 5 For a fruity example: set a, b, c to fruit debug b > Banana This is useful when dealing with small tuples or parsed data: GetPixel task given x,y return buffer.slice( (x+y*width)*4, 4 ) set r, g, b, a to GetPixel(x,y) This compiles to a Javascript construct similar to the following: var $_=GetPixel(x,y); $r=$_[0]; $g=$_[1]; $b=$_[2]; $a=$_[3]; #### Scoping and Set All un-decorated, e.g. bare (`a` as opposed to `$a`), variables are scoped to the object method they're **set** in. For tighter scoping, you can use **local** in place of **set**, this causes the variable to be declared in-place using the Javascript keyword `let`. _Be careful with variable scoping._ set a 1 if a is 1 local a 2 debug a debug a > 2 > 1 #### set pipe operator Documentation under separate cover. ### SUPER - _superclass function invocation_ - keywords - ^super Call the previous ancestral version of the current object method. super ( [ parameters ] ) .. super ( [ parameters ] ) This doesn't happen by default; if you want to chain backwards up the inheritance tree you must do it with **super**. Note there is *no other way* to access an overridden ancestral object method than by using **super** within that specific overriding method itself. SAI is very strict about inheritance that way. Don't even bother exploring the prototype chain. object Parent instance: name 'unknown' Instantiate task given name set @name to name object Child inherit: 'Parent' instance: age 'unknown' Instantiate task given name, age super name set @age to age set billy to create 'Child' 'Billy', 12 debug billy > { name: 'Billy', age: 12 } Contrary to what you might expect, **super** _does not_ use the **@** scoping prefix; that's because super _always and only_ refers to the current object. You cannot call **super** on any other object. If you want to pass arguments to a **super** method without specifying them discretely, the following really ugly idiom gets the job done. super.apply @ $$ ### SWAP - _value swapper_ - keywords - ^swap Exchanges the values. swap [lvalue1] [lvalue2] Intended as syntactic sugar, collapsing three ugly lines into one simple line. swap a b This is functionally equivalent to the following Javascript: var temp1=a; var temp2=b; b=temp1; a=temp2; ### TASK - _define a function_ - keywords - ^task Define a block of code as an object trait or anonymous function. [identifier] task ( given [parameters] ) / ( expects [parameters] ) [code] .. [task] ( given [parameters] ) / ( expects [parameters] ) [code] It’s probably best to explain this by showing what Javascript is made. #### task trait The first example is a task trait on an object. It takes two standard parameters, name and value. Instrument task given name, value debug `${@context} ${name}: ${value} Generated code: function (p, $value) { var $name = p, $ = this; console.log('' + $.context + ' ' + $name + ': ' + $value); } You’ll note that the first parameter in a SAI-generated function is always `p`, which is explained below. More importantly, however, note the assignation `$ = this`. In SAI-generated code, the `$` variable is used as a buffer for the _this_ context. When executing a task trait, SAI captures the current _this_ with `$=this` to provide a reliable context for anonymous functions. #### anonymous task Here’s the same task but for anonymous use: set anon to task given name, value debug `${@context} ${name}: ${value} Generated code: function (p, $value) { var $name = p; console.log('' + $.context + ' ' + $name + ': ' + $value); } Notice how the anonymous task doesn’t capture `this.` into the `$` variable the way the trait task does. Thus, anonymous tasks automatically bind to the context that created them. This is usually what you want when creating anonymous tasks, and is why the `var self=this` idiom has so much traction in Javascript. SAI builds this idiom into the language. However, if you don’t want this behaviour for a particular anonymous task, include the **orphan** statement in it. set anon to task given name, value orphan debug `${@context} ${name}: ${value} Generated code: function (p, $value) { var $name = p; var $ = this; console.log('' + $.context + ' ' + $name + ': ' + $value); } Orphan anonymous tasks bind to the _calling_ context, rather than to the context that created them. #### positional vs. named parameters The second example is a task which expects two named parameters, $angle and $magnitude. As shown above, the first parameter in generated code is always `p`. Named parameters are passed as traits within `p`. SetPolar task expects $angle, $magnitude set x to $magnitude * Math.cos($angle) set y to $magnitude * Math.sin($angle) Generated code: function (p) { var $ = this; _$AI.expectsThrow(p, { "angle": true, "magnitude": true }, 'SetPolar'); $.x = (p.magnitude * Math.cos(p.angle)); $.y = (p.magnitude * Math.sin(p.angle)); } When you use **expects**, a call to _expectsThrow_ is included to validate your assumptions. This task is called by naming the parameters: SetPolar angle 45o, magnitude 3 Which generates this code: $.SetPolar({ angle: 0.7853981633974483, magnitude: 3 }); Notice how named parameters are encapsulated in a plain JS object and passed in the first function call argument (which is always named `p`). It is not necessary to use the **expects** clause to used named parameters. All **expects** does for you is check to see if the names (and types) are as expected. Here is the function without it: SetPolar task set x to $magnitude * Math.cos($angle) set y to $magnitude * Math.sin($angle) Generating this: function (p) { var $ = this; $.x = (p.magnitude * Math.cos(p.angle)); $.y = (p.magnitude * Math.sin(p.angle)); } The use of named parameters with the **$** scoping prefix generates code that assumes the first argument `p` will have the expected named traits. Refer to the entry on **expects** for more on what it does. ### TO - _set clause syntax_ - keywords - ^to __To__ is used in __set__ statements to separate the value being set from the value itself. set [lexpr] to [rexpr] It is almost never necessary to actually use __to__ because the parser can almayst always figure out what's going on. Still, it looks clearer, so you should use it. set a to 5 set a 5 Here's the only common case where there is confusion in the parser: set a - 5 What does that mean? set a to -5 set a self - 5 The compiler complains about this construct specifically, and makes you specify. ### TRAITS - _declare a structure of literal strings_ - keywords - ^traits Used to specify the creation of a plain object with a set of key/term pairs. .. traits [key] [term], [key] [term], ... (;) .. traits [key] [term], [key] [term], ... [key] [term] ... (Contrast with **fields**.) When you want to be specific about creating a plain object from a set of of keyed values & literal terms, use **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 **term** is any valid term (see **list** for a description of valid terms). See **fields** for examples. ### TRUE - _boolean value true_ - keywords - ^true The value TRUE, as in truthy, or not false. .. true This is what all those __if__ statements test for, the holy grail itself. ### UNBOUND - _mark a method as context-free_ - keywords - ^unbound An __unbound__ function is not 'attached' in any way (other than by name) to any instance of its object. object ObjectName [name] unbound task [name] unbound process [name] unbound promise When you declare an object function as __unbound__ the function no longer checks to see if it has become orphaned in execution -- that is, if it's _this_ object is no longer what it is supposed to be -- on a call to that function. Indeed, the _this_ context is specifically undefined in an __unbound__ function. __Unbound__ functions cannot reference bareword (unqualified) instance or given variables, or normal (bound) object functions. (They can access them with direct context, see the example below.) They can also call other unbound functions. object Thing instance: name 'Fred' Bound task debug name // Fred Unbound Unbound2 @ Unbound3 @ Unbound task debug name // COMPILE ERROR, cannot use bareword instance variable within unbound task Unbound2 task given o debug o.name // This is an access with direct context: the variable o. This prints 'Fred' Unbound3 task given o Unbound2 o // This is okay, calling an unbound task within an unbound task Bound // COMPILE ERROR, cannot call bareword bound function from an unbound task o.Bound // But this is okay, because it's got a direct context. So the question is why the heck would you ever mark a function __unbound__ and the answer is sometimes when you're using callbacks, you might not be able to pass in a bound function. Also, depending on your JS compiler, there may be a slight performance gain in marking small, often used utility functions as __unbound__ so there is no need to perform binding checks. It is reasonably safe to just declare things __unbound__ if you think you might want the benefits; the compiler will holler if it thinks you're making a mistake. ### UNDEFINED - _the absence of a place_ - keywords - ^undefined A literal indicating there is no variable, or no place for one. .. undefined This compiles directly to Javascript’s `undefined`. ### USING - _function reference clause_ - keywords - ^using Indicates in many looping constructs that logic is to be provided by a separate callback function. .. using [function reference] Many pipe operators and loops provide a __using__ variant. Have a look on the specific constructs to see how it's used. ### YIELD - _process product_ - keywords - ^yield Produces the _next_ value in a __process__. yield [expr1 (, expr2, ...)] Used with functions defined as **process**; see that keyword in this documentation, and the EcmaScript 6 documentation for details. set OddNumbers to process set i to 1 while true yield i set i + 2 set myOdds OddNumbers() debug myOdds.next debug myOdds.next debug myOdds.next > { value: 1, done: false } > { value: 3, done: false } > { value: 5, done: false } iterate OddNumbers() | limit 4