Skip to content

Commit

Permalink
Merge v3 into master
Browse files Browse the repository at this point in the history
  • Loading branch information
matAtWork committed Oct 7, 2016
1 parent 77d7b52 commit e505864
Show file tree
Hide file tree
Showing 25 changed files with 1,162 additions and 1,712 deletions.
894 changes: 193 additions & 701 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion covers/events.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = function(nodent,opts) {
if (!opts) opts = {} ;
if (!opts.Promise)
opts.Promise = global.Promise || require('../lib/thenable') ;
opts.Promise = global.Promise || nodent.Thenable ;

var events = require('events');
if (!events.EventEmitter.prototype.wait) {
Expand Down
2 changes: 1 addition & 1 deletion covers/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module.exports = function(nodent,config) {

if (!config) config = {} ;
if (!config.Promise)
config.Promise = global.Promise || require('../lib/thenable') ;
config.Promise = global.Promise || nodent.Thenable ;

if (config.autoProtocol) {
protos.https = require('https') ;
Expand Down
4 changes: 2 additions & 2 deletions covers/https.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module.exports = function(nodent,config) {

if (!config) config = {} ;
if (!config.Promise)
config.Promise = global.Promise || require('../lib/thenable') ;
config.Promise = global.Promise || nodent.Thenable ;

var http = require('https') ;
var cover = Object.create(http) ;
Expand All @@ -17,7 +17,7 @@ module.exports = function(nodent,config) {
};

cover.get = function(opts){
return new nodent.Thenable(function($return,$error){
return new (config.Promise)(function($return,$error){
http.get(opts,$return).on('error',$error) ;
}) ;
};
Expand Down
6 changes: 2 additions & 4 deletions covers/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ function MapError(message) {
MapError.prototype = Object.create(Error.prototype);
MapError.prototype.constructor = MapError;

var Thenable = require('../lib/thenable') ;

module.exports = function(nodent,opts) {
if (!opts) opts = {} ;
if (!opts.Promise)
opts.Promise = global.Promise || Thenable ;
opts.Promise = global.Promise || nodent.Thenable ;

function map(what,result,asyncFn) {
var hasError = false ;
Expand Down Expand Up @@ -75,7 +73,7 @@ module.exports = function(nodent,opts) {
asyncFn.apply(this,arguments).then(complete,completeError);
} else {
var f = isArray?e:what[e] ;
if (Thenable.isThenable(f))
if (nodent.isThenable(f))
f.then(complete,completeError);
else
complete(f) ;
Expand Down
1,207 changes: 513 additions & 694 deletions lib/arboriculture.js

Large diffs are not rendered by default.

15 changes: 12 additions & 3 deletions lib/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,19 @@ function precedence(node) {

function out(node,state,type) {
var f = this[type || node.type] ;
if (f)
if (f) {
/*
try {
var attr = Object.keys(node).filter(k=>k[0]==='$').map(k=>k+(node[k]?"+":"-")) ;
if (attr.length)
state.write(node,"/*"+attr.join(", ")+"\u002A/ ") ;
} catch (ex) {} ;
*/
f.call(this, node, state);
else // Unknown node type - just spew its source
state.write(node,state.sourceAt(node.start,node.end)) ;
} else {
// Unknown node type - just spew its source
state.write(node,"/*"+node.type+"?*/ "+state.sourceAt(node.start,node.end)) ;
}
}
function expr(state, parent, node, assoc) {
if (assoc===2 ||
Expand Down
68 changes: 67 additions & 1 deletion lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ function acornParse(code,config) {
var comments = [] ;
var options = {
plugins:{asyncawait:{asyncExits:true, awaitAnywhere:true}},
ecmaVersion:7,
ecmaVersion:8,
allowHashBang:true,
allowReturnOutsideFunction:true,
allowImportExportEverywhere:true,
Expand All @@ -201,7 +201,73 @@ function acornParse(code,config) {
return ast ;
}

var parseCache = {} ;
function partialParse(code,args) {
if (!parseCache[code]) {
parseCache[code] = acornParse(code,{
locations:false,
ranges:false,
onComment:null
}) ;
}

var result = substitute(parseCache[code]) ;
return {body:result.body, expr:result.body[0].type==='ExpressionStatement' ? result.body[0].expression : null} ;

/* parse and substitute:
*
* $1 Substitute the specified expression. If $1 occupies a slot which is an array of expressions (e.g arguments, params)
* and the passed argument is an array, subtitute the whole set
* {$:1} Substitute a single statement
*
*/
function substitute(src,dest) {
if (Array.isArray(dest) && !Array.isArray(src))
throw new Error("Can't substitute an array for a node") ;

dest = dest || {} ;
Object.keys(src).forEach(function(k){
if (!(src[k] instanceof Object))
return dest[k] = src[k] ;

function moreNodes(v){ if (typeof v==="function") v = v() ; dest = dest.concat(v) ; return dest };
function copyNode(v){ if (typeof v==="function") v = v() ; dest[k] = v ; return dest };

// The src is an array, so create/grow the destination
// It could an an array of expressions $1,$2,$3 or statements $:1;$:2;$:3;
if (Array.isArray(src[k]))
return dest[k] = substitute(src[k],[]) ;

var p ;
if (Array.isArray(dest))
p = moreNodes ;
else
p = copyNode ;

// Substitute a single identifier $.. with an expression (TODO: test provided arg is an expression node)
if (src[k].type==='Identifier' && src[k].name[0]==='$')
return p(args[src[k].name.slice(1)]) ;

// Substitute a single labeled statement $:.. with a statement (TODO: test provided arg is a statement node)
if (src[k].type === 'LabeledStatement' && src[k].label.name==='$') {
var spec = src[k].body.expression ;
return p(args[spec.name || spec.value]) ;
}

// Magic label to set call a function to modify a statement node $$method: <statement>
// The newNode = args.method(oldNode)
if (src[k].type === 'LabeledStatement' && src[k].label.name.slice(0,2)==='$$') {
return p(args[src[k].label.name.slice(2)](substitute(src[k]).body)) ;
}

return p(substitute(src[k])) ;
}) ;
return dest ;
}
}

module.exports = {
part:partialParse,
parse: acornParse,
treeWalker:treeWalker,
_acorn:acorn
Expand Down
120 changes: 94 additions & 26 deletions lib/runtime.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,96 @@
var $asyncbind = new Function("self","catcher",
(" var resolver = this; "+
" if (catcher===true) { "+
" if (!Function.prototype.$asyncbind.EagerThenable) "+
" Function.prototype.$asyncbind.EagerThenable = "+require('./eager.js').toString()+"(); "+
" return new (Function.prototype.$asyncbind.EagerThenable)(boundThen); "+
" } "+
" if (catcher) { "+
" if (Function.prototype.$asyncbind.wrapAsyncStack) "+
" catcher = Function.prototype.$asyncbind.wrapAsyncStack(catcher); "+
" return then; "+
" } "+
" function then(result,error){ "+
" try { "+
" return result && (result instanceof Object) && typeof result.then==='function' "+
" ? result.then(then,catcher) : resolver.call(self,result,error||catcher); "+
" } catch (ex) { "+
" return (error||catcher)(ex); "+
" } "+
" } "+
" function boundThen(result,error) { "+
" return resolver.call(self,result,error); "+
" } "+
" boundThen.then = boundThen; "+
" return boundThen; ")
.replace(/\s+/g,' ')) ;
"use strict";
/*
* $asyncbind has multiple uses, depending on the parameter list. It is in Function.prototype, so 'this' is always a function
*
* 1) If called with a single argument (this), it is used when defining an async function to ensure when
* it is invoked, the correct 'this' is present, just like "bind"
* 2) If called with a second parameter ("catcher") and catcher!==true it is being used to invoke an async
* function where the second parameter is the error callback (for sync exceptions and to be passed to
* nested async calls)
* 3) If called with the second parameter===true, it is the same use as (1), but the function is wrapped
* in an 'EagerThenable' as well bound to 'this'.
* It is the same as calling new EagerThenable(this), where 'this' is the function being bound/wrapped
* 4) If called with the second parameter===0, it is the same use as (1), but the function is wrapped
* in a 'Thenable', which executes lazily and can resolve synchronously.
* It is the same as calling new Thenable(fn), where 'this' is the function being bound/wrapped
*/

function processIncludes(includes,input) {
var src = input.toString() ;
var t = "return "+src ;
var args = src.match(/.*\(([^)]*)\)/)[1] ;
var re = /!!!'([^']*)'/g ;
var m = [] ;
while (1) {
var mx = re.exec(t) ;
if (mx)
m.push(mx) ;
else break ;
}
m.reverse().forEach(function(e){
t = t.slice(0,e.index)+includes[e[1]]+t.substr(e.index+e[0].length) ;
}) ;
t = t.replace(/\/\*[^*]*\*\//g,' ').replace(/\s+/g,' ') ;
return new Function(args,t)() ;
}

var $asyncbind = processIncludes({
zousan:require('./zousan').toString(),
thenable:require('./thenableFactory').toString()
},
function $asyncbind(self,catcher) {
"use strict";
if (!Function.prototype.$asyncbind)
Function.prototype.$asyncbind = $asyncbind ;

if (!$asyncbind.trampoline) {
$asyncbind.trampoline = function trampoline(t,x,s,e){
return function b(q) {
while (q) {
if (q.then)
return q.then(b, e);
try {
if (q.pop) {
if (q.length)
return q.pop() ? x.call(t) : q;
q = s;
} else
q = q.call(t)
} catch (r) {
return e(r);
}
}
}
};
}
if (!$asyncbind.LazyThenable) {
$asyncbind.LazyThenable = !!!'thenable'();
$asyncbind.EagerThenable = $asyncbind.Thenable = ($asyncbind.EagerThenableFactory = !!!'zousan')();
}

var resolver = this;
switch (catcher) {
case true:
return new ($asyncbind.Thenable)(boundThen);
case 0:
return new ($asyncbind.LazyThenable)(boundThen);
case undefined:
/* For runtime compatibility with Nodent v2.x, provide a thenable */
boundThen.then = boundThen ;
return boundThen ;
default:
return function(){
try {
return resolver.apply(self,arguments);
} catch(ex) {
return catcher(ex);
}
}
}
function boundThen() {
return resolver.apply(self,arguments);
}
}) ;

function $asyncspawn(promiseProvider,self) {
var genF = this ;
Expand Down Expand Up @@ -62,6 +129,7 @@ function $asyncspawn(promiseProvider,self) {
});
}

$asyncbind() ;
module.exports = {
$asyncbind:$asyncbind,
$asyncspawn:$asyncspawn
Expand Down
Loading

0 comments on commit e505864

Please sign in to comment.