Skip to content

Instantly share code, notes, and snippets.

@trevnorris
Last active August 29, 2015 14:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trevnorris/efac1089d728934d44e2 to your computer and use it in GitHub Desktop.
Save trevnorris/efac1089d728934d44e2 to your computer and use it in GitHub Desktop.
function reblaze(vals, fn) {
if (typeof fn !== 'function')
throw new TypeError('fn should be a function');
// V8 will automatically throw if vals is not an object.
var keys = Object.keys(vals);
var regexp;
var fn_name = fn.name;
var fn_str = fn.toString();
var fn_len = fn_str.length;
var fn_args = fn_str.substring(fn_str.indexOf('(') + 1, fn_str.indexOf(')'));
var fn_body = fn_str.substring(fn_str.indexOf('{') + 1, fn_len - 1).trim();
// TODO: This could be replaced by a replaced function that doesn't need
// to for loop around the keys and instead performs each individually.
// Also the strings would need to be concatinated every time and instead
// turned into a single string.
// XXX: Thought, could I use mutable strings to not need to rewrite each
// of these every time?
for (var i = 0; i < keys.length; i++) {
// Infer if the key is being used as an object key.
regexp = new RegExp('(\\w)(\s|\S)*\\\[\\(\\(\\(' +
keys[i] +
'\\)\\)\\)\\\]', 'g');
fn_body = fn_body.replace(regexp, '$1.' + vals[keys[i]]);
// Need to do this since String#replace() in V8 doesn't support the optional
// third argument, flags, for global replacement.
regexp = new RegExp('\\(\\(\\(' + keys[i] + '\\)\\)\\)', 'g');
fn_body = fn_body.replace(regexp, vals[keys[i]]);
}
return (new Function('return function ' + fn_name +
'(' + fn_args + ') {' + fn_body + '}'))();
}
// Test mixed replacement types.
function replace4(foo) {
return foo[(((BAR)))] + (((BAZ)));
}
var fn = reblaze({ BAR: 'bar', BAZ: 3 }, replace4);
assert_eq(fn.toString(), 'function replace4(foo) {return foo.bar + 3;}');
assert_eq(fn({ bar: 7 }), 10);
// Test multi property names replacement.
function replace3(foo) {
return foo[(((BAR)))] + foo[(((BAR)))];
}
var fn = reblaze({ BAR: 'bar' }, replace3);
assert_eq(fn.toString(), 'function replace3(foo) {return foo.bar + foo.bar;}');
assert_eq(fn({ bar: 13 }), 26);
// Test property names are correctly replaced.
function replace2(foo) {
return foo[(((BAR)))];
}
var fn = reblaze({ BAR: 'bar' }, replace2);
assert_eq(fn.toString(), 'function replace2(foo) {return foo.bar;}');
assert_eq(fn({ bar: 42 }), 42);
// Test arguments are properly inserted.
function replace1(foo, bar) {
return foo + bar + (((BAZ)));
}
var fn = reblaze({ BAZ: 1 }, replace1);
assert_eq(fn.toString(), 'function replace1(foo, bar) {return foo + bar + 1;}');
assert_eq(fn(2, 3), 6);
// Test basic replacement.
function replace0() {
return (((A))) + (((B))) + (((C))) + (((A)));
}
var fn = reblaze({ A: 5, B: 7, C: 11 }, replace0);
assert_eq(fn.toString(), 'function replace0() {return 5 + 7 + 11 + 5;}');
assert_eq(fn(), 28);
function log() {
return console.log.apply(this, arguments);
}
function assert_eq(val0, val1) {
if (val0 !== val1)
throw new Error('Assertion Failed: \n' + val0 + '\n' + val1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment