Skip to content

Instantly share code, notes, and snippets.

@jcmoore
Last active August 29, 2015 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcmoore/4aa88a93ece687122682 to your computer and use it in GitHub Desktop.
Save jcmoore/4aa88a93ece687122682 to your computer and use it in GitHub Desktop.
Fennel.js prototype -- useful for making "curries"
var Fennel = (function FennelMake () {
var Curry = function Curry () {
};
Curry.prototype.method = {};
Curry.prototype.context = {};
Curry.prototype.params = (function paramsMake () {
var cache = [{}];
return function params (remaining) {
var count = cache.length;
while (count < remaining) {
count = cache.push({});
}
return cache[remaining - 1];
};
}) ();
var universal = new Curry();
var extra = null;
var adapt = function adapt () {
var list = extra,
count = list ? list.length : 0,
index = 0,
dish = null,
factory = null;
extra = null;
for (index = 0; index < count; index++) {
dish = list[index];
factory = (Function (dish.recipe));
dish.recipe = null;
dish.spoon = factory()(dish.curry);
}
};
var fallback = (function () {
var gen = (function * fallback (curry) {
var amount = 0,
index = 0,
method = null,
_method = null,
context = null,
_context = null,
params = null,
_params = null,
arg = null,
delegate = function delegate () {
return _method && _method.apply(_context, _params);
};
do {
method = yield curry.method;
context = yield curry.context;
for (index = 0; index < amount; index++) {
arg = yield curry.params(amount - index);
if (!params) {
params = [arg];
} else {
params.push(arg);
}
}
yield curry.params(0);
arg = null;
index = 0;
_method = method,
method = null,
_context = context,
context = null,
_params = params,
params = null,
amount = yield delegate;
_method = null;
_context = null;
_params = null;
} while (!null);
}) (universal);
gen.next(0); // amount...
gen.next(null); // method
gen.next(null); // context
gen.next(null); // queue
return gen;
}) ();
var Ingredient = function () {
this.flavor = null;
this.after = null;
};
Ingredient.prototype.reset = function (flavor, after) {
this.flavor = flavor;
this.after = after || null;
return this;
};
var unpool = (function (Ingredient) {
var collection = null,
count = 0;
Ingredient.prototype.repool = function (flavor, after) {
this.reset.apply(this, arguments);
if (!collection) {
collection = [this];
count = 1;
} else if (count < collection.length) {
collection[count++] = this;
} else {
count = collection.push(this);
}
};
return function unpool (flavor, before) {
var ingredient = null;
if ((count > 0) &&
(collection.length > (count - 1))) {
ingredient = collection[--count];
collection[count] = null;
} else {
ingredient = new Ingredient();
}
ingredient.flavor = flavor;
before && (before.after = ingredient);
return ingredient;
};
}) (Ingredient);
var Meal = function (curry, dish) {
var meal = this,
head = null,
tail = null,
amount = dish.amount,
progress = 0;
this.next = null;
this.next = function Meal_next () {
var result = null,
count = arguments.length,
index = 0,
_head = null,
_progress = null;
for (index = 0; index < count; index++) {
progress++;
tail = unpool(arguments[index], tail);
head = head || tail;
}
if (progress > amount + 3) {
tail = null;
_head = head;
head = null;
_progress = progress;
progress = 0;
result = dish.consume(meal, _head, _progress);
_head = null;
_progress = 0;
}
return result;
};
};
var delicacy = null;
// treat.next() probably has to return the same class every invocation...
var Treat = function () {
var treat = this,
head = null,
tail = null,
progress = 0;
this.next = null;
this.next = function Treat_next () {
var result = null,
managed = this === delicacy,
initial = null,
limit = head ? head.flavor : null,
temp = null,
count = arguments.length,
index;
if (progress === null) {
temp = head;
head = head.after;
result = temp.flavor;
temp.repool();
if (head === null) {
tail = null;
progress = 0;
treat.repool();
}
} else {
index = 0;
if (count > 0) {
if (progress === 0) {
progress = 1;
index = 1;
initial = !null;
if (managed) {
head = tail = unpool(null, tail);
} else {
head = tail = unpool(arguments[0] | 0, tail);
}
}
}
limit = head.flavor;
if (managed &&
!initial) {
limit = count + progress - 1;
head.flavor = limit;
}
while (!null) {
if ((limit !== null) &&
(limit < progress)) {
progress = null;
break;
} else if (index < count) {
progress++;
tail = unpool(arguments[index++], tail);
} else {
break;
}
}
}
return result;
};
};
var api = (function (Treat) {
var collection = null,
count = 0;
Treat.prototype.repool = function () {
if (!collection) {
collection = [this];
count = 1;
} else if (count < collection.length) {
collection[count++] = this;
} else {
count = collection.push(this);
}
};
return function Fennel () {
var treat = null;
if ((count > 0) &&
(collection.length > (count - 1))) {
treat = collection[--count];
collection[count] = null;
} else {
treat = new Treat();
}
return treat;
};
}) (Treat);
Treat.prototype.share = function () {
var amount = this.next(null),
count = arguments.length,
index = 0,
result = api(),
treat = null,
value = amount;
do {
result.next(value);
for (index = 0; index < count; index++) {
treat = arguments[index];
treat.next(value);
}
} while ((amount > 0) && amount-- && ((value = this.next(null)), !null));
return result;
};
delicacy = new Treat();
var _prologue = [
("return function * (curry) {"),
(" var delegate = function delegate () {"),
(" return method && method.call(context"),
].join("\n");
var _act1 = [""];
var _intermission = [
(" );"),
(" };"),
(" do {"),
(" var method = yield curry.method;"),
(" var context = yield curry.context;"),
].join("\n");
var _act2 = [""];
var _epilogue = [
(" yield curry.params(0);"),
(" yield delegate;"),
(" } while (!null);"),
("};"),
].join("\n");
var Dish = function Dish (count, curry) {
var dish = this,
index;
for (index = _act1.length - 1; index < count; index++) {
_act1.push(" , _"+index);
}
for (index = _act2.length - 1; index < count; index++) {
_act2.push(" var _"+index+" = yield curry.params("+(count-index)+");");
}
this.recipe = [
_prologue,
_act1.join("\n"),
_intermission,
_act2.join("\n"),
_epilogue,
].join("\n");
this.amount = count;
this.curry = curry;
this.spoon = null;
this.bouy = 0;
this.pool = null;
if (extra) {
extra.push(this);
} else {
extra = [this];
setTimeout(adapt);
}
};
Dish.prototype.serve = function serve () {
var result = null;
if (this.bouy > 0) {
result = this.pool[--this.bouy];
this.pool[this.bouy] = null;
} else {
result = new Meal(this.curry, this);
}
result.next(this.amount);
return result;
};
Dish.prototype.clean = function clean (meal) {
if (!this.pool) {
this.pool = [meal];
this.bouy = 1;
} else if (this.bouy < this.pool.length) {
this.pool[this.bouy++] = meal;
} else {
this.bouy = this.pool.push(meal);
}
};
Dish.prototype.consume = function consume (meal, ingredient, progress) {
var result = null,
temp = ingredient,
spoon = this.spoon || fallback,
count = 0,
index = 0;
this.clean(meal);
count = this.amount + 3;
for (index = 0; index < count; index++) {
spoon.next(temp.flavor);
temp = temp.after;
}
count = progress;
for (index = 0; index < count; index++) {
temp = ingredient;
ingredient = ingredient.after;
temp.repool();
}
result = spoon.next(null).value();
return result;
};
var cook = api.cook = (function (Dish, curry) {
var reserved = 20;
var cache = [new Dish(0, curry)];
var cook = function cook (amount) {
var count = cache.length;
amount++;
while (count < amount) {
count = cache.push(new Dish(count, curry));
}
amount--;
if (amount < 0) {
amount = 0;
}
return cache[amount].serve();
};
var meal = cook(reserved);
adapt();
meal.next(null); // method
meal.next(null); // context
while (reserved > 0) {
reserved--;
meal.next(null);
}
meal.next(null); // result
return cook;
}) (Dish, universal);
var dessert = api.dessert = function dessert (filling) {
return function () {
var treat = api(),
sauce = cook(arguments.length + 1);
treat.next.call(delicacy, null);
sauce.next(filling, this, treat);
sauce.next.apply(sauce, arguments);
sauce.next(null);
treat.next.call(delicacy);
return treat;
};
};
var dinner = api.dinner = function dinner (main) {
var courses = dessert(main),
amount = main.length ? (main.length - 1) : null;
return function supper () {
var result = null,
range = amount,
count = arguments.length,
index = 0,
sauce = null,
treat = null,
total = 0,
outs = 0,
skip = 0;
sauce = cook(range);
sauce.next(courses, this);
for (index = 0; (index < range) && (index < count); index++) {
sauce.next(arguments[index]);
}
for (index = count; index < range; index++) {
sauce.next(null);
}
treat = sauce.next(null);
total = treat.next(null);
outs = total + count - range;
if (outs > 0) {
sauce = cook(outs - 2);
skip = 1;
sauce.next(treat.next(null));
if (outs > 1) {
skip = 2;
sauce.next(treat.next(null));
} else {
sauce.next(null);
}
for (index = skip; index < total; index++) {
sauce.next(treat.next(null));
}
for (index = range; index < count; index++) {
sauce.next(arguments[index]);
}
result = sauce.next(null);
} else {
for (index = 0; index < total; index++) {
treat.next(null);
}
}
return result;
};
};
return api;
}) ();
var sweet = Fennel.dinner(function (snack, position, method, context) {
snack.next(method, context, 123 + position, null);
});
function printStatus(fn) {
switch(%GetOptimizationStatus(fn)) {
case 1: console.log("Function is optimized"); break;
case 2: console.log("Function is not optimized"); break;
case 3: console.log("Function is always optimized"); break;
case 4: console.log("Function is never optimized"); break;
case 6: console.log("Function is maybe deoptimized"); break;
}
}
//Fill type-info
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { a:1, b:2 });
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { c:1, d:2 }, 1);
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { e:1, f:2 }, 1, 2);
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { g:1, h:2 }, 1, 2, 3);
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { i:1, j:2 }, 1, 2, 3, 4);
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { k:1, l:2 }, 1, 2, 3, 4, 5);
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { m:1, n:2 }, 1, 2, 3, 4, 5, 6);
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { o:1, p:2 }, 1, 2, 3, 4, 5, 6, 7);
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { q:1, r:2 }, 1, 2, 3, 4, 5, 6, 7, 8);
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { s:1, t:2 }, 1, 2, 3, 4, 5, 6, 7, 8, 9);
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { u:1, v:2 }, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
//Check
printStatus(sweet);
%OptimizeFunctionOnNextCall(sweet);
//The next call
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { w:1, x:2 }, "true");
//Check
printStatus(sweet);
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, [1, 2, 3, 4], true, false, 6);
%OptimizeFunctionOnNextCall(sweet);
//The next call
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments.length === 4;
}, { y:1, z:2 }, "true", "false");
//Check
printStatus(sweet);
%OptimizeFunctionOnNextCall(sweet);
//The next call
sweet(789, function (address, particle, truthy, falsey, fireengine) {
return arguments[0] > 12;
}, { _:1, $:2 }, "true", "false", 12, 34);
//Check
printStatus(sweet);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment