UNPKG

sai-language

Version:

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

1,017 lines (572 loc) 26 kB
# This file is automatically generated. _Do not edit._ Unknown Heading ### % - _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 ### * - _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. ### / - _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 ### < - _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. ### << - _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__. ### ?< - _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 ??::. ### 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 ### BIND - _function context binder_ - operators - ^bind The __bind__ operator _binds_ a function reference to the current object context. .. bind [function reference] This is useful when you need to pass in a member function to a callback, and need to ensure the execution context (specifically, the _this_ object) isn't lost. Why is this important? Object car instance: plate '876 ACET' CheckPlate task DB.lookupPlate plate, PlateResults PlateResults task given status debug 'Status for ${plate}: ${status}' In the above function definition of `CheckPlate` the `DB.lookupPlate` function is an asynchronous call with a plate number and a callback function. As written, when the callback is invoked, `PlateResults` will have been unbound from its object; it is a function reference with no context. The code will produce this result: Looking up 876 ACET Status for undefined: Valid Although the test was successful, we have _lost context_ on the callback because `PlateResults` is passed in as an unbound function. Referring to the member trait `plate` within an unbound context produces an undefined result. In order to make this function reference work, we need to bind it to the current object with the __bind__ operator, as follows: DB.lookupPlate plate, bind PlateResults Now when we run the asynchronous function, it works as expected, because the callback has its context; e.g. it knows where it is going (or, more accurately, where it is returning to). Looking up 876 ACET Status for 876 ACET: Valid Note that __bind__ is only necessary when passing references to object member functions. Functions you define in-place won't have this issue, as they create their own context. This version of __bind__ only binds functions to the current object. If you want to bind a function on some other object, you'd write code something like this, making use of the Function object's bind ability: ... !anObject.anyFunction.bind anObject SAI compiled objects do a runtime check on any member method invocation to verify that the current execution context is, if not correct, at least not totally absent. ### COPY - _shallow copy_ - operators - ^copy Use the **copy** unary operator to create a _shallow copy_ of the expression it precedes. .. copy [expr] You sometimes need this because ordinarily SAI and Javascript assign objects, rather than copies of object values. set plate to fruit // i want some fruit plate.push 'Ice cream' // and also dessert debug fruit // but now fruit has changed > [ 'Apple', 'Banana', 'Citron', 'Ice Cream' ] fruit.pop // get rid of the ice cream debug fruit > [ 'Apple', 'Banana', 'Citron' ] set plate to copy fruit plate.push 'Ice cream' debug fruit > [ 'Apple', 'Banana', 'Citron' ] debug plate // there we go > [ 'Apple', 'Banana', 'Citron', 'Ice Cream' ] Note that **copy** only copies enumerable attributes. There's a rabbit hole for ya. ### CREATE - _object creation_ - operators - ^create Creates a SAI object by name. .. create [expr] [parameters] If we are running in dynamic mode (that is, with source that is being dynamically compiled from .SAI files), __create__ will attempt to find the object’s source by using the `SAI.config.Loader` function, which defaults to `SAI.GetSourceFromPaths`, which tries to find a file named `[expr].sai` i n the provided paths. In runtime mode (that is, when running from plain Javascript .js files that have been pre-compiled), __create__ will call `require`, but do see the source in the `sai-library` that discusses this. reference: Tally 'Tally ^1.0.0' .. set inventory to create Tally The example follows best practices of placing object names in a **reference** section, aliasing versioned names into literals. See also __new__, which is a direct link to Javascript's `new`, and __singleton__, which works like __create__ except instantiates the object only once in the scope of the runtime library; subsequent calls will return that same object. ### DEFAULT - _variable initialization_ - operators - ^default Examines the expression on the left; if it has a value, returns it. Otherwise returns the value on the right. ... [lexpr] default [rexpr] set [lexpr] default [rexpr] #### default in expressions If and only if the left expression evaluates to `undefined`, returns the right expression, otherwise returns the left expression. Like **or** except instead of checking for _truthy_ tests to see if a value is present. Most useful in expressions where you’re not sure if a variable is initialized. set magnitude to undefined angle to 60o debug (magnitude default 1) * ~Math.cos(angle) > 0.5 #### default in **set** statements If the left expression is `undefined` then evaluate the right expression and assign it to the left expression. For example: set calculationCache[key] default ExpensiveCalculation(key) This checks to see if the calculation cache has a value for the given key, and if not, calculates the value and assigns it to the cache. ### INITIALIZED - _default with assignment_ - operators - ^initialized Examines the variable on the left; if it has a value, returns it. Otherwise calculates the value on the right, stores it in the variable on the left, and returns it. ... [lexpr] initialized [rexpr] The __initialized__ operator works very much like the __set/default__ combination, but allows you to use it in the middle of an expression. You should only use variables on the left, as trying to assign into a function or calculated expression will not work. The only real use for this is in scenarios where you would like to cache the result of an expression and want the code to reflect this simple idea. Here is a trivial example: GetCachedThing task with identifier return thingCacheidentifier initialized from thingComputation identifier ### IS - _equivalence test_ - operators - ^is Returns **true** if the two expressions are completely indistinguishable. .. [expr] is [expr] More rigorous than **=** when objects differ in type. debug 0 = false debug 0 is false > true > false _Note: Do not use **is** or **isnt** to test values against **NaN**. You must use the specific **isNaN** operator for this._ ### isNaN - _checks for NaN_ - operators - ^isNaN Returns true if the expression is the **NaN** flag. .. isNaN [expr] Note there is no other way to test for NaN than by using **isNaN**. Note also that capitalization matters. set a !parseInt 'This is not a number.' debug a // NaN debug a = 0 // false debug a = false // false debug a = NaN // false debug isNaN a // true Right? Seriously: debug NaN = NaN // false debug NaN is NaN // false So that's why we need this keyword for this one little function. ### ISNT - _equivalence test_ - operators - ^isnt Returns **true** if the two expressions are in any way distinguishable. .. [expr] isnt [expr] More rigorous than **<>** when objects are of differing types. debug 1 <> true debug 1 isnt true > false > true _Note: Do not use **is** or **isnt** to test values against **NaN**. You must use the specific **isNaN** operator for this._ ### ISOF - _object information_ - operators - ^isof All prototyped SAI objects have an **isof** trait that is a plain object that encapsulates detailed information about the SAI object and its heritage. .. [object].isof - or - .. [object] isof [class name expr] Let's just see how this works: 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 debug a.isof > { Fruit: > { version: '1.0.0', > isa: 'Fruit', > context: './sample/Keywords/Fruit.sai' } } debug b.isof > { Fruit: > { version: '1.0.0', > isa: 'Fruit', > context: './sai/sample/Keywords/Fruit.sai' }, > Pear: > { version: '1.0.0', > isa: 'Pear', > context: './sai/sample/Keywords/Pear.sai', > inherit: [ 'Fruit' ] } } You can trace an object’s creation in order recursively by starting at the **isof** entry for the present object — `isof[isa]` — and stepping through the **inherit** array. #### isof operator .. [object] isof [class name expr] Returns true if the object is an instance of a class that is, or inherits from, the given class name. class Parent 1.0.0 class Child 1.0.0 inherits: Parent set a to create 'Child' debug a isof 'Child' // true debug a isof 'Parent' // true ### LSH - _unsigned bitwise left shift_ - operators - ^lsh Convert the expression to a 32 bit integer, then shift it left bitcount times. .. [expr] lsh [bitcount] .. [expr] << [bitcount] // synonym For example: debug 4 lsh 1 // 8 debug -8 lsh 2 // -32 debug 2.8 lsh 0 // 2 The synonym __<<__ does exactly the same thing. ### NAND - _not-and logical_ - operators - ^nand Checks two or more values; returns _false_ if all of the values are _truthy_. Otherwise returns _true_. .. [expr] nand [expr] .. nand [list of expressions] A semantic simplification of `not ([expr] and [expr])`. debug 0 nand 0 // prints 'true' debug 1 nand 0 // prints 'true' debug 0 nand 1 // prints 'true' debug 1 nand 1 // prints 'false' These are logically the same debug a nand b debug not ( a and b ) For syntax of the list-based version of __nand__ please review keyword __and__. ### NEW - _object instantiation_ - operators - ^new Instantiates a new object with the given prototype. .. new [prototype] [parameters] If parameters are supplied, passes them to the objects constructor. **New** is used for traditional Javascript objects; objects where you have in-hand the prototype used to fabricate an instance that object. **New** trusts that the code to create that object has already been located and compiled. **Create** is used for SAI objects; objects where you have in-hand an object name (as a string). **Create** doesn't assume pre-compiled prototypes; if the prototype isn't cached, it will attempt to load it via the `SAI.config.Loader` function. reference: express require('express') object HelloHTTP 1.0.0 Instantiate task set @app express() @app.get '/', task given req, res res.send 'Hello HTTP!' set @server !@app.listen 3000, 'localhost', task with @server.address() debug 'Example app listening at http://${.address}:${.port}' You ordinarily need to use the **~** global scope prefix when creating Javascript built-in objects however `Error` has been promoted to SAI global so you don't have to: if crocodiles throw new Error "Why do we even have that lever?" Notice here that **new** is not expecting any parameters to be wrapped in parentheses; in this way it works like **from**. See also __create__ which is used to instantiate SAI objects by name, and __singleton__ which is used to create singleton SAI objects by name. ### NOR - _not-or logical_ - operators - ^nor Checks two or more values; returns true only if _all_ of them are _falsy_. Otherwise returns true. .. [expr] nor [expr] .. nor [list of expressions] Returns **true** only if left or right expression are both _falsy_. Otherwise returns **false**. A semantic simplification of `not ([expr] or [expr])`. debug 0 nor 0 // prints 'true' debug 1 nor 0 // prints 'false' debug 0 nor 1 // prints 'false' debug 1 nor 1 // prints 'false' These are the same: if not ( a or b ) if a nor b unless a or b For the syntax related to the list-based version of 'nor' review keyworcd __and__. ### NOT - _logical not_ - operators - ^not Performs the logical not operation. .. not [expr] Returns **false** if the right expression is _truthy_, otherwise returns **true**. debug not false // prints 'true' debug not 0 // prints 'true' debug not 'Sam' // prints 'false' debug not empty // prints 'false' These are the same: if not a unless a ### NOTB - _binary not_ - operators - ^notb Performs a bitwise logical not on a 32-bit signed integer. .. notb [expr] For example: debug notb -8 // prints '7' -8 in binary: 11111111111111111111111111111000 +7 in binary: 00000000000000000000000000000111 ### NUMBER - _numeric coercion_ - operators - ^number Attempts to convert the following expression into a numeric value. .. number [expr] If it cannot, the result is 0. debug number '12' debug number 'ralph' > 12 > 0 It is always safe to use __number__ in that it will never return a non-number, and it will never throw an error. It's sole failure mode is to return 0. ### OR - _logical or_ - operators - ^or Checks two or more values; returns the first one examined that is _truthy_. Otherwise returns the last value checked. .. [expr] or [expr] .. and [list of expressions] Examples: debug 0 or 0 // prints '0' debug 1 or 0 // prints '1' debug 0 or 2 // prints '2' debug 1 or 2 // prints '1' __Or__ also works with a list. Please review the __and__ keyword for details on syntax. ### ORB - _bitwise-or_ - operators - ^orb Performs a bitwise or on two 32-bit integers. [expr] orb [expr] Example: debug 5 orb 11 // prints '15' 5 in binary: 00000000000000000000000000000101 11 in binary: 00000000000000000000000000001011 15 in binary: 00000000000000000000000000001111 ### RSH - _signed bitwise right shift_ - operators - ^rsh 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) As in: debug 8 >> 1 // 4 debug -32 rsh 2 // -8 There's also an unsigned variant __>>>__/__ursh__. ### SINGLETON - _singleton object access_ - operators - ^singleton Creates one instance of SAI object by name. .. singleton [expr] [parameters] Creates one instance of SAI object by name. If the object has already been created, returns a reference to it. The scope is that maintained by the runtime library. Like __create__, __singleton__ will attempt to find the object’s source by using the `SAI.config.Loader` function, which defaults to `SAI.GetSourceFromPaths`, which tries to find a file named `[expr].sai` in the provided paths. reference: Tally 'Tally ^1.0.0' .. set inventory to singleton Tally (The example follows best practices of placing object names in a **reference** section, aliasing versioned names into literals.) Singleton objects are useful for library functions, APIs to external resources, tallies, caches, message managers and so on. Since the reference to the singleton object is maintained by the library, singleton objects are never garbage collected (and cannot be destroyed) once created. If you might need to release resources held by a singleton before the end of the application, you must make allowances for that in your object's API; e.g. by crafting a `.Cleanup` task. See also __new__, which is a direct link to Javascript's `new`, and __create__, which will make a new object each time. ### TYPEOF - _javascript type disclosure_ - operators - ^typeof Returns the Javascript-native type of an expression. .. typeof [expr] See e.g. [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof][2] for details. ### URSH - _unsigned bitwise right shift_ - operators - ^ursh 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) This is the difference: debug -32 rsh 2 // -8 debug -32 ursh 2 // 1073741816 You want this one if you're working with bitmap data, for example. Compare with __rsh__. The synonym __>>>__ does the same thing as __ursh__. ### XOR - _logical exclusive-or_ - operators - ^xor If both expressions are _truthy_, or both expressions are _falsy_, return `false`. Otherwise return the expression that is _truthy_. .. [expr] xor [expr] Example: debug 'Fred' xor 'Daphne' debug 'Shaggy' xor 0 debug 0 xor 'Scooby' debug 0 xor 0 > false > Shaggy > Scooby > false Maybe not too often used, but compared to: if (a and b) or not (a and b) ### XORB - _binary exclusive-or_ - operators - ^xorb Performs a bitwise XOR operation on two 32-bit integers. .. [expr] xorb [expr] Example: debug 3 xorb 6 // prints '5' 3 binary: 00000000000000000000000000000011 6 binary: 00000000000000000000000000000110 5 binary: 00000000000000000000000000000101 Favourite of cryptographers everywhere. And data storage architects.