Merging current master

This commit is contained in:
Trevor Burnham
2011-10-23 22:23:29 -04:00
75 changed files with 10792 additions and 6918 deletions

View File

@@ -5,10 +5,16 @@ CoffeeScript = require './lib/coffee-script'
{spawn, exec} = require 'child_process'
# ANSI Terminal Colors.
bold = '\033[0;1m'
red = '\033[0;31m'
green = '\033[0;32m'
reset = '\033[0m'
enableColors = no
unless process.platform is 'win32'
enableColors = not process.env.NODE_DISABLE_COLORS
bold = red = green = reset = ''
if enableColors
bold = '\033[0;1m'
red = '\033[0;31m'
green = '\033[0;32m'
reset = '\033[0m'
# Built file header.
header = """
@@ -69,7 +75,8 @@ task 'build', 'build the CoffeeScript language from source', build = (cb) ->
task 'build:full', 'rebuild the source twice, and run the tests', ->
build ->
build ->
csPath = fs.realpathSync './lib/coffee-script'
csPath = './lib/coffee-script'
delete require.cache[require.resolve csPath]
unless runTests require csPath
process.exit 1

View File

@@ -1,2 +1,10 @@
# Eat lunch.
eat food for food in ['toast', 'cheese', 'wine']
# Fine dining
courses = ['salad', 'entree', 'dessert']
menu course + 1, dish for dish, course in courses
# Health conscious meal
foods = ['broccoli', 'spinach', 'chocolate']
eat food for food in foods when food isnt 'chocolate'

View File

@@ -264,6 +264,10 @@ div.code {
width: 40px;
text-transform: none;
}
.navigation .code a.minibutton.permalink {
top: 38px;
display: block;
}
.bookmark {
display: block;

View File

@@ -84,6 +84,7 @@
</div>
<div id="repl_results_wrap"><pre id="repl_results"></pre></div>
<div class="minibutton dark run" title="Ctrl-Enter">Run</div>
<a class="minibutton permalink" id="repl_permalink">Link</a>
<br class="clear" />
</div>
</div>
@@ -171,7 +172,7 @@ npm install -g coffee-script</pre>
<p>
(Leave off the <tt>-g</tt> if you don't wish to install globally.)
</p>
<p>
If you'd prefer to install the latest master version of CoffeeScript, you
can clone the CoffeeScript
@@ -190,7 +191,7 @@ sudo bin/cake install</pre>
<a href="http://opinionated-programmer.com/2010/12/installing-coffeescript-on-debian-or-ubuntu/">be
careful not to use the existing out-of-date package</a>. If installing on
Windows, your best bet is probably to run Node.js under Cygwin. If you'd
just like to experiment, you can try the
just like to experiment, you can try the
<a href="https://github.com/alisey/CoffeeScript-Compiler-for-Windows">CoffeeScript Compiler For Windows</a>.
</p>
@@ -228,7 +229,7 @@ sudo bin/cake install</pre>
<td><code>-j, --join [FILE]</code></td>
<td>
Before compiling, concatenate all scripts together in the order they
were passed, and write them into the specified file.
were passed, and write them into the specified file.
Useful for building large projects.
</td>
</tr>
@@ -627,7 +628,7 @@ Expressions
are the same as boolean <tt>true</tt>, while <tt>off</tt> and <tt>no</tt> are boolean <tt>false</tt>.
</p>
<p>
For single-line statements, <tt>unless</tt> can be used as the inverse of <tt>if</tt>.
<tt>unless</tt> can be used as the inverse of <tt>if</tt>.
</p>
<p>
As a shortcut for <tt>this.property</tt>, you can use <tt>@property</tt>.
@@ -932,17 +933,17 @@ Expressions
<span id="resources" class="bookmark"></span>
Books and Screencasts
</h2>
<p>
There are a number of excellent books and screencasts to help you get
There are a number of excellent books and screencasts to help you get
started with CoffeeScript, some of which are freely available online.
</p>
<ul>
<li>
<a href="http://arcturo.github.com/library/coffeescript/">The Little Book on CoffeeScript</a>
is a brief 5-chapter introduction to CoffeeScript, written with great
clarity and precision by
clarity and precision by
<a href="http://alexmaccaw.co.uk/">Alex MacCaw</a>.
</li>
<li>
@@ -955,7 +956,7 @@ Expressions
</li>
<li>
<a href="http://pragprog.com/book/tbcoffee/coffeescript">CoffeeScript: Accelerated JavaScript Development</a>
is <a href="http://trevorburnham.com/">Trevor Burnham</a>'s thorough
is <a href="http://trevorburnham.com/">Trevor Burnham</a>'s thorough
introduction to the language. By the end of the book, you'll have built
a fast-paced multiplayer word game, writing both the client-side and Node.js
portions in CoffeeScript.
@@ -973,7 +974,7 @@ Expressions
in 11 minutes.
</li>
</ul>
<h2>
Examples
</h2>
@@ -1040,7 +1041,7 @@ Expressions
<li>
<a href="http://github.com/jashkenas/coffee-script/wiki">The CoffeeScript Wiki</a><br />
If you've ever learned a neat CoffeeScript tip or trick, or ran into a gotcha &mdash; share it on the wiki.
The wiki also serves as a directory of handy
The wiki also serves as a directory of handy
<a href="http://github.com/jashkenas/coffee-script/wiki/Text-editor-plugins">text editor extensions</a>,
<a href="http://github.com/jashkenas/coffee-script/wiki/Web-framework-plugins">web framework plugins</a>,
and general <a href="http://github.com/jashkenas/coffee-script/wiki/Build-tools">CoffeeScript build tools</a>.
@@ -1070,7 +1071,7 @@ Expressions
<span id="change_log" class="bookmark"></span>
Change Log
</h2>
<p>
<b class="header" style="margin-top: 20px;">
<a href="https://github.com/jashkenas/coffee-script/compare/1.1.1...1.1.2">1.1.2</a>
@@ -1080,24 +1081,24 @@ Expressions
against control structures, implicit invocation of a try/catch block,
variadic arguments leaking from local scope, line numbers in syntax errors
following heregexes, property access on parenthesized number literals,
bound class methods and super with reserved names, a REPL overhaul,
bound class methods and super with reserved names, a REPL overhaul,
consecutive compiled semicolons, block comments in implicitly called objects,
and a Chrome bug.
</p>
<p>
<b class="header" style="margin-top: 20px;">1.1.1
<span class="timestamp"> &ndash; <small>May 10, 2011</small></span>
</b>
Bugfix release for classes with external constructor functions, see
Bugfix release for classes with external constructor functions, see
issue #1182.
</p>
<p>
<b class="header" style="margin-top: 20px;">1.1.0
<span class="timestamp"> &ndash; <small>May 1, 2011</small></span>
</b>
When running via the <tt>coffee</tt> executable, <tt>process.argv</tt> and
When running via the <tt>coffee</tt> executable, <tt>process.argv</tt> and
friends now report <tt>coffee</tt> instead of <tt>node</tt>.
Better compatibility with <b>Node.js 0.4.x</b> module lookup changes.
The output in the REPL is now colorized, like Node's is.
@@ -1577,6 +1578,7 @@ Expressions
</div>
<script type="text/coffeescript">
sourceFragment = "try:"
# Set up the compilation function, to run when you stop typing.
compileSource = ->
@@ -1593,6 +1595,9 @@ Expressions
catch error
$('#error').text(error.message).show()
# Update permalink
$('#repl_permalink').attr 'href', "##{sourceFragment}#{encodeURIComponent source}"
# Listen for keypresses and recompile.
$('#repl_source').keyup -> compileSource()
@@ -1639,11 +1644,21 @@ Expressions
$('#open_webchat').click ->
$(this).replaceWith $('<iframe src="http://webchat.freenode.net/?channels=coffeescript" width="625" height="400"></iframe>')
$("#repl_permalink").click (e) ->
window.location = $(this).attr("href")
false
# If source code is included in location.hash, display it.
hash = decodeURIComponent location.hash.replace(/^#/, '')
if hash.indexOf(sourceFragment) == 0
src = hash.substr sourceFragment.length
loadConsole src
compileSource()
</script>
<script src="documentation/vendor/jquery-1.4.2.js"></script>
<script src="documentation/vendor/jquery-1.6.4.js"></script>
<script src="extras/coffee-script.js"></script>
</body>

View File

@@ -1,17 +1,13 @@
var volume, winner;
if (ignition === true) {
launch();
}
if (band !== SpinalTap) {
volume = 10;
}
if (answer !== false) {
letTheWildRumpusBegin();
}
if (car.speed < limit) {
accelerate();
}
if (pick === 47 || pick === 92 || pick === 13) {
winner = true;
}
print(inspect("My name is " + this.name));
if (ignition === true) launch();
if (band !== SpinalTap) volume = 10;
if (answer !== false) letTheWildRumpusBegin();
if (car.speed < limit) accelerate();
if (pick === 47 || pick === 92 || pick === 13) winner = true;
print(inspect("My name is " + this.name));

View File

@@ -1,6 +1,21 @@
var food, _i, _len, _ref;
var course, courses, dish, food, foods, _i, _j, _len, _len2, _len3, _ref;
_ref = ['toast', 'cheese', 'wine'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
food = _ref[_i];
eat(food);
}
}
courses = ['salad', 'entree', 'dessert'];
for (course = 0, _len2 = courses.length; course < _len2; course++) {
dish = courses[course];
menu(course + 1, dish);
}
foods = ['broccoli', 'spinach', 'chocolate'];
for (_j = 0, _len3 = foods.length; _j < _len3; _j++) {
food = foods[_j];
if (food !== 'chocolate') eat(food);
}

View File

@@ -1,4 +1,7 @@
/*
CoffeeScript Compiler v1.1.2
Released under the MIT License
*/
*/

View File

@@ -1,10 +1,13 @@
var fs;
fs = require('fs');
option('-o', '--output [DIR]', 'directory for compiled code');
task('build:parser', 'rebuild the Jison parser', function(options) {
var code, dir;
require('jison');
code = require('./lib/grammar').parser.generate();
dir = options.output || 'lib';
return fs.writeFile("" + dir + "/parser.js", code);
});
});

View File

@@ -1,44 +1,58 @@
var Animal, Horse, Snake, sam, tom;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
};
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
Animal = (function() {
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function(meters) {
return alert(this.name + (" moved " + meters + "m."));
};
return Animal;
})();
Snake = (function() {
__extends(Snake, Animal);
function Snake() {
Snake.__super__.constructor.apply(this, arguments);
}
Snake.prototype.move = function() {
alert("Slithering...");
return Snake.__super__.move.call(this, 5);
};
return Snake;
})();
Horse = (function() {
__extends(Horse, Animal);
function Horse() {
Horse.__super__.constructor.apply(this, arguments);
}
Horse.prototype.move = function() {
alert("Galloping...");
return Horse.__super__.move.call(this, 45);
};
return Horse;
})();
sam = new Snake("Sammy the Python");
tom = new Horse("Tommy the Palomino");
sam.move();
tom.move();
tom.move();

View File

@@ -1,3 +1,5 @@
var cholesterol, healthy;
cholesterol = 127;
healthy = (200 > cholesterol && cholesterol > 60);
healthy = (200 > cholesterol && cholesterol > 60);

View File

@@ -1,12 +1,14 @@
var date, mood;
if (singing) {
mood = greatlyImproved;
}
if (singing) mood = greatlyImproved;
if (happy && knowsIt) {
clapsHands();
chaChaCha();
} else {
showIt();
}
date = friday ? sue : jill;
options || (options = defaults);
options || (options = defaults);

View File

@@ -1,7 +1,6 @@
var fill;
fill = function(container, liquid) {
if (liquid == null) {
liquid = "coffee";
}
if (liquid == null) liquid = "coffee";
return "Filling the " + container + " with " + liquid + "...";
};
};

View File

@@ -1,4 +1,5 @@
var filename, _fn, _i, _len;
_fn = function(filename) {
return fs.readFile(filename, function(err, contents) {
return compile(filename, contents.toString());
@@ -7,4 +8,4 @@ _fn = function(filename) {
for (_i = 0, _len = list.length; _i < _len; _i++) {
filename = list[_i];
_fn(filename);
}
}

View File

@@ -1,4 +1,5 @@
var hi;
hi = function() {
return [document.title, "Hello JavaScript"].join(": ");
};
};

View File

@@ -1,8 +1,9 @@
var footprints, solipsism;
if ((typeof mind !== "undefined" && mind !== null) && !(typeof world !== "undefined" && world !== null)) {
solipsism = true;
}
if (typeof speed === "undefined" || speed === null) {
speed = 75;
}
footprints = typeof yeti !== "undefined" && yeti !== null ? yeti : "bear";
if (typeof speed === "undefined" || speed === null) speed = 75;
footprints = typeof yeti !== "undefined" && yeti !== null ? yeti : "bear";

View File

@@ -1,4 +1,5 @@
var eldest, grade;
grade = function(student) {
if (student.excellentWork) {
return "A+";
@@ -12,4 +13,5 @@ grade = function(student) {
return "C";
}
};
eldest = 24 > 21 ? "Liz" : "Ike";
eldest = 24 > 21 ? "Liz" : "Ike";

View File

@@ -1,2 +1,3 @@
var one, six, three, two;
six = (one = 1) + (two = 2) + (three = 3);
six = (one = 1) + (two = 2) + (three = 3);

View File

@@ -1,4 +1,5 @@
var globals, name;
globals = ((function() {
var _results;
_results = [];
@@ -6,4 +7,4 @@ globals = ((function() {
_results.push(name);
}
return _results;
})()).slice(0, 10);
})()).slice(0, 10);

View File

@@ -1,7 +1,8 @@
alert((function() {
try {
return nonexistent / void 0;
} catch (error) {
return "And the error is ... " + error;
}
})());
})());

View File

@@ -1,9 +1,10 @@
var Account;
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Account = function(customer, cart) {
this.customer = customer;
this.cart = cart;
return $('.shopping_cart').bind('click', __bind(function(event) {
return this.customer.purchase(this.cart);
}, this));
};
};

View File

@@ -1,7 +1,9 @@
var cube, square;
square = function(x) {
return x * x;
};
cube = function(x) {
return square(x) * x;
};
};

View File

@@ -1,2 +1,3 @@
var html;
html = '<strong>\n cup of coffeescript\n</strong>';
html = '<strong>\n cup of coffeescript\n</strong>';

View File

@@ -1,2 +1,3 @@
var OPERATOR;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;

View File

@@ -1,4 +1,7 @@
var author, quote, sentence;
author = "Wittgenstein";
quote = "A picture is a fact. -- " + author;
sentence = "" + (22 / 7) + " is a decent approximation of π";
sentence = "" + (22 / 7) + " is a decent approximation of π";

View File

@@ -1,5 +1,7 @@
var city, forecast, temp, weatherReport, _ref;
weatherReport = function(location) {
return [location, 72, "Mostly Sunny"];
};
_ref = weatherReport("Berkeley, CA"), city = _ref[0], temp = _ref[1], forecast = _ref[2];
_ref = weatherReport("Berkeley, CA"), city = _ref[0], temp = _ref[1], forecast = _ref[2];

View File

@@ -1,9 +1,11 @@
var age, ages, child, yearsOld;
yearsOld = {
max: 10,
ida: 9,
tim: 11
};
ages = (function() {
var _results;
_results = [];
@@ -12,4 +14,4 @@ ages = (function() {
_results.push("" + child + " is " + age);
}
return _results;
})();
})();

View File

@@ -1,4 +1,5 @@
var city, futurists, name, street, _ref, _ref2;
futurists = {
sculptor: "Umberto Boccioni",
painter: "Vladimir Burliuk",
@@ -7,4 +8,5 @@ futurists = {
address: ["Via Roma 42R", "Bellagio, Italy 22021"]
}
};
_ref = futurists.poet, name = _ref.name, _ref2 = _ref.address, street = _ref2[0], city = _ref2[1];
_ref = futurists.poet, name = _ref.name, (_ref2 = _ref.address, street = _ref2[0], city = _ref2[1]);

View File

@@ -1,10 +1,14 @@
var bitlist, kids, singers, song;
song = ["do", "re", "mi", "fa", "so"];
singers = {
Jagger: "Rock",
Elvis: "Roll"
};
bitlist = [1, 0, 1, 0, 0, 1, 1, 1, 0];
kids = {
brother: {
name: "Max",
@@ -14,4 +18,4 @@ kids = {
name: "Ida",
age: 9
}
};
};

View File

@@ -1,4 +1,6 @@
$('.account').attr({
"class": 'active'
});
log(object["class"]);
log(object["class"]);

View File

@@ -1,14 +1,18 @@
var cubes, list, math, num, number, opposite, race, square;
var __slice = Array.prototype.slice;
number = 42;
opposite = true;
if (opposite) {
number = -42;
}
if (opposite) number = -42;
square = function(x) {
return x * x;
};
list = [1, 2, 3, 4, 5];
math = {
root: Math.sqrt,
square: square,
@@ -16,14 +20,15 @@ math = {
return x * square(x);
}
};
race = function() {
var runners, winner;
winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
return print(winner, runners);
};
if (typeof elvis !== "undefined" && elvis !== null) {
alert("I knew it!");
}
if (typeof elvis !== "undefined" && elvis !== null) alert("I knew it!");
cubes = (function() {
var _i, _len, _results;
_results = [];
@@ -32,4 +37,4 @@ cubes = (function() {
_results.push(math.cube(num));
}
return _results;
})();
})();

View File

@@ -1,4 +1,7 @@
var theBait, theSwitch, _ref;
theBait = 1000;
theSwitch = 0;
_ref = [theSwitch, theBait], theBait = _ref[0], theSwitch = _ref[1];
_ref = [theSwitch, theBait], theBait = _ref[0], theSwitch = _ref[1];

View File

@@ -1,4 +1,6 @@
var close, contents, open, tag, _i, _ref;
var __slice = Array.prototype.slice;
tag = "<impossible>";
_ref = tag.split(""), open = _ref[0], contents = 3 <= _ref.length ? __slice.call(_ref, 1, _i = _ref.length - 1) : (_i = 1, []), close = _ref[_i++];
_ref = tag.split(""), open = _ref[0], contents = 3 <= _ref.length ? __slice.call(_ref, 1, _i = _ref.length - 1) : (_i = 1, []), close = _ref[_i++];

View File

@@ -1,3 +1,4 @@
String.prototype.dasherize = function() {
return this.replace(/_/g, "-");
};
};

View File

@@ -1,4 +1,5 @@
var countdown, num;
countdown = (function() {
var _results;
_results = [];
@@ -6,4 +7,4 @@ countdown = (function() {
_results.push(num);
}
return _results;
})();
})();

View File

@@ -1,8 +1,11 @@
var changeNumbers, inner, outer;
outer = 1;
changeNumbers = function() {
var inner;
inner = -1;
return outer = 10;
};
inner = changeNumbers();
inner = changeNumbers();

View File

@@ -1,4 +1,7 @@
var copy, middle, numbers;
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
copy = numbers.slice(0, numbers.length);
middle = copy.slice(3, 7);
middle = copy.slice(3, 7);

View File

@@ -1,2 +1,3 @@
var zip, _ref;
zip = typeof lottery.drawWinner === "function" ? (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0;
zip = typeof lottery.drawWinner === "function" ? (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0;

View File

@@ -1,6 +1,8 @@
var awardMedals, contenders, gold, rest, silver;
var __slice = Array.prototype.slice;
gold = silver = rest = "unknown";
awardMedals = function() {
var first, others, second;
first = arguments[0], second = arguments[1], others = 3 <= arguments.length ? __slice.call(arguments, 2) : [];
@@ -8,8 +10,13 @@ awardMedals = function() {
silver = second;
return rest = others;
};
contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"];
awardMedals.apply(null, contenders);
alert("Gold: " + gold);
alert("Silver: " + silver);
alert("The Field: " + rest);
alert("The Field: " + rest);

View File

@@ -1,3 +1,5 @@
var numbers, _ref;
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
[].splice.apply(numbers, [3, 4].concat(_ref = [-3, -4, -5, -6])), _ref;
[].splice.apply(numbers, [3, 4].concat(_ref = [-3, -4, -5, -6])), _ref;

View File

@@ -1,2 +1,3 @@
var mobyDick;
mobyDick = "Call me Ishmael. Some years ago -- never mind how long precisely -- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...";
mobyDick = "Call me Ishmael. Some years ago -- never mind how long precisely -- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...";

View File

@@ -1,3 +1,4 @@
switch (day) {
case "Mon":
go(work);
@@ -20,4 +21,4 @@ switch (day) {
break;
default:
go(work);
}
}

View File

@@ -1,3 +1,4 @@
try {
allHellBreaksLoose();
catsAndDogsLivingTogether();
@@ -5,4 +6,4 @@ try {
print(error);
} finally {
cleanUp();
}
}

View File

@@ -1,4 +1,5 @@
var lyrics, num;
if (this.studyingEconomics) {
while (supply > demand) {
buy();
@@ -7,7 +8,9 @@ if (this.studyingEconomics) {
sell();
}
}
num = 6;
lyrics = (function() {
var _results;
_results = [];
@@ -15,4 +18,4 @@ lyrics = (function() {
_results.push("" + num + " little monkeys, jumping on the bed. One fell out and bumped his head.");
}
return _results;
})();
})();

File diff suppressed because it is too large Load Diff

9046
documentation/vendor/jquery-1.6.4.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@ selection_sort = (list) ->
min = i
# Check the rest of the array to see if anything is smaller.
min = j if list[j] < list[min] for j in [i + 1...len]
min = k for v, k in list[i+1...] when v < list[min]
# Swap if a smaller item has been found.
[list[i], list[min]] = [list[min], list[i]] if i isnt min

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,22 @@
(function() {
var CoffeeScript, runScripts;
CoffeeScript = require('./coffee-script');
CoffeeScript.require = require;
CoffeeScript.eval = function(code, options) {
return eval(CoffeeScript.compile(code, options));
};
CoffeeScript.run = function(code, options) {
if (options == null) options = {};
options.bare = true;
return Function(CoffeeScript.compile(code, options))();
};
if (typeof window === "undefined" || window === null) return;
CoffeeScript.load = function(url, callback) {
var xhr;
xhr = new (window.ActiveXObject || XMLHttpRequest)('Microsoft.XMLHTTP');
@@ -29,6 +35,7 @@
};
return xhr.send(null);
};
runScripts = function() {
var coffees, execute, index, length, s, scripts;
scripts = document.getElementsByTagName('script');
@@ -57,9 +64,11 @@
})();
return null;
};
if (window.addEventListener) {
addEventListener('DOMContentLoaded', runScripts, false);
} else {
attachEvent('onload', runScripts);
}
}).call(this);

View File

@@ -1,14 +1,24 @@
(function() {
var CoffeeScript, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
var CoffeeScript, cakefileDirectory, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
fs = require('fs');
path = require('path');
helpers = require('./helpers');
optparse = require('./optparse');
CoffeeScript = require('./coffee-script');
tasks = {};
options = {};
switches = [];
oparse = null;
helpers.extend(global, {
task: function(name, description, action) {
var _ref;
@@ -29,26 +39,27 @@
return tasks[name].action(options);
}
});
exports.run = function() {
return path.exists('Cakefile', function(exists) {
var arg, args, _i, _len, _ref, _results;
if (!exists) throw new Error("Cakefile not found in " + (process.cwd()));
args = process.argv.slice(2);
CoffeeScript.run(fs.readFileSync('Cakefile').toString(), {
filename: 'Cakefile'
});
oparse = new optparse.OptionParser(switches);
if (!args.length) return printTasks();
options = oparse.parse(args);
_ref = options.arguments;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
arg = _ref[_i];
_results.push(invoke(arg));
}
return _results;
var arg, args, _i, _len, _ref, _results;
global.__originalDirname = fs.realpathSync('.');
process.chdir(cakefileDirectory(__originalDirname));
args = process.argv.slice(2);
CoffeeScript.run(fs.readFileSync('Cakefile').toString(), {
filename: 'Cakefile'
});
oparse = new optparse.OptionParser(switches);
if (!args.length) return printTasks();
options = oparse.parse(args);
_ref = options.arguments;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
arg = _ref[_i];
_results.push(invoke(arg));
}
return _results;
};
printTasks = function() {
var desc, name, spaces, task;
console.log('');
@@ -61,8 +72,18 @@
}
if (switches.length) return console.log(oparse.help());
};
missingTask = function(task) {
console.log("No such task: \"" + task + "\"");
return process.exit(1);
};
cakefileDirectory = function(dir) {
var parent;
if (path.existsSync(path.join(dir, 'Cakefile'))) return dir;
parent = path.normalize(path.join(dir, '..'));
if (parent !== dir) return cakefileDirectory(parent);
throw new Error("Cakefile not found in " + (process.cwd()));
};
}).call(this);

View File

@@ -1,11 +1,15 @@
(function() {
var Lexer, RESERVED, compile, fs, lexer, parser, path, vm, _ref;
var Lexer, RESERVED, compile, fs, lexer, parser, path, _ref;
var __hasProp = Object.prototype.hasOwnProperty;
fs = require('fs');
path = require('path');
_ref = require('./lexer'), Lexer = _ref.Lexer, RESERVED = _ref.RESERVED;
parser = require('./parser').parser;
vm = require('vm');
if (require.extensions) {
require.extensions['.coffee'] = function(module, filename) {
var content;
@@ -19,9 +23,13 @@
return compile(content);
});
}
exports.VERSION = '1.1.3-pre';
exports.RESERVED = RESERVED;
exports.helpers = require('./helpers');
exports.compile = compile = function(code, options) {
if (options == null) options = {};
try {
@@ -33,9 +41,11 @@
throw err;
}
};
exports.tokens = function(code, options) {
return lexer.tokenize(code, options);
};
exports.nodes = function(source, options) {
if (typeof source === 'string') {
return parser.parse(lexer.tokenize(source, options));
@@ -43,6 +53,7 @@
return parser.parse(source);
}
};
exports.run = function(code, options) {
var Module, mainModule;
mainModule = require.main;
@@ -58,17 +69,19 @@
return mainModule._compile(code, mainModule.filename);
}
};
exports.eval = function(code, options) {
var Module, Script, js, k, o, r, sandbox, v, _i, _len, _module, _ref2, _ref3, _require;
if (options == null) options = {};
if (!(code = code.trim())) return;
Script = vm.Script;
Script = require('vm').Script;
if (Script) {
sandbox = Script.createContext();
sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox;
if (options.sandbox != null) {
if (options.sandbox instanceof Script.createContext().constructor) {
if (options.sandbox instanceof sandbox.constructor) {
sandbox = options.sandbox;
} else {
sandbox = Script.createContext();
_ref2 = options.sandbox;
for (k in _ref2) {
if (!__hasProp.call(_ref2, k)) continue;
@@ -76,17 +89,14 @@
sandbox[k] = v;
}
}
sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox;
} else {
sandbox = global;
}
sandbox.__filename = options.filename || 'eval';
sandbox.__dirname = path.dirname(sandbox.__filename);
if (!(sandbox !== global || sandbox.module || sandbox.require)) {
if (!(sandbox.module || sandbox.require)) {
Module = require('module');
sandbox.module = _module = new Module(options.modulename || 'eval');
sandbox.require = _require = function(path) {
return Module._load(path, _module, true);
return Module._load(path, _module);
};
_module.filename = sandbox.__filename;
_ref3 = Object.getOwnPropertyNames(require);
@@ -108,13 +118,15 @@
}
o.bare = true;
js = compile(code, o);
if (sandbox === global) {
return vm.runInThisContext(js);
if (Script) {
return Script.runInContext(js, sandbox);
} else {
return vm.runInContext(js, sandbox);
return eval(js);
}
};
lexer = new Lexer;
parser.lexer = {
lex: function() {
var tag, _ref2;
@@ -129,5 +141,7 @@
return "";
}
};
parser.yy = require('./nodes');
}).call(this);

View File

@@ -1,25 +1,42 @@
(function() {
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compileScript, compileScripts, compileStdio, contents, exec, forkNode, fs, helpers, lint, loadRequires, optionParser, optparse, opts, parseOptions, path, printLine, printTokens, printWarn, sources, spawn, usage, version, watch, writeJs, _ref;
fs = require('fs');
path = require('path');
helpers = require('./helpers');
optparse = require('./optparse');
CoffeeScript = require('./coffee-script');
_ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec;
EventEmitter = require('events').EventEmitter;
helpers.extend(CoffeeScript, new EventEmitter);
printLine = function(line) {
return process.stdout.write(line + '\n');
};
printWarn = function(line) {
return process.binding('stdio').writeError(line + '\n');
};
BANNER = 'Usage: coffee [options] path/to/script.coffee';
SWITCHES = [['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-o', '--output [DIR]', 'set the directory for compiled JavaScript'], ['-j', '--join [FILE]', 'concatenate the scripts before compiling'], ['-w', '--watch', 'watch scripts for changes, and recompile'], ['-p', '--print', 'print the compiled JavaScript to stdout'], ['-l', '--lint', 'pipe the compiled JavaScript through JavaScript Lint'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-e', '--eval', 'compile a string from the command line'], ['-r', '--require [FILE*]', 'require a library before executing your script'], ['-b', '--bare', 'compile without the top-level function wrapper'], ['-t', '--tokens', 'print the tokens that the lexer produces'], ['-n', '--nodes', 'print the parse tree that Jison produces'], ['--nodejs [ARGS]', 'pass options through to the "node" binary'], ['-v', '--version', 'display CoffeeScript version'], ['-h', '--help', 'display this help message']];
BANNER = 'Usage: coffee [options] path/to/script.coffee\n\nIf called without options, `coffee` will run your script.';
SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-e', '--eval', 'pass a string from the command line as input'], ['-h', '--help', 'display this help message'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling'], ['-l', '--lint', 'pipe the compiled JavaScript through JavaScript Lint'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-r', '--require [FILE*]', 'require a library before executing your script'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce'], ['-v', '--version', 'display the version number'], ['-w', '--watch', 'watch scripts for changes and rerun commands']];
opts = {};
sources = [];
contents = [];
optionParser = null;
exports.run = function() {
parseOptions();
if (opts.nodejs) return forkNode();
@@ -36,6 +53,7 @@
process.execPath = require.main.filename;
return compileScripts();
};
compileScripts = function() {
var base, compile, remaining_files, source, trackCompleteFiles, trackUnprocessedFiles, unprocessed, _i, _j, _len, _len2, _results;
unprocessed = [];
@@ -109,6 +127,7 @@
}
return _results;
};
compileScript = function(file, input, base) {
var o, options, t, task;
o = opts;
@@ -145,6 +164,7 @@
return process.exit(1);
}
};
compileStdio = function() {
var code, stdin;
code = '';
@@ -156,11 +176,13 @@
return compileScript(null, code);
});
};
compileJoin = function() {
var code;
code = contents.join('\n');
return compileScript(opts.join, code, opts.join);
};
loadRequires = function() {
var realFilename, req, _i, _len, _ref2;
realFilename = module.filename;
@@ -172,6 +194,7 @@
}
return module.filename = realFilename;
};
watch = function(source, base) {
return fs.watchFile(source, {
persistent: true,
@@ -186,6 +209,7 @@
});
});
};
writeJs = function(source, js, base) {
var baseDir, compile, dir, filename, jsPath, srcDir;
filename = path.basename(source, path.extname(source)) + '.js';
@@ -211,6 +235,7 @@
}
});
};
lint = function(file, js) {
var conf, jsl, printIt;
printIt = function(buffer) {
@@ -223,6 +248,7 @@
jsl.stdin.write(js);
return jsl.stdin.end();
};
printTokens = function(tokens) {
var strings, tag, token, value;
strings = (function() {
@@ -237,6 +263,7 @@
})();
return printLine(strings.join(' '));
};
parseOptions = function() {
var o;
optionParser = new optparse.OptionParser(SWITCHES, BANNER);
@@ -246,12 +273,14 @@
o.print = !!(o.print || (o.eval || o.stdio && o.compile));
return sources = o.arguments;
};
compileOptions = function(filename) {
return {
filename: filename,
bare: opts.bare
};
};
forkNode = function() {
var args, nodeArgs;
nodeArgs = opts.nodejs.split(/\s+/);
@@ -263,10 +292,13 @@
customFds: [0, 1, 2]
});
};
usage = function() {
return printLine((new optparse.OptionParser(SWITCHES, BANNER)).help());
};
version = function() {
return printLine("CoffeeScript version " + CoffeeScript.VERSION);
};
}).call(this);

View File

@@ -1,7 +1,10 @@
(function() {
var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
Parser = require('jison').Parser;
unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/;
o = function(patternString, action, options) {
var match;
patternString = patternString.replace(/\s{2,}/g, ' ');
@@ -11,6 +14,7 @@
action = action.replace(/\b(?:Block\.wrap|extend)\b/g, 'yy.$&');
return [patternString, "$$ = " + action + ";", options];
};
grammar = {
Root: [
o('', function() {
@@ -550,8 +554,11 @@
})
]
};
operators = [['left', '.', '?.', '::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'DO', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['right', 'POST_IF']];
tokens = [];
for (name in grammar) {
alternatives = grammar[name];
grammar[name] = (function() {
@@ -570,10 +577,12 @@
return _results;
})();
}
exports.parser = new Parser({
tokens: tokens.join(' '),
bnf: grammar,
operators: operators.reverse(),
startSymbol: 'Root'
});
}).call(this);

View File

@@ -1,13 +1,16 @@
(function() {
var extend, flatten;
exports.starts = function(string, literal, start) {
return literal === string.substr(start, literal.length);
};
exports.ends = function(string, literal, back) {
var len;
len = literal.length;
return literal === string.substr(string.length - len - (back || 0), len);
};
exports.compact = function(array) {
var item, _i, _len, _results;
_results = [];
@@ -17,6 +20,7 @@
}
return _results;
};
exports.count = function(string, substr) {
var num, pos;
num = pos = 0;
@@ -26,9 +30,11 @@
}
return num;
};
exports.merge = function(options, overrides) {
return extend(extend({}, options), overrides);
};
extend = exports.extend = function(object, properties) {
var key, val;
for (key in properties) {
@@ -37,6 +43,7 @@
}
return object;
};
exports.flatten = flatten = function(array) {
var element, flattened, _i, _len;
flattened = [];
@@ -50,13 +57,16 @@
}
return flattened;
};
exports.del = function(obj, key) {
var val;
val = obj[key];
delete obj[key];
return val;
};
exports.last = function(array, back) {
return array[array.length - (back || 0) - 1];
};
}).call(this);

View File

@@ -1,8 +1,10 @@
(function() {
var key, val, _ref;
_ref = require('./coffee-script');
for (key in _ref) {
val = _ref[key];
exports[key] = val;
}
}).call(this);

View File

@@ -1,17 +1,17 @@
(function() {
var ASSIGNED, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, starts, _ref;
var __hasProp = Object.prototype.hasOwnProperty, __indexOf = Array.prototype.indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (__hasProp.call(this, i) && this[i] === item) return i;
}
return -1;
};
Rewriter = require('./rewriter').Rewriter;
_ref = require('./helpers'), count = _ref.count, starts = _ref.starts, compact = _ref.compact, last = _ref.last;
var BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, starts, _ref, _ref2;
var __hasProp = Object.prototype.hasOwnProperty, __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; };
_ref = require('./rewriter'), Rewriter = _ref.Rewriter, INVERSES = _ref.INVERSES;
_ref2 = require('./helpers'), count = _ref2.count, starts = _ref2.starts, compact = _ref2.compact, last = _ref2.last;
exports.Lexer = Lexer = (function() {
function Lexer() {}
Lexer.prototype.tokenize = function(code, opts) {
var i;
var i, tag;
if (opts == null) opts = {};
if (WHITESPACE.test(code)) code = "\n" + code;
code = code.replace(/\r/g, '').replace(TRAILING_SPACES, '');
@@ -21,28 +21,31 @@
this.indebt = 0;
this.outdebt = 0;
this.indents = [];
this.ends = [];
this.tokens = [];
i = 0;
while (this.chunk = code.slice(i)) {
i += this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.heredocToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken();
}
this.closeIndentation();
if (tag = this.ends.pop()) this.error("missing " + tag);
if (opts.rewrite === false) return this.tokens;
return (new Rewriter).rewrite(this.tokens);
};
Lexer.prototype.identifierToken = function() {
var colon, forcedIdentifier, id, input, match, prev, tag, _ref2, _ref3;
var colon, forcedIdentifier, id, input, match, prev, tag, _ref3, _ref4;
if (!(match = IDENTIFIER.exec(this.chunk))) return 0;
input = match[0], id = match[1], colon = match[2];
if (id === 'own' && this.tag() === 'FOR') {
this.token('OWN', id);
return id.length;
}
forcedIdentifier = colon || (prev = last(this.tokens)) && (((_ref2 = prev[0]) === '.' || _ref2 === '?.' || _ref2 === '::') || !prev.spaced && prev[0] === '@');
forcedIdentifier = colon || (prev = last(this.tokens)) && (((_ref3 = prev[0]) === '.' || _ref3 === '?.' || _ref3 === '::') || !prev.spaced && prev[0] === '@');
tag = 'IDENTIFIER';
if (!forcedIdentifier && (__indexOf.call(JS_KEYWORDS, id) >= 0 || __indexOf.call(COFFEE_KEYWORDS, id) >= 0)) {
tag = id.toUpperCase();
if (tag === 'WHEN' && (_ref3 = this.tag(), __indexOf.call(LINE_BREAK, _ref3) >= 0)) {
if (tag === 'WHEN' && (_ref4 = this.tag(), __indexOf.call(LINE_BREAK, _ref4) >= 0)) {
tag = 'LEADING_WHEN';
} else if (tag === 'FOR') {
this.seenFor = true;
@@ -69,7 +72,7 @@
id = new String(id);
id.reserved = true;
} else if (__indexOf.call(RESERVED, id) >= 0) {
this.identifierError(id);
this.error("reserved word \"" + word + "\"");
}
}
if (!forcedIdentifier) {
@@ -102,6 +105,7 @@
if (colon) this.token(':', ':');
return input.length;
};
Lexer.prototype.numberToken = function() {
var match, number;
if (!(match = NUMBER.exec(this.chunk))) return 0;
@@ -109,6 +113,7 @@
this.token('NUMBER', number);
return number.length;
};
Lexer.prototype.stringToken = function() {
var match, string;
switch (this.chunk.charAt(0)) {
@@ -130,6 +135,7 @@
this.line += count(string, '\n');
return string.length;
};
Lexer.prototype.heredocToken = function() {
var doc, heredoc, match, quote;
if (!(match = HEREDOC.exec(this.chunk))) return 0;
@@ -149,6 +155,7 @@
this.line += count(heredoc, '\n');
return heredoc.length;
};
Lexer.prototype.commentToken = function() {
var comment, here, match;
if (!(match = this.chunk.match(COMMENT))) return 0;
@@ -163,6 +170,7 @@
this.line += count(comment, '\n');
return comment.length;
};
Lexer.prototype.jsToken = function() {
var match, script;
if (!(this.chunk.charAt(0) === '`' && (match = JSTOKEN.exec(this.chunk)))) {
@@ -171,8 +179,9 @@
this.token('JS', (script = match[0]).slice(1, -1));
return script.length;
};
Lexer.prototype.regexToken = function() {
var length, match, prev, regex, _ref2;
var flags, length, match, prev, regex, _ref3, _ref4;
if (this.chunk.charAt(0) !== '/') return 0;
if (match = HEREGEX.exec(this.chunk)) {
length = this.heregexToken(match);
@@ -180,30 +189,38 @@
return length;
}
prev = last(this.tokens);
if (prev && (_ref2 = prev[0], __indexOf.call((prev.spaced ? NOT_REGEX : NOT_SPACED_REGEX), _ref2) >= 0)) {
if (prev && (_ref3 = prev[0], __indexOf.call((prev.spaced ? NOT_REGEX : NOT_SPACED_REGEX), _ref3) >= 0)) {
return 0;
}
if (!(match = REGEX.exec(this.chunk))) return 0;
regex = match[0];
this.token('REGEX', regex === '//' ? '/(?:)/' : regex);
return regex.length;
_ref4 = match, match = _ref4[0], regex = _ref4[1], flags = _ref4[2];
if (regex.slice(0, 2) === '/*') {
this.error('regular expressions cannot begin with `*`');
}
if (regex === '//') regex = '/(?:)/';
this.token('REGEX', "" + regex + flags);
return match.length;
};
Lexer.prototype.heregexToken = function(match) {
var body, flags, heregex, re, tag, tokens, value, _i, _len, _ref2, _ref3, _ref4, _ref5;
var body, flags, heregex, re, tag, tokens, value, _i, _len, _ref3, _ref4, _ref5, _ref6;
heregex = match[0], body = match[1], flags = match[2];
if (0 > body.indexOf('#{')) {
re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/');
if (re.match(/^\*/)) {
this.error('regular expressions cannot begin with `*`');
}
this.token('REGEX', "/" + (re || '(?:)') + "/" + flags);
return heregex.length;
}
this.token('IDENTIFIER', 'RegExp');
this.tokens.push(['CALL_START', '(']);
tokens = [];
_ref2 = this.interpolateString(body, {
_ref3 = this.interpolateString(body, {
regex: true
});
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
_ref3 = _ref2[_i], tag = _ref3[0], value = _ref3[1];
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
_ref4 = _ref3[_i], tag = _ref4[0], value = _ref4[1];
if (tag === 'TOKENS') {
tokens.push.apply(tokens, value);
} else {
@@ -214,19 +231,21 @@
tokens.push(['+', '+']);
}
tokens.pop();
if (((_ref4 = tokens[0]) != null ? _ref4[0] : void 0) !== 'STRING') {
if (((_ref5 = tokens[0]) != null ? _ref5[0] : void 0) !== 'STRING') {
this.tokens.push(['STRING', '""'], ['+', '+']);
}
(_ref5 = this.tokens).push.apply(_ref5, tokens);
(_ref6 = this.tokens).push.apply(_ref6, tokens);
if (flags) this.tokens.push([',', ','], ['STRING', '"' + flags + '"']);
this.token(')', ')');
return heregex.length;
};
Lexer.prototype.lineToken = function() {
var diff, indent, match, noNewlines, prev, size;
if (!(match = MULTI_DENT.exec(this.chunk))) return 0;
indent = match[0];
this.line += count(indent, '\n');
this.seenFor = false;
prev = last(this.tokens, 1);
size = indent.length - 1 - indent.lastIndexOf('\n');
noNewlines = this.unfinished();
@@ -247,6 +266,7 @@
diff = size - this.indent + this.outdebt;
this.token('INDENT', diff);
this.indents.push(diff);
this.ends.push('OUTDENT');
this.outdebt = this.indebt = 0;
} else {
this.indebt = 0;
@@ -255,7 +275,8 @@
this.indent = size;
return indent.length;
};
Lexer.prototype.outdentToken = function(moveOut, noNewlines, close) {
Lexer.prototype.outdentToken = function(moveOut, noNewlines) {
var dent, len;
while (moveOut > 0) {
len = this.indents.length - 1;
@@ -271,15 +292,20 @@
dent = this.indents.pop() - this.outdebt;
moveOut -= dent;
this.outdebt = 0;
this.pair('OUTDENT');
this.token('OUTDENT', dent);
}
}
if (dent) this.outdebt -= moveOut;
while (this.value() === ';') {
this.tokens.pop();
}
if (!(this.tag() === 'TERMINATOR' || noNewlines)) {
this.token('TERMINATOR', '\n');
}
return this;
};
Lexer.prototype.whitespaceToken = function() {
var match, nline, prev;
if (!((match = WHITESPACE.exec(this.chunk)) || (nline = this.chunk.charAt(0) === '\n'))) {
@@ -293,16 +319,22 @@
return 0;
}
};
Lexer.prototype.newlineToken = function() {
while (this.value() === ';') {
this.tokens.pop();
}
if (this.tag() !== 'TERMINATOR') this.token('TERMINATOR', '\n');
return this;
};
Lexer.prototype.suppressNewlines = function() {
if (this.value() === '\\') this.tokens.pop();
return this;
};
Lexer.prototype.literalToken = function() {
var match, prev, tag, value, _ref2, _ref3, _ref4, _ref5;
var match, prev, tag, value, _ref3, _ref4, _ref5, _ref6;
if (match = OPERATOR.exec(this.chunk)) {
value = match[0];
if (CODE.test(value)) this.tagParameters();
@@ -312,16 +344,17 @@
tag = value;
prev = last(this.tokens);
if (value === '=' && prev) {
if (!prev[1].reserved && (_ref2 = prev[1], __indexOf.call(JS_FORBIDDEN, _ref2) >= 0)) {
this.assignmentError();
if (!prev[1].reserved && (_ref3 = prev[1], __indexOf.call(JS_FORBIDDEN, _ref3) >= 0)) {
this.error("reserved word \"" + (this.value()) + "\" can't be assigned");
}
if ((_ref3 = prev[1]) === '||' || _ref3 === '&&') {
if ((_ref4 = prev[1]) === '||' || _ref4 === '&&') {
prev[0] = 'COMPOUND_ASSIGN';
prev[1] += '=';
return value.length;
}
}
if (value === ';') {
this.seenFor = false;
tag = 'TERMINATOR';
} else if (__indexOf.call(MATH, value) >= 0) {
tag = 'MATH';
@@ -336,10 +369,10 @@
} else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) {
tag = 'LOGIC';
} else if (prev && !prev.spaced) {
if (value === '(' && (_ref4 = prev[0], __indexOf.call(CALLABLE, _ref4) >= 0)) {
if (value === '(' && (_ref5 = prev[0], __indexOf.call(CALLABLE, _ref5) >= 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 === '[' && (_ref6 = prev[0], __indexOf.call(INDEXABLE, _ref6) >= 0)) {
tag = 'INDEX_START';
switch (prev[0]) {
case '?':
@@ -347,21 +380,33 @@
}
}
}
switch (value) {
case '(':
case '{':
case '[':
this.ends.push(INVERSES[value]);
break;
case ')':
case '}':
case ']':
this.pair(value);
}
this.token(tag, value);
return value.length;
};
Lexer.prototype.sanitizeHeredoc = function(doc, options) {
var attempt, herecomment, indent, match, _ref2;
var attempt, herecomment, indent, match, _ref3;
indent = options.indent, herecomment = options.herecomment;
if (herecomment) {
if (HEREDOC_ILLEGAL.test(doc)) {
throw new Error("block comment cannot contain \"*/\", starting on line " + (this.line + 1));
this.error("block comment cannot contain \"*/\", starting");
}
if (doc.indexOf('\n') <= 0) return doc;
} else {
while (match = HEREDOC_INDENT.exec(doc)) {
attempt = match[1];
if (indent === null || (0 < (_ref2 = attempt.length) && _ref2 < indent.length)) {
if (indent === null || (0 < (_ref3 = attempt.length) && _ref3 < indent.length)) {
indent = attempt;
}
}
@@ -370,6 +415,7 @@
if (!herecomment) doc = doc.replace(/^\n/, '');
return doc;
};
Lexer.prototype.tagParameters = function() {
var i, stack, tok, tokens;
if (this.tag() !== ')') return this;
@@ -396,19 +442,15 @@
}
return this;
};
Lexer.prototype.closeIndentation = function() {
return this.outdentToken(this.indent);
};
Lexer.prototype.identifierError = function(word) {
throw SyntaxError("Reserved word \"" + word + "\" on line " + (this.line + 1));
};
Lexer.prototype.assignmentError = function() {
throw SyntaxError("Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned");
};
Lexer.prototype.balancedString = function(str, end) {
var i, letter, match, prev, stack, _ref2;
var i, letter, match, prev, stack, _ref3;
stack = [end];
for (i = 1, _ref2 = str.length; 1 <= _ref2 ? i < _ref2 : i > _ref2; 1 <= _ref2 ? i++ : i--) {
for (i = 1, _ref3 = str.length; 1 <= _ref3 ? i < _ref3 : i > _ref3; 1 <= _ref3 ? i++ : i--) {
switch (letter = str.charAt(i)) {
case '\\':
i++;
@@ -430,10 +472,11 @@
}
prev = letter;
}
throw new Error("missing " + (stack.pop()) + ", starting on line " + (this.line + 1));
return this.error("missing " + (stack.pop()) + ", starting");
};
Lexer.prototype.interpolateString = function(str, options) {
var expr, heredoc, i, inner, interpolated, len, letter, nested, pi, regex, tag, tokens, value, _len, _ref2, _ref3, _ref4;
var expr, heredoc, i, inner, interpolated, len, letter, nested, pi, regex, tag, tokens, value, _len, _ref3, _ref4, _ref5;
if (options == null) options = {};
heredoc = options.heredoc, regex = options.regex;
tokens = [];
@@ -455,7 +498,7 @@
rewrite: false
});
nested.pop();
if (((_ref2 = nested[0]) != null ? _ref2[0] : void 0) === 'TERMINATOR') {
if (((_ref3 = nested[0]) != null ? _ref3[0] : void 0) === 'TERMINATOR') {
nested.shift();
}
if (len = nested.length) {
@@ -475,10 +518,10 @@
if (tokens[0][0] !== 'NEOSTRING') tokens.unshift(['', '']);
if (interpolated = tokens.length > 1) this.token('(', '(');
for (i = 0, _len = tokens.length; i < _len; i++) {
_ref3 = tokens[i], tag = _ref3[0], value = _ref3[1];
_ref4 = tokens[i], tag = _ref4[0], value = _ref4[1];
if (i) this.token('+', '+');
if (tag === 'TOKENS') {
(_ref4 = this.tokens).push.apply(_ref4, value);
(_ref5 = this.tokens).push.apply(_ref5, value);
} else {
this.token('STRING', this.makeString(value, '"', heredoc));
}
@@ -486,24 +529,41 @@
if (interpolated) this.token(')', ')');
return tokens;
};
Lexer.prototype.pair = function(tag) {
var size, wanted;
if (tag !== (wanted = last(this.ends))) {
if ('OUTDENT' !== wanted) this.error("unmatched " + tag);
this.indent -= size = last(this.indents);
this.outdentToken(size, true);
return this.pair(tag);
}
return this.ends.pop();
};
Lexer.prototype.token = function(tag, value) {
return this.tokens.push([tag, value, this.line]);
};
Lexer.prototype.tag = function(index, tag) {
var tok;
return (tok = last(this.tokens, index)) && (tag ? tok[0] = tag : tok[0]);
};
Lexer.prototype.value = function(index, val) {
var tok;
return (tok = last(this.tokens, index)) && (val ? tok[1] = val : tok[1]);
};
Lexer.prototype.unfinished = function() {
var prev, value;
return LINE_CONTINUER.test(this.chunk) || (prev = last(this.tokens, 1)) && prev[0] !== '.' && (value = this.value()) && !value.reserved && NO_NEWLINE.test(value) && !CODE.test(value) && !ASSIGNED.test(this.chunk);
var _ref3;
return LINE_CONTINUER.test(this.chunk) || ((_ref3 = this.tag()) === '\\' || _ref3 === '.' || _ref3 === '?.' || _ref3 === 'UNARY' || _ref3 === 'MATH' || _ref3 === '+' || _ref3 === '-' || _ref3 === 'SHIFT' || _ref3 === 'RELATION' || _ref3 === 'COMPARE' || _ref3 === 'LOGIC' || _ref3 === 'COMPOUND_ASSIGN' || _ref3 === 'THROW' || _ref3 === 'EXTENDS');
};
Lexer.prototype.escapeLines = function(str, heredoc) {
return str.replace(MULTILINER, heredoc ? '\\n' : '');
};
Lexer.prototype.makeString = function(body, quote, heredoc) {
if (!body) return quote + quote;
body = body.replace(/\\([\s\S])/g, function(match, contents) {
@@ -516,10 +576,19 @@
body = body.replace(RegExp("" + quote, "g"), '\\$&');
return quote + this.escapeLines(body, heredoc) + quote;
};
Lexer.prototype.error = function(message) {
throw SyntaxError("" + message + " on line " + (this.line + 1));
};
return Lexer;
})();
JS_KEYWORDS = ['true', 'false', 'null', 'this', 'new', 'delete', 'typeof', 'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally', 'class', 'extends', 'super'];
COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when'];
COFFEE_ALIAS_MAP = {
and: '&&',
or: '||',
@@ -531,6 +600,7 @@
on: 'true',
off: 'false'
};
COFFEE_ALIASES = (function() {
var _results;
_results = [];
@@ -539,41 +609,75 @@
}
return _results;
})();
COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat(COFFEE_ALIASES);
RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice', '__bind', '__indexOf'];
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED);
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS);
IDENTIFIER = /^([$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)([^\n\S]*:(?!:))?/;
NUMBER = /^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;
WHITESPACE = /^[^\n\S]+/;
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/;
CODE = /^[-=]>/;
MULTI_DENT = /^(?:\n[^\n\S]*)+/;
SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/;
JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/;
REGEX = /^\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/[imgy]{0,4}(?!\w)/;
REGEX = /^(\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/)([imgy]{0,4})(?!\w)/;
HEREGEX = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?!\w)/;
HEREGEX_OMIT = /\s+(?:#.*)?/g;
MULTILINER = /\n/g;
HEREDOC_INDENT = /\n+([^\n\S]*)/g;
HEREDOC_ILLEGAL = /\*\//;
ASSIGNED = /^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/;
LINE_CONTINUER = /^\s*(?:,|\??\.(?![.\d])|::)/;
TRAILING_SPACES = /\s+$/;
NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/;
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO'];
LOGIC = ['&&', '||', '&', '|', '^'];
SHIFT = ['<<', '>>', '>>>'];
COMPARE = ['==', '!=', '<', '>', '<=', '>='];
MATH = ['*', '/', '%'];
RELATION = ['IN', 'OF', 'INSTANCEOF'];
BOOL = ['TRUE', 'FALSE', 'NULL', 'UNDEFINED'];
NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', '++', '--', ']'];
NOT_SPACED_REGEX = NOT_REGEX.concat(')', '}', 'THIS', 'IDENTIFIER', 'STRING');
CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER'];
INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL');
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
}).call(this);

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,27 @@
(function() {
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments;
exports.OptionParser = OptionParser = (function() {
function OptionParser(rules, banner) {
this.banner = banner;
this.rules = buildRules(rules);
}
OptionParser.prototype.parse = function(args) {
var arg, i, isOption, matchedRule, options, rule, value, _i, _len, _len2, _ref;
var arg, i, isOption, matchedRule, options, originalArgs, pos, rule, value, _i, _len, _len2, _ref;
options = {
arguments: [],
literals: []
};
originalArgs = args;
args = normalizeArguments(args);
for (i = 0, _len = args.length; i < _len; i++) {
arg = args[i];
if (arg === '--') {
options.literals = args.slice(i + 1);
pos = originalArgs.indexOf('--');
options.arguments = [originalArgs[1 + pos]];
options.literals = originalArgs.slice(2 + pos);
break;
}
isOption = !!(arg.match(LONG_FLAG) || arg.match(SHORT_FLAG));
@@ -34,12 +40,13 @@
throw new Error("unrecognized option: " + arg);
}
if (!isOption) {
options.arguments = args.slice(i);
options.arguments = originalArgs.slice(originalArgs.indexOf(arg));
break;
}
}
return options;
};
OptionParser.prototype.help = function() {
var letPart, lines, rule, spaces, _i, _len, _ref;
lines = [];
@@ -54,12 +61,19 @@
}
return "\n" + (lines.join('\n')) + "\n";
};
return OptionParser;
})();
LONG_FLAG = /^(--\w[\w\-]+)/;
SHORT_FLAG = /^(-\w)/;
MULTI_FLAG = /^-(\w{2,})/;
OPTIONAL = /\[(\w+(\*?))\]/;
buildRules = function(rules) {
var tuple, _i, _len, _results;
_results = [];
@@ -70,6 +84,7 @@
}
return _results;
};
buildRule = function(shortFlag, longFlag, description, options) {
var match;
if (options == null) options = {};
@@ -84,6 +99,7 @@
isList: !!(match && match[2])
};
};
normalizeArguments = function(args) {
var arg, l, match, result, _i, _j, _len, _len2, _ref;
args = args.slice(0);
@@ -102,4 +118,5 @@
}
return result;
};
}).call(this);

View File

@@ -1,22 +1,47 @@
(function() {
var ACCESSOR, CoffeeScript, Module, REPL_PROMPT, REPL_PROMPT_CONTINUATION, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, enableColours, error, getCompletions, inspect, readline, repl, run, stdin, stdout;
var ACCESSOR, CoffeeScript, Module, REPL_PROMPT, REPL_PROMPT_CONTINUATION, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, enableColours, error, g, getCompletions, inspect, nonContextGlobals, readline, repl, run, sandbox, stdin, stdout, _i, _len;
CoffeeScript = require('./coffee-script');
readline = require('readline');
inspect = require('util').inspect;
Script = require('vm').Script;
Module = require('module');
REPL_PROMPT = 'coffee> ';
REPL_PROMPT_CONTINUATION = '......> ';
enableColours = false;
if (process.platform !== 'win32') {
enableColours = !process.env.NODE_DISABLE_COLORS;
}
stdin = process.openStdin();
stdout = process.stdout;
error = function(err) {
return stdout.write((err.stack || err.toString()) + '\n\n');
return stdout.write((err.stack || err.toString()) + '\n');
};
backlog = '';
sandbox = Script.createContext();
nonContextGlobals = ['Buffer', 'console', 'process', 'setInterval', 'clearInterval', 'setTimeout', 'clearTimeout'];
for (_i = 0, _len = nonContextGlobals.length; _i < _len; _i++) {
g = nonContextGlobals[_i];
sandbox[g] = global[g];
}
sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox;
run = function(buffer) {
var code, returnValue, _;
if (!buffer.toString().trim() && !backlog) {
@@ -33,13 +58,14 @@
repl.setPrompt(REPL_PROMPT);
backlog = '';
try {
_ = global._;
_ = sandbox._;
returnValue = CoffeeScript.eval("_=(" + code + "\n)", {
sandbox: sandbox,
filename: 'repl',
modulename: 'repl'
});
if (returnValue === void 0) {
global._ = _;
sandbox._ = _;
} else {
process.stdout.write(inspect(returnValue, false, 2, enableColours) + '\n');
}
@@ -48,17 +74,21 @@
}
return repl.prompt();
};
ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/;
SIMPLEVAR = /\s*(\w*)$/i;
autocomplete = function(text) {
return completeAttribute(text) || completeVariable(text) || [[], text];
};
completeAttribute = function(text) {
var all, completions, match, obj, prefix, val;
if (match = text.match(ACCESSOR)) {
all = match[0], obj = match[1], prefix = match[2];
try {
val = Script.runInThisContext(obj);
val = Script.runInContext(obj, sandbox);
} catch (error) {
return;
}
@@ -66,17 +96,18 @@
return [completions, prefix];
}
};
completeVariable = function(text) {
var completions, free, keywords, possibilities, r, vars, _ref;
free = (_ref = text.match(SIMPLEVAR)) != null ? _ref[1] : void 0;
if (free != null) {
vars = Script.runInThisContext('Object.getOwnPropertyNames(this)');
vars = Script.runInContext('Object.getOwnPropertyNames(this)', sandbox);
keywords = (function() {
var _i, _len, _ref2, _results;
var _j, _len2, _ref2, _results;
_ref2 = CoffeeScript.RESERVED;
_results = [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
r = _ref2[_i];
for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) {
r = _ref2[_j];
if (r.slice(0, 2) !== '__') _results.push(r);
}
return _results;
@@ -86,16 +117,19 @@
return [completions, free];
}
};
getCompletions = function(prefix, candidates) {
var el, _i, _len, _results;
var el, _j, _len2, _results;
_results = [];
for (_i = 0, _len = candidates.length; _i < _len; _i++) {
el = candidates[_i];
for (_j = 0, _len2 = candidates.length; _j < _len2; _j++) {
el = candidates[_j];
if (el.indexOf(prefix) === 0) _results.push(el);
}
return _results;
};
process.on('uncaughtException', error);
if (readline.createInterface.length < 3) {
repl = readline.createInterface(stdin, autocomplete);
stdin.on('data', function(buffer) {
@@ -104,6 +138,7 @@
} else {
repl = readline.createInterface(stdin, stdout, autocomplete);
}
repl.on('attemptClose', function() {
if (backlog) {
backlog = '';
@@ -114,11 +149,16 @@
return repl.close();
}
});
repl.on('close', function() {
process.stdout.write('\n');
return stdin.destroy();
});
repl.on('line', run);
repl.setPrompt(REPL_PROMPT);
repl.prompt();
}).call(this);

View File

@@ -1,13 +1,11 @@
(function() {
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, left, rite, _i, _len, _ref;
var __hasProp = Object.prototype.hasOwnProperty, __indexOf = Array.prototype.indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (__hasProp.call(this, i) && this[i] === item) return i;
}
return -1;
}, __slice = Array.prototype.slice;
var __hasProp = Object.prototype.hasOwnProperty, __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; }, __slice = Array.prototype.slice;
exports.Rewriter = (function() {
function Rewriter() {}
Rewriter.prototype.rewrite = function(tokens) {
this.tokens = tokens;
this.removeLeadingNewlines();
@@ -18,10 +16,9 @@
this.tagPostfixConditionals();
this.addImplicitBraces();
this.addImplicitParentheses();
this.ensureBalance(BALANCED_PAIRS);
this.rewriteClosingParens();
return this.tokens;
};
Rewriter.prototype.scanTokens = function(block) {
var i, token, tokens;
tokens = this.tokens;
@@ -31,6 +28,7 @@
}
return true;
};
Rewriter.prototype.detectEnd = function(i, condition, action) {
var levels, token, tokens, _ref, _ref2;
tokens = this.tokens;
@@ -49,6 +47,7 @@
}
return i - 1;
};
Rewriter.prototype.removeLeadingNewlines = function() {
var i, tag, _len, _ref;
_ref = this.tokens;
@@ -58,6 +57,7 @@
}
if (i) return this.tokens.splice(0, i);
};
Rewriter.prototype.removeMidExpressionNewlines = function() {
return this.scanTokens(function(token, i, tokens) {
var _ref;
@@ -68,6 +68,7 @@
return 0;
});
};
Rewriter.prototype.closeOpenCalls = function() {
var action, condition;
condition = function(token, i) {
@@ -82,6 +83,7 @@
return 1;
});
};
Rewriter.prototype.closeOpenIndexes = function() {
var action, condition;
condition = function(token, i) {
@@ -96,6 +98,7 @@
return 1;
});
};
Rewriter.prototype.addImplicitBraces = function() {
var action, condition, stack, start, startIndent;
stack = [];
@@ -103,7 +106,7 @@
startIndent = 0;
condition = function(token, i) {
var one, tag, three, two, _ref, _ref2;
_ref = this.tokens.slice(i + 1, (i + 3 + 1) || 9e9), one = _ref[0], two = _ref[1], three = _ref[2];
_ref = this.tokens.slice(i + 1, (i + 3) + 1 || 9e9), one = _ref[0], two = _ref[1], three = _ref[2];
if ('HERECOMMENT' === (one != null ? one[0] : void 0)) return false;
tag = token[0];
return ((tag === 'TERMINATOR' || tag === 'OUTDENT') && !((two != null ? two[0] : void 0) === ':' || (one != null ? one[0] : void 0) === '@' && (three != null ? three[0] : void 0) === ':')) || (tag === ',' && one && ((_ref2 = one[0]) !== 'IDENTIFIER' && _ref2 !== 'NUMBER' && _ref2 !== 'STRING' && _ref2 !== '@' && _ref2 !== 'TERMINATOR' && _ref2 !== 'OUTDENT'));
@@ -141,19 +144,18 @@
return 2;
});
};
Rewriter.prototype.addImplicitParentheses = function() {
var action, noCall;
noCall = false;
action = function(token, i) {
var idx;
idx = token[0] === 'OUTDENT' ? i + 1 : i;
return this.tokens.splice(idx, 0, ['CALL_END', ')', token[2]]);
return this.tokens.splice(i, 0, ['CALL_END', ')', token[2]]);
};
return this.scanTokens(function(token, i, tokens) {
var callObject, current, next, prev, seenControl, seenSingle, tag, _ref, _ref2, _ref3;
tag = token[0];
if (tag === 'CLASS' || tag === 'IF') noCall = true;
_ref = tokens.slice(i - 1, (i + 1 + 1) || 9e9), prev = _ref[0], current = _ref[1], next = _ref[2];
_ref = tokens.slice(i - 1, (i + 1) + 1 || 9e9), prev = _ref[0], current = _ref[1], next = _ref[2];
callObject = !noCall && tag === 'INDENT' && next && next.generated && next[0] === '{' && prev && (_ref2 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref2) >= 0);
seenSingle = false;
seenControl = false;
@@ -183,6 +185,7 @@
return 2;
});
};
Rewriter.prototype.addImplicitIndentation = function() {
return this.scanTokens(function(token, i, tokens) {
var action, condition, indent, outdent, starter, tag, _ref, _ref2;
@@ -219,6 +222,7 @@
return 1;
});
};
Rewriter.prototype.tagPostfixConditionals = function() {
var condition;
condition = function(token, i) {
@@ -235,91 +239,50 @@
return 1;
});
};
Rewriter.prototype.ensureBalance = function(pairs) {
var close, level, levels, open, openLine, tag, token, _i, _j, _len, _len2, _ref, _ref2;
levels = {};
openLine = {};
_ref = this.tokens;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
token = _ref[_i];
tag = token[0];
for (_j = 0, _len2 = pairs.length; _j < _len2; _j++) {
_ref2 = pairs[_j], open = _ref2[0], close = _ref2[1];
levels[open] |= 0;
if (tag === open) {
if (levels[open]++ === 0) openLine[open] = token[2];
} else if (tag === close && --levels[open] < 0) {
throw Error("too many " + token[1] + " on line " + (token[2] + 1));
}
}
}
for (open in levels) {
level = levels[open];
if (level > 0) {
throw Error("unclosed " + open + " on line " + (openLine[open] + 1));
}
}
return this;
};
Rewriter.prototype.rewriteClosingParens = function() {
var debt, key, stack;
stack = [];
debt = {};
for (key in INVERSES) {
debt[key] = 0;
}
return this.scanTokens(function(token, i, tokens) {
var inv, match, mtag, oppos, tag, val, _ref;
if (_ref = (tag = token[0]), __indexOf.call(EXPRESSION_START, _ref) >= 0) {
stack.push(token);
return 1;
}
if (__indexOf.call(EXPRESSION_END, tag) < 0) return 1;
if (debt[inv = INVERSES[tag]] > 0) {
debt[inv] -= 1;
tokens.splice(i, 1);
return 0;
}
match = stack.pop();
mtag = match[0];
oppos = INVERSES[mtag];
if (tag === oppos) return 1;
debt[mtag] += 1;
val = [oppos, mtag === 'INDENT' ? match[1] : oppos];
if (this.tag(i + 2) === mtag) {
tokens.splice(i + 3, 0, val);
stack.push(match);
} else {
tokens.splice(i, 0, val);
}
return 1;
});
};
Rewriter.prototype.indentation = function(token) {
return [['INDENT', 2, token[2]], ['OUTDENT', 2, token[2]]];
};
Rewriter.prototype.tag = function(i) {
var _ref;
return (_ref = this.tokens[i]) != null ? _ref[0] : void 0;
};
return Rewriter;
})();
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['CALL_START', 'CALL_END'], ['PARAM_START', 'PARAM_END'], ['INDEX_START', 'INDEX_END']];
INVERSES = {};
exports.INVERSES = INVERSES = {};
EXPRESSION_START = [];
EXPRESSION_END = [];
for (_i = 0, _len = BALANCED_PAIRS.length; _i < _len; _i++) {
_ref = BALANCED_PAIRS[_i], left = _ref[0], rite = _ref[1];
EXPRESSION_START.push(INVERSES[rite] = left);
EXPRESSION_END.push(INVERSES[left] = rite);
}
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'UNARY', 'SUPER', '@', '->', '=>', '[', '(', '{', '--', '++'];
IMPLICIT_UNSPACED_CALL = ['+', '-'];
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ','];
IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR'];
SINGLE_LINERS = ['ELSE', '->', '=>', 'TRY', 'FINALLY', 'THEN'];
SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN'];
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT'];
}).call(this);

View File

@@ -1,8 +1,12 @@
(function() {
var Scope, extend, last, _ref;
_ref = require('./helpers'), extend = _ref.extend, last = _ref.last;
exports.Scope = Scope = (function() {
Scope.root = null;
function Scope(parent, expressions, method) {
this.parent = parent;
this.expressions = expressions;
@@ -16,6 +20,7 @@
this.positions = {};
if (!this.parent) Scope.root = this;
}
Scope.prototype.add = function(name, type, immediate) {
var pos;
if (this.shared && !immediate) return this.parent.add(name, type, immediate);
@@ -28,21 +33,25 @@
}) - 1;
}
};
Scope.prototype.find = function(name, options) {
if (this.check(name, options)) return true;
this.add(name, 'var');
return false;
};
Scope.prototype.parameter = function(name) {
if (this.shared && this.parent.check(name, true)) return;
return this.add(name, 'param');
};
Scope.prototype.check = function(name, immediate) {
var found, _ref2;
found = !!this.type(name);
if (found || immediate) return found;
return !!((_ref2 = this.parent) != null ? _ref2.check(name) : void 0);
};
Scope.prototype.temporary = function(name, index) {
if (name.length > 1) {
return '_' + name + (index > 1 ? index : '');
@@ -50,6 +59,7 @@
return '_' + (index + parseInt(name, 36)).toString(36).replace(/\d/g, 'a');
}
};
Scope.prototype.type = function(name) {
var v, _i, _len, _ref2;
_ref2 = this.variables;
@@ -59,6 +69,7 @@
}
return null;
};
Scope.prototype.freeVariable = function(type) {
var index, temp;
index = 0;
@@ -68,6 +79,7 @@
this.add(temp, 'var', true);
return temp;
};
Scope.prototype.assign = function(name, value) {
this.add(name, {
value: value,
@@ -75,9 +87,11 @@
});
return this.hasAssignments = true;
};
Scope.prototype.hasDeclarations = function() {
return !!this.declaredVariables().length;
};
Scope.prototype.declaredVariables = function() {
var realVars, tempVars, v, _i, _len, _ref2;
realVars = [];
@@ -91,6 +105,7 @@
}
return realVars.sort().concat(tempVars.sort());
};
Scope.prototype.assignedVariables = function() {
var v, _i, _len, _ref2, _results;
_ref2 = this.variables;
@@ -101,6 +116,9 @@
}
return _results;
};
return Scope;
})();
}).call(this);

View File

@@ -39,19 +39,19 @@ helpers.extend global,
missingTask name unless tasks[name]
tasks[name].action options
# Run `cake`. Executes all of the tasks you pass, in order. Note that Node's
# asynchrony may cause tasks to execute in a different order than you'd expect.
# If no tasks are passed, print the help screen.
# If no tasks are passed, print the help screen. Keep a reference to the
# original directory name, when running Cake tasks from subdirectories.
exports.run = ->
path.exists 'Cakefile', (exists) ->
throw new Error("Cakefile not found in #{process.cwd()}") unless exists
args = process.argv.slice 2
CoffeeScript.run fs.readFileSync('Cakefile').toString(), filename: 'Cakefile'
oparse = new optparse.OptionParser switches
return printTasks() unless args.length
options = oparse.parse(args)
invoke arg for arg in options.arguments
global.__originalDirname = fs.realpathSync '.'
process.chdir cakefileDirectory __originalDirname
args = process.argv.slice 2
CoffeeScript.run fs.readFileSync('Cakefile').toString(), filename: 'Cakefile'
oparse = new optparse.OptionParser switches
return printTasks() unless args.length
options = oparse.parse(args)
invoke arg for arg in options.arguments
# Display the list of Cake tasks in a format similar to `rake -T`
printTasks = ->
@@ -67,3 +67,11 @@ printTasks = ->
missingTask = (task) ->
console.log "No such task: \"#{task}\""
process.exit 1
# When `cake` is invoked, search in the current and all parent directories
# to find the relevant Cakefile.
cakefileDirectory = (dir) ->
return dir if path.existsSync path.join dir, 'Cakefile'
parent = path.normalize path.join dir, '..'
return cakefileDirectory parent unless parent is dir
throw new Error "Cakefile not found in #{process.cwd()}"

View File

@@ -22,26 +22,28 @@ printWarn = (line) -> process.binding('stdio').writeError line + '\n'
# The help banner that is printed when `coffee` is called without arguments.
BANNER = '''
Usage: coffee [options] path/to/script.coffee
If called without options, `coffee` will run your script.
'''
# The list of all the valid option flags that `coffee` knows how to handle.
SWITCHES = [
['-b', '--bare', 'compile without a top-level function wrapper']
['-c', '--compile', 'compile to JavaScript and save as .js files']
['-i', '--interactive', 'run an interactive CoffeeScript REPL']
['-o', '--output [DIR]', 'set the directory for compiled JavaScript']
['-j', '--join [FILE]', 'concatenate the scripts before compiling']
['-w', '--watch', 'watch scripts for changes, and recompile']
['-p', '--print', 'print the compiled JavaScript to stdout']
['-l', '--lint', 'pipe the compiled JavaScript through JavaScript Lint']
['-s', '--stdio', 'listen for and compile scripts over stdio']
['-e', '--eval', 'compile a string from the command line']
['-r', '--require [FILE*]', 'require a library before executing your script']
['-b', '--bare', 'compile without the top-level function wrapper']
['-t', '--tokens', 'print the tokens that the lexer produces']
['-n', '--nodes', 'print the parse tree that Jison produces']
[ '--nodejs [ARGS]', 'pass options through to the "node" binary']
['-v', '--version', 'display CoffeeScript version']
['-e', '--eval', 'pass a string from the command line as input']
['-h', '--help', 'display this help message']
['-i', '--interactive', 'run an interactive CoffeeScript REPL']
['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling']
['-l', '--lint', 'pipe the compiled JavaScript through JavaScript Lint']
['-n', '--nodes', 'print out the parse tree that the parser produces']
[ '--nodejs [ARGS]', 'pass options directly to the "node" binary']
['-o', '--output [DIR]', 'set the output directory for compiled JavaScript']
['-p', '--print', 'print out the compiled JavaScript']
['-r', '--require [FILE*]', 'require a library before executing your script']
['-s', '--stdio', 'listen for and compile scripts over stdio']
['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce']
['-v', '--version', 'display the version number']
['-w', '--watch', 'watch scripts for changes and rerun commands']
]
# Top-level objects shared by all the functions.

View File

@@ -7,7 +7,7 @@
#
# Which is a format that can be fed directly into [Jison](http://github.com/zaach/jison).
{Rewriter} = require './rewriter'
{Rewriter, INVERSES} = require './rewriter'
# Import the helpers we need.
{count, starts, compact, last} = require './helpers'
@@ -41,6 +41,7 @@ exports.Lexer = class Lexer
@indebt = 0 # The over-indentation at the current level.
@outdebt = 0 # The under-outdentation at the current level.
@indents = [] # The stack of all current indentation levels.
@ends = [] # The stack for pairing up tokens.
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, line]`.
# At every position, run through this list of attempted matches,
@@ -60,6 +61,7 @@ exports.Lexer = class Lexer
@literalToken()
@closeIndentation()
@error "missing #{tag}" if tag = @ends.pop()
return @tokens if opts.rewrite is off
(new Rewriter).rewrite @tokens
@@ -110,7 +112,7 @@ exports.Lexer = class Lexer
id = new String id
id.reserved = yes
else if id in RESERVED
@identifierError id
@error "reserved word \"#{word}\""
unless forcedIdentifier
id = COFFEE_ALIAS_MAP[id] if id in COFFEE_ALIASES
@@ -196,15 +198,18 @@ exports.Lexer = class Lexer
prev = last @tokens
return 0 if prev and (prev[0] in (if prev.spaced then NOT_REGEX else NOT_SPACED_REGEX))
return 0 unless match = REGEX.exec @chunk
[regex] = match
@token 'REGEX', if regex is '//' then '/(?:)/' else regex
regex.length
[match, regex, flags] = match
if regex[..1] is '/*' then @error 'regular expressions cannot begin with `*`'
if regex is '//' then regex = '/(?:)/'
@token 'REGEX', "#{regex}#{flags}"
match.length
# Matches multiline extended regular expressions.
heregexToken: (match) ->
[heregex, body, flags] = match
if 0 > body.indexOf '#{'
re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/')
if re.match /^\*/ then @error 'regular expressions cannot begin with `*`'
@token 'REGEX', "/#{ re or '(?:)' }/#{flags}"
return heregex.length
@token 'IDENTIFIER', 'RegExp'
@@ -239,6 +244,7 @@ exports.Lexer = class Lexer
return 0 unless match = MULTI_DENT.exec @chunk
indent = match[0]
@line += count indent, '\n'
@seenFor = no
prev = last @tokens, 1
size = indent.length - 1 - indent.lastIndexOf '\n'
noNewlines = @unfinished()
@@ -253,6 +259,7 @@ exports.Lexer = class Lexer
diff = size - @indent + @outdebt
@token 'INDENT', diff
@indents.push diff
@ends .push 'OUTDENT'
@outdebt = @indebt = 0
else
@indebt = 0
@@ -262,7 +269,7 @@ exports.Lexer = class Lexer
# Record an outdent token or multiple tokens, if we happen to be moving back
# inwards past several recorded indents.
outdentToken: (moveOut, noNewlines, close) ->
outdentToken: (moveOut, noNewlines) ->
while moveOut > 0
len = @indents.length - 1
if @indents[len] is undefined
@@ -277,8 +284,10 @@ exports.Lexer = class Lexer
dent = @indents.pop() - @outdebt
moveOut -= dent
@outdebt = 0
@pair 'OUTDENT'
@token 'OUTDENT', dent
@outdebt -= moveOut if dent
@tokens.pop() while @value() is ';'
@token 'TERMINATOR', '\n' unless @tag() is 'TERMINATOR' or noNewlines
this
@@ -293,6 +302,7 @@ exports.Lexer = class Lexer
# Generate a newline token. Consecutive newlines get merged together.
newlineToken: ->
@tokens.pop() while @value() is ';'
@token 'TERMINATOR', '\n' unless @tag() is 'TERMINATOR'
this
@@ -316,12 +326,15 @@ exports.Lexer = class Lexer
tag = value
prev = last @tokens
if value is '=' and prev
@assignmentError() if not prev[1].reserved and prev[1] in JS_FORBIDDEN
if not prev[1].reserved and prev[1] in JS_FORBIDDEN
@error "reserved word \"#{@value()}\" can't be assigned"
if prev[1] in ['||', '&&']
prev[0] = 'COMPOUND_ASSIGN'
prev[1] += '='
return value.length
if value is ';' then tag = 'TERMINATOR'
if value is ';'
@seenFor = no
tag = 'TERMINATOR'
else if value in MATH then tag = 'MATH'
else if value in COMPARE then tag = 'COMPARE'
else if value in COMPOUND_ASSIGN then tag = 'COMPOUND_ASSIGN'
@@ -336,6 +349,9 @@ exports.Lexer = class Lexer
tag = 'INDEX_START'
switch prev[0]
when '?' then prev[0] = 'INDEX_SOAK'
switch value
when '(', '{', '[' then @ends.push INVERSES[value]
when ')', '}', ']' then @pair value
@token tag, value
value.length
@@ -348,7 +364,7 @@ exports.Lexer = class Lexer
{indent, herecomment} = options
if herecomment
if HEREDOC_ILLEGAL.test doc
throw new Error "block comment cannot contain \"*/\", starting on line #{@line + 1}"
@error "block comment cannot contain \"*/\", starting"
return doc if doc.indexOf('\n') <= 0
else
while match = HEREDOC_INDENT.exec doc
@@ -383,16 +399,6 @@ exports.Lexer = class Lexer
closeIndentation: ->
@outdentToken @indent
# The error for when you try to use a forbidden word in JavaScript as
# an identifier.
identifierError: (word) ->
throw SyntaxError "Reserved word \"#{word}\" on line #{@line + 1}"
# The error for when you try to assign to a reserved word in JavaScript,
# like "function" or "default".
assignmentError: ->
throw SyntaxError "Reserved word \"#{@value()}\" on line #{@line + 1} can't be assigned"
# Matches a balanced group such as a single or double-quoted string. Pass in
# a series of delimiters, all of which must be nested correctly within the
# contents of the string. This method allows us to have strings within
@@ -419,8 +425,7 @@ exports.Lexer = class Lexer
else if end is '"' and prev is '#' and letter is '{'
stack.push end = '}'
prev = letter
throw new Error "missing #{ stack.pop() }, starting on line #{ @line + 1 }"
@error "missing #{ stack.pop() }, starting"
# Expand variables and expressions inside double-quoted strings using
# Ruby-like notation for substitution of arbitrary expressions.
@@ -469,6 +474,21 @@ exports.Lexer = class Lexer
@token ')', ')' if interpolated
tokens
# Pairs up a closing token, ensuring that all listed pairs of tokens are
# correctly balanced throughout the course of the token stream.
pair: (tag) ->
unless tag is wanted = last @ends
@error "unmatched #{tag}" unless 'OUTDENT' is wanted
# Auto-close INDENT to support syntax like this:
#
# el.click((event) ->
# el.hide())
#
@indent -= size = last @indents
@outdentToken size, true
return @pair tag
@ends.pop()
# Helpers
# -------
@@ -487,9 +507,8 @@ exports.Lexer = class Lexer
# Are we in the midst of an unfinished expression?
unfinished: ->
LINE_CONTINUER.test(@chunk) or
(prev = last @tokens, 1) and prev[0] isnt '.' and
(value = @value()) and not value.reserved and
NO_NEWLINE.test(value) and not CODE.test(value) and not ASSIGNED.test(@chunk)
@tag() in ['\\', '.', '?.', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION'
'COMPARE', 'LOGIC', 'COMPOUND_ASSIGN', 'THROW', 'EXTENDS']
# Converts newlines for string literals.
escapeLines: (str, heredoc) ->
@@ -502,6 +521,10 @@ exports.Lexer = class Lexer
if contents in ['\n', quote] then contents else match
body = body.replace /// #{quote} ///g, '\\$&'
quote + @escapeLines(body, heredoc) + quote
# Throws a syntax error on the current `@line`.
error: (message) ->
throw SyntaxError "#{message} on line #{ @line + 1}"
# Constants
# ---------
@@ -584,7 +607,7 @@ JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/
# Regex-matching-regexes.
REGEX = /// ^
/ (?! [\s=] ) # disallow leading whitespace or equals signs
(/ (?! [\s=] ) # disallow leading whitespace or equals signs
[^ [ / \n \\ ]* # every other thing
(?:
(?: \\[\s\S] # anything escaped
@@ -594,7 +617,7 @@ REGEX = /// ^
]
) [^ [ / \n \\ ]*
)*
/ [imgy]{0,4} (?!\w)
/) ([imgy]{0,4}) (?!\w)
///
HEREGEX = /// ^ /{3} ([\s\S]+?) /{3} ([imgy]{0,4}) (?!\w) ///
@@ -608,18 +631,10 @@ HEREDOC_INDENT = /\n+([^\n\S]*)/g
HEREDOC_ILLEGAL = /\*\//
ASSIGNED = /^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/
LINE_CONTINUER = /// ^ \s* (?: , | \??\.(?![.\d]) | :: ) ///
TRAILING_SPACES = /\s+$/
NO_NEWLINE = /// ^ (?: # non-capturing group
[-+*&|/%=<>!.\\][<>=&|]* | # symbol operators
and | or | is(?:nt)? | n(?:ot|ew) | # word operators
delete | typeof | instanceof
) $ ///
# Compound assignment tokens.
COMPOUND_ASSIGN = [
'-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='

View File

@@ -76,8 +76,12 @@ exports.Base = class Base
# Construct a node that returns the current node's result.
# Note that this is overridden for smarter behavior for
# many statement nodes (e.g. If, For)...
makeReturn: ->
new Return this
makeReturn: (res) ->
me = @unwrapAll()
if res
new Call new Literal("#{res}.push"), [me]
else
new Return me
# Does this node, or any of its children, contain a node of a certain kind?
# Recursively traverses down the *children* of the nodes, yielding to a block
@@ -191,12 +195,12 @@ exports.Block = class Block extends Base
# A Block node does not return its entire body, rather it
# ensures that the final expression is returned.
makeReturn: ->
makeReturn: (res) ->
len = @expressions.length
while len--
expr = @expressions[len]
if expr not instanceof Comment
@expressions[len] = expr.makeReturn()
@expressions[len] = expr.makeReturn res
@expressions.splice(len, 1) if expr instanceof Return and not expr.expression
break
this
@@ -226,7 +230,11 @@ exports.Block = class Block extends Base
codes.push if node.isStatement o then code else "#{@tab}#{code};"
else
codes.push node.compile o, LEVEL_LIST
return codes.join '\n' if top
if top
if @spaced
return '\n' + codes.join('\n\n') + '\n'
else
return codes.join '\n'
code = codes.join(', ') or 'void 0'
if codes.length > 1 and o.level >= LEVEL_LIST then "(#{code})" else code
@@ -238,8 +246,11 @@ exports.Block = class Block extends Base
o.indent = @tab = if o.bare then '' else TAB
o.scope = new Scope null, this, null
o.level = LEVEL_TOP
@spaced = yes
code = @compileWithDeclarations o
if o.bare then code else "(function() {\n#{code}\n}).call(this);\n"
# the `1` below accounts for `arguments`, always "in scope"
return code if o.bare or o.scope.variables.length <= 1
"(function() {\n#{code}\n}).call(this);\n"
# Compile the expressions body for the contents of a function, with
# declarations of all inner variables pushed up to the top.
@@ -281,7 +292,7 @@ exports.Literal = class Literal extends Base
constructor: (@value) ->
makeReturn: ->
if @isStatement() then this else new Return this
if @isStatement() then this else super
isAssignable: ->
IDENTIFIER.test @value
@@ -301,6 +312,8 @@ exports.Literal = class Literal extends Base
compileNode: (o) ->
code = if @isUndefined
if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0'
else if @value is 'this'
if o.scope.method?.bound then o.scope.method.context else @value
else if @value.reserved and "#{@value}" not in ['eval', 'arguments']
"\"#{@value}\""
else
@@ -374,9 +387,6 @@ exports.Value = class Value extends Base
isSplice: ->
last(@properties) instanceof Slice
makeReturn: ->
if @properties.length then super() else @base.makeReturn()
# The value can be unwrapped as its inner node, if there are no attached
# properties.
unwrap: ->
@@ -442,7 +452,7 @@ exports.Comment = class Comment extends Base
makeReturn: THIS
compileNode: (o, level) ->
code = '/*' + multident(@comment, @tab) + "\n#{@tab}*/\n"
code = '/*' + multident(@comment, @tab) + "\n#{@tab}*/"
code = o.indent + code if (level or o.level) is LEVEL_TOP
code
@@ -722,14 +732,14 @@ exports.Slice = class Slice extends Base
compileNode: (o) ->
{to, from} = @range
fromStr = from and from.compile(o, LEVEL_PAREN) or '0'
compiled = to and to.compile o, LEVEL_PAREN
compiled = to and to.compile o, LEVEL_ACCESS
if to and not (not @range.exclusive and +compiled is -1)
toStr = ', ' + if @range.exclusive
compiled
else if SIMPLENUM.test compiled
(+compiled + 1).toString()
else
"(#{compiled} + 1) || 9e9"
"#{compiled} + 1 || 9e9"
".slice(#{ fromStr }#{ toStr or '' })"
#### Obj
@@ -902,6 +912,7 @@ exports.Class = class Class extends Base
@setContext name
@walkBody name, o
@ensureConstructor name
@body.spaced = yes
@body.expressions.unshift new Extends lname, @parent if @parent
@body.expressions.unshift @ctor unless @ctor instanceof Code
@body.expressions.push lname
@@ -918,6 +929,7 @@ exports.Class = class Class extends Base
exports.Assign = class Assign extends Base
constructor: (@variable, @value, @context, options) ->
@param = options and options.param
@subpattern = options and options.subpattern
children: ['variable', 'value']
@@ -940,13 +952,14 @@ exports.Assign = class Assign extends Base
return @compileSplice o if @variable.isSplice()
return @compileConditional o if @context in ['||=', '&&=', '?=']
name = @variable.compile o, LEVEL_LIST
unless @context or @variable.isAssignable()
throw SyntaxError "\"#{ @variable.compile o }\" cannot be assigned."
unless @context or isValue and (@variable.namespaced or @variable.hasProperties())
if @param
o.scope.add name, 'var'
else
o.scope.find name
unless @context
unless (varBase = @variable.unwrapAll()).isAssignable()
throw SyntaxError "\"#{ @variable.compile o }\" cannot be assigned."
unless varBase.hasProperties?()
if @param
o.scope.add name, 'var'
else
o.scope.find name
if @value instanceof Code and match = METHOD_DEF.exec name
@value.klass = match[1] if match[1]
@value.name = match[2] ? match[3] ? match[4] ? match[5]
@@ -1005,9 +1018,8 @@ exports.Assign = class Assign extends Base
else
idx = if obj.this then obj.properties[0].name else obj
if not splat and obj instanceof Splat
unless obj.name.unwrapAll().isAssignable()
throw SyntaxError "\"#{ obj.name.compile(o) }\" cannot be assigned."
name = obj.name.unwrap().value
obj = obj.unwrap()
val = "#{olen} <= #{vvar}.length ? #{ utility 'slice' }.call(#{vvar}, #{i}"
if rest = olen - i - 1
ivar = o.scope.freeVariable 'i'
@@ -1030,8 +1042,8 @@ exports.Assign = class Assign extends Base
val = new Value new Literal(vvar), [new (if acc then Access else Index) idx]
if name? and name in ['arguments','eval'].concat RESERVED
throw new SyntaxError "assignment to a reserved word: #{obj.compile o} = #{val.compile o}"
assigns.push new Assign(obj, val, null, param: @param).compile o, LEVEL_TOP
assigns.push vvar unless top
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compile o, LEVEL_LIST
assigns.push vvar unless top or @subpattern
code = assigns.join ', '
if o.level < LEVEL_LIST then code else "(#{code})"
@@ -1054,7 +1066,7 @@ exports.Assign = class Assign extends Base
to = +to.compile(o) - +fromRef
to += 1 unless exclusive
else
to = to.compile(o) + ' - ' + fromRef
to = to.compile(o, LEVEL_ACCESS) + ' - ' + fromRef
to += ' + 1' unless exclusive
else
to = "9e9"
@@ -1072,7 +1084,7 @@ exports.Code = class Code extends Base
@params = params or []
@body = body or new Block
@bound = tag is 'boundfunc'
@context = 'this' if @bound
@context = '_this' if @bound
children: ['params', 'body']
@@ -1087,7 +1099,7 @@ exports.Code = class Code extends Base
# a closure.
compileNode: (o) ->
o.scope = new Scope o.scope, @body, this
o.scope.shared = del o, 'sharedScope'
o.scope.shared = del(o, 'sharedScope')
o.indent += TAB
delete o.bare
vars = []
@@ -1114,6 +1126,11 @@ exports.Code = class Code extends Base
@body.expressions.unshift exprs... if exprs.length
o.scope.parameter vars[i] = v.compile o for v, i in vars unless splats
@body.makeReturn() unless wasEmpty or @noReturn
if @bound
if o.scope.parent.method?.bound
@bound = o.scope.parent.method.context
else
o.scope.parent.assign '_this', 'this'
idt = o.indent
code = 'function'
code += ' ' + @name if @ctor
@@ -1121,7 +1138,6 @@ exports.Code = class Code extends Base
code += "\n#{ @body.compileWithDeclarations o }\n#{@tab}" unless @body.isEmpty()
code += '}'
return @tab + code if @ctor
return utility('bind') + "(#{code}, #{@context})" if @bound
if @front or (o.level >= LEVEL_ACCESS) then "(#{code})" else code
# Short-circuit `traverseChildren` method to prevent it from crossing scope boundaries
@@ -1175,6 +1191,8 @@ exports.Splat = class Splat extends Base
compile: (o) ->
if @index? then @compileParam o else @name.compile o
unwrap: -> @name
# Utility function that converts an arbitrary number of elements, mixed with
# splats, to a proper array.
@@ -1210,9 +1228,12 @@ exports.While = class While extends Base
isStatement: YES
makeReturn: ->
@returns = yes
this
makeReturn: (res) ->
if res
super
else
@returns = yes
this
addBody: (@body) ->
this
@@ -1234,10 +1255,9 @@ exports.While = class While extends Base
if body.isEmpty()
body = ''
else
if o.level > LEVEL_TOP or @returns
rvar = o.scope.freeVariable 'results'
if @returns
body.makeReturn rvar = o.scope.freeVariable 'results'
set = "#{@tab}#{rvar} = [];\n"
body = Push.wrap rvar, body if body
if @guard
if body.expressions.length > 1
body.expressions.unshift new If (new Parens @guard).invert(), new Literal "continue"
@@ -1360,9 +1380,11 @@ exports.Op = class Op extends Base
# Compile a unary **Op**.
compileUnary: (o) ->
parts = [op = @operator]
plusMinus = op in ['+', '-']
parts.push ' ' if op in ['new', 'typeof', 'delete'] or
op in ['+', '-'] and @first instanceof Op and @first.operator is op
@first = new Parens @first if op is 'new' and @first.isStatement o
plusMinus and @first instanceof Op and @first.operator is op
if (plusMinus && @first instanceof Op) or (op is 'new' and @first.isStatement o)
@first = new Parens @first
parts.push @first.compile o, LEVEL_OP
parts.reverse() if @flip
parts.join ''
@@ -1388,11 +1410,11 @@ exports.In = class In extends Base
@compileLoopTest o
compileOrTest: (o) ->
return "#{!!@negated}" if @array.base.objects.length is 0
[sub, ref] = @object.cache o, LEVEL_OP
[cmp, cnj] = if @negated then [' !== ', ' && '] else [' === ', ' || ']
tests = for item, i in @array.base.objects
(if i then ref else sub) + cmp + item.compile o, LEVEL_ACCESS
return 'false' if tests.length is 0
tests = tests.join cnj
if o.level < LEVEL_OP then tests else "(#{tests})"
@@ -1419,9 +1441,9 @@ exports.Try = class Try extends Base
jumps: (o) -> @attempt.jumps(o) or @recovery?.jumps(o)
makeReturn: ->
@attempt = @attempt .makeReturn() if @attempt
@recovery = @recovery.makeReturn() if @recovery
makeReturn: (res) ->
@attempt = @attempt .makeReturn res if @attempt
@recovery = @recovery.makeReturn res if @recovery
this
# Compilation is more or less as you would expect -- the *finally* clause
@@ -1497,7 +1519,6 @@ exports.Parens = class Parens extends Base
unwrap : -> @body
isComplex : -> @body.isComplex()
makeReturn: -> @body.makeReturn()
compileNode: (o) ->
expr = @body.unwrap()
@@ -1518,7 +1539,7 @@ exports.Parens = class Parens extends Base
# Unlike Python array comprehensions, they can be multi-line, and you can pass
# the current index of the loop as a second parameter. Unlike Ruby blocks,
# you can map and filter in a single pass.
exports.For = class For extends Base
exports.For = class For extends While
constructor: (body, source) ->
{@source, @guard, @step, @name, @index} = source
@body = Block.wrap [body]
@@ -1534,14 +1555,6 @@ exports.For = class For extends Base
children: ['body', 'source', 'guard', 'step']
isStatement: YES
jumps: While::jumps
makeReturn: ->
@returns = yes
this
# Welcome to the hairiest method in all of CoffeeScript. Handles the inner
# loop, filtering, stepping, and result saving for array, object, and range
# comprehensions. Some of the generated code can be shared in common, and
@@ -1582,7 +1595,7 @@ exports.For = class For extends Base
if @returns
resultPart = "#{@tab}#{rvar} = [];\n"
returnResult = "\n#{@tab}return #{rvar};"
body = Push.wrap rvar, body
body.makeReturn rvar
if @guard
if body.expressions.length > 1
body.expressions.unshift new If (new Parens @guard).invert(), new Literal "continue"
@@ -1636,9 +1649,10 @@ exports.Switch = class Switch extends Base
return block if block.jumps o
@otherwise?.jumps o
makeReturn: ->
pair[1].makeReturn() for pair in @cases
@otherwise?.makeReturn()
makeReturn: (res) ->
pair[1].makeReturn res for pair in @cases
@otherwise or= new Block [new Literal 'void 0'] if res
@otherwise?.makeReturn res
this
compileNode: (o) ->
@@ -1696,9 +1710,10 @@ exports.If = class If extends Base
compileNode: (o) ->
if @isStatement o then @compileStatement o else @compileExpression o
makeReturn: ->
@body and= new Block [@body.makeReturn()]
@elseBody and= new Block [@elseBody.makeReturn()]
makeReturn: (res) ->
@elseBody or= new Block [new Literal 'void 0'] if res
@body and= new Block [@body.makeReturn res]
@elseBody and= new Block [@elseBody.makeReturn res]
this
ensureBlock: (node) ->
@@ -1752,15 +1767,6 @@ exports.If = class If extends Base
# Faux-nodes are never created by the grammar, but are used during code
# generation to generate other combinations of nodes.
#### Push
# The **Push** creates the tree for `array.push(value)`,
# which is helpful for recording the result arrays from comprehensions.
Push =
wrap: (name, exps) ->
return exps if exps.isEmpty() or last(exps.expressions).jumps()
exps.push new Call new Value(new Literal(name), [new Access new Literal 'push']), [exps.pop()]
#### Closure
# A faux-node used to wrap an expressions body in a closure.
@@ -1803,14 +1809,7 @@ UTILITIES =
# Correctly set up a prototype chain for inheritance, including a reference
# to the superclass for `super()` calls, and copies of any static properties.
extends: -> """
function(child, parent) {
for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
}
function(child, parent) { for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }
"""
# Create a function bound to the current value of "this".
@@ -1820,12 +1819,7 @@ UTILITIES =
# Discover if an item is in an array.
indexOf: -> """
Array.prototype.indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (#{utility 'hasProp'}.call(this, i) && this[i] === item) return i;
}
return -1;
}
Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (#{utility 'hasProp'}.call(this, i) && this[i] === item) return i; } return -1; }
"""
# Shortcuts to speed up the lookup time for native functions.

View File

@@ -25,10 +25,13 @@ exports.OptionParser = class OptionParser
# for interpreting the options object.
parse: (args) ->
options = arguments: [], literals: []
args = normalizeArguments args
originalArgs = args
args = normalizeArguments args
for arg, i in args
if arg is '--'
options.literals = args[(i + 1)..]
pos = originalArgs.indexOf '--'
options.arguments = [originalArgs[1 + pos]]
options.literals = originalArgs[(2 + pos)..]
break
isOption = !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
matchedRule = no
@@ -40,7 +43,7 @@ exports.OptionParser = class OptionParser
break
throw new Error "unrecognized option: #{arg}" if isOption and not matchedRule
if not isOption
options.arguments = args.slice i
options.arguments = originalArgs[(originalArgs.indexOf arg)..]
break
options

View File

@@ -26,7 +26,7 @@ stdout = process.stdout
# Log an error.
error = (err) ->
stdout.write (err.stack or err.toString()) + '\n\n'
stdout.write (err.stack or err.toString()) + '\n'
# The current backlog of multi-line code.
backlog = ''

View File

@@ -3,7 +3,7 @@
# the resulting parse table. Instead of making the parser handle it all, we take
# a series of passes over the token stream, using this **Rewriter** to convert
# shorthand into the unambiguous long form, add implicit indentation and
# parentheses, balance incorrect nestings, and generally clean things up.
# parentheses, and generally clean things up.
# The **Rewriter** class is used by the [Lexer](lexer.html), directly against
# its internal array of tokens.
@@ -26,8 +26,6 @@ class exports.Rewriter
@tagPostfixConditionals()
@addImplicitBraces()
@addImplicitParentheses()
@ensureBalance BALANCED_PAIRS
@rewriteClosingParens()
@tokens
# Rewrite the token stream, looking one token ahead and behind.
@@ -133,9 +131,7 @@ class exports.Rewriter
# deal with them.
addImplicitParentheses: ->
noCall = no
action = (token, i) ->
idx = if token[0] is 'OUTDENT' then i + 1 else i
@tokens.splice idx, 0, ['CALL_END', ')', token[2]]
action = (token, i) -> @tokens.splice i, 0, ['CALL_END', ')', token[2]]
@scanTokens (token, i, tokens) ->
tag = token[0]
noCall = yes if tag in ['CLASS', 'IF']
@@ -211,65 +207,6 @@ class exports.Rewriter
original[0] = 'POST_' + original[0] if token[0] isnt 'INDENT'
1
# Ensure that all listed pairs of tokens are correctly balanced throughout
# the course of the token stream.
ensureBalance: (pairs) ->
levels = {}
openLine = {}
for token in @tokens
[tag] = token
for [open, close] in pairs
levels[open] |= 0
if tag is open
openLine[open] = token[2] if levels[open]++ is 0
else if tag is close and --levels[open] < 0
throw Error "too many #{token[1]} on line #{token[2] + 1}"
for open, level of levels when level > 0
throw Error "unclosed #{ open } on line #{openLine[open] + 1}"
this
# We'd like to support syntax like this:
#
# el.click((event) ->
# el.hide())
#
# In order to accomplish this, move outdents that follow closing parens
# inwards, safely. The steps to accomplish this are:
#
# 1. Check that all paired tokens are balanced and in order.
# 2. Rewrite the stream with a stack: if you see an `EXPRESSION_START`, add it
# to the stack. If you see an `EXPRESSION_END`, pop the stack and replace
# it with the inverse of what we've just popped.
# 3. Keep track of "debt" for tokens that we manufacture, to make sure we end
# up balanced in the end.
# 4. Be careful not to alter array or parentheses delimiters with overzealous
# rewriting.
rewriteClosingParens: ->
stack = []
debt = {}
debt[key] = 0 for key of INVERSES
@scanTokens (token, i, tokens) ->
if (tag = token[0]) in EXPRESSION_START
stack.push token
return 1
return 1 unless tag in EXPRESSION_END
if debt[inv = INVERSES[tag]] > 0
debt[inv] -= 1
tokens.splice i, 1
return 0
match = stack.pop()
mtag = match[0]
oppos = INVERSES[mtag]
return 1 if tag is oppos
debt[mtag] += 1
val = [oppos, if mtag is 'INDENT' then match[1] else oppos]
if @tag(i + 2) is mtag
tokens.splice i + 3, 0, val
stack.push match
else
tokens.splice i, 0, val
1
# Generate the indentation tokens, based on another token on the same line.
indentation: (token) ->
[['INDENT', 2, token[2]], ['OUTDENT', 2, token[2]]]
@@ -293,7 +230,7 @@ BALANCED_PAIRS = [
# The inverse mappings of `BALANCED_PAIRS` we're trying to fix up, so we can
# look things up from either end.
INVERSES = {}
exports.INVERSES = INVERSES = {}
# The tokens that signal the start/end of a balanced pair.
EXPRESSION_START = []

View File

@@ -306,3 +306,28 @@ test "#1591, #1101: splatted expressions in destructuring assignment must be ass
nonce = {}
for nonref in ['', '""', '0', 'f()', '(->)'].concat CoffeeScript.RESERVED
eq nonce, (try CoffeeScript.compile "[#{nonref}...] = v" catch e then nonce)
test "#1643: splatted accesses in destructuring assignments should not be declared as variables", ->
nonce = {}
accesses = ['o.a', 'o["a"]', '(o.a)', '(o.a).a', '@o.a', 'C::a', 'C::', 'f().a', 'o?.a', 'o?.a.b', 'f?().a']
for access in accesses
for i,j in [1,2,3] #position can matter
code =
"""
nonce = {}; nonce2 = {}; nonce3 = {};
@o = o = new (class C then a:{}); f = -> o
[#{new Array(i).join('x,')}#{access}...] = [#{new Array(i).join('0,')}nonce, nonce2, nonce3]
unless #{access}[0] is nonce and #{access}[1] is nonce2 and #{access}[2] is nonce3 then throw new Error('[...]')
"""
eq nonce, unless (try CoffeeScript.run code, bare: true catch e then true) then nonce
# subpatterns like `[[a]...]` and `[{a}...]`
subpatterns = ['[sub, sub2, sub3]', '{0: sub, 1: sub2, 2: sub3}']
for subpattern in subpatterns
for i,j in [1,2,3]
code =
"""
nonce = {}; nonce2 = {}; nonce3 = {};
[#{new Array(i).join('x,')}#{subpattern}...] = [#{new Array(i).join('0,')}nonce, nonce2, nonce3]
unless sub is nonce and sub2 is nonce2 and sub3 is nonce3 then throw new Error('[sub...]')
"""
eq nonce, unless (try CoffeeScript.run code, bare: true catch e then true) then nonce

View File

@@ -428,3 +428,28 @@ test "#1326: `by` value is uncached", ->
arrayEq a, rangeCompileSimple
#exercises Range.compile
eq "#{i for i in [0..2] by h()}", '0,1,2'
test "#1669: break/continue should skip the result only for that branch", ->
ns = for n in [0..99]
if n > 9
break
else if n & 1
continue
else
n
eq "#{ns}", '0,2,4,6,8'
# `else undefined` is implied.
ns = for n in [1..9]
if n % 2
continue unless n % 5
n
eq "#{ns}", "1,,3,,,7,,9"
# Ditto.
ns = for n in [1..9]
switch
when n % 2
continue unless n % 5
n
eq "#{ns}", "1,,3,,,7,,9"

View File

@@ -404,7 +404,7 @@ test "Switch with break as the return value of a loop.", ->
when 1 then i
when 0 then break
eq results.join(', '), '9, , 7, , 5, , 3, , 1, '
eq results.join(', '), '9, 7, 5, 3, 1'
test "Issue #997. Switch doesn't fallthrough.", ->

View File

@@ -121,3 +121,26 @@ test "indented heredoc", ->
test "#1492: Nested blocks don't cause double semicolons", ->
js = CoffeeScript.compile '(0;0)'
eq -1, js.indexOf ';;'
test "#1195 Ignore trailing semicolons (before newlines or as the last char in a program)", ->
preNewline = (numSemicolons) ->
"""
nonce = {}; nonce2 = {}
f = -> nonce#{Array(numSemicolons+1).join(';')}
nonce2
unless f() is nonce then throw new Error('; before linebreak should = newline')
"""
CoffeeScript.run(preNewline(n), bare: true) for n in [1,2,3]
lastChar = '-> lastChar;'
doesNotThrow -> CoffeeScript.compile lastChar, bare: true
test "#1299: Disallow token misnesting", ->
try
CoffeeScript.compile '''
[{
]}
'''
ok no
catch e
eq 'unmatched ] on line 2', e.message

View File

@@ -64,6 +64,21 @@ ok obj isnt obj.unbound()
eq obj, obj.nested()
test "even more fancy bound functions", ->
obj =
one: ->
do =>
return this.two()
two: ->
do =>
do =>
do =>
return this.three
three: 3
eq obj.one(), 3
test "self-referencing functions", ->
changeMe = ->
changeMe = 2

View File

@@ -200,6 +200,18 @@ test "#1100: precedence in or-test compilation of `in`", ->
test "#1630: `in` should check `hasOwnProperty`", ->
ok undefined not in length: 1
test "#1714: lexer bug with raw range `for` followed by `in`", ->
0 for [1..2]
ok not ('a' in ['b'])
0 for [1..2]; ok not ('a' in ['b'])
0 for [1..10] # comment ending
ok not ('a' in ['b'])
test "#1099: statically determined `not in []` reporting incorrect result", ->
ok 0 not in []
# Chained Comparison
@@ -243,3 +255,11 @@ test "#1234: Applying a splat to :: applies the splat to the wrong object", ->
arr = []
eq nonce, C::method arr... # should be applied to `C::`
test "#1102: String literal prevents line continuation", ->
eq "': '", '' +
"': '"
test "#1703, ---x is invalid JS", ->
x = 2
eq (- --x), -1

View File

@@ -38,6 +38,9 @@ test "#764: regular expressions should be indexable", ->
test "#584: slashes are allowed unescaped in character classes", ->
ok /^a\/[/]b$/.test 'a//b'
test "#1724: regular expressions beginning with `*`", ->
throws -> CoffeeScript.compile '/*/'
# Heregexe(n|s)
@@ -49,3 +52,12 @@ test "a heregex will ignore whitespace and comments", ->
test "an empty heregex will compile to an empty, non-capturing group", ->
eq /(?:)/ + '', /// /// + ''
test "#1724: regular expressions beginning with `*`", ->
throws -> CoffeeScript.compile '/// * ///'
test "empty regular expressions with flags", ->
fn = (x) -> x
a = "" + //i
fn ""
eq '/(?:)/i', a

View File

@@ -52,6 +52,13 @@ test "string slicing", ->
ok str[0..4] is "abcde"
ok str[-5..] is "vwxyz"
test "#1722: operator precedence in unbounded slice compilation", ->
list = [0..9]
n = 2 # some truthy number in `list`
arrayEq [0..n], list[..n]
arrayEq [0..n], list[..n or 0]
arrayEq [0..n], list[..if n then n else 0]
# Splicing
@@ -114,3 +121,18 @@ test "the return value of a splice literal should be the RHS", ->
eq (ary[0..] = 3), 3
arrayEq [ary[0..0] = 0], [0]
test "#1723: operator precedence in unbounded splice compilation", ->
n = 4 # some truthy number in `list`
list = [0..9]
list[..n] = n
arrayEq [n..9], list
list = [0..9]
list[..n or 0] = n
arrayEq [n..9], list
list = [0..9]
list[..if n then n else 0] = n
arrayEq [n..9], list