lambda.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.lang.functional.lambda"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.lang.functional.lambda"] = true;
  8. dojo.provide("dojox.lang.functional.lambda");
  9. // This module adds high-level functions and related constructs:
  10. // - anonymous functions built from the string
  11. // Acknoledgements:
  12. // - lambda() is based on work by Oliver Steele
  13. // (http://osteele.com/sources/javascript/functional/functional.js)
  14. // which was published under MIT License
  15. // Notes:
  16. // - lambda() produces functions, which after the compilation step are
  17. // as fast as regular JS functions (at least theoretically).
  18. // Lambda input values:
  19. // - returns functions unchanged
  20. // - converts strings to functions
  21. // - converts arrays to a functional composition
  22. (function(){
  23. var df = dojox.lang.functional, lcache = {};
  24. // split() is augmented on IE6 to ensure the uniform behavior
  25. var split = "ab".split(/a*/).length > 1 ? String.prototype.split :
  26. function(sep){
  27. var r = this.split.call(this, sep),
  28. m = sep.exec(this);
  29. if(m && m.index == 0){ r.unshift(""); }
  30. return r;
  31. };
  32. var lambda = function(/*String*/ s){
  33. var args = [], sects = split.call(s, /\s*->\s*/m);
  34. if(sects.length > 1){
  35. while(sects.length){
  36. s = sects.pop();
  37. args = sects.pop().split(/\s*,\s*|\s+/m);
  38. if(sects.length){ sects.push("(function(" + args + "){return (" + s + ")})"); }
  39. }
  40. }else if(s.match(/\b_\b/)){
  41. args = ["_"];
  42. }else{
  43. var l = s.match(/^\s*(?:[+*\/%&|\^\.=<>]|!=)/m),
  44. r = s.match(/[+\-*\/%&|\^\.=<>!]\s*$/m);
  45. if(l || r){
  46. if(l){
  47. args.push("$1");
  48. s = "$1" + s;
  49. }
  50. if(r){
  51. args.push("$2");
  52. s = s + "$2";
  53. }
  54. }else{
  55. // the point of the long regex below is to exclude all well-known
  56. // lower-case words from the list of potential arguments
  57. var vars = s.
  58. 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, "").
  59. match(/([a-z_$][a-z_$\d]*)/gi) || [], t = {};
  60. dojo.forEach(vars, function(v){
  61. if(!(v in t)){
  62. args.push(v);
  63. t[v] = 1;
  64. }
  65. });
  66. }
  67. }
  68. return {args: args, body: s}; // Object
  69. };
  70. var compose = function(/*Array*/ a){
  71. return a.length ?
  72. function(){
  73. var i = a.length - 1, x = df.lambda(a[i]).apply(this, arguments);
  74. for(--i; i >= 0; --i){ x = df.lambda(a[i]).call(this, x); }
  75. return x;
  76. }
  77. :
  78. // identity
  79. function(x){ return x; };
  80. };
  81. dojo.mixin(df, {
  82. // lambda
  83. rawLambda: function(/*String*/ s){
  84. // summary:
  85. // builds a function from a snippet, or array (composing),
  86. // returns an object describing the function; functions are
  87. // passed through unmodified.
  88. // description:
  89. // This method is to normalize a functional representation (a
  90. // text snippet) to an object that contains an array of
  91. // arguments, and a body , which is used to calculate the
  92. // returning value.
  93. return lambda(s); // Object
  94. },
  95. buildLambda: function(/*String*/ s){
  96. // summary:
  97. // builds a function from a snippet, returns a string, which
  98. // represents the function.
  99. // description:
  100. // This method returns a textual representation of a function
  101. // built from the snippet. It is meant to be evaled in the
  102. // proper context, so local variables can be pulled from the
  103. // environment.
  104. s = lambda(s);
  105. return "function(" + s.args.join(",") + "){return (" + s.body + ");}"; // String
  106. },
  107. lambda: function(/*Function|String|Array*/ s){
  108. // summary:
  109. // builds a function from a snippet, or array (composing),
  110. // returns a function object; functions are passed through
  111. // unmodified.
  112. // description:
  113. // This method is used to normalize a functional
  114. // representation (a text snippet, an array, or a function) to
  115. // a function object.
  116. if(typeof s == "function"){ return s; }
  117. if(s instanceof Array){ return compose(s); }
  118. if(s in lcache){ return lcache[s]; }
  119. s = lambda(s);
  120. return lcache[s] = new Function(s.args, "return (" + s.body + ");"); // Function
  121. },
  122. clearLambdaCache: function(){
  123. // summary:
  124. // clears internal cache of lambdas
  125. lcache = {};
  126. }
  127. });
  128. })();
  129. }