Skip to content

Instantly share code, notes, and snippets.

@trxcllnt
Last active August 29, 2015 14:11
Show Gist options
  • Save trxcllnt/ba39d39a774fbad6bb49 to your computer and use it in GitHub Desktop.
Save trxcllnt/ba39d39a774fbad6bb49 to your computer and use it in GitHub Desktop.
Unrolling Enumerable.
macro flatMapVars { rule { } => { } }
macro concatVars { rule { } => { } }
macro flatMapCond { rule { } => { true } }
macro concatsCond { rule { } => { true } }
macro concatBlocks { rule { } => { } }
// wrapArgs(x, i, a) .foo(function (y, j, b) { return y + 1; }) ... ;
macro wrapArgs {
function(stxs, context) {
var name_stx = stxs[0];
var match = mergeMatches([], []);
var renames = [], result = [];
var args = stxs[1].token.inner;
var next = stxs.slice(2, 5);
var rest = stxs.slice(5);
var nextName = next[1];
var nextDelim = next[2].token.inner;
var nextArgs = nextDelim[nextDelim.length - 2];
if(nextArgs && nextArgs.token && nextArgs.token.type === parser.Token.Delimiter) {
nextArgs = nextArgs.token.inner;
var nextBody = nextDelim[nextDelim.length - 1];
var index = -2;
var count = Math.min(args.length, nextArgs.length);
while((index += 2) < count) {
var lhs = nextArgs[index];
var rhs = args[index];
var rn = renames[index / 2] = {
name: __fresh(),
id: lhs
};
lhs = lhs.rename(lhs, rn.name);
if(lhs && rhs) {
result.push(
makeKeyword("var", name_stx),
lhs, makePunc("=", name_stx),
rhs, makePunc(";", name_stx)
);
if(index === 0) {
args[index] = result[result.length - 4];
}
} else {
break;
}
}
result.push(makeIdent(nextName.token.value + "EnumerableX", name_stx));
result.push(makeDelim("()", [
makeDelim("()", args, name_stx),
makePunc("=>", name_stx),
makeDelim("{}", renames.reduce(function(acc, rn) {
return acc.rename(rn.id, rn.name);
}, nextBody).expose().token.inner, name_stx)
], name_stx));
} else {
result.push(makeIdent(nextName.token.value + "EnumerableX", name_stx));
result.push(makeDelim("()", nextDelim, name_stx));
}
result.push(makePunc(";", name_stx));
if(rest[0] && rest[0].token.value !== ";") {
result.push(makeIdent("wrapArgs", name_stx));
result.push(makeDelim("()", args, name_stx));
result.push.apply(result, rest);
}
return { result: result, rest: [] };
}
}
macro guardConcats {
case { $guard { $contents ... } } => {
var concatVarsCond = localExpand(#{concatVars}).reduce(function(acc, stx) {
return acc.concat(makePunc("!", stx), stx, makePunc("&&", stx));
}, []).slice(0, -1);
if(concatVarsCond.length) {
return withSyntax($concatVarsCond ... = concatVarsCond) #{
if($concatVarsCond ...) {
$contents ...
}
}
}
return #{ $contents ... };
}
}
macro completeConcats {
case { $guard { $contents ... } } => {
var concatBlocksStx = localExpand(#{concatBlocks});
if(concatBlocksStx.length) {
return withSyntax($concatBlocks ... = concatBlocksStx) #{
macro concatBlocks { rule { } }
$contents ...
$concatBlocks ...
};
}
return #{ $contents ... };
}
}
macro selectEnumerableX {
case { $select (($args (,) ...) => { $body ... }) ; } => {
var arg = #{ $args ... }[0];
var accLength = 0;
letstx $contents ... = #{ $body ... }.reduce(function(acc, stx) {
if(stx && stx.token && stx.token.value === "return") {
acc[accLength++] = arg;
acc[accLength++] = makePunc("=", arg);
} else {
acc[accLength++] = stx;
}
return acc;
}, []);
return #{ guardConcats { $contents ... } };
}
}
macro mapEnumerableX { rule { } => { selectEnumerableX } }
macro filterEnumerableX {
case { $filter (($args (,) ...) => { $body ... }) ; $rest ... } => {
var result = [], resultCount = 0, body = #{ $body ... },
index = -1, count = body.length - 1,
stx, tok, val, expr;
var filterDelim = #{()};
var concatVarsCond = localExpand(#{concatVars});
if(concatVarsCond.length) {
concatVarsCond = concatVarsCond.reduce(function(acc, stx) {
return acc.concat(makePunc("!", stx), stx, makePunc("&&", stx));
}, []);
filterDelim[0].token.inner = filterDelim[0].token.inner.concat(concatVarsCond);
concatVarsCond = #{!(flatMapCond)};
}
var rest = localExpand(#{$rest ...});
var concatBlocksStx = localExpand(#{concatBlocks});
if(concatVarsCond.length && concatBlocksStx.length) {
concatBlocksStx.unshift(makeKeyword("else", name_stx));
}
while(++index < count) {
stx = body[index];
tok = stx && stx.token;
val = tok && tok.value;
if(val === "return") {
expr = getExpr(body.slice(index + 1));
if(expr.success) {
filterDelim[0].token.inner = filterDelim[0].token.inner.concat(
makePunc("!", stx),
makeDelim("()", expr.result, stx));
result[resultCount++] = makeKeyword("if", stx);
result[resultCount++] = makeDelim("()", filterDelim, stx);
result[resultCount++] = makeDelim("{}",
concatVarsCond.length ? [
makeKeyword("if", stx), makeDelim("()", concatVarsCond, stx),
makeDelim("{}", [makeKeyword("continue", stx)], stx)
].concat(concatBlocksStx) : [makeKeyword("continue", stx)]
, stx);
result.push.apply(result, rest);
break;
}
} else if(stx && tok) {
result[resultCount++] = stx;
}
}
return result;
}
}
macro whereEnumerableX { rule { } => { filterEnumerableX } }
macro selectManyEnumerableX {
case { $selectMany (($args (,) ...) => { $body ... }) ; $rest ... } => {
var ctx = #{ $selectMany }, body = #{ $body ... }, rest = #{ $rest ... },
contents = [], contentsLength = 0,
index = -1, count = body.length, stx;
while(++index < count) {
if((stx = body[index]) && stx.token && stx.token.value === "return") {
contents[contentsLength++] = makeIdent("EnumerableX", ctx);
contents[contentsLength++] = makePunc(".", ctx);
contents[contentsLength++] = makeIdent("selectMany", ctx);
contents.push.apply(contents, body.slice(index + 4, -1).concat(rest.slice(2)));
break;
} else {
contents[contentsLength++] = stx;
}
}
return withSyntax($contents ... = contents)#{
completeConcats { guardConcats { $contents ... } }
};
}
}
macro mapManyEnumerableX { rule { } => { selectManyEnumerableX } }
macro flatMapEnumerableX { rule { } => { selectManyEnumerableX } }
macro concatEnumerableX {
case { $concat (EnumerableX.from($enumerable)) ; $rest ... } => {
return #{ EnumerableX.concat($enumerable) $rest ... };
}
case { $concat (EnumerableX.from($enumerable)) ; } => {
return #{ };
}
}
macro forEachEnumerableX {
rule { (($args (,) ...) => { $body ... }) ; } => { $body ... }
}
macro EnumerableX {
case { $EnumerableX .from($enumerable) . $rest ... ; } => {
//console.clear();
var ctx = #{ $EnumerableX };
var rest = #{ $rest ... ;};
var value = makeIdent("value", ctx);
var index = makeIdent("index", ctx);
var array = makeIdent("array", ctx);
var count = makeIdent("count", ctx);
var flatMapCondArr = localExpand(#{flatMapCond});
flatMapCondArr = flatMapCondArr.length > 1 ? flatMapCondArr.concat(makePunc("&&", ctx)) : [];
var wrapped = rest.length <= 1 && [] || [
makeIdent("wrapArgs", ctx),
makeDelim("()", [
value, makePunc(",", ctx),
index, makePunc(",", ctx),
array], ctx),
makePunc(".", ctx)
].concat(rest);
var concatNames = ["concat"],
flatMapNames = ["flatMap", "selectMany", "mapMany"];
var concatVarIdents = [], flatMapVarIdents = [],
stx, tok, val, index2 = -3, count2 = rest.length;
while((index2 += 3) < count2) {
stx = rest[index2];
tok = stx && stx.token;
val = tok && tok.value;
if(~concatNames.indexOf(val)) {
stx = makeIdent("concat", ctx);
concatVarIdents.push(concatVarIdents.length ? stx.rename(stx, __fresh()) : stx);
} else if(~flatMapNames.indexOf(val)) {
value = makeIdent("value", ctx);
flatMapVarIdents.push([value.rename(value, __fresh()), makePunc("=", ctx), makeValue(null, ctx)]);
index = makeIdent("index", ctx);
flatMapVarIdents.push([index.rename(index, __fresh()), makePunc("=", ctx), makeValue(-1, ctx)]);
array = makeIdent("array", ctx);
flatMapVarIdents.push([array.rename(array, __fresh()), makePunc("=", ctx), makeValue(null, ctx)]);
count = makeIdent("count", ctx);
flatMapVarIdents.push([count.rename(count, __fresh()), makePunc("=", ctx), makeValue(0, ctx)]);
}
}
letstx $value = [value];
letstx $index = [index];
letstx $array = [array];
letstx $count = [count];
letstx $flatMapCond ... = flatMapCondArr;
letstx $flatMapVars ... = flatMapVarIdents.map(function(x) { return x[0]; });
letstx $concatVars ... = concatVarIdents;
letstx $flatMapVarDecls ... = flatMapVarIdents.reduce(function(acc, stx, i, a){
return acc.concat(stx, makePunc(i === a.length - 1 ? ";" : ",", ctx));
}, flatMapVarIdents.length ? [makeKeyword("var", ctx)] : []);
letstx $concatVarDecls ... = concatVarIdents.reduce(function(acc, stx) {
return acc.concat(withSyntax($concatVarIdent = [stx])#{
var $concatVarIdent = false;
});
}, []);
letstx $macros ... = localExpand(#{
var $value, $index = -1, $array = $enumerable, $count = $array.length;
$flatMapVarDecls ...
$concatVarDecls ...
macro flatMapVars { rule { } => { $flatMapVars ... } }
macro concatVars { rule { } => { $concatVars ... } }
macro flatMapCond { rule { } => { $flatMapCond ... ($index === $count - 1) } }
macro concatArray { rule { } => { $array } }
macro concatIndex { rule { } => { $index } }
macro concatCount { rule { } => { $count } }
});
letstx $wrapped ... = localExpand(wrapped);
letstx $concatBlocks ... = localExpand(#{concatBlocks});
return #{
macro concatBlocks { rule { } }
$macros ...
while(++$index < $count) {
$value = $array[$index];
$wrapped ...
$concatBlocks ...
}
};
}
case { $EnumerableX .concat($enumerable) $rest ... ; } => {
//debugger;
var ctx = #{ $EnumerableX };
var rest = #{ $rest ... ;};
var flatMapVarIdents = localExpand(#{flatMapVars});
var count = /*flatMapVarIdents[flatMapVarIdents.length - 1] || */localExpand(#{concatCount})[0];
var array = /*flatMapVarIdents[flatMapVarIdents.length - 2] || */localExpand(#{concatArray})[0];
var index = /*flatMapVarIdents[flatMapVarIdents.length - 3] || */localExpand(#{concatIndex})[0];
var concatVarIdents = localExpand(#{concatVars});
var concat = concatVarIdents.slice(0, 1);
var concatBlocksStx = localExpand(#{concatBlocks});
var concatsCondArr = localExpand(#{concatsCond});
concatsCondArr = concatsCondArr.length > 1 ? [
makePunc("!", ctx),
makeDelim("()", concatsCondArr, ctx),
makePunc("&&", ctx)
] : [];
letstx $array = [array];
letstx $index = [index];
letstx $count = [count];
letstx $concat = concat;
letstx $concatsCond ... = concatsCondArr;
concatBlocksStx.push.apply(concatBlocksStx, (concatBlocksStx.length ?
[makeKeyword("else", ctx)] : []).concat(#{
if(concatsCond && flatMapCond) {
$concat = true;
$array = $enumerable;
$index = -1;
$count = $array.length;
continue;
}
}));
letstx $concatBlocks ... = concatBlocksStx;
letstx $concatVars ... = concatVarIdents.slice(1);
letstx $macros ... = localExpand(#{
macro concatVars { rule { } => { $concatVars ... } }
macro concatsCond { rule { } => { $concatsCond ... !$concat } }
macro concatBlocks { rule { } => { $concatBlocks ... } }
});
letstx $wrapped ... = localExpand(rest);
return #{
$macros ...
$wrapped ...
};
}
case { $EnumerableX .selectMany($enumerable) $rest ... ; } => {
var ctx = #{ $EnumerableX };
var rest = #{ $rest ... ;};
var flatMapVarIdents = localExpand(#{flatMapVars});
var value = flatMapVarIdents[0];
var index = flatMapVarIdents[1];
var array = flatMapVarIdents[2];
var count = flatMapVarIdents[3];
var flatMapCondArr = localExpand(#{ flatMapCond });
flatMapCondArr = flatMapCondArr.concat(flatMapCondArr.length ? makePunc("&&", ctx) : []);
var wrapped = rest.length <= 1 && [] || [
makeIdent("wrapArgs", ctx),
makeDelim("()", [
value, makePunc(",", ctx),
index, makePunc(",", ctx),
array], ctx)
].concat(rest);
letstx $value = [value];
letstx $index = [index];
letstx $array = [array];
letstx $count = [count];
letstx $flatMapVars ... = flatMapVarIdents.slice(4);
letstx $flatMapCond ... = flatMapCondArr;
letstx $macros ... = localExpand(#{
$index = -1;
$array = $enumerable;
$count = $array.length;
macro flatMapVars { rule { } => { $flatMapVars ... } }
macro flatMapCond { rule { } => { $flatMapCond ... ($index === $count - 1) } }
macro concatArray { rule { } => { $array } }
macro concatIndex { rule { } => { $index } }
macro concatCount { rule { } => { $count } }
});
letstx $wrapped ... = localExpand(wrapped);
letstx $concatBlocks ... = localExpand(#{concatBlocks});
return #{
macro concatBlocks { rule { } }
$macros ...
while(++$index < $count) {
$value = $array[$index];
$wrapped ...
$concatBlocks ...
}
};
}
case { $EnumerableX } => { return #{ $[EnumerableX] } }
}
var testEx = false,
testBoth = false,
testPerf = true,
Ix = require('ix'),
Enumerable = Ix.Enumerable,
arr = [], arrCount = 0;
Enumerable.from = Enumerable.fromArray;
this.addItem = function(item) {
arr[arrCount++] = item;
};
if((testEx || testBoth) && !testPerf) {
// uncomment this to run the individual test, then comment out the perf test below.
// EnumerableX
// .from([0, 1, 2, 3])
// .select(function(x) { return x + 9; })
// .filter(function(x) { return x > 10; })
// .concat(EnumerableX.from([4, 5, 6, 7]))
// .flatMap(function(x) { return EnumerableX.from([x, x + 1]); })
// .map(function(y) { return y * 10; })
// .concat(EnumerableX.from([8, 9, 10, 11]))
// .forEach(function(y) {
// this.addItem(y);
// });
console.log("ex:", arr);
} else if(testPerf) {
var runCount = 0,
runBase = 10, runPow = 6,
runTotal = Math.pow(runBase, runPow);
console.log("testing with", runBase + "^" + runPow, "executions.");
var startTime = Date.now();
while(++runCount <= runTotal) {
EnumerableX
.from([0, 1, 2, 3])
.select(function(x) { return x + 9; })
.filter(function(x) { return x > 10; })
.concat(EnumerableX.from([4, 5, 6, 7]))
.flatMap(function(x) { return EnumerableX.from([x, x + 1]); })
.map(function(y) { return y * 10; })
.concat(EnumerableX.from([8, 9, 10, 11]))
.forEach(function(y) { });
}
console.log('ex run time:', (Date.now() - startTime) + "ms");
startTime = Date.now();
runCount = 0;
}
var ixEnum = Enumerable
.from([0, 1, 2, 3])
.select(function(x) { return x + 9; })
.filter(function(x) { return x > 10; })
.concat(Enumerable.from([4, 5, 6, 7]))
.selectMany(function(x) { return Enumerable.from([x, x + 1]); })
.map(function(y) { return y * 10; })
.concat(Enumerable.from([8, 9, 10, 11]));
if((!testEx || testBoth) && !testPerf) {
console.log("ix:", ixEnum.toArray())
} else if(testPerf) {
while(++runCount <= runTotal) {
ixEnum.forEach(function(y) { });
}
console.log('ix run time:', (Date.now() - startTime) + "ms");
}
/* output:
ex: [ 110, 120, 120, 130, 40, 50, 50, 60, 60, 70, 70, 80, 8, 9, 10, 11 ]
ix: [ 110, 120, 120, 130, 40, 50, 50, 60, 60, 70, 70, 80, 8, 9, 10, 11 ]

testing with 10^6 executions.
ex run time: 248ms
ix run time: 10198ms
*/
var testEx = false, testBoth = false, testPerf = true, Ix = require('ix'), Enumerable = Ix.Enumerable, arr = [], arrCount = 0;
Enumerable.from = Enumerable.fromArray;
this.addItem = function (item) {
arr[arrCount++] = item;
};
if ((testEx || testBoth) && !testPerf) {
// uncomment this to run the individual test, then comment out the perf test below.
// EnumerableX
// .from([0, 1, 2, 3])
// .select(function(x) { return x + 9; })
// .filter(function(x) { return x > 10; })
// .concat(EnumerableX.from([4, 5, 6, 7]))
// .flatMap(function(x) { return EnumerableX.from([x, x + 1]); })
// .map(function(y) { return y * 10; })
// .concat(EnumerableX.from([8, 9, 10, 11]))
// .forEach(function(y) {
// this.addItem(y);
// });
console.log('ex:', arr);
} else if (testPerf) {
var runCount = 0, runBase = 10, runPow = 6, runTotal = Math.pow(runBase, runPow);
console.log('testing with', runBase + '^' + runPow, 'executions.');
var startTime = Date.now();
while (++runCount <= runTotal) {
var value, index = -1, array = [
0,
1,
2,
3
], count = array.length;
var value$2 = null, index$2 = -1, array$2 = null, count$2 = 0;
var concat = false;
var concat$2 = false;
while (++index < count) {
value = array[index];
var x = value;
if (!concat && !concat$2) {
x = x + 9;
}
var x$2 = x;
if (!concat && !concat$2 && !(x$2 > 10)) {
if (!(index === count - 1 && index$2 === count$2 - 1)) {
continue;
}
}
var x$3 = x$2;
if (!concat$2) {
index$2 = -1;
array$2 = [
x$3,
x$3 + 1
];
count$2 = array$2.length;
while (++index$2 < count$2) {
value$2 = array$2[index$2];
var y = value$2;
if (!concat$2) {
y = y * 10;
}
var y$2 = y;
if (!!concat && !concat$2 && index === count - 1 && index$2 === count$2 - 1) {
concat$2 = true;
array$2 = [
8,
9,
10,
11
];
index$2 = -1;
count$2 = array$2.length;
continue;
}
}
}
if (!concat && index === count - 1) {
concat = true;
array = [
4,
5,
6,
7
];
index = -1;
count = array.length;
continue;
}
}
}
console.log('ex run time:', Date.now() - startTime + 'ms');
startTime = Date.now();
runCount = 0;
}
var ixEnum = Enumerable.from([
0,
1,
2,
3
]).select(function (x$4) {
return x$4 + 9;
}).filter(function (x$4) {
return x$4 > 10;
}).concat(Enumerable.from([
4,
5,
6,
7
])).selectMany(function (x$4) {
return Enumerable.from([
x$4,
x$4 + 1
]);
}).map(function (y$3) {
return y$3 * 10;
}).concat(Enumerable.from([
8,
9,
10,
11
]));
if ((!testEx || testBoth) && !testPerf) {
console.log('ix:', ixEnum.toArray());
} else if (testPerf) {
while (++runCount <= runTotal) {
ixEnum.forEach(function (y$3) {
});
}
console.log('ix run time:', Date.now() - startTime + 'ms');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment