Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
matAtWork committed Oct 8, 2016
1 parent 2414465 commit 1e5737f
Show file tree
Hide file tree
Showing 6 changed files with 409 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -35,3 +35,4 @@ jspm_packages

# Optional REPL history
.node_repl_history
/.project
26 changes: 26 additions & 0 deletions package.json
@@ -0,0 +1,26 @@
{
"name": "nodent-runtime",
"version": "3.0.0",
"description": "Runtime component of nodent",
"main": "runtime.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/MatAtBread/nodent-runtime.git"
},
"keywords": [
"nodent",
"runtime",
"asunc",
"await",
"es7"
],
"author": "matatbread",
"license": "MIT",
"bugs": {
"url": "https://github.com/MatAtBread/nodent-runtime/issues"
},
"homepage": "https://github.com/MatAtBread/nodent-runtime#readme"
}
148 changes: 148 additions & 0 deletions runtime.js
@@ -0,0 +1,148 @@
"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". For legacy reasons, 'this' is given
* a memeber 'then' which refers to itself.
* 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 'Promise' as well bound to 'this'.
* It is the same as calling 'new Promise(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 'LazyThenable', which executes lazily and can resolve synchronously.
* It is the same as calling 'new LazyThenable(this)' (if such a type were exposed), 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) {
Object.defineProperty(Function.prototype,"$asyncbind",{value:$asyncbind,enumerable:false,configurable:true,writable:true}) ;
}

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) {
if (!Function.prototype.$asyncspawn) {
Object.defineProperty(Function.prototype,"$asyncspawn",{value:$asyncspawn,enumerable:false,configurable:true,writable:true}) ;
}
if (!(this instanceof Function)) return ;

var genF = this ;
return new promiseProvider(function enough(resolve, reject) {
var gen = genF.call(self, resolve, reject);
function step(fn,arg) {
var next;
try {
next = fn.call(gen,arg);
if(next.done) {
if (next.value !== resolve) {
if (next.value && next.value===next.value.then)
return next.value(resolve,reject) ;
resolve && resolve(next.value);
resolve = null ;
}
return;
}

if (next.value.then) {
next.value.then(function(v) {
step(gen.next,v);
}, function(e) {
step(gen.throw,e);
});
} else {
step(gen.next,next.value);
}
} catch(e) {
reject && reject(e);
reject = null ;
return;
}
}
step(gen.next);
});
}

// Initialize async bindings
$asyncbind() ;
$asyncspawn() ;

// Export async bindings
module.exports = {
$asyncbind:$asyncbind,
$asyncspawn:$asyncspawn
};
13 changes: 13 additions & 0 deletions thenable.js
@@ -0,0 +1,13 @@
function Thenable(thenable) {
return thenable.then = thenable ;
};

Thenable.resolve = function(v){
return Thenable.isThenable(v) ? v : {then:function(resolve){return resolve(v)}};
};

Thenable.isThenable = function(obj) {
return obj && (obj instanceof Object) && typeof obj.then==="function";
}

module.exports = Thenable ;
68 changes: 68 additions & 0 deletions thenableFactory.js
@@ -0,0 +1,68 @@
module.exports = function() {
function isThenable(obj) {
return obj && (obj instanceof Object) && typeof obj.then==="function";
}

function resolution(p,r,how) {
try {
/* 2.2.7.1 */
var x = how ? how(r):r ;

if (p===x) /* 2.3.1 */
return p.reject(new TypeError("Promise resolution loop")) ;

if (isThenable(x)) {
/* 2.3.3 */
x.then(function(y){
resolution(p,y);
},function(e){
p.reject(e)
}) ;
} else {
p.resolve(x) ;
}
} catch (ex) {
/* 2.2.7.2 */
p.reject(ex) ;
}
}

function Chained() {};
Chained.prototype = {
resolve:_unchained,
reject:_unchained,
then:thenChain
};
function _unchained(v){}
function thenChain(res,rej){
this.resolve = res;
this.reject = rej;
}

function then(res,rej){
var chain = new Chained() ;
try {
this._resolver(function(value) {
return isThenable(value) ? value.then(res,rej) : resolution(chain,value,res);
},function(ex) {
resolution(chain,ex,rej) ;
}) ;
} catch (ex) {
resolution(chain,ex,rej);
}
return chain ;
}

function Thenable(resolver) {
this._resolver = resolver ;
this.then = then ;
};

Thenable.resolve = function(v){
return Thenable.isThenable(v) ? v : {then:function(resolve){return resolve(v)}};
};

Thenable.isThenable = isThenable ;

return Thenable ;
} ;

0 comments on commit 1e5737f

Please sign in to comment.