mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-01-13 16:57:54 -05:00
Compare commits
233 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3ae0c5c75 | ||
|
|
46895419b3 | ||
|
|
4f6a4bab30 | ||
|
|
9e5ffaea69 | ||
|
|
928d2d7f4a | ||
|
|
10293df1f9 | ||
|
|
41c4c822ba | ||
|
|
6030ac3a23 | ||
|
|
f42329ca0a | ||
|
|
570529f526 | ||
|
|
8b8436d794 | ||
|
|
b0594aad3e | ||
|
|
04b0b94a8c | ||
|
|
e1f46cfb9b | ||
|
|
cc1b74f11b | ||
|
|
104b4666fe | ||
|
|
835fc84330 | ||
|
|
a5d6285cfd | ||
|
|
c2727d964c | ||
|
|
d687d52f9e | ||
|
|
6a43de789f | ||
|
|
1288786fdc | ||
|
|
2b4421fca1 | ||
|
|
6bdd52733e | ||
|
|
bd6b4142fe | ||
|
|
daa6ad5470 | ||
|
|
369e0545c0 | ||
|
|
5f31a3d60e | ||
|
|
8b976acac1 | ||
|
|
b00962db1a | ||
|
|
26dcf025f4 | ||
|
|
d2f90d2236 | ||
|
|
c3391e1dd8 | ||
|
|
b3463a1378 | ||
|
|
f0463e9981 | ||
|
|
1cc583b382 | ||
|
|
39cb8815f7 | ||
|
|
21db08a23d | ||
|
|
1f301d8c07 | ||
|
|
35f185440f | ||
|
|
5ce0c84907 | ||
|
|
a6891e4feb | ||
|
|
8a3ebb9181 | ||
|
|
308299fe04 | ||
|
|
1cc8463b9c | ||
|
|
d7862647d9 | ||
|
|
6c786f0fb7 | ||
|
|
cc345def46 | ||
|
|
818983b6a4 | ||
|
|
b859d92d2c | ||
|
|
8cd9ba168d | ||
|
|
08a57898a7 | ||
|
|
ba4743cc83 | ||
|
|
94e22ab819 | ||
|
|
c1b46f777f | ||
|
|
81cf9ca00f | ||
|
|
96f087ca6b | ||
|
|
bc975e556e | ||
|
|
6804c1065b | ||
|
|
74cf54a84f | ||
|
|
73af30b5d8 | ||
|
|
54633aee3f | ||
|
|
26200f4640 | ||
|
|
13f205404c | ||
|
|
563f14b178 | ||
|
|
5e4cca90a3 | ||
|
|
a61b6ee925 | ||
|
|
b11d956d53 | ||
|
|
ee9febe399 | ||
|
|
15a70f863c | ||
|
|
4eee9c318e | ||
|
|
ac6a76ee10 | ||
|
|
42aa8d256c | ||
|
|
873ed071d4 | ||
|
|
210376f7a9 | ||
|
|
35d327a304 | ||
|
|
e3e8b1c501 | ||
|
|
52a54a7681 | ||
|
|
1fe28c1fc9 | ||
|
|
89efd05a3f | ||
|
|
caafafcf4d | ||
|
|
81d8224b9a | ||
|
|
22c85e216f | ||
|
|
f047ba52b2 | ||
|
|
130899a39f | ||
|
|
6847400ccb | ||
|
|
1102567b0c | ||
|
|
8c6647849b | ||
|
|
de42ad0e1c | ||
|
|
073d025fac | ||
|
|
efe8c68c75 | ||
|
|
45d97b3dfe | ||
|
|
fcc88ca472 | ||
|
|
187ebd0374 | ||
|
|
c4999efda7 | ||
|
|
b6231e50c3 | ||
|
|
592aa33577 | ||
|
|
544c99a9ad | ||
|
|
de0e3baf1f | ||
|
|
aea0f2533b | ||
|
|
138c25fe5f | ||
|
|
e0195756dc | ||
|
|
96ae98fade | ||
|
|
efb9809d3b | ||
|
|
0dada3dd27 | ||
|
|
849c8e8ef4 | ||
|
|
9e9c83f788 | ||
|
|
d41d87a874 | ||
|
|
ab40571ffc | ||
|
|
5d13d14de9 | ||
|
|
1df8abf1cb | ||
|
|
45b60c9a52 | ||
|
|
091bc56a96 | ||
|
|
52789f5b19 | ||
|
|
9ba1d41ec8 | ||
|
|
5456bd5f89 | ||
|
|
0d662c3ad2 | ||
|
|
fa76e2dd21 | ||
|
|
08f6c65c3b | ||
|
|
54840c0cbf | ||
|
|
91ac3fa031 | ||
|
|
2853e718f2 | ||
|
|
c22707cd53 | ||
|
|
351c875576 | ||
|
|
35b64d7f18 | ||
|
|
db87d817e8 | ||
|
|
f3c5cc6774 | ||
|
|
4cc2c305a4 | ||
|
|
a5513c45d0 | ||
|
|
c820e0241e | ||
|
|
eb2ac2c64d | ||
|
|
59cf19fd1c | ||
|
|
d5a25d138d | ||
|
|
a7ecd80c92 | ||
|
|
465cffc675 | ||
|
|
302a46d093 | ||
|
|
392767a04e | ||
|
|
928f949761 | ||
|
|
8bb833d858 | ||
|
|
2b03fa9077 | ||
|
|
cfdb774da9 | ||
|
|
b173a377a6 | ||
|
|
581af4540a | ||
|
|
18e5b6b199 | ||
|
|
6da2306fe2 | ||
|
|
a8e4b78803 | ||
|
|
a3be1f6e48 | ||
|
|
89ef3d4117 | ||
|
|
4cf75ec027 | ||
|
|
830c294aea | ||
|
|
bb86e54ece | ||
|
|
359e17277f | ||
|
|
89f5f9d59d | ||
|
|
26c0f7ca2d | ||
|
|
40c1086efa | ||
|
|
999a3db499 | ||
|
|
50e13f62f2 | ||
|
|
fceff1729c | ||
|
|
1765a7ae0c | ||
|
|
ae79ff9fa3 | ||
|
|
3e9d01d6c6 | ||
|
|
ae4535d639 | ||
|
|
c5120c7980 | ||
|
|
92e83489fc | ||
|
|
1b7491d63d | ||
|
|
ce14ad764a | ||
|
|
96e807c677 | ||
|
|
70994d4b50 | ||
|
|
675095efbe | ||
|
|
0235d12927 | ||
|
|
9d24a3420d | ||
|
|
3ad332d5d4 | ||
|
|
15517df417 | ||
|
|
3c2f0d174e | ||
|
|
8cf6f62ea4 | ||
|
|
9e716b310d | ||
|
|
e44bf9ae81 | ||
|
|
f5f99b3022 | ||
|
|
dc3d70e696 | ||
|
|
77fded3c5e | ||
|
|
e644f7244d | ||
|
|
457cdfde26 | ||
|
|
910e38749c | ||
|
|
2b4a37296f | ||
|
|
3f9cdcf1fa | ||
|
|
4342bedd2f | ||
|
|
fdd5796f5e | ||
|
|
f48aa44386 | ||
|
|
51c625205b | ||
|
|
e581f7d2f0 | ||
|
|
3aa646e425 | ||
|
|
92208fec44 | ||
|
|
cdc603c794 | ||
|
|
46d8902004 | ||
|
|
34c1704286 | ||
|
|
19767a0f10 | ||
|
|
0c9f0fd099 | ||
|
|
7f1088054c | ||
|
|
ef5f58e30e | ||
|
|
13024e6911 | ||
|
|
32e8e562ea | ||
|
|
7fdac5c3b9 | ||
|
|
b68fd9d76e | ||
|
|
25c6001a6c | ||
|
|
7250fdd576 | ||
|
|
b7f8443052 | ||
|
|
4fd5e9a3ab | ||
|
|
3785996c44 | ||
|
|
eb0a222eea | ||
|
|
3d761e73e3 | ||
|
|
cc3b4e8080 | ||
|
|
ba7cb3ab69 | ||
|
|
054443c46e | ||
|
|
c8dae22cb0 | ||
|
|
183ec48308 | ||
|
|
13187b0199 | ||
|
|
3c880bf601 | ||
|
|
8e90aaefc1 | ||
|
|
426ae97e49 | ||
|
|
f277a43645 | ||
|
|
2e408648aa | ||
|
|
f2f10e85a8 | ||
|
|
e7ebdce60f | ||
|
|
fd61476106 | ||
|
|
d57b1aab10 | ||
|
|
22e8856b4d | ||
|
|
08b59aef8a | ||
|
|
fbc019171c | ||
|
|
e237abff84 | ||
|
|
c0d1f22487 | ||
|
|
67fd84fc1d | ||
|
|
3bd4dea305 | ||
|
|
a4249fd573 |
39
Cakefile
39
Cakefile
@@ -1,5 +1,6 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
_ = require 'underscore'
|
||||
CoffeeScript = require './lib/coffee-script'
|
||||
{spawn, exec} = require 'child_process'
|
||||
helpers = require './lib/coffee-script/helpers'
|
||||
@@ -41,6 +42,29 @@ run = (args, cb) ->
|
||||
log = (message, color, explanation) ->
|
||||
console.log color + message + reset + ' ' + (explanation or '')
|
||||
|
||||
codeFor = ->
|
||||
counter = 0
|
||||
hljs = require 'highlight.js'
|
||||
hljs.configure classPrefix: ''
|
||||
(file, executable = false, showLoad = true) ->
|
||||
counter++
|
||||
return unless fs.existsSync "documentation/js/#{file}.js"
|
||||
cs = fs.readFileSync "documentation/coffee/#{file}.coffee", 'utf-8'
|
||||
js = fs.readFileSync "documentation/js/#{file}.js", 'utf-8'
|
||||
js = js.replace /^\/\/ generated.*?\n/i, ''
|
||||
|
||||
cshtml = "<pre><code>#{hljs.highlight('coffeescript', cs).value}</code></pre>"
|
||||
jshtml = "<pre><code>#{hljs.highlight('javascript', js).value}</code></pre>"
|
||||
append = if executable is yes then '' else "alert(#{executable});"
|
||||
if executable and executable != yes
|
||||
cs.replace /(\S)\s*\Z/m, "$1\n\nalert #{executable}"
|
||||
run = if executable is true then 'run' else "run: #{executable}"
|
||||
name = "example#{counter}"
|
||||
script = "<script>window.#{name} = #{JSON.stringify cs}</script>"
|
||||
load = if showLoad then "<div class='minibutton load' onclick='javascript: loadConsole(#{name});'>load</div>" else ''
|
||||
button = if executable then "<div class='minibutton ok' onclick='javascript: #{js};#{append}'>#{run}</div>" else ''
|
||||
"<div class='code'>#{cshtml}#{jshtml}#{script}#{load}#{button}<br class='clear' /></div>"
|
||||
|
||||
option '-p', '--prefix [DIR]', 'set the installation prefix for `cake install`'
|
||||
|
||||
task 'install', 'install CoffeeScript into /usr/local (or --prefix)', (options) ->
|
||||
@@ -84,7 +108,6 @@ task 'build:parser', 'rebuild the Jison parser (run build first)', ->
|
||||
parser = require('./lib/coffee-script/grammar').parser
|
||||
fs.writeFile 'lib/coffee-script/parser.js', parser.generate()
|
||||
|
||||
|
||||
task 'build:browser', 'rebuild the merged script for inclusion in the browser', ->
|
||||
code = ''
|
||||
for name in ['helpers', 'rewriter', 'lexer', 'parser', 'scope', 'nodes', 'sourcemap', 'coffee-script', 'browser']
|
||||
@@ -118,8 +141,17 @@ task 'build:browser', 'rebuild the merged script for inclusion in the browser',
|
||||
|
||||
|
||||
task 'doc:site', 'watch and continually rebuild the documentation for the website', ->
|
||||
exec 'rake doc', (err) ->
|
||||
throw err if err
|
||||
source = 'documentation/index.html.js'
|
||||
exec 'bin/coffee -bc -o documentation/js documentation/coffee/*.coffee'
|
||||
|
||||
do renderIndex = ->
|
||||
codeSnippetCounter = 0
|
||||
rendered = _.template fs.readFileSync(source, 'utf-8'), codeFor: codeFor()
|
||||
fs.writeFileSync 'index.html', rendered
|
||||
log "compiled", green, "#{source}"
|
||||
|
||||
fs.watchFile source, internal: 200, renderIndex
|
||||
log "watching..." , green
|
||||
|
||||
|
||||
task 'doc:source', 'rebuild the internal documentation', ->
|
||||
@@ -155,6 +187,7 @@ task 'bench', 'quick benchmark of compilation time', ->
|
||||
|
||||
# Run the CoffeeScript test suite.
|
||||
runTests = (CoffeeScript) ->
|
||||
CoffeeScript.register()
|
||||
startTime = Date.now()
|
||||
currentFile = null
|
||||
passedTests = 0
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2013 Jeremy Ashkenas
|
||||
Copyright (c) 2009-2014 Jeremy Ashkenas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
15
README
15
README
@@ -1,12 +1,11 @@
|
||||
|
||||
{
|
||||
} } {
|
||||
{ { } }
|
||||
} }{ {
|
||||
{ }{ } } _____ __ __
|
||||
( }{ }{ { ) / ____| / _|/ _|
|
||||
{ }{ }{ { } / ____| / _|/ _|
|
||||
.- { { } { }} -. | | ___ | |_| |_ ___ ___
|
||||
( ( } { } { } } ) | | / _ \| _| _/ _ \/ _ \
|
||||
( { } { } { } } ) | | / _ \| _| _/ _ \/ _ \
|
||||
|`-..________ ..-'| | |___| (_) | | | || __/ __/
|
||||
| | \_____\___/|_| |_| \___|\___|
|
||||
| ;--.
|
||||
@@ -22,13 +21,13 @@
|
||||
|
||||
CoffeeScript is a little language that compiles into JavaScript.
|
||||
|
||||
Install Node.js, and then the CoffeeScript compiler:
|
||||
sudo bin/cake install
|
||||
|
||||
Or, if you have the Node Package Manager installed:
|
||||
If you have the Node Package Manager installed:
|
||||
npm install -g coffee-script
|
||||
(Leave off the -g if you don't wish to install globally.)
|
||||
|
||||
Or, if you don't wish to use npm:
|
||||
sudo bin/cake install
|
||||
|
||||
Execute a script:
|
||||
coffee /path/to/script.coffee
|
||||
|
||||
@@ -47,5 +46,5 @@
|
||||
The source repository:
|
||||
git://github.com/jashkenas/coffee-script.git
|
||||
|
||||
All contributors are listed here:
|
||||
Top 100 contributors are listed here:
|
||||
http://github.com/jashkenas/coffee-script/contributors
|
||||
|
||||
79
Rakefile
79
Rakefile
@@ -1,79 +0,0 @@
|
||||
require 'rubygems'
|
||||
require 'erb'
|
||||
require 'fileutils'
|
||||
require 'rake/testtask'
|
||||
require 'json'
|
||||
|
||||
desc "Build the documentation page"
|
||||
task :doc do
|
||||
source = 'documentation/index.html.erb'
|
||||
child = fork { exec "bin/coffee -bcw -o documentation/js documentation/coffee/*.coffee" }
|
||||
at_exit { Process.kill("INT", child) }
|
||||
Signal.trap("INT") { exit }
|
||||
loop do
|
||||
mtime = File.stat(source).mtime
|
||||
if !@mtime || mtime > @mtime
|
||||
rendered = ERB.new(File.read(source)).result(binding)
|
||||
File.open('index.html', 'w+') {|f| f.write(rendered) }
|
||||
end
|
||||
@mtime = mtime
|
||||
sleep 1
|
||||
end
|
||||
end
|
||||
|
||||
desc "Build coffee-script-source gem"
|
||||
task :gem do
|
||||
require 'rubygems'
|
||||
require 'rubygems/package'
|
||||
|
||||
gemspec = Gem::Specification.new do |s|
|
||||
s.name = 'coffee-script-source'
|
||||
s.version = JSON.parse(File.read('package.json'))["version"]
|
||||
s.date = Time.now.strftime("%Y-%m-%d")
|
||||
|
||||
s.homepage = "http://jashkenas.github.com/coffee-script/"
|
||||
s.summary = "The CoffeeScript Compiler"
|
||||
s.description = <<-EOS
|
||||
CoffeeScript is a little language that compiles into JavaScript.
|
||||
Underneath all of those embarrassing braces and semicolons,
|
||||
JavaScript has always had a gorgeous object model at its heart.
|
||||
CoffeeScript is an attempt to expose the good parts of JavaScript
|
||||
in a simple way.
|
||||
EOS
|
||||
|
||||
s.files = [
|
||||
'lib/coffee_script/coffee-script.js',
|
||||
'lib/coffee_script/source.rb'
|
||||
]
|
||||
|
||||
s.authors = ['Jeremy Ashkenas']
|
||||
s.email = 'jashkenas@gmail.com'
|
||||
s.rubyforge_project = 'coffee-script-source'
|
||||
s.license = "MIT"
|
||||
end
|
||||
|
||||
file = File.open("coffee-script-source.gem", "w")
|
||||
Gem::Package.open(file, 'w') do |pkg|
|
||||
pkg.metadata = gemspec.to_yaml
|
||||
|
||||
path = "lib/coffee_script/source.rb"
|
||||
contents = <<-ERUBY
|
||||
module CoffeeScript
|
||||
module Source
|
||||
def self.bundled_path
|
||||
File.expand_path("../coffee-script.js", __FILE__)
|
||||
end
|
||||
end
|
||||
end
|
||||
ERUBY
|
||||
pkg.add_file_simple(path, 0644, contents.size) do |tar_io|
|
||||
tar_io.write(contents)
|
||||
end
|
||||
|
||||
contents = File.read("extras/coffee-script.js")
|
||||
path = "lib/coffee_script/coffee-script.js"
|
||||
pkg.add_file_simple(path, 0644, contents.size) do |tar_io|
|
||||
tar_io.write(contents)
|
||||
end
|
||||
end
|
||||
end
|
||||
8
documentation/coffee/chaining.coffee
Normal file
8
documentation/coffee/chaining.coffee
Normal file
@@ -0,0 +1,8 @@
|
||||
$ 'body'
|
||||
.click (e) ->
|
||||
$ '.box'
|
||||
.fadeIn 'fast'
|
||||
.addClass '.active'
|
||||
.css 'background', 'white'
|
||||
|
||||
|
||||
@@ -2,3 +2,5 @@ class Person
|
||||
constructor: (options) ->
|
||||
{@name, @age, @height} = options
|
||||
|
||||
tim = new Person age: 4
|
||||
|
||||
|
||||
7
documentation/coffee/expansion.coffee
Normal file
7
documentation/coffee/expansion.coffee
Normal file
@@ -0,0 +1,7 @@
|
||||
text = "Every literary critic believes he will
|
||||
outwit history and have the last word"
|
||||
|
||||
[first, ..., last] = text.split " "
|
||||
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
|
||||
start = numbers[0..2]
|
||||
|
||||
middle = numbers[3...6]
|
||||
middle = numbers[3...-2]
|
||||
|
||||
end = numbers[6..]
|
||||
end = numbers[-2..]
|
||||
|
||||
copy = numbers[..]
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
mobyDick = "Call me Ishmael. Some years ago --
|
||||
never mind how long precisely -- having little
|
||||
or no money in my purse, and nothing particular
|
||||
to interest me on shore, I thought I would sail
|
||||
about a little and see the watery part of the
|
||||
world..."
|
||||
|
||||
|
||||
never mind how long precisely -- having little
|
||||
or no money in my purse, and nothing particular
|
||||
to interest me on shore, I thought I would sail
|
||||
about a little and see the watery part of the
|
||||
world..."
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
pre.idle .InheritedClass {
|
||||
}
|
||||
pre.idle .TypeName {
|
||||
color: #21439C;
|
||||
}
|
||||
pre.idle .Number {
|
||||
}
|
||||
pre.idle .LibraryVariable {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .Storage {
|
||||
color: #FF5600;
|
||||
}
|
||||
pre.idle .line-numbers {
|
||||
background-color: #BAD6FD;
|
||||
color: #000000;
|
||||
}
|
||||
pre.idle {
|
||||
background-color: #FFFFFF;
|
||||
color: #000000;
|
||||
}
|
||||
pre.idle .StringInterpolation {
|
||||
color: #990000;
|
||||
}
|
||||
pre.idle .TagName {
|
||||
}
|
||||
pre.idle .LibraryConstant {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .FunctionArgument {
|
||||
color: #0076ad;
|
||||
}
|
||||
pre.idle .BuiltInConstant {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .Invalid {
|
||||
background-color: #990000;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
pre.idle .LibraryClassType {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .LibraryFunction {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .TagAttribute {
|
||||
}
|
||||
pre.idle .Keyword {
|
||||
color: #FF5600;
|
||||
}
|
||||
pre.idle .UserDefinedConstant {
|
||||
}
|
||||
pre.idle .String {
|
||||
color: #00A33F;
|
||||
}
|
||||
pre.idle .FunctionName {
|
||||
color: #21439C;
|
||||
}
|
||||
pre.idle .Variable {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .Comment {
|
||||
color: #919191;
|
||||
}
|
||||
66
documentation/css/tomorrow.css
Normal file
66
documentation/css/tomorrow.css
Normal file
@@ -0,0 +1,66 @@
|
||||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||
/* Original code:; http://softwaremaniacs.org/media/soft/highlight/styles/tomorrow.css */
|
||||
/* But forked for CoffeeScript */
|
||||
.tomorrow-comment, pre .comment, pre .title {
|
||||
color: #8e908c;
|
||||
}
|
||||
|
||||
.tomorrow-red, pre .variable, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo {
|
||||
color: #c82829;
|
||||
}
|
||||
|
||||
.tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .params, pre .constant {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute {
|
||||
color: #eab700;
|
||||
}
|
||||
|
||||
.tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata {
|
||||
color: #718c00;
|
||||
}
|
||||
|
||||
.tomorrow-aqua, pre .css .hexcolor {
|
||||
color: #3e999f;
|
||||
}
|
||||
|
||||
.tomorrow-blue, pre .function, pre .function .title, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title {
|
||||
color: #21439C;
|
||||
}
|
||||
|
||||
.tomorrow-purple, pre .keyword, pre .reserved, pre .javascript .function {
|
||||
color: #FF5600;
|
||||
}
|
||||
|
||||
pre .subst {
|
||||
color: #A535AE;
|
||||
}
|
||||
|
||||
pre .literal {
|
||||
color: #A535AE;
|
||||
}
|
||||
|
||||
pre .property {
|
||||
color: #A535AE;
|
||||
}
|
||||
|
||||
pre .class .title {
|
||||
color: #21439C;
|
||||
}
|
||||
|
||||
pre code {
|
||||
display: block;
|
||||
background: white;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
pre .coffeescript .javascript,
|
||||
pre .javascript .xml,
|
||||
pre .tex .formula,
|
||||
pre .xml .javascript,
|
||||
pre .xml .vbscript,
|
||||
pre .xml .css,
|
||||
pre .xml .cdata {
|
||||
opacity: 0.5;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,29 +1,3 @@
|
||||
<%
|
||||
require 'uv'
|
||||
require 'json'
|
||||
@counter = 0
|
||||
def code_for(file, executable=false, show_load=true)
|
||||
@counter += 1
|
||||
return '' unless File.exists?("documentation/js/#{file}.js")
|
||||
cs = File.read("documentation/coffee/#{file}.coffee")
|
||||
js = File.read("documentation/js/#{file}.js")
|
||||
js = js.sub(/^\/\/ generated.*?\n/i, '')
|
||||
cshtml = Uv.parse(cs, 'xhtml', 'coffeescript', false, 'idle', false)
|
||||
jshtml = Uv.parse(js, 'xhtml', 'javascript', false, 'idle', false)
|
||||
append = executable == true ? '' : "alert(#{executable});"
|
||||
if executable and executable != true
|
||||
cs.sub!(/(\S)\s*\Z/m, "\\1\n\nalert #{executable}")
|
||||
end
|
||||
run = executable == true ? 'run' : "run: #{executable}"
|
||||
name = "example#{@counter}"
|
||||
script = "<script>window.#{name} = #{cs.to_json}</script>"
|
||||
import = show_load ? "<div class='minibutton load' onclick='javascript: loadConsole(#{name});'>load</div>" : ''
|
||||
button = executable ? "<div class='minibutton ok' onclick='javascript: #{js};#{append}'>#{run}</div>" : ''
|
||||
"<div class='code'>#{cshtml}#{jshtml}#{script}#{import}#{button}<br class='clear' /></div>"
|
||||
end
|
||||
%>
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -31,7 +5,7 @@
|
||||
<title>CoffeeScript</title>
|
||||
<link rel="canonical" href="http://coffeescript.org" />
|
||||
<link rel="stylesheet" type="text/css" href="documentation/css/docs.css" />
|
||||
<link rel="stylesheet" type="text/css" href="documentation/css/idle.css" />
|
||||
<link rel="stylesheet" type="text/css" href="documentation/css/tomorrow.css" />
|
||||
<link rel="shortcut icon" href="documentation/images/favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -137,7 +111,7 @@
|
||||
|
||||
<p>
|
||||
<b>Latest Version:</b>
|
||||
<a href="http://github.com/jashkenas/coffee-script/tarball/1.6.3">1.6.3</a>
|
||||
<a href="http://github.com/jashkenas/coffee-script/tarball/1.7.0">1.7.0</a>
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -150,7 +124,7 @@ sudo npm install -g coffee-script</pre>
|
||||
|
||||
<p><i>CoffeeScript on the left, compiled JavaScript output on the right.</i></p>
|
||||
|
||||
<%= code_for('overview', 'cubes', false) %>
|
||||
<%= codeFor('overview', 'cubes', false) %>
|
||||
|
||||
<h2>
|
||||
<span id="installation" class="bookmark"></span>
|
||||
@@ -435,12 +409,12 @@ Expressions
|
||||
an arrow, and the function body. The empty function looks like this:
|
||||
<tt>-></tt>
|
||||
</p>
|
||||
<%= code_for('functions', 'cube(5)') %>
|
||||
<%= codeFor('functions', 'cube(5)') %>
|
||||
<p>
|
||||
Functions may also have default values for arguments. Override the default
|
||||
value by passing a non-null argument.
|
||||
Functions may also have default values for arguments, which will be used
|
||||
if the incoming argument is missing (<tt>null</tt> or <tt>undefined</tt>).
|
||||
</p>
|
||||
<%= code_for('default_args', 'fill("cup")') %>
|
||||
<%= codeFor('default_args', 'fill("cup")') %>
|
||||
|
||||
<p>
|
||||
<span id="objects_and_arrays" class="bookmark"></span>
|
||||
@@ -450,14 +424,14 @@ Expressions
|
||||
the commas are optional. Objects may be created using indentation instead
|
||||
of explicit braces, similar to <a href="http://yaml.org">YAML</a>.
|
||||
</p>
|
||||
<%= code_for('objects_and_arrays', 'song.join(" ... ")') %>
|
||||
<%= codeFor('objects_and_arrays', 'song.join(" ... ")') %>
|
||||
<p>
|
||||
In JavaScript, you can't use reserved words, like <tt>class</tt>, as properties
|
||||
of an object, without quoting them as strings. CoffeeScript notices reserved words
|
||||
used as keys in objects and quotes them for you, so you don't have to worry
|
||||
about it (say, when using jQuery).
|
||||
</p>
|
||||
<%= code_for('objects_reserved') %>
|
||||
<%= codeFor('objects_reserved') %>
|
||||
|
||||
<p>
|
||||
<span id="lexical-scope" class="bookmark"></span>
|
||||
@@ -466,7 +440,7 @@ Expressions
|
||||
are properly declared within lexical scope — you never need to write
|
||||
<tt>var</tt> yourself.
|
||||
</p>
|
||||
<%= code_for('scope', 'inner') %>
|
||||
<%= codeFor('scope', 'inner') %>
|
||||
<p>
|
||||
Notice how all of the variable declarations have been pushed up to
|
||||
the top of the closest scope, the first time they appear.
|
||||
@@ -511,7 +485,7 @@ Expressions
|
||||
is no explicit ternary statement in CoffeeScript — you simply use
|
||||
a regular <b>if</b> statement on a single line.
|
||||
</p>
|
||||
<%= code_for('conditionals') %>
|
||||
<%= codeFor('conditionals') %>
|
||||
|
||||
<p>
|
||||
<span id="splats" class="bookmark"></span>
|
||||
@@ -521,7 +495,7 @@ Expressions
|
||||
splats <tt>...</tt>, both for function definition as well as invocation,
|
||||
making variable numbers of arguments a little bit more palatable.
|
||||
</p>
|
||||
<%= code_for('splats', true) %>
|
||||
<%= codeFor('splats', true) %>
|
||||
|
||||
<p>
|
||||
<span id="loops" class="bookmark"></span>
|
||||
@@ -532,7 +506,7 @@ Expressions
|
||||
Unlike for loops, array comprehensions are expressions, and can be returned
|
||||
and assigned.
|
||||
</p>
|
||||
<%= code_for('array_comprehensions') %>
|
||||
<%= codeFor('array_comprehensions') %>
|
||||
<p>
|
||||
Comprehensions should be able to handle most places where you otherwise
|
||||
would use a loop, <b>each</b>/<b>forEach</b>, <b>map</b>, or <b>select</b>/<b>filter</b>, for example:
|
||||
@@ -541,7 +515,7 @@ Expressions
|
||||
in fixed-size increments, you can use a range to specify the start and
|
||||
end of your comprehension.
|
||||
</p>
|
||||
<%= code_for('range_comprehensions', 'countdown') %>
|
||||
<%= codeFor('range_comprehensions', 'countdown') %>
|
||||
<p>
|
||||
Note how because we are assigning the value of the comprehensions to a
|
||||
variable in the example above, CoffeeScript is collecting the result of
|
||||
@@ -561,7 +535,7 @@ Expressions
|
||||
an object. Use <tt>of</tt> to signal comprehension over the properties of
|
||||
an object instead of the values in an array.
|
||||
</p>
|
||||
<%= code_for('object_comprehensions', 'ages.join(", ")') %>
|
||||
<%= codeFor('object_comprehensions', 'ages.join(", ")') %>
|
||||
<p>
|
||||
If you would like to iterate over just the keys that are defined on the
|
||||
object itself, by adding a <tt>hasOwnProperty</tt>
|
||||
@@ -574,7 +548,7 @@ Expressions
|
||||
as an expression, returning an array containing the result of each iteration
|
||||
through the loop.
|
||||
</p>
|
||||
<%= code_for('while', 'lyrics.join("\n")') %>
|
||||
<%= codeFor('while', 'lyrics.join("\n")') %>
|
||||
<p>
|
||||
For readability, the <b>until</b> keyword is equivalent to <tt>while not</tt>,
|
||||
and the <b>loop</b> keyword is equivalent to <tt>while true</tt>.
|
||||
@@ -586,7 +560,7 @@ Expressions
|
||||
provides the <tt>do</tt> keyword, which immediately invokes a passed function,
|
||||
forwarding any arguments.
|
||||
</p>
|
||||
<%= code_for('do') %>
|
||||
<%= codeFor('do') %>
|
||||
|
||||
<p>
|
||||
<span id="slices" class="bookmark"></span>
|
||||
@@ -597,12 +571,12 @@ Expressions
|
||||
Slices indices have useful defaults. An omitted first index defaults to
|
||||
zero and an omitted second index defaults to the size of the array.
|
||||
</p>
|
||||
<%= code_for('slices', 'middle') %>
|
||||
<%= codeFor('slices', 'middle') %>
|
||||
<p>
|
||||
The same syntax can be used with assignment to replace a segment of an array
|
||||
with new values, splicing it.
|
||||
</p>
|
||||
<%= code_for('splices', 'numbers') %>
|
||||
<%= codeFor('splices', 'numbers') %>
|
||||
<p>
|
||||
Note that JavaScript strings are immutable, and can't be spliced.
|
||||
</p>
|
||||
@@ -616,7 +590,7 @@ Expressions
|
||||
pushed down into each possible branch of execution in the function
|
||||
below.
|
||||
</p>
|
||||
<%= code_for('expressions', 'eldest') %>
|
||||
<%= codeFor('expressions', 'eldest') %>
|
||||
<p>
|
||||
Even though functions will always return their final value, it's both possible
|
||||
and encouraged to return early from a function body writing out the explicit
|
||||
@@ -626,19 +600,19 @@ Expressions
|
||||
Because variable declarations occur at the top of scope, assignment can
|
||||
be used within expressions, even for variables that haven't been seen before:
|
||||
</p>
|
||||
<%= code_for('expressions_assignment', 'six') %>
|
||||
<%= codeFor('expressions_assignment', 'six') %>
|
||||
<p>
|
||||
Things that would otherwise be statements in JavaScript, when used
|
||||
as part of an expression in CoffeeScript, are converted into expressions
|
||||
by wrapping them in a closure. This lets you do useful things, like assign
|
||||
the result of a comprehension to a variable:
|
||||
</p>
|
||||
<%= code_for('expressions_comprehension', 'globals') %>
|
||||
<%= codeFor('expressions_comprehension', 'globals') %>
|
||||
<p>
|
||||
As well as silly things, like passing a <b>try/catch</b> statement directly
|
||||
into a function call:
|
||||
</p>
|
||||
<%= code_for('expressions_try', true) %>
|
||||
<%= codeFor('expressions_try', true) %>
|
||||
<p>
|
||||
There are a handful of statements in JavaScript that can't be meaningfully
|
||||
converted into expressions, namely <tt>break</tt>, <tt>continue</tt>,
|
||||
@@ -682,7 +656,9 @@ Expressions
|
||||
You can use <tt>in</tt> to test for array presence, and <tt>of</tt> to
|
||||
test for JavaScript object-key presence.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To simplify math expressions, `**` can be used for exponentiation, `//` performs integer division and `%%` provides true mathematical modulo.
|
||||
</p>
|
||||
<p>
|
||||
All together now:
|
||||
</p>
|
||||
@@ -699,9 +675,12 @@ Expressions
|
||||
<tr><td><tt>@, this</tt></td><td><tt>this</tt></td></tr>
|
||||
<tr><td><tt>of</tt></td><td><tt>in</tt></td></tr>
|
||||
<tr><td><tt>in</tt></td><td><i><small>no JS equivalent</small></i></td></tr>
|
||||
<tr><td><tt>a ** b</tt></td><td><tt>Math.pow(a, b)</tt></td></tr>
|
||||
<tr><td><tt>a // b</tt></td><td><tt>Math.floor(a / b)</tt></td></tr>
|
||||
<tr><td><tt>a %% b</tt></td><td><tt>(a % b + b) % b</tt></td></tr>
|
||||
</table>
|
||||
|
||||
<%= code_for('aliases') %>
|
||||
<%= codeFor('aliases') %>
|
||||
|
||||
<p>
|
||||
<b class="header">The Existential Operator</b>
|
||||
@@ -715,7 +694,7 @@ Expressions
|
||||
It can also be used for safer conditional assignment than <tt>||=</tt>
|
||||
provides, for cases where you may be handling numbers or strings.
|
||||
</p>
|
||||
<%= code_for('existence', 'footprints') %>
|
||||
<%= codeFor('existence', 'footprints') %>
|
||||
<p>
|
||||
The accessor variant of the existential operator <tt>?.</tt> can be used to soak
|
||||
up null references in a chain of properties. Use it instead
|
||||
@@ -724,7 +703,7 @@ Expressions
|
||||
result, if the chain is broken, <b>undefined</b> is returned instead of
|
||||
the <b>TypeError</b> that would be raised otherwise.
|
||||
</p>
|
||||
<%= code_for('soaks') %>
|
||||
<%= codeFor('soaks') %>
|
||||
<p>
|
||||
Soaking up nulls is similar to Ruby's
|
||||
<a href="http://andand.rubyforge.org/">andand gem</a>, and to the
|
||||
@@ -757,7 +736,7 @@ Expressions
|
||||
Constructor functions are named, to better support helpful stack traces.
|
||||
In the first class in the example below, <tt>this.constructor.name is "Animal"</tt>.
|
||||
</p>
|
||||
<%= code_for('classes', true) %>
|
||||
<%= codeFor('classes', true) %>
|
||||
<p>
|
||||
If structuring your prototypes classically isn't your cup of tea, CoffeeScript
|
||||
provides a couple of lower-level conveniences. The <tt>extends</tt> operator
|
||||
@@ -766,7 +745,7 @@ Expressions
|
||||
quick access to an object's prototype; and <tt>super()</tt>
|
||||
is converted into a call against the immediate ancestor's method of the same name.
|
||||
</p>
|
||||
<%= code_for('prototypes', '"one_two".dasherize()') %>
|
||||
<%= codeFor('prototypes', '"one_two".dasherize()') %>
|
||||
<p>
|
||||
Finally, class definitions are blocks of executable code, which make for interesting
|
||||
metaprogramming possibilities. Because in the context of a class definition,
|
||||
@@ -786,26 +765,30 @@ Expressions
|
||||
on the right to the variables on the left. In the simplest case, it can be
|
||||
used for parallel assignment:
|
||||
</p>
|
||||
<%= code_for('parallel_assignment', 'theBait') %>
|
||||
<%= codeFor('parallel_assignment', 'theBait') %>
|
||||
<p>
|
||||
But it's also helpful for dealing with functions that return multiple
|
||||
values.
|
||||
</p>
|
||||
<%= code_for('multiple_return_values', 'forecast') %>
|
||||
<%= codeFor('multiple_return_values', 'forecast') %>
|
||||
<p>
|
||||
Destructuring assignment can be used with any depth of array and object nesting,
|
||||
to help pull out deeply nested properties.
|
||||
</p>
|
||||
<%= code_for('object_extraction', 'name + " — " + street') %>
|
||||
<%= codeFor('object_extraction', '"name + "-" + street"') %>
|
||||
<p>
|
||||
Destructuring assignment can even be combined with splats.
|
||||
</p>
|
||||
<%= code_for('patterns_and_splats', 'contents.join("")') %>
|
||||
<%= codeFor('patterns_and_splats', 'contents.join("")') %>
|
||||
<p>
|
||||
Expansion can be used to retrieve elements from the end of an array without having to assign the rest of its values. It works in function parameter lists as well.
|
||||
</p>
|
||||
<%= codeFor('expansion', '"first + " " + last"') %>
|
||||
<p>
|
||||
Destructuring assignment is also useful when combined with class constructors
|
||||
to assign properties to your instance from an options object passed to the constructor.
|
||||
</p>
|
||||
<%= code_for('constructor_destructuring', 'contents.join("")') %>
|
||||
<%= codeFor('constructor_destructuring', 'tim.age') %>
|
||||
|
||||
<p>
|
||||
<span id="fat-arrow" class="bookmark"></span>
|
||||
@@ -825,7 +808,7 @@ Expressions
|
||||
to use with <tt>bind</tt>. Functions created with the fat arrow are able to access
|
||||
properties of the <tt>this</tt> where they're defined.
|
||||
</p>
|
||||
<%= code_for('fat_arrow') %>
|
||||
<%= codeFor('fat_arrow') %>
|
||||
<p>
|
||||
If we had used <tt>-></tt> in the callback above, <tt>@customer</tt> would
|
||||
have referred to the undefined "customer" property of the DOM element,
|
||||
@@ -844,7 +827,7 @@ Expressions
|
||||
snippets of JavaScript within your CoffeeScript, you can
|
||||
use backticks to pass it straight through.
|
||||
</p>
|
||||
<%= code_for('embedded', 'hi()') %>
|
||||
<%= codeFor('embedded', 'hi()') %>
|
||||
|
||||
<p>
|
||||
<span id="switch" class="bookmark"></span>
|
||||
@@ -861,12 +844,12 @@ Expressions
|
||||
values for each <b>when</b> clause. If any of the values match, the clause
|
||||
runs.
|
||||
</p>
|
||||
<%= code_for('switch') %>
|
||||
<%= codeFor('switch') %>
|
||||
|
||||
<p>
|
||||
Switch statements can also be used without a control expression, turning them in to a cleaner alternative to if/else chains.
|
||||
</p>
|
||||
<%= code_for('switch_with_no_expression') %>
|
||||
<%= codeFor('switch_with_no_expression') %>
|
||||
|
||||
<p>
|
||||
<span id="try" class="bookmark"></span>
|
||||
@@ -874,7 +857,7 @@ Expressions
|
||||
Try/catch statements are just about the same as JavaScript (although
|
||||
they work as expressions).
|
||||
</p>
|
||||
<%= code_for('try') %>
|
||||
<%= codeFor('try') %>
|
||||
|
||||
<p>
|
||||
<span id="comparisons" class="bookmark"></span>
|
||||
@@ -884,7 +867,7 @@ Expressions
|
||||
from Python — making it easy to test if a value falls within a
|
||||
certain range.
|
||||
</p>
|
||||
<%= code_for('comparisons', 'healthy') %>
|
||||
<%= codeFor('comparisons', 'healthy') %>
|
||||
|
||||
<p>
|
||||
<span id="strings" class="bookmark"></span>
|
||||
@@ -893,18 +876,18 @@ Expressions
|
||||
strings allow for interpolated values, using <tt>#{ ... }</tt>,
|
||||
and single-quoted strings are literal.
|
||||
</p>
|
||||
<%= code_for('interpolation', 'sentence') %>
|
||||
<%= codeFor('interpolation', 'sentence') %>
|
||||
<p>
|
||||
Multiline strings are allowed in CoffeeScript.
|
||||
Multiline strings are allowed in CoffeeScript. Lines are joined by a single space unless they end with a backslash. Indentation is ignored.
|
||||
</p>
|
||||
<%= code_for('strings', 'mobyDick') %>
|
||||
<%= codeFor('strings', 'mobyDick') %>
|
||||
<p>
|
||||
Block strings can be used to hold formatted or indentation-sensitive text
|
||||
(or, if you just don't feel like escaping quotes and apostrophes). The
|
||||
indentation level that begins the block is maintained throughout, so
|
||||
you can keep it all aligned with the body of your code.
|
||||
</p>
|
||||
<%= code_for('heredocs', 'html') %>
|
||||
<%= codeFor('heredocs', 'html') %>
|
||||
<p>
|
||||
Double-quoted block strings, like other double-quoted strings, allow interpolation.
|
||||
</p>
|
||||
@@ -914,7 +897,7 @@ Expressions
|
||||
the top of a file. Block comments, which mirror the syntax for block strings,
|
||||
are preserved in the generated code.
|
||||
</p>
|
||||
<%= code_for('block_comment') %>
|
||||
<%= codeFor('block_comment') %>
|
||||
|
||||
<p>
|
||||
<span id="regexes" class="bookmark"></span>
|
||||
@@ -925,7 +908,7 @@ Expressions
|
||||
block regexes are delimited by <tt>///</tt> and go a long way towards making complex
|
||||
regular expressions readable. To quote from the CoffeeScript source:
|
||||
</p>
|
||||
<%= code_for('heregexes') %>
|
||||
<%= codeFor('heregexes') %>
|
||||
|
||||
|
||||
<h2>
|
||||
@@ -951,7 +934,7 @@ Expressions
|
||||
be made available in the <tt>options</tt> object. Here's a task that uses
|
||||
the Node.js API to rebuild CoffeeScript's parser:
|
||||
</p>
|
||||
<%= code_for('cake_tasks') %>
|
||||
<%= codeFor('cake_tasks') %>
|
||||
<p>
|
||||
If you need to invoke one task before another — for example, running
|
||||
<tt>build</tt> before <tt>test</tt>, you can use the <tt>invoke</tt> function:
|
||||
@@ -1059,6 +1042,16 @@ Expressions
|
||||
is a succinct and freely downloadable guide to building testable
|
||||
applications with CoffeeScript and Jasmine.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.packtpub.com/coffeescript-application-development/book">CoffeeScript Application Development</a>
|
||||
is a new book from Packt Publishing that introduces CoffeeScript while
|
||||
walking through the process of building a demonstration web application.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.manning.com/lee/">CoffeeScript in Action</a>
|
||||
is a new book from Manning Publications that covers CoffeeScript syntax, composition techniques
|
||||
and application development.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>
|
||||
@@ -1198,6 +1191,47 @@ Expressions
|
||||
Change Log
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">
|
||||
<a href="https://github.com/jashkenas/coffee-script/compare/1.6.3...1.7.0">1.7.0</a>
|
||||
<span class="timestamp"> – <small>January 28, 2014</small></span>
|
||||
</b>
|
||||
<ul>
|
||||
<li>
|
||||
When requiring CoffeeScript files in Node you must now explicitly register the compiler. This can be done with <tt>require 'coffee-script/register'</tt> or <tt>CoffeeScript.register()</tt>. Also for configuration such as Mocha's, use <b>coffee-script/register</b>.
|
||||
</li>
|
||||
<li>
|
||||
Improved error messages, source maps and stack traces. Source maps now use the updated <tt>//#</tt> syntax.
|
||||
</li>
|
||||
<li>
|
||||
Leading <tt>.</tt> now closes all open calls, allowing for simpler chaining syntax.
|
||||
</li>
|
||||
</ul>
|
||||
<%= codeFor('chaining') %>
|
||||
<ul>
|
||||
<li>
|
||||
Added <tt>**</tt>, <tt>//</tt> and <tt>%%</tt> operators and <tt>...</tt> expansion in paramater lists and destructuring expressions.
|
||||
</li>
|
||||
<li>
|
||||
Multiline strings are now joined by a single space and ignore all indentation. A backslash at the end of a line can denote the amount of whitespace between lines, in both strings and heredocs. Backslashes correctly escape whitespace in block regexes.
|
||||
</li>
|
||||
<li>
|
||||
Closing brackets can now be indented and therefore no longer cause unexpected error.
|
||||
</li>
|
||||
<li>
|
||||
Several breaking compilation fixes. Non-callable literals (strings, numbers etc.) don't compile in a call now and multiple postfix conditionals compile properly. Postfix conditionals and loops always bind object literals. Conditional assignment compiles properly in subexpressions. <tt>super</tt> is disallowed outside of methods and works correctly inside <tt>for</tt> loops.
|
||||
</li>
|
||||
<li>
|
||||
Formatting of compiled block comments has been improved.
|
||||
</li>
|
||||
<li>
|
||||
No more <tt>-p</tt> folders on Windows.
|
||||
</li>
|
||||
<li>
|
||||
The <tt>options</tt> object passed to CoffeeScript is no longer mutated.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">
|
||||
<a href="https://github.com/jashkenas/coffee-script/compare/1.6.2...1.6.3">1.6.3</a>
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var volume, winner;
|
||||
|
||||
if (ignition === true) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var courses, dish, food, foods, i, _i, _j, _k, _len, _len1, _len2, _ref;
|
||||
|
||||
_ref = ['toast', 'cheese', 'wine'];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
|
||||
/*
|
||||
SkinnyMochaHalfCaffScript Compiler v1.0
|
||||
Released under the MIT License
|
||||
*/
|
||||
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var fs;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
4
documentation/js/chaining.js
Normal file
4
documentation/js/chaining.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
$('body').click(function(e) {
|
||||
return $('.box').fadeIn('fast').addClass('.active');
|
||||
}).css('background', 'white');
|
||||
@@ -1,5 +1,5 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
var Animal, Horse, Snake, sam, tom, _ref, _ref1,
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var Animal, Horse, Snake, sam, tom,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
@@ -20,8 +20,7 @@ Snake = (function(_super) {
|
||||
__extends(Snake, _super);
|
||||
|
||||
function Snake() {
|
||||
_ref = Snake.__super__.constructor.apply(this, arguments);
|
||||
return _ref;
|
||||
return Snake.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
Snake.prototype.move = function() {
|
||||
@@ -37,8 +36,7 @@ Horse = (function(_super) {
|
||||
__extends(Horse, _super);
|
||||
|
||||
function Horse() {
|
||||
_ref1 = Horse.__super__.constructor.apply(this, arguments);
|
||||
return _ref1;
|
||||
return Horse.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
Horse.prototype.move = function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var cholesterol, healthy;
|
||||
|
||||
cholesterol = 127;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var date, mood;
|
||||
|
||||
if (singing) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
var Person;
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var Person, tim;
|
||||
|
||||
Person = (function() {
|
||||
function Person(options) {
|
||||
@@ -9,3 +9,7 @@ Person = (function() {
|
||||
return Person;
|
||||
|
||||
})();
|
||||
|
||||
tim = new Person({
|
||||
age: 4
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var fill;
|
||||
|
||||
fill = function(container, liquid) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var filename, _fn, _i, _len;
|
||||
|
||||
_fn = function(filename) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var hi;
|
||||
|
||||
hi = function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var footprints, solipsism, speed;
|
||||
|
||||
if ((typeof mind !== "undefined" && mind !== null) && (typeof world === "undefined" || world === null)) {
|
||||
|
||||
6
documentation/js/expansion.js
Normal file
6
documentation/js/expansion.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var first, last, text, _ref;
|
||||
|
||||
text = "Every literary critic believes he will outwit history and have the last word";
|
||||
|
||||
_ref = text.split(" "), first = _ref[0], last = _ref[_ref.length - 1];
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var eldest, grade;
|
||||
|
||||
grade = function(student) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var one, six, three, two;
|
||||
|
||||
six = (one = 1) + (two = 2) + (three = 3);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var globals, name;
|
||||
|
||||
globals = ((function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var error;
|
||||
|
||||
alert((function() {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var Account;
|
||||
|
||||
Account = function(customer, cart) {
|
||||
var _this = this;
|
||||
this.customer = customer;
|
||||
this.cart = cart;
|
||||
return $('.shopping_cart').bind('click', function(event) {
|
||||
return _this.customer.purchase(_this.cart);
|
||||
});
|
||||
return $('.shopping_cart').bind('click', (function(_this) {
|
||||
return function(event) {
|
||||
return _this.customer.purchase(_this.cart);
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var cube, square;
|
||||
|
||||
square = function(x) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var html;
|
||||
|
||||
html = "<strong>\n cup of coffeescript\n</strong>";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var OPERATOR;
|
||||
|
||||
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var author, quote, sentence;
|
||||
|
||||
author = "Wittgenstein";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var city, forecast, temp, weatherReport, _ref;
|
||||
|
||||
weatherReport = function(location) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var age, ages, child, yearsOld;
|
||||
|
||||
yearsOld = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var city, futurists, name, street, _ref, _ref1;
|
||||
|
||||
futurists = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var bitlist, kids, singers, song;
|
||||
|
||||
song = ["do", "re", "mi", "fa", "so"];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
$('.account').attr({
|
||||
"class": 'active'
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var cubes, list, math, num, number, opposite, race, square,
|
||||
__slice = [].slice;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var theBait, theSwitch, _ref;
|
||||
|
||||
theBait = 1000;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var close, contents, open, tag, _i, _ref,
|
||||
__slice = [].slice;
|
||||
|
||||
|
||||
2
documentation/js/prototypes.js
vendored
2
documentation/js/prototypes.js
vendored
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
String.prototype.dasherize = function() {
|
||||
return this.replace(/_/g, "-");
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var countdown, num;
|
||||
|
||||
countdown = (function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var changeNumbers, inner, outer;
|
||||
|
||||
outer = 1;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var copy, end, middle, numbers, start;
|
||||
|
||||
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
start = numbers.slice(0, 3);
|
||||
|
||||
middle = numbers.slice(3, 6);
|
||||
middle = numbers.slice(3, -2);
|
||||
|
||||
end = numbers.slice(6);
|
||||
end = numbers.slice(-2);
|
||||
|
||||
copy = numbers.slice(0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var zip, _ref;
|
||||
|
||||
zip = typeof lottery.drawWinner === "function" ? (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var awardMedals, contenders, gold, rest, silver,
|
||||
__slice = [].slice;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var numbers, _ref;
|
||||
|
||||
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var mobyDick;
|
||||
|
||||
mobyDick = "Call me Ishmael. Some years ago -- never mind how long precisely -- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
switch (day) {
|
||||
case "Mon":
|
||||
go(work);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var grade, score;
|
||||
|
||||
score = 76;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var error;
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
var lyrics, num;
|
||||
|
||||
if (this.studyingEconomics) {
|
||||
@@ -16,7 +16,7 @@ lyrics = (function() {
|
||||
var _results;
|
||||
_results = [];
|
||||
while (num -= 1) {
|
||||
_results.push("" + num + " little monkeys, jumping on the bed. One fell out and bumped his head.");
|
||||
_results.push("" + num + " little monkeys, jumping on the bed. One fell out and bumped his head.");
|
||||
}
|
||||
return _results;
|
||||
})();
|
||||
|
||||
@@ -31,7 +31,7 @@ File.open = (path, mode, block) ->
|
||||
# Write.
|
||||
write = (location, data) ->
|
||||
path = new Pathname location
|
||||
throw new Error "Location does not exist" unless fs.existsSync(location)
|
||||
throw new Error "Location does not exist" unless fs.existsSync location
|
||||
|
||||
File.open path, 'w', (file) ->
|
||||
return false if Digest.MD5.hexdigest(file.read()) is data.hash()
|
||||
|
||||
@@ -13,7 +13,7 @@ run_loop = ->
|
||||
wait()
|
||||
|
||||
# Objects:
|
||||
dense_object_literal = {one: 1, two: 2, three: 3}
|
||||
dense_object_literal = one: 1, two: 2, three: 3
|
||||
|
||||
spaced_out_multiline_object =
|
||||
pi: 3.14159
|
||||
@@ -56,7 +56,7 @@ race = ->
|
||||
run()
|
||||
walk()
|
||||
crawl()
|
||||
if tired then return sleep()
|
||||
return sleep() if tired
|
||||
race()
|
||||
|
||||
# Conditional assignment:
|
||||
@@ -64,7 +64,7 @@ good or= evil
|
||||
wine and= cheese
|
||||
|
||||
# Nested property access and calls.
|
||||
((moon.turn(360))).shapes[3].move({x: 45, y: 30}).position['top'].offset('x')
|
||||
(moon.turn 360).shapes[3].move(x: 45, y: 30).position['top'].offset('x')
|
||||
|
||||
a = b = c = 5
|
||||
|
||||
@@ -79,7 +79,7 @@ try
|
||||
dogs_and_cats_living_together()
|
||||
throw "up"
|
||||
catch error
|
||||
print(error)
|
||||
print error
|
||||
finally
|
||||
clean_up()
|
||||
|
||||
@@ -130,8 +130,8 @@ wednesday = -> eat_breakfast(); go_to_work(); eat_dinner()
|
||||
|
||||
# Multiline strings with inner quotes.
|
||||
story = "Lorem ipsum dolor \"sit\" amet, consectetuer adipiscing elit,
|
||||
sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna
|
||||
aliquam erat volutpat. Ut wisi enim ad."
|
||||
sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna
|
||||
aliquam erat volutpat. Ut wisi enim ad."
|
||||
|
||||
# Inheritance and calling super.
|
||||
class Animal
|
||||
|
||||
@@ -19,7 +19,7 @@ binary_search = (items, value) ->
|
||||
|
||||
|
||||
# Test the function.
|
||||
console.log 2 is binary_search [10, 20, 30, 40, 50], 30
|
||||
console.log 4 is binary_search [-97, 35, 67, 88, 1200], 1200
|
||||
console.log 0 is binary_search [0, 45, 70], 0
|
||||
console.log(-1 is binary_search [0, 45, 70], 10)
|
||||
console.log 2 is binary_search [10, 20, 30, 40, 50], 30
|
||||
console.log 4 is binary_search [-97, 35, 67, 88, 1200], 1200
|
||||
console.log 0 is binary_search [0, 45, 70], 0
|
||||
console.log -1 is binary_search [0, 45, 70], 10
|
||||
@@ -1,8 +1,8 @@
|
||||
# A bubble sort implementation, sorting the given array in-place.
|
||||
bubble_sort = (list) ->
|
||||
for i in [0...list.length]
|
||||
for j in [0...list.length - i]
|
||||
[list[j], list[j+1]] = [list[j+1], list[j]] if list[j] > list[j+1]
|
||||
for j in [0...list.length - i] when list[j] > list[j + 1]
|
||||
[list[j], list[j+1]] = [list[j + 1], list[j]]
|
||||
list
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# "Classic" linked list implementation that doesn't keep track of its size.
|
||||
class LinkedList
|
||||
|
||||
->
|
||||
this._head = null # Pointer to the first item in the list.
|
||||
constructor: ->
|
||||
@_head = null # Pointer to the first item in the list.
|
||||
|
||||
|
||||
# Appends some data to the end of the list. This method traverses the existing
|
||||
@@ -12,10 +12,10 @@ class LinkedList
|
||||
# Create a new node object to wrap the data.
|
||||
node = data: data, next: null
|
||||
|
||||
current = this._head or= node
|
||||
current = @_head or= node
|
||||
|
||||
if this._head isnt node
|
||||
(current = current.next) while current.next
|
||||
if @_head isnt node
|
||||
current = current.next while current.next
|
||||
current.next = node
|
||||
|
||||
this
|
||||
@@ -27,11 +27,11 @@ class LinkedList
|
||||
# Check for out-of-bounds values.
|
||||
return null if index < 0
|
||||
|
||||
current = this._head or null
|
||||
current = @_head or null
|
||||
i = -1
|
||||
|
||||
# Advance through the list.
|
||||
(current = current.next) while current and index > (i += 1)
|
||||
current = current.next while current and index > ++i
|
||||
|
||||
# Return null if we've reached the end.
|
||||
current and current.data
|
||||
@@ -43,16 +43,16 @@ class LinkedList
|
||||
# Check for out-of-bounds values.
|
||||
return null if index < 0
|
||||
|
||||
current = this._head or null
|
||||
current = @_head or null
|
||||
i = -1
|
||||
|
||||
# Special case: removing the first item.
|
||||
if index is 0
|
||||
this._head = current.next
|
||||
@_head = current.next
|
||||
else
|
||||
|
||||
# Find the right location.
|
||||
([previous, current] = [current, current.next]) while index > (i += 1)
|
||||
[previous, current] = [current, current.next] while index > ++i
|
||||
|
||||
# Skip over the item to remove.
|
||||
previous.next = current.next
|
||||
@@ -63,7 +63,7 @@ class LinkedList
|
||||
|
||||
# Calculate the number of items in the list.
|
||||
size: ->
|
||||
current = this._head
|
||||
current = @_head
|
||||
count = 0
|
||||
|
||||
while current
|
||||
@@ -76,7 +76,7 @@ class LinkedList
|
||||
# Convert the list into an array.
|
||||
toArray: ->
|
||||
result = []
|
||||
current = this._head
|
||||
current = @_head
|
||||
|
||||
while current
|
||||
result.push current.data
|
||||
@@ -86,7 +86,7 @@ class LinkedList
|
||||
|
||||
|
||||
# The string representation of the linked list.
|
||||
toString: -> this.toArray().toString()
|
||||
toString: -> @toArray().toString()
|
||||
|
||||
|
||||
# Tests.
|
||||
|
||||
@@ -7,13 +7,13 @@ is_valid_identifier = (identifier) ->
|
||||
sum = 0
|
||||
alt = false
|
||||
|
||||
for i in [identifier.length - 1..0] by -1
|
||||
for c in identifier by -1
|
||||
|
||||
# Get the next digit.
|
||||
num = parseInt identifier.charAt(i), 10
|
||||
num = parseInt c, 10
|
||||
|
||||
# If it's not a valid number, abort.
|
||||
return false if isNaN(num)
|
||||
return false if isNaN num
|
||||
|
||||
# If it's an alternate number...
|
||||
if alt
|
||||
|
||||
@@ -3,13 +3,12 @@ merge_sort = (list) ->
|
||||
|
||||
return list if list.length is 1
|
||||
|
||||
result = []
|
||||
pivot = Math.floor list.length / 2
|
||||
left = merge_sort list.slice 0, pivot
|
||||
right = merge_sort list.slice pivot
|
||||
|
||||
while left.length and right.length
|
||||
result.push(if left[0] < right[0] then left.shift() else right.shift())
|
||||
result = while left.length and right.length
|
||||
if left[0] < right[0] then left.shift() else right.shift()
|
||||
|
||||
result.concat(left).concat(right)
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Examples from the Poignant Guide.
|
||||
# These are examples of syntax differences between CoffeeScript and Ruby,
|
||||
# they won't run.
|
||||
|
||||
# ['toast', 'cheese', 'wine'].each { |food| print food.capitalize }
|
||||
|
||||
['toast', 'wine', 'cheese'].each (food) -> print food.capitalize()
|
||||
print food.capitalize() for food in ['toast', 'wine', 'cheese']
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -14,10 +17,10 @@
|
||||
# end
|
||||
|
||||
LotteryTicket =
|
||||
get_picks: -> @picks
|
||||
set_picks: (@picks) ->
|
||||
get_purchased: -> @purchase
|
||||
set_purchased: (@purchased) ->
|
||||
get_picks: -> @picks
|
||||
set_picks: (@picks) ->
|
||||
get_purchased: -> @purchase
|
||||
set_purchased: (@purchased) ->
|
||||
|
||||
|
||||
|
||||
@@ -42,13 +45,10 @@ LotteryDraw =
|
||||
play: ->
|
||||
result = LotteryTicket.new_random()
|
||||
winners = {}
|
||||
this.tickets.each (buyer, ticket_list) ->
|
||||
ticket_list.each (ticket) ->
|
||||
score = ticket.score result
|
||||
return if score is 0
|
||||
winners[buyer] or= []
|
||||
winners[buyer].push [ticket, score]
|
||||
this.tickets = {}
|
||||
for buyer, ticketList of @tickets
|
||||
for ticket in ticketList when (score = ticket.score result) isnt 0
|
||||
(winners[buyer] or= []).push [ticket, score]
|
||||
@tickets = {}
|
||||
winners
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ LotteryDraw =
|
||||
|
||||
WishScanner =
|
||||
scan_for_a_wish: ->
|
||||
wish = this.read().detect (thought) -> thought.index('wish: ') is 0
|
||||
wish = @read().detect (thought) -> thought.indexOf('wish: ') is 0
|
||||
wish.replace 'wish: ', ''
|
||||
|
||||
|
||||
@@ -109,28 +109,28 @@ Creature =
|
||||
|
||||
# This method applies a hit taken during a fight.
|
||||
hit: (damage) ->
|
||||
p_up = Math.rand this.charisma
|
||||
p_up = Math.rand @charisma
|
||||
if p_up % 9 is 7
|
||||
this.life += p_up / 4
|
||||
console.log "[" + this.name + " magick powers up " + p_up + "!]"
|
||||
this.life -= damage
|
||||
if this.life <= 0 then console.log "[" + this.name + " has died.]"
|
||||
@life += p_up / 4
|
||||
console.log "[#{@name} magick powers up #{p_up}!]"
|
||||
@life -= damage
|
||||
if @life <= 0 then console.log "[#{@name} has died.]"
|
||||
|
||||
# This method takes one turn in a fight.
|
||||
fight: (enemy, weapon) ->
|
||||
if this.life <= 0 then return console.log "[" + this.name + "is too dead to fight!]"
|
||||
return console.log "[#{@name} is too dead to fight!]" if @life <= 0
|
||||
|
||||
# Attack the opponent.
|
||||
your_hit = Math.rand this.strength + weapon
|
||||
console.log "[You hit with " + your_hit + "points of damage!]"
|
||||
your_hit = Math.rand @strength + weapon
|
||||
console.log "[You hit with #{your_hit}points of damage!]"
|
||||
enemy.hit your_hit
|
||||
|
||||
# Retaliation.
|
||||
console.log enemy
|
||||
if enemy.life > 0
|
||||
enemy_hit = Math.rand enemy.strength + enemy.weapon
|
||||
console.log "[Your enemy hit with " + enemy_hit + "points of damage!]"
|
||||
this.hit enemy_hit
|
||||
console.log "[Your enemy hit with #{enemy_hit}points of damage!]"
|
||||
@hit enemy_hit
|
||||
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ code_words.each (real, code) -> idea.replace(real, code)
|
||||
# Save the jibberish to a new file
|
||||
print "File encoded. Please enter a name for this idea: "
|
||||
idea_name = gets().strip()
|
||||
File.open "idea-" + idea_name + '.txt', 'w', (file) -> file.write idea
|
||||
File.open "idea-#{idea_name}.txt", 'w', (file) -> file.write idea
|
||||
|
||||
|
||||
|
||||
@@ -174,8 +174,8 @@ File.open "idea-" + idea_name + '.txt', 'w', (file) -> file.write idea
|
||||
|
||||
wipe_mutterings_from = (sentence) ->
|
||||
throw new Error "cannot wipe mutterings" unless sentence.indexOf
|
||||
while sentence.indexOf('(') >= 0
|
||||
open = sentence.indexOf('(') - 1
|
||||
close = sentence.indexOf(')') + 1
|
||||
sentence = sentence.slice(0, open) + sentence.slice(close, sentence.length)
|
||||
while '(' in sentence
|
||||
open = sentence.indexOf('(')
|
||||
close = sentence.indexOf(')')
|
||||
sentence = "#{sentence[0...open]}#{sentence[close + 1..]}"
|
||||
sentence
|
||||
|
||||
@@ -45,7 +45,7 @@ foods[2]
|
||||
# (key, ' is a ', val) join print.
|
||||
|
||||
for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'}
|
||||
print key + ' is a ' + val
|
||||
print "#{key} is a #{val}"
|
||||
|
||||
|
||||
# Person = class: /name, /age, /sex.
|
||||
@@ -54,7 +54,7 @@ for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'}
|
||||
|
||||
class Person
|
||||
print: ->
|
||||
print 'My name is ' + @name + '.'
|
||||
print "My name is #{@name}."
|
||||
|
||||
|
||||
# p = Person ()
|
||||
@@ -74,7 +74,7 @@ class Policeman extends Person
|
||||
(@rank) ->
|
||||
|
||||
print: ->
|
||||
print 'My name is ' + @name + " and I'm a " + @rank + '.'
|
||||
print "My name is #{@name} and I'm a #{@rank}."
|
||||
|
||||
print new Policeman 'Constable'
|
||||
|
||||
@@ -180,7 +180,7 @@ if 3.gender?
|
||||
# session = url query ? at ('session').
|
||||
|
||||
HomePage::get = (url) ->
|
||||
session = url.query.session if url.query?
|
||||
session = url.query?.session
|
||||
|
||||
|
||||
# BTree = class: /left, /right.
|
||||
|
||||
@@ -7,6 +7,6 @@ server = http.createServer (req, res) ->
|
||||
res.write 'Hello, World!'
|
||||
res.end()
|
||||
|
||||
server.listen 3000
|
||||
server.listen PORT = 3000
|
||||
|
||||
console.log "Server running at http://localhost:3000/"
|
||||
console.log "Server running at http://localhost:#{PORT}/"
|
||||
|
||||
File diff suppressed because one or more lines are too long
1294
index.html
1294
index.html
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var CoffeeScript, compile, runScripts,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
@@ -41,15 +41,18 @@
|
||||
options.sourceMap = true;
|
||||
options.inline = true;
|
||||
_ref = CoffeeScript.compile(code, options), js = _ref.js, v3SourceMap = _ref.v3SourceMap;
|
||||
return "" + js + "\n//@ sourceMappingURL=data:application/json;base64," + (btoa(unescape(encodeURIComponent(v3SourceMap)))) + "\n//@ sourceURL=coffeescript";
|
||||
return "" + js + "\n//# sourceMappingURL=data:application/json;base64," + (btoa(unescape(encodeURIComponent(v3SourceMap)))) + "\n//# sourceURL=coffeescript";
|
||||
};
|
||||
}
|
||||
|
||||
CoffeeScript.load = function(url, callback, options) {
|
||||
CoffeeScript.load = function(url, callback, options, hold) {
|
||||
var xhr;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
if (hold == null) {
|
||||
hold = false;
|
||||
}
|
||||
options.sourceFiles = [url];
|
||||
xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP') : new window.XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
@@ -57,15 +60,18 @@
|
||||
xhr.overrideMimeType('text/plain');
|
||||
}
|
||||
xhr.onreadystatechange = function() {
|
||||
var _ref;
|
||||
var param, _ref;
|
||||
if (xhr.readyState === 4) {
|
||||
if ((_ref = xhr.status) === 0 || _ref === 200) {
|
||||
CoffeeScript.run(xhr.responseText, options);
|
||||
param = [xhr.responseText, options];
|
||||
if (!hold) {
|
||||
CoffeeScript.run.apply(CoffeeScript, param);
|
||||
}
|
||||
} else {
|
||||
throw new Error("Could not load " + url);
|
||||
}
|
||||
if (callback) {
|
||||
return callback();
|
||||
return callback(param);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -73,7 +79,7 @@
|
||||
};
|
||||
|
||||
runScripts = function() {
|
||||
var coffees, coffeetypes, execute, index, length, s, scripts;
|
||||
var coffees, coffeetypes, execute, i, index, s, script, scripts, _fn, _i, _len;
|
||||
scripts = window.document.getElementsByTagName('script');
|
||||
coffeetypes = ['text/coffeescript', 'text/literate-coffeescript'];
|
||||
coffees = (function() {
|
||||
@@ -88,25 +94,35 @@
|
||||
return _results;
|
||||
})();
|
||||
index = 0;
|
||||
length = coffees.length;
|
||||
(execute = function() {
|
||||
var mediatype, options, script;
|
||||
script = coffees[index++];
|
||||
mediatype = script != null ? script.type : void 0;
|
||||
if (__indexOf.call(coffeetypes, mediatype) >= 0) {
|
||||
options = {
|
||||
literate: mediatype === 'text/literate-coffeescript'
|
||||
};
|
||||
if (script.src) {
|
||||
return CoffeeScript.load(script.src, execute, options);
|
||||
} else {
|
||||
options.sourceFiles = ['embedded'];
|
||||
CoffeeScript.run(script.innerHTML, options);
|
||||
return execute();
|
||||
}
|
||||
execute = function() {
|
||||
var param;
|
||||
param = coffees[index];
|
||||
if (param instanceof Array) {
|
||||
CoffeeScript.run.apply(CoffeeScript, param);
|
||||
index++;
|
||||
return execute();
|
||||
}
|
||||
})();
|
||||
return null;
|
||||
};
|
||||
_fn = function(script, i) {
|
||||
var options;
|
||||
options = {
|
||||
literate: script.type === coffeetypes[1]
|
||||
};
|
||||
if (script.src) {
|
||||
return CoffeeScript.load(script.src, function(param) {
|
||||
coffees[i] = param;
|
||||
return execute();
|
||||
}, options, true);
|
||||
} else {
|
||||
options.sourceFiles = ['embedded'];
|
||||
return coffees[i] = [script.innerHTML, options];
|
||||
}
|
||||
};
|
||||
for (i = _i = 0, _len = coffees.length; _i < _len; i = ++_i) {
|
||||
script = coffees[i];
|
||||
_fn(script, i);
|
||||
}
|
||||
return execute();
|
||||
};
|
||||
|
||||
if (window.addEventListener) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var CoffeeScript, cakefileDirectory, existsSync, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
|
||||
var CoffeeScript, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
CoffeeScript = require('./coffee-script');
|
||||
|
||||
existsSync = fs.existsSync || path.existsSync;
|
||||
|
||||
tasks = {};
|
||||
|
||||
options = {};
|
||||
@@ -101,7 +99,7 @@
|
||||
|
||||
cakefileDirectory = function(dir) {
|
||||
var parent;
|
||||
if (existsSync(path.join(dir, 'Cakefile'))) {
|
||||
if (fs.existsSync(path.join(dir, 'Cakefile'))) {
|
||||
return dir;
|
||||
}
|
||||
parent = path.normalize(path.join(dir, '..'));
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var Lexer, Module, SourceMap, child_process, compile, ext, findExtension, fork, formatSourcePosition, fs, helpers, lexer, loadFile, parser, patchStackTrace, patched, path, sourceMaps, vm, _i, _len, _ref,
|
||||
__hasProp = {}.hasOwnProperty;
|
||||
var Lexer, SourceMap, compile, formatSourcePosition, fs, getSourceMap, helpers, lexer, parser, path, sourceMaps, vm, withPrettyErrors,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
@@ -9,8 +10,6 @@
|
||||
|
||||
path = require('path');
|
||||
|
||||
child_process = require('child_process');
|
||||
|
||||
Lexer = require('./lexer').Lexer;
|
||||
|
||||
parser = require('./parser').parser;
|
||||
@@ -19,16 +18,31 @@
|
||||
|
||||
SourceMap = require('./sourcemap');
|
||||
|
||||
exports.VERSION = '1.6.3';
|
||||
exports.VERSION = '1.7.0';
|
||||
|
||||
exports.FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md'];
|
||||
|
||||
exports.helpers = helpers;
|
||||
|
||||
exports.compile = compile = function(code, options) {
|
||||
var answer, currentColumn, currentLine, fragment, fragments, header, js, map, merge, newLines, _i, _len;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
merge = helpers.merge;
|
||||
withPrettyErrors = function(fn) {
|
||||
return function(code, options) {
|
||||
var err;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
try {
|
||||
return fn.call(this, code, options);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
throw helpers.updateSyntaxError(err, code, options.filename);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
exports.compile = compile = withPrettyErrors(function(code, options) {
|
||||
var answer, currentColumn, currentLine, extend, fragment, fragments, header, js, map, merge, newLines, _i, _len;
|
||||
merge = helpers.merge, extend = helpers.extend;
|
||||
options = extend({}, options);
|
||||
if (options.sourceMap) {
|
||||
map = new SourceMap;
|
||||
}
|
||||
@@ -52,7 +66,11 @@
|
||||
}
|
||||
newLines = helpers.count(fragment.code, "\n");
|
||||
currentLine += newLines;
|
||||
currentColumn = fragment.code.length - (newLines ? fragment.code.lastIndexOf("\n") : 0);
|
||||
if (newLines) {
|
||||
currentColumn = fragment.code.length - (fragment.code.lastIndexOf("\n") + 1);
|
||||
} else {
|
||||
currentColumn += fragment.code.length;
|
||||
}
|
||||
}
|
||||
js += fragment.code;
|
||||
}
|
||||
@@ -70,40 +88,35 @@
|
||||
} else {
|
||||
return js;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
exports.tokens = function(code, options) {
|
||||
exports.tokens = withPrettyErrors(function(code, options) {
|
||||
return lexer.tokenize(code, options);
|
||||
};
|
||||
});
|
||||
|
||||
exports.nodes = function(source, options) {
|
||||
exports.nodes = withPrettyErrors(function(source, options) {
|
||||
if (typeof source === 'string') {
|
||||
return parser.parse(lexer.tokenize(source, options));
|
||||
} else {
|
||||
return parser.parse(source);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
exports.run = function(code, options) {
|
||||
var answer, mainModule;
|
||||
var answer, dir, mainModule, _ref;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
mainModule = require.main;
|
||||
if (options.sourceMap == null) {
|
||||
options.sourceMap = true;
|
||||
}
|
||||
mainModule.filename = process.argv[1] = options.filename ? fs.realpathSync(options.filename) : '.';
|
||||
mainModule.moduleCache && (mainModule.moduleCache = {});
|
||||
mainModule.paths = require('module')._nodeModulePaths(path.dirname(fs.realpathSync(options.filename || '.')));
|
||||
dir = options.fileName ? path.dirname(fs.realpathSync(options.filename)) : fs.realpathSync('.');
|
||||
mainModule.paths = require('module')._nodeModulePaths(dir);
|
||||
if (!helpers.isCoffee(mainModule.filename) || require.extensions) {
|
||||
answer = compile(code, options);
|
||||
patchStackTrace();
|
||||
sourceMaps[mainModule.filename] = answer.sourceMap;
|
||||
return mainModule._compile(answer.js, mainModule.filename);
|
||||
} else {
|
||||
return mainModule._compile(code, mainModule.filename);
|
||||
code = (_ref = answer.js) != null ? _ref : answer;
|
||||
}
|
||||
return mainModule._compile(code, mainModule.filename);
|
||||
};
|
||||
|
||||
exports["eval"] = function(code, options) {
|
||||
@@ -169,69 +182,29 @@
|
||||
}
|
||||
};
|
||||
|
||||
loadFile = function(module, filename) {
|
||||
var answer, raw, stripped;
|
||||
raw = fs.readFileSync(filename, 'utf8');
|
||||
stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw;
|
||||
answer = compile(stripped, {
|
||||
filename: filename,
|
||||
sourceMap: true,
|
||||
literate: helpers.isLiterate(filename)
|
||||
});
|
||||
sourceMaps[filename] = answer.sourceMap;
|
||||
return module._compile(answer.js, filename);
|
||||
exports.register = function() {
|
||||
return require('./register');
|
||||
};
|
||||
|
||||
if (require.extensions) {
|
||||
_ref = ['.coffee', '.litcoffee', '.coffee.md'];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
ext = _ref[_i];
|
||||
require.extensions[ext] = loadFile;
|
||||
exports._compileFile = function(filename, sourceMap) {
|
||||
var answer, err, raw, stripped;
|
||||
if (sourceMap == null) {
|
||||
sourceMap = false;
|
||||
}
|
||||
Module = require('module');
|
||||
findExtension = function(filename) {
|
||||
var curExtension, extensions;
|
||||
extensions = path.basename(filename).split('.');
|
||||
if (extensions[0] === '') {
|
||||
extensions.shift();
|
||||
}
|
||||
while (extensions.shift()) {
|
||||
curExtension = '.' + extensions.join('.');
|
||||
if (Module._extensions[curExtension]) {
|
||||
return curExtension;
|
||||
}
|
||||
}
|
||||
return '.js';
|
||||
};
|
||||
Module.prototype.load = function(filename) {
|
||||
var extension;
|
||||
this.filename = filename;
|
||||
this.paths = Module._nodeModulePaths(path.dirname(filename));
|
||||
extension = findExtension(filename);
|
||||
Module._extensions[extension](this, filename);
|
||||
return this.loaded = true;
|
||||
};
|
||||
}
|
||||
|
||||
if (child_process) {
|
||||
fork = child_process.fork;
|
||||
child_process.fork = function(path, args, options) {
|
||||
var execPath;
|
||||
if (args == null) {
|
||||
args = [];
|
||||
}
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
execPath = helpers.isCoffee(path) ? 'coffee' : null;
|
||||
if (!Array.isArray(args)) {
|
||||
args = [];
|
||||
options = args || {};
|
||||
}
|
||||
options.execPath || (options.execPath = execPath);
|
||||
return fork(path, args, options);
|
||||
};
|
||||
}
|
||||
raw = fs.readFileSync(filename, 'utf8');
|
||||
stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw;
|
||||
try {
|
||||
answer = compile(stripped, {
|
||||
filename: filename,
|
||||
sourceMap: sourceMap,
|
||||
literate: helpers.isLiterate(filename)
|
||||
});
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
throw helpers.updateSyntaxError(err, stripped, filename);
|
||||
}
|
||||
return answer;
|
||||
};
|
||||
|
||||
lexer = new Lexer;
|
||||
|
||||
@@ -241,6 +214,7 @@
|
||||
token = this.tokens[this.pos++];
|
||||
if (token) {
|
||||
tag = token[0], this.yytext = token[1], this.yylloc = token[2];
|
||||
this.errorToken = token.origin || token;
|
||||
this.yylineno = this.yylloc.first_line;
|
||||
} else {
|
||||
tag = '';
|
||||
@@ -259,52 +233,12 @@
|
||||
parser.yy = require('./nodes');
|
||||
|
||||
parser.yy.parseError = function(message, _arg) {
|
||||
var token;
|
||||
var errorLoc, errorTag, errorText, errorToken, token, tokens, _ref;
|
||||
token = _arg.token;
|
||||
message = "unexpected " + (token === 1 ? 'end of input' : token);
|
||||
return helpers.throwSyntaxError(message, parser.lexer.yylloc);
|
||||
};
|
||||
|
||||
patched = false;
|
||||
|
||||
sourceMaps = {};
|
||||
|
||||
patchStackTrace = function() {
|
||||
var mainModule;
|
||||
if (patched) {
|
||||
return;
|
||||
}
|
||||
patched = true;
|
||||
mainModule = require.main;
|
||||
return Error.prepareStackTrace = function(err, stack) {
|
||||
var frame, frames, getSourceMapping, sourceFiles, _ref1;
|
||||
sourceFiles = {};
|
||||
getSourceMapping = function(filename, line, column) {
|
||||
var answer, sourceMap;
|
||||
sourceMap = sourceMaps[filename];
|
||||
if (sourceMap) {
|
||||
answer = sourceMap.sourceLocation([line - 1, column - 1]);
|
||||
}
|
||||
if (answer) {
|
||||
return [answer[0] + 1, answer[1] + 1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
frames = (function() {
|
||||
var _j, _len1, _results;
|
||||
_results = [];
|
||||
for (_j = 0, _len1 = stack.length; _j < _len1; _j++) {
|
||||
frame = stack[_j];
|
||||
if (frame.getFunction() === exports.run) {
|
||||
break;
|
||||
}
|
||||
_results.push(" at " + (formatSourcePosition(frame, getSourceMapping)));
|
||||
}
|
||||
return _results;
|
||||
})();
|
||||
return "" + err.name + ": " + ((_ref1 = err.message) != null ? _ref1 : '') + "\n" + (frames.join('\n')) + "\n";
|
||||
};
|
||||
_ref = parser.lexer, errorToken = _ref.errorToken, tokens = _ref.tokens;
|
||||
errorTag = errorToken[0], errorText = errorToken[1], errorLoc = errorToken[2];
|
||||
errorText = errorToken === tokens[tokens.length - 1] ? 'end of input' : errorTag === 'INDENT' || errorTag === 'OUTDENT' ? 'indentation' : helpers.nameWhitespaceCharacter(errorText);
|
||||
return helpers.throwSyntaxError("unexpected " + errorText, errorLoc);
|
||||
};
|
||||
|
||||
formatSourcePosition = function(frame, getSourceMapping) {
|
||||
@@ -326,7 +260,7 @@
|
||||
line = frame.getLineNumber();
|
||||
column = frame.getColumnNumber();
|
||||
source = getSourceMapping(fileName, line, column);
|
||||
fileLocation = source ? "" + fileName + ":" + source[0] + ":" + source[1] + ", <js>:" + line + ":" + column : "" + fileName + ":" + line + ":" + column;
|
||||
fileLocation = source ? "" + fileName + ":" + source[0] + ":" + source[1] : "" + fileName + ":" + line + ":" + column;
|
||||
}
|
||||
functionName = frame.getFunctionName();
|
||||
isConstructor = frame.isConstructor();
|
||||
@@ -355,4 +289,47 @@
|
||||
}
|
||||
};
|
||||
|
||||
sourceMaps = {};
|
||||
|
||||
getSourceMap = function(filename) {
|
||||
var answer, _ref;
|
||||
if (sourceMaps[filename]) {
|
||||
return sourceMaps[filename];
|
||||
}
|
||||
if (_ref = path != null ? path.extname(filename) : void 0, __indexOf.call(exports.FILE_EXTENSIONS, _ref) < 0) {
|
||||
return;
|
||||
}
|
||||
answer = exports._compileFile(filename, true);
|
||||
return sourceMaps[filename] = answer.sourceMap;
|
||||
};
|
||||
|
||||
Error.prepareStackTrace = function(err, stack) {
|
||||
var frame, frames, getSourceMapping, _ref;
|
||||
getSourceMapping = function(filename, line, column) {
|
||||
var answer, sourceMap;
|
||||
sourceMap = getSourceMap(filename);
|
||||
if (sourceMap) {
|
||||
answer = sourceMap.sourceLocation([line - 1, column - 1]);
|
||||
}
|
||||
if (answer) {
|
||||
return [answer[0] + 1, answer[1] + 1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
frames = (function() {
|
||||
var _i, _len, _results;
|
||||
_results = [];
|
||||
for (_i = 0, _len = stack.length; _i < _len; _i++) {
|
||||
frame = stack[_i];
|
||||
if (frame.getFunction() === exports.run) {
|
||||
break;
|
||||
}
|
||||
_results.push(" at " + (formatSourcePosition(frame, getSourceMapping)));
|
||||
}
|
||||
return _results;
|
||||
})();
|
||||
return "" + err.name + ": " + ((_ref = err.message) != null ? _ref : '') + "\n" + (frames.join('\n')) + "\n";
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, exists, forkNode, fs, helpers, hidden, joinTimeout, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, sourceCode, sources, spawn, timeLog, unwatchDir, usage, useWinPathSep, version, wait, watch, watchDir, watchers, writeJs, _ref;
|
||||
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs, _ref,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
@@ -12,12 +13,12 @@
|
||||
|
||||
CoffeeScript = require('./coffee-script');
|
||||
|
||||
mkdirp = require('mkdirp');
|
||||
|
||||
_ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
exists = fs.exists || path.exists;
|
||||
|
||||
useWinPathSep = path.sep === '\\';
|
||||
|
||||
helpers.extend(CoffeeScript, new EventEmitter);
|
||||
@@ -36,7 +37,7 @@
|
||||
|
||||
BANNER = 'Usage: coffee [options] path/to/script.coffee -- [args]\n\nIf called without options, `coffee` will run your script.';
|
||||
|
||||
SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-e', '--eval', 'pass a string from the command line as input'], ['-h', '--help', 'display this help message'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling'], ['-m', '--map', 'generate source map and save as .map files'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-l', '--literate', 'treat stdio as literate style coffee-script'], ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce'], ['-v', '--version', 'display the version number'], ['-w', '--watch', 'watch scripts for changes and rerun commands']];
|
||||
SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-e', '--eval', 'pass a string from the command line as input'], ['-h', '--help', 'display this help message'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling'], ['-m', '--map', 'generate source map and save as .map files'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['--no-header', 'suppress the "Generated by" header'], ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-l', '--literate', 'treat stdio as literate style coffee-script'], ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce'], ['-v', '--version', 'display the version number'], ['-w', '--watch', 'watch scripts for changes and rerun commands']];
|
||||
|
||||
opts = {};
|
||||
|
||||
@@ -46,13 +47,16 @@
|
||||
|
||||
notSources = {};
|
||||
|
||||
watchers = {};
|
||||
watchedDirs = {};
|
||||
|
||||
optionParser = null;
|
||||
|
||||
exports.run = function() {
|
||||
var literals, source, _i, _len, _results;
|
||||
var literals, replCliOpts, source, _i, _len, _ref1, _results;
|
||||
parseOptions();
|
||||
replCliOpts = {
|
||||
useGlobal: true
|
||||
};
|
||||
if (opts.nodejs) {
|
||||
return forkNode();
|
||||
}
|
||||
@@ -63,94 +67,125 @@
|
||||
return version();
|
||||
}
|
||||
if (opts.interactive) {
|
||||
return require('./repl').start();
|
||||
}
|
||||
if (opts.watch && !fs.watch) {
|
||||
return printWarn("The --watch feature depends on Node v0.6.0+. You are running " + process.version + ".");
|
||||
return require('./repl').start(replCliOpts);
|
||||
}
|
||||
if (opts.stdio) {
|
||||
return compileStdio();
|
||||
}
|
||||
if (opts["eval"]) {
|
||||
return compileScript(null, sources[0]);
|
||||
return compileScript(null, opts["arguments"][0]);
|
||||
}
|
||||
if (!sources.length) {
|
||||
return require('./repl').start();
|
||||
if (!opts["arguments"].length) {
|
||||
return require('./repl').start(replCliOpts);
|
||||
}
|
||||
literals = opts.run ? sources.splice(1) : [];
|
||||
literals = opts.run ? opts["arguments"].splice(1) : [];
|
||||
process.argv = process.argv.slice(0, 2).concat(literals);
|
||||
process.argv[0] = 'coffee';
|
||||
if (opts.output) {
|
||||
opts.output = path.resolve(opts.output);
|
||||
}
|
||||
if (opts.join) {
|
||||
opts.join = path.resolve(opts.join);
|
||||
}
|
||||
_ref1 = opts["arguments"];
|
||||
_results = [];
|
||||
for (_i = 0, _len = sources.length; _i < _len; _i++) {
|
||||
source = sources[_i];
|
||||
_results.push(compilePath(source, true, path.normalize(source)));
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
source = _ref1[_i];
|
||||
source = path.resolve(source);
|
||||
_results.push(compilePath(source, true, source));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
compilePath = function(source, topLevel, base) {
|
||||
return fs.stat(source, function(err, stats) {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
if ((err != null ? err.code : void 0) === 'ENOENT') {
|
||||
var code, err, file, files, stats, _i, _len, _results;
|
||||
if (__indexOf.call(sources, source) >= 0 || watchedDirs[source] || !topLevel && (notSources[source] || hidden(source))) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
stats = fs.statSync(source);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (err.code === 'ENOENT') {
|
||||
console.error("File not found: " + source);
|
||||
process.exit(1);
|
||||
}
|
||||
if (stats.isDirectory() && path.dirname(source) !== 'node_modules') {
|
||||
if (opts.watch) {
|
||||
watchDir(source, base);
|
||||
}
|
||||
return fs.readdir(source, function(err, files) {
|
||||
var file, index, _ref1, _ref2;
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
if ((err != null ? err.code : void 0) === 'ENOENT') {
|
||||
return;
|
||||
}
|
||||
index = sources.indexOf(source);
|
||||
files = files.filter(function(file) {
|
||||
return !hidden(file);
|
||||
});
|
||||
[].splice.apply(sources, [index, index - index + 1].concat(_ref1 = (function() {
|
||||
var _i, _len, _results;
|
||||
_results = [];
|
||||
for (_i = 0, _len = files.length; _i < _len; _i++) {
|
||||
file = files[_i];
|
||||
_results.push(path.join(source, file));
|
||||
}
|
||||
return _results;
|
||||
})())), _ref1;
|
||||
[].splice.apply(sourceCode, [index, index - index + 1].concat(_ref2 = files.map(function() {
|
||||
return null;
|
||||
}))), _ref2;
|
||||
return files.forEach(function(file) {
|
||||
return compilePath(path.join(source, file), false, base);
|
||||
});
|
||||
});
|
||||
} else if (topLevel || helpers.isCoffee(source)) {
|
||||
if (opts.watch) {
|
||||
watch(source, base);
|
||||
}
|
||||
return fs.readFile(source, function(err, code) {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
if ((err != null ? err.code : void 0) === 'ENOENT') {
|
||||
return;
|
||||
}
|
||||
return compileScript(source, code.toString(), base);
|
||||
});
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
if (stats.isDirectory()) {
|
||||
if (path.basename(source) === 'node_modules') {
|
||||
notSources[source] = true;
|
||||
return removeSource(source, base);
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (opts.run) {
|
||||
compilePath(findDirectoryIndex(source), topLevel, base);
|
||||
return;
|
||||
}
|
||||
if (opts.watch) {
|
||||
watchDir(source, base);
|
||||
}
|
||||
try {
|
||||
files = fs.readdirSync(source);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (err.code === 'ENOENT') {
|
||||
return;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
_results = [];
|
||||
for (_i = 0, _len = files.length; _i < _len; _i++) {
|
||||
file = files[_i];
|
||||
_results.push(compilePath(path.join(source, file), false, base));
|
||||
}
|
||||
return _results;
|
||||
} else if (topLevel || helpers.isCoffee(source)) {
|
||||
sources.push(source);
|
||||
sourceCode.push(null);
|
||||
delete notSources[source];
|
||||
if (opts.watch) {
|
||||
watch(source, base);
|
||||
}
|
||||
try {
|
||||
code = fs.readFileSync(source);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (err.code === 'ENOENT') {
|
||||
return;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return compileScript(source, code.toString(), base);
|
||||
} else {
|
||||
return notSources[source] = true;
|
||||
}
|
||||
};
|
||||
|
||||
findDirectoryIndex = function(source) {
|
||||
var err, ext, index, _i, _len, _ref1;
|
||||
_ref1 = CoffeeScript.FILE_EXTENSIONS;
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
ext = _ref1[_i];
|
||||
index = path.join(source, "index" + ext);
|
||||
try {
|
||||
if ((fs.statSync(index)).isFile()) {
|
||||
return index;
|
||||
}
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.error("Missing index.coffee or index.litcoffee in " + source);
|
||||
return process.exit(1);
|
||||
};
|
||||
|
||||
compileScript = function(file, input, base) {
|
||||
var compiled, err, message, o, options, t, task, useColors;
|
||||
var compiled, err, message, o, options, t, task;
|
||||
if (base == null) {
|
||||
base = null;
|
||||
}
|
||||
@@ -168,6 +203,7 @@
|
||||
} else if (o.nodes) {
|
||||
return printLine(CoffeeScript.nodes(t.input, t.options).toString().trim());
|
||||
} else if (o.run) {
|
||||
CoffeeScript.register();
|
||||
return CoffeeScript.run(t.input, t.options);
|
||||
} else if (o.join && t.file !== o.join) {
|
||||
if (helpers.isLiterate(file)) {
|
||||
@@ -195,8 +231,7 @@
|
||||
if (CoffeeScript.listeners('failure').length) {
|
||||
return;
|
||||
}
|
||||
useColors = process.stdout.isTTY && !process.env.NODE_DISABLE_COLORS;
|
||||
message = helpers.prettyErrorMessage(err, file || '[stdin]', input, useColors);
|
||||
message = err.stack || ("" + err);
|
||||
if (o.watch) {
|
||||
return printLine(message + '\x07');
|
||||
} else {
|
||||
@@ -237,24 +272,23 @@
|
||||
};
|
||||
|
||||
watch = function(source, base) {
|
||||
var compile, compileTimeout, e, prevStats, rewatch, watchErr, watcher;
|
||||
var compile, compileTimeout, err, prevStats, rewatch, startWatcher, watchErr, watcher;
|
||||
watcher = null;
|
||||
prevStats = null;
|
||||
compileTimeout = null;
|
||||
watchErr = function(e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
if (sources.indexOf(source) === -1) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
rewatch();
|
||||
return compile();
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
removeSource(source, base, true);
|
||||
return compileJoin();
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
watchErr = function(err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
if (__indexOf.call(sources, source) < 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
rewatch();
|
||||
return compile();
|
||||
} catch (_error) {
|
||||
removeSource(source, base);
|
||||
return compileJoin();
|
||||
}
|
||||
};
|
||||
compile = function() {
|
||||
@@ -278,119 +312,130 @@
|
||||
});
|
||||
});
|
||||
};
|
||||
try {
|
||||
watcher = fs.watch(source, compile);
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
watchErr(e);
|
||||
}
|
||||
return rewatch = function() {
|
||||
startWatcher = function() {
|
||||
return watcher = fs.watch(source).on('change', compile).on('error', function(err) {
|
||||
if (err.code !== 'EPERM') {
|
||||
throw err;
|
||||
}
|
||||
return removeSource(source, base);
|
||||
});
|
||||
};
|
||||
rewatch = function() {
|
||||
if (watcher != null) {
|
||||
watcher.close();
|
||||
}
|
||||
return watcher = fs.watch(source, compile);
|
||||
return startWatcher();
|
||||
};
|
||||
try {
|
||||
return startWatcher();
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
return watchErr(err);
|
||||
}
|
||||
};
|
||||
|
||||
watchDir = function(source, base) {
|
||||
var e, readdirTimeout, watcher;
|
||||
var err, readdirTimeout, startWatcher, stopWatcher, watcher;
|
||||
watcher = null;
|
||||
readdirTimeout = null;
|
||||
try {
|
||||
return watcher = fs.watch(source, function() {
|
||||
startWatcher = function() {
|
||||
return watcher = fs.watch(source).on('error', function(err) {
|
||||
if (err.code !== 'EPERM') {
|
||||
throw err;
|
||||
}
|
||||
return stopWatcher();
|
||||
}).on('change', function() {
|
||||
clearTimeout(readdirTimeout);
|
||||
return readdirTimeout = wait(25, function() {
|
||||
return fs.readdir(source, function(err, files) {
|
||||
var file, _i, _len, _results;
|
||||
if (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
watcher.close();
|
||||
return unwatchDir(source, base);
|
||||
var err, file, files, _i, _len, _results;
|
||||
try {
|
||||
files = fs.readdirSync(source);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
_results = [];
|
||||
for (_i = 0, _len = files.length; _i < _len; _i++) {
|
||||
file = files[_i];
|
||||
if (!(!hidden(file) && !notSources[file])) {
|
||||
continue;
|
||||
}
|
||||
file = path.join(source, file);
|
||||
if (sources.some(function(s) {
|
||||
return s.indexOf(file) >= 0;
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
sources.push(file);
|
||||
sourceCode.push(null);
|
||||
_results.push(compilePath(file, false, base));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
return stopWatcher();
|
||||
}
|
||||
_results = [];
|
||||
for (_i = 0, _len = files.length; _i < _len; _i++) {
|
||||
file = files[_i];
|
||||
_results.push(compilePath(path.join(source, file), false, base));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
});
|
||||
};
|
||||
stopWatcher = function() {
|
||||
watcher.close();
|
||||
return removeSourceDir(source, base);
|
||||
};
|
||||
watchedDirs[source] = true;
|
||||
try {
|
||||
return startWatcher();
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
if (e.code !== 'ENOENT') {
|
||||
throw e;
|
||||
err = _error;
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
unwatchDir = function(source, base) {
|
||||
var file, prevSources, toRemove, _i, _len;
|
||||
prevSources = sources.slice(0);
|
||||
toRemove = (function() {
|
||||
var _i, _len, _results;
|
||||
_results = [];
|
||||
for (_i = 0, _len = sources.length; _i < _len; _i++) {
|
||||
file = sources[_i];
|
||||
if (file.indexOf(source) >= 0) {
|
||||
_results.push(file);
|
||||
}
|
||||
removeSourceDir = function(source, base) {
|
||||
var file, sourcesChanged, _i, _len;
|
||||
delete watchedDirs[source];
|
||||
sourcesChanged = false;
|
||||
for (_i = 0, _len = sources.length; _i < _len; _i++) {
|
||||
file = sources[_i];
|
||||
if (!(source === path.dirname(file))) {
|
||||
continue;
|
||||
}
|
||||
return _results;
|
||||
})();
|
||||
for (_i = 0, _len = toRemove.length; _i < _len; _i++) {
|
||||
file = toRemove[_i];
|
||||
removeSource(file, base, true);
|
||||
removeSource(file, base);
|
||||
sourcesChanged = true;
|
||||
}
|
||||
if (!sources.some(function(s, i) {
|
||||
return prevSources[i] !== s;
|
||||
})) {
|
||||
return;
|
||||
if (sourcesChanged) {
|
||||
return compileJoin();
|
||||
}
|
||||
return compileJoin();
|
||||
};
|
||||
|
||||
removeSource = function(source, base, removeJs) {
|
||||
var index, jsPath;
|
||||
removeSource = function(source, base) {
|
||||
var index;
|
||||
index = sources.indexOf(source);
|
||||
sources.splice(index, 1);
|
||||
sourceCode.splice(index, 1);
|
||||
if (removeJs && !opts.join) {
|
||||
jsPath = outputPath(source, base);
|
||||
return exists(jsPath, function(itExists) {
|
||||
if (itExists) {
|
||||
return fs.unlink(jsPath, function(err) {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
return timeLog("removed " + source);
|
||||
});
|
||||
}
|
||||
});
|
||||
if (!opts.join) {
|
||||
silentUnlink(outputPath(source, base));
|
||||
silentUnlink(outputPath(source, base, '.map'));
|
||||
return timeLog("removed " + source);
|
||||
}
|
||||
};
|
||||
|
||||
silentUnlink = function(path) {
|
||||
var err, _ref1;
|
||||
try {
|
||||
return fs.unlinkSync(path);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if ((_ref1 = err.code) !== 'ENOENT' && _ref1 !== 'EPERM') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
outputPath = function(source, base, extension) {
|
||||
var baseDir, basename, dir, srcDir;
|
||||
var basename, dir, srcDir;
|
||||
if (extension == null) {
|
||||
extension = ".js";
|
||||
}
|
||||
basename = helpers.baseFileName(source, true, useWinPathSep);
|
||||
srcDir = path.dirname(source);
|
||||
baseDir = base === '.' ? srcDir : srcDir.substring(base.length);
|
||||
dir = opts.output ? path.join(opts.output, baseDir) : srcDir;
|
||||
if (!opts.output) {
|
||||
dir = srcDir;
|
||||
} else if (source === base) {
|
||||
dir = opts.output;
|
||||
} else {
|
||||
dir = path.join(opts.output, path.relative(base, srcDir));
|
||||
}
|
||||
return path.join(dir, basename + extension);
|
||||
};
|
||||
|
||||
@@ -407,7 +452,7 @@
|
||||
js = ' ';
|
||||
}
|
||||
if (generatedSourceMap) {
|
||||
js = "" + js + "\n/*\n//@ sourceMappingURL=" + (helpers.baseFileName(sourceMapPath, false, useWinPathSep)) + "\n*/\n";
|
||||
js = "" + js + "\n//# sourceMappingURL=" + (helpers.baseFileName(sourceMapPath, false, useWinPathSep)) + "\n";
|
||||
}
|
||||
fs.writeFile(jsPath, js, function(err) {
|
||||
if (err) {
|
||||
@@ -425,11 +470,11 @@
|
||||
});
|
||||
}
|
||||
};
|
||||
return exists(jsDir, function(itExists) {
|
||||
return fs.exists(jsDir, function(itExists) {
|
||||
if (itExists) {
|
||||
return compile();
|
||||
} else {
|
||||
return exec("mkdir -p " + jsDir, compile);
|
||||
return mkdirp(jsDir, compile);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -459,17 +504,12 @@
|
||||
};
|
||||
|
||||
parseOptions = function() {
|
||||
var i, o, source, _i, _len;
|
||||
var o;
|
||||
optionParser = new optparse.OptionParser(SWITCHES, BANNER);
|
||||
o = opts = optionParser.parse(process.argv.slice(2));
|
||||
o.compile || (o.compile = !!o.output);
|
||||
o.run = !(o.compile || o.print || o.map);
|
||||
o.print = !!(o.print || (o["eval"] || o.stdio && o.compile));
|
||||
sources = o["arguments"];
|
||||
for (i = _i = 0, _len = sources.length; _i < _len; i = ++_i) {
|
||||
source = sources[i];
|
||||
sourceCode[i] = null;
|
||||
}
|
||||
return o.print = !!(o.print || (o["eval"] || o.stdio && o.compile));
|
||||
};
|
||||
|
||||
compileOptions = function(filename, base) {
|
||||
@@ -478,7 +518,7 @@
|
||||
filename: filename,
|
||||
literate: opts.literate || helpers.isLiterate(filename),
|
||||
bare: opts.bare,
|
||||
header: opts.compile,
|
||||
header: opts.compile && !opts['no-header'],
|
||||
sourceMap: opts.map
|
||||
};
|
||||
if (filename) {
|
||||
@@ -504,15 +544,18 @@
|
||||
};
|
||||
|
||||
forkNode = function() {
|
||||
var args, nodeArgs;
|
||||
var args, nodeArgs, p;
|
||||
nodeArgs = opts.nodejs.split(/\s+/);
|
||||
args = process.argv.slice(1);
|
||||
args.splice(args.indexOf('--nodejs'), 2);
|
||||
return spawn(process.execPath, nodeArgs.concat(args), {
|
||||
p = spawn(process.execPath, nodeArgs.concat(args), {
|
||||
cwd: process.cwd(),
|
||||
env: process.env,
|
||||
customFds: [0, 1, 2]
|
||||
});
|
||||
return p.on('exit', function(code) {
|
||||
return process.exit(code);
|
||||
});
|
||||
};
|
||||
|
||||
usage = function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
Root: [
|
||||
o('', function() {
|
||||
return new Block;
|
||||
}), o('Body'), o('Block TERMINATOR')
|
||||
}), o('Body')
|
||||
],
|
||||
Body: [
|
||||
o('Line', function() {
|
||||
@@ -96,8 +96,7 @@
|
||||
return new Value($1);
|
||||
}), o('ObjAssignable : Expression', function() {
|
||||
return new Assign(LOC(1)(new Value($1)), $3, 'object');
|
||||
}), o('ObjAssignable :\
|
||||
INDENT Expression OUTDENT', function() {
|
||||
}), o('ObjAssignable : INDENT Expression OUTDENT', function() {
|
||||
return new Assign(LOC(1)(new Value($1)), $4, 'object');
|
||||
}), o('Comment')
|
||||
],
|
||||
@@ -149,6 +148,8 @@
|
||||
return new Param($1, null, true);
|
||||
}), o('ParamVar = Expression', function() {
|
||||
return new Param($1, $3);
|
||||
}), o('...', function() {
|
||||
return new Expansion;
|
||||
})
|
||||
],
|
||||
ParamVar: [o('Identifier'), o('ThisProperty'), o('Array'), o('Object')],
|
||||
@@ -328,7 +329,11 @@
|
||||
return $1.concat($4);
|
||||
})
|
||||
],
|
||||
Arg: [o('Expression'), o('Splat')],
|
||||
Arg: [
|
||||
o('Expression'), o('Splat'), o('...', function() {
|
||||
return new Expansion;
|
||||
})
|
||||
],
|
||||
SimpleArgs: [
|
||||
o('Expression'), o('SimpleArgs , Expression', function() {
|
||||
return [].concat($1, $3);
|
||||
@@ -514,9 +519,9 @@
|
||||
type: $1
|
||||
});
|
||||
}), o('IfBlock ELSE IF Expression Block', function() {
|
||||
return $1.addElse(new If($4, $5, {
|
||||
return $1.addElse(LOC(3, 5)(new If($4, $5, {
|
||||
type: $3
|
||||
}));
|
||||
})));
|
||||
})
|
||||
],
|
||||
If: [
|
||||
@@ -537,14 +542,16 @@
|
||||
Operation: [
|
||||
o('UNARY Expression', function() {
|
||||
return new Op($1, $2);
|
||||
}), o('UNARY_MATH Expression', function() {
|
||||
return new Op($1, $2);
|
||||
}), o('- Expression', (function() {
|
||||
return new Op('-', $2);
|
||||
}), {
|
||||
prec: 'UNARY'
|
||||
prec: 'UNARY_MATH'
|
||||
}), o('+ Expression', (function() {
|
||||
return new Op('+', $2);
|
||||
}), {
|
||||
prec: 'UNARY'
|
||||
prec: 'UNARY_MATH'
|
||||
}), o('-- SimpleAssignable', function() {
|
||||
return new Op('--', $2);
|
||||
}), o('++ SimpleAssignable', function() {
|
||||
@@ -561,6 +568,8 @@
|
||||
return new Op('-', $1, $3);
|
||||
}), o('Expression MATH Expression', function() {
|
||||
return new Op($2, $1, $3);
|
||||
}), o('Expression ** Expression', function() {
|
||||
return new Op($2, $1, $3);
|
||||
}), o('Expression SHIFT Expression', function() {
|
||||
return new Op($2, $1, $3);
|
||||
}), o('Expression COMPARE Expression', function() {
|
||||
@@ -573,14 +582,11 @@
|
||||
} else {
|
||||
return new Op($2, $1, $3);
|
||||
}
|
||||
}), o('SimpleAssignable COMPOUND_ASSIGN\
|
||||
Expression', function() {
|
||||
}), o('SimpleAssignable COMPOUND_ASSIGN Expression', function() {
|
||||
return new Assign($1, $3, $2);
|
||||
}), o('SimpleAssignable COMPOUND_ASSIGN\
|
||||
INDENT Expression OUTDENT', function() {
|
||||
}), o('SimpleAssignable COMPOUND_ASSIGN INDENT Expression OUTDENT', function() {
|
||||
return new Assign($1, $4, $2);
|
||||
}), o('SimpleAssignable COMPOUND_ASSIGN TERMINATOR\
|
||||
Expression', function() {
|
||||
}), o('SimpleAssignable COMPOUND_ASSIGN TERMINATOR Expression', function() {
|
||||
return new Assign($1, $4, $2);
|
||||
}), o('SimpleAssignable EXTENDS Expression', function() {
|
||||
return new Extends($1, $3);
|
||||
@@ -588,7 +594,7 @@
|
||||
]
|
||||
};
|
||||
|
||||
operators = [['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['right', 'POST_IF']];
|
||||
operators = [['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['right', '**'], ['right', 'UNARY_MATH'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['left', 'POST_IF']];
|
||||
|
||||
tokens = [];
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var buildLocationData, extend, flatten, last, repeat, _ref;
|
||||
var buildLocationData, extend, flatten, last, repeat, syntaxErrorToString, _ref;
|
||||
|
||||
exports.starts = function(string, literal, start) {
|
||||
return literal === string.substr(start, literal.length);
|
||||
@@ -167,7 +167,7 @@
|
||||
pathSep = useWinPathSep ? /\\|\// : /\//;
|
||||
parts = file.split(pathSep);
|
||||
file = parts[parts.length - 1];
|
||||
if (!stripExt) {
|
||||
if (!(stripExt && file.indexOf('.') >= 0)) {
|
||||
return file;
|
||||
}
|
||||
parts = file.split('.');
|
||||
@@ -188,36 +188,65 @@
|
||||
|
||||
exports.throwSyntaxError = function(message, location) {
|
||||
var error;
|
||||
if (location.last_line == null) {
|
||||
location.last_line = location.first_line;
|
||||
}
|
||||
if (location.last_column == null) {
|
||||
location.last_column = location.first_column;
|
||||
}
|
||||
error = new SyntaxError(message);
|
||||
error.location = location;
|
||||
error.toString = syntaxErrorToString;
|
||||
error.stack = error.toString();
|
||||
throw error;
|
||||
};
|
||||
|
||||
exports.prettyErrorMessage = function(error, fileName, code, useColors) {
|
||||
var codeLine, colorize, end, first_column, first_line, last_column, last_line, marker, message, start, _ref1;
|
||||
if (!error.location) {
|
||||
return error.stack || ("" + error);
|
||||
exports.updateSyntaxError = function(error, code, filename) {
|
||||
if (error.toString === syntaxErrorToString) {
|
||||
error.code || (error.code = code);
|
||||
error.filename || (error.filename = filename);
|
||||
error.stack = error.toString();
|
||||
}
|
||||
_ref1 = error.location, first_line = _ref1.first_line, first_column = _ref1.first_column, last_line = _ref1.last_line, last_column = _ref1.last_column;
|
||||
codeLine = code.split('\n')[first_line];
|
||||
return error;
|
||||
};
|
||||
|
||||
syntaxErrorToString = function() {
|
||||
var codeLine, colorize, colorsEnabled, end, filename, first_column, first_line, last_column, last_line, marker, start, _ref1, _ref2;
|
||||
if (!(this.code && this.location)) {
|
||||
return Error.prototype.toString.call(this);
|
||||
}
|
||||
_ref1 = this.location, first_line = _ref1.first_line, first_column = _ref1.first_column, last_line = _ref1.last_line, last_column = _ref1.last_column;
|
||||
if (last_line == null) {
|
||||
last_line = first_line;
|
||||
}
|
||||
if (last_column == null) {
|
||||
last_column = first_column;
|
||||
}
|
||||
filename = this.filename || '[stdin]';
|
||||
codeLine = this.code.split('\n')[first_line];
|
||||
start = first_column;
|
||||
end = first_line === last_line ? last_column + 1 : codeLine.length;
|
||||
marker = repeat(' ', start) + repeat('^', end - start);
|
||||
if (useColors) {
|
||||
if (typeof process !== "undefined" && process !== null) {
|
||||
colorsEnabled = process.stdout.isTTY && !process.env.NODE_DISABLE_COLORS;
|
||||
}
|
||||
if ((_ref2 = this.colorful) != null ? _ref2 : colorsEnabled) {
|
||||
colorize = function(str) {
|
||||
return "\x1B[1;31m" + str + "\x1B[0m";
|
||||
};
|
||||
codeLine = codeLine.slice(0, start) + colorize(codeLine.slice(start, end)) + codeLine.slice(end);
|
||||
marker = colorize(marker);
|
||||
}
|
||||
message = "" + fileName + ":" + (first_line + 1) + ":" + (first_column + 1) + ": error: " + error.message + "\n" + codeLine + "\n" + marker;
|
||||
return message;
|
||||
return "" + filename + ":" + (first_line + 1) + ":" + (first_column + 1) + ": error: " + this.message + "\n" + codeLine + "\n" + marker;
|
||||
};
|
||||
|
||||
exports.nameWhitespaceCharacter = function(string) {
|
||||
switch (string) {
|
||||
case ' ':
|
||||
return 'space';
|
||||
case '\n':
|
||||
return 'newline';
|
||||
case '\r':
|
||||
return 'carriage return';
|
||||
case '\t':
|
||||
return 'tab';
|
||||
default:
|
||||
return string;
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var key, val, _ref;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, repeat, starts, throwSyntaxError, _ref, _ref1,
|
||||
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, UNARY_MATH, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, repeat, starts, throwSyntaxError, _ref, _ref1,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
_ref = require('./rewriter'), Rewriter = _ref.Rewriter, INVERSES = _ref.INVERSES;
|
||||
@@ -17,6 +17,7 @@
|
||||
}
|
||||
this.literate = opts.literate;
|
||||
this.indent = 0;
|
||||
this.baseIndent = 0;
|
||||
this.indebt = 0;
|
||||
this.outdebt = 0;
|
||||
this.indents = [];
|
||||
@@ -165,30 +166,25 @@
|
||||
};
|
||||
|
||||
Lexer.prototype.stringToken = function() {
|
||||
var match, octalEsc, string;
|
||||
switch (this.chunk.charAt(0)) {
|
||||
var octalEsc, quote, string, trimmed;
|
||||
switch (quote = this.chunk.charAt(0)) {
|
||||
case "'":
|
||||
if (!(match = SIMPLESTR.exec(this.chunk))) {
|
||||
return 0;
|
||||
}
|
||||
string = match[0];
|
||||
this.token('STRING', string.replace(MULTILINER, '\\\n'), 0, string.length);
|
||||
string = SIMPLESTR.exec(this.chunk)[0];
|
||||
break;
|
||||
case '"':
|
||||
if (!(string = this.balancedString(this.chunk, '"'))) {
|
||||
return 0;
|
||||
}
|
||||
if (0 < string.indexOf('#{', 1)) {
|
||||
this.interpolateString(string.slice(1, -1), {
|
||||
strOffset: 1,
|
||||
lexedLength: string.length
|
||||
});
|
||||
} else {
|
||||
this.token('STRING', this.escapeLines(string, 0, string.length));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
string = this.balancedString(this.chunk, '"');
|
||||
}
|
||||
if (!string) {
|
||||
return 0;
|
||||
}
|
||||
trimmed = this.removeNewlines(string.slice(1, -1));
|
||||
if (quote === '"' && 0 < string.indexOf('#{', 1)) {
|
||||
this.interpolateString(trimmed, {
|
||||
strOffset: 1,
|
||||
lexedLength: string.length
|
||||
});
|
||||
} else {
|
||||
this.token('STRING', quote + this.escapeLines(trimmed) + quote, 0, string.length);
|
||||
}
|
||||
if (octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test(string)) {
|
||||
this.error("octal escape sequences " + string + " are not allowed");
|
||||
@@ -248,8 +244,7 @@
|
||||
if (this.chunk.charAt(0) !== '/') {
|
||||
return 0;
|
||||
}
|
||||
if (match = HEREGEX.exec(this.chunk)) {
|
||||
length = this.heregexToken(match);
|
||||
if (length = this.heregexToken()) {
|
||||
return length;
|
||||
}
|
||||
prev = last(this.tokens);
|
||||
@@ -260,21 +255,24 @@
|
||||
return 0;
|
||||
}
|
||||
_ref3 = match, match = _ref3[0], regex = _ref3[1], flags = _ref3[2];
|
||||
if (regex === '//') {
|
||||
return 0;
|
||||
}
|
||||
if (regex.slice(0, 2) === '/*') {
|
||||
this.error('regular expressions cannot begin with `*`');
|
||||
}
|
||||
if (regex === '//') {
|
||||
regex = '/(?:)/';
|
||||
}
|
||||
this.token('REGEX', "" + regex + flags, 0, match.length);
|
||||
return match.length;
|
||||
};
|
||||
|
||||
Lexer.prototype.heregexToken = function(match) {
|
||||
var body, flags, flagsOffset, heregex, plusToken, prev, re, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
|
||||
Lexer.prototype.heregexToken = function() {
|
||||
var body, flags, flagsOffset, heregex, match, plusToken, prev, re, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
|
||||
if (!(match = HEREGEX.exec(this.chunk))) {
|
||||
return 0;
|
||||
}
|
||||
heregex = match[0], body = match[1], flags = match[2];
|
||||
if (0 > body.indexOf('#{')) {
|
||||
re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/');
|
||||
re = this.escapeLines(body.replace(HEREGEX_OMIT, '$1$2').replace(/\//g, '\\/'), true);
|
||||
if (re.match(/^\*/)) {
|
||||
this.error('regular expressions cannot begin with `*`');
|
||||
}
|
||||
@@ -293,7 +291,7 @@
|
||||
if (tag === 'TOKENS') {
|
||||
tokens.push.apply(tokens, value);
|
||||
} else if (tag === 'NEOSTRING') {
|
||||
if (!(value = value.replace(HEREGEX_OMIT, ''))) {
|
||||
if (!(value = value.replace(HEREGEX_OMIT, '$1$2'))) {
|
||||
continue;
|
||||
}
|
||||
value = value.replace(/\\/g, '\\\\');
|
||||
@@ -346,37 +344,48 @@
|
||||
this.suppressNewlines();
|
||||
return indent.length;
|
||||
}
|
||||
if (!this.tokens.length) {
|
||||
this.baseIndent = this.indent = size;
|
||||
return indent.length;
|
||||
}
|
||||
diff = size - this.indent + this.outdebt;
|
||||
this.token('INDENT', diff, indent.length - size, size);
|
||||
this.indents.push(diff);
|
||||
this.ends.push('OUTDENT');
|
||||
this.outdebt = this.indebt = 0;
|
||||
this.indent = size;
|
||||
} else if (size < this.baseIndent) {
|
||||
this.error('missing indentation', indent.length);
|
||||
} else {
|
||||
this.indebt = 0;
|
||||
this.outdentToken(this.indent - size, noNewlines, indent.length);
|
||||
}
|
||||
this.indent = size;
|
||||
return indent.length;
|
||||
};
|
||||
|
||||
Lexer.prototype.outdentToken = function(moveOut, noNewlines, outdentLength) {
|
||||
var dent, len;
|
||||
var decreasedIndent, dent, lastIndent, _ref2;
|
||||
decreasedIndent = this.indent - moveOut;
|
||||
while (moveOut > 0) {
|
||||
len = this.indents.length - 1;
|
||||
if (this.indents[len] === void 0) {
|
||||
lastIndent = this.indents[this.indents.length - 1];
|
||||
if (!lastIndent) {
|
||||
moveOut = 0;
|
||||
} else if (this.indents[len] === this.outdebt) {
|
||||
} else if (lastIndent === this.outdebt) {
|
||||
moveOut -= this.outdebt;
|
||||
this.outdebt = 0;
|
||||
} else if (this.indents[len] < this.outdebt) {
|
||||
this.outdebt -= this.indents[len];
|
||||
moveOut -= this.indents[len];
|
||||
} else if (lastIndent < this.outdebt) {
|
||||
this.outdebt -= lastIndent;
|
||||
moveOut -= lastIndent;
|
||||
} else {
|
||||
dent = this.indents.pop() + this.outdebt;
|
||||
moveOut -= dent;
|
||||
if (outdentLength && (_ref2 = this.chunk[outdentLength], __indexOf.call(INDENTABLE_CLOSERS, _ref2) >= 0)) {
|
||||
decreasedIndent -= dent - moveOut;
|
||||
moveOut = dent;
|
||||
}
|
||||
this.outdebt = 0;
|
||||
this.pair('OUTDENT');
|
||||
this.token('OUTDENT', dent, 0, outdentLength);
|
||||
this.token('OUTDENT', moveOut, 0, outdentLength);
|
||||
moveOut -= dent;
|
||||
}
|
||||
}
|
||||
if (dent) {
|
||||
@@ -388,6 +397,7 @@
|
||||
if (!(this.tag() === 'TERMINATOR' || noNewlines)) {
|
||||
this.token('TERMINATOR', '\n', outdentLength, 0);
|
||||
}
|
||||
this.indent = decreasedIndent;
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -457,6 +467,8 @@
|
||||
tag = 'COMPOUND_ASSIGN';
|
||||
} else if (__indexOf.call(UNARY, value) >= 0) {
|
||||
tag = 'UNARY';
|
||||
} else if (__indexOf.call(UNARY_MATH, value) >= 0) {
|
||||
tag = 'UNARY_MATH';
|
||||
} else if (__indexOf.call(SHIFT, value) >= 0) {
|
||||
tag = 'SHIFT';
|
||||
} else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) {
|
||||
@@ -586,18 +598,14 @@
|
||||
};
|
||||
|
||||
Lexer.prototype.interpolateString = function(str, options) {
|
||||
var column, expr, heredoc, i, inner, interpolated, len, letter, lexedLength, line, locationToken, nested, offsetInChunk, pi, plusToken, popped, regex, rparen, strOffset, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
|
||||
var column, errorToken, expr, heredoc, i, inner, interpolated, len, letter, lexedLength, line, locationToken, nested, offsetInChunk, pi, plusToken, popped, regex, rparen, strOffset, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
heredoc = options.heredoc, regex = options.regex, offsetInChunk = options.offsetInChunk, strOffset = options.strOffset, lexedLength = options.lexedLength;
|
||||
offsetInChunk = offsetInChunk || 0;
|
||||
strOffset = strOffset || 0;
|
||||
lexedLength = lexedLength || str.length;
|
||||
if (heredoc && str.length > 0 && str[0] === '\n') {
|
||||
str = str.slice(1);
|
||||
strOffset++;
|
||||
}
|
||||
offsetInChunk || (offsetInChunk = 0);
|
||||
strOffset || (strOffset = 0);
|
||||
lexedLength || (lexedLength = str.length);
|
||||
tokens = [];
|
||||
pi = 0;
|
||||
i = -1;
|
||||
@@ -612,6 +620,9 @@
|
||||
if (pi < i) {
|
||||
tokens.push(this.makeToken('NEOSTRING', str.slice(pi, i), strOffset + pi));
|
||||
}
|
||||
if (!errorToken) {
|
||||
errorToken = this.makeToken('', 'string interpolation', offsetInChunk + i + 1, 2);
|
||||
}
|
||||
inner = expr.slice(1, -1);
|
||||
if (inner.length) {
|
||||
_ref2 = this.getLineAndColumnFromChunk(strOffset + i + 1), line = _ref2[0], column = _ref2[1];
|
||||
@@ -648,7 +659,7 @@
|
||||
tokens.unshift(this.makeToken('NEOSTRING', '', offsetInChunk));
|
||||
}
|
||||
if (interpolated = tokens.length > 1) {
|
||||
this.token('(', '(', offsetInChunk, 0);
|
||||
this.token('(', '(', offsetInChunk, 0, errorToken);
|
||||
}
|
||||
for (i = _i = 0, _len = tokens.length; _i < _len; i = ++_i) {
|
||||
token = tokens[i];
|
||||
@@ -684,13 +695,12 @@
|
||||
};
|
||||
|
||||
Lexer.prototype.pair = function(tag) {
|
||||
var size, wanted;
|
||||
var wanted;
|
||||
if (tag !== (wanted = last(this.ends))) {
|
||||
if ('OUTDENT' !== wanted) {
|
||||
this.error("unmatched " + tag);
|
||||
}
|
||||
this.indent -= size = last(this.indents);
|
||||
this.outdentToken(size, true);
|
||||
this.outdentToken(last(this.indents), true);
|
||||
return this.pair(tag);
|
||||
}
|
||||
return this.ends.pop();
|
||||
@@ -733,9 +743,12 @@
|
||||
return token;
|
||||
};
|
||||
|
||||
Lexer.prototype.token = function(tag, value, offsetInChunk, length) {
|
||||
Lexer.prototype.token = function(tag, value, offsetInChunk, length, origin) {
|
||||
var token;
|
||||
token = this.makeToken(tag, value, offsetInChunk, length);
|
||||
if (origin) {
|
||||
token.origin = origin;
|
||||
}
|
||||
this.tokens.push(token);
|
||||
return token;
|
||||
};
|
||||
@@ -752,19 +765,34 @@
|
||||
|
||||
Lexer.prototype.unfinished = function() {
|
||||
var _ref2;
|
||||
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
|
||||
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === 'UNARY_MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === '**' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
|
||||
};
|
||||
|
||||
Lexer.prototype.removeNewlines = function(str) {
|
||||
return str.replace(/^\s*\n\s*/, '').replace(/([^\\]|\\\\)\s*\n\s*$/, '$1');
|
||||
};
|
||||
|
||||
Lexer.prototype.escapeLines = function(str, heredoc) {
|
||||
return str.replace(MULTILINER, heredoc ? '\\n' : '');
|
||||
str = str.replace(/\\[^\S\n]*(\n|\\)\s*/g, function(escaped, character) {
|
||||
if (character === '\n') {
|
||||
return '';
|
||||
} else {
|
||||
return escaped;
|
||||
}
|
||||
});
|
||||
if (heredoc) {
|
||||
return str.replace(MULTILINER, '\\n');
|
||||
} else {
|
||||
return str.replace(/\s*\n\s*/g, ' ');
|
||||
}
|
||||
};
|
||||
|
||||
Lexer.prototype.makeString = function(body, quote, heredoc) {
|
||||
if (!body) {
|
||||
return quote + quote;
|
||||
}
|
||||
body = body.replace(/\\([\s\S])/g, function(match, contents) {
|
||||
if (contents === '\n' || contents === quote) {
|
||||
body = body.replace(RegExp("\\\\(" + quote + "|\\\\)", "g"), function(match, contents) {
|
||||
if (contents === quote) {
|
||||
return contents;
|
||||
} else {
|
||||
return match;
|
||||
@@ -774,10 +802,15 @@
|
||||
return quote + this.escapeLines(body, heredoc) + quote;
|
||||
};
|
||||
|
||||
Lexer.prototype.error = function(message) {
|
||||
Lexer.prototype.error = function(message, offset) {
|
||||
var first_column, first_line, _ref2;
|
||||
if (offset == null) {
|
||||
offset = 0;
|
||||
}
|
||||
_ref2 = this.getLineAndColumnFromChunk(offset), first_line = _ref2[0], first_column = _ref2[1];
|
||||
return throwSyntaxError(message, {
|
||||
first_line: this.chunkLine,
|
||||
first_column: this.chunkColumn
|
||||
first_line: first_line,
|
||||
first_column: first_column
|
||||
});
|
||||
};
|
||||
|
||||
@@ -828,27 +861,27 @@
|
||||
|
||||
NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
|
||||
|
||||
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;
|
||||
HEREDOC = /^("""|''')((?:\\[\s\S]|[^\\])*?)(?:\n[^\n\S]*)?\1/;
|
||||
|
||||
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?(\.|::)|\.{2,3})/;
|
||||
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?(\.|::)|\.{2,3})/;
|
||||
|
||||
WHITESPACE = /^[^\n\S]+/;
|
||||
|
||||
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/;
|
||||
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/;
|
||||
|
||||
CODE = /^[-=]>/;
|
||||
|
||||
MULTI_DENT = /^(?:\n[^\n\S]*)+/;
|
||||
|
||||
SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/;
|
||||
SIMPLESTR = /^'[^\\']*(?:\\[\s\S][^\\']*)*'/;
|
||||
|
||||
JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/;
|
||||
|
||||
REGEX = /^(\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/)([imgy]{0,4})(?!\w)/;
|
||||
|
||||
HEREGEX = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?!\w)/;
|
||||
HEREGEX = /^\/{3}((?:\\?[\s\S])+?)\/{3}([imgy]{0,4})(?!\w)/;
|
||||
|
||||
HEREGEX_OMIT = /\s+(?:#.*)?/g;
|
||||
HEREGEX_OMIT = /((?:\\\\)+)|\\(\s|\/)|\s+(?:#.*)?/g;
|
||||
|
||||
MULTILINER = /\n/g;
|
||||
|
||||
@@ -860,9 +893,11 @@
|
||||
|
||||
TRAILING_SPACES = /\s+$/;
|
||||
|
||||
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
|
||||
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=', '**=', '//=', '%%='];
|
||||
|
||||
UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO'];
|
||||
UNARY = ['NEW', 'TYPEOF', 'DELETE', 'DO'];
|
||||
|
||||
UNARY_MATH = ['!', '~'];
|
||||
|
||||
LOGIC = ['&&', '||', '&', '|', '^'];
|
||||
|
||||
@@ -870,7 +905,7 @@
|
||||
|
||||
COMPARE = ['==', '!=', '<', '>', '<=', '>='];
|
||||
|
||||
MATH = ['*', '/', '%'];
|
||||
MATH = ['*', '/', '%', '//', '%%'];
|
||||
|
||||
RELATION = ['IN', 'OF', 'INSTANCEOF'];
|
||||
|
||||
@@ -886,4 +921,6 @@
|
||||
|
||||
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
|
||||
|
||||
INDENTABLE_CLOSERS = [')', '}', ']'];
|
||||
|
||||
}).call(this);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat;
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
66
lib/coffee-script/register.js
Normal file
66
lib/coffee-script/register.js
Normal file
@@ -0,0 +1,66 @@
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var CoffeeScript, Module, binary, child_process, ext, findExtension, fork, helpers, loadFile, path, _i, _len, _ref;
|
||||
|
||||
CoffeeScript = require('./coffee-script');
|
||||
|
||||
child_process = require('child_process');
|
||||
|
||||
helpers = require('./helpers');
|
||||
|
||||
path = require('path');
|
||||
|
||||
loadFile = function(module, filename) {
|
||||
var answer;
|
||||
answer = CoffeeScript._compileFile(filename, false);
|
||||
return module._compile(answer, filename);
|
||||
};
|
||||
|
||||
if (require.extensions) {
|
||||
_ref = CoffeeScript.FILE_EXTENSIONS;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
ext = _ref[_i];
|
||||
require.extensions[ext] = loadFile;
|
||||
}
|
||||
Module = require('module');
|
||||
findExtension = function(filename) {
|
||||
var curExtension, extensions;
|
||||
extensions = path.basename(filename).split('.');
|
||||
if (extensions[0] === '') {
|
||||
extensions.shift();
|
||||
}
|
||||
while (extensions.shift()) {
|
||||
curExtension = '.' + extensions.join('.');
|
||||
if (Module._extensions[curExtension]) {
|
||||
return curExtension;
|
||||
}
|
||||
}
|
||||
return '.js';
|
||||
};
|
||||
Module.prototype.load = function(filename) {
|
||||
var extension;
|
||||
this.filename = filename;
|
||||
this.paths = Module._nodeModulePaths(path.dirname(filename));
|
||||
extension = findExtension(filename);
|
||||
Module._extensions[extension](this, filename);
|
||||
return this.loaded = true;
|
||||
};
|
||||
}
|
||||
|
||||
if (child_process) {
|
||||
fork = child_process.fork;
|
||||
binary = require.resolve('../../bin/coffee');
|
||||
child_process.fork = function(path, args, options) {
|
||||
if (helpers.isCoffee(path)) {
|
||||
if (!Array.isArray(args)) {
|
||||
options = args || {};
|
||||
args = [];
|
||||
}
|
||||
args = [path].concat(args);
|
||||
path = binary;
|
||||
}
|
||||
return fork(path, args, options);
|
||||
};
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
@@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var CoffeeScript, addHistory, addMultilineHandler, fs, merge, nodeREPL, path, prettyErrorMessage, replDefaults, vm, _ref;
|
||||
var CoffeeScript, addHistory, addMultilineHandler, fs, merge, nodeREPL, path, replDefaults, updateSyntaxError, vm, _ref;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
CoffeeScript = require('./coffee-script');
|
||||
|
||||
_ref = require('./helpers'), merge = _ref.merge, prettyErrorMessage = _ref.prettyErrorMessage;
|
||||
_ref = require('./helpers'), merge = _ref.merge, updateSyntaxError = _ref.updateSyntaxError;
|
||||
|
||||
replDefaults = {
|
||||
prompt: 'coffee> ',
|
||||
historyFile: process.env.HOME ? path.join(process.env.HOME, '.coffee_history') : void 0,
|
||||
historyMaxInputSize: 10240,
|
||||
"eval": function(input, context, filename, cb) {
|
||||
var Assign, Block, Literal, Value, ast, err, js, _ref1;
|
||||
var Assign, Block, Literal, Value, ast, err, js, result, _ref1;
|
||||
input = input.replace(/\uFF00/g, '\n');
|
||||
input = input.replace(/^\(([\s\S]*)\n\)$/m, '$1');
|
||||
_ref1 = require('./nodes'), Block = _ref1.Block, Assign = _ref1.Assign, Value = _ref1.Value, Literal = _ref1.Literal;
|
||||
@@ -30,10 +30,12 @@
|
||||
bare: true,
|
||||
locals: Object.keys(context)
|
||||
});
|
||||
return cb(null, vm.runInContext(js, context, filename));
|
||||
result = context === global ? vm.runInThisContext(js, filename) : vm.runInContext(js, context, filename);
|
||||
return cb(null, result);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
return cb(prettyErrorMessage(err, filename, input, true));
|
||||
updateSyntaxError(err, input);
|
||||
return cb(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -143,6 +145,8 @@
|
||||
console.warn("Node 0.8.0+ required for CoffeeScript REPL");
|
||||
process.exit(1);
|
||||
}
|
||||
CoffeeScript.register();
|
||||
process.argv = ['coffee'].concat(process.argv.slice(2));
|
||||
opts = merge(replDefaults, opts);
|
||||
repl = nodeREPL.start(opts);
|
||||
repl.on('exit', function() {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref,
|
||||
var BALANCED_PAIRS, CALL_CLOSERS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
__slice = [].slice;
|
||||
|
||||
generate = function(tag, value) {
|
||||
generate = function(tag, value, origin) {
|
||||
var tok;
|
||||
tok = [tag, value];
|
||||
tok.generated = true;
|
||||
if (origin) {
|
||||
tok.origin = origin;
|
||||
}
|
||||
return tok;
|
||||
};
|
||||
|
||||
@@ -17,10 +20,9 @@
|
||||
Rewriter.prototype.rewrite = function(tokens) {
|
||||
this.tokens = tokens;
|
||||
this.removeLeadingNewlines();
|
||||
this.removeMidExpressionNewlines();
|
||||
this.closeOpenCalls();
|
||||
this.closeOpenIndexes();
|
||||
this.addImplicitIndentation();
|
||||
this.normalizeLines();
|
||||
this.tagPostfixConditionals();
|
||||
this.addImplicitBracesAndParens();
|
||||
this.addLocationDataToGeneratedTokens();
|
||||
@@ -72,17 +74,6 @@
|
||||
}
|
||||
};
|
||||
|
||||
Rewriter.prototype.removeMidExpressionNewlines = function() {
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var _ref;
|
||||
if (!(token[0] === 'TERMINATOR' && (_ref = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref) >= 0))) {
|
||||
return 1;
|
||||
}
|
||||
tokens.splice(i, 1);
|
||||
return 0;
|
||||
});
|
||||
};
|
||||
|
||||
Rewriter.prototype.closeOpenCalls = function() {
|
||||
var action, condition;
|
||||
condition = function(token, i) {
|
||||
@@ -161,9 +152,9 @@
|
||||
var stack;
|
||||
stack = [];
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, nextTag, offset, prevTag, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
|
||||
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, newLine, nextTag, offset, prevTag, prevToken, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
|
||||
tag = token[0];
|
||||
prevTag = (i > 0 ? tokens[i - 1] : [])[0];
|
||||
prevTag = (prevToken = i > 0 ? tokens[i - 1] : [])[0];
|
||||
nextTag = (i < tokens.length - 1 ? tokens[i + 1] : [])[0];
|
||||
stackTop = function() {
|
||||
return stack[stack.length - 1];
|
||||
@@ -219,7 +210,7 @@
|
||||
ours: true
|
||||
}
|
||||
]);
|
||||
tokens.splice(idx, 0, generate('{', generate(new String('{'))));
|
||||
tokens.splice(idx, 0, generate('{', generate(new String('{')), token));
|
||||
if (j == null) {
|
||||
return i += 1;
|
||||
}
|
||||
@@ -227,7 +218,7 @@
|
||||
endImplicitObject = function(j) {
|
||||
j = j != null ? j : i;
|
||||
stack.pop();
|
||||
tokens.splice(j, 0, generate('}', '}'));
|
||||
tokens.splice(j, 0, generate('}', '}', token));
|
||||
return i += 1;
|
||||
};
|
||||
if (inImplicitCall() && (tag === 'IF' || tag === 'TRY' || tag === 'FINALLY' || tag === 'CATCH' || tag === 'CLASS' || tag === 'SWITCH')) {
|
||||
@@ -287,6 +278,7 @@
|
||||
while (this.tag(s - 2) === 'HERECOMMENT') {
|
||||
s -= 2;
|
||||
}
|
||||
this.insideForDeclaration = nextTag === 'FOR';
|
||||
startsLine = s === 0 || (_ref2 = this.tag(s - 1), __indexOf.call(LINEBREAKS, _ref2) >= 0) || tokens[s - 1].newLine;
|
||||
if (stackTop()) {
|
||||
_ref3 = stackTop(), stackTag = _ref3[0], stackIdx = _ref3[1];
|
||||
@@ -297,20 +289,17 @@
|
||||
startImplicitObject(s, !!startsLine);
|
||||
return forward(2);
|
||||
}
|
||||
if (prevTag === 'OUTDENT' && inImplicitCall() && (tag === '.' || tag === '?.' || tag === '::' || tag === '?::')) {
|
||||
endImplicitCall();
|
||||
return forward(1);
|
||||
}
|
||||
if (inImplicitObject() && __indexOf.call(LINEBREAKS, tag) >= 0) {
|
||||
stackTop()[2].sameLine = false;
|
||||
}
|
||||
if (__indexOf.call(IMPLICIT_END, tag) >= 0) {
|
||||
newLine = prevTag === 'OUTDENT' || prevToken.newLine;
|
||||
if (__indexOf.call(IMPLICIT_END, tag) >= 0 || __indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) {
|
||||
while (inImplicit()) {
|
||||
_ref4 = stackTop(), stackTag = _ref4[0], stackIdx = _ref4[1], (_ref5 = _ref4[2], sameLine = _ref5.sameLine, startsLine = _ref5.startsLine);
|
||||
if (inImplicitCall() && prevTag !== ',') {
|
||||
endImplicitCall();
|
||||
} else if (inImplicitObject() && sameLine && !startsLine) {
|
||||
endImplicitObject();
|
||||
} else if (inImplicitObject() && !this.insideForDeclaration && sameLine && tag !== 'TERMINATOR' && prevTag !== ':' && endImplicitObject()) {
|
||||
|
||||
} else if (inImplicitObject() && tag === 'TERMINATOR' && prevTag !== ',' && !(startsLine && this.looksObjectish(i + 1))) {
|
||||
endImplicitObject();
|
||||
} else {
|
||||
@@ -318,7 +307,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
|
||||
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && !this.insideForDeclaration && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
|
||||
offset = nextTag === 'OUTDENT' ? 1 : 0;
|
||||
while (inImplicitObject()) {
|
||||
endImplicitObject(i + offset);
|
||||
@@ -354,30 +343,32 @@
|
||||
});
|
||||
};
|
||||
|
||||
Rewriter.prototype.addImplicitIndentation = function() {
|
||||
Rewriter.prototype.normalizeLines = function() {
|
||||
var action, condition, indent, outdent, starter;
|
||||
starter = indent = outdent = null;
|
||||
condition = function(token, i) {
|
||||
var _ref, _ref1;
|
||||
return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'ELSE' && starter !== 'THEN') && !(((_ref1 = token[0]) === 'CATCH' || _ref1 === 'FINALLY') && (starter === '->' || starter === '=>'));
|
||||
var _ref, _ref1, _ref2, _ref3;
|
||||
return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'TERMINATOR' && (_ref1 = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref1) >= 0)) && !(token[0] === 'ELSE' && starter !== 'THEN') && !(((_ref2 = token[0]) === 'CATCH' || _ref2 === 'FINALLY') && (starter === '->' || starter === '=>')) || (_ref3 = token[0], __indexOf.call(CALL_CLOSERS, _ref3) >= 0) && this.tokens[i - 1].newLine;
|
||||
};
|
||||
action = function(token, i) {
|
||||
return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent);
|
||||
};
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var j, tag, _i, _ref, _ref1;
|
||||
var j, tag, _i, _ref, _ref1, _ref2;
|
||||
tag = token[0];
|
||||
if (tag === 'TERMINATOR' && this.tag(i + 1) === 'THEN') {
|
||||
tokens.splice(i, 1);
|
||||
return 0;
|
||||
}
|
||||
if (tag === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
|
||||
tokens.splice.apply(tokens, [i, 0].concat(__slice.call(this.indentation())));
|
||||
return 2;
|
||||
if (tag === 'TERMINATOR') {
|
||||
if (this.tag(i + 1) === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
|
||||
tokens.splice.apply(tokens, [i, 1].concat(__slice.call(this.indentation())));
|
||||
return 1;
|
||||
}
|
||||
if (_ref = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref) >= 0) {
|
||||
tokens.splice(i, 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (tag === 'CATCH') {
|
||||
for (j = _i = 1; _i <= 2; j = ++_i) {
|
||||
if (!((_ref = this.tag(i + j)) === 'OUTDENT' || _ref === 'TERMINATOR' || _ref === 'FINALLY')) {
|
||||
if (!((_ref1 = this.tag(i + j)) === 'OUTDENT' || _ref1 === 'TERMINATOR' || _ref1 === 'FINALLY')) {
|
||||
continue;
|
||||
}
|
||||
tokens.splice.apply(tokens, [i + j, 0].concat(__slice.call(this.indentation())));
|
||||
@@ -386,7 +377,7 @@
|
||||
}
|
||||
if (__indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) {
|
||||
starter = tag;
|
||||
_ref1 = this.indentation(true), indent = _ref1[0], outdent = _ref1[1];
|
||||
_ref2 = this.indentation(tokens[i]), indent = _ref2[0], outdent = _ref2[1];
|
||||
if (starter === 'THEN') {
|
||||
indent.fromThen = true;
|
||||
}
|
||||
@@ -425,17 +416,14 @@
|
||||
});
|
||||
};
|
||||
|
||||
Rewriter.prototype.indentation = function(implicit) {
|
||||
Rewriter.prototype.indentation = function(origin) {
|
||||
var indent, outdent;
|
||||
if (implicit == null) {
|
||||
implicit = false;
|
||||
}
|
||||
indent = ['INDENT', 2];
|
||||
outdent = ['OUTDENT', 2];
|
||||
if (implicit) {
|
||||
if (origin) {
|
||||
indent.generated = outdent.generated = true;
|
||||
}
|
||||
if (!implicit) {
|
||||
indent.origin = outdent.origin = origin;
|
||||
} else {
|
||||
indent.explicit = outdent.explicit = true;
|
||||
}
|
||||
return [indent, outdent];
|
||||
@@ -466,11 +454,11 @@
|
||||
EXPRESSION_END.push(INVERSES[left] = rite);
|
||||
}
|
||||
|
||||
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);
|
||||
EXPRESSION_CLOSE = ['CATCH', 'THEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);
|
||||
|
||||
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
|
||||
|
||||
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'];
|
||||
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'];
|
||||
|
||||
IMPLICIT_UNSPACED_CALL = ['+', '-'];
|
||||
|
||||
@@ -482,4 +470,6 @@
|
||||
|
||||
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT'];
|
||||
|
||||
CALL_CLOSERS = ['.', '?.', '::', '?::'];
|
||||
|
||||
}).call(this);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var Scope, extend, last, _ref;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
// Generated by CoffeeScript 1.7.0
|
||||
(function() {
|
||||
var LineMap, SourceMap;
|
||||
|
||||
|
||||
51
package.json
51
package.json
@@ -1,35 +1,42 @@
|
||||
{
|
||||
"name": "coffee-script",
|
||||
"description": "Unfancy JavaScript",
|
||||
"keywords": ["javascript", "language", "coffeescript", "compiler"],
|
||||
"author": "Jeremy Ashkenas",
|
||||
"version": "1.6.3",
|
||||
"licenses": [{
|
||||
"type": "MIT",
|
||||
"url": "https://raw.github.com/jashkenas/coffee-script/master/LICENSE"
|
||||
}],
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
"name": "coffee-script",
|
||||
"description": "Unfancy JavaScript",
|
||||
"keywords": [
|
||||
"javascript",
|
||||
"language",
|
||||
"coffeescript",
|
||||
"compiler"
|
||||
],
|
||||
"author": "Jeremy Ashkenas",
|
||||
"version": "1.7.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
"directories" : {
|
||||
"lib" : "./lib/coffee-script"
|
||||
"directories": {
|
||||
"lib": "./lib/coffee-script"
|
||||
},
|
||||
"main" : "./lib/coffee-script/coffee-script",
|
||||
"bin": {
|
||||
"coffee": "./bin/coffee",
|
||||
"cake": "./bin/cake"
|
||||
"main": "./lib/coffee-script/coffee-script",
|
||||
"bin": {
|
||||
"coffee": "./bin/coffee",
|
||||
"cake": "./bin/cake"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node ./bin/cake test"
|
||||
},
|
||||
"homepage": "http://coffeescript.org",
|
||||
"bugs": "https://github.com/jashkenas/coffee-script/issues",
|
||||
"repository": {
|
||||
"homepage": "http://coffeescript.org",
|
||||
"bugs": "https://github.com/jashkenas/coffee-script/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/jashkenas/coffee-script.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"uglify-js": "~2.2",
|
||||
"jison": ">=0.2.0"
|
||||
"uglify-js": "~2.2",
|
||||
"jison": ">=0.2.0",
|
||||
"highlight.js": "~8.0.0",
|
||||
"underscore": "~1.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"mkdirp": "~0.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
1
register.js
Normal file
1
register.js
Normal file
@@ -0,0 +1 @@
|
||||
require('./lib/coffee-script/register');
|
||||
@@ -29,10 +29,10 @@ if btoa? and JSON? and unescape? and encodeURIComponent?
|
||||
options.sourceMap = true
|
||||
options.inline = true
|
||||
{js, v3SourceMap} = CoffeeScript.compile code, options
|
||||
"#{js}\n//@ sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//@ sourceURL=coffeescript"
|
||||
"#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=coffeescript"
|
||||
|
||||
# Load a remote script from the current domain via XHR.
|
||||
CoffeeScript.load = (url, callback, options = {}) ->
|
||||
CoffeeScript.load = (url, callback, options = {}, hold = false) ->
|
||||
options.sourceFiles = [url]
|
||||
xhr = if window.ActiveXObject
|
||||
new window.ActiveXObject('Microsoft.XMLHTTP')
|
||||
@@ -43,10 +43,11 @@ CoffeeScript.load = (url, callback, options = {}) ->
|
||||
xhr.onreadystatechange = ->
|
||||
if xhr.readyState is 4
|
||||
if xhr.status in [0, 200]
|
||||
CoffeeScript.run xhr.responseText, options
|
||||
param = [xhr.responseText, options]
|
||||
CoffeeScript.run param... unless hold
|
||||
else
|
||||
throw new Error "Could not load #{url}"
|
||||
callback() if callback
|
||||
callback param if callback
|
||||
xhr.send null
|
||||
|
||||
# Activate CoffeeScript in the browser by having it compile and evaluate
|
||||
@@ -57,19 +58,29 @@ runScripts = ->
|
||||
coffeetypes = ['text/coffeescript', 'text/literate-coffeescript']
|
||||
coffees = (s for s in scripts when s.type in coffeetypes)
|
||||
index = 0
|
||||
length = coffees.length
|
||||
do execute = ->
|
||||
script = coffees[index++]
|
||||
mediatype = script?.type
|
||||
if mediatype in coffeetypes
|
||||
options = {literate: mediatype is 'text/literate-coffeescript'}
|
||||
|
||||
execute = ->
|
||||
param = coffees[index]
|
||||
if param instanceof Array
|
||||
CoffeeScript.run param...
|
||||
index++
|
||||
execute()
|
||||
|
||||
for script, i in coffees
|
||||
do (script, i) ->
|
||||
options = literate: script.type is coffeetypes[1]
|
||||
if script.src
|
||||
CoffeeScript.load script.src, execute, options
|
||||
CoffeeScript.load script.src,
|
||||
(param) ->
|
||||
coffees[i] = param
|
||||
execute()
|
||||
options
|
||||
true
|
||||
else
|
||||
options.sourceFiles = ['embedded']
|
||||
CoffeeScript.run script.innerHTML, options
|
||||
execute()
|
||||
null
|
||||
coffees[i] = [script.innerHTML, options]
|
||||
|
||||
execute()
|
||||
|
||||
# Listen for window load, both in decent browsers and in IE.
|
||||
if window.addEventListener
|
||||
|
||||
@@ -13,8 +13,6 @@ helpers = require './helpers'
|
||||
optparse = require './optparse'
|
||||
CoffeeScript = require './coffee-script'
|
||||
|
||||
existsSync = fs.existsSync or path.existsSync
|
||||
|
||||
# Keep track of the list of defined tasks, the accepted options, and so on.
|
||||
tasks = {}
|
||||
options = {}
|
||||
@@ -81,7 +79,7 @@ missingTask = (task) -> fatalError "No such task: #{task}"
|
||||
# When `cake` is invoked, search in the current and all parent directories
|
||||
# to find the relevant Cakefile.
|
||||
cakefileDirectory = (dir) ->
|
||||
return dir if existsSync path.join dir, 'Cakefile'
|
||||
return dir if fs.existsSync path.join dir, 'Cakefile'
|
||||
parent = path.normalize path.join dir, '..'
|
||||
return cakefileDirectory parent unless parent is dir
|
||||
throw new Error "Cakefile not found in #{process.cwd()}"
|
||||
|
||||
@@ -6,18 +6,28 @@
|
||||
fs = require 'fs'
|
||||
vm = require 'vm'
|
||||
path = require 'path'
|
||||
child_process = require 'child_process'
|
||||
{Lexer} = require './lexer'
|
||||
{parser} = require './parser'
|
||||
helpers = require './helpers'
|
||||
SourceMap = require './sourcemap'
|
||||
|
||||
# The current CoffeeScript version number.
|
||||
exports.VERSION = '1.6.3'
|
||||
exports.VERSION = '1.7.0'
|
||||
|
||||
exports.FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md']
|
||||
|
||||
# Expose helpers for testing.
|
||||
exports.helpers = helpers
|
||||
|
||||
# Function wrapper to add source file information to SyntaxErrors thrown by the
|
||||
# lexer/parser/compiler.
|
||||
withPrettyErrors = (fn) ->
|
||||
(code, options = {}) ->
|
||||
try
|
||||
fn.call @, code, options
|
||||
catch err
|
||||
throw helpers.updateSyntaxError err, code, options.filename
|
||||
|
||||
# Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
|
||||
#
|
||||
# If `options.sourceMap` is specified, then `options.filename` must also be specified. All
|
||||
@@ -27,8 +37,9 @@ exports.helpers = helpers
|
||||
# in which case this returns a `{js, v3SourceMap, sourceMap}`
|
||||
# object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for doing programatic
|
||||
# lookups.
|
||||
exports.compile = compile = (code, options = {}) ->
|
||||
{merge} = helpers
|
||||
exports.compile = compile = withPrettyErrors (code, options) ->
|
||||
{merge, extend} = helpers
|
||||
options = extend {}, options
|
||||
|
||||
if options.sourceMap
|
||||
map = new SourceMap
|
||||
@@ -50,7 +61,10 @@ exports.compile = compile = (code, options = {}) ->
|
||||
{noReplace: true})
|
||||
newLines = helpers.count fragment.code, "\n"
|
||||
currentLine += newLines
|
||||
currentColumn = fragment.code.length - (if newLines then fragment.code.lastIndexOf "\n" else 0)
|
||||
if newLines
|
||||
currentColumn = fragment.code.length - (fragment.code.lastIndexOf("\n") + 1)
|
||||
else
|
||||
currentColumn += fragment.code.length
|
||||
|
||||
# Copy the code from each fragment into the final JavaScript.
|
||||
js += fragment.code
|
||||
@@ -68,13 +82,13 @@ exports.compile = compile = (code, options = {}) ->
|
||||
js
|
||||
|
||||
# Tokenize a string of CoffeeScript code, and return the array of tokens.
|
||||
exports.tokens = (code, options) ->
|
||||
exports.tokens = withPrettyErrors (code, options) ->
|
||||
lexer.tokenize code, options
|
||||
|
||||
# Parse a string of CoffeeScript code or an array of lexed tokens, and
|
||||
# return the AST. You can then compile it by calling `.compile()` on the root,
|
||||
# or traverse it by using `.traverseChildren()` with a callback.
|
||||
exports.nodes = (source, options) ->
|
||||
exports.nodes = withPrettyErrors (source, options) ->
|
||||
if typeof source is 'string'
|
||||
parser.parse lexer.tokenize source, options
|
||||
else
|
||||
@@ -84,7 +98,7 @@ exports.nodes = (source, options) ->
|
||||
# setting `__filename`, `__dirname`, and relative `require()`.
|
||||
exports.run = (code, options = {}) ->
|
||||
mainModule = require.main
|
||||
options.sourceMap ?= true
|
||||
|
||||
# Set the filename.
|
||||
mainModule.filename = process.argv[1] =
|
||||
if options.filename then fs.realpathSync(options.filename) else '.'
|
||||
@@ -93,18 +107,18 @@ exports.run = (code, options = {}) ->
|
||||
mainModule.moduleCache and= {}
|
||||
|
||||
# Assign paths for node_modules loading
|
||||
mainModule.paths = require('module')._nodeModulePaths path.dirname fs.realpathSync options.filename or '.'
|
||||
dir = if options.fileName
|
||||
path.dirname fs.realpathSync options.filename
|
||||
else
|
||||
fs.realpathSync '.'
|
||||
mainModule.paths = require('module')._nodeModulePaths dir
|
||||
|
||||
# Compile.
|
||||
if not helpers.isCoffee(mainModule.filename) or require.extensions
|
||||
answer = compile(code, options)
|
||||
# Attach sourceMap object to sourceMaps[options.filename] so that
|
||||
# it is accessible by Error.prepareStackTrace.
|
||||
do patchStackTrace
|
||||
sourceMaps[mainModule.filename] = answer.sourceMap
|
||||
mainModule._compile answer.js, mainModule.filename
|
||||
else
|
||||
mainModule._compile code, mainModule.filename
|
||||
answer = compile code, options
|
||||
code = answer.js ? answer
|
||||
|
||||
mainModule._compile code, mainModule.filename
|
||||
|
||||
# Compile and evaluate a string of CoffeeScript (in a Node.js-like environment).
|
||||
# The CoffeeScript REPL uses this to run the input.
|
||||
@@ -142,54 +156,21 @@ exports.eval = (code, options = {}) ->
|
||||
else
|
||||
vm.runInContext js, sandbox
|
||||
|
||||
# Load and run a CoffeeScript file for Node, stripping any `BOM`s.
|
||||
loadFile = (module, filename) ->
|
||||
exports.register = -> require './register'
|
||||
|
||||
exports._compileFile = (filename, sourceMap = no) ->
|
||||
raw = fs.readFileSync filename, 'utf8'
|
||||
stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw
|
||||
answer = compile(stripped, {filename, sourceMap: true, literate: helpers.isLiterate filename})
|
||||
sourceMaps[filename] = answer.sourceMap
|
||||
module._compile answer.js, filename
|
||||
|
||||
# If the installed version of Node supports `require.extensions`, register
|
||||
# CoffeeScript as an extension.
|
||||
if require.extensions
|
||||
for ext in ['.coffee', '.litcoffee', '.coffee.md']
|
||||
require.extensions[ext] = loadFile
|
||||
try
|
||||
answer = compile(stripped, {filename, sourceMap, literate: helpers.isLiterate filename})
|
||||
catch err
|
||||
# As the filename and code of a dynamically loaded file will be different
|
||||
# from the original file compiled with CoffeeScript.run, add that
|
||||
# information to error so it can be pretty-printed later.
|
||||
throw helpers.updateSyntaxError err, stripped, filename
|
||||
|
||||
# Patch Node's module loader to be able to handle mult-dot extensions.
|
||||
# This is a horrible thing that should not be required. Perhaps, one day,
|
||||
# when a truly benevolent dictator comes to rule over the Republik of Node,
|
||||
# it won't be.
|
||||
Module = require 'module'
|
||||
|
||||
findExtension = (filename) ->
|
||||
extensions = path.basename(filename).split '.'
|
||||
# Remove the initial dot from dotfiles.
|
||||
extensions.shift() if extensions[0] is ''
|
||||
# Start with the longest possible extension and work our way shortwards.
|
||||
while extensions.shift()
|
||||
curExtension = '.' + extensions.join '.'
|
||||
return curExtension if Module._extensions[curExtension]
|
||||
'.js'
|
||||
|
||||
Module::load = (filename) ->
|
||||
@filename = filename
|
||||
@paths = Module._nodeModulePaths path.dirname filename
|
||||
extension = findExtension filename
|
||||
Module._extensions[extension](this, filename)
|
||||
@loaded = true
|
||||
|
||||
# If we're on Node, patch `child_process.fork` so that Coffee scripts are able
|
||||
# to fork both CoffeeScript files, and JavaScript files, directly.
|
||||
if child_process
|
||||
{fork} = child_process
|
||||
child_process.fork = (path, args = [], options = {}) ->
|
||||
execPath = if helpers.isCoffee(path) then 'coffee' else null
|
||||
if not Array.isArray args
|
||||
args = []
|
||||
options = args or {}
|
||||
options.execPath or= execPath
|
||||
fork path, args, options
|
||||
answer
|
||||
|
||||
# Instantiate a Lexer for our use here.
|
||||
lexer = new Lexer
|
||||
@@ -202,6 +183,7 @@ parser.lexer =
|
||||
token = @tokens[@pos++]
|
||||
if token
|
||||
[tag, @yytext, @yylloc] = token
|
||||
@errorToken = token.origin or token
|
||||
@yylineno = @yylloc.first_line
|
||||
else
|
||||
tag = ''
|
||||
@@ -211,52 +193,29 @@ parser.lexer =
|
||||
@pos = 0
|
||||
upcomingInput: ->
|
||||
""
|
||||
|
||||
# Make all the AST nodes visible to the parser.
|
||||
parser.yy = require './nodes'
|
||||
|
||||
# Override Jison's default error handling function.
|
||||
parser.yy.parseError = (message, {token}) ->
|
||||
# Disregard Jison's message, it contains redundant line numer information.
|
||||
message = "unexpected #{if token is 1 then 'end of input' else token}"
|
||||
# Disregard the token, we take its value directly from the lexer in case
|
||||
# the error is caused by a generated token which might refer to its origin.
|
||||
{errorToken, tokens} = parser.lexer
|
||||
[errorTag, errorText, errorLoc] = errorToken
|
||||
|
||||
errorText = if errorToken is tokens[tokens.length - 1]
|
||||
'end of input'
|
||||
else if errorTag in ['INDENT', 'OUTDENT']
|
||||
'indentation'
|
||||
else
|
||||
helpers.nameWhitespaceCharacter errorText
|
||||
|
||||
# The second argument has a `loc` property, which should have the location
|
||||
# data for this token. Unfortunately, Jison seems to send an outdated `loc`
|
||||
# (from the previous token), so we take the location information directly
|
||||
# from the lexer.
|
||||
helpers.throwSyntaxError message, parser.lexer.yylloc
|
||||
|
||||
# Based on [michaelficarra/CoffeeScriptRedux](http://goo.gl/ZTx1p)
|
||||
# NodeJS / V8 have no support for transforming positions in stack traces using
|
||||
# sourceMap, so we must monkey-patch Error to display CoffeeScript source
|
||||
# positions.
|
||||
|
||||
patched = false
|
||||
|
||||
# Map of filenames -> sourceMap object.
|
||||
sourceMaps = {}
|
||||
|
||||
patchStackTrace = ->
|
||||
return if patched
|
||||
patched = true
|
||||
mainModule = require.main
|
||||
|
||||
# (Assigning to a property of the Module object in the normal module cache is
|
||||
# unsuitable, because node deletes those objects from the cache if an
|
||||
# exception is thrown in the module body.)
|
||||
|
||||
Error.prepareStackTrace = (err, stack) ->
|
||||
sourceFiles = {}
|
||||
|
||||
getSourceMapping = (filename, line, column) ->
|
||||
sourceMap = sourceMaps[filename]
|
||||
answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap
|
||||
if answer then [answer[0] + 1, answer[1] + 1] else null
|
||||
|
||||
frames = for frame in stack
|
||||
break if frame.getFunction() is exports.run
|
||||
" at #{formatSourcePosition frame, getSourceMapping}"
|
||||
|
||||
"#{err.name}: #{err.message ? ''}\n#{frames.join '\n'}\n"
|
||||
helpers.throwSyntaxError "unexpected #{errorText}", errorLoc
|
||||
|
||||
# Based on http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js
|
||||
# Modified to handle sourceMap
|
||||
@@ -282,11 +241,10 @@ formatSourcePosition = (frame, getSourceMapping) ->
|
||||
source = getSourceMapping fileName, line, column
|
||||
fileLocation =
|
||||
if source
|
||||
"#{fileName}:#{source[0]}:#{source[1]}, <js>:#{line}:#{column}"
|
||||
"#{fileName}:#{source[0]}:#{source[1]}"
|
||||
else
|
||||
"#{fileName}:#{line}:#{column}"
|
||||
|
||||
|
||||
functionName = frame.getFunctionName()
|
||||
isConstructor = frame.isConstructor()
|
||||
isMethodCall = not (frame.isToplevel() or isConstructor)
|
||||
@@ -312,3 +270,29 @@ formatSourcePosition = (frame, getSourceMapping) ->
|
||||
else
|
||||
fileLocation
|
||||
|
||||
# Map of filenames -> sourceMap object.
|
||||
sourceMaps = {}
|
||||
|
||||
# Generates the source map for a coffee file and stores it in the local cache variable.
|
||||
getSourceMap = (filename) ->
|
||||
return sourceMaps[filename] if sourceMaps[filename]
|
||||
return unless path?.extname(filename) in exports.FILE_EXTENSIONS
|
||||
answer = exports._compileFile filename, true
|
||||
sourceMaps[filename] = answer.sourceMap
|
||||
|
||||
# Based on [michaelficarra/CoffeeScriptRedux](http://goo.gl/ZTx1p)
|
||||
# NodeJS / V8 have no support for transforming positions in stack traces using
|
||||
# sourceMap, so we must monkey-patch Error to display CoffeeScript source
|
||||
# positions.
|
||||
Error.prepareStackTrace = (err, stack) ->
|
||||
getSourceMapping = (filename, line, column) ->
|
||||
sourceMap = getSourceMap filename
|
||||
answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap
|
||||
if answer then [answer[0] + 1, answer[1] + 1] else null
|
||||
|
||||
frames = for frame in stack
|
||||
break if frame.getFunction() is exports.run
|
||||
" at #{formatSourcePosition frame, getSourceMapping}"
|
||||
|
||||
"#{err.name}: #{err.message ? ''}\n#{frames.join '\n'}\n"
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ path = require 'path'
|
||||
helpers = require './helpers'
|
||||
optparse = require './optparse'
|
||||
CoffeeScript = require './coffee-script'
|
||||
mkdirp = require 'mkdirp'
|
||||
{spawn, exec} = require 'child_process'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
exists = fs.exists or path.exists
|
||||
useWinPathSep = path.sep is '\\'
|
||||
|
||||
# Allow CoffeeScript to emit Node.js events.
|
||||
@@ -24,7 +24,7 @@ printWarn = (line) -> process.stderr.write line + '\n'
|
||||
|
||||
hidden = (file) -> /^\.|~$/.test file
|
||||
|
||||
# The help banner that is printed when `coffee` is called without arguments.
|
||||
# The help banner that is printed in conjunction with `-h`/`--help`.
|
||||
BANNER = '''
|
||||
Usage: coffee [options] path/to/script.coffee -- [args]
|
||||
|
||||
@@ -42,6 +42,7 @@ SWITCHES = [
|
||||
['-m', '--map', 'generate source map and save as .map files']
|
||||
['-n', '--nodes', 'print out the parse tree that the parser produces']
|
||||
[ '--nodejs [ARGS]', 'pass options directly to the "node" binary']
|
||||
[ '--no-header', 'suppress the "Generated by" header']
|
||||
['-o', '--output [DIR]', 'set the output directory for compiled JavaScript']
|
||||
['-p', '--print', 'print out the compiled JavaScript']
|
||||
['-s', '--stdio', 'listen for and compile scripts over stdio']
|
||||
@@ -56,7 +57,7 @@ opts = {}
|
||||
sources = []
|
||||
sourceCode = []
|
||||
notSources = {}
|
||||
watchers = {}
|
||||
watchedDirs = {}
|
||||
optionParser = null
|
||||
|
||||
# Run `coffee` by parsing passed options and determining what action to take.
|
||||
@@ -64,64 +65,94 @@ optionParser = null
|
||||
# `--` will be passed verbatim to your script as arguments in `process.argv`
|
||||
exports.run = ->
|
||||
parseOptions()
|
||||
return forkNode() if opts.nodejs
|
||||
return usage() if opts.help
|
||||
return version() if opts.version
|
||||
return require('./repl').start() if opts.interactive
|
||||
if opts.watch and not fs.watch
|
||||
return printWarn "The --watch feature depends on Node v0.6.0+. You are running #{process.version}."
|
||||
return compileStdio() if opts.stdio
|
||||
return compileScript null, sources[0] if opts.eval
|
||||
return require('./repl').start() unless sources.length
|
||||
literals = if opts.run then sources.splice 1 else []
|
||||
# Make the REPL *CLI* use the global context so as to (a) be consistent with the
|
||||
# `node` REPL CLI and, therefore, (b) make packages that modify native prototypes
|
||||
# (such as 'colors' and 'sugar') work as expected.
|
||||
replCliOpts = useGlobal: yes
|
||||
return forkNode() if opts.nodejs
|
||||
return usage() if opts.help
|
||||
return version() if opts.version
|
||||
return require('./repl').start(replCliOpts) if opts.interactive
|
||||
return compileStdio() if opts.stdio
|
||||
return compileScript null, opts.arguments[0] if opts.eval
|
||||
return require('./repl').start(replCliOpts) unless opts.arguments.length
|
||||
literals = if opts.run then opts.arguments.splice 1 else []
|
||||
process.argv = process.argv[0..1].concat literals
|
||||
process.argv[0] = 'coffee'
|
||||
for source in sources
|
||||
compilePath source, yes, path.normalize source
|
||||
|
||||
opts.output = path.resolve opts.output if opts.output
|
||||
opts.join = path.resolve opts.join if opts.join
|
||||
for source in opts.arguments
|
||||
source = path.resolve source
|
||||
compilePath source, yes, source
|
||||
|
||||
# Compile a path, which could be a script or a directory. If a directory
|
||||
# is passed, recursively compile all '.coffee', '.litcoffee', and '.coffee.md'
|
||||
# extension source files in it and all subdirectories.
|
||||
compilePath = (source, topLevel, base) ->
|
||||
fs.stat source, (err, stats) ->
|
||||
throw err if err and err.code isnt 'ENOENT'
|
||||
if err?.code is 'ENOENT'
|
||||
return if source in sources or
|
||||
watchedDirs[source] or
|
||||
not topLevel and (notSources[source] or hidden source)
|
||||
try
|
||||
stats = fs.statSync source
|
||||
catch err
|
||||
if err.code is 'ENOENT'
|
||||
console.error "File not found: #{source}"
|
||||
process.exit 1
|
||||
if stats.isDirectory() and path.dirname(source) isnt 'node_modules'
|
||||
watchDir source, base if opts.watch
|
||||
fs.readdir source, (err, files) ->
|
||||
throw err if err and err.code isnt 'ENOENT'
|
||||
return if err?.code is 'ENOENT'
|
||||
index = sources.indexOf source
|
||||
files = files.filter (file) -> not hidden file
|
||||
sources[index..index] = (path.join source, file for file in files)
|
||||
sourceCode[index..index] = files.map -> null
|
||||
files.forEach (file) ->
|
||||
compilePath (path.join source, file), no, base
|
||||
else if topLevel or helpers.isCoffee source
|
||||
watch source, base if opts.watch
|
||||
fs.readFile source, (err, code) ->
|
||||
throw err if err and err.code isnt 'ENOENT'
|
||||
return if err?.code is 'ENOENT'
|
||||
compileScript(source, code.toString(), base)
|
||||
else
|
||||
throw err
|
||||
if stats.isDirectory()
|
||||
if path.basename(source) is 'node_modules'
|
||||
notSources[source] = yes
|
||||
removeSource source, base
|
||||
return
|
||||
if opts.run
|
||||
compilePath findDirectoryIndex(source), topLevel, base
|
||||
return
|
||||
watchDir source, base if opts.watch
|
||||
try
|
||||
files = fs.readdirSync source
|
||||
catch err
|
||||
if err.code is 'ENOENT' then return else throw err
|
||||
for file in files
|
||||
compilePath (path.join source, file), no, base
|
||||
else if topLevel or helpers.isCoffee source
|
||||
sources.push source
|
||||
sourceCode.push null
|
||||
delete notSources[source]
|
||||
watch source, base if opts.watch
|
||||
try
|
||||
code = fs.readFileSync source
|
||||
catch err
|
||||
if err.code is 'ENOENT' then return else throw err
|
||||
compileScript(source, code.toString(), base)
|
||||
else
|
||||
notSources[source] = yes
|
||||
|
||||
findDirectoryIndex = (source) ->
|
||||
for ext in CoffeeScript.FILE_EXTENSIONS
|
||||
index = path.join source, "index#{ext}"
|
||||
try
|
||||
return index if (fs.statSync index).isFile()
|
||||
catch err
|
||||
throw err unless err.code is 'ENOENT'
|
||||
console.error "Missing index.coffee or index.litcoffee in #{source}"
|
||||
process.exit 1
|
||||
|
||||
# Compile a single source script, containing the given code, according to the
|
||||
# requested options. If evaluating the script directly sets `__filename`,
|
||||
# `__dirname` and `module.filename` to be correct relative to the script's path.
|
||||
compileScript = (file, input, base=null) ->
|
||||
compileScript = (file, input, base = null) ->
|
||||
o = opts
|
||||
options = compileOptions file, base
|
||||
try
|
||||
t = task = {file, input, options}
|
||||
CoffeeScript.emit 'compile', task
|
||||
if o.tokens then printTokens CoffeeScript.tokens t.input, t.options
|
||||
else if o.nodes then printLine CoffeeScript.nodes(t.input, t.options).toString().trim()
|
||||
else if o.run then CoffeeScript.run t.input, t.options
|
||||
if o.tokens
|
||||
printTokens CoffeeScript.tokens t.input, t.options
|
||||
else if o.nodes
|
||||
printLine CoffeeScript.nodes(t.input, t.options).toString().trim()
|
||||
else if o.run
|
||||
CoffeeScript.register()
|
||||
CoffeeScript.run t.input, t.options
|
||||
else if o.join and t.file isnt o.join
|
||||
t.input = helpers.invertLiterate t.input if helpers.isLiterate file
|
||||
sourceCode[sources.indexOf(t.file)] = t.input
|
||||
@@ -141,8 +172,7 @@ compileScript = (file, input, base=null) ->
|
||||
catch err
|
||||
CoffeeScript.emit 'failure', err, task
|
||||
return if CoffeeScript.listeners('failure').length
|
||||
useColors = process.stdout.isTTY and not process.env.NODE_DISABLE_COLORS
|
||||
message = helpers.prettyErrorMessage err, file or '[stdin]', input, useColors
|
||||
message = err.stack or "#{err}"
|
||||
if o.watch
|
||||
printLine message + '\x07'
|
||||
else
|
||||
@@ -173,92 +203,116 @@ compileJoin = ->
|
||||
# time the file is updated. May be used in combination with other options,
|
||||
# such as `--print`.
|
||||
watch = (source, base) ->
|
||||
|
||||
prevStats = null
|
||||
watcher = null
|
||||
prevStats = null
|
||||
compileTimeout = null
|
||||
|
||||
watchErr = (e) ->
|
||||
if e.code is 'ENOENT'
|
||||
return if sources.indexOf(source) is -1
|
||||
try
|
||||
rewatch()
|
||||
compile()
|
||||
catch e
|
||||
removeSource source, base, yes
|
||||
compileJoin()
|
||||
else throw e
|
||||
watchErr = (err) ->
|
||||
throw err unless err.code is 'ENOENT'
|
||||
return unless source in sources
|
||||
try
|
||||
rewatch()
|
||||
compile()
|
||||
catch
|
||||
removeSource source, base
|
||||
compileJoin()
|
||||
|
||||
compile = ->
|
||||
clearTimeout compileTimeout
|
||||
compileTimeout = wait 25, ->
|
||||
fs.stat source, (err, stats) ->
|
||||
return watchErr err if err
|
||||
return rewatch() if prevStats and stats.size is prevStats.size and
|
||||
stats.mtime.getTime() is prevStats.mtime.getTime()
|
||||
return rewatch() if prevStats and
|
||||
stats.size is prevStats.size and
|
||||
stats.mtime.getTime() is prevStats.mtime.getTime()
|
||||
prevStats = stats
|
||||
fs.readFile source, (err, code) ->
|
||||
return watchErr err if err
|
||||
compileScript(source, code.toString(), base)
|
||||
rewatch()
|
||||
|
||||
try
|
||||
watcher = fs.watch source, compile
|
||||
catch e
|
||||
watchErr e
|
||||
startWatcher = ->
|
||||
watcher = fs.watch source
|
||||
.on 'change', compile
|
||||
.on 'error', (err) ->
|
||||
throw err unless err.code is 'EPERM'
|
||||
removeSource source, base
|
||||
|
||||
rewatch = ->
|
||||
watcher?.close()
|
||||
watcher = fs.watch source, compile
|
||||
startWatcher()
|
||||
|
||||
try
|
||||
startWatcher()
|
||||
catch err
|
||||
watchErr err
|
||||
|
||||
# Watch a directory of files for new additions.
|
||||
watchDir = (source, base) ->
|
||||
watcher = null
|
||||
readdirTimeout = null
|
||||
try
|
||||
watcher = fs.watch source, ->
|
||||
|
||||
startWatcher = ->
|
||||
watcher = fs.watch source
|
||||
.on 'error', (err) ->
|
||||
throw err unless err.code is 'EPERM'
|
||||
stopWatcher()
|
||||
.on 'change', ->
|
||||
clearTimeout readdirTimeout
|
||||
readdirTimeout = wait 25, ->
|
||||
fs.readdir source, (err, files) ->
|
||||
if err
|
||||
throw err unless err.code is 'ENOENT'
|
||||
watcher.close()
|
||||
return unwatchDir source, base
|
||||
for file in files when not hidden(file) and not notSources[file]
|
||||
file = path.join source, file
|
||||
continue if sources.some (s) -> s.indexOf(file) >= 0
|
||||
sources.push file
|
||||
sourceCode.push null
|
||||
compilePath file, no, base
|
||||
catch e
|
||||
throw e unless e.code is 'ENOENT'
|
||||
try
|
||||
files = fs.readdirSync source
|
||||
catch err
|
||||
throw err unless err.code is 'ENOENT'
|
||||
return stopWatcher()
|
||||
for file in files
|
||||
compilePath (path.join source, file), no, base
|
||||
|
||||
unwatchDir = (source, base) ->
|
||||
prevSources = sources[..]
|
||||
toRemove = (file for file in sources when file.indexOf(source) >= 0)
|
||||
removeSource file, base, yes for file in toRemove
|
||||
return unless sources.some (s, i) -> prevSources[i] isnt s
|
||||
compileJoin()
|
||||
stopWatcher = ->
|
||||
watcher.close()
|
||||
removeSourceDir source, base
|
||||
|
||||
watchedDirs[source] = yes
|
||||
try
|
||||
startWatcher()
|
||||
catch err
|
||||
throw err unless err.code is 'ENOENT'
|
||||
|
||||
removeSourceDir = (source, base) ->
|
||||
delete watchedDirs[source]
|
||||
sourcesChanged = no
|
||||
for file in sources when source is path.dirname file
|
||||
removeSource file, base
|
||||
sourcesChanged = yes
|
||||
compileJoin() if sourcesChanged
|
||||
|
||||
# Remove a file from our source list, and source code cache. Optionally remove
|
||||
# the compiled JS version as well.
|
||||
removeSource = (source, base, removeJs) ->
|
||||
removeSource = (source, base) ->
|
||||
index = sources.indexOf source
|
||||
sources.splice index, 1
|
||||
sourceCode.splice index, 1
|
||||
if removeJs and not opts.join
|
||||
jsPath = outputPath source, base
|
||||
exists jsPath, (itExists) ->
|
||||
if itExists
|
||||
fs.unlink jsPath, (err) ->
|
||||
throw err if err and err.code isnt 'ENOENT'
|
||||
timeLog "removed #{source}"
|
||||
unless opts.join
|
||||
silentUnlink outputPath source, base
|
||||
silentUnlink outputPath source, base, '.map'
|
||||
timeLog "removed #{source}"
|
||||
|
||||
silentUnlink = (path) ->
|
||||
try
|
||||
fs.unlinkSync path
|
||||
catch err
|
||||
throw err unless err.code in ['ENOENT', 'EPERM']
|
||||
|
||||
# Get the corresponding output JavaScript path for a source file.
|
||||
outputPath = (source, base, extension=".js") ->
|
||||
basename = helpers.baseFileName source, yes, useWinPathSep
|
||||
srcDir = path.dirname source
|
||||
baseDir = if base is '.' then srcDir else srcDir.substring base.length
|
||||
dir = if opts.output then path.join opts.output, baseDir else srcDir
|
||||
if not opts.output
|
||||
dir = srcDir
|
||||
else if source is base
|
||||
dir = opts.output
|
||||
else
|
||||
dir = path.join opts.output, path.relative base, srcDir
|
||||
path.join dir, basename + extension
|
||||
|
||||
# Write out a JavaScript source file with the compiled code. By default, files
|
||||
@@ -273,7 +327,7 @@ writeJs = (base, sourcePath, js, jsPath, generatedSourceMap = null) ->
|
||||
compile = ->
|
||||
if opts.compile
|
||||
js = ' ' if js.length <= 0
|
||||
if generatedSourceMap then js = "#{js}\n/*\n//@ sourceMappingURL=#{helpers.baseFileName sourceMapPath, no, useWinPathSep}\n*/\n"
|
||||
if generatedSourceMap then js = "#{js}\n//# sourceMappingURL=#{helpers.baseFileName sourceMapPath, no, useWinPathSep}\n"
|
||||
fs.writeFile jsPath, js, (err) ->
|
||||
if err
|
||||
printLine err.message
|
||||
@@ -283,8 +337,8 @@ writeJs = (base, sourcePath, js, jsPath, generatedSourceMap = null) ->
|
||||
fs.writeFile sourceMapPath, generatedSourceMap, (err) ->
|
||||
if err
|
||||
printLine "Could not write source map: #{err.message}"
|
||||
exists jsDir, (itExists) ->
|
||||
if itExists then compile() else exec "mkdir -p #{jsDir}", compile
|
||||
fs.exists jsDir, (itExists) ->
|
||||
if itExists then compile() else mkdirp jsDir, compile
|
||||
|
||||
# Convenience for cleaner setTimeouts.
|
||||
wait = (milliseconds, func) -> setTimeout func, milliseconds
|
||||
@@ -309,9 +363,6 @@ parseOptions = ->
|
||||
o.compile or= !!o.output
|
||||
o.run = not (o.compile or o.print or o.map)
|
||||
o.print = !! (o.print or (o.eval or o.stdio and o.compile))
|
||||
sources = o.arguments
|
||||
sourceCode[i] = null for source, i in sources
|
||||
return
|
||||
|
||||
# The compile-time options to pass to the CoffeeScript compiler.
|
||||
compileOptions = (filename, base) ->
|
||||
@@ -319,7 +370,7 @@ compileOptions = (filename, base) ->
|
||||
filename
|
||||
literate: opts.literate or helpers.isLiterate(filename)
|
||||
bare: opts.bare
|
||||
header: opts.compile
|
||||
header: opts.compile and not opts['no-header']
|
||||
sourceMap: opts.map
|
||||
}
|
||||
if filename
|
||||
@@ -346,10 +397,11 @@ forkNode = ->
|
||||
nodeArgs = opts.nodejs.split /\s+/
|
||||
args = process.argv[1..]
|
||||
args.splice args.indexOf('--nodejs'), 2
|
||||
spawn process.execPath, nodeArgs.concat(args),
|
||||
p = spawn process.execPath, nodeArgs.concat(args),
|
||||
cwd: process.cwd()
|
||||
env: process.env
|
||||
customFds: [0, 1, 2]
|
||||
p.on 'exit', (code) -> process.exit code
|
||||
|
||||
# Print the `--help` usage message and exit. Deprecated switches are not
|
||||
# shown.
|
||||
|
||||
@@ -74,7 +74,6 @@ grammar =
|
||||
Root: [
|
||||
o '', -> new Block
|
||||
o 'Body'
|
||||
o 'Block TERMINATOR'
|
||||
]
|
||||
|
||||
# Any list of statements and expressions, separated by line breaks or semicolons.
|
||||
@@ -218,9 +217,10 @@ grammar =
|
||||
o 'ParamVar', -> new Param $1
|
||||
o 'ParamVar ...', -> new Param $1, null, on
|
||||
o 'ParamVar = Expression', -> new Param $1, $3
|
||||
o '...', -> new Expansion
|
||||
]
|
||||
|
||||
# Function Parameters
|
||||
# Function Parameters
|
||||
ParamVar: [
|
||||
o 'Identifier'
|
||||
o 'ThisProperty'
|
||||
@@ -379,6 +379,7 @@ grammar =
|
||||
Arg: [
|
||||
o 'Expression'
|
||||
o 'Splat'
|
||||
o '...', -> new Expansion
|
||||
]
|
||||
|
||||
# Just simple, comma-separated, required arguments (no fancy syntax). We need
|
||||
@@ -512,7 +513,7 @@ grammar =
|
||||
# ambiguity.
|
||||
IfBlock: [
|
||||
o 'IF Expression Block', -> new If $2, $3, type: $1
|
||||
o 'IfBlock ELSE IF Expression Block', -> $1.addElse new If $4, $5, type: $3
|
||||
o 'IfBlock ELSE IF Expression Block', -> $1.addElse LOC(3,5) new If $4, $5, type: $3
|
||||
]
|
||||
|
||||
# The full complement of *if* expressions, including postfix one-liner
|
||||
@@ -532,8 +533,9 @@ grammar =
|
||||
# rules are necessary.
|
||||
Operation: [
|
||||
o 'UNARY Expression', -> new Op $1 , $2
|
||||
o '- Expression', (-> new Op '-', $2), prec: 'UNARY'
|
||||
o '+ Expression', (-> new Op '+', $2), prec: 'UNARY'
|
||||
o 'UNARY_MATH Expression', -> new Op $1 , $2
|
||||
o '- Expression', (-> new Op '-', $2), prec: 'UNARY_MATH'
|
||||
o '+ Expression', (-> new Op '+', $2), prec: 'UNARY_MATH'
|
||||
|
||||
o '-- SimpleAssignable', -> new Op '--', $2
|
||||
o '++ SimpleAssignable', -> new Op '++', $2
|
||||
@@ -547,6 +549,7 @@ grammar =
|
||||
o 'Expression - Expression', -> new Op '-' , $1, $3
|
||||
|
||||
o 'Expression MATH Expression', -> new Op $2, $1, $3
|
||||
o 'Expression ** Expression', -> new Op $2, $1, $3
|
||||
o 'Expression SHIFT Expression', -> new Op $2, $1, $3
|
||||
o 'Expression COMPARE Expression', -> new Op $2, $1, $3
|
||||
o 'Expression LOGIC Expression', -> new Op $2, $1, $3
|
||||
@@ -583,6 +586,8 @@ operators = [
|
||||
['nonassoc', '++', '--']
|
||||
['left', '?']
|
||||
['right', 'UNARY']
|
||||
['right', '**']
|
||||
['right', 'UNARY_MATH']
|
||||
['left', 'MATH']
|
||||
['left', '+', '-']
|
||||
['left', 'SHIFT']
|
||||
@@ -593,7 +598,7 @@ operators = [
|
||||
['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
|
||||
['right', 'FORIN', 'FOROF', 'BY', 'WHEN']
|
||||
['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
|
||||
['right', 'POST_IF']
|
||||
['left', 'POST_IF']
|
||||
]
|
||||
|
||||
# Wrapping Up
|
||||
|
||||
@@ -99,30 +99,30 @@ buildLocationData = (first, last) ->
|
||||
# object is an AST node, updates that object's locationData.
|
||||
# The object is returned either way.
|
||||
exports.addLocationDataFn = (first, last) ->
|
||||
(obj) ->
|
||||
if ((typeof obj) is 'object') and (!!obj['updateLocationDataIfMissing'])
|
||||
obj.updateLocationDataIfMissing buildLocationData(first, last)
|
||||
(obj) ->
|
||||
if ((typeof obj) is 'object') and (!!obj['updateLocationDataIfMissing'])
|
||||
obj.updateLocationDataIfMissing buildLocationData(first, last)
|
||||
|
||||
return obj
|
||||
return obj
|
||||
|
||||
# Convert jison location data to a string.
|
||||
# `obj` can be a token, or a locationData.
|
||||
exports.locationDataToString = (obj) ->
|
||||
if ("2" of obj) and ("first_line" of obj[2]) then locationData = obj[2]
|
||||
else if "first_line" of obj then locationData = obj
|
||||
if ("2" of obj) and ("first_line" of obj[2]) then locationData = obj[2]
|
||||
else if "first_line" of obj then locationData = obj
|
||||
|
||||
if locationData
|
||||
"#{locationData.first_line + 1}:#{locationData.first_column + 1}-" +
|
||||
"#{locationData.last_line + 1}:#{locationData.last_column + 1}"
|
||||
else
|
||||
"No location data"
|
||||
if locationData
|
||||
"#{locationData.first_line + 1}:#{locationData.first_column + 1}-" +
|
||||
"#{locationData.last_line + 1}:#{locationData.last_column + 1}"
|
||||
else
|
||||
"No location data"
|
||||
|
||||
# A `.coffee.md` compatible version of `basename`, that returns the file sans-extension.
|
||||
exports.baseFileName = (file, stripExt = no, useWinPathSep = no) ->
|
||||
pathSep = if useWinPathSep then /\\|\// else /\//
|
||||
parts = file.split(pathSep)
|
||||
file = parts[parts.length - 1]
|
||||
return file unless stripExt
|
||||
return file unless stripExt and file.indexOf('.') >= 0
|
||||
parts = file.split('.')
|
||||
parts.pop()
|
||||
parts.pop() if parts[parts.length - 1] is 'coffee' and parts.length > 1
|
||||
@@ -134,40 +134,65 @@ exports.isCoffee = (file) -> /\.((lit)?coffee|coffee\.md)$/.test file
|
||||
# Determine if a filename represents a Literate CoffeeScript file.
|
||||
exports.isLiterate = (file) -> /\.(litcoffee|coffee\.md)$/.test file
|
||||
|
||||
# Throws a SyntaxError with a source file location data attached to it in a
|
||||
# property called `location`.
|
||||
# Throws a SyntaxError from a given location.
|
||||
# The error's `toString` will return an error message following the "standard"
|
||||
# format <filename>:<line>:<col>: <message> plus the line with the error and a
|
||||
# marker showing where the error is.
|
||||
exports.throwSyntaxError = (message, location) ->
|
||||
location.last_line ?= location.first_line
|
||||
location.last_column ?= location.first_column
|
||||
error = new SyntaxError message
|
||||
error.location = location
|
||||
error.toString = syntaxErrorToString
|
||||
|
||||
# Instead of showing the compiler's stacktrace, show our custom error message
|
||||
# (this is useful when the error bubbles up in Node.js applications that
|
||||
# compile CoffeeScript for example).
|
||||
error.stack = error.toString()
|
||||
|
||||
throw error
|
||||
|
||||
# Creates a nice error message like, following the "standard" format
|
||||
# <filename>:<line>:<col>: <message> plus the line with the error and a marker
|
||||
# showing where the error is.
|
||||
exports.prettyErrorMessage = (error, fileName, code, useColors) ->
|
||||
return error.stack or "#{error}" unless error.location
|
||||
# Update a compiler SyntaxError with source code information if it didn't have
|
||||
# it already.
|
||||
exports.updateSyntaxError = (error, code, filename) ->
|
||||
# Avoid screwing up the `stack` property of other errors (i.e. possible bugs).
|
||||
if error.toString is syntaxErrorToString
|
||||
error.code or= code
|
||||
error.filename or= filename
|
||||
error.stack = error.toString()
|
||||
error
|
||||
|
||||
{first_line, first_column, last_line, last_column} = error.location
|
||||
codeLine = code.split('\n')[first_line]
|
||||
syntaxErrorToString = ->
|
||||
return Error::toString.call @ unless @code and @location
|
||||
|
||||
{first_line, first_column, last_line, last_column} = @location
|
||||
last_line ?= first_line
|
||||
last_column ?= first_column
|
||||
|
||||
filename = @filename or '[stdin]'
|
||||
codeLine = @code.split('\n')[first_line]
|
||||
start = first_column
|
||||
# Show only the first line on multi-line errors.
|
||||
end = if first_line is last_line then last_column + 1 else codeLine.length
|
||||
marker = repeat(' ', start) + repeat('^', end - start)
|
||||
|
||||
if useColors
|
||||
colorize = (str) -> "\x1B[1;31m#{str}\x1B[0m"
|
||||
# Check to see if we're running on a color-enabled TTY.
|
||||
if process?
|
||||
colorsEnabled = process.stdout.isTTY and not process.env.NODE_DISABLE_COLORS
|
||||
|
||||
if @colorful ? colorsEnabled
|
||||
colorize = (str) -> "\x1B[1;31m#{str}\x1B[0m"
|
||||
codeLine = codeLine[...start] + colorize(codeLine[start...end]) + codeLine[end..]
|
||||
marker = colorize marker
|
||||
marker = colorize marker
|
||||
|
||||
message = """
|
||||
#{fileName}:#{first_line + 1}:#{first_column + 1}: error: #{error.message}
|
||||
#{codeLine}
|
||||
#{marker}
|
||||
"""
|
||||
"""
|
||||
#{filename}:#{first_line + 1}:#{first_column + 1}: error: #{@message}
|
||||
#{codeLine}
|
||||
#{marker}
|
||||
"""
|
||||
|
||||
# Uncomment to add stacktrace.
|
||||
#message += "\n#{error.stack}"
|
||||
|
||||
message
|
||||
exports.nameWhitespaceCharacter = (string) ->
|
||||
switch string
|
||||
when ' ' then 'space'
|
||||
when '\n' then 'newline'
|
||||
when '\r' then 'carriage return'
|
||||
when '\t' then 'tab'
|
||||
else string
|
||||
|
||||
173
src/lexer.coffee
173
src/lexer.coffee
@@ -35,18 +35,19 @@ exports.Lexer = class Lexer
|
||||
# Before returning the token stream, run it through the [Rewriter](rewriter.html)
|
||||
# unless explicitly asked not to.
|
||||
tokenize: (code, opts = {}) ->
|
||||
@literate = opts.literate # Are we lexing literate CoffeeScript?
|
||||
@indent = 0 # The current indentation level.
|
||||
@indebt = 0 # The over-indentation at the current level.
|
||||
@outdebt = 0 # The under-outdentation at the current level.
|
||||
@indents = [] # The stack of all current indentation levels.
|
||||
@ends = [] # The stack for pairing up tokens.
|
||||
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, location data]`.
|
||||
@literate = opts.literate # Are we lexing literate CoffeeScript?
|
||||
@indent = 0 # The current indentation level.
|
||||
@baseIndent = 0 # The overall minimum indentation level
|
||||
@indebt = 0 # The over-indentation at the current level.
|
||||
@outdebt = 0 # The under-outdentation at the current level.
|
||||
@indents = [] # The stack of all current indentation levels.
|
||||
@ends = [] # The stack for pairing up tokens.
|
||||
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, location data]`.
|
||||
|
||||
@chunkLine =
|
||||
opts.line or 0 # The start line for the current @chunk.
|
||||
opts.line or 0 # The start line for the current @chunk.
|
||||
@chunkColumn =
|
||||
opts.column or 0 # The start column of the current @chunk.
|
||||
opts.column or 0 # The start column of the current @chunk.
|
||||
code = @clean code # The stripped, cleaned original source code.
|
||||
|
||||
# At every position, run through this list of attempted matches,
|
||||
@@ -83,8 +84,8 @@ exports.Lexer = class Lexer
|
||||
code = code.slice(1) if code.charCodeAt(0) is BOM
|
||||
code = code.replace(/\r/g, '').replace TRAILING_SPACES, ''
|
||||
if WHITESPACE.test code
|
||||
code = "\n#{code}"
|
||||
@chunkLine--
|
||||
code = "\n#{code}"
|
||||
@chunkLine--
|
||||
code = invertLiterate code if @literate
|
||||
code
|
||||
|
||||
@@ -185,19 +186,15 @@ exports.Lexer = class Lexer
|
||||
# Matches strings, including multi-line strings. Ensures that quotation marks
|
||||
# are balanced within the string's contents, and within nested interpolations.
|
||||
stringToken: ->
|
||||
switch @chunk.charAt 0
|
||||
when "'"
|
||||
return 0 unless match = SIMPLESTR.exec @chunk
|
||||
string = match[0]
|
||||
@token 'STRING', string.replace(MULTILINER, '\\\n'), 0, string.length
|
||||
when '"'
|
||||
return 0 unless string = @balancedString @chunk, '"'
|
||||
if 0 < string.indexOf '#{', 1
|
||||
@interpolateString string[1...-1], strOffset: 1, lexedLength: string.length
|
||||
else
|
||||
@token 'STRING', @escapeLines string, 0, string.length
|
||||
else
|
||||
return 0
|
||||
switch quote = @chunk.charAt 0
|
||||
when "'" then [string] = SIMPLESTR.exec @chunk
|
||||
when '"' then string = @balancedString @chunk, '"'
|
||||
return 0 unless string
|
||||
trimmed = @removeNewlines string[1...-1]
|
||||
if quote is '"' and 0 < string.indexOf '#{', 1
|
||||
@interpolateString trimmed, strOffset: 1, lexedLength: string.length
|
||||
else
|
||||
@token 'STRING', quote + @escapeLines(trimmed) + quote, 0, string.length
|
||||
if octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test string
|
||||
@error "octal escape sequences #{string} are not allowed"
|
||||
string.length
|
||||
@@ -237,24 +234,24 @@ exports.Lexer = class Lexer
|
||||
# JavaScript and Ruby.
|
||||
regexToken: ->
|
||||
return 0 if @chunk.charAt(0) isnt '/'
|
||||
if match = HEREGEX.exec @chunk
|
||||
length = @heregexToken match
|
||||
return length
|
||||
return length if length = @heregexToken()
|
||||
|
||||
prev = last @tokens
|
||||
return 0 if prev and (prev[0] in (if prev.spaced then NOT_REGEX else NOT_SPACED_REGEX))
|
||||
return 0 unless match = REGEX.exec @chunk
|
||||
[match, regex, flags] = match
|
||||
# Avoid conflicts with floor division operator.
|
||||
return 0 if regex is '//'
|
||||
if regex[..1] is '/*' then @error 'regular expressions cannot begin with `*`'
|
||||
if regex is '//' then regex = '/(?:)/'
|
||||
@token 'REGEX', "#{regex}#{flags}", 0, match.length
|
||||
match.length
|
||||
|
||||
# Matches multiline extended regular expressions.
|
||||
heregexToken: (match) ->
|
||||
heregexToken: ->
|
||||
return 0 unless match = HEREGEX.exec @chunk
|
||||
[heregex, body, flags] = match
|
||||
if 0 > body.indexOf '#{'
|
||||
re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/')
|
||||
re = @escapeLines body.replace(HEREGEX_OMIT, '$1$2').replace(/\//g, '\\/'), yes
|
||||
if re.match /^\*/ then @error 'regular expressions cannot begin with `*`'
|
||||
@token 'REGEX', "/#{ re or '(?:)' }/#{flags}", 0, heregex.length
|
||||
return heregex.length
|
||||
@@ -266,7 +263,7 @@ exports.Lexer = class Lexer
|
||||
if tag is 'TOKENS'
|
||||
tokens.push value...
|
||||
else if tag is 'NEOSTRING'
|
||||
continue unless value = value.replace HEREGEX_OMIT, ''
|
||||
continue unless value = value.replace HEREGEX_OMIT, '$1$2'
|
||||
# Convert NEOSTRING into STRING
|
||||
value = value.replace /\\/g, '\\\\'
|
||||
token[0] = 'STRING'
|
||||
@@ -322,40 +319,51 @@ exports.Lexer = class Lexer
|
||||
@indebt = size - @indent
|
||||
@suppressNewlines()
|
||||
return indent.length
|
||||
unless @tokens.length
|
||||
@baseIndent = @indent = size
|
||||
return indent.length
|
||||
diff = size - @indent + @outdebt
|
||||
@token 'INDENT', diff, indent.length - size, size
|
||||
@indents.push diff
|
||||
@ends.push 'OUTDENT'
|
||||
@outdebt = @indebt = 0
|
||||
@indent = size
|
||||
else if size < @baseIndent
|
||||
@error 'missing indentation', indent.length
|
||||
else
|
||||
@indebt = 0
|
||||
@outdentToken @indent - size, noNewlines, indent.length
|
||||
@indent = size
|
||||
indent.length
|
||||
|
||||
# Record an outdent token or multiple tokens, if we happen to be moving back
|
||||
# inwards past several recorded indents.
|
||||
# inwards past several recorded indents. Sets new @indent value.
|
||||
outdentToken: (moveOut, noNewlines, outdentLength) ->
|
||||
decreasedIndent = @indent - moveOut
|
||||
while moveOut > 0
|
||||
len = @indents.length - 1
|
||||
if @indents[len] is undefined
|
||||
lastIndent = @indents[@indents.length - 1]
|
||||
if not lastIndent
|
||||
moveOut = 0
|
||||
else if @indents[len] is @outdebt
|
||||
else if lastIndent is @outdebt
|
||||
moveOut -= @outdebt
|
||||
@outdebt = 0
|
||||
else if @indents[len] < @outdebt
|
||||
@outdebt -= @indents[len]
|
||||
moveOut -= @indents[len]
|
||||
else if lastIndent < @outdebt
|
||||
@outdebt -= lastIndent
|
||||
moveOut -= lastIndent
|
||||
else
|
||||
dent = @indents.pop() + @outdebt
|
||||
moveOut -= dent
|
||||
if outdentLength and @chunk[outdentLength] in INDENTABLE_CLOSERS
|
||||
decreasedIndent -= dent - moveOut
|
||||
moveOut = dent
|
||||
@outdebt = 0
|
||||
# pair might call outdentToken, so preserve decreasedIndent
|
||||
@pair 'OUTDENT'
|
||||
@token 'OUTDENT', dent, 0, outdentLength
|
||||
@token 'OUTDENT', moveOut, 0, outdentLength
|
||||
moveOut -= dent
|
||||
@outdebt -= moveOut if dent
|
||||
@tokens.pop() while @value() is ';'
|
||||
|
||||
@token 'TERMINATOR', '\n', outdentLength, 0 unless @tag() is 'TERMINATOR' or noNewlines
|
||||
@indent = decreasedIndent
|
||||
this
|
||||
|
||||
# Matches and consumes non-meaningful whitespace. Tag the previous token
|
||||
@@ -406,6 +414,7 @@ exports.Lexer = class Lexer
|
||||
else if value in COMPARE then tag = 'COMPARE'
|
||||
else if value in COMPOUND_ASSIGN then tag = 'COMPOUND_ASSIGN'
|
||||
else if value in UNARY then tag = 'UNARY'
|
||||
else if value in UNARY_MATH then tag = 'UNARY_MATH'
|
||||
else if value in SHIFT then tag = 'SHIFT'
|
||||
else if value in LOGIC or value is '?' and prev?.spaced then tag = 'LOGIC'
|
||||
else if prev and not prev.spaced
|
||||
@@ -518,14 +527,9 @@ exports.Lexer = class Lexer
|
||||
# current chunk.
|
||||
interpolateString: (str, options = {}) ->
|
||||
{heredoc, regex, offsetInChunk, strOffset, lexedLength} = options
|
||||
offsetInChunk = offsetInChunk || 0
|
||||
strOffset = strOffset || 0
|
||||
lexedLength = lexedLength || str.length
|
||||
|
||||
# Clip leading \n from heredoc
|
||||
if heredoc and str.length > 0 and str[0] == '\n'
|
||||
str = str[1...]
|
||||
strOffset++
|
||||
offsetInChunk ||= 0
|
||||
strOffset ||= 0
|
||||
lexedLength ||= str.length
|
||||
|
||||
# Parse the string.
|
||||
tokens = []
|
||||
@@ -540,6 +544,8 @@ exports.Lexer = class Lexer
|
||||
continue
|
||||
# NEOSTRING is a fake token. This will be converted to a string below.
|
||||
tokens.push @makeToken('NEOSTRING', str[pi...i], strOffset + pi) if pi < i
|
||||
unless errorToken
|
||||
errorToken = @makeToken '', 'string interpolation', offsetInChunk + i + 1, 2
|
||||
inner = expr[1...-1]
|
||||
if inner.length
|
||||
[line, column] = @getLineAndColumnFromChunk(strOffset + i + 1)
|
||||
@@ -565,7 +571,9 @@ exports.Lexer = class Lexer
|
||||
# If the first token is not a string, add a fake empty string to the beginning.
|
||||
tokens.unshift @makeToken('NEOSTRING', '', offsetInChunk) unless tokens[0][0] is 'NEOSTRING'
|
||||
|
||||
@token '(', '(', offsetInChunk, 0 if interpolated = tokens.length > 1
|
||||
if interpolated = tokens.length > 1
|
||||
@token '(', '(', offsetInChunk, 0, errorToken
|
||||
|
||||
# Push all the tokens
|
||||
for token, i in tokens
|
||||
[tag, value] = token
|
||||
@@ -605,8 +613,7 @@ exports.Lexer = class Lexer
|
||||
# el.click((event) ->
|
||||
# el.hide())
|
||||
#
|
||||
@indent -= size = last @indents
|
||||
@outdentToken size, true
|
||||
@outdentToken last(@indents), true
|
||||
return @pair tag
|
||||
@ends.pop()
|
||||
|
||||
@@ -659,8 +666,9 @@ exports.Lexer = class Lexer
|
||||
# not specified, the length of `value` will be used.
|
||||
#
|
||||
# Returns the new token.
|
||||
token: (tag, value, offsetInChunk, length) ->
|
||||
token: (tag, value, offsetInChunk, length, origin) ->
|
||||
token = @makeToken tag, value, offsetInChunk, length
|
||||
token.origin = origin if origin
|
||||
@tokens.push token
|
||||
token
|
||||
|
||||
@@ -675,26 +683,39 @@ exports.Lexer = class Lexer
|
||||
# Are we in the midst of an unfinished expression?
|
||||
unfinished: ->
|
||||
LINE_CONTINUER.test(@chunk) or
|
||||
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION'
|
||||
'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
|
||||
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-',
|
||||
'**', 'SHIFT', 'RELATION', 'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
|
||||
|
||||
# Remove newlines from beginning and (non escaped) from end of string literals.
|
||||
removeNewlines: (str) ->
|
||||
str.replace(/^\s*\n\s*/, '')
|
||||
.replace(/([^\\]|\\\\)\s*\n\s*$/, '$1')
|
||||
|
||||
# Converts newlines for string literals.
|
||||
escapeLines: (str, heredoc) ->
|
||||
str.replace MULTILINER, if heredoc then '\\n' else ''
|
||||
# Ignore escaped backslashes and remove escaped newlines
|
||||
str = str.replace /\\[^\S\n]*(\n|\\)\s*/g, (escaped, character) ->
|
||||
if character is '\n' then '' else escaped
|
||||
if heredoc
|
||||
str.replace MULTILINER, '\\n'
|
||||
else
|
||||
str.replace /\s*\n\s*/g, ' '
|
||||
|
||||
# Constructs a string token by escaping quotes and newlines.
|
||||
makeString: (body, quote, heredoc) ->
|
||||
return quote + quote unless body
|
||||
body = body.replace /\\([\s\S])/g, (match, contents) ->
|
||||
if contents in ['\n', quote] then contents else match
|
||||
# Ignore escaped backslashes and unescape quotes
|
||||
body = body.replace /// \\( #{quote} | \\ ) ///g, (match, contents) ->
|
||||
if contents is quote then contents else match
|
||||
body = body.replace /// #{quote} ///g, '\\$&'
|
||||
quote + @escapeLines(body, heredoc) + quote
|
||||
|
||||
# Throws a compiler error on the current position.
|
||||
error: (message) ->
|
||||
error: (message, offset = 0) ->
|
||||
# TODO: Are there some cases we could improve the error line number by
|
||||
# passing the offset in the chunk where the error happened?
|
||||
throwSyntaxError message, first_line: @chunkLine, first_column: @chunkColumn
|
||||
[first_line, first_column] = @getLineAndColumnFromChunk offset
|
||||
throwSyntaxError message, {first_line, first_column}
|
||||
|
||||
# Constants
|
||||
# ---------
|
||||
@@ -760,27 +781,27 @@ NUMBER = ///
|
||||
^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal
|
||||
///i
|
||||
|
||||
HEREDOC = /// ^ ("""|''') ([\s\S]*?) (?:\n[^\n\S]*)? \1 ///
|
||||
HEREDOC = /// ^ ("""|''') ((?: \\[\s\S] | [^\\] )*?) (?:\n[^\n\S]*)? \1 ///
|
||||
|
||||
OPERATOR = /// ^ (
|
||||
?: [-=]> # function
|
||||
| [-+*/%<>&|^!?=]= # compound assign / compare
|
||||
| >>>=? # zero-fill right shift
|
||||
| ([-+:])\1 # doubles
|
||||
| ([&|<>])\2=? # logic / shift
|
||||
| ([&|<>*/%])\2=? # logic / shift / power / floor division / modulo
|
||||
| \?(\.|::) # soak access
|
||||
| \.{2,3} # range or splat
|
||||
) ///
|
||||
|
||||
WHITESPACE = /^[^\n\S]+/
|
||||
|
||||
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/
|
||||
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/
|
||||
|
||||
CODE = /^[-=]>/
|
||||
|
||||
MULTI_DENT = /^(?:\n[^\n\S]*)+/
|
||||
|
||||
SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/
|
||||
SIMPLESTR = /^'[^\\']*(?:\\[\s\S][^\\']*)*'/
|
||||
|
||||
JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/
|
||||
|
||||
@@ -799,9 +820,13 @@ REGEX = /// ^
|
||||
/) ([imgy]{0,4}) (?!\w)
|
||||
///
|
||||
|
||||
HEREGEX = /// ^ /{3} ([\s\S]+?) /{3} ([imgy]{0,4}) (?!\w) ///
|
||||
HEREGEX = /// ^ /{3} ((?:\\?[\s\S])+?) /{3} ([imgy]{0,4}) (?!\w) ///
|
||||
|
||||
HEREGEX_OMIT = /\s+(?:#.*)?/g
|
||||
HEREGEX_OMIT = ///
|
||||
((?:\\\\)+) # consume (and preserve) an even number of backslashes
|
||||
| \\(\s|/) # preserve escaped whitespace and "de-escape" slashes
|
||||
| \s+(?:#.*)? # remove whitespace and comments
|
||||
///g
|
||||
|
||||
# Token cleaning regexes.
|
||||
MULTILINER = /\n/g
|
||||
@@ -816,23 +841,26 @@ TRAILING_SPACES = /\s+$/
|
||||
|
||||
# Compound assignment tokens.
|
||||
COMPOUND_ASSIGN = [
|
||||
'-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='
|
||||
'-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>='
|
||||
'&=', '^=', '|=', '**=', '//=', '%%='
|
||||
]
|
||||
|
||||
# Unary tokens.
|
||||
UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO']
|
||||
UNARY = ['NEW', 'TYPEOF', 'DELETE', 'DO']
|
||||
|
||||
UNARY_MATH = ['!', '~']
|
||||
|
||||
# Logical tokens.
|
||||
LOGIC = ['&&', '||', '&', '|', '^']
|
||||
LOGIC = ['&&', '||', '&', '|', '^']
|
||||
|
||||
# Bit-shifting tokens.
|
||||
SHIFT = ['<<', '>>', '>>>']
|
||||
SHIFT = ['<<', '>>', '>>>']
|
||||
|
||||
# Comparison tokens.
|
||||
COMPARE = ['==', '!=', '<', '>', '<=', '>=']
|
||||
|
||||
# Mathematical tokens.
|
||||
MATH = ['*', '/', '%']
|
||||
MATH = ['*', '/', '%', '//', '%%']
|
||||
|
||||
# Relational tokens that are negatable with `not` prefix.
|
||||
RELATION = ['IN', 'OF', 'INSTANCEOF']
|
||||
@@ -862,3 +890,6 @@ INDEXABLE = CALLABLE.concat 'NUMBER', 'BOOL', 'NULL', 'UNDEFINED'
|
||||
# occurs at the start of a line. We disambiguate these from trailing whens to
|
||||
# avoid an ambiguity in the grammar.
|
||||
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']
|
||||
|
||||
# Additional indent in front of these is ignored.
|
||||
INDENTABLE_CLOSERS = [')', '}', ']']
|
||||
|
||||
415
src/nodes.coffee
415
src/nodes.coffee
@@ -79,7 +79,17 @@ exports.Base = class Base
|
||||
if jumpNode = @jumps()
|
||||
jumpNode.error 'cannot use a pure statement in an expression'
|
||||
o.sharedScope = yes
|
||||
Closure.wrap(this).compileNode o
|
||||
func = new Code [], Block.wrap [this]
|
||||
args = []
|
||||
if (argumentsNode = @contains isLiteralArguments) or @contains isLiteralThis
|
||||
args = [new Literal 'this']
|
||||
if argumentsNode
|
||||
meth = 'apply'
|
||||
args.push new Literal 'arguments'
|
||||
else
|
||||
meth = 'call'
|
||||
func = new Value func, [new Access new Literal meth]
|
||||
(new Call func, args).compileNode 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,
|
||||
@@ -176,7 +186,8 @@ exports.Base = class Base
|
||||
# For this node and all descendents, set the location data to `locationData`
|
||||
# if the location data is not already set.
|
||||
updateLocationDataIfMissing: (locationData) ->
|
||||
@locationData or= locationData
|
||||
return this if @locationData
|
||||
@locationData = locationData
|
||||
|
||||
@eachChild (child) ->
|
||||
child.updateLocationDataIfMissing locationData
|
||||
@@ -242,7 +253,7 @@ exports.Block = class Block extends Base
|
||||
|
||||
jumps: (o) ->
|
||||
for exp in @expressions
|
||||
return exp if exp.jumps o
|
||||
return jumpNode if jumpNode = exp.jumps o
|
||||
|
||||
# A Block node does not return its entire body, rather it
|
||||
# ensures that the final expression is returned.
|
||||
@@ -468,17 +479,25 @@ exports.Value = class Value extends Base
|
||||
hasProperties: ->
|
||||
!!@properties.length
|
||||
|
||||
bareLiteral: (type) ->
|
||||
not @properties.length and @base instanceof type
|
||||
|
||||
# Some boolean checks for the benefit of other nodes.
|
||||
isArray : -> not @properties.length and @base instanceof Arr
|
||||
isArray : -> @bareLiteral(Arr)
|
||||
isRange : -> @bareLiteral(Range)
|
||||
isComplex : -> @hasProperties() or @base.isComplex()
|
||||
isAssignable : -> @hasProperties() or @base.isAssignable()
|
||||
isSimpleNumber : -> @base instanceof Literal and SIMPLENUM.test @base.value
|
||||
isString : -> @base instanceof Literal and IS_STRING.test @base.value
|
||||
isSimpleNumber : -> @bareLiteral(Literal) and SIMPLENUM.test @base.value
|
||||
isString : -> @bareLiteral(Literal) and IS_STRING.test @base.value
|
||||
isRegex : -> @bareLiteral(Literal) and IS_REGEX.test @base.value
|
||||
isAtomic : ->
|
||||
for node in @properties.concat @base
|
||||
return no if node.soak or node instanceof Call
|
||||
yes
|
||||
|
||||
isNotCallable : -> @isSimpleNumber() or @isString() or @isRegex() or
|
||||
@isArray() or @isRange() or @isSplice() or @isObject()
|
||||
|
||||
isStatement : (o) -> not @properties.length and @base.isStatement o
|
||||
assigns : (name) -> not @properties.length and @base.assigns name
|
||||
jumps : (o) -> not @properties.length and @base.jumps o
|
||||
@@ -490,6 +509,10 @@ exports.Value = class Value extends Base
|
||||
isSplice: ->
|
||||
last(@properties) instanceof Slice
|
||||
|
||||
looksStatic: (className) ->
|
||||
@base.value is className and @properties.length and
|
||||
@properties[0].name?.value isnt 'prototype'
|
||||
|
||||
# The value can be unwrapped as its inner node, if there are no attached
|
||||
# properties.
|
||||
unwrap: ->
|
||||
@@ -555,9 +578,10 @@ exports.Comment = class Comment extends Base
|
||||
makeReturn: THIS
|
||||
|
||||
compileNode: (o, level) ->
|
||||
code = "/*#{multident @comment, @tab}#{if '\n' in @comment then "\n#{@tab}" else ''}*/\n"
|
||||
comment = @comment.replace /^(\s*)#/gm, "$1 *"
|
||||
code = "/*#{multident comment, @tab}#{if '\n' in comment then "\n#{@tab}" else ''} */"
|
||||
code = o.indent + code if (level or o.level) is LEVEL_TOP
|
||||
[@makeCode code]
|
||||
[@makeCode("\n"), @makeCode(code)]
|
||||
|
||||
#### Call
|
||||
|
||||
@@ -568,6 +592,8 @@ exports.Call = class Call extends Base
|
||||
@isNew = false
|
||||
@isSuper = variable is 'super'
|
||||
@variable = if @isSuper then null else variable
|
||||
if variable instanceof Value and variable.isNotCallable()
|
||||
variable.error "literal is not a function"
|
||||
|
||||
children: ['variable', 'args']
|
||||
|
||||
@@ -768,8 +794,8 @@ exports.Range = class Range extends Base
|
||||
[@fromC, @fromVar] = @cacheToCodeFragments @from.cache o, LEVEL_LIST
|
||||
[@toC, @toVar] = @cacheToCodeFragments @to.cache o, LEVEL_LIST
|
||||
[@step, @stepVar] = @cacheToCodeFragments step.cache o, LEVEL_LIST if step = del o, 'step'
|
||||
[@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
|
||||
@stepNum = @stepVar.match(SIMPLENUM) if @stepVar
|
||||
[@fromNum, @toNum] = [@fromVar.match(NUMBER), @toVar.match(NUMBER)]
|
||||
@stepNum = @stepVar.match(NUMBER) if @stepVar
|
||||
|
||||
# When compiled normally, the range returns the contents of the *for loop*
|
||||
# needed to iterate over the values in the range. Used by comprehensions.
|
||||
@@ -789,9 +815,9 @@ exports.Range = class Range extends Base
|
||||
|
||||
# Generate the condition.
|
||||
condPart = if @stepNum
|
||||
if +@stepNum > 0 then "#{lt} #{@toVar}" else "#{gt} #{@toVar}"
|
||||
if parseNum(@stepNum[0]) > 0 then "#{lt} #{@toVar}" else "#{gt} #{@toVar}"
|
||||
else if known
|
||||
[from, to] = [+@fromNum, +@toNum]
|
||||
[from, to] = [parseNum(@fromNum[0]), parseNum(@toNum[0])]
|
||||
if from <= to then "#{lt} #{to}" else "#{gt} #{to}"
|
||||
else
|
||||
cond = if @stepVar then "#{@stepVar} > 0" else "#{@fromVar} <= #{@toVar}"
|
||||
@@ -836,7 +862,7 @@ exports.Range = class Range extends Base
|
||||
cond = "#{@fromVar} <= #{@toVar}"
|
||||
body = "var #{vars}; #{cond} ? #{i} <#{@equals} #{@toVar} : #{i} >#{@equals} #{@toVar}; #{cond} ? #{i}++ : #{i}--"
|
||||
post = "{ #{result}.push(#{i}); }\n#{idt}return #{result};\n#{o.indent}"
|
||||
hasArgs = (node) -> node?.contains (n) -> n instanceof Literal and n.value is 'arguments' and not n.asKey
|
||||
hasArgs = (node) -> node?.contains isLiteralArguments
|
||||
args = ', arguments' if hasArgs(@from) or hasArgs(@to)
|
||||
[@makeCode "(function() {#{pre}\n#{idt}for (#{body})#{post}}).apply(this#{args ? ''})"]
|
||||
|
||||
@@ -1009,13 +1035,11 @@ exports.Class = class Class extends Base
|
||||
if func instanceof Code
|
||||
assign = @ctor = func
|
||||
else
|
||||
@externalCtor = o.scope.freeVariable 'class'
|
||||
@externalCtor = o.classScope.freeVariable 'class'
|
||||
assign = new Assign new Literal(@externalCtor), func
|
||||
else
|
||||
if assign.variable.this
|
||||
func.static = yes
|
||||
if func.bound
|
||||
func.context = name
|
||||
else
|
||||
assign.variable = new Value(new Literal(name), [(new Access new Literal 'prototype'), new Access base])
|
||||
if func instanceof Code and func.bound
|
||||
@@ -1024,14 +1048,17 @@ exports.Class = class Class extends Base
|
||||
assign
|
||||
compact exprs
|
||||
|
||||
# Walk the body of the class, looking for prototype properties to be converted.
|
||||
# Walk the body of the class, looking for prototype properties to be converted
|
||||
# and tagging static assignments.
|
||||
walkBody: (name, o) ->
|
||||
@traverseChildren false, (child) =>
|
||||
cont = true
|
||||
return false if child instanceof Class
|
||||
if child instanceof Block
|
||||
for node, i in exps = child.expressions
|
||||
if node instanceof Value and node.isObject(true)
|
||||
if node instanceof Assign and node.variable.looksStatic name
|
||||
node.value.static = yes
|
||||
else if node instanceof Value and node.isObject(true)
|
||||
cont = false
|
||||
exps[i] = @addProperties node, name, o
|
||||
child.expressions = exps = flatten exps
|
||||
@@ -1049,55 +1076,52 @@ exports.Class = class Class extends Base
|
||||
|
||||
# Make sure that a constructor is defined for the class, and properly
|
||||
# configured.
|
||||
ensureConstructor: (name, o) ->
|
||||
missing = not @ctor
|
||||
@ctor or= new Code
|
||||
ensureConstructor: (name) ->
|
||||
if not @ctor
|
||||
@ctor = new Code
|
||||
if @externalCtor
|
||||
@ctor.body.push new Literal "#{@externalCtor}.apply(this, arguments)"
|
||||
else if @parent
|
||||
@ctor.body.push new Literal "#{name}.__super__.constructor.apply(this, arguments)"
|
||||
@ctor.body.makeReturn()
|
||||
@body.expressions.unshift @ctor
|
||||
@ctor.ctor = @ctor.name = name
|
||||
@ctor.klass = null
|
||||
@ctor.noReturn = yes
|
||||
if missing
|
||||
superCall = new Literal "#{name}.__super__.constructor.apply(this, arguments)" if @parent
|
||||
superCall = new Literal "#{@externalCtor}.apply(this, arguments)" if @externalCtor
|
||||
if superCall
|
||||
ref = new Literal o.scope.freeVariable 'ref'
|
||||
@ctor.body.unshift new Assign ref, superCall
|
||||
@addBoundFunctions o
|
||||
if superCall
|
||||
@ctor.body.push ref
|
||||
@ctor.body.makeReturn()
|
||||
@body.expressions.unshift @ctor
|
||||
else
|
||||
@addBoundFunctions o
|
||||
|
||||
|
||||
# Instead of generating the JavaScript string directly, we build up the
|
||||
# equivalent syntax tree and compile that, in pieces. You can see the
|
||||
# constructor, property assignments, and inheritance getting built out below.
|
||||
compileNode: (o) ->
|
||||
decl = @determineName()
|
||||
name = decl or '_Class'
|
||||
name = "_#{name}" if name.reserved
|
||||
if jumpNode = @body.jumps()
|
||||
jumpNode.error 'Class bodies cannot contain pure statements'
|
||||
if argumentsNode = @body.contains isLiteralArguments
|
||||
argumentsNode.error "Class bodies shouldn't reference arguments"
|
||||
|
||||
name = @determineName() or '_Class'
|
||||
name = "_#{name}" if name.reserved
|
||||
lname = new Literal name
|
||||
func = new Code [], Block.wrap [@body]
|
||||
args = []
|
||||
o.classScope = func.makeScope o.scope
|
||||
|
||||
@hoistDirectivePrologue()
|
||||
@setContext name
|
||||
@walkBody name, o
|
||||
@ensureConstructor name, o
|
||||
@ensureConstructor name
|
||||
@addBoundFunctions o
|
||||
@body.spaced = yes
|
||||
@body.expressions.unshift @ctor unless @ctor instanceof Code
|
||||
@body.expressions.push lname
|
||||
@body.expressions.unshift @directives...
|
||||
|
||||
call = Closure.wrap @body
|
||||
|
||||
if @parent
|
||||
@superClass = new Literal o.scope.freeVariable 'super', no
|
||||
@body.expressions.unshift new Extends lname, @superClass
|
||||
call.args.push @parent
|
||||
params = call.variable.params or call.variable.base.params
|
||||
params.push new Param @superClass
|
||||
superClass = new Literal o.classScope.freeVariable 'super', no
|
||||
@body.expressions.unshift new Extends lname, superClass
|
||||
func.params.push new Param superClass
|
||||
args.push @parent
|
||||
|
||||
klass = new Parens call, yes
|
||||
@body.expressions.unshift @directives...
|
||||
|
||||
klass = new Parens new Call func, args
|
||||
klass = new Assign @variable, klass if @variable
|
||||
klass.compileToFragments o
|
||||
|
||||
@@ -1133,6 +1157,7 @@ exports.Assign = class Assign extends Base
|
||||
return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
|
||||
return @compileSplice o if @variable.isSplice()
|
||||
return @compileConditional o if @context in ['||=', '&&=', '?=']
|
||||
return @compileSpecialMath o if @context in ['**=', '//=', '%%=']
|
||||
compiledName = @variable.compileToFragments o, LEVEL_LIST
|
||||
name = fragmentsToText compiledName
|
||||
unless @context
|
||||
@@ -1145,8 +1170,8 @@ exports.Assign = class Assign extends Base
|
||||
else
|
||||
o.scope.find name
|
||||
if @value instanceof Code and match = METHOD_DEF.exec name
|
||||
@value.klass = match[1] if match[1]
|
||||
@value.name = match[2] ? match[3] ? match[4] ? match[5]
|
||||
@value.klass = match[1] if match[2]
|
||||
@value.name = match[3] ? match[4] ? match[5]
|
||||
val = @value.compileToFragments o, LEVEL_LIST
|
||||
return (compiledName.concat @makeCode(": "), val) if @context is 'object'
|
||||
answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val
|
||||
@@ -1179,10 +1204,10 @@ exports.Assign = class Assign extends Base
|
||||
if obj.unwrap().value in RESERVED
|
||||
obj.error "assignment to a reserved word: #{obj.compile o}"
|
||||
return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP
|
||||
vvar = value.compileToFragments o, LEVEL_LIST
|
||||
vvar = value.compileToFragments o, LEVEL_LIST
|
||||
vvarText = fragmentsToText vvar
|
||||
assigns = []
|
||||
splat = false
|
||||
assigns = []
|
||||
expandedIdx = false
|
||||
# Make vvar into a simple variable if it isn't already.
|
||||
if not IDENTIFIER.test(vvarText) or @variable.assigns(vvarText)
|
||||
assigns.push [@makeCode("#{ ref = o.scope.freeVariable 'ref' } = "), vvar...]
|
||||
@@ -1201,7 +1226,7 @@ exports.Assign = class Assign extends Base
|
||||
[obj, idx] = new Value(obj.unwrapAll()).cacheReference o
|
||||
else
|
||||
idx = if obj.this then obj.properties[0].name else obj
|
||||
if not splat and obj instanceof Splat
|
||||
if not expandedIdx and obj instanceof Splat
|
||||
name = obj.name.unwrap().value
|
||||
obj = obj.unwrap()
|
||||
val = "#{olen} <= #{vvarText}.length ? #{ utility 'slice' }.call(#{vvarText}, #{i}"
|
||||
@@ -1211,13 +1236,23 @@ exports.Assign = class Assign extends Base
|
||||
else
|
||||
val += ") : []"
|
||||
val = new Literal val
|
||||
splat = "#{ivar}++"
|
||||
expandedIdx = "#{ivar}++"
|
||||
else if not expandedIdx and obj instanceof Expansion
|
||||
if rest = olen - i - 1
|
||||
if rest is 1
|
||||
expandedIdx = "#{vvarText}.length - 1"
|
||||
else
|
||||
ivar = o.scope.freeVariable 'i'
|
||||
val = new Literal "#{ivar} = #{vvarText}.length - #{rest}"
|
||||
expandedIdx = "#{ivar}++"
|
||||
assigns.push val.compileToFragments o, LEVEL_LIST
|
||||
continue
|
||||
else
|
||||
name = obj.unwrap().value
|
||||
if obj instanceof Splat
|
||||
obj.error "multiple splats are disallowed in an assignment"
|
||||
if obj instanceof Splat or obj instanceof Expansion
|
||||
obj.error "multiple splats/expansions are disallowed in an assignment"
|
||||
if typeof idx is 'number'
|
||||
idx = new Literal splat or idx
|
||||
idx = new Literal expandedIdx or idx
|
||||
acc = no
|
||||
else
|
||||
acc = isObject and IDENTIFIER.test idx.unwrap().value or 0
|
||||
@@ -1238,8 +1273,18 @@ exports.Assign = class Assign extends Base
|
||||
if not left.properties.length and left.base instanceof Literal and
|
||||
left.base.value != "this" and not o.scope.check left.base.value
|
||||
@variable.error "the variable \"#{left.base.value}\" can't be assigned with #{@context} because it has not been declared before"
|
||||
if "?" in @context then o.isExistentialEquals = true
|
||||
new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o
|
||||
if "?" in @context
|
||||
o.isExistentialEquals = true
|
||||
new If(new Existence(left), right, type: 'if').addElse(new Assign(right, @value, '=')).compileToFragments o
|
||||
else
|
||||
fragments = new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o
|
||||
if o.level <= LEVEL_LIST then fragments else @wrapInBraces fragments
|
||||
|
||||
# Convert special math assignment operators like `a **= b` to the equivalent
|
||||
# extended form `a = a ** b` and then compiles that.
|
||||
compileSpecialMath: (o) ->
|
||||
[left, right] = @variable.cacheReference o
|
||||
new Assign(left, new Op(@context[...-1], right, @value)).compileToFragments o
|
||||
|
||||
# Compile the assignment from an array splice literal, using JavaScript's
|
||||
# `Array#splice` method.
|
||||
@@ -1251,8 +1296,9 @@ exports.Assign = class Assign extends Base
|
||||
else
|
||||
fromDecl = fromRef = '0'
|
||||
if to
|
||||
if from?.isSimpleNumber() and to.isSimpleNumber()
|
||||
to = +to.compile(o) - +fromRef
|
||||
if from instanceof Value and from.isSimpleNumber() and
|
||||
to instanceof Value and to.isSimpleNumber()
|
||||
to = to.compile(o) - fromRef
|
||||
to += 1 unless exclusive
|
||||
else
|
||||
to = to.compile(o, LEVEL_ACCESS) + ' - ' + fromRef
|
||||
@@ -1273,7 +1319,6 @@ exports.Code = class Code extends Base
|
||||
@params = params or []
|
||||
@body = body or new Block
|
||||
@bound = tag is 'boundfunc'
|
||||
@context = '_this' if @bound
|
||||
|
||||
children: ['params', 'body']
|
||||
|
||||
@@ -1281,23 +1326,37 @@ exports.Code = class Code extends Base
|
||||
|
||||
jumps: NO
|
||||
|
||||
makeScope: (parentScope) -> new Scope parentScope, @body, this
|
||||
|
||||
# Compilation creates a new scope unless explicitly asked to share with the
|
||||
# outer scope. Handles splat parameters in the parameter list by peeking at
|
||||
# the JavaScript `arguments` object. If the function is bound with the `=>`
|
||||
# arrow, generates a wrapper that saves the current value of `this` through
|
||||
# a closure.
|
||||
compileNode: (o) ->
|
||||
o.scope = new Scope o.scope, @body, this
|
||||
|
||||
if @bound and o.scope.method?.bound
|
||||
@context = o.scope.method.context
|
||||
|
||||
# Handle bound functions early.
|
||||
if @bound and not @context
|
||||
@context = '_this'
|
||||
wrapper = new Code [new Param new Literal @context], new Block [this]
|
||||
boundfunc = new Call(wrapper, [new Literal 'this'])
|
||||
boundfunc.updateLocationDataIfMissing @locationData
|
||||
return boundfunc.compileNode(o)
|
||||
|
||||
o.scope = del(o, 'classScope') or @makeScope o.scope
|
||||
o.scope.shared = del(o, 'sharedScope')
|
||||
o.indent += TAB
|
||||
delete o.bare
|
||||
delete o.isExistentialEquals
|
||||
params = []
|
||||
exprs = []
|
||||
@eachParamName (name) -> # this step must be performed before the others
|
||||
unless o.scope.check name then o.scope.parameter name
|
||||
for param in @params when param.splat
|
||||
for {name: p} in @params
|
||||
for param in @params when param not instanceof Expansion
|
||||
o.scope.parameter param.asReference o
|
||||
for param in @params when param.splat or param instanceof Expansion
|
||||
for {name: p} in @params when param not instanceof Expansion
|
||||
if p.this then p = p.properties[0].name
|
||||
if p.value then o.scope.add p.value, 'var', yes
|
||||
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
|
||||
@@ -1326,12 +1385,6 @@ exports.Code = class Code extends Base
|
||||
node.error "multiple parameters named '#{name}'" if name in uniqs
|
||||
uniqs.push name
|
||||
@body.makeReturn() unless wasEmpty or @noReturn
|
||||
if @bound
|
||||
if o.scope.parent.method?.bound
|
||||
@bound = @context = o.scope.parent.method.context
|
||||
else if not @static
|
||||
o.scope.parent.assign '_this', 'this'
|
||||
idt = o.indent
|
||||
code = 'function'
|
||||
code += ' ' + @name if @ctor
|
||||
code += '('
|
||||
@@ -1380,6 +1433,7 @@ exports.Param = class Param extends Base
|
||||
node = new Literal o.scope.freeVariable 'arg'
|
||||
node = new Value node
|
||||
node = new Splat node if @splat
|
||||
node.updateLocationDataIfMissing @locationData
|
||||
@reference = node
|
||||
|
||||
isComplex: ->
|
||||
@@ -1416,7 +1470,7 @@ exports.Param = class Param extends Base
|
||||
atParam obj
|
||||
# * simple destructured parameters {foo}
|
||||
else iterator obj.base.value, obj.base
|
||||
else
|
||||
else if obj not instanceof Expansion
|
||||
obj.error "illegal parameter #{obj.compile()}"
|
||||
return
|
||||
|
||||
@@ -1467,6 +1521,22 @@ exports.Splat = class Splat extends Base
|
||||
concatPart = list[index].joinFragmentArrays args, ', '
|
||||
[].concat list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, (last list).makeCode(")")
|
||||
|
||||
#### Expansion
|
||||
|
||||
# Used to skip values inside an array destructuring (pattern matching) or
|
||||
# parameter list.
|
||||
exports.Expansion = class Expansion extends Base
|
||||
|
||||
isComplex: NO
|
||||
|
||||
compileNode: (o) ->
|
||||
@error 'Expansion must be used inside a destructuring assignment or parameter list'
|
||||
|
||||
asReference: (o) ->
|
||||
this
|
||||
|
||||
eachName: (iterator) ->
|
||||
|
||||
#### While
|
||||
|
||||
# A while loop, the only sort of low-level loop exposed by CoffeeScript. From
|
||||
@@ -1495,7 +1565,7 @@ exports.While = class While extends Base
|
||||
{expressions} = @body
|
||||
return no unless expressions.length
|
||||
for node in expressions
|
||||
return node if node.jumps loop: yes
|
||||
return jumpNode if jumpNode = node.jumps loop: yes
|
||||
no
|
||||
|
||||
# The main difference from a JavaScript *while* is that the CoffeeScript
|
||||
@@ -1624,10 +1694,16 @@ exports.Op = class Op extends Base
|
||||
@error "cannot increment/decrement \"#{@first.unwrapAll().value}\""
|
||||
return @compileUnary o if @isUnary()
|
||||
return @compileChain o if isChain
|
||||
return @compileExistence o if @operator is '?'
|
||||
answer = [].concat @first.compileToFragments(o, LEVEL_OP), @makeCode(' ' + @operator + ' '),
|
||||
@second.compileToFragments(o, LEVEL_OP)
|
||||
if o.level <= LEVEL_OP then answer else @wrapInBraces answer
|
||||
switch @operator
|
||||
when '?' then @compileExistence o
|
||||
when '**' then @compilePower o
|
||||
when '//' then @compileFloorDivision o
|
||||
when '%%' then @compileModulo o
|
||||
else
|
||||
lhs = @first.compileToFragments o, LEVEL_OP
|
||||
rhs = @second.compileToFragments o, LEVEL_OP
|
||||
answer = [].concat lhs, @makeCode(" #{@operator} "), rhs
|
||||
if o.level <= LEVEL_OP then answer else @wrapInBraces answer
|
||||
|
||||
# Mimic Python's chained comparisons when multiple comparison operators are
|
||||
# used sequentially. For example:
|
||||
@@ -1643,7 +1719,7 @@ exports.Op = class Op extends Base
|
||||
|
||||
# Keep reference to the left expression, unless this an existential assignment
|
||||
compileExistence: (o) ->
|
||||
if !o.isExistentialEquals and @first.isComplex()
|
||||
if @first.isComplex()
|
||||
ref = new Literal o.scope.freeVariable 'ref'
|
||||
fst = new Parens new Assign ref, @first
|
||||
else
|
||||
@@ -1670,6 +1746,20 @@ exports.Op = class Op extends Base
|
||||
parts.reverse() if @flip
|
||||
@joinFragmentArrays parts, ''
|
||||
|
||||
compilePower: (o) ->
|
||||
# Make a Math.pow call
|
||||
pow = new Value new Literal('Math'), [new Access new Literal 'pow']
|
||||
new Call(pow, [@first, @second]).compileToFragments o
|
||||
|
||||
compileFloorDivision: (o) ->
|
||||
floor = new Value new Literal('Math'), [new Access new Literal 'floor']
|
||||
div = new Op '/', @first, @second
|
||||
new Call(floor, [div]).compileToFragments o
|
||||
|
||||
compileModulo: (o) ->
|
||||
mod = new Value new Literal utility 'modulo'
|
||||
new Call(mod, [@first, @second]).compileToFragments o
|
||||
|
||||
toString: (idt) ->
|
||||
super idt, @constructor.name + ' ' + @operator
|
||||
|
||||
@@ -1682,7 +1772,7 @@ exports.In = class In extends Base
|
||||
invert: NEGATE
|
||||
|
||||
compileNode: (o) ->
|
||||
if @array instanceof Value and @array.isArray()
|
||||
if @array instanceof Value and @array.isArray() and @array.base.objects.length
|
||||
for obj in @array.base.objects when obj instanceof Splat
|
||||
hasSplat = yes
|
||||
break
|
||||
@@ -1691,7 +1781,6 @@ exports.In = class In extends Base
|
||||
@compileLoopTest o
|
||||
|
||||
compileOrTest: (o) ->
|
||||
return [@makeCode("#{!!@negated}")] if @array.base.objects.length is 0
|
||||
[sub, ref] = @object.cache o, LEVEL_OP
|
||||
[cmp, cnj] = if @negated then [' !== ', ' && '] else [' === ', ' || ']
|
||||
tests = []
|
||||
@@ -1837,7 +1926,7 @@ exports.For = class For extends While
|
||||
@pattern = @name instanceof Value
|
||||
@index.error 'indexes do not apply to range loops' if @range and @index
|
||||
@name.error 'cannot pattern match over range loops' if @range and @pattern
|
||||
@index.error 'cannot use own with for-in' if @own and not @object
|
||||
@name.error 'cannot use own with for-in' if @own and not @object
|
||||
@returns = false
|
||||
|
||||
children: ['body', 'source', 'guard', 'step']
|
||||
@@ -1852,7 +1941,7 @@ exports.For = class For extends While
|
||||
@returns = no if lastJumps and lastJumps instanceof Return
|
||||
source = if @range then @source.base else @source
|
||||
scope = o.scope
|
||||
name = @name and (@name.compile o, LEVEL_LIST)
|
||||
name = @name and (@name.compile o, LEVEL_LIST) if not @pattern
|
||||
index = @index and (@index.compile o, LEVEL_LIST)
|
||||
scope.find(name) if name and not @pattern
|
||||
scope.find(index) if index
|
||||
@@ -1862,7 +1951,7 @@ exports.For = class For extends While
|
||||
kvarAssign = if kvar isnt ivar then "#{kvar} = " else ""
|
||||
if @step and not @range
|
||||
[step, stepVar] = @cacheToCodeFragments @step.cache o, LEVEL_LIST
|
||||
stepNum = stepVar.match SIMPLENUM
|
||||
stepNum = stepVar.match NUMBER
|
||||
name = ivar if @pattern
|
||||
varPart = ''
|
||||
guardPart = ''
|
||||
@@ -1879,7 +1968,7 @@ exports.For = class For extends While
|
||||
namePart = "#{name} = #{svar}[#{kvar}]"
|
||||
if not @object
|
||||
defPart += "#{@tab}#{step};\n" if step isnt stepVar
|
||||
lvar = scope.freeVariable 'len' unless @step and stepNum and down = (+stepNum < 0)
|
||||
lvar = scope.freeVariable 'len' unless @step and stepNum and down = (parseNum(stepNum[0]) < 0)
|
||||
declare = "#{kvarAssign}#{ivar} = 0, #{lvar} = #{svar}.length"
|
||||
declareDown = "#{kvarAssign}#{ivar} = #{svar}.length - 1"
|
||||
compare = "#{ivar} < #{lvar}"
|
||||
@@ -1924,7 +2013,7 @@ exports.For = class For extends While
|
||||
for expr, idx in body.expressions
|
||||
expr = expr.unwrapAll()
|
||||
continue unless expr instanceof Call
|
||||
val = expr.variable.unwrapAll()
|
||||
val = expr.variable?.unwrapAll()
|
||||
continue unless (val instanceof Code) or
|
||||
(val instanceof Value and
|
||||
val.base?.unwrapAll() instanceof Code and
|
||||
@@ -1951,7 +2040,7 @@ exports.Switch = class Switch extends Base
|
||||
|
||||
jumps: (o = {block: yes}) ->
|
||||
for [conds, block] in @cases
|
||||
return block if block.jumps o
|
||||
return jumpNode if jumpNode = block.jumps o
|
||||
@otherwise?.jumps o
|
||||
|
||||
makeReturn: (res) ->
|
||||
@@ -2006,6 +2095,7 @@ exports.If = class If extends Base
|
||||
else
|
||||
@isChain = elseBody instanceof If
|
||||
@elseBody = @ensureBlock elseBody
|
||||
@elseBody.updateLocationDataIfMissing elseBody.locationData
|
||||
this
|
||||
|
||||
# The **If** only compiles into a statement if either of its bodies needs
|
||||
@@ -2062,50 +2152,6 @@ exports.If = class If extends Base
|
||||
unfoldSoak: ->
|
||||
@soak and this
|
||||
|
||||
# Faux-Nodes
|
||||
# ----------
|
||||
# Faux-nodes are never created by the grammar, but are used during code
|
||||
# generation to generate other combinations of nodes.
|
||||
|
||||
#### Closure
|
||||
|
||||
# A faux-node used to wrap an expressions body in a closure.
|
||||
Closure =
|
||||
|
||||
# Wrap the expressions body, unless it contains a pure statement,
|
||||
# in which case, no dice. If the body mentions `this` or `arguments`,
|
||||
# then make sure that the closure wrapper preserves the original values.
|
||||
wrap: (expressions, statement, noReturn) ->
|
||||
return expressions if expressions.jumps()
|
||||
func = new Code [], Block.wrap [expressions]
|
||||
args = []
|
||||
argumentsNode = expressions.contains @isLiteralArguments
|
||||
if argumentsNode and expressions.classBody
|
||||
argumentsNode.error "Class bodies shouldn't reference arguments"
|
||||
if argumentsNode or expressions.contains @isLiteralThis
|
||||
meth = new Literal if argumentsNode then 'apply' else 'call'
|
||||
args = [new Literal 'this']
|
||||
args.push new Literal 'arguments' if argumentsNode
|
||||
func = new Value func, [new Access meth]
|
||||
func.noReturn = noReturn
|
||||
call = new Call func, args
|
||||
if statement then Block.wrap [call] else call
|
||||
|
||||
isLiteralArguments: (node) ->
|
||||
node instanceof Literal and node.value is 'arguments' and not node.asKey
|
||||
|
||||
isLiteralThis: (node) ->
|
||||
(node instanceof Literal and node.value is 'this' and not node.asKey) or
|
||||
(node instanceof Code and node.bound) or
|
||||
(node instanceof Call and node.isSuper)
|
||||
|
||||
# Unfold a node's child if soak, then tuck the node under created `If`
|
||||
unfoldSoak = (o, parent, name) ->
|
||||
return unless ifn = parent[name].unfoldSoak o
|
||||
parent[name] = ifn.body
|
||||
ifn.body = new Value parent
|
||||
ifn
|
||||
|
||||
# Constants
|
||||
# ---------
|
||||
|
||||
@@ -2113,18 +2159,42 @@ UTILITIES =
|
||||
|
||||
# Correctly set up a prototype chain for inheritance, including a reference
|
||||
# to the superclass for `super()` calls, and copies of any static properties.
|
||||
extends: -> """
|
||||
function(child, parent) { for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }
|
||||
"""
|
||||
extends: -> "
|
||||
function(child, parent) {
|
||||
for (var key in parent) {
|
||||
if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key];
|
||||
}
|
||||
function ctor() {
|
||||
this.constructor = child;
|
||||
}
|
||||
ctor.prototype = parent.prototype;
|
||||
child.prototype = new ctor();
|
||||
child.__super__ = parent.prototype;
|
||||
return child;
|
||||
}
|
||||
"
|
||||
|
||||
# Create a function bound to the current value of "this".
|
||||
bind: -> '''
|
||||
function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
|
||||
'''
|
||||
bind: -> '
|
||||
function(fn, me){
|
||||
return function(){
|
||||
return fn.apply(me, arguments);
|
||||
};
|
||||
}
|
||||
'
|
||||
|
||||
# Discover if an item is in an array.
|
||||
indexOf: -> """
|
||||
[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }
|
||||
indexOf: -> "
|
||||
[].indexOf || function(item) {
|
||||
for (var i = 0, l = this.length; i < l; i++) {
|
||||
if (i in this && this[i] === item) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
"
|
||||
|
||||
modulo: -> """
|
||||
function(a, b) { return (a % b + +b) % b; }
|
||||
"""
|
||||
|
||||
# Shortcuts to speed up the lookup time for native functions.
|
||||
@@ -2146,27 +2216,27 @@ TAB = ' '
|
||||
IDENTIFIER_STR = "[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*"
|
||||
IDENTIFIER = /// ^ #{IDENTIFIER_STR} $ ///
|
||||
SIMPLENUM = /^[+-]?\d+$/
|
||||
METHOD_DEF = ///
|
||||
^
|
||||
(?:
|
||||
(#{IDENTIFIER_STR})
|
||||
\.prototype
|
||||
(?:
|
||||
\.(#{IDENTIFIER_STR})
|
||||
| \[("(?:[^\\"\r\n]|\\.)*"|'(?:[^\\'\r\n]|\\.)*')\]
|
||||
| \[(0x[\da-fA-F]+ | \d*\.?\d+ (?:[eE][+-]?\d+)?)\]
|
||||
)
|
||||
)
|
||||
|
|
||||
(#{IDENTIFIER_STR})
|
||||
$
|
||||
///
|
||||
HEXNUM = /^[+-]?0x[\da-f]+/i
|
||||
NUMBER = ///^[+-]?(?:
|
||||
0x[\da-f]+ | # hex
|
||||
\d*\.?\d+ (?:e[+-]?\d+)? # decimal
|
||||
)$///i
|
||||
|
||||
# Is a literal value a string?
|
||||
METHOD_DEF = /// ^
|
||||
(#{IDENTIFIER_STR})
|
||||
(\.prototype)?
|
||||
(?: \.(#{IDENTIFIER_STR})
|
||||
| \[("(?:[^\\"\r\n]|\\.)*"|'(?:[^\\'\r\n]|\\.)*')\]
|
||||
| \[(0x[\da-fA-F]+ | \d*\.?\d+ (?:[eE][+-]?\d+)?)\]
|
||||
)
|
||||
$ ///
|
||||
|
||||
# Is a literal value a string/regex?
|
||||
IS_STRING = /^['"]/
|
||||
IS_REGEX = /^\//
|
||||
|
||||
# Utility Functions
|
||||
# -----------------
|
||||
# Helper Functions
|
||||
# ----------------
|
||||
|
||||
# Helper for ensuring that utility functions are assigned at the top level.
|
||||
utility = (name) ->
|
||||
@@ -2177,3 +2247,28 @@ utility = (name) ->
|
||||
multident = (code, tab) ->
|
||||
code = code.replace /\n/g, '$&' + tab
|
||||
code.replace /\s+$/, ''
|
||||
|
||||
# Parse a number (+- decimal/hexadecimal)
|
||||
# Examples: 0, -1, 1, 2e3, 2e-3, -0xfe, 0xfe
|
||||
parseNum = (x) ->
|
||||
if not x?
|
||||
0
|
||||
else if x.match HEXNUM
|
||||
parseInt x, 16
|
||||
else
|
||||
parseFloat x
|
||||
|
||||
isLiteralArguments = (node) ->
|
||||
node instanceof Literal and node.value is 'arguments' and not node.asKey
|
||||
|
||||
isLiteralThis = (node) ->
|
||||
(node instanceof Literal and node.value is 'this' and not node.asKey) or
|
||||
(node instanceof Code and node.bound) or
|
||||
(node instanceof Call and node.isSuper)
|
||||
|
||||
# Unfold a node's child if soak, then tuck the node under created `If`
|
||||
unfoldSoak = (o, parent, name) ->
|
||||
return unless ifn = parent[name].unfoldSoak o
|
||||
parent[name] = ifn.body
|
||||
ifn.body = new Value parent
|
||||
ifn
|
||||
|
||||
52
src/register.coffee
Normal file
52
src/register.coffee
Normal file
@@ -0,0 +1,52 @@
|
||||
CoffeeScript = require './coffee-script'
|
||||
child_process = require 'child_process'
|
||||
helpers = require './helpers'
|
||||
path = require 'path'
|
||||
|
||||
# Load and run a CoffeeScript file for Node, stripping any `BOM`s.
|
||||
loadFile = (module, filename) ->
|
||||
answer = CoffeeScript._compileFile filename, false
|
||||
module._compile answer, filename
|
||||
|
||||
# If the installed version of Node supports `require.extensions`, register
|
||||
# CoffeeScript as an extension.
|
||||
if require.extensions
|
||||
for ext in CoffeeScript.FILE_EXTENSIONS
|
||||
require.extensions[ext] = loadFile
|
||||
|
||||
# Patch Node's module loader to be able to handle mult-dot extensions.
|
||||
# This is a horrible thing that should not be required. Perhaps, one day,
|
||||
# when a truly benevolent dictator comes to rule over the Republik of Node,
|
||||
# it won't be.
|
||||
Module = require 'module'
|
||||
|
||||
findExtension = (filename) ->
|
||||
extensions = path.basename(filename).split '.'
|
||||
# Remove the initial dot from dotfiles.
|
||||
extensions.shift() if extensions[0] is ''
|
||||
# Start with the longest possible extension and work our way shortwards.
|
||||
while extensions.shift()
|
||||
curExtension = '.' + extensions.join '.'
|
||||
return curExtension if Module._extensions[curExtension]
|
||||
'.js'
|
||||
|
||||
Module::load = (filename) ->
|
||||
@filename = filename
|
||||
@paths = Module._nodeModulePaths path.dirname filename
|
||||
extension = findExtension filename
|
||||
Module._extensions[extension](this, filename)
|
||||
@loaded = true
|
||||
|
||||
# If we're on Node, patch `child_process.fork` so that Coffee scripts are able
|
||||
# to fork both CoffeeScript files, and JavaScript files, directly.
|
||||
if child_process
|
||||
{fork} = child_process
|
||||
binary = require.resolve '../../bin/coffee'
|
||||
child_process.fork = (path, args, options) ->
|
||||
if helpers.isCoffee path
|
||||
unless Array.isArray args
|
||||
options = args or {}
|
||||
args = []
|
||||
args = [path].concat args
|
||||
path = binary
|
||||
fork path, args, options
|
||||
@@ -3,7 +3,7 @@ path = require 'path'
|
||||
vm = require 'vm'
|
||||
nodeREPL = require 'repl'
|
||||
CoffeeScript = require './coffee-script'
|
||||
{merge, prettyErrorMessage} = require './helpers'
|
||||
{merge, updateSyntaxError} = require './helpers'
|
||||
|
||||
replDefaults =
|
||||
prompt: 'coffee> ',
|
||||
@@ -27,9 +27,15 @@ replDefaults =
|
||||
new Assign (new Value new Literal '_'), ast, '='
|
||||
]
|
||||
js = ast.compile bare: yes, locals: Object.keys(context)
|
||||
cb null, vm.runInContext(js, context, filename)
|
||||
result = if context is global
|
||||
vm.runInThisContext js, filename
|
||||
else
|
||||
vm.runInContext js, context, filename
|
||||
cb null, result
|
||||
catch err
|
||||
cb prettyErrorMessage(err, filename, input, yes)
|
||||
# AST's `compile` does not add source code information to syntax errors.
|
||||
updateSyntaxError err, input
|
||||
cb err
|
||||
|
||||
addMultilineHandler = (repl) ->
|
||||
{rli, inputStream, outputStream} = repl
|
||||
@@ -125,6 +131,8 @@ module.exports =
|
||||
console.warn "Node 0.8.0+ required for CoffeeScript REPL"
|
||||
process.exit 1
|
||||
|
||||
CoffeeScript.register()
|
||||
process.argv = ['coffee'].concat process.argv[2..]
|
||||
opts = merge replDefaults, opts
|
||||
repl = nodeREPL.start opts
|
||||
repl.on 'exit', -> repl.outputStream.write '\n'
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
# parentheses, and generally clean things up.
|
||||
|
||||
# Create a generated token: one that exists due to a use of implicit syntax.
|
||||
generate = (tag, value) ->
|
||||
tok = [tag, value]
|
||||
tok.generated = yes
|
||||
tok
|
||||
generate = (tag, value, origin) ->
|
||||
tok = [tag, value]
|
||||
tok.generated = yes
|
||||
tok.origin = origin if origin
|
||||
tok
|
||||
|
||||
# The **Rewriter** class is used by the [Lexer](lexer.html), directly against
|
||||
# its internal array of tokens.
|
||||
@@ -26,10 +27,9 @@ class exports.Rewriter
|
||||
# corrected before implicit parentheses can be wrapped around blocks of code.
|
||||
rewrite: (@tokens) ->
|
||||
@removeLeadingNewlines()
|
||||
@removeMidExpressionNewlines()
|
||||
@closeOpenCalls()
|
||||
@closeOpenIndexes()
|
||||
@addImplicitIndentation()
|
||||
@normalizeLines()
|
||||
@tagPostfixConditionals()
|
||||
@addImplicitBracesAndParens()
|
||||
@addLocationDataToGeneratedTokens()
|
||||
@@ -65,14 +65,6 @@ class exports.Rewriter
|
||||
break for [tag], i in @tokens when tag isnt 'TERMINATOR'
|
||||
@tokens.splice 0, i if i
|
||||
|
||||
# Some blocks occur in the middle of expressions -- when we're expecting
|
||||
# this, remove their trailing newlines.
|
||||
removeMidExpressionNewlines: ->
|
||||
@scanTokens (token, i, tokens) ->
|
||||
return 1 unless token[0] is 'TERMINATOR' and @tag(i + 1) in EXPRESSION_CLOSE
|
||||
tokens.splice i, 1
|
||||
0
|
||||
|
||||
# The lexer has tagged the opening parenthesis of a method call. Match it with
|
||||
# its paired close. We have the mis-nested outdent case included here for
|
||||
# calls that close on the same line, just before their outdent.
|
||||
@@ -140,7 +132,7 @@ class exports.Rewriter
|
||||
|
||||
@scanTokens (token, i, tokens) ->
|
||||
[tag] = token
|
||||
[prevTag] = if i > 0 then tokens[i - 1] else []
|
||||
[prevTag] = prevToken = if i > 0 then tokens[i - 1] else []
|
||||
[nextTag] = if i < tokens.length - 1 then tokens[i + 1] else []
|
||||
stackTop = -> stack[stack.length - 1]
|
||||
startIdx = i
|
||||
@@ -171,13 +163,13 @@ class exports.Rewriter
|
||||
startImplicitObject = (j, startsLine = yes) ->
|
||||
idx = j ? i
|
||||
stack.push ['{', idx, sameLine: yes, startsLine: startsLine, ours: yes]
|
||||
tokens.splice idx, 0, generate '{', generate(new String('{'))
|
||||
tokens.splice idx, 0, generate '{', generate(new String('{')), token
|
||||
i += 1 if not j?
|
||||
|
||||
endImplicitObject = (j) ->
|
||||
j = j ? i
|
||||
stack.pop()
|
||||
tokens.splice j, 0, generate '}', '}'
|
||||
tokens.splice j, 0, generate '}', '}', token
|
||||
i += 1
|
||||
|
||||
# Don't end an implicit call on next indent if any of these are in an argument
|
||||
@@ -264,6 +256,9 @@ class exports.Rewriter
|
||||
if @tag(i - 2) is '@' then s = i - 2 else s = i - 1
|
||||
s -= 2 while @tag(s - 2) is 'HERECOMMENT'
|
||||
|
||||
# Mark if the value is a for loop
|
||||
@insideForDeclaration = nextTag is 'FOR'
|
||||
|
||||
startsLine = s is 0 or @tag(s - 1) in LINEBREAKS or tokens[s - 1].newLine
|
||||
# Are we just continuing an already declared object?
|
||||
if stackTop()
|
||||
@@ -284,13 +279,16 @@ class exports.Rewriter
|
||||
# c
|
||||
# .h a
|
||||
#
|
||||
if prevTag is 'OUTDENT' and inImplicitCall() and tag in ['.', '?.', '::', '?::']
|
||||
endImplicitCall()
|
||||
return forward(1)
|
||||
# and also
|
||||
#
|
||||
# f a
|
||||
# .g b
|
||||
# .h a
|
||||
|
||||
stackTop()[2].sameLine = no if inImplicitObject() and tag in LINEBREAKS
|
||||
|
||||
if tag in IMPLICIT_END
|
||||
newLine = prevTag is 'OUTDENT' or prevToken.newLine
|
||||
if tag in IMPLICIT_END or tag in CALL_CLOSERS and newLine
|
||||
while inImplicit()
|
||||
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()
|
||||
# Close implicit calls when reached end of argument list
|
||||
@@ -298,7 +296,8 @@ class exports.Rewriter
|
||||
endImplicitCall()
|
||||
# Close implicit objects such as:
|
||||
# return a: 1, b: 2 unless true
|
||||
else if inImplicitObject() and sameLine and not startsLine
|
||||
else if inImplicitObject() and not @insideForDeclaration and sameLine and
|
||||
tag isnt 'TERMINATOR' and prevTag isnt ':' and
|
||||
endImplicitObject()
|
||||
# Close implicit objects when at end of line, line didn't end with a comma
|
||||
# and the implicit object didn't start the line or the next line doesn't look like
|
||||
@@ -323,6 +322,7 @@ class exports.Rewriter
|
||||
# f a, b: c, d: e, f, g: h: i, j
|
||||
#
|
||||
if tag is ',' and not @looksObjectish(i + 1) and inImplicitObject() and
|
||||
not @insideForDeclaration and
|
||||
(nextTag isnt 'TERMINATOR' or not @looksObjectish(i + 2))
|
||||
# When nextTag is OUTDENT the comma is insignificant and
|
||||
# should just be ignored so embed it in the implicit object.
|
||||
@@ -341,11 +341,11 @@ class exports.Rewriter
|
||||
return 1 if token[2]
|
||||
return 1 unless token.generated or token.explicit
|
||||
if token[0] is '{' and nextLocation=tokens[i + 1]?[2]
|
||||
{first_line: line, first_column: column} = nextLocation
|
||||
{first_line: line, first_column: column} = nextLocation
|
||||
else if prevLocation = tokens[i - 1]?[2]
|
||||
{last_line: line, last_column: column} = prevLocation
|
||||
{last_line: line, last_column: column} = prevLocation
|
||||
else
|
||||
line = column = 0
|
||||
line = column = 0
|
||||
token[2] =
|
||||
first_line: line
|
||||
first_column: column
|
||||
@@ -355,27 +355,31 @@ class exports.Rewriter
|
||||
|
||||
# Because our grammar is LALR(1), it can't handle some single-line
|
||||
# expressions that lack ending delimiters. The **Rewriter** adds the implicit
|
||||
# blocks, so it doesn't need to. ')' can close a single-line block,
|
||||
# but we need to make sure it's balanced.
|
||||
addImplicitIndentation: ->
|
||||
# blocks, so it doesn't need to. To keep the grammar clean and tidy, trailing
|
||||
# newlines within expressions are removed and the indentation tokens of empty
|
||||
# blocks are added.
|
||||
normalizeLines: ->
|
||||
starter = indent = outdent = null
|
||||
|
||||
condition = (token, i) ->
|
||||
token[1] isnt ';' and token[0] in SINGLE_CLOSERS and
|
||||
not (token[0] is 'TERMINATOR' and @tag(i + 1) in EXPRESSION_CLOSE) and
|
||||
not (token[0] is 'ELSE' and starter isnt 'THEN') and
|
||||
not (token[0] in ['CATCH', 'FINALLY'] and starter in ['->', '=>'])
|
||||
not (token[0] in ['CATCH', 'FINALLY'] and starter in ['->', '=>']) or
|
||||
token[0] in CALL_CLOSERS and @tokens[i - 1].newLine
|
||||
|
||||
action = (token, i) ->
|
||||
@tokens.splice (if @tag(i - 1) is ',' then i - 1 else i), 0, outdent
|
||||
|
||||
@scanTokens (token, i, tokens) ->
|
||||
[tag] = token
|
||||
if tag is 'TERMINATOR' and @tag(i + 1) is 'THEN'
|
||||
tokens.splice i, 1
|
||||
return 0
|
||||
if tag is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
|
||||
tokens.splice i, 0, @indentation()...
|
||||
return 2
|
||||
if tag is 'TERMINATOR'
|
||||
if @tag(i + 1) is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
|
||||
tokens.splice i, 1, @indentation()...
|
||||
return 1
|
||||
if @tag(i + 1) in EXPRESSION_CLOSE
|
||||
tokens.splice i, 1
|
||||
return 0
|
||||
if tag is 'CATCH'
|
||||
for j in [1..2] when @tag(i + j) in ['OUTDENT', 'TERMINATOR', 'FINALLY']
|
||||
tokens.splice i + j, 0, @indentation()...
|
||||
@@ -383,7 +387,7 @@ class exports.Rewriter
|
||||
if tag in SINGLE_LINERS and @tag(i + 1) isnt 'INDENT' and
|
||||
not (tag is 'ELSE' and @tag(i + 1) is 'IF')
|
||||
starter = tag
|
||||
[indent, outdent] = @indentation yes
|
||||
[indent, outdent] = @indentation tokens[i]
|
||||
indent.fromThen = true if starter is 'THEN'
|
||||
tokens.splice i + 1, 0, indent
|
||||
@detectEnd i + 2, condition, action
|
||||
@@ -413,11 +417,14 @@ class exports.Rewriter
|
||||
return 1
|
||||
|
||||
# Generate the indentation tokens, based on another token on the same line.
|
||||
indentation: (implicit = no) ->
|
||||
indentation: (origin) ->
|
||||
indent = ['INDENT', 2]
|
||||
outdent = ['OUTDENT', 2]
|
||||
indent.generated = outdent.generated = yes if implicit
|
||||
indent.explicit = outdent.explicit = yes if not implicit
|
||||
if origin
|
||||
indent.generated = outdent.generated = yes
|
||||
indent.origin = outdent.origin = origin
|
||||
else
|
||||
indent.explicit = outdent.explicit = yes
|
||||
[indent, outdent]
|
||||
|
||||
generate: generate
|
||||
@@ -452,7 +459,7 @@ for [left, rite] in BALANCED_PAIRS
|
||||
EXPRESSION_END .push INVERSES[left] = rite
|
||||
|
||||
# Tokens that indicate the close of a clause of an expression.
|
||||
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END
|
||||
EXPRESSION_CLOSE = ['CATCH', 'THEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END
|
||||
|
||||
# Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation.
|
||||
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS']
|
||||
@@ -460,8 +467,8 @@ IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@
|
||||
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
||||
IMPLICIT_CALL = [
|
||||
'IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS'
|
||||
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'SUPER'
|
||||
'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'
|
||||
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY',
|
||||
'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'
|
||||
]
|
||||
|
||||
IMPLICIT_UNSPACED_CALL = ['+', '-']
|
||||
@@ -477,3 +484,6 @@ SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADIN
|
||||
|
||||
# Tokens that end a line.
|
||||
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT']
|
||||
|
||||
# Tokens that close open calls when they follow a newline.
|
||||
CALL_CLOSERS = ['.', '?.', '::', '?::']
|
||||
|
||||
@@ -81,6 +81,9 @@ test "compound assignment should be careful about caching variables", ->
|
||||
eq 5, base.five
|
||||
eq 5, count
|
||||
|
||||
eq 5, base().five ?= 6
|
||||
eq 6, count
|
||||
|
||||
test "compound assignment with implicit objects", ->
|
||||
obj = undefined
|
||||
obj ?=
|
||||
@@ -265,6 +268,22 @@ test "#2055: destructuring assignment with `new`", ->
|
||||
{length} = new Array
|
||||
eq 0, length
|
||||
|
||||
test "#156: destructuring with expansion", ->
|
||||
array = [1..5]
|
||||
[first, ..., last] = array
|
||||
eq 1, first
|
||||
eq 5, last
|
||||
[..., lastButOne, last] = array
|
||||
eq 4, lastButOne
|
||||
eq 5, last
|
||||
[first, second, ..., last] = array
|
||||
eq 2, second
|
||||
[..., last] = 'strings as well -> x'
|
||||
eq 'x', last
|
||||
throws (-> CoffeeScript.compile "[1, ..., 3]"), null, "prohibit expansion outside of assignment"
|
||||
throws (-> CoffeeScript.compile "[..., a, b...] = c"), null, "prohibit expansion and a splat"
|
||||
throws (-> CoffeeScript.compile "[...] = c"), null, "prohibit lone expansion"
|
||||
|
||||
|
||||
# Existential Assignment
|
||||
|
||||
@@ -380,3 +399,9 @@ test "#2613: parens on LHS of destructuring", ->
|
||||
a = {}
|
||||
[(a).b] = [1, 2, 3]
|
||||
eq a.b, 1
|
||||
|
||||
test "#2181: conditional assignment as a subexpression", ->
|
||||
a = false
|
||||
false && a or= true
|
||||
eq false, a
|
||||
eq false, not a or= true
|
||||
|
||||
@@ -699,6 +699,15 @@ test "#2052: classes should work in strict mode", ->
|
||||
catch e
|
||||
ok no
|
||||
|
||||
test "directives in class with extends ", ->
|
||||
strictTest = """
|
||||
class extends Object
|
||||
### comment ###
|
||||
'use strict'
|
||||
do -> eq this, undefined
|
||||
"""
|
||||
CoffeeScript.run strictTest, bare: yes
|
||||
|
||||
test "#2630: class bodies can't reference arguments", ->
|
||||
throws ->
|
||||
CoffeeScript.compile('class Test then arguments')
|
||||
@@ -791,3 +800,31 @@ test "#2796: ditto, ditto, ditto", ->
|
||||
|
||||
new Base
|
||||
eq answer, 'right!'
|
||||
|
||||
test "#3063: Class bodies cannot contain pure statements", ->
|
||||
throws -> CoffeeScript.compile """
|
||||
class extends S
|
||||
return if S.f
|
||||
@f: => this
|
||||
"""
|
||||
|
||||
test "#2949: super in static method with reserved name", ->
|
||||
class Foo
|
||||
@static: -> 'baz'
|
||||
|
||||
class Bar extends Foo
|
||||
@static: -> super
|
||||
|
||||
eq Bar.static(), 'baz'
|
||||
|
||||
test "#3232: super in static methods (not object-assigned)", ->
|
||||
class Foo
|
||||
@baz = -> true
|
||||
@qux = -> true
|
||||
|
||||
class Bar extends Foo
|
||||
@baz = -> super
|
||||
Bar.qux = -> super
|
||||
|
||||
ok Bar.baz()
|
||||
ok Bar.qux()
|
||||
|
||||
@@ -211,3 +211,191 @@ test "#2916: block comment before implicit call with implicit object", ->
|
||||
### ###
|
||||
fn
|
||||
a: yes
|
||||
|
||||
test "#3132: Format single-line block comment nicely", ->
|
||||
input = """
|
||||
### Single-line block comment without additional space here => ###"""
|
||||
|
||||
result = """
|
||||
|
||||
/* Single-line block comment without additional space here => */
|
||||
|
||||
|
||||
"""
|
||||
eq CoffeeScript.compile(input, bare: on), result
|
||||
|
||||
test "#3132: Format multi-line block comment nicely", ->
|
||||
input = """
|
||||
###
|
||||
# Multi-line
|
||||
# block
|
||||
# comment
|
||||
###"""
|
||||
|
||||
result = """
|
||||
|
||||
/*
|
||||
* Multi-line
|
||||
* block
|
||||
* comment
|
||||
*/
|
||||
|
||||
|
||||
"""
|
||||
eq CoffeeScript.compile(input, bare: on), result
|
||||
|
||||
test "#3132: Format simple block comment nicely", ->
|
||||
input = """
|
||||
###
|
||||
No
|
||||
Preceding hash
|
||||
###"""
|
||||
|
||||
result = """
|
||||
|
||||
/*
|
||||
No
|
||||
Preceding hash
|
||||
*/
|
||||
|
||||
|
||||
"""
|
||||
|
||||
eq CoffeeScript.compile(input, bare: on), result
|
||||
|
||||
test "#3132: Format indented block-comment nicely", ->
|
||||
input = """
|
||||
fn = () ->
|
||||
###
|
||||
# Indented
|
||||
Multiline
|
||||
###
|
||||
1"""
|
||||
|
||||
result = """
|
||||
var fn;
|
||||
|
||||
fn = function() {
|
||||
|
||||
/*
|
||||
* Indented
|
||||
Multiline
|
||||
*/
|
||||
return 1;
|
||||
};
|
||||
|
||||
"""
|
||||
eq CoffeeScript.compile(input, bare: on), result
|
||||
|
||||
# Although adequately working, block comment-placement is not yet perfect.
|
||||
# (Considering a case where multiple variables have been declared …)
|
||||
test "#3132: Format jsdoc-style block-comment nicely", ->
|
||||
input = """
|
||||
###*
|
||||
# Multiline for jsdoc-"@doctags"
|
||||
#
|
||||
# @type {Function}
|
||||
###
|
||||
fn = () -> 1
|
||||
"""
|
||||
|
||||
result = """
|
||||
|
||||
/**
|
||||
* Multiline for jsdoc-"@doctags"
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
var fn;
|
||||
|
||||
fn = function() {
|
||||
return 1;
|
||||
};
|
||||
|
||||
"""
|
||||
eq CoffeeScript.compile(input, bare: on), result
|
||||
|
||||
# Although adequately working, block comment-placement is not yet perfect.
|
||||
# (Considering a case where multiple variables have been declared …)
|
||||
test "#3132: Format hand-made (raw) jsdoc-style block-comment nicely", ->
|
||||
input = """
|
||||
###*
|
||||
* Multiline for jsdoc-"@doctags"
|
||||
*
|
||||
* @type {Function}
|
||||
###
|
||||
fn = () -> 1
|
||||
"""
|
||||
|
||||
result = """
|
||||
|
||||
/**
|
||||
* Multiline for jsdoc-"@doctags"
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
var fn;
|
||||
|
||||
fn = function() {
|
||||
return 1;
|
||||
};
|
||||
|
||||
"""
|
||||
eq CoffeeScript.compile(input, bare: on), result
|
||||
|
||||
# Although adequately working, block comment-placement is not yet perfect.
|
||||
# (Considering a case where multiple variables have been declared …)
|
||||
test "#3132: Place block-comments nicely", ->
|
||||
input = """
|
||||
###*
|
||||
# A dummy class definition
|
||||
#
|
||||
# @class
|
||||
###
|
||||
class DummyClass
|
||||
|
||||
###*
|
||||
# @constructor
|
||||
###
|
||||
constructor: ->
|
||||
|
||||
###*
|
||||
# Singleton reference
|
||||
#
|
||||
# @type {DummyClass}
|
||||
###
|
||||
@instance = new DummyClass()
|
||||
|
||||
"""
|
||||
|
||||
result = """
|
||||
|
||||
/**
|
||||
* A dummy class definition
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
var DummyClass;
|
||||
|
||||
DummyClass = (function() {
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function DummyClass() {}
|
||||
|
||||
|
||||
/**
|
||||
* Singleton reference
|
||||
*
|
||||
* @type {DummyClass}
|
||||
*/
|
||||
|
||||
DummyClass.instance = new DummyClass();
|
||||
|
||||
return DummyClass;
|
||||
|
||||
})();
|
||||
|
||||
"""
|
||||
eq CoffeeScript.compile(input, bare: on), result
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user