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,33 +0,0 @@
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
- })();
@@ -1,377 +0,0 @@
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
- })();
@@ -1,289 +0,0 @@
1
- module CoffeeScript
2
-
3
- # In order to keep the grammar simple, the stream of tokens that the Lexer
4
- # emits is rewritten by the Rewriter, smoothing out ambiguities, mis-nested
5
- # indentation, and single-line flavors of expressions.
6
- class Rewriter
7
-
8
- # Tokens that must be balanced.
9
- BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], [:INDENT, :OUTDENT],
10
- [:PARAM_START, :PARAM_END], [:CALL_START, :CALL_END], [:INDEX_START, :INDEX_END]]
11
-
12
- # Tokens that signal the start of a balanced pair.
13
- EXPRESSION_START = BALANCED_PAIRS.map {|pair| pair.first }
14
-
15
- # Tokens that signal the end of a balanced pair.
16
- EXPRESSION_TAIL = BALANCED_PAIRS.map {|pair| pair.last }
17
-
18
- # Tokens that indicate the close of a clause of an expression.
19
- EXPRESSION_CLOSE = [:CATCH, :WHEN, :ELSE, :FINALLY] + EXPRESSION_TAIL
20
-
21
- # Tokens pairs that, in immediate succession, indicate an implicit call.
22
- IMPLICIT_FUNC = [:IDENTIFIER, :SUPER, ')', :CALL_END, ']', :INDEX_END]
23
- IMPLICIT_END = [:IF, :UNLESS, :FOR, :WHILE, "\n", :OUTDENT]
24
- IMPLICIT_CALL = [:IDENTIFIER, :NUMBER, :STRING, :JS, :REGEX, :NEW, :PARAM_START,
25
- :TRY, :DELETE, :TYPEOF, :SWITCH,
26
- :TRUE, :FALSE, :YES, :NO, :ON, :OFF, '!', '!!', :NOT,
27
- '@', '->', '=>', '[', '(', '{']
28
-
29
- # The inverse mappings of token pairs we're trying to fix up.
30
- INVERSES = BALANCED_PAIRS.inject({}) do |memo, pair|
31
- memo[pair.first] = pair.last
32
- memo[pair.last] = pair.first
33
- memo
34
- end
35
-
36
- # Single-line flavors of block expressions that have unclosed endings.
37
- # The grammar can't disambiguate them, so we insert the implicit indentation.
38
- SINGLE_LINERS = [:ELSE, "->", "=>", :TRY, :FINALLY, :THEN]
39
- SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT, :LEADING_WHEN, :PARAM_START]
40
-
41
- # Rewrite the token stream in multiple passes, one logical filter at
42
- # a time. This could certainly be changed into a single pass through the
43
- # stream, with a big ol' efficient switch, but it's much nicer like this.
44
- def rewrite(tokens)
45
- @tokens = tokens
46
- adjust_comments
47
- remove_leading_newlines
48
- remove_mid_expression_newlines
49
- move_commas_outside_outdents
50
- close_open_calls_and_indexes
51
- add_implicit_parentheses
52
- add_implicit_indentation
53
- ensure_balance(*BALANCED_PAIRS)
54
- rewrite_closing_parens
55
- @tokens
56
- end
57
-
58
- # Rewrite the token stream, looking one token ahead and behind.
59
- # Allow the return value of the block to tell us how many tokens to move
60
- # forwards (or backwards) in the stream, to make sure we don't miss anything
61
- # as the stream changes length under our feet.
62
- def scan_tokens
63
- i = 0
64
- loop do
65
- break unless @tokens[i]
66
- move = yield(@tokens[i - 1], @tokens[i], @tokens[i + 1], i)
67
- i += move
68
- end
69
- end
70
-
71
- # Massage newlines and indentations so that comments don't have to be
72
- # correctly indented, or appear on their own line.
73
- def adjust_comments
74
- scan_tokens do |prev, token, post, i|
75
- next 1 unless token[0] == :COMMENT
76
- before, after = @tokens[i - 2], @tokens[i + 2]
77
- if before && after &&
78
- ((before[0] == :INDENT && after[0] == :OUTDENT) ||
79
- (before[0] == :OUTDENT && after[0] == :INDENT)) &&
80
- before[1] == after[1]
81
- @tokens.delete_at(i + 2)
82
- @tokens.delete_at(i - 2)
83
- next 0
84
- elsif prev[0] == "\n" && [:INDENT].include?(after[0])
85
- @tokens.delete_at(i + 2)
86
- @tokens[i - 1] = after
87
- next 1
88
- elsif !["\n", :INDENT, :OUTDENT].include?(prev[0])
89
- @tokens.insert(i, ["\n", Value.new("\n", token[1].line)])
90
- next 2
91
- else
92
- next 1
93
- end
94
- end
95
- end
96
-
97
- # Leading newlines would introduce an ambiguity in the grammar, so we
98
- # dispatch them here.
99
- def remove_leading_newlines
100
- @tokens.shift if @tokens[0][0] == "\n"
101
- end
102
-
103
- # Some blocks occur in the middle of expressions -- when we're expecting
104
- # this, remove their trailing newlines.
105
- def remove_mid_expression_newlines
106
- scan_tokens do |prev, token, post, i|
107
- next 1 unless post && EXPRESSION_CLOSE.include?(post[0]) && token[0] == "\n"
108
- @tokens.delete_at(i)
109
- next 0
110
- end
111
- end
112
-
113
- # Make sure that we don't accidentally break trailing commas, which need
114
- # to go on the outside of expression closers.
115
- def move_commas_outside_outdents
116
- scan_tokens do |prev, token, post, i|
117
- if token[0] == :OUTDENT && prev[0] == ','
118
- @tokens.delete_at(i)
119
- @tokens.insert(i - 1, token)
120
- end
121
- next 1
122
- end
123
- end
124
-
125
- # We've tagged the opening parenthesis of a method call, and the opening
126
- # bracket of an indexing operation. Match them with their close.
127
- def close_open_calls_and_indexes
128
- parens, brackets = [0], [0]
129
- scan_tokens do |prev, token, post, i|
130
- case token[0]
131
- when :CALL_START then parens.push(0)
132
- when :INDEX_START then brackets.push(0)
133
- when '(' then parens[-1] += 1
134
- when '[' then brackets[-1] += 1
135
- when ')'
136
- if parens.last == 0
137
- parens.pop
138
- token[0] = :CALL_END
139
- else
140
- parens[-1] -= 1
141
- end
142
- when ']'
143
- if brackets.last == 0
144
- brackets.pop
145
- token[0] = :INDEX_END
146
- else
147
- brackets[-1] -= 1
148
- end
149
- end
150
- next 1
151
- end
152
- end
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
-
178
- # Because our grammar is LALR(1), it can't handle some single-line
179
- # expressions that lack ending delimiters. Use the lexer to add the implicit
180
- # blocks, so it doesn't need to.
181
- # ')' can close a single-line block, but we need to make sure it's balanced.
182
- def add_implicit_indentation
183
- scan_tokens do |prev, token, post, i|
184
- next 1 unless SINGLE_LINERS.include?(token[0]) && post[0] != :INDENT &&
185
- !(token[0] == :ELSE && post[0] == :IF) # Elsifs shouldn't get blocks.
186
- starter = token[0]
187
- line = token[1].line
188
- @tokens.insert(i + 1, [:INDENT, Value.new(2, line)])
189
- idx = i + 1
190
- parens = 0
191
- loop do
192
- idx += 1
193
- tok = @tokens[idx]
194
- if (!tok || SINGLE_CLOSERS.include?(tok[0]) ||
195
- (tok[0] == ')' && parens == 0)) &&
196
- !(starter == :ELSE && tok[0] == :ELSE)
197
- insertion = @tokens[idx - 1][0] == "," ? idx - 1 : idx
198
- @tokens.insert(insertion, [:OUTDENT, Value.new(2, line)])
199
- break
200
- end
201
- parens += 1 if tok[0] == '('
202
- parens -= 1 if tok[0] == ')'
203
- end
204
- next 1 unless token[0] == :THEN
205
- @tokens.delete_at(i)
206
- next 0
207
- end
208
- end
209
-
210
- # Ensure that all listed pairs of tokens are correctly balanced throughout
211
- # the course of the token stream.
212
- def ensure_balance(*pairs)
213
- puts "\nbefore ensure_balance: #{@tokens.inspect}" if ENV['VERBOSE']
214
- levels, lines = Hash.new(0), Hash.new
215
- scan_tokens do |prev, token, post, i|
216
- pairs.each do |pair|
217
- open, close = *pair
218
- levels[open] += 1 if token[0] == open
219
- levels[open] -= 1 if token[0] == close
220
- lines[token[0]] = token[1].line
221
- raise ParseError.new(token[0], token[1], nil) if levels[open] < 0
222
- end
223
- next 1
224
- end
225
- unclosed = levels.detect {|k, v| v > 0 }
226
- sym = unclosed && unclosed[0]
227
- raise ParseError.new(sym, Value.new(sym, lines[sym]), nil, "unclosed '#{sym}'") if unclosed
228
- end
229
-
230
- # We'd like to support syntax like this:
231
- # el.click((event) ->
232
- # el.hide())
233
- # In order to accomplish this, move outdents that follow closing parens
234
- # inwards, safely. The steps to accomplish this are:
235
- #
236
- # 1. Check that all paired tokens are balanced and in order.
237
- # 2. Rewrite the stream with a stack: if you see an '(' or INDENT, add it
238
- # to the stack. If you see an ')' or OUTDENT, pop the stack and replace
239
- # it with the inverse of what we've just popped.
240
- # 3. Keep track of "debt" for tokens that we fake, to make sure we end
241
- # up balanced in the end.
242
- #
243
- def rewrite_closing_parens
244
- verbose = ENV['VERBOSE']
245
- stack, debt = [], Hash.new(0)
246
- stack_stats = lambda { "stack: #{stack.inspect} debt: #{debt.inspect}\n\n" }
247
- puts "rewrite_closing_original: #{@tokens.inspect}" if verbose
248
- scan_tokens do |prev, token, post, i|
249
- tag, inv = token[0], INVERSES[token[0]]
250
- # Push openers onto the stack.
251
- if EXPRESSION_START.include?(tag)
252
- stack.push(token)
253
- puts "pushing #{tag} #{stack_stats[]}" if verbose
254
- next 1
255
- # The end of an expression, check stack and debt for a pair.
256
- elsif EXPRESSION_TAIL.include?(tag)
257
- puts @tokens[i..-1].inspect if verbose
258
- # If the tag is already in our debt, swallow it.
259
- if debt[inv] > 0
260
- debt[inv] -= 1
261
- @tokens.delete_at(i)
262
- puts "tag in debt #{tag} #{stack_stats[]}" if verbose
263
- next 0
264
- else
265
- # Pop the stack of open delimiters.
266
- match = stack.pop
267
- mtag = match[0]
268
- # Continue onwards if it's the expected tag.
269
- if tag == INVERSES[mtag]
270
- puts "expected tag #{tag} #{stack_stats[]}" if verbose
271
- next 1
272
- else
273
- # Unexpected close, insert correct close, adding to the debt.
274
- debt[mtag] += 1
275
- puts "unexpected #{tag}, replacing with #{INVERSES[mtag]} #{stack_stats[]}" if verbose
276
- val = mtag == :INDENT ? match[1] : INVERSES[mtag]
277
- @tokens.insert(i, [INVERSES[mtag], Value.new(val, token[1].line)])
278
- next 1
279
- end
280
- end
281
- else
282
- # Uninteresting token:
283
- next 1
284
- end
285
- end
286
- end
287
-
288
- end
289
- end