coffee-script 0.3.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,50 +0,0 @@
1
- (function(){
2
- var compiler, path;
3
- // Executes the `coffee` Ruby program to convert from CoffeeScript to JavaScript.
4
- path = require('path');
5
- // The path to the CoffeeScript executable.
6
- compiler = path.normalize(path.dirname(__filename) + '/../../bin/coffee');
7
- // Compile a string over stdin, with global variables, for the REPL.
8
- exports.compile = function compile(code, callback) {
9
- var coffee, js;
10
- js = '';
11
- coffee = process.createChildProcess(compiler, ['--eval', '--no-wrap', '--globals']);
12
- coffee.addListener('output', function(results) {
13
- if ((typeof results !== "undefined" && results !== null)) {
14
- return js += results;
15
- }
16
- });
17
- coffee.addListener('exit', function() {
18
- return callback(js);
19
- });
20
- coffee.write(code);
21
- return coffee.close();
22
- };
23
- // Compile a list of CoffeeScript files on disk.
24
- exports.compile_files = function compile_files(paths, callback) {
25
- var coffee, exit_ran, js;
26
- js = '';
27
- coffee = process.createChildProcess(compiler, ['--print'].concat(paths));
28
- coffee.addListener('output', function(results) {
29
- if ((typeof results !== "undefined" && results !== null)) {
30
- return js += results;
31
- }
32
- });
33
- // NB: we have to add a mutex to make sure it doesn't get called twice.
34
- exit_ran = false;
35
- coffee.addListener('exit', function() {
36
- if (exit_ran) {
37
- return null;
38
- }
39
- exit_ran = true;
40
- return callback(js);
41
- });
42
- return coffee.addListener('error', function(message) {
43
- if (!(message)) {
44
- return null;
45
- }
46
- puts(message);
47
- throw new Error("CoffeeScript compile error");
48
- });
49
- };
50
- })();
@@ -1,235 +0,0 @@
1
- require 'optparse'
2
- require 'fileutils'
3
- require 'open3'
4
- begin
5
- require File.expand_path(File.dirname(__FILE__) + '/../coffee-script')
6
- rescue LoadError => e
7
- puts(e.message)
8
- puts("use \"rake build:parser\" to regenerate parser.rb")
9
- exit(1)
10
- end
11
-
12
- module CoffeeScript
13
-
14
- # The CommandLine handles all of the functionality of the `coffee`
15
- # utility.
16
- class CommandLine
17
-
18
- BANNER = <<-EOS
19
- coffee compiles CoffeeScript source files into JavaScript.
20
-
21
- Usage:
22
- coffee path/to/script.coffee
23
- EOS
24
-
25
- # Seconds to pause between checks for changed source files.
26
- WATCH_INTERVAL = 0.5
27
-
28
- # Path to the root of the CoffeeScript install.
29
- ROOT = File.expand_path(File.dirname(__FILE__) + '/../..')
30
-
31
- # Commands to execute CoffeeScripts.
32
- RUNNERS = {
33
- :node => "node #{ROOT}/lib/coffee_script/runner.js",
34
- :narwhal => "narwhal -p #{ROOT} -e 'require(\"coffee-script\").run(system.args);'"
35
- }
36
-
37
- # Run the CommandLine off the contents of ARGV.
38
- def initialize
39
- @mtimes = {}
40
- parse_options
41
- return launch_repl if @options[:interactive]
42
- return eval_scriptlet if @options[:eval]
43
- check_sources
44
- return run_scripts if @options[:run]
45
- @sources.each {|source| compile_javascript(source) }
46
- watch_coffee_scripts if @options[:watch]
47
- end
48
-
49
- # The "--help" usage message.
50
- def usage
51
- puts "\n#{@option_parser}\n"
52
- exit
53
- end
54
-
55
-
56
- private
57
-
58
- # Compiles (or partially compiles) the source CoffeeScript file, returning
59
- # the desired JS, tokens, or lint results.
60
- def compile_javascript(source)
61
- script = File.read(source)
62
- return tokens(script) if @options[:tokens]
63
- js = compile(script, source)
64
- return unless js
65
- return puts(js) if @options[:print]
66
- return lint(js) if @options[:lint]
67
- File.open(path_for(source), 'w+') {|f| f.write(js) }
68
- end
69
-
70
- # Spins up a watcher thread to keep track of the modification times of the
71
- # source files, recompiling them whenever they're saved.
72
- def watch_coffee_scripts
73
- watch_thread = Thread.start do
74
- loop do
75
- @sources.each do |source|
76
- mtime = File.stat(source).mtime
77
- @mtimes[source] ||= mtime
78
- if mtime > @mtimes[source]
79
- @mtimes[source] = mtime
80
- compile_javascript(source)
81
- end
82
- end
83
- sleep WATCH_INTERVAL
84
- end
85
- end
86
- Signal.trap("INT") { watch_thread.kill }
87
- watch_thread.join
88
- end
89
-
90
- # Ensure that all of the source files exist.
91
- def check_sources
92
- usage if @sources.empty?
93
- missing = @sources.detect {|s| !File.exists?(s) }
94
- if missing
95
- STDERR.puts("File not found: '#{missing}'")
96
- exit(1)
97
- end
98
- end
99
-
100
- # Pipe compiled JS through JSLint (requires a working 'jsl' command).
101
- def lint(js)
102
- stdin, stdout, stderr = Open3.popen3('jsl -nologo -stdin')
103
- stdin.write(js)
104
- stdin.close
105
- puts stdout.read.tr("\n", '')
106
- errs = stderr.read.chomp
107
- puts errs unless errs.empty?
108
- stdout.close and stderr.close
109
- end
110
-
111
- # Eval a little piece of CoffeeScript directly from the command line.
112
- def eval_scriptlet
113
- script = STDIN.tty? ? @sources.join(' ') : STDIN.read
114
- return tokens(script) if @options[:tokens]
115
- js = compile(script)
116
- return lint(js) if @options[:lint]
117
- puts js
118
- end
119
-
120
- # Use Node.js or Narwhal to run an interactive CoffeeScript session.
121
- def launch_repl
122
- exec "#{RUNNERS[@options[:runner]]}"
123
- rescue Errno::ENOENT
124
- puts "Error: #{@options[:runner]} must be installed to use the interactive REPL."
125
- exit(1)
126
- end
127
-
128
- # Use Node.js or Narwhal to compile and execute CoffeeScripts.
129
- def run_scripts
130
- sources = @sources.join(' ')
131
- exec "#{RUNNERS[@options[:runner]]} #{sources}"
132
- rescue Errno::ENOENT
133
- puts "Error: #{@options[:runner]} must be installed in order to execute scripts."
134
- exit(1)
135
- end
136
-
137
- # Print the tokens that the lexer generates from a source script.
138
- def tokens(script)
139
- puts Lexer.new.tokenize(script).inspect
140
- end
141
-
142
- # Compile a single source file to JavaScript.
143
- def compile(script, source='error')
144
- begin
145
- options = {}
146
- options[:no_wrap] = true if @options[:no_wrap]
147
- options[:globals] = true if @options[:globals]
148
- CoffeeScript.compile(script, options)
149
- rescue CoffeeScript::ParseError => e
150
- STDERR.puts "#{source}: #{e.message}"
151
- exit(1) unless @options[:watch]
152
- nil
153
- end
154
- end
155
-
156
- # Write out JavaScript alongside CoffeeScript unless an output directory
157
- # is specified.
158
- def path_for(source)
159
- filename = File.basename(source, File.extname(source)) + '.js'
160
- dir = @options[:output] || File.dirname(source)
161
- File.join(dir, filename)
162
- end
163
-
164
- # Install the CoffeeScript TextMate bundle to ~/Library.
165
- def install_bundle
166
- bundle_dir = File.expand_path('~/Library/Application Support/TextMate/Bundles/')
167
- FileUtils.cp_r("#{ROOT}/extras/CoffeeScript.tmbundle", bundle_dir)
168
- end
169
-
170
- # Use OptionParser for all the options.
171
- def parse_options
172
- @options = {:runner => :node}
173
- @option_parser = OptionParser.new do |opts|
174
- opts.on('-i', '--interactive', 'run an interactive CoffeeScript REPL') do |i|
175
- @options[:interactive] = true
176
- end
177
- opts.on('-r', '--run', 'compile and run a CoffeeScript') do |r|
178
- @options[:run] = true
179
- end
180
- opts.on('-o', '--output [DIR]', 'set the directory for compiled JavaScript') do |d|
181
- @options[:output] = d
182
- FileUtils.mkdir_p(d) unless File.exists?(d)
183
- end
184
- opts.on('-w', '--watch', 'watch scripts for changes, and recompile') do |w|
185
- @options[:watch] = true
186
- end
187
- opts.on('-p', '--print', 'print the compiled JavaScript to stdout') do |d|
188
- @options[:print] = true
189
- end
190
- opts.on('-l', '--lint', 'pipe the compiled JavaScript through JSLint') do |l|
191
- @options[:lint] = true
192
- end
193
- opts.on('-e', '--eval', 'compile a cli scriptlet or read from stdin') do |e|
194
- @options[:eval] = true
195
- end
196
- opts.on('-t', '--tokens', 'print the tokens that the lexer produces') do |t|
197
- @options[:tokens] = true
198
- end
199
- opts.on('-v', '--verbose', 'print at every step of code generation') do |v|
200
- ENV['VERBOSE'] = 'true'
201
- end
202
- opts.on('-n', '--no-wrap', 'raw output, no function safety wrapper') do |n|
203
- @options[:no_wrap] = true
204
- end
205
- opts.on('-g', '--globals', 'attach all top-level variable as globals') do |n|
206
- @options[:globals] = true
207
- end
208
- opts.on_tail('--narwhal', 'use Narwhal instead of Node.js') do |n|
209
- @options[:runner] = :narwhal
210
- end
211
- opts.on_tail('--install-bundle', 'install the CoffeeScript TextMate bundle') do |i|
212
- install_bundle
213
- exit
214
- end
215
- opts.on_tail('--version', 'display CoffeeScript version') do
216
- puts "CoffeeScript version #{CoffeeScript::VERSION}"
217
- exit
218
- end
219
- opts.on_tail('-h', '--help', 'display this help message') do
220
- usage
221
- end
222
- end
223
- @option_parser.banner = BANNER
224
- begin
225
- @option_parser.parse!(ARGV)
226
- rescue OptionParser::InvalidOption => e
227
- puts e.message
228
- exit(1)
229
- end
230
- @sources = ARGV
231
- end
232
-
233
- end
234
-
235
- end
@@ -1,481 +0,0 @@
1
- class Parser
2
-
3
- # Declare terminal tokens produced by the lexer.
4
- token IF ELSE UNLESS
5
- token NUMBER STRING REGEX
6
- token TRUE FALSE YES NO ON OFF
7
- token IDENTIFIER PROPERTY_ACCESS PROTOTYPE_ACCESS SOAK_ACCESS
8
- token CODE PARAM_START PARAM PARAM_END NEW RETURN
9
- token CALL_START CALL_END INDEX_START INDEX_END
10
- token TRY CATCH FINALLY THROW
11
- token BREAK CONTINUE
12
- token FOR IN OF BY WHEN WHILE
13
- token SWITCH LEADING_WHEN
14
- token DELETE INSTANCEOF TYPEOF
15
- token SUPER EXTENDS
16
- token ASSIGN RETURN
17
- token NEWLINE
18
- token COMMENT
19
- token JS
20
- token INDENT OUTDENT
21
-
22
- # Declare order of operations.
23
- prechigh
24
- nonassoc UMINUS UPLUS NOT '!' '!!' '~' '++' '--'
25
- left '*' '/' '%' '?' '.'
26
- left '+' '-'
27
- left '<<' '>>' '>>>' '&' '|' '^'
28
- left '<=' '<' '>' '>='
29
- right '==' '!=' IS ISNT
30
- left '&&' '||' AND OR
31
- right '-=' '+=' '/=' '*=' '%=' '||=' '&&=' '?='
32
- right DELETE INSTANCEOF TYPEOF
33
- right INDENT
34
- left OUTDENT
35
- right WHEN LEADING_WHEN IN OF BY
36
- right THROW FOR NEW SUPER
37
- left EXTENDS
38
- right ASSIGN RETURN
39
- right '->' '=>' UNLESS IF ELSE WHILE
40
- preclow
41
-
42
- rule
43
-
44
- # All parsing will end in this rule, being the trunk of the AST.
45
- Root:
46
- /* nothing */ { result = Expressions.new }
47
- | Terminator { result = Expressions.new }
48
- | Expressions { result = val[0] }
49
- | Block Terminator { result = val[0] }
50
- ;
51
-
52
- # Any list of expressions or method body, seperated by line breaks or semis.
53
- Expressions:
54
- Expression { result = Expressions.wrap(val) }
55
- | Expressions Terminator Expression { result = val[0] << val[2] }
56
- | Expressions Terminator { result = val[0] }
57
- ;
58
-
59
- # All types of expressions in our language. The basic unit of CoffeeScript
60
- # is the expression.
61
- Expression:
62
- Value
63
- | Call
64
- | Code
65
- | Operation
66
- | Assign
67
- | If
68
- | Try
69
- | Throw
70
- | Return
71
- | While
72
- | For
73
- | Switch
74
- | Extends
75
- | Splat
76
- | Existence
77
- | Comment
78
- ;
79
-
80
- # A block of expressions. Note that the Rewriter will convert some postfix
81
- # forms into blocks for us, by altering the token stream.
82
- Block:
83
- INDENT Expressions OUTDENT { result = val[1] }
84
- | INDENT OUTDENT { result = Expressions.new }
85
- ;
86
-
87
- # Tokens that can terminate an expression.
88
- Terminator:
89
- "\n"
90
- | ";"
91
- ;
92
-
93
- # All hard-coded values. These can be printed straight to JavaScript.
94
- Literal:
95
- NUMBER { result = LiteralNode.new(val[0]) }
96
- | STRING { result = LiteralNode.new(val[0]) }
97
- | JS { result = LiteralNode.new(val[0]) }
98
- | REGEX { result = LiteralNode.new(val[0]) }
99
- | BREAK { result = LiteralNode.new(val[0]) }
100
- | CONTINUE { result = LiteralNode.new(val[0]) }
101
- | TRUE { result = LiteralNode.new(Value.new(true)) }
102
- | FALSE { result = LiteralNode.new(Value.new(false)) }
103
- | YES { result = LiteralNode.new(Value.new(true)) }
104
- | NO { result = LiteralNode.new(Value.new(false)) }
105
- | ON { result = LiteralNode.new(Value.new(true)) }
106
- | OFF { result = LiteralNode.new(Value.new(false)) }
107
- ;
108
-
109
- # Assignment to a variable (or index).
110
- Assign:
111
- Value ASSIGN Expression { result = AssignNode.new(val[0], val[2]) }
112
- ;
113
-
114
- # Assignment within an object literal (can be quoted).
115
- AssignObj:
116
- IDENTIFIER ASSIGN Expression { result = AssignNode.new(ValueNode.new(val[0]), val[2], :object) }
117
- | STRING ASSIGN Expression { result = AssignNode.new(ValueNode.new(LiteralNode.new(val[0])), val[2], :object) }
118
- | Comment { result = val[0] }
119
- ;
120
-
121
- # A return statement.
122
- Return:
123
- RETURN Expression { result = ReturnNode.new(val[1]) }
124
- | RETURN { result = ReturnNode.new(ValueNode.new(Value.new('null'))) }
125
- ;
126
-
127
- # A comment.
128
- Comment:
129
- COMMENT { result = CommentNode.new(val[0]) }
130
- ;
131
-
132
- # Arithmetic and logical operators
133
- # For Ruby's Operator precedence, see:
134
- # https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
135
- Operation:
136
- '!' Expression { result = OpNode.new(val[0], val[1]) }
137
- | '!!' Expression { result = OpNode.new(val[0], val[1]) }
138
- | '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }
139
- | '+' Expression = UPLUS { result = OpNode.new(val[0], val[1]) }
140
- | NOT Expression { result = OpNode.new(val[0], val[1]) }
141
- | '~' Expression { result = OpNode.new(val[0], val[1]) }
142
- | '--' Expression { result = OpNode.new(val[0], val[1]) }
143
- | '++' Expression { result = OpNode.new(val[0], val[1]) }
144
- | DELETE Expression { result = OpNode.new(val[0], val[1]) }
145
- | TYPEOF Expression { result = OpNode.new(val[0], val[1]) }
146
- | Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
147
- | Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
148
-
149
- | Expression '*' Expression { result = OpNode.new(val[1], val[0], val[2]) }
150
- | Expression '/' Expression { result = OpNode.new(val[1], val[0], val[2]) }
151
- | Expression '%' Expression { result = OpNode.new(val[1], val[0], val[2]) }
152
-
153
- | Expression '+' Expression { result = OpNode.new(val[1], val[0], val[2]) }
154
- | Expression '-' Expression { result = OpNode.new(val[1], val[0], val[2]) }
155
-
156
- | Expression '<<' Expression { result = OpNode.new(val[1], val[0], val[2]) }
157
- | Expression '>>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
158
- | Expression '>>>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
159
-
160
- | Expression '&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
161
- | Expression '|' Expression { result = OpNode.new(val[1], val[0], val[2]) }
162
- | Expression '^' Expression { result = OpNode.new(val[1], val[0], val[2]) }
163
-
164
- | Expression '<=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
165
- | Expression '<' Expression { result = OpNode.new(val[1], val[0], val[2]) }
166
- | Expression '>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
167
- | Expression '>=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
168
-
169
- | Expression '==' Expression { result = OpNode.new(val[1], val[0], val[2]) }
170
- | Expression '!=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
171
- | Expression IS Expression { result = OpNode.new(val[1], val[0], val[2]) }
172
- | Expression ISNT Expression { result = OpNode.new(val[1], val[0], val[2]) }
173
-
174
- | Expression '&&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
175
- | Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }
176
- | Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) }
177
- | Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) }
178
- | Expression '?' Expression { result = OpNode.new(val[1], val[0], val[2]) }
179
-
180
- | Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
181
- | Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
182
- | Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
183
- | Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
184
- | Expression '%=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
185
- | Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
186
- | Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
187
- | Expression '?=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
188
-
189
- | Expression INSTANCEOF Expression { result = OpNode.new(val[1], val[0], val[2]) }
190
- | Expression IN Expression { result = OpNode.new(val[1], val[0], val[2]) }
191
- ;
192
-
193
- # The existence operator.
194
- Existence:
195
- Expression '?' { result = ExistenceNode.new(val[0]) }
196
- ;
197
-
198
- # Function definition.
199
- Code:
200
- PARAM_START ParamList PARAM_END
201
- FuncGlyph Block { result = CodeNode.new(val[1], val[4], val[3]) }
202
- | FuncGlyph Block { result = CodeNode.new([], val[1], val[0]) }
203
- ;
204
-
205
- # The symbols to signify functions, and bound functions.
206
- FuncGlyph:
207
- '->' { result = :func }
208
- | '=>' { result = :boundfunc }
209
- ;
210
-
211
- # The parameters to a function definition.
212
- ParamList:
213
- Param { result = val }
214
- | ParamList "," Param { result = val[0] << val[2] }
215
- ;
216
-
217
- # A Parameter (or ParamSplat) in a function definition.
218
- Param:
219
- PARAM
220
- | PARAM "." "." "." { result = SplatNode.new(val[0]) }
221
- ;
222
-
223
- # A regular splat.
224
- Splat:
225
- Expression "." "." "." { result = SplatNode.new(val[0]) }
226
- ;
227
-
228
- # Expressions that can be treated as values.
229
- Value:
230
- IDENTIFIER { result = ValueNode.new(val[0]) }
231
- | Literal { result = ValueNode.new(val[0]) }
232
- | Array { result = ValueNode.new(val[0]) }
233
- | Object { result = ValueNode.new(val[0]) }
234
- | Parenthetical { result = ValueNode.new(val[0]) }
235
- | Range { result = ValueNode.new(val[0]) }
236
- | This { result = ValueNode.new(val[0]) }
237
- | Value Accessor { result = val[0] << val[1] }
238
- | Invocation Accessor { result = ValueNode.new(val[0], [val[1]]) }
239
- ;
240
-
241
- # Accessing into an object or array, through dot or index notation.
242
- Accessor:
243
- PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) }
244
- | PROTOTYPE_ACCESS IDENTIFIER { result = AccessorNode.new(val[1], :prototype) }
245
- | SOAK_ACCESS IDENTIFIER { result = AccessorNode.new(val[1], :soak) }
246
- | Index { result = val[0] }
247
- | Slice { result = SliceNode.new(val[0]) }
248
- ;
249
-
250
- # Indexing into an object or array.
251
- Index:
252
- INDEX_START Expression INDEX_END { result = IndexNode.new(val[1]) }
253
- ;
254
-
255
- # An object literal.
256
- Object:
257
- "{" AssignList "}" { result = ObjectNode.new(val[1]) }
258
- ;
259
-
260
- # Assignment within an object literal (comma or newline separated).
261
- AssignList:
262
- /* nothing */ { result = [] }
263
- | AssignObj { result = val }
264
- | AssignList "," AssignObj { result = val[0] << val[2] }
265
- | AssignList Terminator AssignObj { result = val[0] << val[2] }
266
- | AssignList ","
267
- Terminator AssignObj { result = val[0] << val[3] }
268
- | INDENT AssignList OUTDENT { result = val[1] }
269
- ;
270
-
271
- # All flavors of function call (instantiation, super, and regular).
272
- Call:
273
- Invocation { result = val[0] }
274
- | NEW Invocation { result = val[1].new_instance }
275
- | Super { result = val[0] }
276
- ;
277
-
278
- # Extending an object's prototype.
279
- Extends:
280
- Value EXTENDS Value { result = ExtendsNode.new(val[0], val[2]) }
281
- ;
282
-
283
- # A generic function invocation.
284
- Invocation:
285
- Value Arguments { result = CallNode.new(val[0], val[1]) }
286
- | Invocation Arguments { result = CallNode.new(val[0], val[1]) }
287
- ;
288
-
289
- # The list of arguments to a function invocation.
290
- Arguments:
291
- CALL_START ArgList CALL_END { result = val[1] }
292
- ;
293
-
294
- # Calling super.
295
- Super:
296
- SUPER CALL_START ArgList CALL_END { result = CallNode.new(Value.new('super'), val[2]) }
297
- ;
298
-
299
- # This references, either naked or to a property.
300
- This:
301
- '@' { result = ThisNode.new }
302
- | '@' IDENTIFIER { result = ThisNode.new(val[1]) }
303
- ;
304
-
305
- # The range literal.
306
- Range:
307
- "[" Expression
308
- "." "." Expression "]" { result = RangeNode.new(val[1], val[4]) }
309
- | "[" Expression
310
- "." "." "." Expression "]" { result = RangeNode.new(val[1], val[5], true) }
311
- ;
312
-
313
- # The slice literal.
314
- Slice:
315
- INDEX_START Expression "." "."
316
- Expression INDEX_END { result = RangeNode.new(val[1], val[4]) }
317
- | INDEX_START Expression "." "." "."
318
- Expression INDEX_END { result = RangeNode.new(val[1], val[5], true) }
319
- ;
320
-
321
- # The array literal.
322
- Array:
323
- "[" ArgList "]" { result = ArrayNode.new(val[1]) }
324
- ;
325
-
326
- # A list of arguments to a method call, or as the contents of an array.
327
- ArgList:
328
- /* nothing */ { result = [] }
329
- | Expression { result = val }
330
- | INDENT Expression { result = [val[1]] }
331
- | ArgList "," Expression { result = val[0] << val[2] }
332
- | ArgList Terminator Expression { result = val[0] << val[2] }
333
- | ArgList "," Terminator Expression { result = val[0] << val[3] }
334
- | ArgList "," INDENT Expression { result = val[0] << val[3] }
335
- | ArgList OUTDENT { result = val[0] }
336
- ;
337
-
338
- # Just simple, comma-separated, required arguments (no fancy syntax).
339
- SimpleArgs:
340
- Expression { result = val[0] }
341
- | SimpleArgs "," Expression { result = ([val[0]] << val[2]).flatten }
342
- ;
343
-
344
- # Try/catch/finally exception handling blocks.
345
- Try:
346
- TRY Block Catch { result = TryNode.new(val[1], val[2][0], val[2][1]) }
347
- | TRY Block FINALLY Block { result = TryNode.new(val[1], nil, nil, val[3]) }
348
- | TRY Block Catch
349
- FINALLY Block { result = TryNode.new(val[1], val[2][0], val[2][1], val[4]) }
350
- ;
351
-
352
- # A catch clause.
353
- Catch:
354
- CATCH IDENTIFIER Block { result = [val[1], val[2]] }
355
- ;
356
-
357
- # Throw an exception.
358
- Throw:
359
- THROW Expression { result = ThrowNode.new(val[1]) }
360
- ;
361
-
362
- # Parenthetical expressions.
363
- Parenthetical:
364
- "(" Expression ")" { result = ParentheticalNode.new(val[1], val[0].line) }
365
- ;
366
-
367
- # The while loop. (there is no do..while).
368
- While:
369
- WHILE Expression Block { result = WhileNode.new(val[1], val[2]) }
370
- | WHILE Expression { result = WhileNode.new(val[1], nil) }
371
- | Expression WHILE Expression { result = WhileNode.new(val[2], Expressions.wrap(val[0])) }
372
- ;
373
-
374
- # Array comprehensions, including guard and current index.
375
- # Looks a little confusing, check nodes.rb for the arguments to ForNode.
376
- For:
377
- Expression FOR
378
- ForVariables ForSource { result = ForNode.new(val[0], val[3], val[2][0], val[2][1]) }
379
- | FOR ForVariables ForSource Block { result = ForNode.new(val[3], val[2], val[1][0], val[1][1]) }
380
- ;
381
-
382
- # An array comprehension has variables for the current element and index.
383
- ForVariables:
384
- IDENTIFIER { result = val }
385
- | IDENTIFIER "," IDENTIFIER { result = [val[0], val[2]] }
386
- ;
387
-
388
- # The source of the array comprehension can optionally be filtered.
389
- ForSource:
390
- IN Expression { result = {:source => val[1]} }
391
- | OF Expression { result = {:source => val[1], :object => true} }
392
- | ForSource
393
- WHEN Expression { result = val[0].merge(:filter => val[2]) }
394
- | ForSource
395
- BY Expression { result = val[0].merge(:step => val[2]) }
396
- ;
397
-
398
- # Switch/When blocks.
399
- Switch:
400
- SWITCH Expression INDENT
401
- Whens OUTDENT { result = val[3].rewrite_condition(val[1]) }
402
- | SWITCH Expression INDENT
403
- Whens ELSE Block OUTDENT { result = val[3].rewrite_condition(val[1]).add_else(val[5]) }
404
- ;
405
-
406
- # The inner list of whens.
407
- Whens:
408
- When { result = val[0] }
409
- | Whens When { result = val[0] << val[1] }
410
- ;
411
-
412
- # An individual when.
413
- When:
414
- LEADING_WHEN SimpleArgs Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
415
- | LEADING_WHEN SimpleArgs Block
416
- Terminator { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
417
- | Comment Terminator When { result = val[2].add_comment(val[0]) }
418
- ;
419
-
420
- # The most basic form of "if".
421
- IfBlock:
422
- IF Expression Block { result = IfNode.new(val[1], val[2]) }
423
- ;
424
-
425
- # An elsif portion of an if-else block.
426
- ElsIf:
427
- ELSE IfBlock { result = val[1].force_statement }
428
- ;
429
-
430
- # Multiple elsifs can be chained together.
431
- ElsIfs:
432
- ElsIf { result = val[0] }
433
- | ElsIfs ElsIf { result = val[0].add_else(val[1]) }
434
- ;
435
-
436
- # Terminating else bodies are strictly optional.
437
- ElseBody
438
- /* nothing */ { result = nil }
439
- | ELSE Block { result = val[1] }
440
- ;
441
-
442
- # All the alternatives for ending an if-else block.
443
- IfEnd:
444
- ElseBody { result = val[0] }
445
- | ElsIfs ElseBody { result = val[0].add_else(val[1]) }
446
- ;
447
-
448
- # The full complement of if blocks, including postfix one-liner ifs and unlesses.
449
- If:
450
- IfBlock IfEnd { result = val[0].add_else(val[1]) }
451
- | Expression IF Expression { result = IfNode.new(val[2], Expressions.wrap(val[0]), nil, {:statement => true}) }
452
- | Expression UNLESS Expression { result = IfNode.new(val[2], Expressions.wrap(val[0]), nil, {:statement => true, :invert => true}) }
453
- ;
454
-
455
- end
456
-
457
- ---- header
458
- module CoffeeScript
459
-
460
- ---- inner
461
- # Lex and parse a CoffeeScript.
462
- def parse(code)
463
- # Uncomment the following line to enable grammar debugging, in combination
464
- # with the -g flag in the Rake build task.
465
- # @yydebug = true
466
- @tokens = Lexer.new.tokenize(code)
467
- do_parse
468
- end
469
-
470
- # Retrieve the next token from the list.
471
- def next_token
472
- @tokens.shift
473
- end
474
-
475
- # Raise a custom error class that knows about line numbers.
476
- def on_error(error_token_id, error_value, value_stack)
477
- raise ParseError.new(token_to_str(error_token_id), error_value, value_stack)
478
- end
479
-
480
- ---- footer
481
- end