Last active
August 29, 2015 14:11
-
-
Save trxcllnt/ba39d39a774fbad6bb49 to your computer and use it in GitHub Desktop.
Unrolling Enumerable.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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