dojo.provide("dojox.lang.functional.lambda"); // This module adds high-level functions and related constructs: // - anonymous functions built from the string // Acknoledgements: // - lambda() is based on work by Oliver Steele // (http://osteele.com/sources/javascript/functional/functional.js) // which was published under MIT License // Notes: // - lambda() produces functions, which after the compilation step are // as fast as regular JS functions (at least theoretically). // Lambda input values: // - returns functions unchanged // - converts strings to functions // - converts arrays to a functional composition (function(){ var df = dojox.lang.functional, lcache = {}; // split() is augmented on IE6 to ensure the uniform behavior var split = "ab".split(/a*/).length > 1 ? String.prototype.split : function(sep){ var r = this.split.call(this, sep), m = sep.exec(this); if(m && m.index == 0){ r.unshift(""); } return r; }; var lambda = function(/*String*/ s){ var args = [], sects = split.call(s, /\s*->\s*/m); if(sects.length > 1){ while(sects.length){ s = sects.pop(); args = sects.pop().split(/\s*,\s*|\s+/m); if(sects.length){ sects.push("(function(" + args + "){return (" + s + ")})"); } } }else if(s.match(/\b_\b/)){ args = ["_"]; }else{ var l = s.match(/^\s*(?:[+*\/%&|\^\.=<>]|!=)/m), r = s.match(/[+\-*\/%&|\^\.=<>!]\s*$/m); if(l || r){ if(l){ args.push("$1"); s = "$1" + s; } if(r){ args.push("$2"); s = s + "$2"; } }else{ // the point of the long regex below is to exclude all well-known // lower-case words from the list of potential arguments var vars = s. replace(/(?:\b[A-Z]|\.[a-zA-Z_$])[a-zA-Z_$\d]*|[a-zA-Z_$][a-zA-Z_$\d]*:|this|true|false|null|undefined|typeof|instanceof|in|delete|new|void|arguments|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|escape|eval|isFinite|isNaN|parseFloat|parseInt|unescape|dojo|dijit|dojox|window|document|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/g, ""). match(/([a-z_$][a-z_$\d]*)/gi) || [], t = {}; dojo.forEach(vars, function(v){ if(!(v in t)){ args.push(v); t[v] = 1; } }); } } return {args: args, body: s}; // Object }; var compose = function(/*Array*/ a){ return a.length ? function(){ var i = a.length - 1, x = df.lambda(a[i]).apply(this, arguments); for(--i; i >= 0; --i){ x = df.lambda(a[i]).call(this, x); } return x; } : // identity function(x){ return x; }; }; dojo.mixin(df, { // lambda rawLambda: function(/*String*/ s){ // summary: // builds a function from a snippet, or array (composing), // returns an object describing the function; functions are // passed through unmodified. // description: // This method is to normalize a functional representation (a // text snippet) to an object that contains an array of // arguments, and a body , which is used to calculate the // returning value. return lambda(s); // Object }, buildLambda: function(/*String*/ s){ // summary: // builds a function from a snippet, returns a string, which // represents the function. // description: // This method returns a textual representation of a function // built from the snippet. It is meant to be evaled in the // proper context, so local variables can be pulled from the // environment. s = lambda(s); return "function(" + s.args.join(",") + "){return (" + s.body + ");}"; // String }, lambda: function(/*Function|String|Array*/ s){ // summary: // builds a function from a snippet, or array (composing), // returns a function object; functions are passed through // unmodified. // description: // This method is used to normalize a functional // representation (a text snippet, an array, or a function) to // a function object. if(typeof s == "function"){ return s; } if(s instanceof Array){ return compose(s); } if(s in lcache){ return lcache[s]; } s = lambda(s); return lcache[s] = new Function(s.args, "return (" + s.body + ");"); // Function }, clearLambdaCache: function(){ // summary: // clears internal cache of lambdas lcache = {}; } }); })();