npm update + tweaks...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2022-08-03 01:57:57 +03:00
parent 30140f0918
commit 2b58903675
4 changed files with 495 additions and 305 deletions

View File

@ -7,6 +7,8 @@ BOOTSTRAP_FILES := \
README.md
LOCAL_MODULES := \
node_modules/ig-doc/doc.js \
node_modules/ig-stoppable/stoppable.js \
node_modules/ig-object/object.js \
node_modules/ig-actions/actions.js \
node_modules/ig-features/features.js

137
lib/doc.js Executable file
View File

@ -0,0 +1,137 @@
/**********************************************************************
*
* doc.js
*
* Basic JavaScript self-documentation utils
*
***********************************************/ /* c8 ignore next 2 */
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/
module.TAB_SIZE = 4
module.LEADING_TABS = 1
// Normalize code indent...
//
// normalizeIndent(text)
// -> text
//
//
// This will remove common indent from each line of text, this is useful
// for printing function code of functions that were defined at deep
// levels of indent.
//
// This will ignore the indent of the first line.
//
// If the last line is indented higher or equal to the rest of the text
// we will use leading_tabs (defaults to LEADING_TABS) to indent the
// rest of the text.
// This will indent the following styles correctnly:
//
// |function(a, b){ |function(a, b){
// | return a + b } | return a + b
// | |}
//
//
// NOTE: this will trim out both leading and trailing white-space.
// NOTE: this is generally code-agnostic with one sigificant
// exception -- normalizeIndent(..) will break code written
// in Whitespace.
//
// XXX BUG?
// `a `a `a
// | b -> |b expected? | b
// | c` | c` | c`
// while:
// `a `a
// | b -> | b as expected.
// | c` | c`
// this leads to functions like the following to get messed up:
// |function(a){
// | return a
// | || 'moo' }
//
// XXX is this the right place for this???
// ...when moving take care that ImageGrid's core.doc uses this...
var normalizeIndent =
module.normalizeIndent =
function(text, {tab_size=module.TAB_SIZE, leading_tabs=module.LEADING_TABS, pad_tabs=0}={}){
leading_tabs *= tab_size
var padding = ' '.repeat(pad_tabs*tab_size)
// prepare text...
var tab = ' '.repeat(tab_size || 0)
text = tab != '' ?
text.replace(/\t/g, tab)
: text
// trim the tail and remove leading blank lines...
var lines = text.trimEnd().split(/\n/)
while(lines.length > 0
&& lines[0].trim() == ''){
// XXX we have two options here:
// - indent everyline including the first non-blank
// - do not indent anything (current)
// ...not sure which is best...
leading_tabs = 0
lines.shift() }
// count common indent...
var l = lines
.reduce(function(l, e, i){
var indent = e.length - e.trimLeft().length
return e.trim().length == 0
// ignore 0 indent of first line...
|| (i == 0 && indent == 0) ?
l
// last line...
: i == lines.length-1
&& indent >= l ?
// XXX feels a bit overcomplicated...
(l < 0 ?
// last of two with 0 indent on first -> indent...
Math.max(indent - leading_tabs, 0)
// ignore leading_tabs if lower indent...
: Math.min(l, Math.max(indent - leading_tabs, 0)))
// initial state...
: l < 0 ?
indent
// min...
: Math.min(l, indent) }, -1) || 0
// normalize...
return padding
+lines
.map(function(line, i){
return i == 0 ?
line
: line.slice(l) })
.join('\n'+ padding)
.trim() }
// shorthand more suted for text...
var normalizeTextIndent =
module.normalizeTextIndent =
function(text, opts={leading_tabs: 0}){
return module.normalizeIndent(text, opts) }
// template string tag versions of the above...
var doc =
module.doc =
function(strings, ...values){
return normalizeIndent(strings
.map(function(s, i){ return s + (values[i] || '') })
.join('')) }
var text =
module.text =
function(strings, ...values){
return normalizeTextIndent(strings
.map(function(s, i){ return s + (values[i] || '') })
.join('')) }
/**********************************************************************
* vim:set ts=4 sw=4 nowrap : */ return module })

