mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00

* Bump version to 2.0.0; bump dependencies versions * Make v2 docs the primary docs; jettison the v1 docs’ source: whenever the v1 docs need to be rebuild in the future, that can be done on the `1` branch and copied over; simplify folder tree * Updated v1 docs that reflect that v2 is out and have updated paths to reflect that the v2 docs are now the primary docs, and the v1 docs only live under /v1/ * Add Google Analytics; track navigation, editing code and running code * 2.0.0 changelog * Fix link to root docs * No more @next; installing local copy should be --save-dev * Analytics on the browser-based tests page should prove fascinating . . . * Update annotated source * Add note to changelog clarifying scope
333 lines
11 KiB
JavaScript
333 lines
11 KiB
JavaScript
// Generated by CoffeeScript 2.0.0
|
|
(function() {
|
|
// This file contains the common helper functions that we'd like to share among
|
|
// the **Lexer**, **Rewriter**, and the **Nodes**. Merge objects, flatten
|
|
// arrays, count characters, that sort of thing.
|
|
|
|
// Peek at the beginning of a given string to see if it matches a sequence.
|
|
var attachCommentsToNode, buildLocationData, buildLocationHash, extend, flatten, ref, repeat, syntaxErrorToString;
|
|
|
|
exports.starts = function(string, literal, start) {
|
|
return literal === string.substr(start, literal.length);
|
|
};
|
|
|
|
// Peek at the end of a given string to see if it matches a sequence.
|
|
exports.ends = function(string, literal, back) {
|
|
var len;
|
|
len = literal.length;
|
|
return literal === string.substr(string.length - len - (back || 0), len);
|
|
};
|
|
|
|
// Repeat a string `n` times.
|
|
exports.repeat = repeat = function(str, n) {
|
|
var res;
|
|
// Use clever algorithm to have O(log(n)) string concatenation operations.
|
|
res = '';
|
|
while (n > 0) {
|
|
if (n & 1) {
|
|
res += str;
|
|
}
|
|
n >>>= 1;
|
|
str += str;
|
|
}
|
|
return res;
|
|
};
|
|
|
|
// Trim out all falsy values from an array.
|
|
exports.compact = function(array) {
|
|
var i, item, len1, results;
|
|
results = [];
|
|
for (i = 0, len1 = array.length; i < len1; i++) {
|
|
item = array[i];
|
|
if (item) {
|
|
results.push(item);
|
|
}
|
|
}
|
|
return results;
|
|
};
|
|
|
|
// Count the number of occurrences of a string in a string.
|
|
exports.count = function(string, substr) {
|
|
var num, pos;
|
|
num = pos = 0;
|
|
if (!substr.length) {
|
|
return 1 / 0;
|
|
}
|
|
while (pos = 1 + string.indexOf(substr, pos)) {
|
|
num++;
|
|
}
|
|
return num;
|
|
};
|
|
|
|
// Merge objects, returning a fresh copy with attributes from both sides.
|
|
// Used every time `Base#compile` is called, to allow properties in the
|
|
// options hash to propagate down the tree without polluting other branches.
|
|
exports.merge = function(options, overrides) {
|
|
return extend(extend({}, options), overrides);
|
|
};
|
|
|
|
// Extend a source object with the properties of another object (shallow copy).
|
|
extend = exports.extend = function(object, properties) {
|
|
var key, val;
|
|
for (key in properties) {
|
|
val = properties[key];
|
|
object[key] = val;
|
|
}
|
|
return object;
|
|
};
|
|
|
|
// Return a flattened version of an array.
|
|
// Handy for getting a list of `children` from the nodes.
|
|
exports.flatten = flatten = function(array) {
|
|
var element, flattened, i, len1;
|
|
flattened = [];
|
|
for (i = 0, len1 = array.length; i < len1; i++) {
|
|
element = array[i];
|
|
if ('[object Array]' === Object.prototype.toString.call(element)) {
|
|
flattened = flattened.concat(flatten(element));
|
|
} else {
|
|
flattened.push(element);
|
|
}
|
|
}
|
|
return flattened;
|
|
};
|
|
|
|
// Delete a key from an object, returning the value. Useful when a node is
|
|
// looking for a particular method in an options hash.
|
|
exports.del = function(obj, key) {
|
|
var val;
|
|
val = obj[key];
|
|
delete obj[key];
|
|
return val;
|
|
};
|
|
|
|
// Typical Array::some
|
|
exports.some = (ref = Array.prototype.some) != null ? ref : function(fn) {
|
|
var e, i, len1, ref1;
|
|
ref1 = this;
|
|
for (i = 0, len1 = ref1.length; i < len1; i++) {
|
|
e = ref1[i];
|
|
if (fn(e)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
// Helper function for extracting code from Literate CoffeeScript by stripping
|
|
// out all non-code blocks, producing a string of CoffeeScript code that can
|
|
// be compiled “normally.”
|
|
exports.invertLiterate = function(code) {
|
|
var blankLine, i, indented, insideComment, len1, line, listItemStart, out, ref1;
|
|
out = [];
|
|
blankLine = /^\s*$/;
|
|
indented = /^[\t ]/;
|
|
listItemStart = /^(?:\t?| {0,3})(?:[\*\-\+]|[0-9]{1,9}\.)[ \t]/; // Up to one tab, or up to three spaces, or neither;
|
|
// followed by `*`, `-` or `+`;
|
|
// or by an integer up to 9 digits long, followed by a period;
|
|
// followed by a space or a tab.
|
|
insideComment = false;
|
|
ref1 = code.split('\n');
|
|
for (i = 0, len1 = ref1.length; i < len1; i++) {
|
|
line = ref1[i];
|
|
if (blankLine.test(line)) {
|
|
insideComment = false;
|
|
out.push(line);
|
|
} else if (insideComment || listItemStart.test(line)) {
|
|
insideComment = true;
|
|
out.push(`# ${line}`);
|
|
} else if (!insideComment && indented.test(line)) {
|
|
out.push(line);
|
|
} else {
|
|
insideComment = true;
|
|
out.push(`# ${line}`);
|
|
}
|
|
}
|
|
return out.join('\n');
|
|
};
|
|
|
|
// Merge two jison-style location data objects together.
|
|
// If `last` is not provided, this will simply return `first`.
|
|
buildLocationData = function(first, last) {
|
|
if (!last) {
|
|
return first;
|
|
} else {
|
|
return {
|
|
first_line: first.first_line,
|
|
first_column: first.first_column,
|
|
last_line: last.last_line,
|
|
last_column: last.last_column
|
|
};
|
|
}
|
|
};
|
|
|
|
buildLocationHash = function(loc) {
|
|
return `${loc.first_line}x${loc.first_column}-${loc.last_line}x${loc.last_column}`;
|
|
};
|
|
|
|
// This returns a function which takes an object as a parameter, and if that
|
|
// object is an AST node, updates that object's locationData.
|
|
// The object is returned either way.
|
|
exports.addDataToNode = function(parserState, first, last) {
|
|
return function(obj) {
|
|
var i, len1, objHash, ref1, token, tokenHash;
|
|
// Add location data
|
|
if (((obj != null ? obj.updateLocationDataIfMissing : void 0) != null) && (first != null)) {
|
|
obj.updateLocationDataIfMissing(buildLocationData(first, last));
|
|
}
|
|
// Add comments data
|
|
if (!parserState.tokenComments) {
|
|
parserState.tokenComments = {};
|
|
ref1 = parserState.parser.tokens;
|
|
for (i = 0, len1 = ref1.length; i < len1; i++) {
|
|
token = ref1[i];
|
|
if (!token.comments) {
|
|
continue;
|
|
}
|
|
tokenHash = buildLocationHash(token[2]);
|
|
if (parserState.tokenComments[tokenHash] == null) {
|
|
parserState.tokenComments[tokenHash] = token.comments;
|
|
} else {
|
|
parserState.tokenComments[tokenHash].push(...token.comments);
|
|
}
|
|
}
|
|
}
|
|
if (obj.locationData != null) {
|
|
objHash = buildLocationHash(obj.locationData);
|
|
if (parserState.tokenComments[objHash] != null) {
|
|
attachCommentsToNode(parserState.tokenComments[objHash], obj);
|
|
}
|
|
}
|
|
return obj;
|
|
};
|
|
};
|
|
|
|
exports.attachCommentsToNode = attachCommentsToNode = function(comments, node) {
|
|
if ((comments == null) || comments.length === 0) {
|
|
return;
|
|
}
|
|
if (node.comments == null) {
|
|
node.comments = [];
|
|
}
|
|
return node.comments.push(...comments);
|
|
};
|
|
|
|
// Convert jison location data to a string.
|
|
// `obj` can be a token, or a locationData.
|
|
exports.locationDataToString = function(obj) {
|
|
var locationData;
|
|
if (("2" in obj) && ("first_line" in obj[2])) {
|
|
locationData = obj[2];
|
|
} else if ("first_line" in obj) {
|
|
locationData = obj;
|
|
}
|
|
if (locationData) {
|
|
return `${locationData.first_line + 1}:${locationData.first_column + 1}-` + `${locationData.last_line + 1}:${locationData.last_column + 1}`;
|
|
} else {
|
|
return "No location data";
|
|
}
|
|
};
|
|
|
|
// A `.coffee.md` compatible version of `basename`, that returns the file sans-extension.
|
|
exports.baseFileName = function(file, stripExt = false, useWinPathSep = false) {
|
|
var parts, pathSep;
|
|
pathSep = useWinPathSep ? /\\|\// : /\//;
|
|
parts = file.split(pathSep);
|
|
file = parts[parts.length - 1];
|
|
if (!(stripExt && file.indexOf('.') >= 0)) {
|
|
return file;
|
|
}
|
|
parts = file.split('.');
|
|
parts.pop();
|
|
if (parts[parts.length - 1] === 'coffee' && parts.length > 1) {
|
|
parts.pop();
|
|
}
|
|
return parts.join('.');
|
|
};
|
|
|
|
// Determine if a filename represents a CoffeeScript file.
|
|
exports.isCoffee = function(file) {
|
|
return /\.((lit)?coffee|coffee\.md)$/.test(file);
|
|
};
|
|
|
|
// Determine if a filename represents a Literate CoffeeScript file.
|
|
exports.isLiterate = function(file) {
|
|
return /\.(litcoffee|coffee\.md)$/.test(file);
|
|
};
|
|
|
|
// Throws a SyntaxError from a given location.
|
|
// The error's `toString` will return an error message following the "standard"
|
|
// format `<filename>:<line>:<col>: <message>` plus the line with the error and a
|
|
// marker showing where the error is.
|
|
exports.throwSyntaxError = function(message, location) {
|
|
var error;
|
|
error = new SyntaxError(message);
|
|
error.location = location;
|
|
error.toString = syntaxErrorToString;
|
|
// Instead of showing the compiler's stacktrace, show our custom error message
|
|
// (this is useful when the error bubbles up in Node.js applications that
|
|
// compile CoffeeScript for example).
|
|
error.stack = error.toString();
|
|
throw error;
|
|
};
|
|
|
|
// Update a compiler SyntaxError with source code information if it didn't have
|
|
// it already.
|
|
exports.updateSyntaxError = function(error, code, filename) {
|
|
// Avoid screwing up the `stack` property of other errors (i.e. possible bugs).
|
|
if (error.toString === syntaxErrorToString) {
|
|
error.code || (error.code = code);
|
|
error.filename || (error.filename = filename);
|
|
error.stack = error.toString();
|
|
}
|
|
return error;
|
|
};
|
|
|
|
syntaxErrorToString = function() {
|
|
var codeLine, colorize, colorsEnabled, end, filename, first_column, first_line, last_column, last_line, marker, ref1, ref2, ref3, start;
|
|
if (!(this.code && this.location)) {
|
|
return Error.prototype.toString.call(this);
|
|
}
|
|
({first_line, first_column, last_line, last_column} = this.location);
|
|
if (last_line == null) {
|
|
last_line = first_line;
|
|
}
|
|
if (last_column == null) {
|
|
last_column = first_column;
|
|
}
|
|
filename = this.filename || '[stdin]';
|
|
codeLine = this.code.split('\n')[first_line];
|
|
start = first_column;
|
|
// Show only the first line on multi-line errors.
|
|
end = first_line === last_line ? last_column + 1 : codeLine.length;
|
|
marker = codeLine.slice(0, start).replace(/[^\s]/g, ' ') + repeat('^', end - start);
|
|
// Check to see if we're running on a color-enabled TTY.
|
|
if (typeof process !== "undefined" && process !== null) {
|
|
colorsEnabled = ((ref1 = process.stdout) != null ? ref1.isTTY : void 0) && !((ref2 = process.env) != null ? ref2.NODE_DISABLE_COLORS : void 0);
|
|
}
|
|
if ((ref3 = this.colorful) != null ? ref3 : colorsEnabled) {
|
|
colorize = function(str) {
|
|
return `\x1B[1;31m${str}\x1B[0m`;
|
|
};
|
|
codeLine = codeLine.slice(0, start) + colorize(codeLine.slice(start, end)) + codeLine.slice(end);
|
|
marker = colorize(marker);
|
|
}
|
|
return `${filename}:${first_line + 1}:${first_column + 1}: error: ${this.message}\n${codeLine}\n${marker}`;
|
|
};
|
|
|
|
exports.nameWhitespaceCharacter = function(string) {
|
|
switch (string) {
|
|
case ' ':
|
|
return 'space';
|
|
case '\n':
|
|
return 'newline';
|
|
case '\r':
|
|
return 'carriage return';
|
|
case '\t':
|
|
return 'tab';
|
|
default:
|
|
return string;
|
|
}
|
|
};
|
|
|
|
}).call(this);
|