mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-01-14 09:17:55 -05:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05d95acfc3 | ||
|
|
22674bc536 | ||
|
|
23c5ebb00f | ||
|
|
c14869f008 | ||
|
|
c1427d6558 | ||
|
|
2a46e13d33 | ||
|
|
b26e577244 | ||
|
|
9f8710b631 | ||
|
|
aba8cb1b08 | ||
|
|
92cd80226c | ||
|
|
10d335ccb1 | ||
|
|
4eeb8c4bd2 | ||
|
|
4d146bacb1 | ||
|
|
7de4caffca | ||
|
|
8db0cb9fa5 | ||
|
|
c30b3d3c48 | ||
|
|
52db4fbf8c | ||
|
|
5cd8f2c52c | ||
|
|
5a1aa44393 | ||
|
|
432696d6eb | ||
|
|
3df7bd98f4 | ||
|
|
1f870911c9 | ||
|
|
4bb9392753 | ||
|
|
fdffacfb40 | ||
|
|
a64afe6162 | ||
|
|
15b86a5f7a | ||
|
|
9b78fb67cf | ||
|
|
4817b96bac | ||
|
|
6985802eb3 | ||
|
|
aad0ce162d |
10
Cakefile
10
Cakefile
@@ -30,7 +30,7 @@ task 'build:parser', 'rebuild the Jison parser (run build first)', ->
|
||||
|
||||
|
||||
task 'build:ultraviolet', 'build and install the Ultraviolet syntax highlighter', ->
|
||||
exec('plist2syntax extras/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage').addCallback ->
|
||||
exec 'plist2syntax extras/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage', (err) ->
|
||||
exec 'sudo mv coffeescript.yaml /usr/local/lib/ruby/gems/1.8/gems/ultraviolet-0.10.2/syntax/coffeescript.syntax'
|
||||
|
||||
|
||||
@@ -38,6 +38,14 @@ task 'build:underscore', 'rebuild the Underscore.coffee documentation page', ->
|
||||
exec 'uv -s coffeescript -t idle -h examples/underscore.coffee > documentation/underscore.html'
|
||||
|
||||
|
||||
task 'build:browser', 'rebuild the merged script for inclusion in the browser', ->
|
||||
exec 'rake browser'
|
||||
|
||||
|
||||
task 'doc', 'watch and continually rebuild the documentation', ->
|
||||
exec 'rake doc'
|
||||
|
||||
|
||||
task 'test', 'run the CoffeeScript language test suite', ->
|
||||
process.mixin require 'assert'
|
||||
test_count: 0
|
||||
|
||||
11
Rakefile
11
Rakefile
@@ -1,6 +1,8 @@
|
||||
require 'erb'
|
||||
require 'fileutils'
|
||||
require 'rake/testtask'
|
||||
require 'rubygems'
|
||||
require 'yui/compressor'
|
||||
|
||||
desc "Build the documentation page"
|
||||
task :doc do
|
||||
@@ -18,3 +20,12 @@ task :doc do
|
||||
sleep 1
|
||||
end
|
||||
end
|
||||
|
||||
desc "Build the single concatenated and minified script for the browser"
|
||||
task :browser do
|
||||
sources = %w(rewriter.js lexer.js parser.js scope.js nodes.js coffee-script.js)
|
||||
code = sources.map {|s| File.read('lib/' + s) }.join('')
|
||||
code = YUI::JavaScriptCompressor.new.compress(code)
|
||||
File.open('extras/coffee-script.js', 'w+') {|f| f.write(code) }
|
||||
end
|
||||
|
||||
|
||||
2
bin/cake
2
bin/cake
@@ -2,6 +2,6 @@
|
||||
|
||||
process.mixin(require('sys'));
|
||||
|
||||
require.paths.unshift('./lib');
|
||||
require.paths.unshift(__dirname + '/../lib');
|
||||
|
||||
require('cake').run();
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
process.mixin(require('sys'));
|
||||
|
||||
require.paths.unshift('./lib');
|
||||
require.paths.unshift(__dirname + '/../lib');
|
||||
|
||||
require('command_line').run();
|
||||
|
||||
@@ -63,6 +63,11 @@ code, pre, tt, textarea {
|
||||
padding: 3px 0 3px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
pre.no_bar {
|
||||
border-left: 0;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
div.code {
|
||||
position: relative;
|
||||
border: 1px solid #cacaca;
|
||||
@@ -130,7 +135,8 @@ div.code {
|
||||
.navigation.try {
|
||||
border-left: 0;
|
||||
}
|
||||
.navigation:hover {
|
||||
.navigation:hover,
|
||||
.navigation.active {
|
||||
background: #d0d0d0;
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(#f0f0f0), to(#c0c0c0));
|
||||
background: -moz-linear-gradient(top, #f0f0f0, #c0c0c0);
|
||||
@@ -147,7 +153,7 @@ div.code {
|
||||
-webkit-border-top-right-radius: 0; -moz-border-radius-topright: 0;
|
||||
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
|
||||
}
|
||||
.navigation:hover .contents {
|
||||
.navigation.active .contents {
|
||||
display: block;
|
||||
}
|
||||
.navigation .contents.repl_wrapper {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%
|
||||
require 'uv'
|
||||
def code_for(file, executable=false)
|
||||
@stripper ||= /(\A\(function\(\)\{\n|\}\)\(\);\Z|^ )/
|
||||
@stripper ||= /(\A\(function\(\)\{\n|\}\)\(\);\n*\Z|^ )/
|
||||
return '' unless File.exists?("documentation/js/#{file}.js")
|
||||
cs = File.read("documentation/coffee/#{file}.coffee")
|
||||
js = File.read("documentation/js/#{file}.js").gsub(@stripper, '')
|
||||
@@ -60,6 +60,7 @@
|
||||
<a href="#comparisons">Chained Comparisons</a>
|
||||
<a href="#strings">Multiline Strings and Heredocs</a>
|
||||
<a href="#cake">Cake, and Cakefiles</a>
|
||||
<a href="#scripts">"text/coffeescript" Script Tags</a>
|
||||
<a href="#resources">Resources</a>
|
||||
<a href="#change_log">Change Log</a>
|
||||
</div>
|
||||
@@ -107,7 +108,7 @@ alert reverse '!tpircseeffoC'</textarea>
|
||||
|
||||
<p>
|
||||
<b>Latest Version:</b>
|
||||
<a href="http://github.com/jashkenas/coffee-script/tarball/0.5.0">0.5.0</a>
|
||||
<a href="http://github.com/jashkenas/coffee-script/tarball/0.5.2">0.5.2</a>
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
@@ -138,18 +139,21 @@ alert reverse '!tpircseeffoC'</textarea>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
The CoffeeScript compiler is written in pure CoffeeScript, and is available
|
||||
The CoffeeScript compiler is written in pure CoffeeScript, using a
|
||||
<a href="http://github.com/jashkenas/coffee-script/blob/master/src/grammar.coffee">small DSL</a>
|
||||
on top of the <a href="http://github.com/zaach/jison">Jison parser generator</a>, and is available
|
||||
as a <a href="http://nodejs.org/">Node.js</a> utility. The core compiler however,
|
||||
does not depend on Node, and can be run in other server-side-JavaScript environments,
|
||||
or in the browser (see "Try CoffeeScript", above).
|
||||
or in the browser (see "Try CoffeeScript", above). This may be helpful,
|
||||
as Node only run on flavors of nix, and not Windows, for the time being.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To install, first make sure you have a working version of
|
||||
To install, first make sure you have a working version of
|
||||
<a href="http://nodejs.org/">Node.js</a>, 0.1.30 or higher. Then clone the CoffeeScript
|
||||
<a href="http://github.com/jashkenas/coffee-script">source repository</a>
|
||||
from GitHub, or download the latest
|
||||
release: <a href="http://github.com/jashkenas/coffee-script/tarball/0.5.0">0.5.0</a>.
|
||||
release: <a href="http://github.com/jashkenas/coffee-script/tarball/0.5.2">0.5.2</a>.
|
||||
To install the CoffeeScript compiler system-wide
|
||||
under <tt>/usr/local</tt>, open the directory and run:
|
||||
</p>
|
||||
@@ -211,6 +215,14 @@ sudo bin/cake install</pre>
|
||||
conjunction with <tt>--watch</tt>)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-s, --stdio</code></td>
|
||||
<td>
|
||||
Pipe in CoffeeScript to STDIN and get back JavaScript over STDOUT.
|
||||
Good for use with processes written in other languages. An example:<br />
|
||||
<tt>cat src/cake.coffee | coffee -s</tt>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-e, --eval</code></td>
|
||||
<td>
|
||||
@@ -237,14 +249,14 @@ sudo bin/cake install</pre>
|
||||
<td>
|
||||
Instead of compiling the CoffeeScript, just lex and parse it, and print
|
||||
out the parse tree:
|
||||
<pre>
|
||||
Expressions
|
||||
Assign
|
||||
Value "square"
|
||||
Code "x"
|
||||
Op *
|
||||
Value "x"
|
||||
Value "x"</pre>
|
||||
<pre class="no_bar">
|
||||
Expressions
|
||||
Assign
|
||||
Value "square"
|
||||
Code "x"
|
||||
Op *
|
||||
Value "x"
|
||||
Value "x"</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -714,6 +726,34 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
</p>
|
||||
<%= code_for('cake_tasks') %>
|
||||
|
||||
<h2>
|
||||
<span id="scripts" class="bookmark"></span>
|
||||
"text/coffeescript" Script Tags
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
While it's not recommended for serious use, CoffeeScripts may be included
|
||||
directly within the browser using <tt><script type="text/coffeescript"></tt>
|
||||
tags. The codebase includes a compressed and minified version of the compiler
|
||||
(<a href="extras/coffee-script.js">Download current version here, 43k when gzipped</a>).
|
||||
Include <tt>coffee-script.js</tt> on the page <b>after</b> any <tt>text/coffeescript</tt> tags
|
||||
with inline CoffeeScript, and it will compile and evaluate them in order.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In fact, the little bit of glue script that runs "Try CoffeeScript" above,
|
||||
as well as jQuery for the menu, is implemented in just this way.
|
||||
View source and look at the bottom of the page to see the example.
|
||||
Including the script also gives you access to <tt>CoffeeScript.compile()</tt>
|
||||
so you can pop open Firebug and try compiling some strings.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The usual caveats about CoffeeScript apply — your inline scripts will
|
||||
run within a closure wrapper, so if you want to expose global variables or
|
||||
functions, attach them to the <tt>window</tt> object.
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<span id="resources" class="bookmark"></span>
|
||||
Resources
|
||||
@@ -742,6 +782,23 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
Change Log
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.5.2</b>
|
||||
Added a compressed version of the compiler for inclusion in web pages as
|
||||
<br /><tt>extras/coffee-script.js</tt>. It'll automatically run any script tags
|
||||
with type <tt>text/coffeescript</tt> for you. Added a <tt>--stdio</tt> option
|
||||
to the <tt>coffee</tt> command, for piped-in compiles.
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.5.1</b>
|
||||
Improvements to null soaking with the existential operator, including
|
||||
soaks on indexed properties. Added conditions to <tt>while</tt> loops,
|
||||
so you can use them as filters with <tt>when</tt>, in the same manner as
|
||||
comprehensions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.5.0</b>
|
||||
CoffeeScript 0.5.0 is a major release, While there are no language changes,
|
||||
@@ -904,32 +961,42 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="lib/rewriter.js"></script>
|
||||
<script type="text/javascript" src="lib/lexer.js"></script>
|
||||
<script type="text/javascript" src="lib/parser.js"></script>
|
||||
<script type="text/javascript" src="lib/scope.js"></script>
|
||||
<script type="text/javascript" src="lib/nodes.js"></script>
|
||||
<script type="text/javascript" src="lib/coffee-script.js"></script>
|
||||
<script type="text/coffeescript">
|
||||
|
||||
window.repl_compile: ->
|
||||
source: $('#repl_source').val()
|
||||
window.compiled_js: ''
|
||||
try
|
||||
window.compiled_js: CoffeeScript.compile source, {no_wrap: true}
|
||||
catch error then alert error
|
||||
$('#repl_results').html window.compiled_js
|
||||
|
||||
window.repl_run: ->
|
||||
try
|
||||
eval window.compiled_js
|
||||
catch error then alert error
|
||||
|
||||
nav: $('.navigation')
|
||||
current_nav: null
|
||||
|
||||
close_menus: ->
|
||||
current_nav.removeClass 'active' if current_nav
|
||||
current_nav: null
|
||||
|
||||
nav.click (e) ->
|
||||
return if e.target.tagName.toLowerCase() is 'a'
|
||||
if this isnt (current_nav and current_nav[0])
|
||||
close_menus();
|
||||
current_nav: $(this)
|
||||
current_nav.addClass 'active'
|
||||
false
|
||||
|
||||
$(document.body).click -> close_menus()
|
||||
|
||||
<script type="text/javascript">
|
||||
window.repl_compile = function() {
|
||||
var source = document.getElementById('repl_source').value;
|
||||
window.compiled_js = '';
|
||||
try {
|
||||
window.compiled_js = CoffeeScript.compile(source, {no_wrap: true});
|
||||
} catch(error) {
|
||||
alert(error);
|
||||
}
|
||||
document.getElementById('repl_results').innerHTML = window.compiled_js;
|
||||
}
|
||||
window.repl_run = function() {
|
||||
try {
|
||||
eval(window.compiled_js);
|
||||
} catch(error) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<script src="extras/coffee-script.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
}
|
||||
car.speed < speed_limit ? accelerate() : null;
|
||||
print("My name is " + this.name);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
return alert(arguments.reverse());
|
||||
};
|
||||
backwards("stairway", "to", "heaven");
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -23,4 +23,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
var difficulty, greeting;
|
||||
greeting = "Hello CoffeeScript";
|
||||
difficulty = 0.5;
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
}
|
||||
return _a;
|
||||
});
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
var cholesterol, healthy;
|
||||
cholesterol = 127;
|
||||
healthy = (200 > cholesterol) && (cholesterol > 60);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
}
|
||||
date = friday ? sue : jill;
|
||||
expensive = expensive || do_the_math();
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
hi = function() {
|
||||
return [document.title, "Hello JavaScript"].join(": ");
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
solipsism = true;
|
||||
}
|
||||
speed = (typeof speed !== "undefined" && speed !== null) ? speed : 140;
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
}
|
||||
};
|
||||
eldest = 24 > 21 ? "Liz" : "Ike";
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(function(){
|
||||
var one, six, three, two;
|
||||
six = ((one = 1)) + ((two = 2)) + ((three = 3));
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
}}
|
||||
return _a;
|
||||
}).call(this).slice(0, 10);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
return "And the error is ... " + error;
|
||||
}
|
||||
}).call(this));
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
});
|
||||
})(this));
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
cube = function cube(x) {
|
||||
return square(x) * x;
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(function(){
|
||||
var html;
|
||||
html = "<strong>\n cup of coffeescript\n</strong>";
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
city = _a[0];
|
||||
temp = _a[1];
|
||||
forecast = _a[2];
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
}}
|
||||
return _a;
|
||||
}).call(this);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
_c = _b.address;
|
||||
street = _c[0];
|
||||
city = _c[1];
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
tim: 11
|
||||
};
|
||||
matrix = [1, 0, 1, 0, 0, 1, 1, 1, 0];
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -40,4 +40,4 @@
|
||||
}
|
||||
return _a;
|
||||
}).call(this);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
_a = [and_switch, bait];
|
||||
bait = _a[0];
|
||||
and_switch = _a[1];
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
var _a, _b, _c, _d, _e, countdown, egg_delivery, num;
|
||||
countdown = (function() {
|
||||
_a = []; _d = 10; _e = 1;
|
||||
for (_c=0, num=_d; (_d <= _e ? num <= _e : num >= _e); (_d <= _e ? num += 1 : num -= 1), _c++) {
|
||||
for (_c = 0, num=_d; (_d <= _e ? num <= _e : num >= _e); (_d <= _e ? num += 1 : num -= 1), _c++) {
|
||||
_a.push(num);
|
||||
}
|
||||
return _a;
|
||||
@@ -10,7 +10,7 @@
|
||||
egg_delivery = function egg_delivery() {
|
||||
var _f, _g, _h, _i, _j, dozen_eggs, i;
|
||||
_f = []; _i = 0; _j = eggs.length;
|
||||
for (_h=0, i=_i; (_i <= _j ? i < _j : i > _j); (_i <= _j ? i += 12 : i -= 12), _h++) {
|
||||
for (_h = 0, i=_i; (_i <= _j ? i < _j : i > _j); (_i <= _j ? i += 12 : i -= 12), _h++) {
|
||||
_f.push((function() {
|
||||
dozen_eggs = eggs.slice(i, i + 12);
|
||||
return deliver(new egg_carton(dozen));
|
||||
@@ -18,4 +18,4 @@
|
||||
}
|
||||
return _f;
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
return num = 10;
|
||||
};
|
||||
new_num = change_numbers();
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
three_to_six = numbers.slice(3, 6 + 1);
|
||||
numbers_copy = numbers.slice(0, numbers.length);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(function(){
|
||||
var _a;
|
||||
(_a = lottery.draw_winner()) == null ? undefined : _a.address == null ? undefined : _a.address.zipcode;
|
||||
})();
|
||||
(_a = lottery.draw_winner()) == undefined ? undefined : _a.address == undefined ? undefined : _a.address.zipcode;
|
||||
})();
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
alert("Gold: " + gold);
|
||||
alert("Silver: " + silver);
|
||||
alert("The Field: " + the_field);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
var numbers;
|
||||
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
numbers.splice.apply(numbers, [3, 6 - 3 + 1].concat([-3, -4, -5, -6]));
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -6,4 +6,4 @@ 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...";
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -34,4 +34,4 @@
|
||||
tom = new Horse("Tommy the Palomino");
|
||||
sam.move();
|
||||
tom.move();
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
} else {
|
||||
go_to_work();
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
} finally {
|
||||
clean_up();
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -19,4 +19,4 @@ One fell out and bumped his head.");
|
||||
}
|
||||
return _a;
|
||||
}).call(this);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -8,7 +8,7 @@ get '/hello', ->
|
||||
# Append.
|
||||
append: (location, data) ->
|
||||
path: new Pathname location
|
||||
throw "Location does not exist" unless path.exists()
|
||||
throw new Error("Location does not exist") unless path.exists()
|
||||
|
||||
File.open path, 'a', (file) ->
|
||||
file.puts YAML.dump data
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
This folder includes rough cuts of CoffeeScript syntax highlighters for
|
||||
EXTRAS:
|
||||
|
||||
"extras/coffee-script.js" is a concatenated and compressed version of the
|
||||
CoffeeScript compiler. To use it in the browser, include the script after any
|
||||
inline script tags of type "text/coffeescript" on the page. It will compile
|
||||
and evaluate all of the scripts in order.
|
||||
|
||||
|
||||
This folder also includes rough cuts of CoffeeScript syntax highlighters for
|
||||
TextMate and Vim. Improvements to their lexing ability are always welcome.
|
||||
|
||||
To install the TextMate bundle, drop it into:
|
||||
|
||||
1
extras/coffee-script.js
Normal file
1
extras/coffee-script.js
Normal file
File diff suppressed because one or more lines are too long
151
index.html
151
index.html
@@ -46,6 +46,7 @@
|
||||
<a href="#comparisons">Chained Comparisons</a>
|
||||
<a href="#strings">Multiline Strings and Heredocs</a>
|
||||
<a href="#cake">Cake, and Cakefiles</a>
|
||||
<a href="#scripts">"text/coffeescript" Script Tags</a>
|
||||
<a href="#resources">Resources</a>
|
||||
<a href="#change_log">Change Log</a>
|
||||
</div>
|
||||
@@ -93,7 +94,7 @@ alert reverse '!tpircseeffoC'</textarea>
|
||||
|
||||
<p>
|
||||
<b>Latest Version:</b>
|
||||
<a href="http://github.com/jashkenas/coffee-script/tarball/0.5.0">0.5.0</a>
|
||||
<a href="http://github.com/jashkenas/coffee-script/tarball/0.5.2">0.5.2</a>
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
@@ -235,18 +236,21 @@ cubed_list = (function() {
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
The CoffeeScript compiler is written in pure CoffeeScript, and is available
|
||||
The CoffeeScript compiler is written in pure CoffeeScript, using a
|
||||
<a href="http://github.com/jashkenas/coffee-script/blob/master/src/grammar.coffee">small DSL</a>
|
||||
on top of the <a href="http://github.com/zaach/jison">Jison parser generator</a>, and is available
|
||||
as a <a href="http://nodejs.org/">Node.js</a> utility. The core compiler however,
|
||||
does not depend on Node, and can be run in other server-side-JavaScript environments,
|
||||
or in the browser (see "Try CoffeeScript", above).
|
||||
or in the browser (see "Try CoffeeScript", above). This may be helpful,
|
||||
as Node only run on flavors of nix, and not Windows, for the time being.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To install, first make sure you have a working version of
|
||||
To install, first make sure you have a working version of
|
||||
<a href="http://nodejs.org/">Node.js</a>, 0.1.30 or higher. Then clone the CoffeeScript
|
||||
<a href="http://github.com/jashkenas/coffee-script">source repository</a>
|
||||
from GitHub, or download the latest
|
||||
release: <a href="http://github.com/jashkenas/coffee-script/tarball/0.5.0">0.5.0</a>.
|
||||
release: <a href="http://github.com/jashkenas/coffee-script/tarball/0.5.2">0.5.2</a>.
|
||||
To install the CoffeeScript compiler system-wide
|
||||
under <tt>/usr/local</tt>, open the directory and run:
|
||||
</p>
|
||||
@@ -308,6 +312,14 @@ sudo bin/cake install</pre>
|
||||
conjunction with <tt>--watch</tt>)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-s, --stdio</code></td>
|
||||
<td>
|
||||
Pipe in CoffeeScript to STDIN and get back JavaScript over STDOUT.
|
||||
Good for use with processes written in other languages. An example:<br />
|
||||
<tt>cat src/cake.coffee | coffee -s</tt>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-e, --eval</code></td>
|
||||
<td>
|
||||
@@ -334,14 +346,14 @@ sudo bin/cake install</pre>
|
||||
<td>
|
||||
Instead of compiling the CoffeeScript, just lex and parse it, and print
|
||||
out the parse tree:
|
||||
<pre>
|
||||
Expressions
|
||||
Assign
|
||||
Value "square"
|
||||
Code "x"
|
||||
Op *
|
||||
Value "x"
|
||||
Value "x"</pre>
|
||||
<pre class="no_bar">
|
||||
Expressions
|
||||
Assign
|
||||
Value "square"
|
||||
Code "x"
|
||||
Op *
|
||||
Value "x"
|
||||
Value "x"</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -851,7 +863,7 @@ _d <span class="Keyword">=</span> asteroids;
|
||||
</pre><pre class="idle"><span class="Storage">var</span> _a, _b, _c, _d, _e, countdown, egg_delivery, num;
|
||||
countdown <span class="Keyword">=</span> (<span class="Storage">function</span>() {
|
||||
_a <span class="Keyword">=</span> []; _d <span class="Keyword">=</span> <span class="Number">10</span>; _e <span class="Keyword">=</span> <span class="Number">1</span>;
|
||||
<span class="Keyword">for</span> (_c<span class="Keyword">=</span><span class="Number">0</span>, num<span class="Keyword">=</span>_d; (_d <span class="Keyword"><=</span> _e ? num <span class="Keyword"><=</span> _e : num <span class="Keyword">>=</span> _e); (_d <span class="Keyword"><=</span> _e ? num <span class="Keyword">+</span><span class="Keyword">=</span> <span class="Number">1</span> : num <span class="Keyword">-</span><span class="Keyword">=</span> <span class="Number">1</span>), _c<span class="Keyword">++</span>) {
|
||||
<span class="Keyword">for</span> (_c <span class="Keyword">=</span> <span class="Number">0</span>, num<span class="Keyword">=</span>_d; (_d <span class="Keyword"><=</span> _e ? num <span class="Keyword"><=</span> _e : num <span class="Keyword">>=</span> _e); (_d <span class="Keyword"><=</span> _e ? num <span class="Keyword">+</span><span class="Keyword">=</span> <span class="Number">1</span> : num <span class="Keyword">-</span><span class="Keyword">=</span> <span class="Number">1</span>), _c<span class="Keyword">++</span>) {
|
||||
_a.<span class="LibraryFunction">push</span>(num);
|
||||
}
|
||||
<span class="Keyword">return</span> _a;
|
||||
@@ -859,7 +871,7 @@ countdown <span class="Keyword">=</span> (<span class="Storage">function</span>(
|
||||
egg_delivery <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">egg_delivery</span>() {
|
||||
<span class="Storage">var</span> _f, _g, _h, _i, _j, dozen_eggs, i;
|
||||
_f <span class="Keyword">=</span> []; _i <span class="Keyword">=</span> <span class="Number">0</span>; _j <span class="Keyword">=</span> eggs.<span class="LibraryConstant">length</span>;
|
||||
<span class="Keyword">for</span> (_h<span class="Keyword">=</span><span class="Number">0</span>, i<span class="Keyword">=</span>_i; (_i <span class="Keyword"><=</span> _j ? i <span class="Keyword"><</span> _j : i <span class="Keyword">></span> _j); (_i <span class="Keyword"><=</span> _j ? i <span class="Keyword">+</span><span class="Keyword">=</span> <span class="Number">12</span> : i <span class="Keyword">-</span><span class="Keyword">=</span> <span class="Number">12</span>), _h<span class="Keyword">++</span>) {
|
||||
<span class="Keyword">for</span> (_h <span class="Keyword">=</span> <span class="Number">0</span>, i<span class="Keyword">=</span>_i; (_i <span class="Keyword"><=</span> _j ? i <span class="Keyword"><</span> _j : i <span class="Keyword">></span> _j); (_i <span class="Keyword"><=</span> _j ? i <span class="Keyword">+</span><span class="Keyword">=</span> <span class="Number">12</span> : i <span class="Keyword">-</span><span class="Keyword">=</span> <span class="Number">12</span>), _h<span class="Keyword">++</span>) {
|
||||
_f.<span class="LibraryFunction">push</span>((<span class="Storage">function</span>() {
|
||||
dozen_eggs <span class="Keyword">=</span> eggs.<span class="LibraryFunction">slice</span>(i, i <span class="Keyword">+</span> <span class="Number">12</span>);
|
||||
<span class="Keyword">return</span> deliver(<span class="Keyword">new</span> <span class="TypeName">egg_carton</span>(dozen));
|
||||
@@ -870,7 +882,7 @@ egg_delivery <span class="Keyword">=</span> <span class="Storage">function</span
|
||||
</pre><button onclick='javascript: var _a, _b, _c, _d, _e, countdown, egg_delivery, num;
|
||||
countdown = (function() {
|
||||
_a = []; _d = 10; _e = 1;
|
||||
for (_c=0, num=_d; (_d <= _e ? num <= _e : num >= _e); (_d <= _e ? num += 1 : num -= 1), _c++) {
|
||||
for (_c = 0, num=_d; (_d <= _e ? num <= _e : num >= _e); (_d <= _e ? num += 1 : num -= 1), _c++) {
|
||||
_a.push(num);
|
||||
}
|
||||
return _a;
|
||||
@@ -878,7 +890,7 @@ countdown = (function() {
|
||||
egg_delivery = function egg_delivery() {
|
||||
var _f, _g, _h, _i, _j, dozen_eggs, i;
|
||||
_f = []; _i = 0; _j = eggs.length;
|
||||
for (_h=0, i=_i; (_i <= _j ? i < _j : i > _j); (_i <= _j ? i += 12 : i -= 12), _h++) {
|
||||
for (_h = 0, i=_i; (_i <= _j ? i < _j : i > _j); (_i <= _j ? i += 12 : i -= 12), _h++) {
|
||||
_f.push((function() {
|
||||
dozen_eggs = eggs.slice(i, i + 12);
|
||||
return deliver(new egg_carton(dozen));
|
||||
@@ -1131,7 +1143,7 @@ speed = (typeof speed !== "undefined" && speed !== null) ? speed : 140;
|
||||
</p>
|
||||
<div class='code'><pre class="idle">lottery.draw_winner()<span class="Keyword">?</span>.address<span class="Keyword">?</span>.zipcode
|
||||
</pre><pre class="idle"><span class="Storage">var</span> _a;
|
||||
(_a <span class="Keyword">=</span> lottery.draw_winner()) <span class="Keyword">==</span> <span class="BuiltInConstant">null</span> ? undefined : _a.address <span class="Keyword">==</span> <span class="BuiltInConstant">null</span> ? undefined : _a.address.zipcode;
|
||||
(_a <span class="Keyword">=</span> lottery.draw_winner()) <span class="Keyword">==</span> undefined ? undefined : _a.address <span class="Keyword">==</span> undefined ? undefined : _a.address.zipcode;
|
||||
</pre><br class='clear' /></div>
|
||||
<p>
|
||||
Soaking up nulls is similar to Ruby's
|
||||
@@ -1594,6 +1606,34 @@ task(<span class="String"><span class="String">'</span>test<span class="String">
|
||||
});
|
||||
</pre><br class='clear' /></div>
|
||||
|
||||
<h2>
|
||||
<span id="scripts" class="bookmark"></span>
|
||||
"text/coffeescript" Script Tags
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
While it's not recommended for serious use, CoffeeScripts may be included
|
||||
directly within the browser using <tt><script type="text/coffeescript"></tt>
|
||||
tags. The codebase includes a compressed and minified version of the compiler
|
||||
(<a href="extras/coffee-script.js">Download current version here, 43k when gzipped</a>).
|
||||
Include <tt>coffee-script.js</tt> on the page <b>after</b> any <tt>text/coffeescript</tt> tags
|
||||
with inline CoffeeScript, and it will compile and evaluate them in order.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In fact, the little bit of glue script that runs "Try CoffeeScript" above,
|
||||
as well as jQuery for the menu, is implemented in just this way.
|
||||
View source and look at the bottom of the page to see the example.
|
||||
Including the script also gives you access to <tt>CoffeeScript.compile()</tt>
|
||||
so you can pop open Firebug and try compiling some strings.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The usual caveats about CoffeeScript apply — your inline scripts will
|
||||
run within a closure wrapper, so if you want to expose global variables or
|
||||
functions, attach them to the <tt>window</tt> object.
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<span id="resources" class="bookmark"></span>
|
||||
Resources
|
||||
@@ -1622,6 +1662,23 @@ task(<span class="String"><span class="String">'</span>test<span class="String">
|
||||
Change Log
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.5.2</b>
|
||||
Added a compressed version of the compiler for inclusion in web pages as
|
||||
<br /><tt>extras/coffee-script.js</tt>. It'll automatically run any script tags
|
||||
with type <tt>text/coffeescript</tt> for you. Added a <tt>--stdio</tt> option
|
||||
to the <tt>coffee</tt> command, for piped-in compiles.
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.5.1</b>
|
||||
Improvements to null soaking with the existential operator, including
|
||||
soaks on indexed properties. Added conditions to <tt>while</tt> loops,
|
||||
so you can use them as filters with <tt>when</tt>, in the same manner as
|
||||
comprehensions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.5.0</b>
|
||||
CoffeeScript 0.5.0 is a major release, While there are no language changes,
|
||||
@@ -1784,32 +1841,42 @@ task(<span class="String"><span class="String">'</span>test<span class="String">
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="lib/rewriter.js"></script>
|
||||
<script type="text/javascript" src="lib/lexer.js"></script>
|
||||
<script type="text/javascript" src="lib/parser.js"></script>
|
||||
<script type="text/javascript" src="lib/scope.js"></script>
|
||||
<script type="text/javascript" src="lib/nodes.js"></script>
|
||||
<script type="text/javascript" src="lib/coffee-script.js"></script>
|
||||
<script type="text/coffeescript">
|
||||
|
||||
window.repl_compile: ->
|
||||
source: $('#repl_source').val()
|
||||
window.compiled_js: ''
|
||||
try
|
||||
window.compiled_js: CoffeeScript.compile source, {no_wrap: true}
|
||||
catch error then alert error
|
||||
$('#repl_results').html window.compiled_js
|
||||
|
||||
window.repl_run: ->
|
||||
try
|
||||
eval window.compiled_js
|
||||
catch error then alert error
|
||||
|
||||
nav: $('.navigation')
|
||||
current_nav: null
|
||||
|
||||
close_menus: ->
|
||||
current_nav.removeClass 'active' if current_nav
|
||||
current_nav: null
|
||||
|
||||
nav.click (e) ->
|
||||
return if e.target.tagName.toLowerCase() is 'a'
|
||||
if this isnt (current_nav and current_nav[0])
|
||||
close_menus();
|
||||
current_nav: $(this)
|
||||
current_nav.addClass 'active'
|
||||
false
|
||||
|
||||
$(document.body).click -> close_menus()
|
||||
|
||||
<script type="text/javascript">
|
||||
window.repl_compile = function() {
|
||||
var source = document.getElementById('repl_source').value;
|
||||
window.compiled_js = '';
|
||||
try {
|
||||
window.compiled_js = CoffeeScript.compile(source, {no_wrap: true});
|
||||
} catch(error) {
|
||||
alert(error);
|
||||
}
|
||||
document.getElementById('repl_results').innerHTML = window.compiled_js;
|
||||
}
|
||||
window.repl_run = function() {
|
||||
try {
|
||||
eval(window.compiled_js);
|
||||
} catch(error) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<script src="extras/coffee-script.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
spaces = 20 - name.length;
|
||||
spaces = spaces > 0 ? (function() {
|
||||
_c = []; _f = 0; _g = spaces;
|
||||
for (_e=0, i=_f; (_f <= _g ? i <= _g : i >= _g); (_f <= _g ? i += 1 : i -= 1), _e++) {
|
||||
for (_e = 0, i=_f; (_f <= _g ? i <= _g : i >= _g); (_f <= _g ? i += 1 : i -= 1), _e++) {
|
||||
_c.push(' ');
|
||||
}
|
||||
return _c;
|
||||
@@ -77,4 +77,4 @@
|
||||
});
|
||||
});
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(function(){
|
||||
var lexer, parser, path;
|
||||
var _a, _b, lexer, parser, path, tag;
|
||||
// Set up for both the browser and the server.
|
||||
if ((typeof process !== "undefined" && process !== null)) {
|
||||
process.mixin(require('nodes'));
|
||||
@@ -32,7 +32,7 @@
|
||||
return this.pos;
|
||||
}
|
||||
};
|
||||
exports.VERSION = '0.5.0';
|
||||
exports.VERSION = '0.5.2';
|
||||
// Compile CoffeeScript to JavaScript, using the Coffee/Jison compiler.
|
||||
exports.compile = function compile(code, options) {
|
||||
return (parser.parse(lexer.tokenize(code))).compile(options);
|
||||
@@ -45,17 +45,13 @@
|
||||
exports.tree = function tree(code) {
|
||||
return parser.parse(lexer.tokenize(code));
|
||||
};
|
||||
// Pretty-print a token stream.
|
||||
exports.print_tokens = function print_tokens(tokens) {
|
||||
var _a, _b, _c, strings, token;
|
||||
strings = (function() {
|
||||
_a = []; _b = tokens;
|
||||
for (_c = 0; _c < _b.length; _c++) {
|
||||
token = _b[_c];
|
||||
_a.push('[' + token[0] + ' ' + token[1].toString().replace(/\n/, '\\n') + ']');
|
||||
}
|
||||
return _a;
|
||||
}).call(this);
|
||||
return puts(strings.join(' '));
|
||||
};
|
||||
})();
|
||||
// Activate CoffeeScript in the browser by having it compile and eval
|
||||
// all script tags with a content-type of text/coffeescript.
|
||||
if ((typeof document !== "undefined" && document !== null) && document.getElementsByTagName) {
|
||||
_a = document.getElementsByTagName('script');
|
||||
for (_b = 0; _b < _a.length; _b++) {
|
||||
tag = _a[_b];
|
||||
tag.type === 'text/coffeescript' ? eval(exports.compile(tag.innerHTML)) : null;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(function(){
|
||||
var BANNER, SWITCHES, coffee, compile_script, compile_scripts, fs, lint, option_parser, options, optparse, parse_options, path, sources, usage, version, watch_scripts, write_js;
|
||||
var BANNER, CoffeeScript, SWITCHES, compile_options, compile_script, compile_scripts, compile_stdio, fs, lint, option_parser, options, optparse, parse_options, path, print_tokens, sources, usage, version, watch_scripts, write_js;
|
||||
fs = require('fs');
|
||||
path = require('path');
|
||||
coffee = require('coffee-script');
|
||||
optparse = require('optparse');
|
||||
CoffeeScript = require('coffee-script');
|
||||
BANNER = "coffee compiles CoffeeScript source files into JavaScript.\n\nUsage:\n coffee path/to/script.coffee";
|
||||
SWITCHES = [['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-r', '--run', 'compile and run a CoffeeScript'], ['-o', '--output [DIR]', 'set the directory for compiled JavaScript'], ['-w', '--watch', 'watch scripts for changes, and recompile'], ['-p', '--print', 'print the compiled JavaScript to stdout'], ['-l', '--lint', 'pipe the compiled JavaScript through JSLint'], ['-e', '--eval', 'compile a string from the command line'], ['-t', '--tokens', 'print the tokens that the lexer produces'], ['-tr', '--tree', 'print the parse tree that Jison produces'], ['-n', '--no-wrap', 'compile without the top-level function wrapper'], ['-v', '--version', 'display CoffeeScript version'], ['-h', '--help', 'display this help message']];
|
||||
SWITCHES = [['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-r', '--run', 'compile and run a CoffeeScript'], ['-o', '--output [DIR]', 'set the directory for compiled JavaScript'], ['-w', '--watch', 'watch scripts for changes, and recompile'], ['-p', '--print', 'print the compiled JavaScript to stdout'], ['-l', '--lint', 'pipe the compiled JavaScript through JSLint'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-e', '--eval', 'compile a string from the command line'], ['-n', '--no-wrap', 'compile without the top-level function wrapper'], ['-t', '--tokens', 'print the tokens that the lexer produces'], ['-tr', '--tree', 'print the parse tree that Jison produces'], ['-v', '--version', 'display CoffeeScript version'], ['-h', '--help', 'display this help message']];
|
||||
options = {};
|
||||
sources = [];
|
||||
option_parser = null;
|
||||
@@ -13,14 +13,23 @@
|
||||
exports.run = function run() {
|
||||
var flags, separator;
|
||||
parse_options();
|
||||
if (options.help) {
|
||||
return usage();
|
||||
}
|
||||
if (options.version) {
|
||||
return version();
|
||||
}
|
||||
if (options.interactive) {
|
||||
return require('repl');
|
||||
}
|
||||
if (options.stdio) {
|
||||
return compile_stdio();
|
||||
}
|
||||
if (options.eval) {
|
||||
return compile_script('terminal', sources[0]);
|
||||
return compile_script('unknown', sources[0]);
|
||||
}
|
||||
if (!(sources.length)) {
|
||||
usage();
|
||||
return usage();
|
||||
}
|
||||
separator = sources.indexOf('--');
|
||||
flags = [];
|
||||
@@ -42,7 +51,7 @@
|
||||
};
|
||||
// The "--version" message.
|
||||
version = function version() {
|
||||
puts("CoffeeScript version " + coffee.VERSION);
|
||||
puts("CoffeeScript version " + CoffeeScript.VERSION);
|
||||
return process.exit(0);
|
||||
};
|
||||
// Compiles the source CoffeeScript, returning the desired JavaScript, tokens,
|
||||
@@ -64,36 +73,47 @@
|
||||
// Compile a single source script, containing the given code, according to the
|
||||
// requested options. Both compile_scripts and watch_scripts share this method.
|
||||
compile_script = function compile_script(source, code) {
|
||||
var js, o, opts;
|
||||
opts = options;
|
||||
o = opts.no_wrap ? {
|
||||
no_wrap: true
|
||||
} : {};
|
||||
var js, o;
|
||||
o = options;
|
||||
try {
|
||||
if (opts.tokens) {
|
||||
return coffee.print_tokens(coffee.tokenize(code));
|
||||
} else if (opts.tree) {
|
||||
return puts(coffee.tree(code).toString());
|
||||
if (o.tokens) {
|
||||
return print_tokens(CoffeeScript.tokenize(code));
|
||||
} else if (o.tree) {
|
||||
return puts(CoffeeScript.tree(code).toString());
|
||||
} else {
|
||||
js = coffee.compile(code, o);
|
||||
if (opts.run) {
|
||||
js = CoffeeScript.compile(code, compile_options());
|
||||
if (o.run) {
|
||||
return eval(js);
|
||||
} else if (opts.print) {
|
||||
return puts(js);
|
||||
} else if (opts.lint) {
|
||||
} else if (o.lint) {
|
||||
return lint(js);
|
||||
} else if (o.print || o.eval) {
|
||||
return print(js);
|
||||
} else {
|
||||
return write_js(source, js);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (opts.watch) {
|
||||
if (o.watch) {
|
||||
return puts(err.message);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
// Listen for and compile scripts over stdio.
|
||||
compile_stdio = function compile_stdio() {
|
||||
var code;
|
||||
code = '';
|
||||
process.stdio.open();
|
||||
process.stdio.addListener('data', function(string) {
|
||||
if (string) {
|
||||
return code += string;
|
||||
}
|
||||
});
|
||||
return process.stdio.addListener('close', function() {
|
||||
return process.stdio.write(CoffeeScript.compile(code, compile_options()));
|
||||
});
|
||||
};
|
||||
// Watch a list of source CoffeeScript files, recompiling them every time the
|
||||
// files are updated.
|
||||
watch_scripts = function watch_scripts() {
|
||||
@@ -143,59 +163,29 @@
|
||||
jsl.write(js);
|
||||
return jsl.close();
|
||||
};
|
||||
// Pretty-print a token stream.
|
||||
print_tokens = function print_tokens(tokens) {
|
||||
var _a, _b, _c, strings, token;
|
||||
strings = (function() {
|
||||
_a = []; _b = tokens;
|
||||
for (_c = 0; _c < _b.length; _c++) {
|
||||
token = _b[_c];
|
||||
_a.push('[' + token[0] + ' ' + token[1].toString().replace(/\n/, '\\n') + ']');
|
||||
}
|
||||
return _a;
|
||||
}).call(this);
|
||||
return puts(strings.join(' '));
|
||||
};
|
||||
// Use OptionParser for all the options.
|
||||
parse_options = function parse_options() {
|
||||
var oparser, opts, paths;
|
||||
opts = (options = {});
|
||||
oparser = (option_parser = new optparse.OptionParser(SWITCHES));
|
||||
oparser.banner = BANNER;
|
||||
oparser.add('interactive', function() {
|
||||
return opts.interactive = true;
|
||||
});
|
||||
oparser.add('run', function() {
|
||||
return opts.run = true;
|
||||
});
|
||||
oparser.add('output', function(dir) {
|
||||
return opts.output = dir;
|
||||
});
|
||||
oparser.add('watch', function() {
|
||||
return opts.watch = true;
|
||||
});
|
||||
oparser.add('print', function() {
|
||||
return opts.print = true;
|
||||
});
|
||||
oparser.add('lint', function() {
|
||||
return opts.lint = true;
|
||||
});
|
||||
oparser.add('eval', function() {
|
||||
return opts.eval = true;
|
||||
});
|
||||
oparser.add('tokens', function() {
|
||||
return opts.tokens = true;
|
||||
});
|
||||
oparser.add('tree', function() {
|
||||
return opts.tree = true;
|
||||
});
|
||||
oparser.add('no-wrap', function() {
|
||||
return opts.no_wrap = true;
|
||||
});
|
||||
oparser.add('help', (function(__this) {
|
||||
var __func = function() {
|
||||
return usage();
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
oparser.add('version', (function(__this) {
|
||||
var __func = function() {
|
||||
return version();
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
paths = oparser.parse(process.ARGV);
|
||||
return sources = paths.slice(2, paths.length);
|
||||
option_parser = new optparse.OptionParser(SWITCHES, BANNER);
|
||||
options = option_parser.parse(process.ARGV);
|
||||
return sources = options.arguments.slice(2, options.arguments.length);
|
||||
};
|
||||
})();
|
||||
// The options to pass to the CoffeeScript compiler.
|
||||
compile_options = function compile_options() {
|
||||
return options['no-wrap'] ? {
|
||||
no_wrap: true
|
||||
} : {};
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -285,6 +285,8 @@
|
||||
// Indexing into an object or array.
|
||||
Index: [o("INDEX_START Expression INDEX_END", function() {
|
||||
return new IndexNode($2);
|
||||
}), o("SOAKED_INDEX_START Expression SOAKED_INDEX_END", function() {
|
||||
return new IndexNode($2, 'soak');
|
||||
})
|
||||
],
|
||||
// An object literal.
|
||||
@@ -343,8 +345,6 @@
|
||||
return new ValueNode(new LiteralNode('this'));
|
||||
}), o("@ Identifier", function() {
|
||||
return new ValueNode(new LiteralNode('this'), [new AccessorNode($2)]);
|
||||
}), o("@ Index", function() {
|
||||
return new ValueNode(new LiteralNode('this'), [$2]);
|
||||
})
|
||||
],
|
||||
// The range literal.
|
||||
@@ -416,13 +416,20 @@
|
||||
return new ParentheticalNode($2);
|
||||
})
|
||||
],
|
||||
// The condition for a while loop.
|
||||
WhileSource: [o("WHILE Expression", function() {
|
||||
return new WhileNode($2);
|
||||
}), o("WHILE Expression WHEN Expression", function() {
|
||||
return new WhileNode($2, {
|
||||
filter: $4
|
||||
});
|
||||
})
|
||||
],
|
||||
// The while loop. (there is no do..while).
|
||||
While: [o("WHILE Expression Block", function() {
|
||||
return new WhileNode($2, $3);
|
||||
}), o("WHILE Expression", function() {
|
||||
return new WhileNode($2, null);
|
||||
}), o("Expression WHILE Expression", function() {
|
||||
return new WhileNode($3, Expressions.wrap([$1]));
|
||||
While: [o("WhileSource Block", function() {
|
||||
return $1.add_body($2);
|
||||
}), o("Expression WhileSource", function() {
|
||||
return $2.add_body($1);
|
||||
})
|
||||
],
|
||||
// Array comprehensions, including guard and current index.
|
||||
@@ -462,7 +469,7 @@
|
||||
Switch: [o("SWITCH Expression INDENT Whens OUTDENT", function() {
|
||||
return $4.rewrite_condition($2);
|
||||
}), o("SWITCH Expression INDENT Whens ELSE Block OUTDENT", function() {
|
||||
return $4.rewrite_condition($2).add_else($6);
|
||||
return $4.rewrite_condition($2).add_else($6, true);
|
||||
})
|
||||
],
|
||||
// The inner list of whens.
|
||||
@@ -554,4 +561,4 @@
|
||||
}, {
|
||||
debug: false
|
||||
});
|
||||
})();
|
||||
})();
|
||||
|
||||
18
lib/lexer.js
18
lib/lexer.js
@@ -285,24 +285,30 @@
|
||||
// Multi-character operators are also literal tokens, so that Racc can assign
|
||||
// the proper order of operations.
|
||||
lex.prototype.literal_token = function literal_token() {
|
||||
var match, tag, value;
|
||||
var match, not_spaced, tag, value;
|
||||
match = this.chunk.match(OPERATOR);
|
||||
value = match && match[1];
|
||||
if (value && value.match(CODE)) {
|
||||
this.tag_parameters();
|
||||
}
|
||||
value = value || this.chunk.substr(0, 1);
|
||||
not_spaced = !this.prev() || !this.prev().spaced;
|
||||
tag = value;
|
||||
if (value.match(ASSIGNMENT)) {
|
||||
tag = 'ASSIGN';
|
||||
if (JS_FORBIDDEN.indexOf(this.value()) >= 0) {
|
||||
throw new Error('SyntaxError: Reserved word "' + this.value() + '" on line ' + this.line + ' can\'t be assigned');
|
||||
}
|
||||
}
|
||||
if (value === ';') {
|
||||
} else if (value === ';') {
|
||||
tag = 'TERMINATOR';
|
||||
}
|
||||
if (CALLABLE.indexOf(this.tag()) >= 0 && (!this.prev() || !this.prev().spaced)) {
|
||||
} else if (value === '[' && this.tag() === '?' && not_spaced) {
|
||||
tag = 'SOAKED_INDEX_START';
|
||||
this.soaked_index = true;
|
||||
this.tokens.pop();
|
||||
} else if (value === ']' && this.soaked_index) {
|
||||
tag = 'SOAKED_INDEX_END';
|
||||
this.soaked_index = false;
|
||||
} else if (CALLABLE.indexOf(this.tag()) >= 0 && not_spaced) {
|
||||
if (value === '(') {
|
||||
tag = 'CALL_START';
|
||||
}
|
||||
@@ -396,4 +402,4 @@
|
||||
lex.prototype.close_indentation = function close_indentation() {
|
||||
return this.outdent_token(this.indent);
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -41,4 +41,4 @@
|
||||
return factories[topId] = factories[topId] || this.reload(topId, path);
|
||||
};
|
||||
require.loader.loaders.unshift([".coffee", loader]);
|
||||
})();
|
||||
})();
|
||||
|
||||
161
lib/nodes.js
161
lib/nodes.js
@@ -1,5 +1,5 @@
|
||||
(function(){
|
||||
var AccessorNode, ArrayNode, AssignNode, CallNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IfNode, IndexNode, LiteralNode, Node, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, ValueNode, WhileNode, compact, del, flatten, inherit, merge, statement;
|
||||
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IfNode, IndexNode, LiteralNode, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, ValueNode, WhileNode, compact, del, flatten, inherit, merge, statement;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
(typeof process !== "undefined" && process !== null) ? process.mixin(require('scope')) : (this.exports = this);
|
||||
// Some helper functions
|
||||
@@ -90,12 +90,12 @@
|
||||
// generated code should be wrapped up in a closure. An options hash is passed
|
||||
// and cloned throughout, containing messages from higher in the AST,
|
||||
// information about the current scope, and indentation level.
|
||||
Node = (exports.Node = function Node() { });
|
||||
BaseNode = (exports.BaseNode = function BaseNode() { });
|
||||
// This is extremely important -- we convert JS statements into expressions
|
||||
// by wrapping them in a closure, only if it's possible, and we're not at
|
||||
// the top level of a block (which would be unnecessary), and we haven't
|
||||
// already been asked to return the result.
|
||||
Node.prototype.compile = function compile(o) {
|
||||
BaseNode.prototype.compile = function compile(o) {
|
||||
var closure, top;
|
||||
this.options = merge(o || {});
|
||||
this.indent = o.indent;
|
||||
@@ -110,23 +110,31 @@
|
||||
};
|
||||
// Statements converted into expressions share scope with their parent
|
||||
// closure, to preserve JavaScript-style lexical scope.
|
||||
Node.prototype.compile_closure = function compile_closure(o) {
|
||||
BaseNode.prototype.compile_closure = function compile_closure(o) {
|
||||
this.indent = o.indent;
|
||||
o.shared_scope = o.scope;
|
||||
return ClosureNode.wrap(this).compile(o);
|
||||
};
|
||||
// If the code generation wishes to use the result of a complex expression
|
||||
// in multiple places, ensure that the expression is only ever evaluated once.
|
||||
BaseNode.prototype.compile_reference = function compile_reference(o) {
|
||||
var compiled, reference;
|
||||
reference = new LiteralNode(o.scope.free_variable());
|
||||
compiled = new AssignNode(reference, this);
|
||||
return [compiled, reference];
|
||||
};
|
||||
// Quick short method for the current indentation level, plus tabbing in.
|
||||
Node.prototype.idt = function idt(tabs) {
|
||||
BaseNode.prototype.idt = function idt(tabs) {
|
||||
var _a, _b, _c, _d, i, idt;
|
||||
idt = (this.indent || '');
|
||||
_c = 0; _d = (tabs || 0);
|
||||
for (_b=0, i=_c; (_c <= _d ? i < _d : i > _d); (_c <= _d ? i += 1 : i -= 1), _b++) {
|
||||
for (_b = 0, i=_c; (_c <= _d ? i < _d : i > _d); (_c <= _d ? i += 1 : i -= 1), _b++) {
|
||||
idt += TAB;
|
||||
}
|
||||
return idt;
|
||||
};
|
||||
// Does this node, or any of its children, contain a node of a certain kind?
|
||||
Node.prototype.contains = function contains(block) {
|
||||
BaseNode.prototype.contains = function contains(block) {
|
||||
var _a, _b, node;
|
||||
_a = this.children;
|
||||
for (_b = 0; _b < _a.length; _b++) {
|
||||
@@ -134,14 +142,14 @@
|
||||
if (block(node)) {
|
||||
return true;
|
||||
}
|
||||
if (node instanceof Node && node.contains(block)) {
|
||||
if (node instanceof BaseNode && node.contains(block)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
// toString representation of the node, for inspecting the parse tree.
|
||||
Node.prototype.toString = function toString(idt) {
|
||||
BaseNode.prototype.toString = function toString(idt) {
|
||||
var _a, _b, _c, child;
|
||||
idt = idt || '';
|
||||
return '\n' + idt + this.type + (function() {
|
||||
@@ -154,24 +162,24 @@
|
||||
}).call(this).join('');
|
||||
};
|
||||
// Default implementations of the common node methods.
|
||||
Node.prototype.unwrap = function unwrap() {
|
||||
BaseNode.prototype.unwrap = function unwrap() {
|
||||
return this;
|
||||
};
|
||||
Node.prototype.children = [];
|
||||
Node.prototype.is_statement = function is_statement() {
|
||||
BaseNode.prototype.children = [];
|
||||
BaseNode.prototype.is_statement = function is_statement() {
|
||||
return false;
|
||||
};
|
||||
Node.prototype.is_statement_only = function is_statement_only() {
|
||||
BaseNode.prototype.is_statement_only = function is_statement_only() {
|
||||
return false;
|
||||
};
|
||||
Node.prototype.top_sensitive = function top_sensitive() {
|
||||
BaseNode.prototype.top_sensitive = function top_sensitive() {
|
||||
return false;
|
||||
};
|
||||
Node.prototype.operation_sensitive = function operation_sensitive() {
|
||||
BaseNode.prototype.operation_sensitive = function operation_sensitive() {
|
||||
return false;
|
||||
};
|
||||
// A collection of nodes, each one representing an expression.
|
||||
Expressions = (exports.Expressions = inherit(Node, {
|
||||
Expressions = (exports.Expressions = inherit(BaseNode, {
|
||||
type: 'Expressions',
|
||||
constructor: function constructor(nodes) {
|
||||
this.children = (this.expressions = compact(flatten(nodes || [])));
|
||||
@@ -204,7 +212,7 @@
|
||||
},
|
||||
compile: function compile(o) {
|
||||
o = o || {};
|
||||
return o.scope ? Node.prototype.compile.call(this, o) : this.compile_root(o);
|
||||
return o.scope ? BaseNode.prototype.compile.call(this, o) : this.compile_root(o);
|
||||
},
|
||||
// Compile each expression in the Expressions body.
|
||||
compile_node: function compile_node(o) {
|
||||
@@ -225,7 +233,7 @@
|
||||
o.scope = new Scope(null, this, null);
|
||||
code = o.globals ? this.compile_node(o) : this.compile_with_declarations(o);
|
||||
code = code.replace(TRAILING_WHITESPACE, '');
|
||||
return o.no_wrap ? code : "(function(){\n" + code + "\n})();";
|
||||
return o.no_wrap ? code : "(function(){\n" + code + "\n})();\n";
|
||||
},
|
||||
// Compile the expressions body, with declarations of all inner variables
|
||||
// pushed up to the top.
|
||||
@@ -279,7 +287,7 @@
|
||||
statement(Expressions);
|
||||
// Literals are static values that can be passed through directly into
|
||||
// JavaScript without translation, eg.: strings, numbers, true, false, null...
|
||||
LiteralNode = (exports.LiteralNode = inherit(Node, {
|
||||
LiteralNode = (exports.LiteralNode = inherit(BaseNode, {
|
||||
type: 'Literal',
|
||||
constructor: function constructor(value) {
|
||||
this.value = value;
|
||||
@@ -302,7 +310,7 @@
|
||||
}));
|
||||
LiteralNode.prototype.is_statement_only = LiteralNode.prototype.is_statement;
|
||||
// Return an expression, or wrap it in a closure and return it.
|
||||
ReturnNode = (exports.ReturnNode = inherit(Node, {
|
||||
ReturnNode = (exports.ReturnNode = inherit(BaseNode, {
|
||||
type: 'Return',
|
||||
constructor: function constructor(expression) {
|
||||
this.children = [(this.expression = expression)];
|
||||
@@ -319,9 +327,9 @@
|
||||
}));
|
||||
statement(ReturnNode, true);
|
||||
// A value, indexed or dotted into, or vanilla.
|
||||
ValueNode = (exports.ValueNode = inherit(Node, {
|
||||
ValueNode = (exports.ValueNode = inherit(BaseNode, {
|
||||
type: 'Value',
|
||||
SOAK: " == null ? undefined : ",
|
||||
SOAK: " == undefined ? undefined : ",
|
||||
constructor: function constructor(base, properties) {
|
||||
this.children = flatten([(this.base = base), (this.properties = (properties || []))]);
|
||||
return this;
|
||||
@@ -357,7 +365,7 @@
|
||||
return this.base.is_statement && this.base.is_statement() && !this.has_properties();
|
||||
},
|
||||
compile_node: function compile_node(o) {
|
||||
var _a, _b, baseline, code, only, op, part, parts, prop, props, soaked, temp;
|
||||
var _a, _b, baseline, complete, only, op, part, prop, props, soaked, temp;
|
||||
soaked = false;
|
||||
only = del(o, 'only_first');
|
||||
op = del(o, 'operation');
|
||||
@@ -366,33 +374,32 @@
|
||||
if (this.base instanceof ObjectNode && this.has_properties()) {
|
||||
baseline = '(' + baseline + ')';
|
||||
}
|
||||
parts = [baseline];
|
||||
complete = (this.last = baseline);
|
||||
_a = props;
|
||||
for (_b = 0; _b < _a.length; _b++) {
|
||||
prop = _a[_b];
|
||||
if (prop instanceof AccessorNode && prop.soak) {
|
||||
this.source = baseline;
|
||||
if (prop.soak_node) {
|
||||
soaked = true;
|
||||
if (this.base instanceof CallNode && prop === props[0]) {
|
||||
temp = o.scope.free_variable();
|
||||
parts[parts.length - 1] = '(' + temp + ' = ' + baseline + ')' + this.SOAK + ((baseline = temp + prop.compile(o)));
|
||||
complete = '(' + temp + ' = ' + complete + ')' + this.SOAK + ((baseline = temp + prop.compile(o)));
|
||||
} else {
|
||||
parts[parts.length - 1] += (this.SOAK + (baseline += prop.compile(o)));
|
||||
complete = complete + this.SOAK + (baseline += prop.compile(o));
|
||||
}
|
||||
} else {
|
||||
part = prop.compile(o);
|
||||
baseline += part;
|
||||
parts.push(part);
|
||||
complete += part;
|
||||
this.last = part;
|
||||
}
|
||||
}
|
||||
this.last = parts[parts.length - 1];
|
||||
this.source = parts.length > 1 ? parts.slice(0, (parts.length - 1)).join('') : null;
|
||||
code = parts.join('').replace(/\)\(\)\)/, '()))');
|
||||
return op && soaked ? '(' + code + ')' : code;
|
||||
return op && soaked ? '(' + complete + ')' : complete;
|
||||
}
|
||||
}));
|
||||
// Pass through CoffeeScript comments into JavaScript comments at the
|
||||
// same position.
|
||||
CommentNode = (exports.CommentNode = inherit(Node, {
|
||||
CommentNode = (exports.CommentNode = inherit(BaseNode, {
|
||||
type: 'Comment',
|
||||
constructor: function constructor(lines) {
|
||||
this.lines = lines;
|
||||
@@ -405,7 +412,7 @@
|
||||
statement(CommentNode);
|
||||
// Node for a function invocation. Takes care of converting super() calls into
|
||||
// calls against the prototype's function of the same name.
|
||||
CallNode = (exports.CallNode = inherit(Node, {
|
||||
CallNode = (exports.CallNode = inherit(BaseNode, {
|
||||
type: 'Call',
|
||||
constructor: function constructor(variable, args) {
|
||||
this.children = flatten([(this.variable = variable), (this.args = (args || []))]);
|
||||
@@ -466,19 +473,11 @@
|
||||
return _a;
|
||||
}).call(this);
|
||||
return this.prefix + meth + '.apply(' + obj + ', ' + args.join('') + ')';
|
||||
},
|
||||
// If the code generation wished to use the result of a function call
|
||||
// in multiple places, ensure that the function is only ever called once.
|
||||
compile_reference: function compile_reference(o) {
|
||||
var call, reference;
|
||||
reference = new LiteralNode(o.scope.free_variable());
|
||||
call = new ParentheticalNode(new AssignNode(reference, this));
|
||||
return [call, reference];
|
||||
}
|
||||
}));
|
||||
// Node to extend an object's prototype with an ancestor object.
|
||||
// After goog.inherits from the Closure Library.
|
||||
ExtendsNode = (exports.ExtendsNode = inherit(Node, {
|
||||
ExtendsNode = (exports.ExtendsNode = inherit(BaseNode, {
|
||||
type: 'Extends',
|
||||
constructor: function constructor(child, parent) {
|
||||
this.children = [(this.child = child), (this.parent = parent)];
|
||||
@@ -507,12 +506,12 @@
|
||||
statement(ExtendsNode);
|
||||
// A dotted accessor into a part of a value, or the :: shorthand for
|
||||
// an accessor into the object's prototype.
|
||||
AccessorNode = (exports.AccessorNode = inherit(Node, {
|
||||
AccessorNode = (exports.AccessorNode = inherit(BaseNode, {
|
||||
type: 'Accessor',
|
||||
constructor: function constructor(name, tag) {
|
||||
this.children = [(this.name = name)];
|
||||
this.prototype = tag === 'prototype';
|
||||
this.soak = tag === 'soak';
|
||||
this.soak_node = tag === 'soak';
|
||||
return this;
|
||||
},
|
||||
compile_node: function compile_node(o) {
|
||||
@@ -520,10 +519,11 @@
|
||||
}
|
||||
}));
|
||||
// An indexed accessor into a part of an array or object.
|
||||
IndexNode = (exports.IndexNode = inherit(Node, {
|
||||
IndexNode = (exports.IndexNode = inherit(BaseNode, {
|
||||
type: 'Index',
|
||||
constructor: function constructor(index) {
|
||||
constructor: function constructor(index, tag) {
|
||||
this.children = [(this.index = index)];
|
||||
this.soak_node = tag === 'soak';
|
||||
return this;
|
||||
},
|
||||
compile_node: function compile_node(o) {
|
||||
@@ -532,7 +532,7 @@
|
||||
}));
|
||||
// A range literal. Ranges can be used to extract portions (slices) of arrays,
|
||||
// or to specify a range for list comprehensions.
|
||||
RangeNode = (exports.RangeNode = inherit(Node, {
|
||||
RangeNode = (exports.RangeNode = inherit(BaseNode, {
|
||||
type: 'Range',
|
||||
constructor: function constructor(from, to, exclusive) {
|
||||
this.children = [(this.from = from), (this.to = to)];
|
||||
@@ -577,7 +577,7 @@
|
||||
// An array slice literal. Unlike JavaScript's Array#slice, the second parameter
|
||||
// specifies the index of the end of the slice (just like the first parameter)
|
||||
// is the index of the beginning.
|
||||
SliceNode = (exports.SliceNode = inherit(Node, {
|
||||
SliceNode = (exports.SliceNode = inherit(BaseNode, {
|
||||
type: 'Slice',
|
||||
constructor: function constructor(range) {
|
||||
this.children = [(this.range = range)];
|
||||
@@ -592,7 +592,7 @@
|
||||
}
|
||||
}));
|
||||
// An object literal.
|
||||
ObjectNode = (exports.ObjectNode = inherit(Node, {
|
||||
ObjectNode = (exports.ObjectNode = inherit(BaseNode, {
|
||||
type: 'Object',
|
||||
constructor: function constructor(props) {
|
||||
this.children = (this.objects = (this.properties = props || []));
|
||||
@@ -639,7 +639,7 @@
|
||||
}
|
||||
}));
|
||||
// An array literal.
|
||||
ArrayNode = (exports.ArrayNode = inherit(Node, {
|
||||
ArrayNode = (exports.ArrayNode = inherit(BaseNode, {
|
||||
type: 'Array',
|
||||
constructor: function constructor(objects) {
|
||||
this.children = (this.objects = objects || []);
|
||||
@@ -694,7 +694,7 @@
|
||||
}
|
||||
});
|
||||
// Setting the value of a local variable, or the value of an object property.
|
||||
AssignNode = (exports.AssignNode = inherit(Node, {
|
||||
AssignNode = (exports.AssignNode = inherit(BaseNode, {
|
||||
type: 'Assign',
|
||||
PROTO_ASSIGN: /^(\S+)\.prototype/,
|
||||
LEADING_DOT: /^\.(prototype\.)?/,
|
||||
@@ -798,7 +798,7 @@
|
||||
}));
|
||||
// A function definition. The only node that creates a new Scope.
|
||||
// A CodeNode does not have any children -- they're within the new scope.
|
||||
CodeNode = (exports.CodeNode = inherit(Node, {
|
||||
CodeNode = (exports.CodeNode = inherit(BaseNode, {
|
||||
type: 'Code',
|
||||
constructor: function constructor(params, body, tag) {
|
||||
this.params = params;
|
||||
@@ -865,7 +865,7 @@
|
||||
}));
|
||||
// A splat, either as a parameter to a function, an argument to a call,
|
||||
// or in a destructuring assignment.
|
||||
SplatNode = (exports.SplatNode = inherit(Node, {
|
||||
SplatNode = (exports.SplatNode = inherit(BaseNode, {
|
||||
type: 'Splat',
|
||||
constructor: function constructor(name) {
|
||||
if (!(name.compile)) {
|
||||
@@ -875,7 +875,8 @@
|
||||
return this;
|
||||
},
|
||||
compile_node: function compile_node(o) {
|
||||
return (typeof this.index !== "undefined" && this.index !== null) ? this.compile_param(o) : this.name.compile(o);
|
||||
var _a;
|
||||
return (typeof (_a = this.index) !== "undefined" && _a !== null) ? this.compile_param(o) : this.name.compile(o);
|
||||
},
|
||||
compile_param: function compile_param(o) {
|
||||
var name;
|
||||
@@ -889,10 +890,15 @@
|
||||
}));
|
||||
// A while loop, the only sort of low-level loop exposed by CoffeeScript. From
|
||||
// it, all other loops can be manufactured.
|
||||
WhileNode = (exports.WhileNode = inherit(Node, {
|
||||
WhileNode = (exports.WhileNode = inherit(BaseNode, {
|
||||
type: 'While',
|
||||
constructor: function constructor(condition, body) {
|
||||
this.children = [(this.condition = condition), (this.body = body)];
|
||||
constructor: function constructor(condition, opts) {
|
||||
this.children = [(this.condition = condition)];
|
||||
this.filter = opts && opts.filter;
|
||||
return this;
|
||||
},
|
||||
add_body: function add_body(body) {
|
||||
this.children.push((this.body = body));
|
||||
return this;
|
||||
},
|
||||
top_sensitive: function top_sensitive() {
|
||||
@@ -909,20 +915,25 @@
|
||||
if (!top) {
|
||||
rvar = o.scope.free_variable();
|
||||
set = this.idt() + rvar + ' = [];\n';
|
||||
this.body = PushNode.wrap(rvar, this.body);
|
||||
if (this.body) {
|
||||
this.body = PushNode.wrap(rvar, this.body);
|
||||
}
|
||||
}
|
||||
post = returns ? '\n' + this.idt() + 'return ' + rvar + ';' : '';
|
||||
pre = set + this.idt() + 'while (' + cond + ')';
|
||||
if (!this.body) {
|
||||
return pre + ' null;' + post;
|
||||
}
|
||||
if (this.filter) {
|
||||
this.body = Expressions.wrap([new IfNode(this.filter, this.body)]);
|
||||
}
|
||||
return pre + ' {\n' + this.body.compile(o) + '\n' + this.idt() + '}' + post;
|
||||
}
|
||||
}));
|
||||
statement(WhileNode);
|
||||
// Simple Arithmetic and logical operations. Performs some conversion from
|
||||
// CoffeeScript operations into their JavaScript equivalents.
|
||||
OpNode = (exports.OpNode = inherit(Node, {
|
||||
OpNode = (exports.OpNode = inherit(BaseNode, {
|
||||
type: 'Op',
|
||||
CONVERSIONS: {
|
||||
'==': '===',
|
||||
@@ -1008,7 +1019,7 @@
|
||||
}
|
||||
}));
|
||||
// A try/catch/finally block.
|
||||
TryNode = (exports.TryNode = inherit(Node, {
|
||||
TryNode = (exports.TryNode = inherit(BaseNode, {
|
||||
type: 'Try',
|
||||
constructor: function constructor(attempt, error, recovery, ensure) {
|
||||
this.children = compact([(this.attempt = attempt), (this.recovery = recovery), (this.ensure = ensure)]);
|
||||
@@ -1029,7 +1040,7 @@
|
||||
}));
|
||||
statement(TryNode);
|
||||
// Throw an exception.
|
||||
ThrowNode = (exports.ThrowNode = inherit(Node, {
|
||||
ThrowNode = (exports.ThrowNode = inherit(BaseNode, {
|
||||
type: 'Throw',
|
||||
constructor: function constructor(expression) {
|
||||
this.children = [(this.expression = expression)];
|
||||
@@ -1041,7 +1052,7 @@
|
||||
}));
|
||||
statement(ThrowNode, true);
|
||||
// Check an expression for existence (meaning not null or undefined).
|
||||
ExistenceNode = (exports.ExistenceNode = inherit(Node, {
|
||||
ExistenceNode = (exports.ExistenceNode = inherit(BaseNode, {
|
||||
type: 'Existence',
|
||||
constructor: function constructor(expression) {
|
||||
this.children = [(this.expression = expression)];
|
||||
@@ -1056,7 +1067,7 @@
|
||||
_a = [variable, variable];
|
||||
first = _a[0];
|
||||
second = _a[1];
|
||||
if (variable instanceof CallNode) {
|
||||
if (variable instanceof CallNode || (variable instanceof ValueNode && variable.has_properties())) {
|
||||
_b = variable.compile_reference(o);
|
||||
first = _b[0];
|
||||
second = _b[1];
|
||||
@@ -1064,7 +1075,7 @@
|
||||
return '(typeof ' + first.compile(o) + ' !== "undefined" && ' + second.compile(o) + ' !== null)';
|
||||
};
|
||||
// An extra set of parentheses, specified explicitly in the source.
|
||||
ParentheticalNode = (exports.ParentheticalNode = inherit(Node, {
|
||||
ParentheticalNode = (exports.ParentheticalNode = inherit(BaseNode, {
|
||||
type: 'Paren',
|
||||
constructor: function constructor(expression) {
|
||||
this.children = [(this.expression = expression)];
|
||||
@@ -1090,7 +1101,7 @@
|
||||
// into a for loop. Also acts as an expression, able to return the result
|
||||
// of the comprehenion. Unlike Python array comprehensions, it's able to pass
|
||||
// the current index of the loop as a second parameter.
|
||||
ForNode = (exports.ForNode = inherit(Node, {
|
||||
ForNode = (exports.ForNode = inherit(BaseNode, {
|
||||
type: 'For',
|
||||
constructor: function constructor(body, source, name, index) {
|
||||
var _a;
|
||||
@@ -1133,7 +1144,7 @@
|
||||
if (range) {
|
||||
index_var = scope.free_variable();
|
||||
source_part = source.compile_variables(o);
|
||||
for_part = index_var + '=0, ' + source.compile(merge(o, {
|
||||
for_part = index_var + ' = 0, ' + source.compile(merge(o, {
|
||||
index: ivar,
|
||||
step: this.step
|
||||
})) + ', ' + index_var + '++';
|
||||
@@ -1188,7 +1199,7 @@
|
||||
// expression by pushing down requested returns to the expression bodies.
|
||||
// Single-expression IfNodes are compiled into ternary operators if possible,
|
||||
// because ternaries are first-class returnable assignable expressions.
|
||||
IfNode = (exports.IfNode = inherit(Node, {
|
||||
IfNode = (exports.IfNode = inherit(BaseNode, {
|
||||
type: 'If',
|
||||
constructor: function constructor(condition, body, else_body, tags) {
|
||||
this.condition = condition;
|
||||
@@ -1246,9 +1257,15 @@
|
||||
return this;
|
||||
},
|
||||
// Rewrite a chain of IfNodes to add a default case as the final else.
|
||||
add_else: function add_else(exprs) {
|
||||
this.is_chain() ? this.else_body.add_else(exprs) : (this.else_body = exprs && exprs.unwrap());
|
||||
this.children.push(exprs);
|
||||
add_else: function add_else(exprs, statement) {
|
||||
if (this.is_chain()) {
|
||||
this.else_body.add_else(exprs, statement);
|
||||
} else {
|
||||
if (!(statement)) {
|
||||
exprs = exprs.unwrap();
|
||||
}
|
||||
this.children.push((this.else_body = exprs));
|
||||
}
|
||||
return this;
|
||||
},
|
||||
// If the else_body is an IfNode itself, then we've got an if-else chain.
|
||||
@@ -1308,4 +1325,4 @@
|
||||
return if_part + ' : ' + else_part;
|
||||
}
|
||||
}));
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
(function(){
|
||||
var LONG_FLAG, OPTIONAL, SHORT_FLAG, build_rule, build_rules, op, spaces;
|
||||
// Create an OptionParser with a list of valid options.
|
||||
op = (exports.OptionParser = function OptionParser(rules) {
|
||||
this.banner = 'Usage: [Options]';
|
||||
op = (exports.OptionParser = function OptionParser(rules, banner) {
|
||||
this.banner = banner || 'Usage: [Options]';
|
||||
this.options_title = 'Available options:';
|
||||
this.rules = build_rules(rules);
|
||||
this.actions = {};
|
||||
return this;
|
||||
});
|
||||
// Add a callback to fire when a particular option is encountered.
|
||||
op.prototype.add = function add(value, callback) {
|
||||
return this.actions[value] = callback;
|
||||
};
|
||||
// Parse the argument array, calling defined callbacks, returning the remaining non-option arguments.
|
||||
op.prototype.parse = function parse(args) {
|
||||
var _a, _b, arg, callback, is_option, results, rule, value;
|
||||
results = [];
|
||||
var _a, _b, arg, is_option, options, rule;
|
||||
arguments = Array.prototype.slice.call(arguments, 0);
|
||||
options = {
|
||||
arguments: []
|
||||
};
|
||||
args = args.concat([]);
|
||||
while (((arg = args.shift()))) {
|
||||
is_option = false;
|
||||
@@ -23,20 +21,16 @@
|
||||
for (_b = 0; _b < _a.length; _b++) {
|
||||
rule = _a[_b];
|
||||
if (rule.letter === arg || rule.flag === arg) {
|
||||
callback = this.actions[rule.name];
|
||||
value = rule.argument && args.shift();
|
||||
if (callback) {
|
||||
callback(value);
|
||||
}
|
||||
options[rule.name] = rule.argument ? args.shift() : true;
|
||||
is_option = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(is_option)) {
|
||||
results.push(arg);
|
||||
options.arguments.push(arg);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
return options;
|
||||
};
|
||||
// Return the help text for this OptionParser, for --help and such.
|
||||
op.prototype.help = function help() {
|
||||
@@ -114,4 +108,4 @@
|
||||
}
|
||||
return builder.join('');
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
136
lib/parser.js
136
lib/parser.js
File diff suppressed because one or more lines are too long
@@ -29,4 +29,4 @@
|
||||
process.stdio.addListener('data', readline);
|
||||
process.stdio.open();
|
||||
print(prompt);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// indentation, and single-line flavors of expressions.
|
||||
exports.Rewriter = (re = function re() { });
|
||||
// Tokens that must be balanced.
|
||||
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END']];
|
||||
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END'], ['SOAKED_INDEX_START', 'SOAKED_INDEX_END']];
|
||||
// Tokens that signal the start of a balanced pair.
|
||||
EXPRESSION_START = (function() {
|
||||
_a = []; _b = BALANCED_PAIRS;
|
||||
@@ -212,7 +212,7 @@
|
||||
idx = tag === 'OUTDENT' ? i + 1 : i;
|
||||
stack_pointer = tag === 'INDENT' ? 2 : 1;
|
||||
_k = 0; _l = stack[stack.length - stack_pointer];
|
||||
for (_j=0, tmp=_k; (_k <= _l ? tmp < _l : tmp > _l); (_k <= _l ? tmp += 1 : tmp -= 1), _j++) {
|
||||
for (_j = 0, tmp=_k; (_k <= _l ? tmp < _l : tmp > _l); (_k <= _l ? tmp += 1 : tmp -= 1), _j++) {
|
||||
this.tokens.splice(idx, 0, ['CALL_END', ')', token[2]]);
|
||||
}
|
||||
size = stack[stack.length - stack_pointer] + 1;
|
||||
@@ -295,7 +295,7 @@
|
||||
levels[open] -= 1;
|
||||
}
|
||||
if (levels[open] < 0) {
|
||||
throw "too many " + token[1];
|
||||
throw new Error("too many " + token[1]);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@@ -380,4 +380,4 @@
|
||||
});
|
||||
})(this));
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -111,4 +111,4 @@
|
||||
Scope.prototype.compiled_assignments = function compiled_assignments() {
|
||||
return this.assigned_variables().join(', ');
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"description": "Unfancy JavaScript",
|
||||
"keywords": ["javascript", "language"],
|
||||
"author": "Jeremy Ashkenas",
|
||||
"version": "0.5.0"
|
||||
"version": "0.5.2"
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ parser.lexer: {
|
||||
showPosition: -> @pos
|
||||
}
|
||||
|
||||
exports.VERSION: '0.5.0'
|
||||
exports.VERSION: '0.5.2'
|
||||
|
||||
# Compile CoffeeScript to JavaScript, using the Coffee/Jison compiler.
|
||||
exports.compile: (code, options) ->
|
||||
@@ -38,8 +38,8 @@ exports.tokenize: (code) ->
|
||||
exports.tree: (code) ->
|
||||
parser.parse lexer.tokenize code
|
||||
|
||||
# Pretty-print a token stream.
|
||||
exports.print_tokens: (tokens) ->
|
||||
strings: for token in tokens
|
||||
'[' + token[0] + ' ' + token[1].toString().replace(/\n/, '\\n') + ']'
|
||||
puts strings.join(' ')
|
||||
# Activate CoffeeScript in the browser by having it compile and eval
|
||||
# all script tags with a content-type of text/coffeescript.
|
||||
if document? and document.getElementsByTagName
|
||||
for tag in document.getElementsByTagName('script') when tag.type is 'text/coffeescript'
|
||||
eval exports.compile tag.innerHTML
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
fs: require 'fs'
|
||||
path: require 'path'
|
||||
coffee: require 'coffee-script'
|
||||
optparse: require('optparse')
|
||||
fs: require 'fs'
|
||||
path: require 'path'
|
||||
optparse: require 'optparse'
|
||||
CoffeeScript: require 'coffee-script'
|
||||
|
||||
BANNER: '''
|
||||
coffee compiles CoffeeScript source files into JavaScript.
|
||||
@@ -17,10 +17,11 @@ SWITCHES: [
|
||||
['-w', '--watch', 'watch scripts for changes, and recompile']
|
||||
['-p', '--print', 'print the compiled JavaScript to stdout']
|
||||
['-l', '--lint', 'pipe the compiled JavaScript through JSLint']
|
||||
['-s', '--stdio', 'listen for and compile scripts over stdio']
|
||||
['-e', '--eval', 'compile a string from the command line']
|
||||
['-n', '--no-wrap', 'compile without the top-level function wrapper']
|
||||
['-t', '--tokens', 'print the tokens that the lexer produces']
|
||||
['-tr','--tree', 'print the parse tree that Jison produces']
|
||||
['-n', '--no-wrap', 'compile without the top-level function wrapper']
|
||||
['-v', '--version', 'display CoffeeScript version']
|
||||
['-h', '--help', 'display this help message']
|
||||
]
|
||||
@@ -32,9 +33,12 @@ option_parser: null
|
||||
# The CommandLine handles all of the functionality of the `coffee` utility.
|
||||
exports.run: ->
|
||||
parse_options()
|
||||
return require 'repl' if options.interactive
|
||||
return compile_script 'terminal', sources[0] if options.eval
|
||||
usage() unless sources.length
|
||||
return usage() if options.help
|
||||
return version() if options.version
|
||||
return require 'repl' if options.interactive
|
||||
return compile_stdio() if options.stdio
|
||||
return compile_script 'unknown', sources[0] if options.eval
|
||||
return usage() unless sources.length
|
||||
separator: sources.indexOf '--'
|
||||
flags: []
|
||||
if separator >= 0
|
||||
@@ -52,7 +56,7 @@ usage: ->
|
||||
|
||||
# The "--version" message.
|
||||
version: ->
|
||||
puts "CoffeeScript version " + coffee.VERSION
|
||||
puts "CoffeeScript version " + CoffeeScript.VERSION
|
||||
process.exit 0
|
||||
|
||||
# Compiles the source CoffeeScript, returning the desired JavaScript, tokens,
|
||||
@@ -62,23 +66,30 @@ compile_scripts: ->
|
||||
fs.readFile source, (err, code) -> compile_script(source, code)
|
||||
compile(source) for source in sources
|
||||
|
||||
|
||||
# Compile a single source script, containing the given code, according to the
|
||||
# requested options. Both compile_scripts and watch_scripts share this method.
|
||||
compile_script: (source, code) ->
|
||||
opts: options
|
||||
o: if opts.no_wrap then {no_wrap: true} else {}
|
||||
o: options
|
||||
try
|
||||
if opts.tokens then coffee.print_tokens coffee.tokenize code
|
||||
else if opts.tree then puts coffee.tree(code).toString()
|
||||
if o.tokens then print_tokens CoffeeScript.tokenize code
|
||||
else if o.tree then puts CoffeeScript.tree(code).toString()
|
||||
else
|
||||
js: coffee.compile code, o
|
||||
if opts.run then eval js
|
||||
else if opts.print then puts js
|
||||
else if opts.lint then lint js
|
||||
else write_js source, js
|
||||
js: CoffeeScript.compile code, compile_options()
|
||||
if o.run then eval js
|
||||
else if o.lint then lint js
|
||||
else if o.print or o.eval then print js
|
||||
else write_js source, js
|
||||
catch err
|
||||
if opts.watch then puts err.message else throw err
|
||||
if o.watch then puts err.message else throw err
|
||||
|
||||
# Listen for and compile scripts over stdio.
|
||||
compile_stdio: ->
|
||||
code: ''
|
||||
process.stdio.open()
|
||||
process.stdio.addListener 'data', (string) ->
|
||||
code += string if string
|
||||
process.stdio.addListener 'close', ->
|
||||
process.stdio.write CoffeeScript.compile code, compile_options()
|
||||
|
||||
# Watch a list of source CoffeeScript files, recompiling them every time the
|
||||
# files are updated.
|
||||
@@ -106,25 +117,18 @@ lint: (js) ->
|
||||
jsl.write js
|
||||
jsl.close()
|
||||
|
||||
# Pretty-print a token stream.
|
||||
print_tokens: (tokens) ->
|
||||
strings: for token in tokens
|
||||
'[' + token[0] + ' ' + token[1].toString().replace(/\n/, '\\n') + ']'
|
||||
puts strings.join(' ')
|
||||
|
||||
# Use OptionParser for all the options.
|
||||
parse_options: ->
|
||||
opts: options: {}
|
||||
oparser: option_parser: new optparse.OptionParser SWITCHES
|
||||
oparser.banner: BANNER
|
||||
|
||||
oparser.add 'interactive', -> opts.interactive: true
|
||||
oparser.add 'run', -> opts.run: true
|
||||
oparser.add 'output', (dir) -> opts.output: dir
|
||||
oparser.add 'watch', -> opts.watch: true
|
||||
oparser.add 'print', -> opts.print: true
|
||||
oparser.add 'lint', -> opts.lint: true
|
||||
oparser.add 'eval', -> opts.eval: true
|
||||
oparser.add 'tokens', -> opts.tokens: true
|
||||
oparser.add 'tree', -> opts.tree: true
|
||||
oparser.add 'no-wrap', -> opts.no_wrap: true
|
||||
oparser.add 'help', => usage()
|
||||
oparser.add 'version', => version()
|
||||
|
||||
paths: oparser.parse(process.ARGV)
|
||||
sources: paths[2...paths.length]
|
||||
option_parser: new optparse.OptionParser SWITCHES, BANNER
|
||||
options: option_parser.parse(process.ARGV)
|
||||
sources: options.arguments[2...options.arguments.length]
|
||||
|
||||
# The options to pass to the CoffeeScript compiler.
|
||||
compile_options: ->
|
||||
if options['no-wrap'] then {no_wrap: true} else {}
|
||||
|
||||
@@ -251,6 +251,7 @@ grammar: {
|
||||
# Indexing into an object or array.
|
||||
Index: [
|
||||
o "INDEX_START Expression INDEX_END", -> new IndexNode($2)
|
||||
o "SOAKED_INDEX_START Expression SOAKED_INDEX_END", -> new IndexNode($2, 'soak')
|
||||
]
|
||||
|
||||
# An object literal.
|
||||
@@ -300,7 +301,6 @@ grammar: {
|
||||
This: [
|
||||
o "@", -> new ValueNode(new LiteralNode('this'))
|
||||
o "@ Identifier", -> new ValueNode(new LiteralNode('this'), [new AccessorNode($2)])
|
||||
o "@ Index", -> new ValueNode(new LiteralNode('this'), [$2])
|
||||
]
|
||||
|
||||
# The range literal.
|
||||
@@ -361,11 +361,16 @@ grammar: {
|
||||
o "( Expression )", -> new ParentheticalNode($2)
|
||||
]
|
||||
|
||||
# The condition for a while loop.
|
||||
WhileSource: [
|
||||
o "WHILE Expression", -> new WhileNode($2)
|
||||
o "WHILE Expression WHEN Expression", -> new WhileNode($2, {filter : $4})
|
||||
]
|
||||
|
||||
# The while loop. (there is no do..while).
|
||||
While: [
|
||||
o "WHILE Expression Block", -> new WhileNode($2, $3)
|
||||
o "WHILE Expression", -> new WhileNode($2, null)
|
||||
o "Expression WHILE Expression", -> new WhileNode($3, Expressions.wrap([$1]))
|
||||
o "WhileSource Block", -> $1.add_body $2
|
||||
o "Expression WhileSource", -> $2.add_body $1
|
||||
]
|
||||
|
||||
# Array comprehensions, including guard and current index.
|
||||
@@ -385,16 +390,14 @@ grammar: {
|
||||
ForSource: [
|
||||
o "IN Expression", -> {source: $2}
|
||||
o "OF Expression", -> {source: $2, object: true}
|
||||
o "ForSource WHEN Expression", ->
|
||||
$1.filter: $3; $1
|
||||
o "ForSource BY Expression", ->
|
||||
$1.step: $3; $1
|
||||
o "ForSource WHEN Expression", -> $1.filter: $3; $1
|
||||
o "ForSource BY Expression", -> $1.step: $3; $1
|
||||
]
|
||||
|
||||
# Switch/When blocks.
|
||||
Switch: [
|
||||
o "SWITCH Expression INDENT Whens OUTDENT", -> $4.rewrite_condition($2)
|
||||
o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> $4.rewrite_condition($2).add_else($6)
|
||||
o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> $4.rewrite_condition($2).add_else($6, true)
|
||||
]
|
||||
|
||||
# The inner list of whens.
|
||||
@@ -407,8 +410,7 @@ grammar: {
|
||||
When: [
|
||||
o "LEADING_WHEN SimpleArgs Block", -> new IfNode($2, $3, null, {statement: true})
|
||||
o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode($2, $3, null, {statement: true})
|
||||
o "Comment TERMINATOR When", ->
|
||||
$3.comment: $1; $3
|
||||
o "Comment TERMINATOR When", -> $3.comment: $1; $3
|
||||
]
|
||||
|
||||
# The most basic form of "if".
|
||||
|
||||
@@ -246,13 +246,21 @@ lex::literal_token: ->
|
||||
value: match and match[1]
|
||||
@tag_parameters() if value and value.match(CODE)
|
||||
value ||= @chunk.substr(0, 1)
|
||||
not_spaced: not @prev() or not @prev().spaced
|
||||
tag: value
|
||||
if value.match(ASSIGNMENT)
|
||||
tag: 'ASSIGN'
|
||||
throw new Error('SyntaxError: Reserved word "' + @value() + '" on line ' + @line + ' can\'t be assigned') if JS_FORBIDDEN.indexOf(@value()) >= 0
|
||||
tag: 'TERMINATOR' if value == ';'
|
||||
|
||||
if CALLABLE.indexOf(@tag()) >= 0 and (not @prev() or not @prev().spaced)
|
||||
else if value is ';'
|
||||
tag: 'TERMINATOR'
|
||||
else if value is '[' and @tag() is '?' and not_spaced
|
||||
tag: 'SOAKED_INDEX_START'
|
||||
@soaked_index: true
|
||||
@tokens.pop()
|
||||
else if value is ']' and @soaked_index
|
||||
tag: 'SOAKED_INDEX_END'
|
||||
@soaked_index: false
|
||||
else if CALLABLE.indexOf(@tag()) >= 0 and not_spaced
|
||||
tag: 'CALL_START' if value is '('
|
||||
tag: 'INDEX_START' if value is '['
|
||||
@token tag, value
|
||||
|
||||
144
src/nodes.coffee
144
src/nodes.coffee
@@ -54,13 +54,13 @@ statement: (klass, only) ->
|
||||
# generated code should be wrapped up in a closure. An options hash is passed
|
||||
# and cloned throughout, containing messages from higher in the AST,
|
||||
# information about the current scope, and indentation level.
|
||||
Node: exports.Node: ->
|
||||
BaseNode: exports.BaseNode: ->
|
||||
|
||||
# This is extremely important -- we convert JS statements into expressions
|
||||
# by wrapping them in a closure, only if it's possible, and we're not at
|
||||
# the top level of a block (which would be unnecessary), and we haven't
|
||||
# already been asked to return the result.
|
||||
Node::compile: (o) ->
|
||||
BaseNode::compile: (o) ->
|
||||
@options: merge o or {}
|
||||
@indent: o.indent
|
||||
del @options, 'operation' unless @operation_sensitive()
|
||||
@@ -72,39 +72,46 @@ Node::compile: (o) ->
|
||||
|
||||
# Statements converted into expressions share scope with their parent
|
||||
# closure, to preserve JavaScript-style lexical scope.
|
||||
Node::compile_closure: (o) ->
|
||||
BaseNode::compile_closure: (o) ->
|
||||
@indent: o.indent
|
||||
o.shared_scope: o.scope
|
||||
ClosureNode.wrap(this).compile(o)
|
||||
|
||||
# If the code generation wishes to use the result of a complex expression
|
||||
# in multiple places, ensure that the expression is only ever evaluated once.
|
||||
BaseNode::compile_reference: (o) ->
|
||||
reference: new LiteralNode(o.scope.free_variable())
|
||||
compiled: new AssignNode(reference, this)
|
||||
[compiled, reference]
|
||||
|
||||
# Quick short method for the current indentation level, plus tabbing in.
|
||||
Node::idt: (tabs) ->
|
||||
BaseNode::idt: (tabs) ->
|
||||
idt: (@indent || '')
|
||||
idt += TAB for i in [0...(tabs or 0)]
|
||||
idt
|
||||
|
||||
# Does this node, or any of its children, contain a node of a certain kind?
|
||||
Node::contains: (block) ->
|
||||
BaseNode::contains: (block) ->
|
||||
for node in @children
|
||||
return true if block(node)
|
||||
return true if node instanceof Node and node.contains block
|
||||
return true if node instanceof BaseNode and node.contains block
|
||||
false
|
||||
|
||||
# toString representation of the node, for inspecting the parse tree.
|
||||
Node::toString: (idt) ->
|
||||
BaseNode::toString: (idt) ->
|
||||
idt ||= ''
|
||||
'\n' + idt + @type + (child.toString(idt + TAB) for child in @children).join('')
|
||||
|
||||
# Default implementations of the common node methods.
|
||||
Node::unwrap: -> this
|
||||
Node::children: []
|
||||
Node::is_statement: -> false
|
||||
Node::is_statement_only: -> false
|
||||
Node::top_sensitive: -> false
|
||||
Node::operation_sensitive: -> false
|
||||
BaseNode::unwrap: -> this
|
||||
BaseNode::children: []
|
||||
BaseNode::is_statement: -> false
|
||||
BaseNode::is_statement_only: -> false
|
||||
BaseNode::top_sensitive: -> false
|
||||
BaseNode::operation_sensitive: -> false
|
||||
|
||||
# A collection of nodes, each one representing an expression.
|
||||
Expressions: exports.Expressions: inherit Node, {
|
||||
Expressions: exports.Expressions: inherit BaseNode, {
|
||||
type: 'Expressions'
|
||||
|
||||
constructor: (nodes) ->
|
||||
@@ -137,7 +144,7 @@ Expressions: exports.Expressions: inherit Node, {
|
||||
|
||||
compile: (o) ->
|
||||
o ||= {}
|
||||
if o.scope then Node::compile.call(this, o) else @compile_root(o)
|
||||
if o.scope then BaseNode::compile.call(this, o) else @compile_root(o)
|
||||
|
||||
# Compile each expression in the Expressions body.
|
||||
compile_node: (o) ->
|
||||
@@ -149,7 +156,7 @@ Expressions: exports.Expressions: inherit Node, {
|
||||
o.scope: new Scope(null, this, null)
|
||||
code: if o.globals then @compile_node(o) else @compile_with_declarations(o)
|
||||
code: code.replace(TRAILING_WHITESPACE, '')
|
||||
if o.no_wrap then code else "(function(){\n"+code+"\n})();"
|
||||
if o.no_wrap then code else "(function(){\n"+code+"\n})();\n"
|
||||
|
||||
# Compile the expressions body, with declarations of all inner variables
|
||||
# pushed up to the top.
|
||||
@@ -185,7 +192,7 @@ statement Expressions
|
||||
|
||||
# Literals are static values that can be passed through directly into
|
||||
# JavaScript without translation, eg.: strings, numbers, true, false, null...
|
||||
LiteralNode: exports.LiteralNode: inherit Node, {
|
||||
LiteralNode: exports.LiteralNode: inherit BaseNode, {
|
||||
type: 'Literal'
|
||||
|
||||
constructor: (value) ->
|
||||
@@ -210,7 +217,7 @@ LiteralNode: exports.LiteralNode: inherit Node, {
|
||||
LiteralNode::is_statement_only: LiteralNode::is_statement
|
||||
|
||||
# Return an expression, or wrap it in a closure and return it.
|
||||
ReturnNode: exports.ReturnNode: inherit Node, {
|
||||
ReturnNode: exports.ReturnNode: inherit BaseNode, {
|
||||
type: 'Return'
|
||||
|
||||
constructor: (expression) ->
|
||||
@@ -226,10 +233,10 @@ ReturnNode: exports.ReturnNode: inherit Node, {
|
||||
statement ReturnNode, true
|
||||
|
||||
# A value, indexed or dotted into, or vanilla.
|
||||
ValueNode: exports.ValueNode: inherit Node, {
|
||||
ValueNode: exports.ValueNode: inherit BaseNode, {
|
||||
type: 'Value'
|
||||
|
||||
SOAK: " == null ? undefined : "
|
||||
SOAK: " == undefined ? undefined : "
|
||||
|
||||
constructor: (base, properties) ->
|
||||
@children: flatten [@base: base, @properties: (properties or [])]
|
||||
@@ -272,31 +279,30 @@ ValueNode: exports.ValueNode: inherit Node, {
|
||||
props: if only then @properties[0...@properties.length - 1] else @properties
|
||||
baseline: @base.compile o
|
||||
baseline: '(' + baseline + ')' if @base instanceof ObjectNode and @has_properties()
|
||||
parts: [baseline]
|
||||
complete: @last: baseline
|
||||
|
||||
for prop in props
|
||||
if prop instanceof AccessorNode and prop.soak
|
||||
@source: baseline
|
||||
if prop.soak_node
|
||||
soaked: true
|
||||
if @base instanceof CallNode and prop is props[0]
|
||||
temp: o.scope.free_variable()
|
||||
parts[parts.length - 1]: '(' + temp + ' = ' + baseline + ')' + @SOAK + (baseline: temp + prop.compile(o))
|
||||
complete: '(' + temp + ' = ' + complete + ')' + @SOAK + (baseline: temp + prop.compile(o))
|
||||
else
|
||||
parts[parts.length - 1] += (@SOAK + (baseline += prop.compile(o)))
|
||||
complete: complete + @SOAK + (baseline += prop.compile(o))
|
||||
else
|
||||
part: prop.compile(o)
|
||||
baseline += part
|
||||
parts.push(part)
|
||||
complete += part
|
||||
@last: part
|
||||
|
||||
@last: parts[parts.length - 1]
|
||||
@source: if parts.length > 1 then parts[0...(parts.length - 1)].join('') else null
|
||||
code: parts.join('').replace(/\)\(\)\)/, '()))')
|
||||
if op and soaked then '(' + code + ')' else code
|
||||
if op and soaked then '(' + complete + ')' else complete
|
||||
|
||||
}
|
||||
|
||||
# Pass through CoffeeScript comments into JavaScript comments at the
|
||||
# same position.
|
||||
CommentNode: exports.CommentNode: inherit Node, {
|
||||
CommentNode: exports.CommentNode: inherit BaseNode, {
|
||||
type: 'Comment'
|
||||
|
||||
constructor: (lines) ->
|
||||
@@ -312,7 +318,7 @@ statement CommentNode
|
||||
|
||||
# Node for a function invocation. Takes care of converting super() calls into
|
||||
# calls against the prototype's function of the same name.
|
||||
CallNode: exports.CallNode: inherit Node, {
|
||||
CallNode: exports.CallNode: inherit BaseNode, {
|
||||
type: 'Call'
|
||||
|
||||
constructor: (variable, args) ->
|
||||
@@ -356,18 +362,11 @@ CallNode: exports.CallNode: inherit Node, {
|
||||
if i is 0 then code else '.concat(' + code + ')'
|
||||
@prefix + meth + '.apply(' + obj + ', ' + args.join('') + ')'
|
||||
|
||||
# If the code generation wished to use the result of a function call
|
||||
# in multiple places, ensure that the function is only ever called once.
|
||||
compile_reference: (o) ->
|
||||
reference: new LiteralNode(o.scope.free_variable())
|
||||
call: new ParentheticalNode(new AssignNode(reference, this))
|
||||
[call, reference]
|
||||
|
||||
}
|
||||
|
||||
# Node to extend an object's prototype with an ancestor object.
|
||||
# After goog.inherits from the Closure Library.
|
||||
ExtendsNode: exports.ExtendsNode: inherit Node, {
|
||||
ExtendsNode: exports.ExtendsNode: inherit BaseNode, {
|
||||
type: 'Extends'
|
||||
|
||||
constructor: (child, parent) ->
|
||||
@@ -400,13 +399,13 @@ statement ExtendsNode
|
||||
|
||||
# A dotted accessor into a part of a value, or the :: shorthand for
|
||||
# an accessor into the object's prototype.
|
||||
AccessorNode: exports.AccessorNode: inherit Node, {
|
||||
AccessorNode: exports.AccessorNode: inherit BaseNode, {
|
||||
type: 'Accessor'
|
||||
|
||||
constructor: (name, tag) ->
|
||||
@children: [@name: name]
|
||||
@prototype: tag is 'prototype'
|
||||
@soak: tag is 'soak'
|
||||
@soak_node: tag is 'soak'
|
||||
this
|
||||
|
||||
compile_node: (o) ->
|
||||
@@ -415,11 +414,12 @@ AccessorNode: exports.AccessorNode: inherit Node, {
|
||||
}
|
||||
|
||||
# An indexed accessor into a part of an array or object.
|
||||
IndexNode: exports.IndexNode: inherit Node, {
|
||||
IndexNode: exports.IndexNode: inherit BaseNode, {
|
||||
type: 'Index'
|
||||
|
||||
constructor: (index) ->
|
||||
@children: [@index: index]
|
||||
constructor: (index, tag) ->
|
||||
@children: [@index: index]
|
||||
@soak_node: tag is 'soak'
|
||||
this
|
||||
|
||||
compile_node: (o) ->
|
||||
@@ -429,7 +429,7 @@ IndexNode: exports.IndexNode: inherit Node, {
|
||||
|
||||
# A range literal. Ranges can be used to extract portions (slices) of arrays,
|
||||
# or to specify a range for list comprehensions.
|
||||
RangeNode: exports.RangeNode: inherit Node, {
|
||||
RangeNode: exports.RangeNode: inherit BaseNode, {
|
||||
type: 'Range'
|
||||
|
||||
constructor: (from, to, exclusive) ->
|
||||
@@ -469,7 +469,7 @@ RangeNode: exports.RangeNode: inherit Node, {
|
||||
# An array slice literal. Unlike JavaScript's Array#slice, the second parameter
|
||||
# specifies the index of the end of the slice (just like the first parameter)
|
||||
# is the index of the beginning.
|
||||
SliceNode: exports.SliceNode: inherit Node, {
|
||||
SliceNode: exports.SliceNode: inherit BaseNode, {
|
||||
type: 'Slice'
|
||||
|
||||
constructor: (range) ->
|
||||
@@ -485,7 +485,7 @@ SliceNode: exports.SliceNode: inherit Node, {
|
||||
}
|
||||
|
||||
# An object literal.
|
||||
ObjectNode: exports.ObjectNode: inherit Node, {
|
||||
ObjectNode: exports.ObjectNode: inherit BaseNode, {
|
||||
type: 'Object'
|
||||
|
||||
constructor: (props) ->
|
||||
@@ -512,7 +512,7 @@ ObjectNode: exports.ObjectNode: inherit Node, {
|
||||
}
|
||||
|
||||
# An array literal.
|
||||
ArrayNode: exports.ArrayNode: inherit Node, {
|
||||
ArrayNode: exports.ArrayNode: inherit BaseNode, {
|
||||
type: 'Array'
|
||||
|
||||
constructor: (objects) ->
|
||||
@@ -559,7 +559,7 @@ ClosureNode: exports.ClosureNode: {
|
||||
}
|
||||
|
||||
# Setting the value of a local variable, or the value of an object property.
|
||||
AssignNode: exports.AssignNode: inherit Node, {
|
||||
AssignNode: exports.AssignNode: inherit BaseNode, {
|
||||
type: 'Assign'
|
||||
|
||||
PROTO_ASSIGN: /^(\S+)\.prototype/
|
||||
@@ -632,7 +632,7 @@ AssignNode: exports.AssignNode: inherit Node, {
|
||||
|
||||
# A function definition. The only node that creates a new Scope.
|
||||
# A CodeNode does not have any children -- they're within the new scope.
|
||||
CodeNode: exports.CodeNode: inherit Node, {
|
||||
CodeNode: exports.CodeNode: inherit BaseNode, {
|
||||
type: 'Code'
|
||||
|
||||
constructor: (params, body, tag) ->
|
||||
@@ -676,7 +676,7 @@ CodeNode: exports.CodeNode: inherit Node, {
|
||||
|
||||
# A splat, either as a parameter to a function, an argument to a call,
|
||||
# or in a destructuring assignment.
|
||||
SplatNode: exports.SplatNode: inherit Node, {
|
||||
SplatNode: exports.SplatNode: inherit BaseNode, {
|
||||
type: 'Splat'
|
||||
|
||||
constructor: (name) ->
|
||||
@@ -699,11 +699,16 @@ SplatNode: exports.SplatNode: inherit Node, {
|
||||
|
||||
# A while loop, the only sort of low-level loop exposed by CoffeeScript. From
|
||||
# it, all other loops can be manufactured.
|
||||
WhileNode: exports.WhileNode: inherit Node, {
|
||||
WhileNode: exports.WhileNode: inherit BaseNode, {
|
||||
type: 'While'
|
||||
|
||||
constructor: (condition, body) ->
|
||||
@children:[@condition: condition, @body: body]
|
||||
constructor: (condition, opts) ->
|
||||
@children:[@condition: condition]
|
||||
@filter: opts and opts.filter
|
||||
this
|
||||
|
||||
add_body: (body) ->
|
||||
@children.push @body: body
|
||||
this
|
||||
|
||||
top_sensitive: ->
|
||||
@@ -719,10 +724,11 @@ WhileNode: exports.WhileNode: inherit Node, {
|
||||
if not top
|
||||
rvar: o.scope.free_variable()
|
||||
set: @idt() + rvar + ' = [];\n'
|
||||
@body: PushNode.wrap(rvar, @body)
|
||||
@body: PushNode.wrap(rvar, @body) if @body
|
||||
post: if returns then '\n' + @idt() + 'return ' + rvar + ';' else ''
|
||||
pre: set + @idt() + 'while (' + cond + ')'
|
||||
return pre + ' null;' + post if not @body
|
||||
@body: Expressions.wrap([new IfNode(@filter, @body)]) if @filter
|
||||
pre + ' {\n' + @body.compile(o) + '\n' + @idt() + '}' + post
|
||||
|
||||
}
|
||||
@@ -731,7 +737,7 @@ statement WhileNode
|
||||
|
||||
# Simple Arithmetic and logical operations. Performs some conversion from
|
||||
# CoffeeScript operations into their JavaScript equivalents.
|
||||
OpNode: exports.OpNode: inherit Node, {
|
||||
OpNode: exports.OpNode: inherit BaseNode, {
|
||||
type: 'Op'
|
||||
|
||||
CONVERSIONS: {
|
||||
@@ -795,7 +801,7 @@ OpNode: exports.OpNode: inherit Node, {
|
||||
}
|
||||
|
||||
# A try/catch/finally block.
|
||||
TryNode: exports.TryNode: inherit Node, {
|
||||
TryNode: exports.TryNode: inherit BaseNode, {
|
||||
type: 'Try'
|
||||
|
||||
constructor: (attempt, error, recovery, ensure) ->
|
||||
@@ -816,7 +822,7 @@ TryNode: exports.TryNode: inherit Node, {
|
||||
statement TryNode
|
||||
|
||||
# Throw an exception.
|
||||
ThrowNode: exports.ThrowNode: inherit Node, {
|
||||
ThrowNode: exports.ThrowNode: inherit BaseNode, {
|
||||
type: 'Throw'
|
||||
|
||||
constructor: (expression) ->
|
||||
@@ -831,7 +837,7 @@ ThrowNode: exports.ThrowNode: inherit Node, {
|
||||
statement ThrowNode, true
|
||||
|
||||
# Check an expression for existence (meaning not null or undefined).
|
||||
ExistenceNode: exports.ExistenceNode: inherit Node, {
|
||||
ExistenceNode: exports.ExistenceNode: inherit BaseNode, {
|
||||
type: 'Existence'
|
||||
|
||||
constructor: (expression) ->
|
||||
@@ -845,11 +851,12 @@ ExistenceNode: exports.ExistenceNode: inherit Node, {
|
||||
|
||||
ExistenceNode.compile_test: (o, variable) ->
|
||||
[first, second]: [variable, variable]
|
||||
[first, second]: variable.compile_reference(o) if variable instanceof CallNode
|
||||
if variable instanceof CallNode or (variable instanceof ValueNode and variable.has_properties())
|
||||
[first, second]: variable.compile_reference(o)
|
||||
'(typeof ' + first.compile(o) + ' !== "undefined" && ' + second.compile(o) + ' !== null)'
|
||||
|
||||
# An extra set of parentheses, specified explicitly in the source.
|
||||
ParentheticalNode: exports.ParentheticalNode: inherit Node, {
|
||||
ParentheticalNode: exports.ParentheticalNode: inherit BaseNode, {
|
||||
type: 'Paren'
|
||||
|
||||
constructor: (expression) ->
|
||||
@@ -872,7 +879,7 @@ ParentheticalNode: exports.ParentheticalNode: inherit Node, {
|
||||
# into a for loop. Also acts as an expression, able to return the result
|
||||
# of the comprehenion. Unlike Python array comprehensions, it's able to pass
|
||||
# the current index of the loop as a second parameter.
|
||||
ForNode: exports.ForNode: inherit Node, {
|
||||
ForNode: exports.ForNode: inherit BaseNode, {
|
||||
type: 'For'
|
||||
|
||||
constructor: (body, source, name, index) ->
|
||||
@@ -908,7 +915,7 @@ ForNode: exports.ForNode: inherit Node, {
|
||||
if range
|
||||
index_var: scope.free_variable()
|
||||
source_part: source.compile_variables(o)
|
||||
for_part: index_var + '=0, ' + source.compile(merge(o, {index: ivar, step: @step})) + ', ' + index_var + '++'
|
||||
for_part: index_var + ' = 0, ' + source.compile(merge(o, {index: ivar, step: @step})) + ', ' + index_var + '++'
|
||||
else
|
||||
index_var: null
|
||||
source_part: svar + ' = ' + @source.compile(o) + ';\n' + @idt()
|
||||
@@ -942,7 +949,7 @@ statement ForNode
|
||||
# expression by pushing down requested returns to the expression bodies.
|
||||
# Single-expression IfNodes are compiled into ternary operators if possible,
|
||||
# because ternaries are first-class returnable assignable expressions.
|
||||
IfNode: exports.IfNode: inherit Node, {
|
||||
IfNode: exports.IfNode: inherit BaseNode, {
|
||||
type: 'If'
|
||||
|
||||
constructor: (condition, body, else_body, tags) ->
|
||||
@@ -985,9 +992,12 @@ IfNode: exports.IfNode: inherit Node, {
|
||||
this
|
||||
|
||||
# Rewrite a chain of IfNodes to add a default case as the final else.
|
||||
add_else: (exprs) ->
|
||||
if @is_chain() then @else_body.add_else(exprs) else @else_body: exprs and exprs.unwrap()
|
||||
@children.push(exprs)
|
||||
add_else: (exprs, statement) ->
|
||||
if @is_chain()
|
||||
@else_body.add_else exprs, statement
|
||||
else
|
||||
exprs: exprs.unwrap() unless statement
|
||||
@children.push @else_body: exprs
|
||||
this
|
||||
|
||||
# If the else_body is an IfNode itself, then we've got an if-else chain.
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
# Create an OptionParser with a list of valid options.
|
||||
op: exports.OptionParser: (rules) ->
|
||||
@banner: 'Usage: [Options]'
|
||||
op: exports.OptionParser: (rules, banner) ->
|
||||
@banner: banner or 'Usage: [Options]'
|
||||
@options_title: 'Available options:'
|
||||
@rules: build_rules(rules)
|
||||
@actions: {}
|
||||
this
|
||||
|
||||
# Add a callback to fire when a particular option is encountered.
|
||||
op::add: (value, callback) ->
|
||||
@actions[value]: callback
|
||||
|
||||
# Parse the argument array, calling defined callbacks, returning the remaining non-option arguments.
|
||||
op::parse: (args) ->
|
||||
results: []
|
||||
options: {arguments: []}
|
||||
args: args.concat []
|
||||
while (arg: args.shift())
|
||||
is_option: false
|
||||
for rule in @rules
|
||||
if rule.letter is arg or rule.flag is arg
|
||||
callback: @actions[rule.name]
|
||||
value: rule.argument and args.shift()
|
||||
callback(value) if callback
|
||||
options[rule.name]: if rule.argument then args.shift() else true
|
||||
is_option: true
|
||||
break
|
||||
results.push arg unless is_option
|
||||
results
|
||||
options.arguments.push arg unless is_option
|
||||
options
|
||||
|
||||
# Return the help text for this OptionParser, for --help and such.
|
||||
op::help: ->
|
||||
|
||||
@@ -7,7 +7,8 @@ exports.Rewriter: re: ->
|
||||
|
||||
# Tokens that must be balanced.
|
||||
BALANCED_PAIRS: [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'],
|
||||
['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END']]
|
||||
['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'],
|
||||
['INDEX_START', 'INDEX_END'], ['SOAKED_INDEX_START', 'SOAKED_INDEX_END']]
|
||||
|
||||
# Tokens that signal the start of a balanced pair.
|
||||
EXPRESSION_START: pair[0] for pair in BALANCED_PAIRS
|
||||
@@ -199,7 +200,7 @@ re::ensure_balance: (pairs) ->
|
||||
levels[open] ||= 0
|
||||
levels[open] += 1 if token[0] is open
|
||||
levels[open] -= 1 if token[0] is close
|
||||
throw "too many " + token[1] if levels[open] < 0
|
||||
throw new Error("too many " + token[1]) if levels[open] < 0
|
||||
return 1
|
||||
unclosed: key for key, value of levels when value > 0
|
||||
throw new Error("unclosed " + unclosed[0]) if unclosed.length
|
||||
|
||||
@@ -41,10 +41,16 @@ obj: {
|
||||
|
||||
ok obj?.prop is "hello"
|
||||
|
||||
ok obj?['prop'] is "hello"
|
||||
|
||||
ok obj.prop?.length is 5
|
||||
|
||||
ok obj?['prop']?['length'] is 5
|
||||
|
||||
ok obj?.prop?.non?.existent?.property is undefined
|
||||
|
||||
ok obj?['non']?['existent'].property is undefined
|
||||
|
||||
|
||||
# Soaks and caches method calls as well.
|
||||
|
||||
@@ -68,3 +74,8 @@ result: value?.toString().toLowerCase()
|
||||
|
||||
ok result is '10'
|
||||
|
||||
|
||||
# Safely existence test on soaks.
|
||||
result: not value?.property?
|
||||
ok result
|
||||
|
||||
|
||||
@@ -26,12 +26,6 @@ reg: /\\/
|
||||
ok reg(str) and str is '\\'
|
||||
|
||||
|
||||
i: 10
|
||||
while i -= 1
|
||||
|
||||
ok i is 0
|
||||
|
||||
|
||||
money$: 'dollars'
|
||||
|
||||
ok money$ is 'dollars'
|
||||
|
||||
@@ -41,3 +41,24 @@ result: switch num += 5
|
||||
|
||||
ok result
|
||||
|
||||
|
||||
# Ensure that trailing switch elses don't get rewritten.
|
||||
result: false
|
||||
switch "word"
|
||||
when "one thing"
|
||||
do_something()
|
||||
else
|
||||
result: true unless false
|
||||
|
||||
ok result
|
||||
|
||||
result: false
|
||||
switch "word"
|
||||
when "one thing"
|
||||
do_something()
|
||||
when "other thing"
|
||||
do_something()
|
||||
else
|
||||
result: true unless false
|
||||
|
||||
ok result
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
i: 100
|
||||
while i -= 1
|
||||
|
||||
ok i is 0
|
||||
|
||||
|
||||
i: 5
|
||||
list: while i -= 1
|
||||
i * 2
|
||||
@@ -25,4 +19,12 @@ results: while func 1
|
||||
assert()
|
||||
i
|
||||
|
||||
ok results.join(' ') is '4 3 2 1'
|
||||
ok results.join(' ') is '4 3 2 1'
|
||||
|
||||
|
||||
i: 10
|
||||
results: while i -= 1 when i % 2 is 0
|
||||
i * 2
|
||||
|
||||
ok results.join(' ') is '16 12 8 4'
|
||||
|
||||
|
||||
6
vendor/jison/lib/jison.js
vendored
6
vendor/jison/lib/jison.js
vendored
@@ -369,8 +369,8 @@ lookaheadMixin.followSets = function followSets () {
|
||||
var part = production.handle.slice(i+1);
|
||||
|
||||
set = self.first(part);
|
||||
if (set.length === 0 && bool) { // set was nullable
|
||||
set = nonterminals[production.symbol].follows;
|
||||
if (self.nullable(part) && bool) {
|
||||
set.push.apply(set, nonterminals[production.symbol].follows);
|
||||
}
|
||||
}
|
||||
oldcount = nonterminals[t].follows.length;
|
||||
@@ -838,7 +838,7 @@ lrGeneratorMixin.generateModule = function generateModule (opt) {
|
||||
var out = "/* Jison generated parser */\n";
|
||||
out += (moduleName.match(/\./) ? moduleName : "var "+moduleName)+" = (function(){";
|
||||
out += "\nvar parser = "+this.generateModule_();
|
||||
if (this.lexer) {
|
||||
if (this.lexer && this.lexer.generateModule) {
|
||||
out += this.lexer.generateModule();
|
||||
out += "\nparser.lexer = lexer;";
|
||||
}
|
||||
|
||||
3
vendor/jison/lib/jison/lexer.js
vendored
3
vendor/jison/lib/jison/lexer.js
vendored
@@ -57,6 +57,9 @@ function buildActions (dict, tokens) {
|
||||
}
|
||||
|
||||
function RegExpLexer (dict, input, tokens) {
|
||||
if (typeof dict === 'string') {
|
||||
dict = require("./jisonlex").parse(dict);
|
||||
}
|
||||
dict = dict || {};
|
||||
|
||||
this.performAction = buildActions.call(this, dict, tokens);
|
||||
|
||||
19
vendor/jison/lib/jison/util/bnf-parser.js
vendored
19
vendor/jison/lib/jison/util/bnf-parser.js
vendored
@@ -138,8 +138,6 @@ parse: function parse(input) {
|
||||
var symbol, state, action, a, r, yyval = {}, p, len, ip = 0, newState, expected;
|
||||
symbol = lex();
|
||||
while (true) {
|
||||
this.trace("stack:", JSON.stringify(stack), "\n\t\t\tinput:", this.lexer._input);
|
||||
this.trace("vstack:", JSON.stringify(vstack));
|
||||
state = stack[stack.length - 1];
|
||||
action = table[state] && table[state][symbol];
|
||||
if (typeof action == "undefined" || !action.length || !action[0]) {
|
||||
@@ -149,11 +147,11 @@ parse: function parse(input) {
|
||||
expected.push("'" + this.terminals_[p] + "'");
|
||||
}
|
||||
}
|
||||
self.trace("stack:", JSON.stringify(stack), "symbol:", symbol, "input", this.lexer.upcomingInput());
|
||||
if (this.lexer.upcomingInput) {
|
||||
self.trace("input", this.lexer.upcomingInput());
|
||||
if (this.lexer.showPosition) {
|
||||
parseError("Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", "), {text: this.lexer.match, token: this.terminals_[symbol], line: this.lexer.yylineno, expected: expected});
|
||||
} else {
|
||||
parseError("Parse error on line " + (yylineno + 1) + ": Unexpected '" + this.terminals_[symbol] + "'", {text: this.lexer.match, token: this.terminals_[symbol], line: this.lexer.yylineno, expected: expected});
|
||||
}
|
||||
parseError("Parse error on line " + (yylineno + 1) + ". Expecting: " + expected.join(", ") + "\n" + (this.lexer.showPosition && this.lexer.showPosition()), {text: this.lexer.match, token: symbol, line: this.lexer.yylineno});
|
||||
}
|
||||
this.trace("action:", action);
|
||||
if (action.length > 1) {
|
||||
@@ -175,15 +173,12 @@ parse: function parse(input) {
|
||||
case 2:
|
||||
reductions++;
|
||||
len = this.productions_[a[1]][1];
|
||||
this.trace("reduce by: ", this.productions ? this.productions[a[1]] : a[1]);
|
||||
yyval.$ = vstack[vstack.length - len];
|
||||
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, a[1], vstack);
|
||||
if (typeof r !== "undefined") {
|
||||
return r;
|
||||
}
|
||||
this.trace("yyval=", JSON.stringify(yyval.$));
|
||||
if (len) {
|
||||
this.trace("production length:", len);
|
||||
stack = stack.slice(0, -1 * len * 2);
|
||||
vstack = vstack.slice(0, -1 * len);
|
||||
}
|
||||
@@ -193,10 +188,8 @@ parse: function parse(input) {
|
||||
stack.push(newState);
|
||||
break;
|
||||
case 3:
|
||||
this.trace("stack:", stack, "\n\tinput:", this.lexer._input);
|
||||
this.trace("vstack:", JSON.stringify(vstack));
|
||||
this.trace("Total reductions:", reductions);
|
||||
this.trace("Total shifts:", shifts);
|
||||
this.reductionCount = reductions;
|
||||
this.shiftCount = shifts;
|
||||
return true;
|
||||
default:;
|
||||
}
|
||||
|
||||
19
vendor/jison/lib/jison/util/lex-parser.js
vendored
19
vendor/jison/lib/jison/util/lex-parser.js
vendored
@@ -134,8 +134,6 @@ parse: function parse(input) {
|
||||
var symbol, state, action, a, r, yyval = {}, p, len, ip = 0, newState, expected;
|
||||
symbol = lex();
|
||||
while (true) {
|
||||
this.trace("stack:", JSON.stringify(stack), "\n\t\t\tinput:", this.lexer._input);
|
||||
this.trace("vstack:", JSON.stringify(vstack));
|
||||
state = stack[stack.length - 1];
|
||||
action = table[state] && table[state][symbol];
|
||||
if (typeof action == "undefined" || !action.length || !action[0]) {
|
||||
@@ -145,11 +143,11 @@ parse: function parse(input) {
|
||||
expected.push("'" + this.terminals_[p] + "'");
|
||||
}
|
||||
}
|
||||
self.trace("stack:", JSON.stringify(stack), "symbol:", symbol, "input", this.lexer.upcomingInput());
|
||||
if (this.lexer.upcomingInput) {
|
||||
self.trace("input", this.lexer.upcomingInput());
|
||||
if (this.lexer.showPosition) {
|
||||
parseError("Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", "), {text: this.lexer.match, token: this.terminals_[symbol], line: this.lexer.yylineno, expected: expected});
|
||||
} else {
|
||||
parseError("Parse error on line " + (yylineno + 1) + ": Unexpected '" + this.terminals_[symbol] + "'", {text: this.lexer.match, token: this.terminals_[symbol], line: this.lexer.yylineno, expected: expected});
|
||||
}
|
||||
parseError("Parse error on line " + (yylineno + 1) + ". Expecting: " + expected.join(", ") + "\n" + (this.lexer.showPosition && this.lexer.showPosition()), {text: this.lexer.match, token: symbol, line: this.lexer.yylineno});
|
||||
}
|
||||
this.trace("action:", action);
|
||||
if (action.length > 1) {
|
||||
@@ -171,15 +169,12 @@ parse: function parse(input) {
|
||||
case 2:
|
||||
reductions++;
|
||||
len = this.productions_[a[1]][1];
|
||||
this.trace("reduce by: ", this.productions ? this.productions[a[1]] : a[1]);
|
||||
yyval.$ = vstack[vstack.length - len];
|
||||
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, a[1], vstack);
|
||||
if (typeof r !== "undefined") {
|
||||
return r;
|
||||
}
|
||||
this.trace("yyval=", JSON.stringify(yyval.$));
|
||||
if (len) {
|
||||
this.trace("production length:", len);
|
||||
stack = stack.slice(0, -1 * len * 2);
|
||||
vstack = vstack.slice(0, -1 * len);
|
||||
}
|
||||
@@ -189,10 +184,8 @@ parse: function parse(input) {
|
||||
stack.push(newState);
|
||||
break;
|
||||
case 3:
|
||||
this.trace("stack:", stack, "\n\tinput:", this.lexer._input);
|
||||
this.trace("vstack:", JSON.stringify(vstack));
|
||||
this.trace("Total reductions:", reductions);
|
||||
this.trace("Total shifts:", shifts);
|
||||
this.reductionCount = reductions;
|
||||
this.shiftCount = shifts;
|
||||
return true;
|
||||
default:;
|
||||
}
|
||||
|
||||
12
vendor/jison/tests/lexer/regexplexer.js
vendored
12
vendor/jison/tests/lexer/regexplexer.js
vendored
@@ -403,3 +403,15 @@ exports["test DJ lexer"] = function() {
|
||||
assert.equal(typeof tok, "string");
|
||||
}
|
||||
};
|
||||
|
||||
exports["test instantiation from string"] = function() {
|
||||
var dict = "%%\n'x' {return 'X';}\n'y' {return 'Y';}\n<<EOF>> {return 'EOF';}";
|
||||
|
||||
var input = "x";
|
||||
|
||||
var lexer = new RegExpLexer(dict);
|
||||
lexer.setInput(input);
|
||||
|
||||
assert.equal(lexer.lex(), "X");
|
||||
assert.equal(lexer.lex(), "EOF");
|
||||
};
|
||||
|
||||
14
vendor/jison/tests/parser/lalr.js
vendored
14
vendor/jison/tests/parser/lalr.js
vendored
@@ -167,3 +167,17 @@ exports["test LR(1) grammar"] = function () {
|
||||
var gen = new Jison.Generator(grammar, {type: "lalr"});
|
||||
assert.equal(gen.conflicts, 2);
|
||||
};
|
||||
|
||||
exports["test BNF grammar bootstrap"] = function () {
|
||||
var grammar = "%%\n\nspec\n : declaration_list '%%' grammar EOF\n {$$ = $1; $$.bnf = $3; return $$;}\n | declaration_list '%%' grammar '%%' EOF\n {$$ = $1; $$.bnf = $3; return $$;}\n ;\n\ndeclaration_list\n : declaration_list declaration\n {$$ = $1; yy.addDeclaration($$, $2);}\n | \n <$$ = {};>\n ;\n\ndeclaration\n : START id\n <$$ = {start: $2};>\n | operator\n <$$ = {operator: $1};>\n ;\n\noperator\n : associativity token_list\n {$$ = [$1]; $$.push.apply($$, $2);}\n ;\n\nassociativity\n : LEFT\n {$$ = 'left';}\n | RIGHT\n {$$ = 'right';}\n | NONASSOC\n {$$ = 'nonassoc';}\n ;\n\ntoken_list\n : token_list symbol\n {$$ = $1; $$.push($2);}\n | symbol\n {$$ = [$1];}\n ;\n\ngrammar\n : production_list\n {$$ = $1;}\n ;\n\nproduction_list\n : production_list production\n {$$ = $1; $$[$2[0]] = $2[1];}\n | production\n <$$ = {}; $$[$1[0]] = $1[1];>\n ;\n\nproduction\n : id ':' handle_list ';'\n {$$ = [$1, $3];}\n ;\n\nhandle_list\n : handle_list '|' handle_action\n {$$ = $1; $$.push($3);}\n | handle_action\n {$$ = [$1];}\n ;\n\nhandle_action\n : handle action prec\n {$$ = [($1.length ? $1.join(' ') : '')];\n if($2) $$.push($2);\n if($3) $$.push($3);\n if ($$.length === 1) $$ = $$[0];\n }\n ;\n\nhandle\n : handle symbol\n {$$ = $1; $$.push($2)}\n | \n {$$ = [];}\n ;\n\nprec\n : PREC symbol\n <$$ = {prec: $2};>\n | \n {$$ = null;}\n ;\n\nsymbol\n : id\n {$$ = $1;}\n | STRING\n {$$ = yytext;}\n ;\n\nid\n : ID\n {$$ = yytext;}\n ;\n\naction\n : ACTION\n {$$ = yytext;}\n | \n {$$ = '';}\n ;\n\n";
|
||||
|
||||
var lex = "\n%%\n\\s+ \t{/* skip whitespace */}\n\"/*\"[^*]*\"*\" \t{return yy.lexComment(this);}\n[a-zA-Z][a-zA-Z0-9_-]* \t{return 'ID';}\n'\"'[^\"]+'\"' \t{yytext = yytext.substr(1, yyleng-2); return 'STRING';}\n\"'\"[^']+\"'\" \t{yytext = yytext.substr(1, yyleng-2); return 'STRING';}\n\":\" \t{return ':';}\n\";\" \t{return ';';}\n\"|\" \t{return '|';}\n\"%%\" \t{return '%%';}\n\"%prec\" \t{return 'PREC';}\n\"%start\" \t{return 'START';}\n\"%left\" \t{return 'LEFT';}\n\"%right\" \t{return 'RIGHT';}\n\"%nonassoc\" \t{return 'NONASSOC';}\n\"%\"[a-zA-Z]+[^\\n]* \t{/* ignore unrecognized decl */}\n\"{{\"[^}]*\"}\" \t{return yy.lexAction(this);}\n\"{\"[^}]*\"}\" \t{yytext = yytext.substr(1, yyleng-2); return 'ACTION';}\n\"<\"[^>]*\">\" \t{yytext = yytext.substr(1, yyleng-2); return 'ACTION';}\n. \t{/* ignore bad characters */}\n<<EOF>> \t{return 'EOF';}\n\n%%\n\n";
|
||||
|
||||
|
||||
var gen = new Jison.Generator(grammar, {type: "lalr"});
|
||||
gen.lexer = new Lexer(lex);
|
||||
|
||||
var parser = gen.createParser();
|
||||
|
||||
assert.ok(parser.parse(grammar), "bootstrapped bnf parser should parse correctly.");
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user