View File

@ -21,6 +21,19 @@
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/
var doc = require('ig-doc')
module.doc = doc.doc
module.text = doc.text
module.normalizeIndent = doc.normalizeIndent
module.normalizeTextIndent = doc.normalizeTextIndent
var STOP =
module.STOP =
require('ig-stoppable').STOP
//---------------------------------------------------------------------
// Function methods to link into a constructor producing a callable
// defined via .__call__(..)
@ -49,7 +62,9 @@ module.LINK_FUNCTION_METHODS = [
//
var BOOTSTRAP =
function(func){
var b = BOOTSTRAP.__delayed = BOOTSTRAP.__delayed || []
var b = BOOTSTRAP.__delayed =
BOOTSTRAP.__delayed
|| []
func ?
b.push(func)
: b.map(function(f){ f() }) }
@ -58,144 +73,36 @@ function(func){
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
module.TAB_SIZE = 4
module.LEADING_TABS = 1
// Normalize code indent...
//
// normalizeIndent(text)
// -> text
//
//
// This will remove common indent from each line of text, this is useful
// for printing function code of functions that were defined at deep
// levels of indent.
//
// This will ignore the indent of the first line.
//
// If the last line is indented higher or equal to the rest of the text
// we will use leading_tabs (defaults to LEADING_TABS) to indent the
// rest of the text.
// This will indent the following styles correctnly:
//
// |function(a, b){ |function(a, b){
// | return a + b } | return a + b
// | |}
//
//
// NOTE: this will trim out both leading and trailing white-space.
// NOTE: this is generally code-agnostic with one sigificant
// exception -- normalizeIndent(..) will break code written
// in Whitespace.
//
// XXX BUG?
// `a `a `a
// | b -> |b expected? | b
// | c` | c` | c`
// while:
// `a `a
// | b -> | b as expected.
// | c` | c`
// this leads to functions like the following to get messed up:
// |function(a){
// | return a
// | || 'moo' }
//
// XXX is this the right place for this???
// ...when moving take care that ImageGrid's core.doc uses this...
var normalizeIndent =
module.normalizeIndent =
function(text, {tab_size=module.TAB_SIZE, leading_tabs=module.LEADING_TABS, pad_tabs=0}={}){
leading_tabs *= tab_size
var padding = ' '.repeat(pad_tabs*tab_size)
// prepare text...
var tab = ' '.repeat(tab_size || 0)
text = tab != '' ?
text.replace(/\t/g, tab)
: text
// trim the tail and remove leading blank lines...
var lines = text.trimEnd().split(/\n/)
while(lines.length > 0
&& lines[0].trim() == ''){
// XXX we have two options here:
// - indent everyline including the first non-blank
// - do not indent anything (current)
// ...not sure which is best...
leading_tabs = 0
lines.shift() }
// count common indent...
var l = lines
.reduce(function(l, e, i){
var indent = e.length - e.trimLeft().length
return e.trim().length == 0
// ignore 0 indent of first line...
|| (i == 0 && indent == 0) ?
l
// last line...
: i == lines.length-1
&& indent >= l ?
// XXX feels a bit overcomplicated...
(l < 0 ?
// last of two with 0 indent on first -> indent...
Math.max(indent - leading_tabs, 0)
// ignore leading_tabs if lower indent...
: Math.min(l, Math.max(indent - leading_tabs, 0)))
// initial state...
: l < 0 ?
indent
// min...
: Math.min(l, indent) }, -1) || 0
// normalize...
return padding
+lines
.map(function(line, i){
return i == 0 ?
line
: line.slice(l) })
.join('\n'+ padding)
.trim() }
// shorthand more suted for text...
var normalizeTextIndent =
module.normalizeTextIndent =
function(text, opts={leading_tabs: 0}){
return module.normalizeIndent(text, opts) }
// template string tag versions of the above...
var doc =
module.doc =
function(strings, ...values){
return normalizeIndent(strings
.map(function(s, i){ return s + (values[i] || '') })
.join('')) }
var text =
module.text =
function(strings, ...values){
return normalizeTextIndent(strings
.map(function(s, i){ return s + (values[i] || '') })
.join('')) }
// Get keys from prototype chain...
//
// deepKeys(obj)
// deepKeys(obj, stop)
// deepKeys(obj[, all])
// deepKeys(obj, stop[, all])
// -> keys
//
// List all keys incuding non-enumerable...
// deepKeys(obj, true)
// deepKeys(obj, stop, true)
// -> keys
//
//
// NOTE: this is like Object.keys(..) but will get keys for all levels
// till stop if given...
// NOTE: by default this lists only enumerable keys...
var deepKeys =
module.deepKeys =
function(obj, stop){
function(obj, stop, all){
all = arguments[arguments.length-1]
all = (all === true || all === false) ?
all
: false
stop = (stop === true || stop === false) ?
undefined
: stop
var res = []
while(obj != null){
res.push(Object.keys(obj))
res.push(Object.keys(all ?
Object.getOwnPropertyDescriptors(obj)
: obj))
if(obj === stop){
break }
obj = obj.__proto__ }
@ -218,8 +125,8 @@ function(obj, stop){
// Non-strict match...
// match(a, b, true)
//
// This is similar to the default case but uses equality rather than
// identity to match values.
// This is similar to the default case but uses equality rather than
// identity to match values.
//
//
// NOTE: this will do a shallow test using Object.keys(..) thus .__proto__
@ -235,7 +142,8 @@ function(base, obj, non_strict){
return false }
// attr count...
var o = Object.keys(Object.getOwnPropertyDescriptors(obj))
if(Object.keys(Object.getOwnPropertyDescriptors(base)).length != o.length){
if(Object.keys(Object.getOwnPropertyDescriptors(base)).length
!= o.length){
return false }
// names and values...
o = o.map(function(k){
@ -285,13 +193,16 @@ module.create =
function(obj){
// name given...
var name = ''
if(typeof(obj) == 'string' && arguments.length > 1){
if(typeof(obj) == 'string'
&& arguments.length > 1){
;[name, obj] = arguments
// sanity check...
if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name.trim())){
throw new Error(`create(..): invalid name: "${name}"`) } }
// calable...
if(typeof(obj) == 'function'){
// NOTE: this is ignored by c8 as we will never run this directly
// as it will immediately get eval(..)'ed...
/* c8 ignore next 9 */
var func = function(){
return '__call__' in func ?
@ -306,12 +217,15 @@ function(obj){
// NOTE: we just created func(..) so no need to sanitize it, the
// only potential vector of atack (AFAIK) here is name and
// that is checked above...
func.name = name
Object.defineProperty(func, 'name', {value: name})
/* XXX NAME...
//func.name = name
func.name != name
&& (func = eval('('+
func
.toString()
.replace(/function\(/, `function ${name}(`) +')'))
//*/
func.__proto__ = obj
__toStringProxy(func)
return func }
@ -329,82 +243,43 @@ BOOTSTRAP(function(){
//
// XXX EXPERIMENTAL
module.Error =
Constructor('Error', Error, {
get name(){
return this.constructor.name },
Constructor('Error', Error, {
get name(){
return this.constructor.name },
// XXX BUG? is this an error that with this everything seems to work
// while without this instances of this work fine while instances
// of "sub-classes" do not set the .stack correctly???
// ...is this a JS quirk or am I missing something???
__new__: function(context, ...args){
return Reflect.construct(module.Error.__proto__, args, this.constructor) },
//return Reflect.construct(Error, args, this.constructor) },
})
// XXX BUG? is this an error that with this everything seems
// to work while without this instances of this work
// fine while instances of "sub-classes" do not set
// the .stack correctly???
// ...is this a JS quirk or am I missing something???
__new__: function(context, ...args){
return Reflect.construct(
module.Error.__proto__, args, this.constructor) },
//return Reflect.construct(Error, args, this.constructor) },
})
})
//---------------------------------------------------------------------
// Prototype chain content access...
BOOTSTRAP(function(){
// Value trigger iteration stop and to carry results...
//
module.STOP =
Constructor('STOP', {
doc: 'stop iteration.',
__init__: function(value){
this.value = value },
})
})
// Get a list of source objects for a prop/attr name...
// Get source objects for a prop/attr name...
//
// sources(obj, name)
// sources(obj, name, callback)
// -> list
// -> []
//
// Get callables or objects defining .__call__ (special-case)
// sources(obj, '__call__')
// sources(obj, '__call__', callback)
// -> list
// -> []
// -> iterator
//
// Get full chain...
// sources(obj)
// sources(obj, callback)
// -> list
// -> iterator
//
//
// callback(obj, i)
// -> STOP
// -> STOP(value)
// -> ..
//
//
// The callback(..) is called with each matching object.
//
// callback(..) return values:
// - STOP - stop the search and return the match list terminated
// with the object triggering the stop.
// - STOP(value) - stop the search and return the match list terminated
// with the value passed to STOP(..)
// - undefined - return the triggering object as-is
// NOTE: this is the same as returning [obj]
// - array - merge array content into the result insteaad of
// the triggering value.
// NOTE: an ampty array will effectively omit the
// triggering object from the results.
// - other - return a value instead of the triggering object.
// Get callables or objects defining .__call__ (special-case)
// sources(obj, '__call__')
// -> iterator
//
//
// NOTE: this gos up the prototype chain, not caring about any role (
// NOTE: this goes up the prototype chain, not caring about any role (
// instance/class or instance/prototype) bounderies and depends
// only on the object given as the starting point.
// It is possible to start the search from this, thus checking
@ -413,96 +288,64 @@ BOOTSTRAP(function(){
// NOTE: this will not trigger any props...
var sources =
module.sources =
function(obj, name, callback){
// get full chain...
if(typeof(name) == 'function'){
callback = name
name = undefined
}
var i = 0
var o
var res = []
function*(obj, name=undefined){
while(obj != null){
//if(obj.hasOwnProperty(name)){
if(name === undefined
|| obj.hasOwnProperty(name)
|| (name == '__call__' && typeof(obj) == 'function')){
// handle callback...
o = callback
&& callback(obj, i++)
// manage results...
res.push(
(o === undefined || o === module.STOP) ?
[obj]
: o instanceof module.STOP ?
o.value
: o )
// stop...
if(o === module.STOP
|| o instanceof module.STOP){
return res.flat() } }
obj = obj.__proto__ }
return res.flat() }
|| obj.hasOwnProperty(name)
|| (name == '__call__'
&& typeof(obj) == 'function')){
yield obj }
obj = obj.__proto__ } }
// Get a list of values/props set in source objects for a prop/attr name...
// Get object-value/prop pairs set in source objects for a prop/attr name...
//
// entries(obj, name)
// -> iterator
//
// Get values...
// values(obj, name)
// values(obj, name, callback)
// -> list
// -> []
//
// Get propery descriptors...
// values(obj, name, true)
// values(obj, name, callback, true)
// -> list
// -> []
//
// callback(value/prop, obj)
// -> STOP
// -> STOP(value)
// -> ..
//
// entries(obj, name, true)
// -> iterator
//
//
// Item format:
// [
// source,
// value,
// ]
//
//
// Special case: name is given as '__call__'
// This will return either the value the object if it is callable
// or the value of .__call__ attribute...
//
//
// NOTE: for more docs on the callback(..) see sources(..)
var values =
module.values =
function(obj, name, callback, props){
props = callback === true ?
callback
: props
var _get = function(obj, name){
return props ?
// NOTE: for more docs see sources(..)
var entries =
module.entries =
function*(obj, name, props=false){
for(var obj of sources(obj, name)){
yield [
obj,
props ?
Object.getOwnPropertyDescriptor(obj, name)
// handle callable instance...
: !(name in obj)
&& name == '__call__'
&& typeof(obj) == 'function' ?
obj
// normal attr...
: obj[name] }
// wrap the callback if given...
var c = typeof(callback) == 'function'
&& function(obj, i){
var val = _get(obj, name)
var res = callback(val, obj, i)
return res === module.STOP ?
// wrap the expected stop result if the user did not do it...
module.STOP(val)
: res }
return c ?
// NOTE: we do not need to handle the callback return values as
// this is fully done by c(..) in sources(..)
sources(obj, name, c)
: sources(obj, name)
.map(function(obj){
return _get(obj, name) }) }
: obj[name],
] }}
// Get values/props set in source objects for a prop/attr name...
//
// NOTE: this is specialization of entries(..), see that for more info.
var values =
module.values =
function*(obj, name, props=false){
for(var [_, value] of entries(...arguments)){
yield value }}
// Find the next parent attribute in the prototype chain.
@ -588,24 +431,22 @@ function(proto, name){
if(name == ''){
throw new Error('parent(..): need a method with non-empty .name') }
// get first matching source...
proto = sources(that, name,
function(obj, i){
// NOTE: the .hasOwnProperty(..) test is here so as
// to skip the base callable when searching for
// .__call__ that is returned as a special case
// by sourcei(..) and this should have no effect
// or other cases...
// NOTE: this will only skip the root callable...
return (i > 0 || obj.hasOwnProperty(name))
&& obj[name] === proto
&& module.STOP })
.pop() }
var i = 0
for(var obj of sources(that, name)){
// NOTE: the .hasOwnProperty(..) test is here so as
// to skip the base callable when searching for
// .__call__ that is returned as a special case
// by source(..) and this should have no effect
// or other cases...
// NOTE: this will only skip the root callable...
if((i++ > 0 || obj.hasOwnProperty(name))
&& obj[name] === proto){
proto = obj
break }}}
// get first source...
var res = sources(proto, name,
function(obj, i){
return i == 1
&& module.STOP })
.pop()
var res = sources(proto, name)
res.next()
res = res.next().value
return !res ?
undefined
:(!(name in res) && typeof(res) == 'function') ?
@ -624,11 +465,9 @@ var parentProperty =
module.parentProperty =
function(proto, name){
// get second source...
var res = sources(proto, name,
function(obj, i){
return i == 1
&& module.STOP })
.pop()
var res = sources(proto, name)
res.next()
res = res.next().value
return res ?
// get next value...
Object.getOwnPropertyDescriptor(res, name)
@ -674,7 +513,7 @@ function(proto, name, that, ...args){
var parentOf =
module.parentOf =
function(parent, child){
return new Set(sources(child)).has(parent) }
return new Set([...sources(child)]).has(parent) }
// Reverse of parentOf(..)
var childOf =
@ -711,7 +550,7 @@ function(func){
: '__call__' in this ?
this.__call__
: this.__proto__)
return module.normalizeIndent(f.toString(...args)) },
return doc.normalizeIndent(f.toString(...args)) },
enumerable: false,
})
return func }
@ -955,28 +794,78 @@ function(context, constructor, ...args){
// to be rooted in Function.
// though the typeof(..) == 'function' will always work.
// NOTE: this will fail with non-identifier names...
// XXX is this a bug or a feature??? =)
// NOTE: a bit more obvious syntax could be something like:
// var A = Constructor('A', {
// __proto__: B,
//
// // constructor stuff...
//
// prototype: {
// // instance stuff...
// }
// })
// vs:
// var A = Constructor('A', B, {
// // constructor stuff...
//
// }, {
// // instance stuff...
// })
// this though a bit more obvious is in the general case can be
// more verbose, i.e. on the top level we define the constructor
// stuff which in the general case secondary while .prototype is a
// level below that while in use it's the main functionality... so,
// this is a question of ease of use vs. mirroring the JS structure,
// the answer chosen here is to prioritize simplicity and conciseness
// over verbose mirroring...
// XXX needs revision from time to time...
// XXX might be a good idea to implement the above syntax and test
// it out...
// Constructor(<name>[, <parent>], <mirror-block>)
// Constructor(<name>[, <parent>], <prototype-block>)
// Constructor(<name>[, <parent>], <prototype-block>, <prototype-block>)
//
// <mirror-block> ::=
// {
// ...
//
// prototype: {
// ...
// },
// }
// The only questions here is weather a set .prototype is a good
// enough indicator and should we use .__proto__?
var Constructor =
module.Constructor =
// shorthand...
module.C =
function Constructor(name, a, b, c){
var args = [...arguments].slice(1, 4)
// sanity check...
if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name.trim())){
throw new Error(`Constructor(..): invalid name: "${name}"`) }
// parse args...
var args = [...arguments].slice(1, 4)
// Constructor(name[[, constructor[, mixin]], proto])
var proto = args.pop() || {}
var constructor_proto = typeof(args[0]) == 'function' ?
args.shift()
: undefined
var constructor_proto =
typeof(args[0]) == 'function' ?
args.shift()
: undefined
var constructor_mixin = args.pop()
// handle:
// Constructor(name, constructor, ..)
/* XXX EXPERIMENTAL...
// handle: Constructor(name[, constructor], { prototype: { .. }, .. })
//
if(proto.hasOwnProperty('prototype')
&& proto.prototype){
console.log('>>>>', name)
constructor_mixin = proto
proto = proto.prototype }
//*/
// handle: Constructor(name, constructor, ..)
//
// NOTE: this is a bit too functional in style by an if-tree would
// be more bulky and less readable...
@ -1015,6 +904,8 @@ function Constructor(name, a, b, c){
&& (proto.__proto__ = constructor_proto.prototype) }
// the constructor base...
// NOTE: this is ignored by c8 as we will never run this directly as
// it will immediately get eval(..)'ed...
/* c8 ignore next 9 */
var _constructor = function Constructor(){
// create raw instance...
@ -1031,7 +922,9 @@ function Constructor(name, a, b, c){
// Object.defineProperty(_constructor, 'name', { value: name })
// because this does not affect the name displayed by the Chrome
// DevTools. FF does not seem to care about either version of code...
_constructor.name = name
Object.defineProperty(_constructor, 'name', {value: name})
/* XXX NAME...
//_constructor.name = name
// just in case the browser/node refuses to change the name, we'll make
// them a different offer ;)
// NOTE: it is not possible to abstract this eval(..) into something
@ -1044,6 +937,7 @@ function Constructor(name, a, b, c){
_constructor
.toString()
.replace(/Constructor/g, name) +')'))
//*/
// set .toString(..)...
// NOTE: this test is here to enable mixinFlat(..) to overwrite
// .toString(..) below...
@ -1065,7 +959,7 @@ function Constructor(name, a, b, c){
.toString()
.replace(/[^{]*{/, '{')
: '{ .. }'
return `${this.name}(${args})${module.normalizeIndent(code)}` },
return `${this.name}(${args})${doc.normalizeIndent(code)}` },
enumerable: false,
})
// set generic raw instance constructor...
@ -1228,6 +1122,8 @@ function(base, ...objects){
// NOTE: this will also match base...
// NOTE: if base matches directly callback(..) will get undefined as parent
// NOTE: for more docs on the callback(..) see sources(..)
//
// XXX should this be a generator???
var mixins =
module.mixins =
function(base, object, callback){
@ -1275,11 +1171,9 @@ function(base, object){
mixins(base, object, function(){ return module.STOP })
.length > 0
// flat mixin search...
|| sources(base, function(p){
return matchPartial(p, object) ?
module.STOP
: [] })
.length > 0 )}
|| [...sources(base)]
.some(function(p){
return matchPartial(p, object) }) )}
// Mix-out sets of methods/props/attrs out of an object prototype chain...
@ -1435,4 +1329,4 @@ BOOTSTRAP()
/**********************************************************************
* vim:set ts=4 sw=4 : */ return module })
* vim:set ts=4 sw=4 nowrap : */ return module })

157
lib/stoppable.js Executable file
View File

@ -0,0 +1,157 @@
/**********************************************************************
*
* stoppable.js
*
* Utility library implementing tooling to make stoppable functions...
*
*
* Repo and docs:
* https://github.com/flynx/stoppable.js
*
*
***********************************************/ /* c8 ignore next 2 */
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/
//---------------------------------------------------------------------
// helpers...
var AsyncFunction =
(async function(){}).constructor
var Generator =
(function*(){}).constructor
var AsyncGenerator =
(async function*(){}).constructor
//---------------------------------------------------------------------
// Wrap a callable in a STOP handler
//
// stoppable(func)
// -> func
//
// stoppable(gen)
// -> gen
//
// stoppable(asyncgen)
// -> asyncgen
//
//
// The client callable can be one of:
// - function
// - generator
// - async generator
//
// The returned callable will be of the same type as the input callable.
//
// The wrapper handles STOP slightly differently if the client is a
// function or if it is a generator / async generator:
// - function
// STOP returned / thrown
// -> return undefined
// STOP(value) returned / thrown
// -> return value
// - generator / async generator
// STOP yielded / thrown
// -> iteration stops
// STOP(value) yielded / thrown
// -> value yielded and iteration stops
//
//
// NOTE: this repeats the same code at lest twice, not sure yet how to avoid
// this...
module =
function(func){
return Object.assign(
// NOTE: the below implementations are almost the same, the main
// differences being the respective generator/async mechanics...
func instanceof Generator ?
function*(){
try{
for(var res of func.call(this, ...arguments)){
if(res === module.STOP){
return }
if(res instanceof module.STOP){
yield res.value
return }
yield res }
} catch(err){
if(err === module.STOP){
return
} else if(err instanceof module.STOP){
yield err.value
return }
throw err } }
: func instanceof AsyncGenerator ?
async function*(){
try{
for await(var res of func.call(this, ...arguments)){
if(res === module.STOP){
return }
if(res instanceof module.STOP){
yield res.value
return }
yield res }
} catch(err){
if(err === module.STOP){
return
} else if(err instanceof module.STOP){
yield err.value
return }
throw err } }
: func instanceof AsyncFunction ?
async function(){
try{
var res = await func.call(this, ...arguments)
// NOTE: this is here for uniformity...
if(res === module.STOP){
return }
if(res instanceof module.STOP){
return res.value }
return res
} catch(err){
if(err === module.STOP){
return
} else if(err instanceof module.STOP){
return err.value }
throw err } }
: function(){
try{
var res = func.call(this, ...arguments)
// NOTE: this is here for uniformity...
if(res === module.STOP){
return }
if(res instanceof module.STOP){
return res.value }
return res
} catch(err){
if(err === module.STOP){
return
} else if(err instanceof module.STOP){
return err.value }
throw err } },
{ toString: function(){
return func.toString() }, }) }
//---------------------------------------------------------------------
module.STOP =
function(value){
return {
__proto__: module.STOP.prototype,
doc: 'stop iteration.',
value,
} }
/**********************************************************************
* vim:set ts=4 sw=4 nowrap : */ return module })