PageRenderTime 26ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/private/lib/helpers.js

http://github.com/dyoo/mzscheme-vm
JavaScript | 587 lines | 506 code | 43 blank | 38 comment | 112 complexity | 6b77320f83f7df5b108d2ce7f625f7ee MD5 | raw file
  1. //////////////////////////////////////////////////////////////
  2. // File of helper functions for primitives and world.
  3. var helpers = {};
  4. (function() {
  5. var format = function(formatStr, args, functionName) {
  6. var throwFormatError = function() {
  7. functionName = functionName || '#<procedure>';
  8. var matches = formatStr.match(new RegExp('~[sSaA]', 'g'));
  9. var expectedNumberOfArgs = matches == null ? 0 : matches.length;
  10. var errorStrBuffer = [functionName + ': format string requires ' + expectedNumberOfArgs
  11. + ' arguments, given ' + args.length + '; arguments were:',
  12. types.toWrittenString(formatStr)];
  13. for (var i = 0; i < args.length; i++) {
  14. errorStrBuffer.push( types.toWrittenString(args[i]) );
  15. }
  16. raise( types.incompleteExn(types.exnFailContract, errorStrBuffer.join(' '), []) );
  17. }
  18. var pattern = new RegExp("~[sSaAneE%~]", "g");
  19. var buffer = args.slice(0);;
  20. function f(s) {
  21. if (s == "~~") {
  22. return "~";
  23. } else if (s == '~n' || s == '~%') {
  24. return "\n";
  25. } else if (s == '~s' || s == "~S") {
  26. if (buffer.length == 0) {
  27. throwFormatError();
  28. }
  29. return types.toWrittenString(buffer.shift());
  30. } else if (s == '~e' || s == "~E") {
  31. // FIXME: we don't yet have support for the error-print
  32. // handler, and currently treat ~e just like ~s.
  33. if (buffer.length == 0) {
  34. throwFormatError();
  35. }
  36. return types.toWrittenString(buffer.shift());
  37. } else if (s == '~a' || s == "~A") {
  38. if (buffer.length == 0) {
  39. throwFormatError();
  40. }
  41. return types.toDisplayedString(buffer.shift());
  42. } else {
  43. throw types.internalError('format: string.replace matched invalid regexp', false);
  44. }
  45. }
  46. var result = formatStr.replace(pattern, f);
  47. if (buffer.length > 0) {
  48. throwFormatError();
  49. }
  50. return result;
  51. };
  52. // forEachK: CPS( array CPS(array -> void) (error -> void) -> void )
  53. // Iterates through an array and applies f to each element using CPS
  54. // If an error is thrown, it catches the error and calls f_error on it
  55. var forEachK = function(a, f, f_error, k) {
  56. var forEachHelp = function(i) {
  57. if( i >= a.length ) {
  58. if (k) {
  59. return k();
  60. } else {
  61. return;
  62. }
  63. }
  64. try {
  65. return f(a[i], function() { return forEachHelp(i+1); });
  66. } catch (e) {
  67. f_error(e);
  68. }
  69. };
  70. return forEachHelp(0);
  71. };
  72. // reportError: (or exception string) -> void
  73. // Reports an error to the user, either at the console
  74. // if the console exists, or as alerts otherwise.
  75. var reportError = function(e) {
  76. var reporter;
  77. if (typeof(console) != 'undefined' &&
  78. typeof(console.log) != 'undefined') {
  79. reporter = (function(x) { console.log(x); });
  80. } else {
  81. reporter = (function(x) { alert(x); });
  82. }
  83. if (typeof e == 'string') {
  84. reporter(e);
  85. } else if ( types.isSchemeError(e) ) {
  86. if ( types.isExn(e.val) ) {
  87. reporter( types.exnMessage(e.val) );
  88. }
  89. else {
  90. reporter(e.val);
  91. }
  92. } else if ( types.isInternalError(e) ) {
  93. reporter(e.val);
  94. } else if (e.message) {
  95. reporter(e.message);
  96. } else {
  97. reporter(e.toString());
  98. }
  99. // if (plt.Kernel.lastLoc) {
  100. // var loc = plt.Kernel.lastLoc;
  101. // if (typeof(loc) === 'string') {
  102. // reporter("Error was raised around " + loc);
  103. // } else if (typeof(loc) !== 'undefined' &&
  104. // typeof(loc.line) !== 'undefined') {
  105. // reporter("Error was raised around: "
  106. // + plt.Kernel.locToString(loc));
  107. // }
  108. // }
  109. };
  110. var raise = function(v) {
  111. throw types.schemeError(v);
  112. };
  113. var procArityContains = function(n) {
  114. return function(proc) {
  115. var singleCase = function(aCase) {
  116. if ( aCase instanceof types.ContinuationClosureValue ) {
  117. return true;
  118. }
  119. return (aCase.numParams == n ||
  120. (aCase.isRest && aCase.numParams <= n));
  121. };
  122. var cases = [];
  123. if ( proc instanceof types.ContinuationClosureValue ||
  124. proc instanceof types.ClosureValue ||
  125. proc instanceof types.PrimProc ) {
  126. return singleCase(proc);
  127. }
  128. else if (proc instanceof types.CasePrimitive) {
  129. cases = proc.cases;
  130. }
  131. else if (proc instanceof types.CaseLambdaValue) {
  132. cases = proc.closures;
  133. }
  134. for (var i = 0; i < cases.length; i++) {
  135. if ( singleCase(cases[i]) )
  136. return true;
  137. }
  138. return false;
  139. }
  140. };
  141. var throwCheckError = function(details, pos, args) {
  142. var errorFormatStr;
  143. if (args && args.length > 1) {
  144. var errorFormatStrBuffer = ['~a: expects type <~a> as ~a arguments, given: ~s; other arguments were:'];
  145. for (var i = 0; i < args.length; i++) {
  146. if ( i != pos-1 ) {
  147. errorFormatStrBuffer.push( types.toWrittenString(args[i]) );
  148. }
  149. }
  150. errorFormatStr = errorFormatStrBuffer.join(' ');
  151. }
  152. else {
  153. errorFormatStr = "~a: expects argument of type <~a>, given: ~s";
  154. details.splice(2, 1);
  155. }
  156. raise( types.incompleteExn(types.exnFailContract,
  157. helpers.format(errorFormatStr, details),
  158. []) );
  159. };
  160. var check = function(x, f, functionName, typeName, position, args) {
  161. if ( !f(x) ) {
  162. throwCheckError([functionName,
  163. typeName,
  164. helpers.ordinalize(position),
  165. x],
  166. position,
  167. args);
  168. }
  169. };
  170. var isList = function(x) {
  171. var seenPairs = makeLowLevelEqHash();
  172. while (true) {
  173. if (seenPairs.containsKey(x)) {
  174. return true;
  175. } else if (x === types.EMPTY) {
  176. return true;
  177. } else if (types.isPair(x)) {
  178. seenPairs.put(x, true);
  179. x = x.rest();
  180. } else {
  181. return false;
  182. }
  183. }
  184. };
  185. var isListOf = function(x, f) {
  186. var seenPairs = makeLowLevelEqHash();
  187. while (true) {
  188. if (seenPairs.containsKey(x)) {
  189. return true;
  190. } else if (x === types.EMPTY) {
  191. return true;
  192. } else if (types.isPair(x)) {
  193. seenPairs.put(x, true);
  194. if (f(x.first())) {
  195. x = x.rest();
  196. } else {
  197. return false;
  198. }
  199. } else {
  200. return false;
  201. }
  202. }
  203. };
  204. var checkListOf = function(lst, f, functionName, typeName, position, args) {
  205. if ( !isListOf(lst, f) ) {
  206. helpers.throwCheckError([functionName,
  207. 'list of ' + typeName,
  208. helpers.ordinalize(position),
  209. lst],
  210. position,
  211. args);
  212. }
  213. };
  214. // // remove: array any -> array
  215. // // removes the first instance of v in a
  216. // // or returns a copy of a if v does not exist
  217. // var remove = function(a, v) {
  218. // for (var i = 0; i < a.length; i++) {
  219. // if (a[i] === v) {
  220. // return a.slice(0, i).concat( a.slice(i+1, a.length) );
  221. // }
  222. // }
  223. // return a.slice(0);
  224. // };
  225. // map: array (any -> any) -> array
  226. // applies f to each element of a and returns the result
  227. // as a new array
  228. var map = function(f, a) {
  229. var b = new Array(a.length);
  230. for (var i = 0; i < a.length; i++) {
  231. b[i] = f(a[i]);
  232. }
  233. return b;
  234. };
  235. var concatMap = function(f, a) {
  236. var b = [];
  237. for (var i = 0; i < a.length; i++) {
  238. b = b.concat( f(a[i]) );
  239. }
  240. return b;
  241. };
  242. var schemeListToArray = function(lst) {
  243. var result = [];
  244. while ( !lst.isEmpty() ) {
  245. result.push(lst.first());
  246. lst = lst.rest();
  247. }
  248. return result;
  249. }
  250. // deepListToArray: any -> any
  251. // Converts list structure to array structure.
  252. var deepListToArray = function(x) {
  253. var thing = x;
  254. if (thing === types.EMPTY) {
  255. return [];
  256. } else if (types.isPair(thing)) {
  257. var result = [];
  258. while (!thing.isEmpty()) {
  259. result.push(deepListToArray(thing.first()));
  260. thing = thing.rest();
  261. }
  262. return result;
  263. } else {
  264. return x;
  265. }
  266. }
  267. var flattenSchemeListToArray = function(x) {
  268. if ( !isList(x) ) {
  269. return [x];
  270. }
  271. var ret = [];
  272. while ( !x.isEmpty() ) {
  273. ret = ret.concat( flattenSchemeListToArray(x.first()) );
  274. x = x.rest();
  275. }
  276. return ret;
  277. };
  278. // assocListToHash: (listof (list X Y)) -> (hashof X Y)
  279. var assocListToHash = function(lst) {
  280. var result = {};
  281. while ( !lst.isEmpty() ) {
  282. var key = lst.first().first();
  283. var val = lst.first().rest().first();
  284. result[key] = val;
  285. lst = lst.rest();
  286. }
  287. return result;
  288. };
  289. var ordinalize = function(n) {
  290. // special case for 11th:
  291. if ( n % 100 == 11 ) {
  292. return n + 'th';
  293. }
  294. var res = n;
  295. switch( n % 10 ) {
  296. case 1: res += 'st'; break;
  297. case 2: res += 'nd'; break;
  298. case 3: res += 'rd'; break;
  299. default: res += 'th'; break;
  300. }
  301. return res;
  302. }
  303. var wrapJsValue = function(x) {
  304. if (x === undefined) {
  305. return types.jsValue('undefined', x);
  306. }
  307. else if (x === null) {
  308. return types.jsValue('null', x);
  309. }
  310. else if (typeof(x) == 'function') {
  311. return types.jsValue('function', x);
  312. }
  313. else if ( x instanceof Array ) {
  314. return types.jsValue('array', x);
  315. }
  316. else if ( typeof(x) == 'string' ) {
  317. return types.jsValue("'" + x.toString() + "'", x);
  318. }
  319. else {
  320. return types.jsValue(x.toString(), x);
  321. }
  322. };
  323. var getKeyCodeName = function(e) {
  324. var code = e.charCode || e.keyCode;
  325. var keyname;
  326. switch(code) {
  327. case 16: keyname = "shift"; break;
  328. case 17: keyname = "control"; break;
  329. case 19: keyname = "pause"; break;
  330. case 27: keyname = "escape"; break;
  331. case 33: keyname = "prior"; break;
  332. case 34: keyname = "next"; break;
  333. case 35: keyname = "end"; break;
  334. case 36: keyname = "home"; break;
  335. case 37: keyname = "left"; break;
  336. case 38: keyname = "up"; break;
  337. case 39: keyname = "right"; break;
  338. case 40: keyname = "down"; break;
  339. case 42: keyname = "print"; break;
  340. case 45: keyname = "insert"; break;
  341. case 46: keyname = String.fromCharCode(127); break;
  342. case 106: keyname = "*"; break;
  343. case 107: keyname = "+"; break;
  344. case 109: keyname = "-"; break;
  345. case 110: keyname = "."; break;
  346. case 111: keyname = "/"; break;
  347. case 144: keyname = "numlock"; break;
  348. case 145: keyname = "scroll"; break;
  349. case 186: keyname = ";"; break;
  350. case 187: keyname = "="; break;
  351. case 188: keyname = ","; break;
  352. case 189: keyname = "-"; break;
  353. case 190: keyname = "."; break;
  354. case 191: keyname = "/"; break;
  355. case 192: keyname = "`"; break;
  356. case 219: keyname = "["; break;
  357. case 220: keyname = "\\"; break;
  358. case 221: keyname = "]"; break;
  359. case 222: keyname = "'"; break;
  360. default: if (code >= 96 && code <= 105) {
  361. keyname = (code - 96).toString();
  362. }
  363. else if (code >= 112 && code <= 123) {
  364. keyname = "f" + (code - 111);
  365. }
  366. else {
  367. keyname = String.fromCharCode(code).toLowerCase();
  368. }
  369. break;
  370. }
  371. return keyname;
  372. };
  373. // maybeCallAfterAttach: dom-node -> void
  374. // walk the tree rooted at aNode, and call afterAttach if the element has
  375. // such a method.
  376. var maybeCallAfterAttach = function(aNode) {
  377. var stack = [aNode];
  378. while (stack.length !== 0) {
  379. var nextNode = stack.pop();
  380. if (nextNode.afterAttach) {
  381. nextNode.afterAttach(nextNode);
  382. }
  383. if (nextNode.hasChildNodes && nextNode.hasChildNodes()) {
  384. var children = nextNode.childNodes;
  385. for (var i = 0; i < children.length; i++) {
  386. stack.push(children[i]);
  387. }
  388. }
  389. }
  390. };
  391. // makeLocationDom: location -> dom
  392. // Dom type that has special support in the editor through the print hook.
  393. // The print hook is expected to look at the printing of dom values with
  394. // this particular structure. In the context of WeScheme, the environment
  395. // will rewrite these to be clickable links.
  396. var makeLocationDom = function(aLocation) {
  397. var locationSpan = document.createElement("span");
  398. var idSpan = document.createElement("span");
  399. var offsetSpan = document.createElement("span");
  400. var lineSpan = document.createElement("span");
  401. var columnSpan = document.createElement("span");
  402. var spanSpan = document.createElement("span");
  403. locationSpan['className'] = 'location-reference';
  404. idSpan['className'] = 'location-id';
  405. offsetSpan['className'] = 'location-offset';
  406. lineSpan['className'] = 'location-line';
  407. columnSpan['className'] = 'location-column';
  408. spanSpan['className'] = 'location-span';
  409. idSpan.appendChild(document.createTextNode(String(aLocation.id)));
  410. offsetSpan.appendChild(document.createTextNode(String(aLocation.offset)));
  411. lineSpan.appendChild(document.createTextNode(String(aLocation.line)));
  412. columnSpan.appendChild(document.createTextNode(String(aLocation.column)));
  413. spanSpan.appendChild(document.createTextNode(String(aLocation.span)));
  414. locationSpan.appendChild(idSpan);
  415. locationSpan.appendChild(offsetSpan);
  416. locationSpan.appendChild(lineSpan);
  417. locationSpan.appendChild(columnSpan);
  418. locationSpan.appendChild(spanSpan);
  419. return locationSpan;
  420. };
  421. var isLocationDom = function(thing) {
  422. return (thing
  423. &&
  424. (thing.nodeType === Node.TEXT_NODE ||
  425. thing.nodeType === Node.ELEMENT_NODE)
  426. &&
  427. thing['className'] === 'location-reference');
  428. };
  429. var _eqHashCodeCounter = 0;
  430. makeEqHashCode = function() {
  431. _eqHashCodeCounter++;
  432. return _eqHashCodeCounter;
  433. };
  434. // getHashCode: any -> (or fixnum string)
  435. // Produces a hashcode appropriate for eq.
  436. getEqHashCode = function(x) {
  437. if (typeof(x) === 'string') {
  438. return x;
  439. }
  440. if (typeof(x) === 'number') {
  441. return String(x);
  442. }
  443. if (x && !x._eqHashCode) {
  444. x._eqHashCode = makeEqHashCode();
  445. }
  446. if (x && x._eqHashCode) {
  447. return x._eqHashCode;
  448. }
  449. return 0;
  450. };
  451. var makeLowLevelEqHash = function() {
  452. return new _Hashtable(function(x) { return getEqHashCode(x); },
  453. function(x, y) { return x === y; });
  454. };
  455. // Inheritance.
  456. var heir = function(parentPrototype) {
  457. var f = function() {}
  458. f.prototype = parentPrototype;
  459. return new f();
  460. };
  461. ////////////////////////////////////////////////
  462. helpers.format = format;
  463. helpers.forEachK = forEachK;
  464. helpers.reportError = reportError;
  465. helpers.raise = raise;
  466. helpers.procArityContains = procArityContains;
  467. helpers.throwCheckError = throwCheckError;
  468. helpers.isList = isList;
  469. helpers.isListOf = isListOf;
  470. helpers.check = check;
  471. helpers.checkListOf = checkListOf;
  472. // helpers.remove = remove;
  473. helpers.map = map;
  474. helpers.concatMap = concatMap;
  475. helpers.schemeListToArray = schemeListToArray;
  476. helpers.deepListToArray = deepListToArray;
  477. helpers.flattenSchemeListToArray = flattenSchemeListToArray;
  478. helpers.assocListToHash = assocListToHash;
  479. helpers.ordinalize = ordinalize;
  480. helpers.wrapJsValue = wrapJsValue;
  481. helpers.getKeyCodeName = getKeyCodeName;
  482. helpers.maybeCallAfterAttach = maybeCallAfterAttach;
  483. helpers.makeLocationDom = makeLocationDom;
  484. helpers.isLocationDom = isLocationDom;
  485. helpers.getEqHashCode = getEqHashCode;
  486. helpers.makeLowLevelEqHash = makeLowLevelEqHash;
  487. helpers.heir = heir;
  488. })();
  489. /////////////////////////////////////////////////////////////////