sai-language
Version:
An object-oriented language designed to afford code comprehension and maintenance. Transpiles in-place to Javascript.
1,715 lines (1,091 loc) • 205 kB
Markdown
# REFERENCE
This file is automatically generated. _Do not edit._
Full reference documentation.
### How to use this documentation
The categorized index at the top provides a quick overview of each language element. To learn
more about a particular entry, search the full reference document for `^name`, where _name_ is the
symbol or word of interest.
## Index by category
### CONSTRUCTS
- __all__ _(see: promising, all)_
- __any__ _(see: promising, any)_
- __break__ - exit a loop or switch case
- __case__ _(see: switch)_
- __catch__ _(see: promising, try)_
- __continue__ - shortcut to the next iteration of a loop
- __count__ - numerically controlled looping
- __countdown__ - numerically controlled looping in reverse
- __dountil__ - until variant that always executes the loop once
- __dowhile__ - while variant that always executes the loop once
- __each__ - object attribute enumeration
- __else__ _(see: every, iterate, each, index, ply, count, countdown, if, unless, exists, switch)_
- __elsexists__ _(see: if, unless, exists)_
- __elsif__ _(see: if, unless, exists)_
- __elsunless__ _(see: if, unless, exists)_
- __every__ - general-purpose collection iterator
- __exists__ - conditional flow control
- __finalized__ _(see: promising)_
- __finally__ _(see: promising, try)_
- __if__ - conditional flow control
- __index__ - custom order iterator
- __iterate__ - sequentially examine values from a generator/iterator
- __ply__ - iterate over an array-like collection
- __promising__ - asynchronous flow control for Promises
- __rejected__ _(see: promising)_
- __resolved__ _(see: promising)_
- __state__ - asynchronous finite state machine
- __switch__ - choice-based conditional
- __then__ _(see: promising)_
- __throw__ - generate an exception
- __try__ - handle exceptional errors
- __unless__ - conditional flow control
- __until__ - loop over a block of code while an expression is false
- __while__ - loop over a block of code while an expression is true
- __with__ - block-level pronoun assignment
### GLOBALS
- __assert__ - run-time error checking
- __debug__ - print diagnostics to the console
- __environment__ - return SAI runtime environment information
### KEYWORDS
- __$$__ - function arguments pseudo-array
- __array__ - expression-oriented list literal
- __as__ - pronoun renaming clause
- __blank__ - an object with no traits
- __chain__ - verb chain initiation
- __contract__ - requirements for child objects
- __dec__ - decrement verb
- __delete__ - a verb that deletes variables
- __empty__ - an array with no items
- __enum__ - literal number enumeration
- __exists__ - check for undefined
- __expects__ - runtime paramater type validation
- __false__ - boolean value false
- __fields__ - structure literal
- __get__ - dynamic object attribute
- __given__ - parameter declaration and static values
- __inc__ - value increment
- __Infinity__ - constant representing infinity
- __inherit__ - class heritage specification
- __instance__ - object instance variable declaration
- __.isa__ - object name attribute
- __Iterator__ - name-of-the-Iterator method
- __list__ - literal-based array construction
- __local__ - locally scoped variable declaration
- __main__ - indicates object should be instantiated at runtime
- __NaN__ - not-a-number
- __nop__ - no-operation statement
- __null__ - a value that is no value
- __object__ - object declaration
- __orphan__ - rescoping this
- __process__ - generative function definition
- __promise__ - declare a Promise-producing function
- __reference__ - declare global values
- __reject__ - mark a Promise as having failed
- __resolve__ - mark a Promise as succeeding
- __return__ - exit a function, returning a value maybe
- __set__ - variable assignment
- __super__ - superclass function invocation
- __swap__ - value swapper
- __task__ - define a function
- __to__ - set clause syntax
- __traits__ - declare a structure of literal strings
- __true__ - boolean value true
- __unbound__ - mark a method as context-free
- __undefined__ - the absence of a place
- __using__ - function reference clause
- __yield__ - process product
- __yielding__ - process inclusion
### OPERATORS
- __%__ - modulus
- __*__ - multiplication
- __**__ - exponentiation
- __+__ - addition / string concatenation
- __-__ - subtraction/negation
- __/__ - mathematical division
- __<__ - less comparison
- __<<__ - bitwise left shift
- __<=__ - less or equal comparison
- __<=>__ - comparison
- __<>__ - inequality comparison
- __=__ - equality comparison
- __>__ - greater comparison
- __>=__ - greater or equal comparison
- __>>__ - signed bitwise right shift
- __>>>__ - unsigned bitwise right shift
- __?<__ - minimum
- __?>__ - maximum
- __?? ::__ - trinary conditional
- __and__ - logical and
- __andb__ - bitwise and
- __bind__ - function context binder
- __copy__ - shallow copy
- __create__ - object creation
- __default__ - variable initialization
- __initialized__ - default with assignment
- __is__ - equivalence test
- __isNaN__ - checks for NaN
- __isnt__ - equivalence test
- __isof__ - object information
- __lsh__ - unsigned bitwise left shift
- __nand__ - not-and logical
- __new__ - object instantiation
- __nor__ - not-or logical
- __not__ - logical not
- __notb__ - binary not
- __number__ - numeric coercion
- __or__ - logical or
- __orb__ - bitwise-or
- __rsh__ - signed bitwise right shift
- __singleton__ - singleton object access
- __typeof__ - javascript type disclosure
- __ursh__ - unsigned bitwise right shift
- __xor__ - logical exclusive-or
- __xorb__ - binary exclusive-or
### PIPERS
- __all__ - query a collection for all values
- __and__ - confirms a collection
- __any__ - query a collection for any of one-or-more values
- __asc__ _(see: by)_
- __audit__ - observe the elements of a collection
- __by__ - collection sorting
- __collect__ - collect values from an iterator into a static list
- __concat__ - assemble two collections into a single list
- __count__ - collection item count
- __delete__ - multi-attribute removal
- __desc__ _(see: by)_
- __enkey__ - convert a list/iterator into a set of traits
- __enlist__ - convert a collection to an array
- __entrait__ - convert collections to object/traits
- __expects__ - rule enforcer for traits
- __first__ - first value
- __has__ - collection filtering
- __highest__ - search collection for highest value
- __into__ - collection integration
- __iterate__ - transforms a static collection into an iterated form
- __keys__ - return a list of trait names
- __last__ - last value
- __limit__ - select certain array/iterator elements
- __lowest__ - search collection for lowest value
- __observe__ - inspect a value without changing it
- __or__ - explores a collection
- __reduce__ - collection reduction
- __replicate__ - create a list by copying an element
- __select__ - multi-attribute query
- __set__ - replacement operator
- __thru__ - transform a collection with a map-like operation
- __total__ - reduction with addition/concatenation
- __update__ - multi-attribute update
- __values__ - return a list of collection values
### PRONOUNS
- __counter__ - pronount for integer-based loops
- __error__ - catch pronoun
- __it__ - context-sensitive pronoun
- __key__ - looping pronoun
- __self__ - pronoun for set statements
- __sum__ - reduction pronoun
- __trial__ - pronoun for conditionals and switch
### SYNTAX
- __!__ - function invocation operator
- __#__ - existence assertion
- __$__ - function parameter scoping prefix
- __${ }__ - string composition
- __' and "__ - string literals
- __'''__ - multi-line string literal
- __'d__ - conversational syntax
- __'s__ - conversational syntax
- __( )__ - parentheticals
- __.__ - attribute lookup
- __..__ - pastes two related lines together
- __...__ - statement pasting operator
- __//__ - source code comment indicator
- __0b, 0o, 0x, and o__ - binary, octal, hexadecimal and radian numeric literals
- __:__ - create an array or object literal
- __;__ - structure definition closer
- __<-__ - calculation precedence modifier
- __?__ - safe fetch
- __@__ - context-aware scoping prefix
- __@@__ - context-ignoring scoping prefix
- __[ ]__ - attribute lookup
- __|__ - pipe operator prefix
- __~__ - global scoping prefix
- __call__ - function invocation
- __from__ - function invocation
## Encyclopedia
### ! - _function invocation operator_ - syntax - ^!
In unary form **!** (bang) invokes the following identifier as a function call that returns a value,
passing in any parameters that follow. In binary form, invokes the method on the left as a method
of the object on the right.
... ! [identifier] ([parameters]) (;)
... [method] ! [object] ([parameters]) (;)
The following are synonymous:
set record FetchRow!cursor
set record !cursor.FetchRow
set record cursor.FetchRow()
set cursor !db.Query 'select * from names'
set cursor to db.Query('select * from names')
If you like the `()` style of invoking functions, that's fine. But when you get into function
invokations that span multiple lines, you'll need to use the __!__ or do some really ugly
patchup work with __...__.
Neither the leading **!** nor parenthesis are needed when the verb begins the line as in
the `cursor.Close` statement below.
set cursor !db.Query 'select * from names'
set records !cursor.FetchAll
cursor.Close
If you want to invert the verb/object in verb statements, you can do this:
Close!cursor
If you wish to use **!** in a continuing expression, close the parameter
clause with a semicolon (like you'd close a structural literal).
The following examples are equivalent.
set @x Math.sin($angle) * $magnitude
set @x !Math.sin $angle; * $magnitude
### # - _existence assertion_ - syntax - ^#
When defining fields or traits, using a hash in front of a trait _name_ will automatically assign
a value of **true** to that trait.
.. traits #[tagname]
.. fields #[tagname]
The following two definitions are identical:
set row1 to: name 'John', age 19, #cat, #dog
set row2 to: name 'John', age 19, cat true, dog true
### $ - _function parameter scoping prefix_ - syntax - ^$
Easy access to the first parameter a function was called with (even if no parameters are declared
with __given__).
.. $
.. $[attribute]
When alone, **$** returns the first parameter a function was called with.
set ShowParameter to task
debug $
ShowParameter 'Bianca'
> Bianca
When followed immediately by an attribute name, that attribute of the first parameter in a
function. (It is not necessary to include the dot.)
set ShowAttribute to task
debug $name
ShowAttribute friends[0]
> Sara
Even if you use **given** to name your parameters, **$** continues to refer to the first parameter.
set ShowParameters to task given p1, p2
debug $
debug p1
debug p2
ShowParameters 'First', 'Second'
> First
> First
> Second
### $$ - _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.
### ${ } - _string composition_ - syntax - ^${ }
Allows the insertion of any arbitrary expression in the middle of a string literal.
.. 'String ${ [expr] } continues.' // single quote
.. "String ${ [expr] } continues." // double quote
.. `String ${ [expr] } continues. // backtick bareline
.. ''' // here document
String ${ [expr] } continues.
As shown above, this is supported in all four string literal formats.
set FriendSummary to task
return 'Name: ${$name}, age: ${$age}'
ply friends limit 3 as friend
debug 'Subject ${key+1}: ${FriendSummary(friend)}'
> Subject 1: Sara, age 23
> Subject 2: John, age 19
> Subject 3: Ellie, age 22
Use `\${` to represent `${` in a string literal.
debug 'But I really need to include \${} in my output!'
> But I really need to include ${} in my output!
### % - _modulus_ - operators - ^%
Calculates the mathematical **modulus**; returns the remainder of the left expression divided by the right.
.. [expr] % [expr]
Examples:
debug 5 % 2
debug 6.2 % 1
> 1
> 0.2
### ' and " - _string literals_ - syntax - ^' and "
Declare a single line delimited literal string.
.. 'Type your string literal here.'
.. 'Use a backslash to escape ' and " and \ in your strings.'
String literals support embedded expressions (a.k.a. string compositions) using the
syntax: `${expr}`. Any normal expression can be evaluated and will be concatenated in-place.
Use backslash as an escape for string delimiters `\'` and `\"`, dollar signs
before braces `\${`, and the standard control characters `\n \r \t \b \f`.
debug 'This here's ${'a'} test using \${} composition.'
> This here's is a test using ${} composition.
Double quote is semantically identical to single quote..
### ''' - _multi-line string literal_ - syntax - ^'''
Declare a multi-line string ideal for embedding longer passages of markup.
.. '''
Many lines of text
Some Indented
Continuing until
Indent resets
on the line below this.
..
This is also known as a _here document_ as in "here's a document!"
.. '''
Now you can type as much text as you want,
remembering that leading and trailing whitespace
will be stripped, though newlines will be preserved.
So it's perfect for markup.
It will even keep track of your indenting, assigning
column zero to the start of the first line.
The string goes on as long as the indenting does.
And you can include \${'compo'+'sitions'} as well.
That’s three single quotes in a row, then an indented block.
Blank lines, indentation and comments are preserved, unless the comment
is outside the left margin.
### 'd - _conversational syntax_ - syntax - ^'d
The _verb'd_. An alternate way of invoking a verb on an object.
.. [verb]'d [object] (parameters)
Here are some functionally identical ways of writing the same thing:
biglist.push item
push'd biglist item
debug name.toUpperCase()
debug !name.toUpperCase
debug toUpperCase'd name
The thing I find displeasing about the subject-first syntax is that it obscures the action taken.
When the action (the verb) is not foremost on the line, program flow is more difficult to discern.
You can use any number of indirections on the object to be verb'd:
set index to join'd library\reference\category.titles '\n'
I find this much clearer than:
set index !library\reference\category.titles.join '\n'
Because as you're reading the code, you see WHAT action will take place first, then you learn
the subject and object and how they relate to each other. Hey if you don't like it,
don't use it.
### 's - _conversational syntax_ - syntax - ^'s
The _posessive_. An alternate way of referencing the attributes of an object.
.. [value]'s [attribute]
[object]'s verb
Equivalent to the use of `.` when accessing named attributes.
Functionally equivalent examples:
set mixer's colour to 'red'
set mixer.colour to 'red'
set mixer['colour'] to 'red'
set index !library\reference\category.titles
set index !library\reference\category's titles
if config.options.speedometer
if config.options's speedometer
if config's options.speedometer
if config's options's speedometer
In that last set of four, there was probably one that most accurately represented how you
would think about the way configuration data is organized. Using a coding style that reflects
that mental representation helps you write maintainable code.
### ( ) - _parentheticals_ - syntax - ^( )
Parenthesis wrap expressions that should be evaluated together, and also invokes functions.
.. verb(parameters)
.. ( expression )
.. ( expression [as identifier])
#### invoking functions with parenthesis
Use parenthesis after a value to invoke that value as a function, passing in the values within
the parenthesis as parameters to the function.
set fs to require('fs')
set angle to Math.atan2(y, x)
You don't use parenthesis when the verb is the first thing on the line:
db.query(key) // incorrect
db.query key // correct
You also don't use parenthesis when you're in the middle of a function chain:
set lines to chain fs.readFileSync(fn,'utf8')
split /[
]+/ // no parens
| has .length > 0 // pipe operator
You can't easily use parenthesis when your function call spans multiple lines, in this case you'd
use the exclamation mark before the function name, instead of parenthesis after
(see the documentation for __!__ for more):
return !textReplacer text, fields
name db.name
address db.address
phone db.phone
Note these are all valid ways of writing the same functionally:
set angle Math.atan2(y, x) // invokes Math.atan2 with parameters y, x
set angle atan2!Math y, x // ditto
set angle !Math.atan2 y, x // ditto
#### controlling precedence with parenthesis
The much more useful way that parens are used is to control the precedence of operations.
For example, what the heck is the result of this statement:
set f to 3 * 8 - 2 / 2 + 5
There are certainly RULES about how this would be evaluated; multiplication and division would be done first,
then addition and subtraction. But who wants to have to think about that all the damn time? And what if
your assumptions are wrong, or you are just too tired or rushed to get it right?
set f to ( ( 3 * 8 ) - 2 ) / (2 + 5)
That's what parenthesis are for. My advice is: use them any time you have more than one operator in an
expression, unless the operators are all the same. And maybe even then. Always use them when you're
altering values in parameters.
#### using __as__ with parenthesis
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 ]
Parenthetic __as__ seems like it might be evil but I believe its use can be justified if
done with care and caution.
### * - _multiplication_ - operators - ^*
Multiplies two expressions.
.. [expr] * [expr]
As in:
debug 2 * 3
> 6
### ** - _exponentiation_ - operators - ^**
Raises one value to the power of another.
.. [lexpr] ** [rexpr]
Calculates an exponent; equivalent to `Math.pow([lexpr],[rexpr])`.
debug 2 ** 3
> 8
### + - _addition / string concatenation_ - operators - ^+
summary
.. [expr] + [expr]
Adds two numbers OR concatenates two strings. If both expressions are numbers, adds them.
Otherwise converts them both to strings and concatenates.
debug 1+2
debug '1'+2
debug 1+'2'
debug '1'+'2'
> 3
> 12
> 12
> 12
### - - _subtraction/negation_ - operators - ^-
summary
.. [lexpr] - [rexpr]
.. - [expr]
Used as a binary operator, subtracts the right expression from the left.
debug 3 - 5
> -2
Used as a unary operator, negates the expression (subtracts it from 0).
set b to 5
debug 3 + -b
> -2
SAI will not let you write the following;
set a - 3
Because that could be parsed one of two ways:
- place the value `-3` in the variable `a`
- subtract `3` from the variable `a`
If you try to write code like this, a compiler error will result. Choose one of these
formats instead:
set a to -3 // store -3 in a
set a self - 3 // subtract 3 from a
Just a little gotcha.
### . - _attribute lookup_ - syntax - ^.
Looks up the object trait named _attribute_ in the expression.
.. [expression].[attribute]
.. .[attribute]
.. .[numeric array index]
This is a _literal_ dereferencing; the text of the attribute is used as the lookup.
set friend to friends.0
debug friend.name
debug friend['name']
with friend
debug it.name
debug .name
// prints 'Sara' four times
When used without a leading value,
references the named attribute in the **it** pronoun.
Named attributes can also be referenced with `'s` because why not.
### .. - _pastes two related lines together_ - syntax - ^..
You can use the two-dot _caboose_ to stitch a *single line* child code block onto the end of the
statement that requires it.
line 1 .. line 2
The following are functionally identical:
if error
return -1
if error .. return -1
Don't use the caboose unless it will help make your code clearer.
### ... - _statement pasting operator_ - syntax - ^...
You can use the three-dot _continue_ syntax to insert line breaks, or multiple child code blocks,
into a single statement.
first part of statement
maybe other stuff
... continuing statement
perhaps additional stuff
... and more of the statement.
This is not considered good practice (period), as most overly complex statements should be
broken down into multiple statements (period), just like a run-on sentence should be
clarified by a good editor.
But should you really need it, it is there.
The three dot continuation must start at the same indent as the line it continues.
debug traits
name Sally
age 32
province QC
... update traits
#cat
province ON
> { name: 'Sally', age: 32, province: 'ON', cat: true }
Or even:
debug 1
... +
... 2
... +
... 3
> 6
Syntactically, the continue is equivalent to a whitespace character with a bonus ability
to reset level of indent.
One more time; if you find yourself tempted to use a continue, take a hard look at the code you're
writing and see if it can't be written more clearly. It's okay to use more lines of code.
It's okay to use temporary variable names for clarity. It's okay to create clear and simple
subroutines. All of these things make for more maintainable code.
### / - _mathematical division_ - operators - ^/
Divides the expression on the left by the expression on the right.
.. [expr1] / [expr2]
As in:
debug 22 / 7
debug 355 / 113
> 3.142857142857143
> 3.1415929203539825
### // - _source code comment indicator_ - syntax - ^//
Two forward slashes indicate the start of a comment.
// any text goes here, is ignored through the end of line.
Judicious comments that indicate _intent_ are a good idea. Commenting how everything works
minutely is not so good. If code function isn't clear, the code has a problem.
One gotcha with comments and ''' here documents:
debug '''
This is a here document
// this is a comment line in the here document
// and so is this
// but this one is not in the document.
and it ends here.
> This is a here document
> // this is a comment line in the here document
> // and so is this
> and it ends here.
Comments WITHIN the indentation bounds of a here document are included in the document as text.
Comments OUTSIDE the indentation bounds are treated as true comments.
### 0b, 0o, 0x, and o - _binary, octal, hexadecimal and radian numeric literals_ - syntax - ^0b, 0o, 0x, and o
Numeric literals in different formats.
binary: 0b0101010101
octal: 0o01234567
hexadecimal: 0x0123456789abcdef
radians: 360o
Numeric literals in a variety of formats are to be encouraged as a way of more clearly showing
data values.
debug 0b01011101
debug 0o777
debug 0xdeadbeef
debug 180o
> 93
> 511
> 3735928559
> 3.141592653589793
### : - _create an array or object literal_ - syntax - ^:
The colon __:__ is used to begin the declaration of a data structure.
.. : [expr] (, [expr], [expr], ...) (;) // array
.. :
[expr] ( , [expr], ... ) // array
( [expr], [expr], ...
[expr]
... )
.. : [name] [expr], [name] [expr], ... (;) // object
.. :
[name] [expr] (, [name] [expr], ...) // object
( [name] [expr], [name] [expr], ...
[name] [expr]
... )
The **:** structure definition parser determines whether to create an array or fields by
whether or not a field name is included before the first expression.
debug :1, 2, 3;
debug:
1, 2
3
debug :a 1, b 2, c 3;
debug:
a 1
b 2
c 3
> [ 1, 2, 3 ]
> [ 1, 2, 3 ]
> { a: 1, b: 2, c: 3 }
> { a: 1, b: 2, c: 3 }
The semicolon at the end of the inline variety is not required except for clarity. Which means
it's a _really good idea_ to use it if the structure isn't the only thing on the line.
Give the documentation entry for __;__ a read to understand why!
### ; - _structure definition closer_ - syntax - ^;
Optional, closes the current structure definition when needed for clarity.
.. list a, b, c;
.. array 1, 2, 3;
.. traits a Apple, b Banana, c Cabbage;
.. fields r colour.red, g colour.green, b colour.blue;
.. :1, 1, 2, 3, 5, 8;
.. :name 'Jane, age 45, city 'Halifax';
Only needed when there is additional code on the same line that might mistakenly
bind too tightly to the final value in the structure.
debug list 3, 2, 1 | by it
debug array 3, 2, 1 | by it
debug array 3, 2, 1; | by it
> [ 1, 2, 3 ]
> [ 3, 2, 1 ] // undesired result
> [ 1, 2, 3 ]
In the example above, the **list** literal sorts properly because **list** elements
are not allowed to be mathematical expressions, so the parser can correctly
bind `by it` to the entire list.
However, the first **array** literal _doesn’t_ sort correctly because array elements
can be expressions, and `by it` is binding to the final term in the literal. Thus, the
definition is parsed as `array (3), (2), (1 by it)`.
One can force **list** to parse incorrectly by using the **=** list element expression
evaluation flag, but that’s ridiculous.
debug list =3, =2, =1 | by it // don't write code like this
debug list =3, =2, =1; | by it // just don't
> [ 3, 2, 1 ]
> [ 1, 2, 3 ]
The semicolon can also close parameter lists if using the **from** form of function invocation. The following examples are identical:
set x to ~Math.sin(angle) * magnitude
set x !Math.sin angle; * magnitude
### < - _less comparison_ - operators - ^<
Evaluates **true** if lexpr is numerically or lexically lower than rexpr, **false** otherwise.
.. [lexpr] < [rexpr]
For example:
debug 1 < 1 // false
debug 1 < 2 // true
debug 'a' < 'b' // true
debug 'a' < 'B' // false, case matters
If you want a case insensitive comparison, you must ensure both expressions are of the same case.
### <- - _calculation precedence modifier_ - syntax - ^<-
The _precedent_ operator, __<-__ ensures the expressions on the right is calculated before the expression on the left.
.. [expr] [operator1] <- [expr] [operator2] [expr]
Useful when chaining operators of identical precedence, the after operator changes the order
of operations from left->right to right->left. Effectively, the right side of the after operator
is wrapped in parenthesis.
debug 1 - 2 - 3
More to write...
### << - _bitwise left shift_ - operators - ^<<
Convert the expression to a 32 bit integer, then perform a binary left shift bitcount times.
.. [expr] << [bitcount]
.. [expr] lsh [bitcount] // synonym
See its synonym __lsh__.
### <= - _less or equal comparison_ - operators - ^<=
Evaluates to **true** if lexpr is numerically or lexically lower or equal to rexpr, **false** otherwise.
.. [lexpr] <= [rexpr]
You know how this works.
### <=> - _comparison_ - operators - ^<=>
Evaluates to **-1** if lexpr is lower than rexpr, **1** if it is greater, and **0** if they are equal.
.. [lexpr] <=> [rexpr]
For example:
debug 1 <=> 0 // returns 1
debug 1 <=> 1 // returns 0
debug 1 <=> 2 // returns -1
debug 'b' <=> 'a' // returns 1
debug 'b' <=> 'b' // returns 0
debug 'b' <=> 'c' // returns -1
Useful in sorting operations!
### <> - _inequality comparison_ - operators - ^<>
Tests two values for value inequality.
.. [expr] <> [expr]
Compares two values for value equality; returns **true** if the values appear to be the
same, **false** otherwise.
This is implemented with Javascript’s != operator and the behaviour is identical.
Contrast with **isnt**.
### = - _equality comparison_ - operators - ^=
Evaluates to **true** if the two expressions are equivalent.
.. [expr] = [expr]
This compiles directly to Javascript’s `==` operator and has the same occasionally
bizarre side effects. Basically, don’t use = except for comparing numeric, boolean or string values.
Use **is** or **isnt** to compare objects or object types, except when dealing with **NaN** in which
case the only reliable way to test for its existence is to use **isNaN**. That’s Javascript for you.
### > - _greater comparison_ - operators - ^>
Evaluates **true** if lexpr is numerically or lexically greater than rexpr, **false** otherwise.
.. [lexpr] > [rexpr]
For example:
debug 1 > 1 // true
debug 1 > 2 // false
debug 'a' > 'b' // false
debug 'a' > 'B' // true, case matters
If you want a case insensitive comparison, you must ensure both expressions are of the same case.
### >= - _greater or equal comparison_ - operators - ^>=
Evaluates **true** if lexpr is numerically or lexically greater than or equal to rexpr, **false** otherwise.
.. [lexpr] >= [rexpr]
You know the drill.
### >> - _signed bitwise right shift_ - operators - ^>>
Convert the expression to a 32 bit signed integer, then shift it right bitcount times,
maintaining the sign of the expression.
.. [expr] rsh [bitcount]
.. [expr] >> [bitcount] // synonym
See __rsh__.
### >>> - _unsigned bitwise right shift_ - operators - ^>>>
Convert the expression to a 32 bit unsigned integer, then shift it right bitcount times,
Zero bits are added on the left side (msb), which does not preserve negative sign.
.. [expr] ursh [bitcount]
.. [expr] >>> [bitcount] // synonym
See __ursh__.
### ? - _safe fetch_ - syntax - ^?
Adds extra type-checking to an expression ensuring that it will not throw an exception if roots are
undefined; instead just returning **undefined**.
.. ? [expr]
Here's why you want this sometimes:
set a to undefined
debug a.a
// throws an exception
debug ?a.a
> undefined
Super helpful when building caches, for example.
### ?< - _minimum_ - operators - ^?<
Evaluates to whichever expression is numerically or lexically lower.
.. [lexpr] ?< [rexpr]
Kind of like Math.min except as an operator.
debug 1 ?< 2 // returns 1
debug 2 ?< 1 // returns 1
Handy when you need it.
### ?> - _maximum_ - operators - ^?>
Evaluates to whichever expression is numerically or lexically higher.
.. [lexpr] ?> [rexpr]
Kind of like Math.max except as an operator.
debug 1 ?> 2 // returns 2
debug 2 ?> 1 // returns 2
Of course it only operates on two values at a time. But that's cool.
### ?? :: - _trinary conditional_ - operators - ^?? ::
Evaluates to expr1 if the left expression is _truthy_, otherwise evaluates to expr2.
.. [expr] ?? [expr1] :: [expr2]
I get the grar about the ?: operator, it can make for unclear logic. But I've found
with this _doubled up_ version that it's much easier to see when it's used, especially
if you keep spaces on either side of the operators.
debug true ?? 'True' :: 'False' // prints 'True'
debug false ?? 'True' :: 'False' // prints 'False'
It's still kind of not great, but on the other hand there are times when five lines of code
could be reduced to one without losing much clarity. And those are the times for ??::.
### @ - _context-aware scoping prefix_ - syntax - ^@
Reference a trait or method attached to the current object. Equivalent to Javascript’s `this.`.
.. @
.. @attribute
@Task [parameters]
Here's the thing. In SAI you almost never need to use @ (this). That is because the compiler
knows what your instance variables are. So it just assumes when you use the name of an instance
variable, that you mean _this_ instance. If you want it to reference some other instance, use
the the dot accessor in the usual way.
Here’s a short implementation of a 2D vector by way of illustration.
object Vector2 1.0.0
instance: // declare some member variables up front so the compiler knows what
x 0 // we are doing. Also so that other programmers, like future you,
y 0 // know what we are did.
angle set given radians
SetPolar radians, magnitude // SetPolar is an instance method. No need for "@"
get
return !Math.atan2 y, x
magnitude set given units
SetPolar angle, units
get
return !Math.sqrt x*x + y*y
Instantiate task given x, y
// OH NO THIS IS A COMPILER ERROR because x and y are instance variables
// and you can't use instance variable names as parameters. Try again!
// You also can't use them as renames for pronouns. The compiler tries very
// hard to keep you from stepping on yourself.
Instantiate task given px, py
if px isof 'Vector2' // if we were passed an object
set x to px.x // copy its values.
set y to px.y // no "@" for our object, "px." for the other one.
else
set x to px
set y to py
SetXY task given px, py
set x to px
set y to py
SetPolar task given radians, units
set x to units * Math.cos(radians)
set y to units * Math.sin(radians)
#### You need @ for inherited attributes
It's worth noting that you can use @ if you want to. And sometimes, you must.
You can avoid the user of @ for member variables/methods only on variables/methods defined
in the same source file. Inherited members must be referenced with @.
This was borne of laziness in writing the compiler BUT ALSO it has the interesting property
of making it explicit in the source code where the use of an object member is _inherited_ vs
when it is not. So for the moment I'm happy that it's like this.
#### Advanced information
Internally, @ always points to a variable that has been _assigned_ from the JS value
of `this`. It doesn't reference `this.` directly. In ever member function definition of the compiled
JS object code, you'll see this bit of code in the local variable declarations:
$ = this['Dedenter'] ? this : $bindfail('Dedenter')
This does a couple things -- first, it verifies the `this` object is actually a kind of object
we're expecting, that is, that the object has a member attribute that matches the function name.
(If not, it throws a "binding fail" message.)
But if the test is successful, the `this` value is
assigned to our work variable `$`. Whenever you use reference member attributes in your code,
you're actually referencing the attributes of the `$` variable, not of `this`.
This captures the instance that was active at the start of the function call. If we
did not do this, and just used `this.` then it is very likely that asynchronous callbacks to functions
defined in the method would lose their object context -- because _callbacks are almost always unbound_ from
an object.
So, instead of forcing you to use that ugly-ass `self` workaround, SAI just does the work for you and
always captures the `this` value, because it's really unlikely that you wouldn't want it to work this
way anyhow.
And if, in case, you did, you could use the __@@__ prefix, or the __orphan__ keyword.
### @@ - _context-ignoring scoping prefix_ - syntax - ^@@
Maps directly to Javascript's `this`, the context-ignorant object scope.
.. @@
.. @@attribute
@@Task [parameters]
Used inside called functions to refer to the "this" object in the scope of the call,
rather than the scope of the function definition.
You can use the __orphan__ statement to force decontextualization of the usual
"this" scope. Have a look at that entry for info.
### [ ] - _attribute lookup_ - syntax - ^[ ]
Performs an indirect lookup of a trait/element in the given value.
.. [value] '[' [expr] ']'
The following debug statements print identical results.
set field to 'name'
set friend to friends[0]
debug friend.name
debug friend['name']
debug friend['na'+'me']
debug friend[field]
debug friend\field
// prints 'Sara' five times.
### | - _pipe operator prefix_ - syntax - ^|
The pipe character is a scoping prefix for second-class operators defined with the _piper_ system.
.. | [operator name] [clauses] ...
A note about order of operations.
Pipers are processed left-to-right, as if data were feeding through a series of tubes, or machines
that slurped up what's given on the left and spits it out to the next machine along the line or as the
result of the operation.
Be sure when you're coding that you're aware of the order of operations, especially in a set statement.
For example, this may not produce what you want:
set a list 1, 2, 3
set b list 4, 5, 6
set a | concat b | thru it * 2
debug a
> [ 2, 4, 6, 8, 10, 12 ]
Were you expecting this:
> [ 1, 2, 3, 8, 10, 12 ]
Keep in mind that the flow of data is left to right, and in a self-modifying __set__ statement
the assignment happens at the end. These are the same:
set a | concat b | thru it * 2
set a to a | concat b | thru it * 2
If you want that construct to work the other way, use parenthesis, or the "precedent" syntax:
set a | concat ( b | thru it * 2 )
set a | concat <- b | thru it * 2
### ~ - _global scoping prefix_ - syntax - ^~
Use `~` in front of an identifier to access an object defined globally, e.g. by the runtime environment.
.. ~GlobalIdentifier
You must use `~` in each of the following examples:
set validated to ~parseInt(raw)
~process.exit 1
set attendees to new ~Set
set func to new ~Function(arg1, src)
You do not need to use `~` on file-level pseudo-globals defined with `reference`; it is only required
for functions/values/objects that originate outside SAI.
### | ALL - _query a collection for all values_ - pipers - ^all
Checks a collection to see if it has _all_ of multiple values. If so, returns true.
... | all [expr]
The expression to the left is the _haystack_, it is where we are looking. The expression on the right is the
(set of) _needles_ that we are looking for.
The __all__ piper examines the haystack to see if it has _all_ of the needles. If so, it returns __true__.
Otherwise, it returns __undefined__.
Example:
set menu list tortilla, bean, lettuce, rice, meat, sauce
debug menu | any list tofu, avocado // undefined
debug menu | any list fish, meat // undefined
debug menu | any list rice, bean // true
You can also test against a single value:
debug menu | any 'bean' // 'bean'
debug menu | any 'gelato' // undefined
#### Quirks
You can use iterators on either side. Iterators on the needle side are collected, so don't try to
needle with an infinite generator.
When the haystack is an object, its keys are examined, not values.
Using an object (or any scalar value) as the needle creates a test for _that object/value_.
as if you had placed the object/value in an array by itself.
No optimizations are made that require assumptions about the sortability of either haystack or
needles, so this function is slower at the expense of being robust. If none of the needles are
found in the haystack, every needle has been compared with every item in the haystack. It might
be faster in some cases to sort and search, but this function does not do that.
### | AND - _confirms a collection_ - pipers - ^and
Examines every item in a collection, returning the collection itself if all of them are _truthy_.
... | and
For example:
set t: 'horse', 9, true
set f: 0, 'goat', 99
debug t | and
debug f | and
> [ 'horse', 9, true ]
> 0
Rules for source data:
- __undefined__: returns `undefined`.
- __value__: returns the value.
- __array__: returns _the array_ itself if _every_ element in the array is _truthy_,
otherwise returns the first _falsy_ element.
- __traits__: returns _the object_ itself if _every_ value in the object is _truthy_,
otherwise returns the first _falsy_ value.
- __iterable__: yields `true` if _every_ value returned by the iterator is _truthy_,
otherwise yields the first _falsy_ value. This _vill_ lock up on infinite _truthy_
generators as it will try forever to find a _falsy_ value.
If given an iterator, __and__ returns an iterator that will yield one value (or lock up).
### AND - _logical and_ - operators - ^and
Checks two or more values; if they're all _truthy_, return a _truthy_ value.
Otherwise returns a _falsy_ value.
.. [expr] and [expr]
.. and [list of expressions]
As a binary operator, if the left expression evaluates to **falsy**, returns it,
otherwise returns the right expression.
Binay operator examples:
debug 1 and 0 // prints '0'
debug 1 and false // prints 'false'
debug 0 and 1 // prints '0'
debug false and 1 // prints 'false'
debug 1 and 1 // prints '1'
debug true and 'Fred'
> Fred
As a unary operator given a list, returns the right-most element of the list if all the
elements in the list are _truthy_, otherwise returns the first _falsy_ element.
Unary operator examples:
debug and 'frog', 2, true
debug and 'toad', false, 'mario'
> true
> false
The list can be split on more than one line, indented:
debug and:
'hello', true
'this', 4, 88
lookup('thing')
> [ result of call to lookup ]
You can nest logical operators on multiple lines:
debug and:
or test1(2), test2(3)
or fallback(1), fallback(2)
It can get very clunky to try and use multi-line logical expressions with IF statements.
Instead, assign the results of your test to a clearly named variable that describes your
test and then use that in your IF statement:
set buildOk to and:
fileExists(fn)
fileDate(fn) > lastBuild\fn
not alreadyBuilt\fn
extensionhandler[PATH.extension(fn)]
if buildOk as hdlr
hdlr.build fn // rule about AND: last element in the list if all are true.
### ANDB - _bitwise and_ - operators - ^andb
Bitwise and operator. Converts, if possible, both left and right expressions into 32 bit signed integers, and performs a bitwise AND operation on them.
.. [expr] andb [expr]
Examples:
debug 5 andb 11
> 1
// 5 in binary: 00000000000000000000000000000101
// 11 in binary: 00000000000000000000000000001011
// 1 in binary: 00000000000000000000000000000001
### | ANY - _query a collection for any of one-or-more values_ - pipers - ^any
Checks a collection to see if it has _any_ of multiple values. If so, returns the value it found first.
... | any [expr]
The expression to the left is the _haystack_, it is where we are looking. The expression on the right is the
(set of) _needles_ that we are looking for.
The __any__ piper examines the haystack to see if it has _any_ of the needles. If so, it returns the first
one it finds. Otherwise, it returns __undefined__.
Example:
set menu list tortilla, bean, lettuce, rice, meat, sauce
debug menu | any list tofu, avocado // undefined
debug menu | any list fish, meat // 'meat'
debug menu | any list rice, bean // 'rice'
You can also test against a single value:
debug menu | any 'bean' // 'bean'
debug menu | any 'gelato' // undefined
#### Quirks
You can use iterators on either side. Iterators on the haystack side are collected, so don't try to
query with an infinite generator.
When the haystack is an object, its keys are examined, not values.
Using an object (or any scalar value) as the needle creates a test for _that object_
as if you had placed the object/value in an array by itself.
No optimizations are made that require assumptions about the sortability of either haystack or
needles, so this function is slower at the expense of being robust. If none of the needles are
found in the haystack, every needle has been compared with every item in the haystack. It might
be faster in some cases to sort and search, but this function does not do that.
### 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 ]
### ASC
_See: by_
### ASSERT - _run-time error checking_ - globals - ^assert
If the first expression is _falsy_, throw an exception with the second expression as a message.
Otherwise, return the expression.
assert [test value], [error message]
.. !assert [test value], [error message]
For example:
assert everythingIsAwesome, 'I have some bad news...'
> Error: SAI: failed assertion: I have some bad news...
You can use __assert__ in an expression, because it returns the value passed.
set sock !assert GetSo