mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
Merge branch '2' into get-set-warning
# Conflicts: # lib/coffeescript/lexer.js
This commit is contained in:
@@ -24,15 +24,10 @@
|
||||
|
||||
helpers.extend(global, {
|
||||
task: function(name, description, action) {
|
||||
var ref;
|
||||
if (!action) {
|
||||
ref = [description, action], action = ref[0], description = ref[1];
|
||||
[action, description] = [description, action];
|
||||
}
|
||||
return tasks[name] = {
|
||||
name: name,
|
||||
description: description,
|
||||
action: action
|
||||
};
|
||||
return tasks[name] = {name, description, action};
|
||||
},
|
||||
option: function(letter, flag, description) {
|
||||
return switches.push([letter, flag, description]);
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
path = require('path');
|
||||
|
||||
Lexer = require('./lexer').Lexer;
|
||||
({Lexer} = require('./lexer'));
|
||||
|
||||
parser = require('./parser').parser;
|
||||
({parser} = require('./parser'));
|
||||
|
||||
helpers = require('./helpers');
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
exports.compile = compile = withPrettyErrors(function(code, options) {
|
||||
var currentColumn, currentLine, encoded, extend, filename, fragment, fragments, generateSourceMap, header, i, j, js, len, len1, map, merge, newLines, ref, ref1, sourceMapDataURI, sourceURL, token, tokens, v3SourceMap;
|
||||
merge = helpers.merge, extend = helpers.extend;
|
||||
({merge, extend} = helpers);
|
||||
options = extend({}, options);
|
||||
generateSourceMap = options.sourceMap || options.inlineMap || (options.filename == null);
|
||||
filename = options.filename || '<anonymous>';
|
||||
@@ -132,7 +132,7 @@
|
||||
}
|
||||
if (options.sourceMap) {
|
||||
return {
|
||||
js: js,
|
||||
js,
|
||||
sourceMap: map,
|
||||
v3SourceMap: JSON.stringify(v3SourceMap, null, 2)
|
||||
};
|
||||
@@ -254,9 +254,9 @@
|
||||
stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw;
|
||||
try {
|
||||
answer = compile(stripped, {
|
||||
filename: filename,
|
||||
sourceMap: sourceMap,
|
||||
inlineMap: inlineMap,
|
||||
filename,
|
||||
sourceMap,
|
||||
inlineMap,
|
||||
sourceFiles: [filename],
|
||||
literate: helpers.isLiterate(filename)
|
||||
});
|
||||
@@ -274,7 +274,7 @@
|
||||
var tag, token;
|
||||
token = parser.tokens[this.pos++];
|
||||
if (token) {
|
||||
tag = token[0], this.yytext = token[1], this.yylloc = token[2];
|
||||
[tag, this.yytext, this.yylloc] = token;
|
||||
parser.errorToken = token.origin || token;
|
||||
this.yylineno = this.yylloc.first_line;
|
||||
} else {
|
||||
@@ -293,11 +293,10 @@
|
||||
|
||||
parser.yy = require('./nodes');
|
||||
|
||||
parser.yy.parseError = function(message, arg) {
|
||||
var errorLoc, errorTag, errorText, errorToken, token, tokens;
|
||||
token = arg.token;
|
||||
errorToken = parser.errorToken, tokens = parser.tokens;
|
||||
errorTag = errorToken[0], errorText = errorToken[1], errorLoc = errorToken[2];
|
||||
parser.yy.parseError = function(message, {token}) {
|
||||
var errorLoc, errorTag, errorText, errorToken, tokens;
|
||||
({errorToken, tokens} = parser);
|
||||
[errorTag, errorText, errorLoc] = errorToken;
|
||||
errorText = (function() {
|
||||
switch (false) {
|
||||
case errorToken !== tokens[tokens.length - 1]:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript 2.0.0-alpha1
|
||||
(function() {
|
||||
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, makePrelude, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, ref, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs,
|
||||
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, makePrelude, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
fs = require('fs');
|
||||
@@ -13,9 +13,9 @@
|
||||
|
||||
CoffeeScript = require('./coffeescript');
|
||||
|
||||
ref = require('child_process'), spawn = ref.spawn, exec = ref.exec;
|
||||
({spawn, exec} = require('child_process'));
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
({EventEmitter} = require('events'));
|
||||
|
||||
useWinPathSep = path.sep === '\\';
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
optionParser = null;
|
||||
|
||||
exports.run = function() {
|
||||
var i, len, literals, ref1, replCliOpts, results, source;
|
||||
var i, len, literals, ref, replCliOpts, results, source;
|
||||
parseOptions();
|
||||
replCliOpts = {
|
||||
useGlobal: true
|
||||
@@ -90,10 +90,10 @@
|
||||
opts.join = path.resolve(opts.join);
|
||||
console.error('\nThe --join option is deprecated and will be removed in a future version.\n\nIf for some reason it\'s necessary to share local variables between files,\nreplace...\n\n $ coffee --compile --join bundle.js -- a.coffee b.coffee c.coffee\n\nwith...\n\n $ cat a.coffee b.coffee c.coffee | coffee --compile --stdio > bundle.js\n');
|
||||
}
|
||||
ref1 = opts["arguments"];
|
||||
ref = opts["arguments"];
|
||||
results = [];
|
||||
for (i = 0, len = ref1.length; i < len; i++) {
|
||||
source = ref1[i];
|
||||
for (i = 0, len = ref.length; i < len; i++) {
|
||||
source = ref[i];
|
||||
source = path.resolve(source);
|
||||
results.push(compilePath(source, true, source));
|
||||
}
|
||||
@@ -104,7 +104,7 @@
|
||||
return requires.map(function(module) {
|
||||
var _, match, name;
|
||||
if (match = module.match(/^(.*)=(.*)$/)) {
|
||||
_ = match[0], name = match[1], module = match[2];
|
||||
[_, name, module] = match;
|
||||
}
|
||||
name || (name = helpers.baseFileName(module, true, useWinPathSep));
|
||||
return `${name} = require('${module}')`;
|
||||
@@ -178,10 +178,10 @@
|
||||
};
|
||||
|
||||
findDirectoryIndex = function(source) {
|
||||
var err, ext, i, index, len, ref1;
|
||||
ref1 = CoffeeScript.FILE_EXTENSIONS;
|
||||
for (i = 0, len = ref1.length; i < len; i++) {
|
||||
ext = ref1[i];
|
||||
var err, ext, i, index, len, ref;
|
||||
ref = CoffeeScript.FILE_EXTENSIONS;
|
||||
for (i = 0, len = ref.length; i < len; i++) {
|
||||
ext = ref[i];
|
||||
index = path.join(source, `index${ext}`);
|
||||
try {
|
||||
if ((fs.statSync(index)).isFile()) {
|
||||
@@ -203,11 +203,7 @@
|
||||
o = opts;
|
||||
options = compileOptions(file, base);
|
||||
try {
|
||||
t = task = {
|
||||
file: file,
|
||||
input: input,
|
||||
options: options
|
||||
};
|
||||
t = task = {file, input, options};
|
||||
CoffeeScript.emit('compile', task);
|
||||
if (o.tokens) {
|
||||
return printTokens(CoffeeScript.tokens(t.input, t.options));
|
||||
@@ -425,12 +421,12 @@
|
||||
};
|
||||
|
||||
silentUnlink = function(path) {
|
||||
var err, ref1;
|
||||
var err, ref;
|
||||
try {
|
||||
return fs.unlinkSync(path);
|
||||
} catch (error) {
|
||||
err = error;
|
||||
if ((ref1 = err.code) !== 'ENOENT' && ref1 !== 'EPERM') {
|
||||
if ((ref = err.code) !== 'ENOENT' && ref !== 'EPERM') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -546,7 +542,7 @@
|
||||
compileOptions = function(filename, base) {
|
||||
var answer, cwd, jsDir, jsPath;
|
||||
answer = {
|
||||
filename: filename,
|
||||
filename,
|
||||
literate: opts.literate || helpers.isLiterate(filename),
|
||||
bare: opts.bare,
|
||||
header: opts.compile && !opts['no-header'],
|
||||
@@ -559,7 +555,7 @@
|
||||
jsPath = outputPath(filename, base);
|
||||
jsDir = path.dirname(jsPath);
|
||||
answer = helpers.merge(answer, {
|
||||
jsPath: jsPath,
|
||||
jsPath,
|
||||
sourceRoot: path.relative(jsDir, cwd),
|
||||
sourceFiles: [path.relative(cwd, filename)],
|
||||
generatedFile: helpers.baseFileName(jsPath, false, useWinPathSep)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
(function() {
|
||||
var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
|
||||
|
||||
Parser = require('jison').Parser;
|
||||
({Parser} = require('jison'));
|
||||
|
||||
unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Generated by CoffeeScript 2.0.0-alpha1
|
||||
(function() {
|
||||
var buildLocationData, extend, flatten, marked, ref, repeat, syntaxErrorToString;
|
||||
var buildLocationData, extend, flatten, md, ref, repeat, syntaxErrorToString;
|
||||
|
||||
marked = require('marked');
|
||||
md = require('markdown-it')();
|
||||
|
||||
exports.starts = function(string, literal, start) {
|
||||
return literal === string.substr(start, literal.length);
|
||||
@@ -28,10 +28,10 @@
|
||||
};
|
||||
|
||||
exports.compact = function(array) {
|
||||
var i, item, len1, results;
|
||||
var item, j, len1, results;
|
||||
results = [];
|
||||
for (i = 0, len1 = array.length; i < len1; i++) {
|
||||
item = array[i];
|
||||
for (j = 0, len1 = array.length; j < len1; j++) {
|
||||
item = array[j];
|
||||
if (item) {
|
||||
results.push(item);
|
||||
}
|
||||
@@ -65,10 +65,10 @@
|
||||
};
|
||||
|
||||
exports.flatten = flatten = function(array) {
|
||||
var element, flattened, i, len1;
|
||||
var element, flattened, j, len1;
|
||||
flattened = [];
|
||||
for (i = 0, len1 = array.length; i < len1; i++) {
|
||||
element = array[i];
|
||||
for (j = 0, len1 = array.length; j < len1; j++) {
|
||||
element = array[j];
|
||||
if ('[object Array]' === Object.prototype.toString.call(element)) {
|
||||
flattened = flattened.concat(flatten(element));
|
||||
} else {
|
||||
@@ -86,10 +86,10 @@
|
||||
};
|
||||
|
||||
exports.some = (ref = Array.prototype.some) != null ? ref : function(fn) {
|
||||
var e, i, len1, ref1;
|
||||
var e, j, len1, ref1;
|
||||
ref1 = this;
|
||||
for (i = 0, len1 = ref1.length; i < len1; i++) {
|
||||
e = ref1[i];
|
||||
for (j = 0, len1 = ref1.length; j < len1; j++) {
|
||||
e = ref1[j];
|
||||
if (fn(e)) {
|
||||
return true;
|
||||
}
|
||||
@@ -98,24 +98,23 @@
|
||||
};
|
||||
|
||||
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`;
|
||||
var out;
|
||||
out = [];
|
||||
md.renderer.rules = {
|
||||
code_block: function(tokens, idx) {
|
||||
var i, j, len1, line, lines, results, startLine;
|
||||
startLine = tokens[idx].map[0];
|
||||
lines = tokens[idx].content.split('\n');
|
||||
results = [];
|
||||
for (i = j = 0, len1 = lines.length; j < len1; i = ++j) {
|
||||
line = lines[i];
|
||||
results.push(out[startLine + i] = line);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
out.replace(token, "\t");
|
||||
return out;
|
||||
};
|
||||
md.render(code);
|
||||
return out.join('\n');
|
||||
};
|
||||
|
||||
buildLocationData = function(first, last) {
|
||||
@@ -197,11 +196,11 @@
|
||||
};
|
||||
|
||||
syntaxErrorToString = function() {
|
||||
var codeLine, colorize, colorsEnabled, end, filename, first_column, first_line, last_column, last_line, marker, ref1, ref2, ref3, ref4, start;
|
||||
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);
|
||||
}
|
||||
ref1 = this.location, first_line = ref1.first_line, first_column = ref1.first_column, last_line = ref1.last_line, last_column = ref1.last_column;
|
||||
({first_line, first_column, last_line, last_column} = this.location);
|
||||
if (last_line == null) {
|
||||
last_line = first_line;
|
||||
}
|
||||
@@ -214,9 +213,9 @@
|
||||
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);
|
||||
colorsEnabled = ((ref1 = process.stdout) != null ? ref1.isTTY : void 0) && !((ref2 = process.env) != null ? ref2.NODE_DISABLE_COLORS : void 0);
|
||||
}
|
||||
if ((ref4 = this.colorful) != null ? ref4 : colorsEnabled) {
|
||||
if ((ref3 = this.colorful) != null ? ref3 : colorsEnabled) {
|
||||
colorize = function(str) {
|
||||
return `\x1B[1;31m${str}\x1B[0m`;
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Generated by CoffeeScript 2.0.0-alpha1
|
||||
(function() {
|
||||
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, ref, ref1, repeat, starts, throwSyntaxError,
|
||||
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, repeat, starts, throwSyntaxError,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
ref = require('./rewriter'), Rewriter = ref.Rewriter, INVERSES = ref.INVERSES;
|
||||
({Rewriter, INVERSES} = require('./rewriter'));
|
||||
|
||||
ref1 = require('./helpers'), count = ref1.count, starts = ref1.starts, compact = ref1.compact, repeat = ref1.repeat, invertLiterate = ref1.invertLiterate, locationDataToString = ref1.locationDataToString, throwSyntaxError = ref1.throwSyntaxError;
|
||||
({count, starts, compact, repeat, invertLiterate, locationDataToString, throwSyntaxError} = require('./helpers'));
|
||||
|
||||
exports.Lexer = Lexer = class Lexer {
|
||||
tokenize(code, opts = {}) {
|
||||
var consumed, end, i, ref2;
|
||||
var consumed, end, i;
|
||||
this.literate = opts.literate;
|
||||
this.indent = 0;
|
||||
this.baseIndent = 0;
|
||||
@@ -29,7 +29,7 @@
|
||||
i = 0;
|
||||
while (this.chunk = code.slice(i)) {
|
||||
consumed = this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken();
|
||||
ref2 = this.getLineAndColumnFromChunk(consumed), this.chunkLine = ref2[0], this.chunkColumn = ref2[1];
|
||||
[this.chunkLine, this.chunkColumn] = this.getLineAndColumnFromChunk(consumed);
|
||||
i += consumed;
|
||||
if (opts.untilBalanced && this.ends.length === 0) {
|
||||
return {
|
||||
@@ -64,11 +64,11 @@
|
||||
}
|
||||
|
||||
identifierToken() {
|
||||
var alias, colon, colonOffset, id, idLength, input, match, poppedToken, prev, prevprev, ref10, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, tag, tagToken;
|
||||
var alias, colon, colonOffset, id, idLength, input, match, poppedToken, prev, prevprev, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, tag, tagToken;
|
||||
if (!(match = IDENTIFIER.exec(this.chunk))) {
|
||||
return 0;
|
||||
}
|
||||
input = match[0], id = match[1], colon = match[2];
|
||||
[input, id, colon] = match;
|
||||
idLength = id.length;
|
||||
poppedToken = void 0;
|
||||
if (id === 'own' && this.tag() === 'FOR') {
|
||||
@@ -82,27 +82,27 @@
|
||||
if (id === 'as' && this.seenImport) {
|
||||
if (this.value() === '*') {
|
||||
this.tokens[this.tokens.length - 1][0] = 'IMPORT_ALL';
|
||||
} else if (ref2 = this.value(), indexOf.call(COFFEE_KEYWORDS, ref2) >= 0) {
|
||||
} else if (ref = this.value(), indexOf.call(COFFEE_KEYWORDS, ref) >= 0) {
|
||||
this.tokens[this.tokens.length - 1][0] = 'IDENTIFIER';
|
||||
}
|
||||
if ((ref3 = this.tag()) === 'DEFAULT' || ref3 === 'IMPORT_ALL' || ref3 === 'IDENTIFIER') {
|
||||
if ((ref1 = this.tag()) === 'DEFAULT' || ref1 === 'IMPORT_ALL' || ref1 === 'IDENTIFIER') {
|
||||
this.token('AS', id);
|
||||
return id.length;
|
||||
}
|
||||
}
|
||||
if (id === 'as' && this.seenExport && ((ref4 = this.tag()) === 'IDENTIFIER' || ref4 === 'DEFAULT')) {
|
||||
if (id === 'as' && this.seenExport && ((ref2 = this.tag()) === 'IDENTIFIER' || ref2 === 'DEFAULT')) {
|
||||
this.token('AS', id);
|
||||
return id.length;
|
||||
}
|
||||
if (id === 'default' && this.seenExport && ((ref5 = this.tag()) === 'EXPORT' || ref5 === 'AS')) {
|
||||
if (id === 'default' && this.seenExport && ((ref3 = this.tag()) === 'EXPORT' || ref3 === 'AS')) {
|
||||
this.token('DEFAULT', id);
|
||||
return id.length;
|
||||
}
|
||||
prev = this.prev();
|
||||
tag = colon || (prev != null) && (((ref6 = prev[0]) === '.' || ref6 === '?.' || ref6 === '::' || ref6 === '?::') || !prev.spaced && prev[0] === '@') ? 'PROPERTY' : 'IDENTIFIER';
|
||||
tag = colon || (prev != null) && (((ref4 = prev[0]) === '.' || ref4 === '?.' || ref4 === '::' || ref4 === '?::') || !prev.spaced && prev[0] === '@') ? 'PROPERTY' : 'IDENTIFIER';
|
||||
if (tag === 'IDENTIFIER' && (indexOf.call(JS_KEYWORDS, id) >= 0 || indexOf.call(COFFEE_KEYWORDS, id) >= 0) && !(this.exportSpecifierList && indexOf.call(COFFEE_KEYWORDS, id) >= 0)) {
|
||||
tag = id.toUpperCase();
|
||||
if (tag === 'WHEN' && (ref7 = this.tag(), indexOf.call(LINE_BREAK, ref7) >= 0)) {
|
||||
if (tag === 'WHEN' && (ref5 = this.tag(), indexOf.call(LINE_BREAK, ref5) >= 0)) {
|
||||
tag = 'LEADING_WHEN';
|
||||
} else if (tag === 'FOR') {
|
||||
this.seenFor = true;
|
||||
@@ -130,11 +130,11 @@
|
||||
tag = 'FORFROM';
|
||||
this.seenFor = false;
|
||||
} else if (tag === 'PROPERTY' && prev) {
|
||||
if (prev.spaced && (ref8 = prev[0], indexOf.call(CALLABLE, ref8) >= 0) && /^[gs]et$/.test(prev[1])) {
|
||||
if (prev.spaced && (ref6 = prev[0], indexOf.call(CALLABLE, ref6) >= 0) && /^[gs]et$/.test(prev[1])) {
|
||||
this.error(`'${prev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prev[2]);
|
||||
} else {
|
||||
prevprev = this.tokens[this.tokens.length - 2];
|
||||
if (((ref9 = prev[0]) === '@' || ref9 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1])) {
|
||||
if (((ref7 = prev[0]) === '@' || ref7 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1])) {
|
||||
this.error(`'${prevprev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prevprev[2]);
|
||||
}
|
||||
}
|
||||
@@ -176,7 +176,7 @@
|
||||
tagToken.origin = [tag, alias, tagToken[2]];
|
||||
}
|
||||
if (poppedToken) {
|
||||
ref10 = [poppedToken[2].first_line, poppedToken[2].first_column], tagToken[2].first_line = ref10[0], tagToken[2].first_column = ref10[1];
|
||||
[tagToken[2].first_line, tagToken[2].first_column] = [poppedToken[2].first_line, poppedToken[2].first_column];
|
||||
}
|
||||
if (colon) {
|
||||
colonOffset = input.lastIndexOf(':');
|
||||
@@ -232,8 +232,8 @@
|
||||
}
|
||||
|
||||
stringToken() {
|
||||
var $, attempt, delimiter, doc, end, heredoc, i, indent, indentRegex, match, prev, quote, ref2, ref3, ref4, regex, token, tokens;
|
||||
quote = (STRING_START.exec(this.chunk) || [])[0];
|
||||
var $, attempt, delimiter, doc, end, heredoc, i, indent, indentRegex, match, prev, quote, ref, ref1, regex, token, tokens;
|
||||
[quote] = STRING_START.exec(this.chunk) || [];
|
||||
if (!quote) {
|
||||
return 0;
|
||||
}
|
||||
@@ -241,7 +241,7 @@
|
||||
if (prev && this.value() === 'from' && (this.seenImport || this.seenExport)) {
|
||||
prev[0] = 'FROM';
|
||||
}
|
||||
if (prev && prev.spaced && (ref2 = prev[0], indexOf.call(CALLABLE, ref2) >= 0) && /^[gs]et$/.test(prev[1])) {
|
||||
if (prev && prev.spaced && (ref = prev[0], indexOf.call(CALLABLE, ref) >= 0) && /^[gs]et$/.test(prev[1])) {
|
||||
this.error(`'${prev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prev[2]);
|
||||
}
|
||||
regex = (function() {
|
||||
@@ -257,7 +257,10 @@
|
||||
}
|
||||
})();
|
||||
heredoc = quote.length === 3;
|
||||
ref3 = this.matchWithInterpolations(regex, quote), tokens = ref3.tokens, end = ref3.index;
|
||||
({
|
||||
tokens,
|
||||
index: end
|
||||
} = this.matchWithInterpolations(regex, quote));
|
||||
$ = tokens.length - 1;
|
||||
delimiter = quote.charAt(0);
|
||||
if (heredoc) {
|
||||
@@ -275,16 +278,14 @@
|
||||
})()).join('#{}');
|
||||
while (match = HEREDOC_INDENT.exec(doc)) {
|
||||
attempt = match[1];
|
||||
if (indent === null || (0 < (ref4 = attempt.length) && ref4 < indent.length)) {
|
||||
if (indent === null || (0 < (ref1 = attempt.length) && ref1 < indent.length)) {
|
||||
indent = attempt;
|
||||
}
|
||||
}
|
||||
if (indent) {
|
||||
indentRegex = RegExp(`\\n${indent}`, "g");
|
||||
}
|
||||
this.mergeInterpolationTokens(tokens, {
|
||||
delimiter: delimiter
|
||||
}, (value, i) => {
|
||||
this.mergeInterpolationTokens(tokens, {delimiter}, (value, i) => {
|
||||
value = this.formatString(value);
|
||||
if (indentRegex) {
|
||||
value = value.replace(indentRegex, '\n');
|
||||
@@ -298,9 +299,7 @@
|
||||
return value;
|
||||
});
|
||||
} else {
|
||||
this.mergeInterpolationTokens(tokens, {
|
||||
delimiter: delimiter
|
||||
}, (value, i) => {
|
||||
this.mergeInterpolationTokens(tokens, {delimiter}, (value, i) => {
|
||||
value = this.formatString(value);
|
||||
value = value.replace(SIMPLE_STRING_OMIT, function(match, offset) {
|
||||
if ((i === 0 && offset === 0) || (i === $ && offset + match.length === value.length)) {
|
||||
@@ -320,7 +319,7 @@
|
||||
if (!(match = this.chunk.match(COMMENT))) {
|
||||
return 0;
|
||||
}
|
||||
comment = match[0], here = match[1];
|
||||
[comment, here] = match;
|
||||
if (here) {
|
||||
if (match = HERECOMMENT_ILLEGAL.exec(comment)) {
|
||||
this.error(`block comments cannot contain ${match[0]}`, {
|
||||
@@ -349,7 +348,7 @@
|
||||
}
|
||||
|
||||
regexToken() {
|
||||
var body, closed, end, flags, index, match, origin, prev, ref2, ref3, regex, tokens;
|
||||
var body, closed, end, flags, index, match, origin, prev, ref, ref1, regex, tokens;
|
||||
switch (false) {
|
||||
case !(match = REGEX_ILLEGAL.exec(this.chunk)):
|
||||
this.error(`regular expressions cannot begin with ${match[2]}`, {
|
||||
@@ -357,10 +356,10 @@
|
||||
});
|
||||
break;
|
||||
case !(match = this.matchWithInterpolations(HEREGEX, '///')):
|
||||
tokens = match.tokens, index = match.index;
|
||||
({tokens, index} = match);
|
||||
break;
|
||||
case !(match = REGEX.exec(this.chunk)):
|
||||
regex = match[0], body = match[1], closed = match[2];
|
||||
[regex, body, closed] = match;
|
||||
this.validateEscapes(body, {
|
||||
isRegex: true,
|
||||
offsetInChunk: 1
|
||||
@@ -368,11 +367,11 @@
|
||||
index = regex.length;
|
||||
prev = this.prev();
|
||||
if (prev) {
|
||||
if (prev.spaced && (ref2 = prev[0], indexOf.call(CALLABLE, ref2) >= 0)) {
|
||||
if (prev.spaced && (ref = prev[0], indexOf.call(CALLABLE, ref) >= 0)) {
|
||||
if (!closed || POSSIBLY_DIVISION.test(regex)) {
|
||||
return 0;
|
||||
}
|
||||
} else if (ref3 = prev[0], indexOf.call(NOT_REGEX, ref3) >= 0) {
|
||||
} else if (ref1 = prev[0], indexOf.call(NOT_REGEX, ref1) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -383,7 +382,7 @@
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
flags = REGEX_FLAGS.exec(this.chunk.slice(index))[0];
|
||||
[flags] = REGEX_FLAGS.exec(this.chunk.slice(index));
|
||||
end = index + flags.length;
|
||||
origin = this.makeToken('REGEX', null, 0, end);
|
||||
switch (false) {
|
||||
@@ -482,7 +481,7 @@
|
||||
}
|
||||
|
||||
outdentToken(moveOut, noNewlines, outdentLength) {
|
||||
var decreasedIndent, dent, lastIndent, ref2;
|
||||
var decreasedIndent, dent, lastIndent, ref;
|
||||
decreasedIndent = this.indent - moveOut;
|
||||
while (moveOut > 0) {
|
||||
lastIndent = this.indents[this.indents.length - 1];
|
||||
@@ -496,7 +495,7 @@
|
||||
moveOut -= lastIndent;
|
||||
} else {
|
||||
dent = this.indents.pop() + this.outdebt;
|
||||
if (outdentLength && (ref2 = this.chunk[outdentLength], indexOf.call(INDENTABLE_CLOSERS, ref2) >= 0)) {
|
||||
if (outdentLength && (ref = this.chunk[outdentLength], indexOf.call(INDENTABLE_CLOSERS, ref) >= 0)) {
|
||||
decreasedIndent -= dent - moveOut;
|
||||
moveOut = dent;
|
||||
}
|
||||
@@ -554,9 +553,9 @@
|
||||
}
|
||||
|
||||
literalToken() {
|
||||
var match, message, origin, prev, ref2, ref3, ref4, ref5, skipToken, tag, token, value;
|
||||
var match, message, origin, prev, ref, ref1, ref2, ref3, skipToken, tag, token, value;
|
||||
if (match = OPERATOR.exec(this.chunk)) {
|
||||
value = match[0];
|
||||
[value] = match;
|
||||
if (CODE.test(value)) {
|
||||
this.tagParameters();
|
||||
}
|
||||
@@ -567,14 +566,14 @@
|
||||
prev = this.prev();
|
||||
if (prev && indexOf.call(['=', ...COMPOUND_ASSIGN], value) >= 0) {
|
||||
skipToken = false;
|
||||
if (value === '=' && ((ref2 = prev[1]) === '||' || ref2 === '&&') && !prev.spaced) {
|
||||
if (value === '=' && ((ref = prev[1]) === '||' || ref === '&&') && !prev.spaced) {
|
||||
prev[0] = 'COMPOUND_ASSIGN';
|
||||
prev[1] += '=';
|
||||
prev = this.tokens[this.tokens.length - 2];
|
||||
skipToken = true;
|
||||
}
|
||||
if (prev && prev[0] !== 'PROPERTY') {
|
||||
origin = (ref3 = prev.origin) != null ? ref3 : prev;
|
||||
origin = (ref1 = prev.origin) != null ? ref1 : prev;
|
||||
message = isUnassignable(prev[1], origin[1]);
|
||||
if (message) {
|
||||
this.error(message, origin[2]);
|
||||
@@ -609,12 +608,12 @@
|
||||
} else if (value === '?' && (prev != null ? prev.spaced : void 0)) {
|
||||
tag = 'BIN?';
|
||||
} else if (prev && !prev.spaced) {
|
||||
if (value === '(' && (ref4 = prev[0], indexOf.call(CALLABLE, ref4) >= 0)) {
|
||||
if (value === '(' && (ref2 = prev[0], indexOf.call(CALLABLE, ref2) >= 0)) {
|
||||
if (prev[0] === '?') {
|
||||
prev[0] = 'FUNC_EXIST';
|
||||
}
|
||||
tag = 'CALL_START';
|
||||
} else if (value === '[' && (ref5 = prev[0], indexOf.call(INDEXABLE, ref5) >= 0)) {
|
||||
} else if (value === '[' && (ref3 = prev[0], indexOf.call(INDEXABLE, ref3) >= 0)) {
|
||||
tag = 'INDEX_START';
|
||||
switch (prev[0]) {
|
||||
case '?':
|
||||
@@ -647,7 +646,7 @@
|
||||
return this;
|
||||
}
|
||||
stack = [];
|
||||
tokens = this.tokens;
|
||||
({tokens} = this);
|
||||
i = tokens.length;
|
||||
tokens[--i][0] = 'PARAM_END';
|
||||
while (tok = tokens[--i]) {
|
||||
@@ -675,7 +674,7 @@
|
||||
}
|
||||
|
||||
matchWithInterpolations(regex, delimiter) {
|
||||
var close, column, firstToken, index, lastToken, line, nested, offsetInChunk, open, ref2, ref3, ref4, str, strPart, tokens;
|
||||
var close, column, firstToken, index, lastToken, line, nested, offsetInChunk, open, ref, str, strPart, tokens;
|
||||
tokens = [];
|
||||
offsetInChunk = delimiter.length;
|
||||
if (this.chunk.slice(0, offsetInChunk) !== delimiter) {
|
||||
@@ -683,10 +682,10 @@
|
||||
}
|
||||
str = this.chunk.slice(offsetInChunk);
|
||||
while (true) {
|
||||
strPart = regex.exec(str)[0];
|
||||
[strPart] = regex.exec(str);
|
||||
this.validateEscapes(strPart, {
|
||||
isRegex: delimiter.charAt(0) === '/',
|
||||
offsetInChunk: offsetInChunk
|
||||
offsetInChunk
|
||||
});
|
||||
tokens.push(this.makeToken('NEOSTRING', strPart, offsetInChunk));
|
||||
str = str.slice(strPart.length);
|
||||
@@ -694,18 +693,21 @@
|
||||
if (str.slice(0, 2) !== '#{') {
|
||||
break;
|
||||
}
|
||||
ref2 = this.getLineAndColumnFromChunk(offsetInChunk + 1), line = ref2[0], column = ref2[1];
|
||||
ref3 = new Lexer().tokenize(str.slice(1), {
|
||||
[line, column] = this.getLineAndColumnFromChunk(offsetInChunk + 1);
|
||||
({
|
||||
tokens: nested,
|
||||
index
|
||||
} = new Lexer().tokenize(str.slice(1), {
|
||||
line: line,
|
||||
column: column,
|
||||
untilBalanced: true
|
||||
}), nested = ref3.tokens, index = ref3.index;
|
||||
}));
|
||||
index += 1;
|
||||
open = nested[0], close = nested[nested.length - 1];
|
||||
open[0] = open[1] = '(';
|
||||
close[0] = close[1] = ')';
|
||||
close.origin = ['', 'end of interpolation', close[2]];
|
||||
if (((ref4 = nested[1]) != null ? ref4[0] : void 0) === 'TERMINATOR') {
|
||||
if (((ref = nested[1]) != null ? ref[0] : void 0) === 'TERMINATOR') {
|
||||
nested.splice(1, 1);
|
||||
}
|
||||
tokens.push(['TOKENS', nested]);
|
||||
@@ -729,7 +731,7 @@
|
||||
lastToken[2].last_column -= 1;
|
||||
}
|
||||
return {
|
||||
tokens: tokens,
|
||||
tokens,
|
||||
index: offsetInChunk + delimiter.length
|
||||
};
|
||||
}
|
||||
@@ -742,7 +744,7 @@
|
||||
firstIndex = this.tokens.length;
|
||||
for (i = j = 0, len = tokens.length; j < len; i = ++j) {
|
||||
token = tokens[i];
|
||||
tag = token[0], value = token[1];
|
||||
[tag, value] = token;
|
||||
switch (tag) {
|
||||
case 'TOKENS':
|
||||
if (value.length === 2) {
|
||||
@@ -800,13 +802,13 @@
|
||||
}
|
||||
|
||||
pair(tag) {
|
||||
var lastIndent, prev, ref2, ref3, wanted;
|
||||
ref2 = this.ends, prev = ref2[ref2.length - 1];
|
||||
var lastIndent, prev, ref, ref1, wanted;
|
||||
ref = this.ends, prev = ref[ref.length - 1];
|
||||
if (tag !== (wanted = prev != null ? prev.tag : void 0)) {
|
||||
if ('OUTDENT' !== wanted) {
|
||||
this.error(`unmatched ${tag}`);
|
||||
}
|
||||
ref3 = this.indents, lastIndent = ref3[ref3.length - 1];
|
||||
ref1 = this.indents, lastIndent = ref1[ref1.length - 1];
|
||||
this.outdentToken(lastIndent, true);
|
||||
return this.pair(tag);
|
||||
}
|
||||
@@ -814,7 +816,7 @@
|
||||
}
|
||||
|
||||
getLineAndColumnFromChunk(offset) {
|
||||
var column, lastLine, lineCount, ref2, string;
|
||||
var column, lastLine, lineCount, ref, string;
|
||||
if (offset === 0) {
|
||||
return [this.chunkLine, this.chunkColumn];
|
||||
}
|
||||
@@ -826,7 +828,7 @@
|
||||
lineCount = count(string, '\n');
|
||||
column = this.chunkColumn;
|
||||
if (lineCount > 0) {
|
||||
ref2 = string.split('\n'), lastLine = ref2[ref2.length - 1];
|
||||
ref = string.split('\n'), lastLine = ref[ref.length - 1];
|
||||
column = lastLine.length;
|
||||
} else {
|
||||
column += string.length;
|
||||
@@ -835,11 +837,11 @@
|
||||
}
|
||||
|
||||
makeToken(tag, value, offsetInChunk = 0, length = value.length) {
|
||||
var lastCharacter, locationData, ref2, ref3, token;
|
||||
var lastCharacter, locationData, token;
|
||||
locationData = {};
|
||||
ref2 = this.getLineAndColumnFromChunk(offsetInChunk), locationData.first_line = ref2[0], locationData.first_column = ref2[1];
|
||||
[locationData.first_line, locationData.first_column] = this.getLineAndColumnFromChunk(offsetInChunk);
|
||||
lastCharacter = length > 0 ? length - 1 : 0;
|
||||
ref3 = this.getLineAndColumnFromChunk(offsetInChunk + lastCharacter), locationData.last_line = ref3[0], locationData.last_column = ref3[1];
|
||||
[locationData.last_line, locationData.last_column] = this.getLineAndColumnFromChunk(offsetInChunk + lastCharacter);
|
||||
token = [tag, value, locationData];
|
||||
return token;
|
||||
}
|
||||
@@ -855,14 +857,14 @@
|
||||
}
|
||||
|
||||
tag() {
|
||||
var ref2, token;
|
||||
ref2 = this.tokens, token = ref2[ref2.length - 1];
|
||||
var ref, token;
|
||||
ref = this.tokens, token = ref[ref.length - 1];
|
||||
return token != null ? token[0] : void 0;
|
||||
}
|
||||
|
||||
value() {
|
||||
var ref2, token;
|
||||
ref2 = this.tokens, token = ref2[ref2.length - 1];
|
||||
var ref, token;
|
||||
ref = this.tokens, token = ref[ref.length - 1];
|
||||
return token != null ? token[1] : void 0;
|
||||
}
|
||||
|
||||
@@ -871,8 +873,8 @@
|
||||
}
|
||||
|
||||
unfinished() {
|
||||
var ref2;
|
||||
return LINE_CONTINUER.test(this.chunk) || ((ref2 = this.tag()) === '\\' || ref2 === '.' || ref2 === '?.' || ref2 === '?::' || ref2 === 'UNARY' || ref2 === 'MATH' || ref2 === 'UNARY_MATH' || ref2 === '+' || ref2 === '-' || ref2 === '**' || ref2 === 'SHIFT' || ref2 === 'RELATION' || ref2 === 'COMPARE' || ref2 === '&' || ref2 === '^' || ref2 === '|' || ref2 === '&&' || ref2 === '||' || ref2 === 'BIN?' || ref2 === 'THROW' || ref2 === 'EXTENDS');
|
||||
var ref;
|
||||
return LINE_CONTINUER.test(this.chunk) || ((ref = this.tag()) === '\\' || ref === '.' || ref === '?.' || ref === '?::' || ref === 'UNARY' || ref === 'MATH' || ref === 'UNARY_MATH' || ref === '+' || ref === '-' || ref === '**' || ref === 'SHIFT' || ref === 'RELATION' || ref === 'COMPARE' || ref === '&' || ref === '^' || ref === '|' || ref === '&&' || ref === '||' || ref === 'BIN?' || ref === 'THROW' || ref === 'EXTENDS');
|
||||
}
|
||||
|
||||
formatString(str) {
|
||||
@@ -884,7 +886,7 @@
|
||||
}
|
||||
|
||||
validateEscapes(str, options = {}) {
|
||||
var before, hex, invalidEscape, match, message, octal, ref2, unicode;
|
||||
var before, hex, invalidEscape, match, message, octal, ref, unicode;
|
||||
match = INVALID_ESCAPE.exec(str);
|
||||
if (!match) {
|
||||
return;
|
||||
@@ -896,7 +898,7 @@
|
||||
message = octal ? "octal escape sequences are not allowed" : "invalid escape sequence";
|
||||
invalidEscape = `\\${octal || hex || unicode}`;
|
||||
return this.error(`${message} ${invalidEscape}`, {
|
||||
offset: ((ref2 = options.offsetInChunk) != null ? ref2 : 0) + match.index + before.length,
|
||||
offset: ((ref = options.offsetInChunk) != null ? ref : 0) + match.index + before.length,
|
||||
length: invalidEscape.length
|
||||
});
|
||||
}
|
||||
@@ -939,11 +941,11 @@
|
||||
}
|
||||
|
||||
error(message, options = {}) {
|
||||
var first_column, first_line, location, ref2, ref3, ref4;
|
||||
location = 'first_line' in options ? options : ((ref3 = this.getLineAndColumnFromChunk((ref2 = options.offset) != null ? ref2 : 0), first_line = ref3[0], first_column = ref3[1], ref3), {
|
||||
first_line: first_line,
|
||||
first_column: first_column,
|
||||
last_column: first_column + ((ref4 = options.length) != null ? ref4 : 1) - 1
|
||||
var first_column, first_line, location, ref, ref1;
|
||||
location = 'first_line' in options ? options : ([first_line, first_column] = this.getLineAndColumnFromChunk((ref = options.offset) != null ? ref : 0), {
|
||||
first_line,
|
||||
first_column,
|
||||
last_column: first_column + ((ref1 = options.length) != null ? ref1 : 1) - 1
|
||||
});
|
||||
return throwSyntaxError(message, location);
|
||||
}
|
||||
@@ -966,7 +968,7 @@
|
||||
exports.isUnassignable = isUnassignable;
|
||||
|
||||
isForFrom = function(prev) {
|
||||
var ref2;
|
||||
var ref;
|
||||
if (prev[0] === 'IDENTIFIER') {
|
||||
if (prev[1] === 'from') {
|
||||
prev[1][0] = 'IDENTIFIER';
|
||||
@@ -975,7 +977,7 @@
|
||||
return true;
|
||||
} else if (prev[0] === 'FOR') {
|
||||
return false;
|
||||
} else if ((ref2 = prev[1]) === '{' || ref2 === '[' || ref2 === ',' || ref2 === ':') {
|
||||
} else if ((ref = prev[1]) === '{' || ref === '[' || ref === ',' || ref === ':') {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
(function() {
|
||||
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat;
|
||||
|
||||
repeat = require('./helpers').repeat;
|
||||
({repeat} = require('./helpers'));
|
||||
|
||||
exports.OptionParser = OptionParser = class OptionParser {
|
||||
constructor(rules, banner) {
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
|
||||
if (child_process) {
|
||||
fork = child_process.fork;
|
||||
({fork} = child_process);
|
||||
binary = require.resolve('../../bin/coffee');
|
||||
child_process.fork = function(path, args, options) {
|
||||
if (helpers.isCoffee(path)) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript 2.0.0-alpha1
|
||||
(function() {
|
||||
var CoffeeScript, addHistory, addMultilineHandler, fs, getCommandId, merge, nodeREPL, path, ref, replDefaults, runInContext, updateSyntaxError, vm;
|
||||
var CoffeeScript, addHistory, addMultilineHandler, fs, getCommandId, merge, nodeREPL, path, replDefaults, runInContext, updateSyntaxError, vm;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
@@ -12,18 +12,18 @@
|
||||
|
||||
CoffeeScript = require('./coffeescript');
|
||||
|
||||
ref = require('./helpers'), merge = ref.merge, updateSyntaxError = ref.updateSyntaxError;
|
||||
({merge, updateSyntaxError} = require('./helpers'));
|
||||
|
||||
replDefaults = {
|
||||
prompt: 'coffee> ',
|
||||
historyFile: process.env.HOME ? path.join(process.env.HOME, '.coffee_history') : void 0,
|
||||
historyMaxInputSize: 10240,
|
||||
"eval": function(input, context, filename, cb) {
|
||||
var Assign, Block, Literal, Value, ast, err, js, ref1, referencedVars, token, tokens;
|
||||
var Assign, Block, Literal, Value, ast, err, js, referencedVars, token, tokens;
|
||||
input = input.replace(/\uFF00/g, '\n');
|
||||
input = input.replace(/^\(([\s\S]*)\n\)$/m, '$1');
|
||||
input = input.replace(/^\s*try\s*{([\s\S]*)}\s*catch.*$/m, '$1');
|
||||
ref1 = require('./nodes'), Block = ref1.Block, Assign = ref1.Assign, Value = ref1.Value, Literal = ref1.Literal;
|
||||
({Block, Assign, Value, Literal} = require('./nodes'));
|
||||
try {
|
||||
tokens = CoffeeScript.tokens(input);
|
||||
referencedVars = (function() {
|
||||
@@ -42,7 +42,7 @@
|
||||
js = ast.compile({
|
||||
bare: true,
|
||||
locals: Object.keys(context),
|
||||
referencedVars: referencedVars
|
||||
referencedVars
|
||||
});
|
||||
return cb(null, runInContext(js, context, filename));
|
||||
} catch (error) {
|
||||
@@ -62,9 +62,9 @@
|
||||
};
|
||||
|
||||
addMultilineHandler = function(repl) {
|
||||
var inputStream, multiline, nodeLineListener, origPrompt, outputStream, ref1, rli;
|
||||
rli = repl.rli, inputStream = repl.inputStream, outputStream = repl.outputStream;
|
||||
origPrompt = (ref1 = repl._prompt) != null ? ref1 : repl.prompt;
|
||||
var inputStream, multiline, nodeLineListener, origPrompt, outputStream, ref, rli;
|
||||
({rli, inputStream, outputStream} = repl);
|
||||
origPrompt = (ref = repl._prompt) != null ? ref : repl.prompt;
|
||||
multiline = {
|
||||
enabled: false,
|
||||
initialPrompt: origPrompt.replace(/^[^> ]*/, function(x) {
|
||||
@@ -168,10 +168,10 @@
|
||||
|
||||
module.exports = {
|
||||
start: function(opts = {}) {
|
||||
var build, major, minor, ref1, repl;
|
||||
ref1 = process.versions.node.split('.').map(function(n) {
|
||||
var build, major, minor, repl;
|
||||
[major, minor, build] = process.versions.node.split('.').map(function(n) {
|
||||
return parseInt(n);
|
||||
}), major = ref1[0], minor = ref1[1], build = ref1[2];
|
||||
});
|
||||
if (major < 6) {
|
||||
console.warn("Node 6+ required for CoffeeScript REPL");
|
||||
process.exit(1);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript 2.0.0-alpha1
|
||||
(function() {
|
||||
var BALANCED_PAIRS, CALL_CLOSERS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, k, left, len, ref, rite,
|
||||
var BALANCED_PAIRS, CALL_CLOSERS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, k, left, len, rite,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
generate = function(tag, value, origin) {
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
scanTokens(block) {
|
||||
var i, token, tokens;
|
||||
tokens = this.tokens;
|
||||
({tokens} = this);
|
||||
i = 0;
|
||||
while (token = tokens[i]) {
|
||||
i += block.call(this, token, i, tokens);
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
detectEnd(i, condition, action) {
|
||||
var levels, ref, ref1, token, tokens;
|
||||
tokens = this.tokens;
|
||||
({tokens} = this);
|
||||
levels = 0;
|
||||
while (token = tokens[i]) {
|
||||
if (levels === 0 && condition.call(this, token, i)) {
|
||||
@@ -63,7 +63,7 @@
|
||||
var i, k, len, ref, tag;
|
||||
ref = this.tokens;
|
||||
for (i = k = 0, len = ref.length; k < len; i = ++k) {
|
||||
tag = ref[i][0];
|
||||
[tag] = ref[i];
|
||||
if (tag !== 'TERMINATOR') {
|
||||
break;
|
||||
}
|
||||
@@ -168,10 +168,10 @@
|
||||
stack = [];
|
||||
start = null;
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, newLine, nextTag, offset, prevTag, prevToken, ref, ref1, ref2, ref3, ref4, ref5, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag;
|
||||
tag = token[0];
|
||||
prevTag = (prevToken = i > 0 ? tokens[i - 1] : [])[0];
|
||||
nextTag = (i < tokens.length - 1 ? tokens[i + 1] : [])[0];
|
||||
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, newLine, nextTag, offset, prevTag, prevToken, ref, ref1, ref2, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag;
|
||||
[tag] = token;
|
||||
[prevTag] = prevToken = i > 0 ? tokens[i - 1] : [];
|
||||
[nextTag] = i < tokens.length - 1 ? tokens[i + 1] : [];
|
||||
stackTop = function() {
|
||||
return stack[stack.length - 1];
|
||||
};
|
||||
@@ -302,7 +302,7 @@
|
||||
this.insideForDeclaration = nextTag === 'FOR';
|
||||
startsLine = s === 0 || (ref2 = this.tag(s - 1), indexOf.call(LINEBREAKS, ref2) >= 0) || tokens[s - 1].newLine;
|
||||
if (stackTop()) {
|
||||
ref3 = stackTop(), stackTag = ref3[0], stackIdx = ref3[1];
|
||||
[stackTag, stackIdx] = stackTop();
|
||||
if ((stackTag === '{' || stackTag === 'INDENT' && this.tag(stackIdx - 1) === '{') && (startsLine || this.tag(s - 1) === ',' || this.tag(s - 1) === '{')) {
|
||||
return forward(1);
|
||||
}
|
||||
@@ -316,7 +316,7 @@
|
||||
newLine = prevTag === 'OUTDENT' || prevToken.newLine;
|
||||
if (indexOf.call(IMPLICIT_END, tag) >= 0 || indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) {
|
||||
while (inImplicit()) {
|
||||
ref4 = stackTop(), stackTag = ref4[0], stackIdx = ref4[1], (ref5 = ref4[2], sameLine = ref5.sameLine, startsLine = ref5.startsLine);
|
||||
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop();
|
||||
if (inImplicitCall() && prevTag !== ',') {
|
||||
endImplicitCall();
|
||||
} else if (inImplicitObject() && !this.insideForDeclaration && sameLine && tag !== 'TERMINATOR' && prevTag !== ':') {
|
||||
@@ -351,9 +351,15 @@
|
||||
return 1;
|
||||
}
|
||||
if (token[0] === '{' && (nextLocation = (ref = tokens[i + 1]) != null ? ref[2] : void 0)) {
|
||||
line = nextLocation.first_line, column = nextLocation.first_column;
|
||||
({
|
||||
first_line: line,
|
||||
first_column: column
|
||||
} = nextLocation);
|
||||
} else if (prevLocation = (ref1 = tokens[i - 1]) != null ? ref1[2] : void 0) {
|
||||
line = prevLocation.last_line, column = prevLocation.last_column;
|
||||
({
|
||||
last_line: line,
|
||||
last_column: column
|
||||
} = prevLocation);
|
||||
} else {
|
||||
line = column = 0;
|
||||
}
|
||||
@@ -395,8 +401,8 @@
|
||||
return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent);
|
||||
};
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var j, k, ref, ref1, ref2, tag;
|
||||
tag = token[0];
|
||||
var j, k, ref, ref1, tag;
|
||||
[tag] = token;
|
||||
if (tag === 'TERMINATOR') {
|
||||
if (this.tag(i + 1) === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
|
||||
tokens.splice(i, 1, ...this.indentation());
|
||||
@@ -418,7 +424,7 @@
|
||||
}
|
||||
if (indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) {
|
||||
starter = tag;
|
||||
ref2 = this.indentation(tokens[i]), indent = ref2[0], outdent = ref2[1];
|
||||
[indent, outdent] = this.indentation(tokens[i]);
|
||||
if (starter === 'THEN') {
|
||||
indent.fromThen = true;
|
||||
}
|
||||
@@ -438,8 +444,8 @@
|
||||
original = null;
|
||||
condition = function(token, i) {
|
||||
var prevTag, tag;
|
||||
tag = token[0];
|
||||
prevTag = this.tokens[i - 1][0];
|
||||
[tag] = token;
|
||||
[prevTag] = this.tokens[i - 1];
|
||||
return tag === 'TERMINATOR' || (tag === 'INDENT' && indexOf.call(SINGLE_LINERS, prevTag) < 0);
|
||||
};
|
||||
action = function(token, i) {
|
||||
@@ -492,7 +498,7 @@
|
||||
EXPRESSION_END = [];
|
||||
|
||||
for (k = 0, len = BALANCED_PAIRS.length; k < len; k++) {
|
||||
ref = BALANCED_PAIRS[k], left = ref[0], rite = ref[1];
|
||||
[left, rite] = BALANCED_PAIRS[k];
|
||||
EXPRESSION_START.push(INVERSES[rite] = left);
|
||||
EXPRESSION_END.push(INVERSES[left] = rite);
|
||||
}
|
||||
|
||||
@@ -30,10 +30,7 @@
|
||||
if (Object.prototype.hasOwnProperty.call(this.positions, name)) {
|
||||
return this.variables[this.positions[name]].type = type;
|
||||
} else {
|
||||
return this.positions[name] = this.variables.push({
|
||||
name: name,
|
||||
type: type
|
||||
}) - 1;
|
||||
return this.positions[name] = this.variables.push({name, type}) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +107,7 @@
|
||||
|
||||
assign(name, value) {
|
||||
this.add(name, {
|
||||
value: value,
|
||||
value,
|
||||
assigned: true
|
||||
}, true);
|
||||
return this.hasAssignments = true;
|
||||
|
||||
@@ -8,20 +8,15 @@
|
||||
this.columns = [];
|
||||
}
|
||||
|
||||
add(column, arg, options) {
|
||||
var sourceColumn, sourceLine;
|
||||
sourceLine = arg[0], sourceColumn = arg[1];
|
||||
if (options === void 0) {
|
||||
options = {};
|
||||
}
|
||||
add(column, [sourceLine, sourceColumn], options = {}) {
|
||||
if (this.columns[column] && options.noReplace) {
|
||||
return;
|
||||
}
|
||||
return this.columns[column] = {
|
||||
line: this.line,
|
||||
column: column,
|
||||
sourceLine: sourceLine,
|
||||
sourceColumn: sourceColumn
|
||||
column,
|
||||
sourceLine,
|
||||
sourceColumn
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,14 +40,13 @@
|
||||
|
||||
add(sourceLocation, generatedLocation, options = {}) {
|
||||
var base, column, line, lineMap;
|
||||
line = generatedLocation[0], column = generatedLocation[1];
|
||||
[line, column] = generatedLocation;
|
||||
lineMap = ((base = this.lines)[line] || (base[line] = new LineMap(line)));
|
||||
return lineMap.add(column, sourceLocation, options);
|
||||
}
|
||||
|
||||
sourceLocation(arg) {
|
||||
var column, line, lineMap;
|
||||
line = arg[0], column = arg[1];
|
||||
sourceLocation([line, column]) {
|
||||
var lineMap;
|
||||
while (!((lineMap = this.lines[line]) || (line <= 0))) {
|
||||
line--;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,6 @@
|
||||
"underscore": "~1.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"marked": "~0.3.6"
|
||||
"markdown-it": "^8.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,7 @@
|
||||
# the **Lexer**, **Rewriter**, and the **Nodes**. Merge objects, flatten
|
||||
# arrays, count characters, that sort of thing.
|
||||
|
||||
marked = require 'marked'
|
||||
# marked.setOptions
|
||||
# renderer: new marked.Renderer()
|
||||
# gfm: true
|
||||
# tables: true
|
||||
# breaks: false
|
||||
# pedantic: false
|
||||
# sanitize: true
|
||||
# smartLists: true
|
||||
# smartypants: false
|
||||
md = require('markdown-it')()
|
||||
|
||||
# Peek at the beginning of a given string to see if it matches a sequence.
|
||||
exports.starts = (string, literal, start) ->
|
||||
@@ -80,23 +71,18 @@ exports.some = Array::some ? (fn) ->
|
||||
|
||||
# Simple 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.”
|
||||
# be compiled “normally.” Uses [MarkdownIt](https://markdown-it.github.io/)
|
||||
# to tell the difference between Markdown and code blocks.
|
||||
exports.invertLiterate = (code) ->
|
||||
# Create a placeholder for tabs, that isn’t used anywhere in `code`, and then
|
||||
# re-insert the tabs after code extraction.
|
||||
generateRandomToken = ->
|
||||
"#{Math.random() * Date.now()}"
|
||||
while token is undefined or code.indexOf(token) isnt -1
|
||||
token = generateRandomToken()
|
||||
|
||||
code = code.replace "\t", token
|
||||
# Parse as markdown, discard everything except code blocks.
|
||||
out = ""
|
||||
for item in marked.lexer code, {}
|
||||
out += "#{item.text}\n" if item.type is 'code'
|
||||
# Put the tabs back in.
|
||||
out.replace token, "\t"
|
||||
out
|
||||
out = []
|
||||
md.renderer.rules =
|
||||
code_block: (tokens, idx) ->
|
||||
startLine = tokens[idx].map[0]
|
||||
lines = tokens[idx].content.split '\n'
|
||||
for line, i in lines
|
||||
out[startLine + i] = line
|
||||
md.render code
|
||||
out.join '\n'
|
||||
|
||||
# Merge two jison-style location data objects together.
|
||||
# If `last` is not provided, this will simply return `first`.
|
||||
|
||||
341
src/nodes.coffee
341
src/nodes.coffee
@@ -279,7 +279,7 @@ exports.Base = class Base
|
||||
makeCode: (code) ->
|
||||
new CodeFragment this, code
|
||||
|
||||
wrapInBraces: (fragments) ->
|
||||
wrapInParentheses: (fragments) ->
|
||||
[].concat @makeCode('('), fragments, @makeCode(')')
|
||||
|
||||
# `fragmentsList` is an array of arrays of fragments. Each array in fragmentsList will be
|
||||
@@ -432,7 +432,7 @@ exports.Block = class Block extends Base
|
||||
answer = @joinFragmentArrays(compiledNodes, ', ')
|
||||
else
|
||||
answer = [@makeCode "void 0"]
|
||||
if compiledNodes.length > 1 and o.level >= LEVEL_LIST then @wrapInBraces answer else answer
|
||||
if compiledNodes.length > 1 and o.level >= LEVEL_LIST then @wrapInParentheses answer else answer
|
||||
|
||||
# If we happen to be the top-level **Block**, wrap everything in
|
||||
# a safety closure, unless requested not to.
|
||||
@@ -532,7 +532,7 @@ exports.NaNLiteral = class NaNLiteral extends NumberLiteral
|
||||
|
||||
compileNode: (o) ->
|
||||
code = [@makeCode '0/0']
|
||||
if o.level >= LEVEL_OP then @wrapInBraces code else code
|
||||
if o.level >= LEVEL_OP then @wrapInParentheses code else code
|
||||
|
||||
exports.StringLiteral = class StringLiteral extends Literal
|
||||
|
||||
@@ -543,6 +543,9 @@ exports.PassthroughLiteral = class PassthroughLiteral extends Literal
|
||||
exports.IdentifierLiteral = class IdentifierLiteral extends Literal
|
||||
isAssignable: YES
|
||||
|
||||
eachName: (iterator) ->
|
||||
iterator @
|
||||
|
||||
exports.PropertyName = class PropertyName extends Literal
|
||||
isAssignable: YES
|
||||
|
||||
@@ -581,8 +584,7 @@ exports.BooleanLiteral = class BooleanLiteral extends Literal
|
||||
|
||||
#### Return
|
||||
|
||||
# A `return` is a *pureStatement* -- wrapping it in a closure wouldn't
|
||||
# make sense.
|
||||
# A `return` is a *pureStatement*—wrapping it in a closure wouldn’t make sense.
|
||||
exports.Return = class Return extends Base
|
||||
constructor: (@expression) ->
|
||||
super()
|
||||
@@ -627,14 +629,15 @@ exports.AwaitReturn = class AwaitReturn extends Return
|
||||
# A value, variable or literal or parenthesized, indexed or dotted into,
|
||||
# or vanilla.
|
||||
exports.Value = class Value extends Base
|
||||
constructor: (base, props, tag) ->
|
||||
constructor: (base, props, tag, isDefaultValue = no) ->
|
||||
return base if not props and base instanceof Value
|
||||
|
||||
super()
|
||||
|
||||
@base = base
|
||||
@properties = props or []
|
||||
@[tag] = true if tag
|
||||
@base = base
|
||||
@properties = props or []
|
||||
@[tag] = yes if tag
|
||||
@isDefaultValue = isDefaultValue
|
||||
return this
|
||||
|
||||
children: ['base', 'properties']
|
||||
@@ -740,6 +743,14 @@ exports.Value = class Value extends Base
|
||||
return new If new Existence(fst), snd, soak: on
|
||||
no
|
||||
|
||||
eachName: (iterator) ->
|
||||
if @hasProperties()
|
||||
iterator @
|
||||
else if @base.isAssignable()
|
||||
@base.eachName iterator
|
||||
else
|
||||
@error 'tried to assign to unassignable value'
|
||||
|
||||
#### Comment
|
||||
|
||||
# CoffeeScript passes through block comments as JavaScript block comments
|
||||
@@ -1097,13 +1108,26 @@ exports.Slice = class Slice extends Base
|
||||
|
||||
# An object literal, nothing fancy.
|
||||
exports.Obj = class Obj extends Base
|
||||
constructor: (props, @generated = false) ->
|
||||
constructor: (props, @generated = no, @lhs = no) ->
|
||||
super()
|
||||
|
||||
@objects = @properties = props or []
|
||||
|
||||
children: ['properties']
|
||||
|
||||
isAssignable: ->
|
||||
for prop in @properties
|
||||
# Check for reserved words.
|
||||
message = isUnassignable prop.unwrapAll().value
|
||||
prop.error message if message
|
||||
|
||||
prop = prop.value if prop instanceof Assign and prop.context is 'object'
|
||||
return no unless prop.isAssignable()
|
||||
yes
|
||||
|
||||
shouldCache: ->
|
||||
not @isAssignable()
|
||||
|
||||
compileNode: (o) ->
|
||||
props = @properties
|
||||
if @generated
|
||||
@@ -1111,56 +1135,95 @@ exports.Obj = class Obj extends Base
|
||||
node.error 'cannot have an implicit value in an implicit object'
|
||||
idt = o.indent += TAB
|
||||
lastNoncom = @lastNonComment @properties
|
||||
|
||||
isCompact = yes
|
||||
for prop in @properties
|
||||
if prop instanceof Comment or (prop instanceof Assign and prop.context is 'object')
|
||||
isCompact = no
|
||||
|
||||
answer = []
|
||||
answer.push @makeCode "{#{if props.length is 0 then '}' else '\n'}"
|
||||
answer.push @makeCode "{#{if isCompact then '' else '\n'}"
|
||||
for prop, i in props
|
||||
join = if i is props.length - 1
|
||||
''
|
||||
else if isCompact
|
||||
', '
|
||||
else if prop is lastNoncom or prop instanceof Comment
|
||||
'\n'
|
||||
else
|
||||
',\n'
|
||||
indent = if prop instanceof Comment then '' else idt
|
||||
if prop instanceof Assign
|
||||
if prop.context isnt 'object'
|
||||
prop.operatorToken.error "unexpected #{prop.operatorToken.value}"
|
||||
if prop.variable instanceof Value and prop.variable.hasProperties()
|
||||
prop.variable.error 'invalid object key'
|
||||
if prop instanceof Value and prop.this
|
||||
prop = new Assign prop.properties[0].name, prop, 'object'
|
||||
if prop not instanceof Comment and prop not instanceof Assign
|
||||
indent = if isCompact or prop instanceof Comment then '' else idt
|
||||
|
||||
key = if prop instanceof Assign and prop.context is 'object'
|
||||
prop.variable
|
||||
else if prop instanceof Assign
|
||||
prop.operatorToken.error "unexpected #{prop.operatorToken.value}" unless @lhs
|
||||
prop.variable
|
||||
else if prop not instanceof Comment
|
||||
prop
|
||||
|
||||
if key instanceof Value and key.hasProperties()
|
||||
key.error 'invalid object key' if prop.context is 'object' or not key.this
|
||||
key = key.properties[0].name
|
||||
prop = new Assign key, prop, 'object'
|
||||
|
||||
if key is prop
|
||||
if prop.shouldCache()
|
||||
[key, value] = prop.base.cache o
|
||||
key = new PropertyName key.value if key instanceof IdentifierLiteral
|
||||
prop = new Assign key, value, 'object'
|
||||
else
|
||||
else if not prop.bareLiteral?(IdentifierLiteral)
|
||||
prop = new Assign prop, prop, 'object'
|
||||
|
||||
if indent then answer.push @makeCode indent
|
||||
answer.push prop.compileToFragments(o, LEVEL_TOP)...
|
||||
if join then answer.push @makeCode join
|
||||
answer.push @makeCode "\n#{@tab}}" unless props.length is 0
|
||||
if @front then @wrapInBraces answer else answer
|
||||
answer.push @makeCode "#{if isCompact then '' else "\n#{@tab}"}}"
|
||||
if @front then @wrapInParentheses answer else answer
|
||||
|
||||
assigns: (name) ->
|
||||
for prop in @properties when prop.assigns name then return yes
|
||||
no
|
||||
|
||||
eachName: (iterator) ->
|
||||
for prop in @properties
|
||||
prop = prop.value if prop instanceof Assign and prop.context is 'object'
|
||||
prop = prop.unwrapAll()
|
||||
prop.eachName iterator if prop.eachName?
|
||||
|
||||
#### Arr
|
||||
|
||||
# An array literal.
|
||||
exports.Arr = class Arr extends Base
|
||||
constructor: (objs) ->
|
||||
constructor: (objs, @lhs = no) ->
|
||||
super()
|
||||
|
||||
@objects = objs or []
|
||||
|
||||
children: ['objects']
|
||||
|
||||
isAssignable: ->
|
||||
return no unless @objects.length
|
||||
|
||||
for obj, i in @objects
|
||||
return no if obj instanceof Splat and i + 1 isnt @objects.length
|
||||
return no unless obj.isAssignable() and (not obj.isAtomic or obj.isAtomic())
|
||||
yes
|
||||
|
||||
shouldCache: ->
|
||||
not @isAssignable()
|
||||
|
||||
compileNode: (o) ->
|
||||
return [@makeCode '[]'] unless @objects.length
|
||||
o.indent += TAB
|
||||
|
||||
answer = []
|
||||
# If this array is the left-hand side of an assignment, all its children
|
||||
# are too.
|
||||
if @lhs
|
||||
for obj in @objects
|
||||
unwrappedObj = obj.unwrapAll()
|
||||
unwrappedObj.lhs = yes if unwrappedObj instanceof Arr or unwrappedObj instanceof Obj
|
||||
compiledObjs = (obj.compileToFragments o, LEVEL_LIST for obj in @objects)
|
||||
for fragments, index in compiledObjs
|
||||
if index
|
||||
@@ -1178,6 +1241,11 @@ exports.Arr = class Arr extends Base
|
||||
for obj in @objects when obj.assigns name then return yes
|
||||
no
|
||||
|
||||
eachName: (iterator) ->
|
||||
for obj in @objects
|
||||
obj = obj.unwrapAll()
|
||||
obj.eachName iterator
|
||||
|
||||
#### Class
|
||||
|
||||
# The CoffeeScript class definition.
|
||||
@@ -1195,7 +1263,7 @@ exports.Class = class Class extends Base
|
||||
|
||||
# Special handling to allow `class expr.A extends A` declarations
|
||||
parentName = @parent.base.value if @parent instanceof Value and not @parent.hasProperties()
|
||||
@hasNameClash = @name? and @name == parentName
|
||||
@hasNameClash = @name? and @name is parentName
|
||||
|
||||
if executableBody or @hasNameClash
|
||||
@compileNode = @compileClassDeclaration
|
||||
@@ -1205,7 +1273,7 @@ exports.Class = class Class extends Base
|
||||
result = @compileClassDeclaration o
|
||||
|
||||
# Anonymous classes are only valid in expressions
|
||||
result = @wrapInBraces result if not @name? and o.level is LEVEL_TOP
|
||||
result = @wrapInParentheses result if not @name? and o.level is LEVEL_TOP
|
||||
|
||||
if @variable
|
||||
assign = new Assign @variable, new Literal(''), null, { @moduleDeclaration }
|
||||
@@ -1304,7 +1372,7 @@ exports.Class = class Class extends Base
|
||||
@boundMethods.push method.name
|
||||
method.bound = false
|
||||
|
||||
if initializer.length != expressions.length
|
||||
if initializer.length isnt expressions.length
|
||||
@body.expressions = (expression.hoist() for expression in initializer)
|
||||
new Block expressions
|
||||
|
||||
@@ -1405,7 +1473,7 @@ exports.ExecutableClassBody = class ExecutableClassBody extends Base
|
||||
@class.externalCtor = externalCtor
|
||||
@externalCtor.variable.base = externalCtor
|
||||
|
||||
if @name != @class.name
|
||||
if @name isnt @class.name
|
||||
@body.expressions.unshift new Assign (new IdentifierLiteral @name), @class
|
||||
else
|
||||
@body.expressions.unshift @class
|
||||
@@ -1640,6 +1708,8 @@ exports.Assign = class Assign extends Base
|
||||
|
||||
children: ['variable', 'value']
|
||||
|
||||
isAssignable: YES
|
||||
|
||||
isStatement: (o) ->
|
||||
o?.level is LEVEL_TOP and @context? and (@moduleDeclaration or "?" in @context)
|
||||
|
||||
@@ -1654,39 +1724,57 @@ exports.Assign = class Assign extends Base
|
||||
unfoldSoak: (o) ->
|
||||
unfoldSoak o, this, 'variable'
|
||||
|
||||
# Compile an assignment, delegating to `compilePatternMatch` or
|
||||
# Compile an assignment, delegating to `compileDestructuring` or
|
||||
# `compileSplice` if appropriate. Keep track of the name of the base object
|
||||
# we've been assigned to, for correct internal references. If the variable
|
||||
# has not been seen yet within the current scope, declare it.
|
||||
compileNode: (o) ->
|
||||
if isValue = @variable instanceof Value
|
||||
return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
|
||||
isValue = @variable instanceof Value
|
||||
if isValue
|
||||
# When compiling `@variable`, remember if it is part of a function parameter.
|
||||
@variable.param = @param
|
||||
|
||||
# If `@variable` is an array or an object, we’re destructuring;
|
||||
# if it’s also `isAssignable()`, the destructuring syntax is supported
|
||||
# in ES and we can output it as is; otherwise we `@compileDestructuring`
|
||||
# and convert this ES-unsupported destructuring into acceptable output.
|
||||
if @variable.isArray() or @variable.isObject()
|
||||
# This is the left-hand side of an assignment; let `Arr` and `Obj`
|
||||
# know that, so that those nodes know that they’re assignable as
|
||||
# destructured variables.
|
||||
@variable.base.lhs = yes
|
||||
return @compileDestructuring o unless @variable.isAssignable()
|
||||
|
||||
return @compileSplice o if @variable.isSplice()
|
||||
return @compileConditional o if @context in ['||=', '&&=', '?=']
|
||||
return @compileSpecialMath o if @context in ['**=', '//=', '%%=']
|
||||
|
||||
unless @context
|
||||
varBase = @variable.unwrapAll()
|
||||
unless varBase.isAssignable()
|
||||
@variable.error "'#{@variable.compile o}' can't be assigned"
|
||||
|
||||
varBase.eachName (name) =>
|
||||
return if name.hasProperties?()
|
||||
|
||||
message = isUnassignable name.value
|
||||
name.error message if message
|
||||
|
||||
# `moduleDeclaration` can be `'import'` or `'export'`
|
||||
@checkAssignability o, name
|
||||
if @moduleDeclaration
|
||||
o.scope.add name.value, @moduleDeclaration
|
||||
else
|
||||
o.scope.find name.value
|
||||
|
||||
if @value instanceof Code
|
||||
if @value.isStatic
|
||||
@value.name = @variable.properties[0]
|
||||
else if @variable.properties?.length >= 2
|
||||
[properties..., prototype, name] = @variable.properties
|
||||
@value.name = name if prototype.name?.value is 'prototype'
|
||||
unless @context
|
||||
varBase = @variable.unwrapAll()
|
||||
unless varBase.isAssignable()
|
||||
@variable.error "'#{@variable.compile o}' can't be assigned"
|
||||
unless varBase.hasProperties?()
|
||||
# `moduleDeclaration` can be `'import'` or `'export'`
|
||||
if @moduleDeclaration
|
||||
@checkAssignability o, varBase
|
||||
o.scope.add varBase.value, @moduleDeclaration
|
||||
else if @param
|
||||
o.scope.add varBase.value, 'var'
|
||||
else
|
||||
@checkAssignability o, varBase
|
||||
o.scope.find varBase.value
|
||||
|
||||
val = @value.compileToFragments o, LEVEL_LIST
|
||||
@variable.front = true if isValue and @variable.base instanceof Obj
|
||||
compiledName = @variable.compileToFragments o, LEVEL_LIST
|
||||
|
||||
if @context is 'object'
|
||||
@@ -1699,25 +1787,40 @@ exports.Assign = class Assign extends Base
|
||||
return compiledName.concat @makeCode(": "), val
|
||||
|
||||
answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val
|
||||
if o.level <= LEVEL_LIST then answer else @wrapInBraces answer
|
||||
# Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration,
|
||||
# if we’re destructuring without declaring, the destructuring assignment must be wrapped in parentheses.
|
||||
if o.level > LEVEL_LIST or (isValue and @variable.base instanceof Obj and not @param)
|
||||
@wrapInParentheses answer
|
||||
else
|
||||
answer
|
||||
|
||||
# Brief implementation of recursive pattern matching, when assigning array or
|
||||
# object literals to a value. Peeks at their properties to assign inner names.
|
||||
compilePatternMatch: (o) ->
|
||||
compileDestructuring: (o) ->
|
||||
top = o.level is LEVEL_TOP
|
||||
{value} = this
|
||||
{objects} = @variable.base
|
||||
unless olen = objects.length
|
||||
olen = objects.length
|
||||
|
||||
# Special-case for `{} = a` and `[] = a` (empty patterns).
|
||||
# Compile to simply `a`.
|
||||
if olen is 0
|
||||
code = value.compileToFragments o
|
||||
return if o.level >= LEVEL_OP then @wrapInBraces code else code
|
||||
return if o.level >= LEVEL_OP then @wrapInParentheses code else code
|
||||
[obj] = objects
|
||||
|
||||
# Disallow `[...] = a` for some reason. (Could be equivalent to `[] = a`?)
|
||||
if olen is 1 and obj instanceof Expansion
|
||||
obj.error 'Destructuring assignment has no target'
|
||||
|
||||
isObject = @variable.isObject()
|
||||
|
||||
# Special case for when there's only one thing destructured off of
|
||||
# something. `{a} = b`, `[a] = b`, `{a: b} = c`
|
||||
if top and olen is 1 and obj not instanceof Splat
|
||||
# Pick the property straight off the value when there’s just one to pick
|
||||
# (no need to cache the value into a variable).
|
||||
defaultValue = null
|
||||
defaultValue = undefined
|
||||
if obj instanceof Assign and obj.context is 'object'
|
||||
# A regular object pattern-match.
|
||||
{variable: {base: idx}, value: obj} = obj
|
||||
@@ -1742,24 +1845,43 @@ exports.Assign = class Assign extends Base
|
||||
value.properties.push new (if acc then Access else Index) idx
|
||||
message = isUnassignable obj.unwrap().value
|
||||
obj.error message if message
|
||||
value = new Op '?', value, defaultValue if defaultValue
|
||||
if defaultValue
|
||||
defaultValue.isDefaultValue = yes
|
||||
value = new Op '?', value, defaultValue
|
||||
return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP
|
||||
|
||||
vvar = value.compileToFragments o, LEVEL_LIST
|
||||
vvarText = fragmentsToText vvar
|
||||
assigns = []
|
||||
expandedIdx = false
|
||||
# Make vvar into a simple variable if it isn't already.
|
||||
|
||||
# At this point, there are several things to destructure. So the `fn()` in
|
||||
# `{a, b} = fn()` must be cached, for example. Make vvar into a simple
|
||||
# variable if it isn't already.
|
||||
if value.unwrap() not instanceof IdentifierLiteral or @variable.assigns(vvarText)
|
||||
assigns.push [@makeCode("#{ ref = o.scope.freeVariable 'ref' } = "), vvar...]
|
||||
ref = o.scope.freeVariable 'ref'
|
||||
assigns.push [@makeCode(ref + ' = '), vvar...]
|
||||
vvar = [@makeCode ref]
|
||||
vvarText = ref
|
||||
|
||||
# And here comes the big loop that handles all of these cases:
|
||||
# `[a, b] = c`
|
||||
# `[a..., b] = c`
|
||||
# `[..., a, b] = c`
|
||||
# `[@a, b] = c`
|
||||
# `[a = 1, b] = c`
|
||||
# `{a, b} = c`
|
||||
# `{@a, b} = c`
|
||||
# `{a = 1, b} = c`
|
||||
# etc.
|
||||
for obj, i in objects
|
||||
idx = i
|
||||
if not expandedIdx and obj instanceof Splat
|
||||
name = obj.name.unwrap().value
|
||||
obj = obj.unwrap()
|
||||
val = "#{olen} <= #{vvarText}.length ? #{ utility 'slice', o }.call(#{vvarText}, #{i}"
|
||||
if rest = olen - i - 1
|
||||
rest = olen - i - 1
|
||||
if rest isnt 0
|
||||
ivar = o.scope.freeVariable 'i', single: true
|
||||
val += ", #{ivar} = #{vvarText}.length - #{rest}) : (#{ivar} = #{i}, [])"
|
||||
else
|
||||
@@ -1767,7 +1889,8 @@ exports.Assign = class Assign extends Base
|
||||
val = new Literal val
|
||||
expandedIdx = "#{ivar}++"
|
||||
else if not expandedIdx and obj instanceof Expansion
|
||||
if rest = olen - i - 1
|
||||
rest = olen - i - 1
|
||||
if rest isnt 0
|
||||
if rest is 1
|
||||
expandedIdx = "#{vvarText}.length - 1"
|
||||
else
|
||||
@@ -1779,7 +1902,7 @@ exports.Assign = class Assign extends Base
|
||||
else
|
||||
if obj instanceof Splat or obj instanceof Expansion
|
||||
obj.error "multiple splats/expansions are disallowed in an assignment"
|
||||
defaultValue = null
|
||||
defaultValue = undefined
|
||||
if obj instanceof Assign and obj.context is 'object'
|
||||
# A regular object pattern-match.
|
||||
{variable: {base: idx}, value: obj} = obj
|
||||
@@ -1802,14 +1925,17 @@ exports.Assign = class Assign extends Base
|
||||
name = obj.unwrap().value
|
||||
acc = idx.unwrap() instanceof PropertyName
|
||||
val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx]
|
||||
val = new Op '?', val, defaultValue if defaultValue
|
||||
if defaultValue
|
||||
defaultValue.isDefaultValue = yes
|
||||
val = new Op '?', val, defaultValue
|
||||
if name?
|
||||
message = isUnassignable name
|
||||
obj.error message if message
|
||||
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
|
||||
|
||||
assigns.push vvar unless top or @subpattern
|
||||
fragments = @joinFragmentArrays assigns, ', '
|
||||
if o.level < LEVEL_LIST then fragments else @wrapInBraces fragments
|
||||
if o.level < LEVEL_LIST then fragments else @wrapInParentheses fragments
|
||||
|
||||
# When compiling a conditional assignment, take care to ensure that the
|
||||
# operands are only evaluated once, even though we have to reference them
|
||||
@@ -1825,7 +1951,7 @@ exports.Assign = class Assign extends Base
|
||||
new If(new Existence(left), right, type: 'if').addElse(new Assign(right, @value, '=')).compileToFragments o
|
||||
else
|
||||
fragments = new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o
|
||||
if o.level <= LEVEL_LIST then fragments else @wrapInBraces fragments
|
||||
if o.level <= LEVEL_LIST then fragments else @wrapInParentheses fragments
|
||||
|
||||
# Convert special math assignment operators like `a **= b` to the equivalent
|
||||
# extended form `a = a ** b` and then compiles that.
|
||||
@@ -1853,7 +1979,10 @@ exports.Assign = class Assign extends Base
|
||||
to = "9e9"
|
||||
[valDef, valRef] = @value.cache o, LEVEL_LIST
|
||||
answer = [].concat @makeCode("[].splice.apply(#{name}, [#{fromDecl}, #{to}].concat("), valDef, @makeCode(")), "), valRef
|
||||
if o.level > LEVEL_TOP then @wrapInBraces answer else answer
|
||||
if o.level > LEVEL_TOP then @wrapInParentheses answer else answer
|
||||
|
||||
eachName: (iterator) ->
|
||||
@variable.unwrapAll().eachName iterator
|
||||
|
||||
#### Code
|
||||
|
||||
@@ -1947,13 +2076,18 @@ exports.Code = class Code extends Base
|
||||
|
||||
haveSplatParam = yes
|
||||
if param.splat
|
||||
params.push ref = param.asReference o
|
||||
splatParamName = fragmentsToText ref.compileNode o
|
||||
if param.name instanceof Arr
|
||||
# Splat arrays are treated oddly by ES; deal with them the legacy
|
||||
# way in the function body. TODO: Should this be handled in the
|
||||
# function parameter list, and if so, how?
|
||||
splatParamName = o.scope.freeVariable 'arg'
|
||||
params.push ref = new Value new IdentifierLiteral splatParamName
|
||||
exprs.push new Assign new Value(param.name), ref, null, param: yes
|
||||
else
|
||||
params.push ref = param.asReference o
|
||||
splatParamName = fragmentsToText ref.compileNode o
|
||||
if param.shouldCache()
|
||||
exprs.push new Assign new Value(param.name), ref, '=', param: yes
|
||||
# TODO: output destructured parameters as is, and fix destructuring
|
||||
# of objects with default values to work in this context (see
|
||||
# Obj.compileNode `if prop.context isnt 'object'`).
|
||||
exprs.push new Assign new Value(param.name), ref, null, param: yes
|
||||
else # `param` is an Expansion
|
||||
splatParamName = o.scope.freeVariable 'args'
|
||||
params.push new Value new IdentifierLiteral splatParamName
|
||||
@@ -1972,11 +2106,11 @@ exports.Code = class Code extends Base
|
||||
# to the function body assigning it, e.g.
|
||||
# `(arg) => { var a = arg.a; }`, with a default value if it has one.
|
||||
if param.value?
|
||||
condition = new Op '==', param, new UndefinedLiteral
|
||||
ifTrue = new Assign new Value(param.name), param.value, '=', param: yes
|
||||
condition = new Op '===', param, new UndefinedLiteral
|
||||
ifTrue = new Assign new Value(param.name), param.value, null, param: yes
|
||||
exprs.push new If condition, ifTrue
|
||||
else
|
||||
exprs.push new Assign new Value(param.name), param.asReference(o), '=', param: yes
|
||||
exprs.push new Assign new Value(param.name), param.asReference(o), null, param: yes
|
||||
|
||||
# If this parameter comes before the splat or expansion, it will go
|
||||
# in the function definition parameter list.
|
||||
@@ -1989,11 +2123,17 @@ exports.Code = class Code extends Base
|
||||
ref = param.asReference o
|
||||
else
|
||||
if param.value? and not param.assignedInBody
|
||||
ref = new Assign new Value(param.name), param.value, '='
|
||||
ref = new Assign new Value(param.name), param.value, null, param: yes
|
||||
else
|
||||
ref = param
|
||||
# Add this parameter’s reference to the function scope
|
||||
o.scope.parameter fragmentsToText (if param.value? then param else ref).compileToFragments o
|
||||
# Add this parameter’s reference(s) to the function scope.
|
||||
if param.name instanceof Arr or param.name instanceof Obj
|
||||
# This parameter is destructured.
|
||||
param.name.lhs = yes
|
||||
param.name.eachName (prop) ->
|
||||
o.scope.parameter prop.value
|
||||
else
|
||||
o.scope.parameter fragmentsToText (if param.value? then param else ref).compileToFragments o
|
||||
params.push ref
|
||||
else
|
||||
paramsAfterSplat.push param
|
||||
@@ -2001,8 +2141,8 @@ exports.Code = class Code extends Base
|
||||
# function parameter list we need to assign its default value
|
||||
# (if necessary) as an expression in the body.
|
||||
if param.value? and not param.shouldCache()
|
||||
condition = new Op '==', param, new UndefinedLiteral
|
||||
ifTrue = new Assign new Value(param.name), param.value, '='
|
||||
condition = new Op '===', param, new UndefinedLiteral
|
||||
ifTrue = new Assign new Value(param.name), param.value
|
||||
exprs.push new If condition, ifTrue
|
||||
# Add this parameter to the scope, since it wouldn’t have been added yet since it was skipped earlier.
|
||||
o.scope.add param.name.value, 'var', yes if param.name?.value?
|
||||
@@ -2056,7 +2196,7 @@ exports.Code = class Code extends Base
|
||||
answer.push @makeCode '}'
|
||||
|
||||
return [@makeCode(@tab), answer...] if @isMethod
|
||||
if @front or (o.level >= LEVEL_ACCESS) then @wrapInBraces answer else answer
|
||||
if @front or (o.level >= LEVEL_ACCESS) then @wrapInParentheses answer else answer
|
||||
|
||||
eachParamName: (iterator) ->
|
||||
param.eachName iterator for param in @params
|
||||
@@ -2084,7 +2224,7 @@ exports.Code = class Code extends Base
|
||||
superCall.error "'super' is only allowed in derived class constructors" if @ctor is 'base'
|
||||
superCall.expressions = thisAssignments
|
||||
|
||||
haveThisParam = thisAssignments.length and thisAssignments.length != @thisAssignments?.length
|
||||
haveThisParam = thisAssignments.length and thisAssignments.length isnt @thisAssignments?.length
|
||||
if @ctor is 'derived' and not seenSuper and haveThisParam
|
||||
param = thisAssignments[0].variable
|
||||
param.error "Can't use @params in derived class constructors without calling super"
|
||||
@@ -2205,7 +2345,8 @@ exports.Splat = class Splat extends Base
|
||||
|
||||
children: ['name']
|
||||
|
||||
isAssignable: YES
|
||||
isAssignable: ->
|
||||
@name.isAssignable() and (not @name.isAtomic or @name.isAtomic())
|
||||
|
||||
constructor: (name) ->
|
||||
super()
|
||||
@@ -2299,7 +2440,7 @@ exports.While = class While extends Base
|
||||
# Simple Arithmetic and logical operations. Performs some conversion from
|
||||
# CoffeeScript operations into their JavaScript equivalents.
|
||||
exports.Op = class Op extends Base
|
||||
constructor: (op, first, second, flip ) ->
|
||||
constructor: (op, first, second, flip) ->
|
||||
return new In first, second if op is 'in'
|
||||
if op is 'do'
|
||||
return Op::generateDo first
|
||||
@@ -2410,7 +2551,7 @@ exports.Op = class Op extends Base
|
||||
return @compileUnary o if @isUnary()
|
||||
return @compileChain o if isChain
|
||||
switch @operator
|
||||
when '?' then @compileExistence o
|
||||
when '?' then @compileExistence o, @second.isDefaultValue
|
||||
when '**' then @compilePower o
|
||||
when '//' then @compileFloorDivision o
|
||||
when '%%' then @compileModulo o
|
||||
@@ -2418,7 +2559,7 @@ exports.Op = class Op extends Base
|
||||
lhs = @first.compileToFragments o, LEVEL_OP
|
||||
rhs = @second.compileToFragments o, LEVEL_OP
|
||||
answer = [].concat lhs, @makeCode(" #{@operator} "), rhs
|
||||
if o.level <= LEVEL_OP then answer else @wrapInBraces answer
|
||||
if o.level <= LEVEL_OP then answer else @wrapInParentheses answer
|
||||
|
||||
# Mimic Python's chained comparisons when multiple comparison operators are
|
||||
# used sequentially. For example:
|
||||
@@ -2430,17 +2571,17 @@ exports.Op = class Op extends Base
|
||||
fst = @first.compileToFragments o, LEVEL_OP
|
||||
fragments = fst.concat @makeCode(" #{if @invert then '&&' else '||'} "),
|
||||
(shared.compileToFragments o), @makeCode(" #{@operator} "), (@second.compileToFragments o, LEVEL_OP)
|
||||
@wrapInBraces fragments
|
||||
@wrapInParentheses fragments
|
||||
|
||||
# Keep reference to the left expression, unless this an existential assignment
|
||||
compileExistence: (o) ->
|
||||
compileExistence: (o, checkOnlyUndefined) ->
|
||||
if @first.shouldCache()
|
||||
ref = new IdentifierLiteral o.scope.freeVariable 'ref'
|
||||
fst = new Parens new Assign ref, @first
|
||||
else
|
||||
fst = @first
|
||||
ref = fst
|
||||
new If(new Existence(fst), ref, type: 'if').addElse(@second).compileToFragments o
|
||||
new If(new Existence(fst, checkOnlyUndefined), ref, type: 'if').addElse(@second).compileToFragments o
|
||||
|
||||
# Compile a unary **Op**.
|
||||
compileUnary: (o) ->
|
||||
@@ -2521,7 +2662,7 @@ exports.In = class In extends Base
|
||||
for item, i in @array.base.objects
|
||||
if i then tests.push @makeCode cnj
|
||||
tests = tests.concat (if i then ref else sub), @makeCode(cmp), item.compileToFragments(o, LEVEL_ACCESS)
|
||||
if o.level < LEVEL_OP then tests else @wrapInBraces tests
|
||||
if o.level < LEVEL_OP then tests else @wrapInParentheses tests
|
||||
|
||||
compileLoopTest: (o) ->
|
||||
[sub, ref] = @object.cache o, LEVEL_LIST
|
||||
@@ -2529,7 +2670,7 @@ exports.In = class In extends Base
|
||||
@makeCode(", "), ref, @makeCode(") " + if @negated then '< 0' else '>= 0')
|
||||
return fragments if fragmentsToText(sub) is fragmentsToText(ref)
|
||||
fragments = sub.concat @makeCode(', '), fragments
|
||||
if o.level < LEVEL_LIST then fragments else @wrapInBraces fragments
|
||||
if o.level < LEVEL_LIST then fragments else @wrapInParentheses fragments
|
||||
|
||||
toString: (idt) ->
|
||||
super idt, @constructor.name + if @negated then '!' else ''
|
||||
@@ -2600,12 +2741,13 @@ exports.Throw = class Throw extends Base
|
||||
|
||||
#### Existence
|
||||
|
||||
# Checks a variable for existence -- not *null* and not *undefined*. This is
|
||||
# Checks a variable for existence -- not `null` and not `undefined`. This is
|
||||
# similar to `.nil?` in Ruby, and avoids having to consult a JavaScript truth
|
||||
# table.
|
||||
# table. Optionally only check if a variable is not `undefined`.
|
||||
exports.Existence = class Existence extends Base
|
||||
constructor: (@expression) ->
|
||||
constructor: (@expression, onlyNotUndefined = no) ->
|
||||
super()
|
||||
@comparisonTarget = if onlyNotUndefined then 'undefined' else 'null'
|
||||
|
||||
children: ['expression']
|
||||
|
||||
@@ -2616,10 +2758,19 @@ exports.Existence = class Existence extends Base
|
||||
code = @expression.compile o, LEVEL_OP
|
||||
if @expression.unwrap() instanceof IdentifierLiteral and not o.scope.check code
|
||||
[cmp, cnj] = if @negated then ['===', '||'] else ['!==', '&&']
|
||||
code = "typeof #{code} #{cmp} \"undefined\" #{cnj} #{code} #{cmp} null"
|
||||
code = "typeof #{code} #{cmp} \"undefined\"" + if @comparisonTarget isnt 'undefined' then " #{cnj} #{code} #{cmp} #{@comparisonTarget}" else ''
|
||||
else
|
||||
# do not use strict equality here; it will break existing code
|
||||
code = "#{code} #{if @negated then '==' else '!='} null"
|
||||
# We explicity want to use loose equality (`==`) when comparing against `null`,
|
||||
# so that an existence check roughly corresponds to a check for truthiness.
|
||||
# Do *not* change this to `===` for `null`, as this will break mountains of
|
||||
# existing code. When comparing only against `undefined`, however, we want to
|
||||
# use `===` because this use case is for parity with ES2015+ default values,
|
||||
# which only get assigned when the variable is `undefined` (but not `null`).
|
||||
cmp = if @comparisonTarget is 'null'
|
||||
if @negated then '==' else '!='
|
||||
else # `undefined`
|
||||
if @negated then '===' else '!=='
|
||||
code = "#{code} #{cmp} #{@comparisonTarget}"
|
||||
[@makeCode(if o.level <= LEVEL_COND then code else "(#{code})")]
|
||||
|
||||
#### Parens
|
||||
@@ -2647,7 +2798,7 @@ exports.Parens = class Parens extends Base
|
||||
fragments = expr.compileToFragments o, LEVEL_PAREN
|
||||
bare = o.level < LEVEL_OP and (expr instanceof Op or expr instanceof Call or
|
||||
(expr instanceof For and expr.returns))
|
||||
if bare then fragments else @wrapInBraces fragments
|
||||
if bare then fragments else @wrapInParentheses fragments
|
||||
|
||||
#### StringWithInterpolations
|
||||
|
||||
@@ -2719,7 +2870,7 @@ exports.For = class For extends While
|
||||
@index.error 'cannot use index with for-from' if @from and @index
|
||||
source.ownTag.error "cannot use own with for-#{if @from then 'from' else 'in'}" if @own and not @object
|
||||
[@name, @index] = [@index, @name] if @object
|
||||
@index.error 'index cannot be a pattern matching expression' if @index instanceof Value and not @index.isAssignable()
|
||||
@index.error 'index cannot be a pattern matching expression' if @index?.isArray?() or @index?.isObject?()
|
||||
@range = @source instanceof Value and @source.base instanceof Range and not @source.properties.length and not @from
|
||||
@pattern = @name instanceof Value
|
||||
@index.error 'indexes do not apply to range loops' if @range and @index
|
||||
@@ -2954,7 +3105,7 @@ exports.If = class If extends Base
|
||||
body = @bodyNode().compileToFragments o, LEVEL_LIST
|
||||
alt = if @elseBodyNode() then @elseBodyNode().compileToFragments(o, LEVEL_LIST) else [@makeCode('void 0')]
|
||||
fragments = cond.concat @makeCode(" ? "), body, @makeCode(" : "), alt
|
||||
if o.level >= LEVEL_COND then @wrapInBraces fragments else fragments
|
||||
if o.level >= LEVEL_COND then @wrapInParentheses fragments else fragments
|
||||
|
||||
unfoldSoak: ->
|
||||
@soak and this
|
||||
|
||||
@@ -142,7 +142,8 @@ test "#1192: assignment starting with object literals", ->
|
||||
# Destructuring Assignment
|
||||
|
||||
test "empty destructuring assignment", ->
|
||||
{} = [] = undefined
|
||||
{} = {}
|
||||
[] = []
|
||||
|
||||
test "chained destructuring assignments", ->
|
||||
[a] = {0: b} = {'0': c} = [nonce={}]
|
||||
@@ -305,7 +306,7 @@ test "simple array destructuring defaults", ->
|
||||
[a = 2] = [undefined]
|
||||
eq 2, a
|
||||
[a = 3] = [null]
|
||||
eq 3, a
|
||||
eq null, a # Breaking change in CS2: per ES2015, default values are applied for `undefined` but not for `null`.
|
||||
[a = 4] = [0]
|
||||
eq 0, a
|
||||
arr = [a = 5]
|
||||
@@ -318,7 +319,7 @@ test "simple object destructuring defaults", ->
|
||||
{b = 2} = {b: undefined}
|
||||
eq b, 2
|
||||
{b = 3} = {b: null}
|
||||
eq b, 3
|
||||
eq b, null # Breaking change in CS2: per ES2015, default values are applied for `undefined` but not for `null`.
|
||||
{b = 4} = {b: 0}
|
||||
eq b, 0
|
||||
|
||||
@@ -327,17 +328,17 @@ test "simple object destructuring defaults", ->
|
||||
{b: c = 2} = {b: undefined}
|
||||
eq c, 2
|
||||
{b: c = 3} = {b: null}
|
||||
eq c, 3
|
||||
eq c, null # Breaking change in CS2: per ES2015, default values are applied for `undefined` but not for `null`.
|
||||
{b: c = 4} = {b: 0}
|
||||
eq c, 0
|
||||
|
||||
test "multiple array destructuring defaults", ->
|
||||
[a = 1, b = 2, c] = [null, 12, 13]
|
||||
[a = 1, b = 2, c] = [undefined, 12, 13]
|
||||
eq a, 1
|
||||
eq b, 12
|
||||
eq c, 13
|
||||
[a, b = 2, c = 3] = [null, 12, 13]
|
||||
eq a, null
|
||||
[a, b = 2, c = 3] = [undefined, 12, 13]
|
||||
eq a, undefined
|
||||
eq b, 12
|
||||
eq c, 13
|
||||
[a = 1, b, c = 3] = [11, 12]
|
||||
@@ -368,7 +369,7 @@ test "destructuring assignment with context (@) properties and defaults", ->
|
||||
a={}; b={}; c={}; d={}; e={}
|
||||
obj =
|
||||
fn: () ->
|
||||
local = [a, {b, c: null}, d]
|
||||
local = [a, {b, c: undefined}, d]
|
||||
[@a, {b: @b = b, @c = c}, @d, @e = e] = local
|
||||
eq undefined, obj[key] for key in ['a','b','c','d','e']
|
||||
obj.fn()
|
||||
@@ -387,7 +388,7 @@ test "destructuring assignment with defaults single evaluation", ->
|
||||
[a = fn()] = [10]
|
||||
eq 10, a
|
||||
eq 1, callCount
|
||||
{a = fn(), b: c = fn()} = {a: 20, b: null}
|
||||
{a = fn(), b: c = fn()} = {a: 20, b: undefined}
|
||||
eq 20, a
|
||||
eq c, 1
|
||||
eq callCount, 2
|
||||
|
||||
@@ -179,7 +179,7 @@ test "destructuring in function definition", ->
|
||||
{url, async, beforeSend, cache, method, data}
|
||||
|
||||
fn = ->
|
||||
deepEqual ajax('/home', beforeSend: fn, cache: null, method: 'post'), {
|
||||
deepEqual ajax('/home', beforeSend: fn, method: 'post'), {
|
||||
url: '/home', async: true, beforeSend: fn, cache: true, method: 'post', data: {}
|
||||
}
|
||||
|
||||
@@ -352,3 +352,13 @@ test "#4406 Destructured parameter default evaluation order with generator funct
|
||||
next = -> ++current
|
||||
foo = ({ a = next() }, b = next()) -> [ a, b ]
|
||||
arrayEq foo({}), [1, 2]
|
||||
|
||||
test "Destructured parameter with default value, that itself has a default value", ->
|
||||
# Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
|
||||
draw = ({size = 'big', coords = {x: 0, y: 0}, radius = 25} = {}) -> "#{size}-#{coords.x}-#{coords.y}-#{radius}"
|
||||
output = draw
|
||||
coords:
|
||||
x: 18
|
||||
y: 30
|
||||
radius: 30
|
||||
eq output, 'big-18-30-30'
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
Literate CoffeeScript Test
|
||||
--------------------------
|
||||
# Literate CoffeeScript Test
|
||||
|
||||
comment comment
|
||||
|
||||
test "basic literate CoffeeScript parsing", ->
|
||||
ok yes
|
||||
|
||||
|
||||
now with a...
|
||||
|
||||
|
||||
test "broken up indentation", ->
|
||||
|
||||
|
||||
... broken up ...
|
||||
|
||||
do ->
|
||||
|
||||
|
||||
... nested block.
|
||||
|
||||
ok yes
|
||||
@@ -25,36 +24,36 @@ Code must be separated from text by a blank line.
|
||||
The next line is part of the text and will not be executed.
|
||||
fail()
|
||||
|
||||
ok yes
|
||||
|
||||
ok yes
|
||||
|
||||
Code in `backticks is not parsed` and...
|
||||
|
||||
test "comments in indented blocks work", ->
|
||||
do ->
|
||||
do ->
|
||||
# Regular comment.
|
||||
|
||||
|
||||
###
|
||||
Block comment.
|
||||
###
|
||||
|
||||
|
||||
ok yes
|
||||
|
||||
Regular [Markdown](http://example.com/markdown) features, like links
|
||||
|
||||
Regular [Markdown](http://example.com/markdown) features, like links
|
||||
and unordered lists, are fine:
|
||||
|
||||
* I
|
||||
|
||||
* I
|
||||
|
||||
* Am
|
||||
|
||||
|
||||
* A
|
||||
|
||||
|
||||
* List
|
||||
|
||||
Tabs work too:
|
||||
|
||||
test "tabbed code", ->
|
||||
ok yes
|
||||
test "tabbed code", ->
|
||||
ok yes
|
||||
|
||||
---
|
||||
|
||||
@@ -63,11 +62,12 @@ Tabs work too:
|
||||
|
||||
<p>
|
||||
|
||||
executed = true # should not execute, this is just HTML para, not code!
|
||||
if true
|
||||
executed = true # should not execute, this is just HTML para, not code!
|
||||
|
||||
</p>
|
||||
|
||||
test "should ignore indented sections inside HTML", ->
|
||||
test "should ignore code blocks inside HTML", ->
|
||||
eq executed, false
|
||||
|
||||
---
|
||||
@@ -119,24 +119,8 @@ Tabs work too:
|
||||
|
||||
---
|
||||
|
||||
This next one probably passes because a string is inoffensive in compiled js, also, can't get `marked` to parse it correctly, and not sure if empty line is permitted between title and reference
|
||||
|
||||
This is [an example][id] reference-style link.
|
||||
[id]: http://example.com/
|
||||
|
||||
"Optional Title Here"
|
||||
|
||||
---
|
||||
|
||||
executed = no
|
||||
|
||||
1986. What a great season.
|
||||
executed = yes
|
||||
|
||||
and test...
|
||||
|
||||
test "should recognise indented code blocks in lists", ->
|
||||
ok executed
|
||||
[id]: http://example.com/ "Optional Title Here"
|
||||
|
||||
---
|
||||
|
||||
@@ -148,7 +132,7 @@ and test...
|
||||
|
||||
and test...
|
||||
|
||||
test "should recognise indented code blocks in lists with empty line as separator", ->
|
||||
test "should recognize indented code blocks in lists with empty line as separator", ->
|
||||
ok executed
|
||||
|
||||
---
|
||||
@@ -163,3 +147,11 @@ and test...
|
||||
test "should ignore indented code in escaped list like number", ->
|
||||
eq executed, no
|
||||
|
||||
one last test!
|
||||
|
||||
test "block quotes should render correctly", ->
|
||||
quote = '''
|
||||
foo
|
||||
and bar!
|
||||
'''
|
||||
eq quote, 'foo\n and bar!'
|
||||
|
||||
157
test/literate_tabbed.litcoffee
Normal file
157
test/literate_tabbed.litcoffee
Normal file
@@ -0,0 +1,157 @@
|
||||
# Tabbed Literate CoffeeScript Test
|
||||
|
||||
comment comment
|
||||
|
||||
test "basic literate CoffeeScript parsing", ->
|
||||
ok yes
|
||||
|
||||
now with a...
|
||||
|
||||
test "broken up indentation", ->
|
||||
|
||||
... broken up ...
|
||||
|
||||
do ->
|
||||
|
||||
... nested block.
|
||||
|
||||
ok yes
|
||||
|
||||
Code must be separated from text by a blank line.
|
||||
|
||||
test "code blocks must be preceded by a blank line", ->
|
||||
|
||||
The next line is part of the text and will not be executed.
|
||||
fail()
|
||||
|
||||
ok yes
|
||||
|
||||
Code in `backticks is not parsed` and...
|
||||
|
||||
test "comments in indented blocks work", ->
|
||||
do ->
|
||||
do ->
|
||||
# Regular comment.
|
||||
|
||||
###
|
||||
Block comment.
|
||||
###
|
||||
|
||||
ok yes
|
||||
|
||||
Regular [Markdown](http://example.com/markdown) features, like links
|
||||
and unordered lists, are fine:
|
||||
|
||||
* I
|
||||
|
||||
* Am
|
||||
|
||||
* A
|
||||
|
||||
* List
|
||||
|
||||
Spaces work too:
|
||||
|
||||
test "spaced code", ->
|
||||
ok yes
|
||||
|
||||
---
|
||||
|
||||
# keep track of whether code blocks are executed or not
|
||||
executed = false
|
||||
|
||||
<p>
|
||||
|
||||
if true
|
||||
executed = true # should not execute, this is just HTML para, not code!
|
||||
|
||||
</p>
|
||||
|
||||
test "should ignore code blocks inside HTML", ->
|
||||
eq executed, false
|
||||
|
||||
---
|
||||
|
||||
* A list item with a code block:
|
||||
|
||||
test "basic literate CoffeeScript parsing", ->
|
||||
ok yes
|
||||
|
||||
---
|
||||
|
||||
* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
|
||||
Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
|
||||
viverra nec, fringilla in, laoreet vitae, risus.
|
||||
|
||||
* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
|
||||
Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||
|
||||
---
|
||||
|
||||
1. This is a list item with two paragraphs. Lorem ipsum dolor
|
||||
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
|
||||
mi posuere lectus.
|
||||
|
||||
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
|
||||
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
|
||||
sit amet velit.
|
||||
|
||||
2. Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||
|
||||
---
|
||||
|
||||
1. This is a list item with two paragraphs. Lorem ipsum dolor
|
||||
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
|
||||
mi posuere lectus.
|
||||
|
||||
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
|
||||
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
|
||||
sit amet velit.
|
||||
|
||||
2. Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||
|
||||
---
|
||||
|
||||
* A list item with a blockquote:
|
||||
|
||||
> This is a blockquote
|
||||
> inside a list item.
|
||||
|
||||
---
|
||||
|
||||
This is [an example][id] reference-style link.
|
||||
[id]: http://example.com/ "Optional Title Here"
|
||||
|
||||
---
|
||||
|
||||
executed = no
|
||||
|
||||
1986. What a great season.
|
||||
|
||||
executed = yes
|
||||
|
||||
and test...
|
||||
|
||||
test "should recognize indented code blocks in lists with empty line as separator", ->
|
||||
ok executed
|
||||
|
||||
---
|
||||
|
||||
executed = no
|
||||
|
||||
1986\. What a great season.
|
||||
executed = yes
|
||||
|
||||
and test...
|
||||
|
||||
test "should ignore indented code in escaped list like number", ->
|
||||
eq executed, no
|
||||
|
||||
one last test!
|
||||
|
||||
test "block quotes should render correctly", ->
|
||||
quote = '''
|
||||
foo
|
||||
and bar!
|
||||
'''
|
||||
eq quote, 'foo\n\t\tand bar!'
|
||||
@@ -1,7 +1,7 @@
|
||||
# Strict Early Errors
|
||||
# -------------------
|
||||
|
||||
# The following are prohibited under ES5's `strict` mode
|
||||
# The following are prohibited under ES5’s `strict` mode
|
||||
# * `Octal Integer Literals`
|
||||
# * `Octal Escape Sequences`
|
||||
# * duplicate property definitions in `Object Literal`s
|
||||
|
||||
Reference in New Issue
Block a user