coffee-script 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,33 @@
1
+ (function(){
2
+ var coffee, prompt, quit, readline, run;
3
+ // A CoffeeScript port/version of the Node.js REPL.
4
+ // Required modules.
5
+ coffee = require('./coffee-script');
6
+ process.mixin(require('sys'));
7
+ // Shortcut variables.
8
+ prompt = 'coffee> ';
9
+ quit = function quit() {
10
+ return process.stdio.close();
11
+ };
12
+ // The main REPL function. Called everytime a line of code is entered.
13
+ readline = function readline(code) {
14
+ return coffee.compile(code, run);
15
+ };
16
+ // Attempt to evaluate the command. If there's an exception, print it.
17
+ run = function run(js) {
18
+ var val;
19
+ try {
20
+ val = eval(js);
21
+ if (val !== undefined) {
22
+ p(val);
23
+ }
24
+ } catch (err) {
25
+ puts(err.stack || err.toString());
26
+ }
27
+ return print(prompt);
28
+ };
29
+ // Start up the REPL.
30
+ process.stdio.open();
31
+ process.stdio.addListener('data', readline);
32
+ print(prompt);
33
+ })();
@@ -0,0 +1,377 @@
1
+ (function(){
2
+ var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_START, EXPRESSION_TAIL, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, SINGLE_CLOSERS, SINGLE_LINERS, __a, __b, __c, __d, __e, __f, __g, __h, pair, re;
3
+ var __hasProp = Object.prototype.hasOwnProperty;
4
+ // In order to keep the grammar simple, the stream of tokens that the Lexer
5
+ // emits is rewritten by the Rewriter, smoothing out ambiguities, mis-nested
6
+ // indentation, and single-line flavors of expressions.
7
+ exports.Rewriter = (re = function re() { });
8
+ // Tokens that must be balanced.
9
+ BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END']];
10
+ // Tokens that signal the start of a balanced pair.
11
+ EXPRESSION_START = (function() {
12
+ __a = []; __b = BALANCED_PAIRS;
13
+ for (__c = 0; __c < __b.length; __c++) {
14
+ pair = __b[__c];
15
+ __a.push(pair[0]);
16
+ }
17
+ return __a;
18
+ }).call(this);
19
+ // Tokens that signal the end of a balanced pair.
20
+ EXPRESSION_TAIL = (function() {
21
+ __d = []; __e = BALANCED_PAIRS;
22
+ for (__f = 0; __f < __e.length; __f++) {
23
+ pair = __e[__f];
24
+ __d.push(pair[1]);
25
+ }
26
+ return __d;
27
+ }).call(this);
28
+ // Tokens that indicate the close of a clause of an expression.
29
+ EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_TAIL);
30
+ // Tokens pairs that, in immediate succession, indicate an implicit call.
31
+ IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END'];
32
+ IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', 'TERMINATOR', 'OUTDENT'];
33
+ IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'ARGUMENTS', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT', '->', '=>', '[', '(', '{'];
34
+ // The inverse mappings of token pairs we're trying to fix up.
35
+ INVERSES = {
36
+ };
37
+ __g = BALANCED_PAIRS;
38
+ for (__h = 0; __h < __g.length; __h++) {
39
+ pair = __g[__h];
40
+ INVERSES[pair[0]] = pair[1];
41
+ INVERSES[pair[1]] = pair[0];
42
+ }
43
+ // Single-line flavors of block expressions that have unclosed endings.
44
+ // The grammar can't disambiguate them, so we insert the implicit indentation.
45
+ SINGLE_LINERS = ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN'];
46
+ SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN', 'PARAM_START'];
47
+ // Rewrite the token stream in multiple passes, one logical filter at
48
+ // a time. This could certainly be changed into a single pass through the
49
+ // stream, with a big ol' efficient switch, but it's much nicer like this.
50
+ re.prototype.rewrite = function rewrite(tokens) {
51
+ this.tokens = tokens;
52
+ this.adjust_comments();
53
+ this.remove_leading_newlines();
54
+ this.remove_mid_expression_newlines();
55
+ this.move_commas_outside_outdents();
56
+ this.close_open_calls_and_indexes();
57
+ this.add_implicit_parentheses();
58
+ this.add_implicit_indentation();
59
+ this.ensure_balance(BALANCED_PAIRS);
60
+ this.rewrite_closing_parens();
61
+ return this.tokens;
62
+ };
63
+ // Rewrite the token stream, looking one token ahead and behind.
64
+ // Allow the return value of the block to tell us how many tokens to move
65
+ // forwards (or backwards) in the stream, to make sure we don't miss anything
66
+ // as the stream changes length under our feet.
67
+ re.prototype.scan_tokens = function scan_tokens(block) {
68
+ var i, move;
69
+ i = 0;
70
+ while (true) {
71
+ if (!(this.tokens[i])) {
72
+ break;
73
+ }
74
+ move = block(this.tokens[i - 1], this.tokens[i], this.tokens[i + 1], i);
75
+ i += move;
76
+ }
77
+ return true;
78
+ };
79
+ // Massage newlines and indentations so that comments don't have to be
80
+ // correctly indented, or appear on their own line.
81
+ re.prototype.adjust_comments = function adjust_comments() {
82
+ return this.scan_tokens((function(__this) {
83
+ var __func = function(prev, token, post, i) {
84
+ var after, before;
85
+ if (!(token[0] === 'COMMENT')) {
86
+ return 1;
87
+ }
88
+ before = this.tokens[i - 2];
89
+ after = this.tokens[i + 2];
90
+ if (before && after && ((before[0] === 'INDENT' && after[0] === 'OUTDENT') || (before[0] === 'OUTDENT' && after[0] === 'INDENT')) && before[1] === after[1]) {
91
+ this.tokens.splice(i + 2, 1);
92
+ this.tokens.splice(i - 2, 1);
93
+ return 0;
94
+ } else if (prev[0] === 'TERMINATOR' && after[0] === 'INDENT') {
95
+ this.tokens.splice(i + 2, 1);
96
+ this.tokens[i - 1] = after;
97
+ return 1;
98
+ } else if (prev[0] !== 'TERMINATOR' && prev[0] !== 'INDENT' && prev[0] !== 'OUTDENT') {
99
+ this.tokens.splice(i, 0, ['TERMINATOR', "\n", prev[2]]);
100
+ return 2;
101
+ } else {
102
+ return 1;
103
+ }
104
+ };
105
+ return (function() {
106
+ return __func.apply(__this, arguments);
107
+ });
108
+ })(this));
109
+ };
110
+ // Leading newlines would introduce an ambiguity in the grammar, so we
111
+ // dispatch them here.
112
+ re.prototype.remove_leading_newlines = function remove_leading_newlines() {
113
+ if (this.tokens[0][0] === 'TERMINATOR') {
114
+ return this.tokens.shift();
115
+ }
116
+ };
117
+ // Some blocks occur in the middle of expressions -- when we're expecting
118
+ // this, remove their trailing newlines.
119
+ re.prototype.remove_mid_expression_newlines = function remove_mid_expression_newlines() {
120
+ return this.scan_tokens((function(__this) {
121
+ var __func = function(prev, token, post, i) {
122
+ if (!(post && EXPRESSION_CLOSE.indexOf(post[0]) >= 0 && token[0] === 'TERMINATOR')) {
123
+ return 1;
124
+ }
125
+ this.tokens.splice(i, 1);
126
+ return 0;
127
+ };
128
+ return (function() {
129
+ return __func.apply(__this, arguments);
130
+ });
131
+ })(this));
132
+ };
133
+ // Make sure that we don't accidentally break trailing commas, which need
134
+ // to go on the outside of expression closers.
135
+ re.prototype.move_commas_outside_outdents = function move_commas_outside_outdents() {
136
+ return this.scan_tokens((function(__this) {
137
+ var __func = function(prev, token, post, i) {
138
+ if (token[0] === 'OUTDENT' && prev[0] === ',') {
139
+ this.tokens.splice(i, 1, token);
140
+ }
141
+ return 1;
142
+ };
143
+ return (function() {
144
+ return __func.apply(__this, arguments);
145
+ });
146
+ })(this));
147
+ };
148
+ // We've tagged the opening parenthesis of a method call, and the opening
149
+ // bracket of an indexing operation. Match them with their close.
150
+ re.prototype.close_open_calls_and_indexes = function close_open_calls_and_indexes() {
151
+ var brackets, parens;
152
+ parens = [0];
153
+ brackets = [0];
154
+ return this.scan_tokens((function(__this) {
155
+ var __func = function(prev, token, post, i) {
156
+ if (token[0] === 'CALL_START') {
157
+ parens.push(0);
158
+ } else if (token[0] === 'INDEX_START') {
159
+ brackets.push(0);
160
+ } else if (token[0] === '(') {
161
+ parens[parens.length - 1] += 1;
162
+ } else if (token[0] === '[') {
163
+ brackets[brackets.length - 1] += 1;
164
+ } else if (token[0] === ')') {
165
+ if (parens[parens.length - 1] === 0) {
166
+ parens.pop();
167
+ token[0] = 'CALL_END';
168
+ } else {
169
+ parens[parens.length - 1] -= 1;
170
+ }
171
+ } else if (token[0] === ']') {
172
+ if (brackets[brackets.length - 1] === 0) {
173
+ brackets.pop();
174
+ token[0] = 'INDEX_END';
175
+ } else {
176
+ brackets[brackets.length - 1] -= 1;
177
+ }
178
+ }
179
+ return 1;
180
+ };
181
+ return (function() {
182
+ return __func.apply(__this, arguments);
183
+ });
184
+ })(this));
185
+ };
186
+ // Methods may be optionally called without parentheses, for simple cases.
187
+ // Insert the implicit parentheses here, so that the parser doesn't have to
188
+ // deal with them.
189
+ re.prototype.add_implicit_parentheses = function add_implicit_parentheses() {
190
+ var stack;
191
+ stack = [0];
192
+ return this.scan_tokens((function(__this) {
193
+ var __func = function(prev, token, post, i) {
194
+ var __i, __j, __k, __l, idx, last, size, tmp;
195
+ if (token[0] === 'INDENT') {
196
+ stack.push(0);
197
+ }
198
+ if (token[0] === 'OUTDENT') {
199
+ last = stack.pop();
200
+ stack[stack.length - 1] += last;
201
+ }
202
+ if (stack[stack.length - 1] > 0 && (IMPLICIT_END.indexOf(token[0]) >= 0 || (typeof !post !== "undefined" && !post !== null))) {
203
+ idx = token[0] === 'OUTDENT' ? i + 1 : i;
204
+ __k = 0; __l = stack[stack.length - 1];
205
+ for (__j=0, tmp=__k; (__k <= __l ? tmp < __l : tmp > __l); (__k <= __l ? tmp += 1 : tmp -= 1), __j++) {
206
+ this.tokens.splice(idx, 0, ['CALL_END', ')']);
207
+ }
208
+ size = stack[stack.length - 1] + 1;
209
+ stack[stack.length - 1] = 0;
210
+ return size;
211
+ }
212
+ if (!(prev && IMPLICIT_FUNC.indexOf(prev[0]) >= 0 && IMPLICIT_CALL.indexOf(token[0]) >= 0)) {
213
+ return 1;
214
+ }
215
+ this.tokens.splice(i, 0, ['CALL_START', '(']);
216
+ stack[stack.length - 1] += 1;
217
+ return 2;
218
+ };
219
+ return (function() {
220
+ return __func.apply(__this, arguments);
221
+ });
222
+ })(this));
223
+ };
224
+ // Because our grammar is LALR(1), it can't handle some single-line
225
+ // expressions that lack ending delimiters. Use the lexer to add the implicit
226
+ // blocks, so it doesn't need to.
227
+ // ')' can close a single-line block, but we need to make sure it's balanced.
228
+ re.prototype.add_implicit_indentation = function add_implicit_indentation() {
229
+ return this.scan_tokens((function(__this) {
230
+ var __func = function(prev, token, post, i) {
231
+ var idx, insertion, parens, starter, tok;
232
+ if (!(SINGLE_LINERS.indexOf(token[0]) >= 0 && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) {
233
+ return 1;
234
+ }
235
+ starter = token[0];
236
+ this.tokens.splice(i + 1, 0, ['INDENT', 2]);
237
+ idx = i + 1;
238
+ parens = 0;
239
+ while (true) {
240
+ idx += 1;
241
+ tok = this.tokens[idx];
242
+ if ((!tok || SINGLE_CLOSERS.indexOf(tok[0]) >= 0 || (tok[0] === ')' && parens === 0)) && !(starter === 'ELSE' && tok[0] === 'ELSE')) {
243
+ insertion = this.tokens[idx - 1][0] === "," ? idx - 1 : idx;
244
+ this.tokens.splice(insertion, 0, ['OUTDENT', 2]);
245
+ break;
246
+ }
247
+ if (tok[0] === '(') {
248
+ parens += 1;
249
+ }
250
+ if (tok[0] === ')') {
251
+ parens -= 1;
252
+ }
253
+ }
254
+ if (!(token[0] === 'THEN')) {
255
+ return 1;
256
+ }
257
+ this.tokens.splice(i, 1);
258
+ return 0;
259
+ };
260
+ return (function() {
261
+ return __func.apply(__this, arguments);
262
+ });
263
+ })(this));
264
+ };
265
+ // Ensure that all listed pairs of tokens are correctly balanced throughout
266
+ // the course of the token stream.
267
+ re.prototype.ensure_balance = function ensure_balance(pairs) {
268
+ var __i, __j, key, levels, unclosed, value;
269
+ levels = {
270
+ };
271
+ this.scan_tokens((function(__this) {
272
+ var __func = function(prev, token, post, i) {
273
+ var __i, __j, __k, close, open;
274
+ __i = pairs;
275
+ for (__j = 0; __j < __i.length; __j++) {
276
+ pair = __i[__j];
277
+ __k = pair;
278
+ open = __k[0];
279
+ close = __k[1];
280
+ levels[open] = levels[open] || 0;
281
+ if (token[0] === open) {
282
+ levels[open] += 1;
283
+ }
284
+ if (token[0] === close) {
285
+ levels[open] -= 1;
286
+ }
287
+ if (levels[open] < 0) {
288
+ throw "too many " + token[1];
289
+ }
290
+ }
291
+ return 1;
292
+ };
293
+ return (function() {
294
+ return __func.apply(__this, arguments);
295
+ });
296
+ })(this));
297
+ unclosed = (function() {
298
+ __i = []; __j = levels;
299
+ for (key in __j) {
300
+ value = __j[key];
301
+ if (__hasProp.call(__j, key)) {
302
+ if (value > 0) {
303
+ __i.push(key);
304
+ }
305
+ }
306
+ }
307
+ return __i;
308
+ }).call(this);
309
+ if (unclosed.length) {
310
+ throw "unclosed " + unclosed[0];
311
+ }
312
+ };
313
+ // We'd like to support syntax like this:
314
+ // el.click((event) ->
315
+ // el.hide())
316
+ // In order to accomplish this, move outdents that follow closing parens
317
+ // inwards, safely. The steps to accomplish this are:
318
+ //
319
+ // 1. Check that all paired tokens are balanced and in order.
320
+ // 2. Rewrite the stream with a stack: if you see an '(' or INDENT, add it
321
+ // to the stack. If you see an ')' or OUTDENT, pop the stack and replace
322
+ // it with the inverse of what we've just popped.
323
+ // 3. Keep track of "debt" for tokens that we fake, to make sure we end
324
+ // up balanced in the end.
325
+ re.prototype.rewrite_closing_parens = function rewrite_closing_parens() {
326
+ var __i, debt, key, stack, val;
327
+ stack = [];
328
+ debt = {
329
+ };
330
+ __i = INVERSES;
331
+ for (key in __i) {
332
+ val = __i[key];
333
+ if (__hasProp.call(__i, key)) {
334
+ ((debt[key] = 0));
335
+ }
336
+ }
337
+ return this.scan_tokens((function(__this) {
338
+ var __func = function(prev, token, post, i) {
339
+ var inv, match, mtag, tag;
340
+ tag = token[0];
341
+ inv = INVERSES[token[0]];
342
+ // Push openers onto the stack.
343
+ if (EXPRESSION_START.indexOf(tag) >= 0) {
344
+ stack.push(token);
345
+ return 1;
346
+ // The end of an expression, check stack and debt for a pair.
347
+ } else if (EXPRESSION_TAIL.indexOf(tag) >= 0) {
348
+ // If the tag is already in our debt, swallow it.
349
+ if (debt[inv] > 0) {
350
+ debt[inv] -= 1;
351
+ this.tokens.splice(i, 1);
352
+ return 0;
353
+ } else {
354
+ // Pop the stack of open delimiters.
355
+ match = stack.pop();
356
+ mtag = match[0];
357
+ // Continue onwards if it's the expected tag.
358
+ if (tag === INVERSES[mtag]) {
359
+ return 1;
360
+ } else {
361
+ // Unexpected close, insert correct close, adding to the debt.
362
+ debt[mtag] += 1;
363
+ val = mtag === 'INDENT' ? match[1] : INVERSES[mtag];
364
+ this.tokens.splice(i, 0, [INVERSES[mtag], val]);
365
+ return 1;
366
+ }
367
+ }
368
+ } else {
369
+ return 1;
370
+ }
371
+ };
372
+ return (function() {
373
+ return __func.apply(__this, arguments);
374
+ });
375
+ })(this));
376
+ };
377
+ })();
@@ -22,9 +22,9 @@ module CoffeeScript
22
22
  IMPLICIT_FUNC = [:IDENTIFIER, :SUPER, ')', :CALL_END, ']', :INDEX_END]
23
23
  IMPLICIT_END = [:IF, :UNLESS, :FOR, :WHILE, "\n", :OUTDENT]
24
24
  IMPLICIT_CALL = [:IDENTIFIER, :NUMBER, :STRING, :JS, :REGEX, :NEW, :PARAM_START,
25
- :TRY, :DELETE, :TYPEOF, :SWITCH, :ARGUMENTS,
25
+ :TRY, :DELETE, :TYPEOF, :SWITCH,
26
26
  :TRUE, :FALSE, :YES, :NO, :ON, :OFF, '!', '!!', :NOT,
27
- '->', '=>', '[', '(', '{']
27
+ '@', '->', '=>', '[', '(', '{']
28
28
 
29
29
  # The inverse mappings of token pairs we're trying to fix up.
30
30
  INVERSES = BALANCED_PAIRS.inject({}) do |memo, pair|
@@ -151,6 +151,30 @@ module CoffeeScript
151
151
  end
152
152
  end
153
153
 
154
+ # Methods may be optionally called without parentheses, for simple cases.
155
+ # Insert the implicit parentheses here, so that the parser doesn't have to
156
+ # deal with them.
157
+ def add_implicit_parentheses
158
+ stack = [0]
159
+ scan_tokens do |prev, token, post, i|
160
+ stack.push(0) if token[0] == :INDENT
161
+ if token[0] == :OUTDENT
162
+ last = stack.pop
163
+ stack[-1] += last
164
+ end
165
+ if stack.last > 0 && (IMPLICIT_END.include?(token[0]) || post.nil?)
166
+ idx = token[0] == :OUTDENT ? i + 1 : i
167
+ stack.last.times { @tokens.insert(idx, [:CALL_END, Value.new(')', token[1].line)]) }
168
+ size, stack[-1] = stack[-1] + 1, 0
169
+ next size
170
+ end
171
+ next 1 unless IMPLICIT_FUNC.include?(prev[0]) && IMPLICIT_CALL.include?(token[0])
172
+ @tokens.insert(i, [:CALL_START, Value.new('(', token[1].line)])
173
+ stack[-1] += 1
174
+ next 2
175
+ end
176
+ end
177
+
154
178
  # Because our grammar is LALR(1), it can't handle some single-line
155
179
  # expressions that lack ending delimiters. Use the lexer to add the implicit
156
180
  # blocks, so it doesn't need to.
@@ -183,30 +207,6 @@ module CoffeeScript
183
207
  end
184
208
  end
185
209
 
186
- # Methods may be optionally called without parentheses, for simple cases.
187
- # Insert the implicit parentheses here, so that the parser doesn't have to
188
- # deal with them.
189
- def add_implicit_parentheses
190
- stack = [0]
191
- scan_tokens do |prev, token, post, i|
192
- stack.push(0) if token[0] == :INDENT
193
- if token[0] == :OUTDENT
194
- last = stack.pop
195
- stack[-1] += last
196
- end
197
- if stack.last > 0 && (IMPLICIT_END.include?(token[0]) || post.nil?)
198
- idx = token[0] == :OUTDENT ? i + 1 : i
199
- stack.last.times { @tokens.insert(idx, [:CALL_END, Value.new(')', token[1].line)]) }
200
- size, stack[-1] = stack[-1] + 1, 0
201
- next size
202
- end
203
- next 1 unless IMPLICIT_FUNC.include?(prev[0]) && IMPLICIT_CALL.include?(token[0])
204
- @tokens.insert(i, [:CALL_START, Value.new('(', token[1].line)])
205
- stack[-1] += 1
206
- next 2
207
- end
208
- end
209
-
210
210
  # Ensure that all listed pairs of tokens are correctly balanced throughout
211
211
  # the course of the token stream.
212
212
  def ensure_balance(*pairs)