hopper
Version:
An interpreter for the Grace programming language
239 lines (176 loc) • 4.47 kB
JavaScript
// Common utility definitions.
;
var hasOwnProp, proto, slice, unicode;
unicode = require("./unicode");
proto = Object.prototype;
// Simple identity function.
exports.id = function (x) {
return x;
};
slice = Array.prototype.slice;
// Standard not-quite-array slicer.
exports.slice = function (list, from, to) {
return slice.call(list, from, to);
};
exports.contains = function (list, value) {
var i, l;
for (i = 0, l = list.length; i < l; i += 1) {
if (list[i] === value) {
return true;
}
}
return false;
};
// Strip the parentheses from Grace method names.
exports.uglify = function (name) {
return name.replace(/\(\)/g, "").replace(/ :=/, ":=").replace(/ /g, "_");
};
hasOwnProp = proto.hasOwnProperty;
// Ensures the correct hasOwnProperty is used.
function owns(object, name) {
return hasOwnProp.call(object, name);
}
exports.owns = owns;
// Run a function for every iterable property directly in an object.
function forProperties(from, func) {
var key;
for (key in from) {
if (owns(from, key)) {
func(key, from[key]);
}
}
}
exports.forProperties = forProperties;
// Simple object key copier.
function extend(into, from) {
var key;
for (key in from) {
if (owns(from, key) && !owns(into, key)) {
into[key] = from[key];
}
}
}
exports.extend = extend;
exports.extendAll = function (into, from) {
var key;
for (key in from) {
if (!owns(into, key)) {
into[key] = from[key];
}
}
};
exports.map = function (list, func) {
var i, l, newList;
newList = [];
for (i = 0, l = list.length; i < l; i += 1) {
newList.push(func(list[i]));
}
return newList;
};
function pad(str) {
while (str.length < 4) {
str = "0" + str;
}
return str;
}
// Escape quotes, backslashes, and control characters in a string, making it
// safe to render inside quotes.
exports.escape = function (str) {
var c, i, l, string;
string = "";
for (i = 0, l = str.length; i < l; i += 1) {
c = str[i];
if (unicode.isControl(c)) {
string += "\\" + (c === "\b" ? "b" : c === "\n" ? "n" : c === "\r" ? "r" :
c === "\t" ? "t" : c === "\f" ? "f" : c === "\v" ? "v" :
c === "\u0000" ? "0" : "u" + pad(c.charCodeAt(0).toString(16)));
} else if (c === '"') {
string += '\\"';
} else if (c === "\\") {
string += "\\\\";
} else {
string += c;
}
}
return string;
};
exports.newApply = function (Constructor, args) {
function Temp() {
Constructor.apply(this, args);
}
Temp.prototype = Constructor.prototype;
return new Temp();
};
// Test if a value is an array.
exports.isArray = Array.isArray || function (value) {
return proto.toString.call(value) === "[object Array]";
};
// Replicate a value in a list the given number of times.
exports.replicate = function (count, value) {
var i, list;
list = [];
for (i = 0; i < count; i += 1) {
list.push(value);
}
return list;
};
// Repeat the contents of a list the given number of times.
exports.repeat = function (count, elements) {
var i, list;
list = [];
for (i = 0; i < count; i += 1) {
list = list.concat(elements);
}
return list;
};
// A memoising function that also bans any recursion.
exports.once = function (func) {
var hasFailed, hasFinished, isRunning, result;
isRunning = false;
hasFailed = false;
hasFinished = false;
return function () {
if (hasFailed) {
throw result;
}
if (hasFinished) {
return result;
}
if (isRunning) {
throw new Error("Memoised function called itself");
}
isRunning = true;
try {
result = func.apply(this, arguments);
} catch (error) {
hasFailed = true;
result = error;
throw error;
} finally {
isRunning = false;
}
hasFinished = true;
return result;
};
};
function makeCloneable(value) {
var l, properties;
properties = slice.call(arguments, 1);
l = properties.length;
function Clone() {
makeCloneable.apply(null, [this].concat(properties));
}
Clone.prototype = value;
value.clone = function () {
var clone, i, property;
clone = new Clone();
for (i = 0; i < l; i += 1) {
property = properties[i];
clone[property] = this[property];
}
return clone;
};
}
exports.makeCloneable = makeCloneable;
// Include the system utilities too.
extend(exports, require("util"));