coffee-script 0.3.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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