/*global $*/ /*jslint browser: true, unparam: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */ // list structures (pairs, empty) (function (baselib) { 'use strict'; var exports = {}; baselib.lists = exports; var Empty = function () { }; Empty.EMPTY = new Empty(); var EMPTY = Empty.EMPTY; Empty.prototype.equals = function (other, aUnionFind) { return other instanceof Empty; }; Empty.prototype.hashCode = function(depth) { return baselib.hashes.getEqualHashCode("empty"); }; Empty.prototype.reverse = function () { return this; }; Empty.prototype.toWrittenString = function (cache) { return "empty"; }; Empty.prototype.toDisplayedString = function (cache) { return "empty"; }; Empty.prototype.toString = function (cache) { return "()"; }; Empty.prototype.toDomNode = function(params) { if (params.getMode() === "display") { return $("").text("()").get(0); } else if (params.getMode() === "write") { return $("").text("()").get(0); } else if (params.getMode() === "print") { if (params.getDepth() === 0) { return $("").text("'()").get(0); } else { return $("").text("()").get(0); } } else if (params.getMode() === "constructor") { return $("").text("(list)").get(0); } else { return $("").text("()").get(0); } }; // Empty.append: (listof X) -> (listof X) Empty.prototype.append = function (b) { return b; }; ////////////////////////////////////////////////////////////////////// // Cons Pairs var Cons = function (first, rest) { this.first = first; this.rest = rest; }; var makePair = function (first, rest) { return new Cons(first, rest); }; Cons.prototype.reverse = function () { var lst = this; var ret = EMPTY; while (lst !== EMPTY) { ret = makePair(lst.first, ret); lst = lst.rest; } return ret; }; // FIXME: can we reduce the recursion on this? Cons.prototype.equals = function (other, aUnionFind) { if (!(other instanceof Cons)) { return false; } return (baselib.equality.equals(this.first, other.first, aUnionFind) && baselib.equality.equals(this.rest, other.rest, aUnionFind)); }; Cons.prototype.hashCode = function(depth) { var k = baselib.hashes.getEqualHashCode("Cons"); k += baselib.hashes.getEqualHashCode(this.first, depth); k = baselib.hashes.hashMix(k); k += baselib.hashes.getEqualHashCode(this.rest, depth); k = baselib.hashes.hashMix(k); return k; }; // Cons.append: (listof X) -> (listof X) Cons.prototype.append = function (b) { if (b === EMPTY) { return this; } var ret = b; var lst = this.reverse(); while (lst !== EMPTY) { ret = makePair(lst.first, ret); lst = lst.rest; } return ret; }; Cons.prototype.toWrittenString = function (cache) { cache.put(this, true); var texts = []; var p = this; while (p instanceof Cons) { texts.push(baselib.format.toWrittenString(p.first, cache)); p = p.rest; if (typeof (p) === 'object' && cache.containsKey(p)) { break; } } if (p !== EMPTY) { texts.push('.'); texts.push(baselib.format.toWrittenString(p, cache)); } return "(" + texts.join(" ") + ")"; }; Cons.prototype.toString = Cons.prototype.toWrittenString; Cons.prototype.toDisplayedString = function (cache) { cache.put(this, true); var texts = []; var p = this; while (p instanceof Cons) { texts.push(baselib.format.toDisplayedString(p.first, cache)); p = p.rest; if (typeof (p) === 'object' && cache.containsKey(p)) { break; } } if (p !== EMPTY) { texts.push('.'); texts.push(baselib.format.toDisplayedString(p, cache)); } return "(" + texts.join(" ") + ")"; }; Cons.prototype.toDomNode = function (params) { var node; var subelts = [], dottedPair = false, i; var p = this; while (p instanceof Cons) { subelts.push(params.recur(p.first)); p = p.rest; if (typeof (p) === 'object' && params.containsKey(p)) { break; } } if (p !== EMPTY) { dottedPair = true; subelts.push(params.recur(p)); } if (params.getMode() === 'constructor') { if (dottedPair) { node = subelts[subelts.length - 1]; for (i = subelts.length - 2; i >= 0; i--) { node = $('') .text("(cons ") .append(subelts[i]) .append(" ") .append(node) .append(")").get(0); } return node; } else { node = document.createElement("span"); node.appendChild(document.createTextNode("(")); node.appendChild(document.createTextNode("list")); node.appendChild(document.createTextNode(" ")); node.appendChild(subelts[0]); for (i = 1; i < subelts.length; i++) { node.appendChild(document.createTextNode(" ")); node.appendChild(subelts[i]); } node.appendChild(document.createTextNode(")")); return node; } } node = document.createElement('span'); if (params.getMode() === 'print') { node.appendChild(document.createTextNode("'")); } node.appendChild(document.createTextNode("(")); node.appendChild(subelts[0]); if (subelts.length > 1) { for (i = 1; i < subelts.length - 1; i++) { node.appendChild(document.createTextNode(" ")); node.appendChild(subelts[i]); } if (dottedPair) { node.appendChild(document.createTextNode(" ")); node.appendChild(document.createTextNode(".")); } node.appendChild(document.createTextNode(" ")); node.appendChild(subelts[subelts.length - 1]); } node.appendChild(document.createTextNode(")")); return node; }; var isPair = function (x) { return x instanceof Cons; }; var isEmpty = function (x) { return x === EMPTY; }; var makeList = function () { var result = EMPTY, i; for (i = arguments.length - 1; i >= 0; i--) { result = makePair(arguments[i], result); } return result; }; var arrayToList = function (arr) { var result = EMPTY, i; for (i = arr.length -1; i >= 0; i--) { result = makePair(arr[i], result); } return result; }; // Coerse a list back into a JavaScript array. var listToArray = function (lst) { var result = []; while (lst !== EMPTY) { result.push(lst.first); lst = lst.rest; } return result; }; // isList: Any -> Boolean // Returns true if x is a list (a chain of pairs terminated by EMPTY). var isList = function (x) { var tortoise, hare; tortoise = hare = x; if (hare === EMPTY) { return true; } if (!(hare instanceof Cons)) { return false; } while (true) { // Loop invariant: at the beginning of the loop, both tortoise // and hare should be pointing to a cons cell. tortoise = tortoise.rest; hare = hare.rest; if (hare instanceof Cons) { // optimization to get amortized linear time isList: if (hare._isList !== void(0)) { tortoise._isList = hare._isList; return hare._isList; } hare = hare.rest; // optimization to get amortized linear time isList: if (hare instanceof Cons && hare._isList !== void(0)) { tortoise._isList = hare._isList; return hare._isList; } } if (hare === EMPTY) { // optimization to get amortized linear time isList: tortoise._isList = true; return true; } if (tortoise === hare) { tortoise._isList = false; return false; } if (!(hare instanceof Cons)) { tortoise._isList = false; return false; } } }; var reverse = function (lst) { var rev = EMPTY; while (lst !== EMPTY) { rev = makePair(lst.first, rev); lst = lst.rest; } return rev; }; var length = function (lst) { var len = 0; while (lst !== EMPTY) { len++; lst = lst.rest; } return len; }; var listRef = function (lst, n) { var i; for (i = 0; i < n; i++) { lst = lst.rest; } return lst.first; }; ////////////////////////////////////////////////////////////////////// exports.EMPTY = EMPTY; exports.Empty = Empty; exports.Cons = Cons; exports.isPair = isPair; exports.isList = isList; exports.isEmpty = isEmpty; exports.makePair = makePair; exports.makeList = makeList; exports.reverse = reverse; exports.length = length; exports.listRef = listRef; exports.listToArray = listToArray; exports.arrayToList = arrayToList; }(this.plt.baselib));