sai-library
Version:
Runtime library for sai-language compiled scripts.
1,374 lines (1,300 loc) • 36.8 kB
JavaScript
"use strict";
var FS = require('fs');
///////////////////////////////////////////////
//
// SAI Runtime Library
//
// original native Javascript version
// no longer maintained
//
var SAILib = exports = {}
try { module.exports=SAILib; } catch(e) {}
// coverage testing housekeeping
//
var coverageCache={};
var coverage=function(i,j) {
var key=j+i;
if (undefined===coverageCache[key]) {
console.log("covered "+i+" in "+j);
coverageCache[key]=true;
}
}
// canIterate (utility)
//
// returns true if the candidate seems to be a true iterator, or at least acts like one
//
var canIterate=function(i) { // full coverage pass
if (!i) { //coverage(1,"canIterate");
return false;
}
if (i[Symbol.iterator]) { //coverage(2,"canIterate");
return true;
}
if (typeof i === 'function') { // coverage(3,"canIterate");
return true; // possibly a generator?
}
//coverage(4,"canIterate");
return (typeof i.next)==='function';
}
// mustIterate (utility)
//
// returns true if the candidate MUST be iterated
//
var mustIterate=function(i) { // full coverage pass
if (!i) { // coverage(1,"mustIterate");
return false;
}
if ((typeof i.next)==='function') { // coverage(2,"mustIterate");
return true;
}
if (typeof i === 'function') { // coverage(3,"mustIterate");
return true;
}
// coverage(4,"mustIterate");
return false;
}
// isObject (utility)
//
// returns true if an actual Javascript object
//
var isObject=function(i) { // full coverage pass
if (i===null) { // coverage(1,'isObject');
return false;
}
// coverage(2,'isObject');
return (typeof i)==='object';
}
// isArray (utility)
//
// returns true if an actual Javascript array
//
var isArray=Array.isArray;
// isMergable (utility)
//
// returns true if it has items, attributes, or is an iterator
//
var isMergable=function(i) {
return isArray(i) || isObject(i) || canIterate(i);
}
// isCollection (utility)
//
// returns true if an array or an object
//
var isCollection=function(i) {
return isArray(i) || isObject(i);
}
// assert
//
// throw an error if the test is false
//
SAILib.assert=function(test,msg) {
if (!test) {
if (!msg) msg='';
throw new Error("SAI: failed assertion: "+msg);
}
}
// debug_
//
// where do we send debug messages?
//
SAILib.debugFunction=function(o) {
console.log(o);
}
// debug
//
// display debug information
//
SAILib.debug_op=function(o) {
try {
if (o===undefined) {
SAILib.debugFunction("undefined");
} else if (typeof o === 'function') {
SAILib.debugFunction("{function}");
} else if (typeof o.next === 'function') {
SAILib.debugFunction("{likely iterator via .next}");
} else {
SAILib.debugFunction(o);
}
} catch (e) {
SAILib.debugFunction(`SAILib.debug exception: ${e.message}`);
SAILib.debugFunction(o);
}
}
// iterator
//
// If the object seems an iterator ALREADY under iteration, return it.
// Otherwise, attempt to invoke the generator to create an iteration
// with blank parameters.
//
SAILib.iterator_op=function(i) {
if (!i) {
coverage(1,"iterator");
return i;
}
if (typeof i.next === 'function') { // coverage(2,"iterator"); // pass
return i;
}
if (typeof i === 'function') { // coverage(3,"iterator"); // pass
return i();
}
if (i[Symbol.iterator]) { // coverage(4,"iterator"); // pass
return i[Symbol.iterator]();
}
coverage(5,"iterator");
return i;
}
/*
// generator -- THIS CODE DOES NOT SEEM TO BE USED
//
// Force the parameter into being a generator.
//
SAILib.generator=function(i) {
if (!i) {
coverage(1,"generator");
return i;
}
if (typeof i.next === 'function') {
coverage(2,"generator");
return function*(){yield*i}();
}
if (typeof i === 'function') {
coverage(3,"generator");
return i;
}
var iter=i[Symbol.iterator];
if (iter) {
coverage(4,"generator");
return iter;
}
coverage(5,"generator");
return i;
}
*/
// iterate
//
// given an object, create and invoke an iterator for it.
//
SAILib.iterate_op=function(a) { // full coverage pass
if (a===undefined) { // coverage(1,"iterate"); // pass
return undefined;
}
if (mustIterate(a)) {
//coverage(2,"iterate");
return SAILib.iterator_op(a);
}
if (a[Symbol.iterator]) { // coverage(3,"iterate"); // pass
return a[Symbol.iterator]();
}
if (isArray(a)) { // this path will only be executed if Array doesn't have a built-in iterator
//coverage(4,"iterate");
return function*(){ for (var i in a) yield a[i]; }();
}
if (isObject(a)) { // coverage(5,"iterate"); // pass
return function*(){ for (var i in a) yield [i,a[i]]; }();
}
// coverage(6,"iterate"); // pass
return function*(){ yield a; }();
}
// _kviterate (key value iterate)
//
// return an iterator that echoes an iterator
// but returns a row counter as well
//
SAILib.kviterateHelper=function*(a) {
var i=0;
for (var v of a) {
yield [i++, v];
}
}
// kviterate (key value iterate)
//
// given any object or value, create and invoke an iterator for it.
// for every available element, the iterater will return an array with key, value
// This is used by EVERY
//
SAILib.kviterate_op=function(a) { // full coverage pass
if (a===undefined) { //coverage(1,"kviterate"); // pass
return function*(){}();
}
if (mustIterate(a)) {
//coverage(2,"kviterate");
return SAILib.kviterateHelper(SAILib.iterator_op(a));
}
if (a[Symbol.iterator]) {
//coverage(3,"kviterate"); // pass
return SAILib.kviterateHelper(a[Symbol.iterator]());
}
if (isArray(a)) { // this path will only be executed if Array doesn't have a built-in iterator
//coverage(4,"kviterate");
return function*(){
var i=0,l=a.length;
while (i<l) {
yield [i++,a[l]];
}
}();
}
if (isObject(a)) {
//coverage(5,"kviterate"); // pass
return function*(){
for (var i in a) {
yield [i,a[i]];
}
}();
}
//coverage(6,"kviterate"); // pass
return function*(){
yield [0, a];
}();
}
// _collect (worker)
//
// given an iterator, create an array of all of its yielded values.
//
SAILib.collectHelper=function(iterable) {
var a=[];
for (var val of iterable) a.push(val);
return a;
}
// collect
//
// If an object must be iterated, return all of its yielded values.
// Otherwise return it unchanged.
SAILib.collect_op=function(a) { // full coverage pass
if (a===undefined) { // coverage(1,"collect");
return undefined;
}
if (!mustIterate(a)) { // coverage(2,"collect");
return a;
}
// coverage(3,"collect");
a=SAILib.iterator_op(a);
return SAILib.collectHelper(a);
}
// drain
//
// drain a generator, throwing values away
//
SAILib.drain_op=function(a) {
if (undefined===a) {
return;
}
if (!mustIterate(a)) {
return;
}
for (var val of a); // throw away
}
// sort
//
// Given a value, force it into an array, then sort it.
//
SAILib.sort_op=function(a,f) { // full coverage pass
if (isArray(a)) { // coverage(1,"sort");
return a.slice(0).sort(f);
}
if (mustIterate(a)) { // coverage(2,"sort");
return SAILib.collectHelper(a).sort(f);
}
if (isObject(a)) { // coverage(3,"sort");
return SAILib.values_op(a).sort(f);
}
// coverage(4,"sort");
return a;
};
// enlist
//
// Given any object, array, iterator or value, return an
// array with all of its values.
//
// undefined -> undefined
// value -> [value]
// array -> array
// object -> [[key,value],[key,value],...]
// iterator -> [yielded values]
//
SAILib.enlist_op=function(a) { // full coverage pass
if (a===undefined) { // coverage(1,"enlist");
return undefined;
}
if (isArray(a)) { // coverage(2,"enlist");
return a;
}
var out=[];
if (mustIterate(a)) { // coverage(3,"enlist");
for (var j=a.next(); !j.done; j=a.next()) out.push(j.value);
} else if (isObject(a)) { // coverage(4,"enlist");
for (var i in a) out.push([i,a[i]]);
} else { // coverage(5,"enlist");
out.push(a);
}
return out;
}
// entrait
//
// given any object, array, iterator or value, return an
// object with all of its values.
//
// undefined -> undefined
// value -> {value: true}
// array -> {[0][0]:[0][1], [1][0]:[1][1], ...}
// object -> object
// iterator -> {y0[0]:y0[1], y1[0]:y1[1], ...}
//
SAILib.entrait_op=function(a) { // full coverage pass
if (a===undefined) { // coverage(1,"entrait");
return undefined;
}
var out={};
var assign=function(k,v) {
if (k!==undefined && v!==undefined) out[k]=v;
}
if (mustIterate(a)) { // coverage(2,"entrait");
for (var j=a.next(); !j.done; j=a.next()) assign(j.value[0],j.value[1]);
} else if (isArray(a)) { // coverage(3,"entrait");
for (var i in a) assign(a[i][0],a[i][1]);
} else if (isObject(a)) { // coverage(4,"entrait");
return a;
} else { // coverage(5,"entrait");
out[a]=true;
}
return out;
}
// alter
//
// return the value of a function
//
SAILib.alter_op = function(a,f) { // test 'alter *'
// coverage(1,"alter"); pass
return f(a);
}
// observe
//
// execute a function, leaving the object unaltered
//
SAILib.observe_op = function(a,f) {
// coverage(1,"observe"); pass
f(a); // test 'observe *'
return a;
}
// audit
//
// Execute a function on every element of an array/list/generator
// But DOES NOT produce the results or alter the original array
// Returns the original array.
//
SAILib.audit_op = function(a,f) { // full coverage pass
if (isArray(a)) { // coverage(1,"audit");
var k=0,l=a.length;
while (k<l) { f(a[k],k); k++; }
} else if (mustIterate(a)) { // coverage(2,"audit");
a=SAILib.iterator_op(a);
return function *(){
var k=0;
for (var val of a) { f(val,k++); yield val; }
}();
} else if (isObject(a)) { // coverage(3,"audit");
for (var k in a) f(a[k],k);
}
// coverage(4,"audit");
return a;
}
// concat
//
// Create an array by concatenating two arrays.
// Forces things that are not arrays to be arrays.
// Returns a new array, unless "inplace" is set,
// then will modify the first array inplace if possible.
//
// [1, 2] concat [3, 4] -> [1, 2, 3, 4]
// [1, 2] concat [[3, 4], [5, 6]] -> [1, 2, [3, 4], [5, 6]]
// [1, 2] concat 3 -> [1, 2, 3]
// [1, 2] concat {c:3, d:4} -> [1, 2, {c:3, d:4}]
// [1, 2] concat undef -> [1, 2]
// 1 concat [3, 4] -> [1, 3, 4]
// 1 concat 3 -> [1, 3]
// 1 concat {c:3, d:4} -> [1, {c:3, d:4}]
// 1 concat undef -> [1]
// {a:1, b:2} concat [3, 4] -> [{a:1, b:2}, 3, 4]
// {a:1, b:2} concat 3 -> [{a:1, b:2}, 3]
// {a:1, b:2} concat {c:3, d:4} -> [{a:1, b:2}, {c:3, d:4}]
// {a:1, b:2} concat undef -> [{a:1, b:2}]
// undef concat undef -> undef
// undef concat 3 -> [3]
// undef concat {c:3, d:4} -> [{c:3, d:4}]
// undef concat [3, 4] -> [3, 4]
//
SAILib.concat_op = function(a,b,inplace) { // full coverage pass
if (a===undefined) {
if (b===undefined) { // coverage(1,"concat");
return undefined;
}
if (isArray(b) || mustIterate(b)) { // coverage(2,"concat");
return b;
}
// coverage(3,"concat");
return [b];
}
if (b===undefined) {
if (isArray(a) || mustIterate(a)) { // coverage(4,"concat");
return a;
}
// coverage(5,"concat");
return [a];
}
// promote values to single element arrays
if (mustIterate(a)) {
if (mustIterate(b)) { // coverage(6,"concat");
return function *() {
for (var val of a) yield val;
for (var val of b) yield val;
}();
} else if (isArray(b)) { // coverage(7,"concat");
b=b.slice(0);
return function *() {
for (var val of a) yield val;
for (var i in b) yield b[i];
}();
} else { // coverage(8,"concat");
return function *() {
for (var val of a) yield val;
yield b;
}();
}
throw new Error("Unhandled case in SAILib.concat");
}
if (!isArray(a)) { // coverage(9,"concat");
a=[a];
} else if (!inplace) { // coverage(10,"concat");
a=a.slice(0);
} else { // coverage(11,"concat");
;
}
if (mustIterate(b)) { // coverage(12,"concat");
for (var val of b) a.push(val);
} else if (isArray(b)) { // coverage(13,"concat");
a=a.concat(b);
} else { // coverage(14,"concat");
a.push(b);
}
// coverage(15,"concat");
return a;
}
// map
//
// execute a function on every element of an array/object/iterator
// returning a new array/object/iterator with the product of that
// repeatedly invoked function
//
SAILib.map_op = function(a,f) { // full coverage pass
if (a===undefined) { //coverage(1,"map");
return undefined; // test 'map undef'
}
if (isArray(a)) { // coverage(2,"map"); // test 'map list'
var r=[];
var k=0,l=a.length;
r.length=l;
while (k<l) {
r[k]=f(a[k],k);
k++;
}
return r;
} else if (mustIterate(a)) { // coverage(3,"map"); // test 'map iterable'
a=SAILib.iterator_op(a);
return function *(){
var k=0;
for (var val of a) yield f(val, k++);
}();
} else if (isObject(a)) { // coverage(4,"map"); // test 'map traits'
var r={};
for (var k in a) {
r[k]=f(a[k],k);
}
return r;
}
// coverage(5,"map");
return f(a); // test 'map value'
}
// filter
//
// Evaluats a function on every element of an array/object/iterator
// and returns a new array/object/iterator with only the elements
// the function returned "true" on.
//
SAILib.filter_op = function(a,f) { // full coverage pass
if (a===undefined) { // coverage(1, "filter");
return undefined;
}
if (isArray(a)) { // coverage(2,"filter");// test 'filter list'
var r=[];
for (var k in a) {
var v=a[k];
if (f(v,k)) r.push(v);
}
return r;
} else if (mustIterate(a)) { // coverage(3,"filter"); // test 'filter iterator'
a=SAILib.iterator_op(a);
return function *(){
var k=0;
for (var val of a) {
if (f(val, k++)) yield val;
}
}();
} else if (isObject(a)) { // coverage(4,"filter"); // test 'filter traits*'
var r={};
for (var k in a) {
var v=a[k];
if (f(v,k)) r[k]=v;
}
return r;
}
// coverage(5,"filter");
return f(a,undefined)?a:undefined; // test 'filter value*'
}
// reduce
//
// With a starting value (accumulator) that persists through invocations,
// invoke a function on every element of the provided array/object/iterator
// and return the accumulator.
//
SAILib.reduce_op = function(a,f,accum) { // full coverage pass
if (a===undefined) { // coverage(1,"reduce");
return undefined; // test 'reduce undef'
}
if (isArray(a)) {
var l=a.length;
if (!l) { //coverage(2,"reduce");
return accum;
}
// coverage(3,"reduce");
var k=0;
if (undefined===accum) { // coverage(7,"reduce");
accum=a[k++];
}
while (k<l) {
accum=f(accum,a[k],k);
k++;
}
return accum;
}
if (mustIterate(a)) { // coverage(4,"reduce");
a=SAILib.iterator_op(a);
return function*(){
var step=a.next();
if (step.done) { // coverage(5,"reduce");
yield accum; return;
}
// coverage(6,"reduce");
var k=0;
if (undefined===accum) { // coverage(8,"reduce");
accum=step.value;
step=a.next();
k++;
}
while (!step.done) {
accum=f(accum,step.value,k++);
step=a.next();
}
yield accum;
}();
}
if (isObject(a)) { // test 'reduce traits*'
if (undefined===accum) { // coverage(9,"reduce");
var first=true;
for (var k in a) {
if (first) {
accum=a[k];
first=0;
} else {
accum=f(accum,a[k],k);
}
}
} else { // coverage(10,"reduce");
for (var k in a) {
accum=f(accum,a[k],k);
}
}
return accum;
} // coverage(11,"reduce");
// simple value; wrap it in an array and try again, yay tail call
return SAILib.reduce_op([a],f,accum);
}
// slice
//
// return a subset of elements of the given list/iterator
//
// Singletons are wrapped in an array first.
//
/* rules
slice undef, -y last y rows
slice undef, 0 nothing
slice undef, undef everything
slice undef, +y first y rows
slice -x, -y everything except last y rows starting x from end of list
slice -x, 0 nothing
slice -x, undef last x rows
slice -x, +y y rows starting x from end of list
slice 0, -y everything except last y rows
slice 0, 0 nothing
slice 0, undef everything
slice 0, +y first y rows
slice +x, -y everything except last y rows starting at x
slice +x, 0 nothing
slice +x, undef everything starting at x
slice +x, +y y rows starting at x
*/
SAILib.slice_op = function(a,start,count) { // full coverage pass
//console.log(`slice ${start} ${count}`);
if (a===undefined) { // coverage(1,"slice");
return undefined;
}
if (count===0) { // return empty set
if (mustIterate(a)) { // coverage(2,"slice");
return function*(){};
} else { // coverage(3,"slice");
return [];
}
}
if (mustIterate(a)||isArray(a)) {
//coverage(4,"slice");
} else { //coverage(5,"slice");
a=[a];
}
if (count===undefined && (start===undefined || start===0)) { // return everything
if (isArray(a)) { // coverage(6,"slice");
return a.slice(0);
} else { // coverage(7,"slice");
return a;
}
}
if (count>0 && (start===undefined || start===0)) { // return first count rows
if (isArray(a)) { // coverage(8,"slice");
return a.slice(0,count);
} else { // coverage(9,"slice");
a=SAILib.iterator_op(a);
return function*() {
let v=a.next();
while (count-- && !v.done) {
yield v.value;
v=a.next();
}
}();
}
}
if (isArray(a)) { //coverage(10,"slice");
let len=a.length;
let i1=0,i2=len;
if (start===undefined && count<0) { // coverage(11,"slice");// u, -y
i1=len+count;
} else if (start===0 && count<0) { // coverage(12,"slice");// 0, -y
i2=len+count;
} else if (start>0) { // coverage(13,"slice");
i1=start;
if (count<0) { // coverage(14,"slice");// +x -y
i2=len+count;
} else if (count>0) { // coverage(15,"slice");// +x +y
i2=i1+count;
} // +x u
} else if (start<0) { // coverage(16,"slice");
i1=len+start;
if (count<0) { // coverage(17,"slice");// -x -y
i2=len+count;
} else if (count>0) { // coverage(18,"slice"); // -x +y
i2=i1+count;
} // -x u
}
return a.slice(i1,i2);
}
// generator
let skip=0; // items to skip
let crop=0; // items to throw away off the end
let collect=0; // size of cache
let limit=undefined; // stop after collecting this many
let keep=true;
if ((start===undefined && count<0)) { // coverage(19,"slice");// u, -
keep=false;
collect=-count;
} else if (start<0) { // coverage(20,"slice");
collect=-start;
keep=false;
if (count<0) { // coverage(21,"slice");// -, -
crop=-count;
} else if (count>0) { // coverage(22,"slice"); // -, +
crop=collect-count;
} // -, u
} else if (start>0) { // coverage(23,"slice");
skip=start;
if (count<0) { // coverage(24,"slice");// +, -
collect=-count;
crop=-count;
} else if (count>0) { // coverage(25,"slice");// +, +
limit=count;
} // +, u
} else { // coverage(26,"slice");// 0, -
collect=-count;
crop=-count;
}
a=SAILib.iterator_op(a);
//console.log(`skip ${skip} - crop ${crop} - collect ${collect} - limit ${limit}`);
return function*(){
let cache=[];
let v=a.next();
while (skip-- && !v.done) { // coverage(27,"slice");
v=a.next();
}
while (!v.done) { // coverage(28,"slice");
cache.push(v.value);
if (cache.length>collect) { // coverage(29,"slice");
let k=cache.shift();
if (keep) { // coverage(30,"slice");
if (limit-- <= 0) { // coverage(31,"slice");
return;
}
yield k;
}
}
v=a.next();
}
while (cache.length>crop) { // coverage(32,"slice");
yield cache.shift();
}
}();
}
// element
//
// returns a single element from an array/iterator
//
SAILib.element_op = function(a,index) { // full coverage pass
if (a===undefined) { //coverage(4,"element");
return undefined;
}
if (isArray(a)) { // coverage(1,"element");
return a[index];
} else if (mustIterate(a)) { // coverage(2,"element");
a=SAILib.iterator_op(a);
a=SAILib.slice_op(a,index,1);
var v=a.next();
return v.value;
}
if (index===0 || index===-1) { //coverage(6,"element");
return a;
}
throw new Error("SAILib: unexpected code path in SAILib.element");
}
// clone
//
// create a shallow copy of an array or object
//
SAILib.clone_op = function(a) { // full coverage pass
if (isArray(a)) { // coverage(1,"clone");
return a.slice(0);
}
if (isObject(a)) { // coverage(2,"clone");
var b={};
for (var i in a) {
if (undefined!==a[i]) b[i]=a[i];
}
return b;
}
// coverage(3,"clone");
return a;
}
// overlay
//
// creates a new collection with the left collection overlaid by the right collection
//
// [1, 2, 3] overlay [4, undefined, 6] -> [4, 2, 6]
// {a:1, b:2} overlay {c:3, b:4, a:undefined} -> {a:1, b:4, c:3}
//
SAILib.overlay_op = function(l,r) {// full coverage pass
if (l===undefined) { // coverage(1,"overlay");
l={};
}
if (!isMergable(l)) { // coverage(2,"overlay");
throw new Error("SAI: Attempt to OVERLAY onto something that's not a collection/iterable.");
}
if (!isMergable(r)) { // coverage(3,"overlay");
throw new Error("SAI: Attempt to OVERLAY with something that's not a collection/iterable.");
}
if (!mustIterate(l)) { // left side static
l=SAILib.clone_op(l); // no in-place modification
if (mustIterate(r)) { // coverage(4,"overlay");
r=SAILib.iterator_op(r);
// right side iterator
return function*(){
var v=r.next();
for (var i in l) {
if (!v.done) {
yield (undefined===v.value) ? l[i] : v.value;
v=r.next();
} else {
yield l[i];
}
}
}();
}
// coverage(5,"overlay");
// right side static - things were so much simpler then
for (var i in r) {
if (r[i]!==undefined) l[i]=r[i];
}
return l;
} else {
l=SAILib.iterator_op(l);
// left side iterable
if (mustIterate(r)) { // coverage(6,"overlay");
r=SAILib.iterator_op(r);
// right side iterable
return function*(){
var vl=l.next(),vr=r.next();
while (!vr.done) {
yield (vr.value===undefined) ? vl.value : vr.value;
vl=l.next(); vr=r.next();
}
yield *l;
}();
}
// coverage(7,"overlay");
// right side static
r=SAILib.clone_op(r); // in case it is changed
return function*(){
var i=0,v=l.next();
while (!v.done) {
var o=r[i++];
yield (o===undefined)?v.value:o;
v=l.next();
}
}();
}
throw new Error("SAI: unexpected code path in OVERLAY");
}
// select
//
// get only elements of src enumerated by keys
//
// ['Apple', 'Banana', 'Cherry', 'Durian'] select [2, 0] -> ['Cherry', 'Apple']
// {a:1, b:2, c:3, d:4} select ["a","c"] -> {a:1, c:3}
//
SAILib.select_op = function(src,keys) { // full coverage pass
if (!isMergable(src)) { // coverage(1,"select");
throw new Error("SAI: Left argument to SELECT must be list/traits/iterable.");
}
if (!isMergable(keys)) {
if (keys===undefined) { // coverage(2,"select");
return undefined;
}
// coverage(3,"select");
keys=[keys];
}
if (isArray(src)) { // lhs array
if (isArray(keys)) { // coverage(4,"select"); // test 'select list list' // console.log("path 1");
var j=0,result=[];
for (var i in keys) result[j++]=src[keys[i]];
return result;
} else if (mustIterate(keys)) { // coverage(5,"select"); // test 'select list iterable' // console.log("path 2");
keys=SAILib.iterator_op(keys);
src=SAILib.clone_op(src);
return function*(){
for (var i of keys) yield src[i];
}();
} // test 'select list traits' // else rhs traits console.log("path 3");
// coverage(6,"select");
var j=0,result=[];
for (var i in keys) result.push(src[i]);
return result;
} else if (mustIterate(src)) { // lhs iterator
src=SAILib.iterator_op(src);
if (mustIterate(keys)) { // coverage(7,"select");// test 'select iterable iterable' // rhs iterator console.log("path 4");
keys=SAILib.iterator_op(keys);
return function*(){
var buf=[],len=0;
for (v of keys) {
while (len<=v) {
var lv=src.next();
if (lv.done) return; // request for element that doesn't exist, terminate iteration
buf[len++]=lv.value;
}
yield buf[v];
}
}();
}
if (isArray(keys)) { // coverage(8,"select"); // test 'select iterable list' // rhs list console.log("path 5");
keys=SAILib.clone_op(keys);
return function*(){
var buf=[],len=0;
for (var i in keys) {
var v=keys[i];
while (len<=v) {
var lv=src.next();
if (lv.done) return; // request for element that doesn't exist, terminate iteration
buf[len++]=lv.value;
}
yield buf[v];
}
}();
} // test 'select iterable traits' // rhs traits console.log("path 6");
// coverage(9,"select");
keys=SAILib.keys_op(keys).sort();
return function*(){
var i=0,j=0;
for (var v of src) if (i++==keys[j]) { yield v; j++; if (j>=keys.length) break;}
}();
} // else lhs traits
if (mustIterate(keys)) { // coverage(10,"select"); // test 'select traits iterable' // console.log("path 7");
keys=SAILib.iterator_op(keys);
src=SAILib.clone_op(src);
return function*(){
for (var v of keys) {
if (v!==undefined) yield src[v];
}
}();
} else if (isArray(keys)) { // coverage(11,"select"); // test 'select traits list' console.log("path 8");
var result={};
for (var i in keys) { var v=keys[i]; result[v]=src[v]; }
return result;
} // rhs traits // test 'select traits traits
var result={};
if (isObject(keys)) { // coverage(12,"select");
for (var i in keys) result[i]=src[i];
} else { // coverage(13,"select");
result[keys]=src[keys];
}
return result;
}
// update
//
// Updates a collection of traits in-place.
//
// [1, 2, 3] update [4, undefined, 6] -> [4, 2, 6]
// {a:1, b:2} update {c:3, b:4, a:undefined} -> {a:1, b:4, c:3}
//
SAILib.update_op = function(dest,keys) { // full coverage pass // ITERATORS ONLY ON RIGHT SIDE
if (dest===undefined) { // coverage(1,"update");
dest={};
}
if (!(isArray(dest)||isObject(dest))) { // coverage(2,"update");
throw new Error("Attempt to UPDATE into something that's not a list or traits.");
}
if (!isMergable(keys)) { // coverage(3,"update");
throw new Error("Attempt to UPDATE from something that's not a list or traits.");
}
if (mustIterate(keys)) { // coverage(4,"update");
keys=SAILib.iterator_op(keys);
var i=0;
for (var v of keys) {
if (v!==undefined) dest[i]=v;
i++;
}
} else { // coverage(5,"update");
for (var i in keys) {
var v=keys[i];
if (v!==undefined) dest[i]=v;
}
}
return dest;
}
// delete
//
// Delete, in place, specific items from a collectios
//
// [1, 2, 3, 4] delete [1, 2] -> [1, 3]
// [a:1, b:2, c:3] delete ["a"] -> [b:2, c:3]
// [a:1, b:2, c:3] delete {b:5} -> [a:1, c:3]
//
SAILib.delete_op = function(dest,keys) { // full coverage pass // ITERATORS ONLY ON RIGHT SIDE
if (mustIterate(dest)) { // coverage(1,"delete");
throw new Error("SAI: Attempt to DELETE from an iterator, which is not presently supported.")
}
if (!isObject(dest)) { // coverage(2,"delete");
throw new Error("SAI: Attempt to DELETE from something that's not a list or traits.");
}
if (isArray(dest)) {
if (!isMergable(keys)) {
if (keys !== undefined) { // coverage(3,"delete");
dest.splice(keys,1);
} else { // coverage(4,"delete");
}
} else if (isArray(keys)) { // coverage(5,"delete");
for (var i in keys) { var v=keys[i]; if (v!==undefined) dest.splice(keys[i],1); }
} else if (mustIterate(keys)) { // coverage(6,"delete");
keys=SAILib.iterator_op(keys);
for (var v of keys) { if (v!==undefined) dest.splice(v,1); }
} else if (isObject(keys)) { // coverage(7,"delete");
for (var i in keys) dest.splice(i,1);
} else {
throw new Error("SAI: Unexpected execution path in SAILib.delete what the heck is the destination value?!")
}
} else {
if (!isMergable(keys)) { // coverage(8,"delete");
delete dest[keys];
} else if (isArray(keys)) { // coverage(9,"delete");
for (var i in keys) { var v=keys[i]; if (v!==undefined) delete dest[keys[i]]; }
} else if (mustIterate(keys)) { // coverage(10,"delete");
keys=SAILib.iterator_op(keys);
for (var v of keys) { if (v!==undefined) delete dest[v]; }
} else {
if (isObject(keys)) { // coverage(11,"delete");
for (var i in keys) delete dest[i];
} else { // coverage(12,"delete");
delete dest[keys];
}
}
}
return dest;
}
// deepFreeze
//
// Freeze an object and all of its properties (ensure they cannot be changed)
//
SAILib.deepFreeze = function(o) {
var prop, propKey;
Object.freeze(o); // First freeze the object.
for (propKey in o) {
prop = o[propKey];
if (!o.hasOwnProperty(propKey) || !(typeof prop === 'object') || Object.isFrozen(prop)) {
continue;
}
SAILib.deepFreeze(prop); // Recursively call deepFreeze.
}
}
// xor
//
// return true if a XOR b
//
SAILib.xor_op = function(a,b) { // test 'xor'
return a?(b?false:a):(b?b:false);
}
// min
//
// return the lower value
//
SAILib.min_op = function(a,b) { // test 'min'
return (a<b)?(a):(b);
}
// max
//
// return the higher value
//
SAILib.max_op = function(a,b) { // test 'max'
return (a>b)?(a):(b);
}
// compare
//
// return -1 if a is less than b
// return 1 if a is greater than b
// otherwise return 0
//
SAILib.compare_op = function(a,b) {
if (a<b) return -1;
if (a>b) return 1;
return 0;
}
// keys
//
// return the keys (item identifiers) from an object in an array.
//
SAILib.keys_op = function(a) { // full coverage pass
var result=[];
if (isArray(a)) { // coverage(1,"keys"); // test 'keys list'
var len=a.length;
for (var i = 0; i<len; result.push(i++));
} else if (mustIterate(a)) { // coverage(2,"keys");
a=SAILib.iterator_op(a);
var i=0;
for (var v of a) result.push(i++);
} else if (isObject(a)) { // coverage(3,"keys");
for (var i in a) result.push(i);
} // coverage(4,"keys");
// test 'keys value' & 'keys undefined'
return result;
}
// count
//
// return how many items are in a collection
//
SAILib.count_op = function(a) { // full coverage pass
var result=0;
if (isArray(a)) { // coverage(1,"count");
result=a.length;
} else if (mustIterate(a)) { // coverage(2,"count");
a=SAILib.iterator_op(a);
for (var v of a) result++;
} else if (isObject(a)) { // coverage(3,"count");
for (var i in a) result++;
} else if (a===undefined) { // coverage(4,"count");
result=0;
} else { // coverage(5,"count");
result=1;
}
return result;
}
// values
//
// return a list of all of the item values in a collectios
//
SAILib.values_op = function(a) { // full coverage pass
var result=[];
if (isArray(a)) { // coverage(1,"values"); // test 'values list'
return SAILib.clone_op(a);
} else if (mustIterate(a)) { // coverage(2,"values");// test 'values iterable'
return SAILib.collect_op(a);
} else if (isObject(a)) { // coverage(3,"values"); // test 'values traits'
for (var i in a) if (a[i]!==undefined) result.push(a[i]);
} else if (a !== undefined) { // coverage(4,"values"); // test 'values value'
result.push(a);
} else { // coverage(5,"values");
;
}
return result;
}
// newerror
//
// prepare an error object for throwing
//
SAILib.newerror = function(line,file,parameters) {
var e = new Error(parameters.message,file,line);
for (var i in parameters) e[i]=parameters[i];
return e;
}
// number
//
// convert a value into a number, or zero if that's not possible.
//
SAILib.number_op = function(x) {
var n=parseFloat(x);
return isNaN(n)?0:n;
}
// expects
//
// (see 20.Keywords.md for description of purpose)
//
SAILib.expects_op = function(params,prototype) {
var result=[];
for (var j in prototype) {
var type=prototype[j];
if (j==='_root') {
if (type===typeof params) {
// good
} else if (params.isof && params.isof[type]) {
// good
} else {
if (!params.isof) {
result.push({trait:j,expects:type,found:typeof params});
} else {
result.push({trait:j,expects:type,found:params.isa});
}
}
} else if (!params[j]) {
result.push({trait:j,expects:type,found:'undefined'});
} else if (type!==true) {
var param=params[j];
if (type===typeof param) {
// good
} else if (param.isof && param.isof[type]) {
// good
} else {
if (!param.isof) {
result.push({trait:j,expects:type,found:typeof param});
} else {
result.push({trait:j,expects:type,found:param.isa});
}
}
}
}
return result;
}
// expectsThrow
//
// Verify parameters match a prototype, throwing an exception if they don't.
//
SAILib.expectsThrow = function(params,prototype,name) {
var x=SAILib.expects_op(params,prototype);
if (!x.length) return;
var err=[];
for (var i in x) {
var j=x[i];
err.push(j.trait+" should be "+j.expects+", but it's "+j.found);
}
throw new Error('SAI: parameter exception in '+name+'\n'+err.join('\n'));
}
// FinalizePrototype
//
// Lock and freeze prototype attributes as needed.
// Verify contracts are fulfilled
// Bind and build an instantiation function.
//
SAILib.finalizePrototype = function(proto) {
for (var i in proto.__tobelocked) {
Object.defineProperty(proto,proto.__tobelocked[i],{configurable:false});
}
delete proto.__tobelocked;
for (var i in proto.__tobefrozen) {
SAILib.deepFreeze(proto[proto.__tobefrozen[i]]);
}
delete proto.__tobefrozen;
if (proto.__unverified) {
for (var i in proto.__contracts) {
var l=proto.__contracts[i];
if (undefined===proto[l]) {
throw new Error("SAI: Contractually required trait '"+l+"' does not exist in object '"+proto.isa+"'.");
}
}
delete proto.__unverified;
delete proto.__contracts;
}
//console.log("finalizing "+proto.isa);
proto.constructor=function() {
//console.log("constructor for "+proto.isa);
var obj=Object.create(proto);
if (obj.Constructor) obj.Constructor();
if (obj.Instantiate) obj.Instantiate.apply(obj,arguments);
return obj;
};
}
// create
//
// Function called by compiled SAI to instantiate new SAI objects by name.
// The following code is overridden when running SAI in managed mode.
//
SAILib._protocache = {};
SAILib.create_op = function(name,parameters) {
// console.log("trying to create a "+name);
var proto=undefined;
if (!(proto=this._protocache[name])) {
var fn=name+".js";
var src=FS.readFileSync(fn,"utf8");
src='(function(exports, require, module, __filename, __dirname) {'+src+'});';
var mod=eval(src);
var proto=mod({},require,{},fn,__dirname);
this._protocache[name]=proto;
}
if (!proto) throw new Error('SAI.Create: Do not know how to create SAI object "'+name+'".');
var obj=Object.create(proto);
if (obj.Constructor) obj.Constructor();
if (obj.Instantiate) {
// console.log("Instantiating "+name);
obj.Instantiate.apply(obj,parameters);
}
return obj;
}
// singleton
//
// Function called by compiled SAI to instantiate singleton SAI objects by name.
//
SAILib._singletoncache = {};
SAILib.singleton_op = function(name,parameters) {
if (SAILib._singletoncache[name]) {
return SAILib._singletoncache[name];
}
return SAILib._singletoncache[name]=SAILib.create_op(name,parameters);
}
// _prototype
//
// Prototype for SAI objects
//
SAILib._prototype = function() {
this.Constructor=function(){};
this.__tobelocked=[];
this.__tobefrozen=[];
this.__contracts=[];
this.__unverified=true;
this.isof={};
}