jashkenas--coffeescript/lib/coffee-script/helpers.js

245 lines
6.7 KiB
JavaScript
Raw Normal View History

// Generated by CoffeeScript 2.0.0-alpha
2010-07-25 07:15:12 +00:00
(function() {
var buildLocationData, extend, flatten, marked, ref, repeat, syntaxErrorToString;
marked = require('marked');
2010-09-25 00:29:44 +00:00
exports.starts = function(string, literal, start) {
2010-09-25 00:18:47 +00:00
return literal === string.substr(start, literal.length);
};
2010-09-25 00:29:44 +00:00
exports.ends = function(string, literal, back) {
2010-09-26 14:28:48 +00:00
var len;
len = literal.length;
return literal === string.substr(string.length - len - (back || 0), len);
2010-09-25 00:18:47 +00:00
};
exports.repeat = repeat = function(str, n) {
2013-02-26 17:41:01 +00:00
var res;
res = '';
while (n > 0) {
if (n & 1) {
res += str;
}
n >>>= 1;
str += str;
}
return res;
2013-02-25 17:41:34 +00:00
};
2010-09-25 00:29:44 +00:00
exports.compact = function(array) {
var i, item, len1, results;
results = [];
for (i = 0, len1 = array.length; i < len1; i++) {
item = array[i];
2012-04-10 18:57:45 +00:00
if (item) {
results.push(item);
2012-04-10 18:57:45 +00:00
}
}
return results;
2010-09-25 00:18:47 +00:00
};
exports.count = function(string, substr) {
var num, pos;
num = pos = 0;
2012-04-10 18:57:45 +00:00
if (!substr.length) {
return 1 / 0;
}
while (pos = 1 + string.indexOf(substr, pos)) {
2010-09-25 00:18:47 +00:00
num++;
}
return num;
2010-09-25 00:18:47 +00:00
};
2010-09-25 00:29:44 +00:00
exports.merge = function(options, overrides) {
2010-09-25 00:18:47 +00:00
return extend(extend({}, options), overrides);
};
extend = exports.extend = function(object, properties) {
var key, val;
2010-10-01 22:26:37 +00:00
for (key in properties) {
val = properties[key];
2010-09-26 14:28:48 +00:00
object[key] = val;
}
2010-09-25 00:18:47 +00:00
return object;
};
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;
};
2010-09-25 00:29:44 +00:00
exports.del = function(obj, key) {
var val;
val = obj[key];
delete obj[key];
return val;
2010-09-25 00:18:47 +00:00
};
exports.some = (ref = Array.prototype.some) != null ? ref : function(fn) {
Refactor `Literal` into several subtypes Previously, the parser created `Literal` nodes for many things. This resulted in information loss. Instead of being able to check the node type, we had to use regexes to tell the different types of `Literal`s apart. That was a bit like parsing literals twice: Once in the lexer, and once (or more) in the compiler. It also caused problems, such as `` `this` `` and `this` being indistinguishable (fixes #2009). Instead returning `new Literal` in the grammar, subtypes of it are now returned instead, such as `NumberLiteral`, `StringLiteral` and `IdentifierLiteral`. `new Literal` by itself is only used to represent code chunks that fit no category. (While mentioning `NumberLiteral`, there's also `InfinityLiteral` now, which is a subtype of `NumberLiteral`.) `StringWithInterpolations` has been added as a subtype of `Parens`, and `RegexWithInterpolations` as a subtype of `Call`. This makes it easier for other programs to make use of CoffeeScript's "AST" (nodes). For example, it is now possible to distinguish between `"a #{b} c"` and `"a " + b + " c"`. Fixes #4192. `SuperCall` has been added as a subtype of `Call`. Note, though, that some information is still lost, especially in the lexer. For example, there is no way to distinguish a heredoc from a regular string, or a heregex without interpolations from a regular regex. Binary and octal number literals are indistinguishable from hexadecimal literals. After the new subtypes were added, they were taken advantage of, removing most regexes in nodes.coffee. `SIMPLENUM` (which matches non-hex integers) had to be kept, though, because such numbers need special handling in JavaScript (for example in `1..toString()`). An especially nice hack to get rid of was using `new String()` for the token value for reserved identifiers (to be able to set a property on them which could survive through the parser). Now it's a good old regular string. In range literals, slices, splices and for loop steps when number literals are involved, CoffeeScript can do some optimizations, such as precomputing the value of, say, `5 - 3` (outputting `2` instead of `5 - 3` literally). As a side bonus, this now also works with hexadecimal number literals, such as `0x02`. Finally, this also improves the output of `coffee --nodes`: # Before: $ bin/coffee -ne 'while true "#{a}" break' Block While Value Bool Block Value Parens Block Op + Value """" Value Parens Block Value "a" "break" # After: $ bin/coffee -ne 'while true "#{a}" break' Block While Value BooleanLiteral: true Block Value StringWithInterpolations Block Op + Value StringLiteral: "" Value Parens Block Value IdentifierLiteral: a StatementLiteral: break
2016-01-31 19:24:31 +00:00
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;
};
exports.invertLiterate = function(code) {
var generateRandomToken, i, item, len1, out, ref1, token;
generateRandomToken = function() {
return `${Math.random() * Date.now()}`;
};
while (token === void 0 || code.indexOf(token) !== -1) {
token = generateRandomToken();
}
code = code.replace("\t", token);
out = "";
ref1 = marked.lexer(code, {});
for (i = 0, len1 = ref1.length; i < len1; i++) {
item = ref1[i];
if (item.type === 'code') {
out += `${item.text}\n`;
}
}
out.replace(token, "\t");
return out;
};
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
};
}
};
exports.addLocationDataFn = function(first, last) {
return function(obj) {
if (((typeof obj) === 'object') && (!!obj['updateLocationDataIfMissing'])) {
obj.updateLocationDataIfMissing(buildLocationData(first, last));
}
return obj;
};
};
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";
}
};
[CS2] Output ES2015 arrow functions, default parameters, rest parameters (#4311) * Eliminate wrapper around “bound” (arrow) functions; output `=>` for such functions * Remove irrelevant (and breaking) tests * Minor cleanup * When a function parameter is a splat (i.e., it uses the ES2015 rest parameter syntax) output that parameter as ES2015 * Rearrange function parameters when one of the parameters is a splat and isn’t the last parameter (very WIP) * Handle params like `@param`, adding assignment expressions for them when they appear; ensure splat parameter is last * Add parameter names (not a text like `'\nValue IdentifierLiteral: a'`) to the scope, so that parameters can’t be deleted; move body-related lines together; more explanation of what’s going on * For parameters with a default value, correctly add the parameter name to the function scope * Handle expansions in function parameters: when an expansion is found, set the parameters to only be the original parameters left of the expansion, then an `...args` parameter; and in the function body define variables for the parameters to the right of the expansion, including setting default values * Handle splat parameters the same way we handle expansions: if a splat parameter is found, it becomes the last parameter in the function definition, and all following parameters get declared in the function body. Fix the splat/rest parameter values after the post-splat parameters have been extracted from it. Clean up `Code.compileNode` so that we loop through the parameters only once, and we create all expressions using calls like `new IdentifierLiteral` rather than `@makeCode`. * Fix parameter name when a parameter is a splat attached to `this` (e.g. `@param...`) * Rather than assigning post-splat parameters based on index, use slice; passes test “Functions with splats being called with too few arguments” * Dial back our w00t indentation * Better parsing of parameter names (WIP) * Refactor processing of splat/expansion parameters * Fix assignment of default parameters for parameters that come after a splat * Better check for whether a param is attached to `this` * More understandable variable names * For parameters after a splat or expansion, assign them similar to the 1.x destructuring method of using `arguments`, except only concern ourselves with the post-splat parameters instead of all parameters; and use the splat/expansion parameter name, since `arguments` in ES fat arrow functions refers to the parent function’s `arguments` rather than the fat arrow function’s arguments/parameters * Don’t add unnamed parameters (like `[]` as a parameter) to the function scope * Disallow multiple splat/expansion parameters in function definitions; disallow lone expansion parameters * Fix `this` params not getting assigned if the parameter is after a splat parameter * Allow names of function parameters attached to `this` to be reserved words * Always add a statement to the function body defining a variable with its default value, if it has one, if the variable `== null`; this covers the case when ES doesn’t apply the default value when `null` is passed in as a value, but CoffeeScript expects `null` and `undefined` to act interchangeably * Aftermath of having both `undefined` and `null` trigger the use of default values for parameters with default values * More careful parsing of destructured parameters * Fall back to processing destructured parameters in the function body, to account for `this` or default values within destructured objects * Clean up comments * Restore new bare function test, minus the arrow function part of it * Test that bound/arrow functions aren’t overwriting the `arguments` object, which should refer to the parent scope’s `arguments` (like `this`) * Follow ES2015 spec for parameter default values: `null` gets assigned as as `null`, not the default value * Mimic ES default parameters behavior for parameters after a splat or expansion parameter * Bound functions cannot be generators: remove no-longer-relevant test, add check to throw error if `yield` appears inside a bound (arrow) function * Error for bound generator functions should underline the `yield`
2016-10-26 05:26:13 +00:00
exports.baseFileName = function(file, stripExt = false, useWinPathSep = false) {
var parts, pathSep;
pathSep = useWinPathSep ? /\\|\// : /\//;
2013-03-15 22:13:04 +00:00
parts = file.split(pathSep);
file = parts[parts.length - 1];
if (!(stripExt && file.indexOf('.') >= 0)) {
2013-03-04 22:07:16 +00:00
return file;
}
parts = file.split('.');
parts.pop();
if (parts[parts.length - 1] === 'coffee' && parts.length > 1) {
parts.pop();
}
return parts.join('.');
};
exports.isCoffee = function(file) {
return /\.((lit)?coffee|coffee\.md)$/.test(file);
};
exports.isLiterate = function(file) {
return /\.(litcoffee|coffee\.md)$/.test(file);
};
exports.throwSyntaxError = function(message, location) {
var error;
error = new SyntaxError(message);
error.location = location;
error.toString = syntaxErrorToString;
error.stack = error.toString();
throw error;
};
exports.updateSyntaxError = function(error, code, filename) {
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, ref4, start;
if (!(this.code && this.location)) {
return Error.prototype.toString.call(this);
}
ref1 = this.location, first_line = ref1.first_line, first_column = ref1.first_column, last_line = ref1.last_line, last_column = ref1.last_column;
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;
end = first_line === last_line ? last_column + 1 : codeLine.length;
marker = codeLine.slice(0, start).replace(/[^\s]/g, ' ') + repeat('^', end - start);
if (typeof process !== "undefined" && process !== null) {
colorsEnabled = ((ref2 = process.stdout) != null ? ref2.isTTY : void 0) && !((ref3 = process.env) != null ? ref3.NODE_DISABLE_COLORS : void 0);
}
if ((ref4 = this.colorful) != null ? ref4 : 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);