fixed the while-loop-condition-with-implicit-function-call bug

This commit is contained in:
Jeremy Ashkenas 2010-02-15 23:05:54 -05:00
parent 4ea8be8e0b
commit fa63288f52
3 changed files with 52 additions and 28 deletions

View File

@ -1,5 +1,5 @@
(function(){
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_START, EXPRESSION_TAIL, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, SINGLE_CLOSERS, SINGLE_LINERS, _1, _2, _3, _4, _5, _6, _7, _8, pair, re;
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_START, EXPRESSION_TAIL, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, SINGLE_CLOSERS, SINGLE_LINERS, _1, _2, _3, _4, _5, _6, _7, _8, pair, re;
var __hasProp = Object.prototype.hasOwnProperty;
if (!((typeof process !== "undefined" && process !== null))) {
this.exports = this;
@ -32,7 +32,8 @@
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_TAIL);
// Tokens pairs that, in immediate succession, indicate an implicit call.
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END'];
IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', 'TERMINATOR', 'OUTDENT'];
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ','];
IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', 'TERMINATOR', 'INDENT', 'OUTDENT'];
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT', '@', '->', '=>', '[', '(', '{'];
// The inverse mappings of token pairs we're trying to fix up.
INVERSES = {};
@ -193,25 +194,32 @@
stack = [0];
return this.scan_tokens((function(__this) {
var __func = function(prev, token, post, i) {
var _10, _11, _12, _9, idx, last, size, tmp;
if (token[0] === 'INDENT') {
var _10, _11, _12, _9, idx, last, size, stack_pointer, tag, tmp;
tag = token[0];
if (tag === 'INDENT') {
stack.push(0);
}
if (token[0] === 'OUTDENT') {
if (tag === 'OUTDENT') {
last = stack.pop();
stack[stack.length - 1] += last;
}
if (stack[stack.length - 1] > 0 && (IMPLICIT_END.indexOf(token[0]) >= 0 || !(typeof post !== "undefined" && post !== null))) {
idx = token[0] === 'OUTDENT' ? i + 1 : i;
_11 = 0; _12 = stack[stack.length - 1];
for (_10=0, tmp=_11; (_11 <= _12 ? tmp < _12 : tmp > _12); (_11 <= _12 ? tmp += 1 : tmp -= 1), _10++) {
this.tokens.splice(idx, 0, ['CALL_END', ')', token[2]]);
if (IMPLICIT_END.indexOf(tag) >= 0 || !(typeof post !== "undefined" && post !== null)) {
if (tag === 'INDENT' && prev && IMPLICIT_BLOCK.indexOf(prev[0]) >= 0) {
return 1;
}
if (stack[stack.length - 1] > 0 || tag === 'INDENT') {
idx = tag === 'OUTDENT' ? i + 1 : i;
stack_pointer = tag === 'INDENT' ? 2 : 1;
_11 = 0; _12 = stack[stack.length - stack_pointer];
for (_10=0, tmp=_11; (_11 <= _12 ? tmp < _12 : tmp > _12); (_11 <= _12 ? tmp += 1 : tmp -= 1), _10++) {
this.tokens.splice(idx, 0, ['CALL_END', ')', token[2]]);
}
size = stack[stack.length - stack_pointer] + 1;
stack[stack.length - stack_pointer] = 0;
return size;
}
size = stack[stack.length - 1] + 1;
stack[stack.length - 1] = 0;
return size;
}
if (!(prev && IMPLICIT_FUNC.indexOf(prev[0]) >= 0 && IMPLICIT_CALL.indexOf(token[0]) >= 0)) {
if (!(prev && IMPLICIT_FUNC.indexOf(prev[0]) >= 0 && IMPLICIT_CALL.indexOf(tag) >= 0)) {
return 1;
}
this.tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
@ -306,7 +314,7 @@
return _9;
}).call(this);
if (unclosed.length) {
throw "unclosed " + unclosed[0];
throw new Error("unclosed " + unclosed[0]);
}
};
// We'd like to support syntax like this:

View File

@ -20,7 +20,8 @@ EXPRESSION_CLOSE: ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_TAIL)
# Tokens pairs that, in immediate succession, indicate an implicit call.
IMPLICIT_FUNC: ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END']
IMPLICIT_END: ['IF', 'UNLESS', 'FOR', 'WHILE', 'TERMINATOR', 'OUTDENT']
IMPLICIT_BLOCK:['->', '=>', '{', '[', ',']
IMPLICIT_END: ['IF', 'UNLESS', 'FOR', 'WHILE', 'TERMINATOR', 'INDENT', 'OUTDENT']
IMPLICIT_CALL: ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START',
'TRY', 'DELETE', 'TYPEOF', 'SWITCH',
'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT',
@ -140,18 +141,22 @@ re::close_open_calls_and_indexes: ->
re::add_implicit_parentheses: ->
stack: [0]
@scan_tokens (prev, token, post, i) =>
stack.push(0) if token[0] is 'INDENT'
if token[0] is 'OUTDENT'
tag: token[0]
stack.push(0) if tag is 'INDENT'
if tag is 'OUTDENT'
last: stack.pop()
stack[stack.length - 1] += last
if stack[stack.length - 1] > 0 and (IMPLICIT_END.indexOf(token[0]) >= 0 or !post?)
idx: if token[0] is 'OUTDENT' then i + 1 else i
for tmp in [0...stack[stack.length - 1]]
@tokens.splice(idx, 0, ['CALL_END', ')', token[2]])
size: stack[stack.length - 1] + 1
stack[stack.length - 1]: 0
return size
return 1 unless prev and IMPLICIT_FUNC.indexOf(prev[0]) >= 0 and IMPLICIT_CALL.indexOf(token[0]) >= 0
if IMPLICIT_END.indexOf(tag) >= 0 or !post?
return 1 if tag is 'INDENT' and prev and IMPLICIT_BLOCK.indexOf(prev[0]) >= 0
if stack[stack.length - 1] > 0 or tag is 'INDENT'
idx: if tag is 'OUTDENT' then i + 1 else i
stack_pointer: if tag is 'INDENT' then 2 else 1
for tmp in [0...stack[stack.length - stack_pointer]]
@tokens.splice(idx, 0, ['CALL_END', ')', token[2]])
size: stack[stack.length - stack_pointer] + 1
stack[stack.length - stack_pointer]: 0
return size
return 1 unless prev and IMPLICIT_FUNC.indexOf(prev[0]) >= 0 and IMPLICIT_CALL.indexOf(tag) >= 0
@tokens.splice(i, 0, ['CALL_START', '(', token[2]])
stack[stack.length - 1] += 1
return 2
@ -196,7 +201,7 @@ re::ensure_balance: (pairs) ->
throw "too many " + token[1] if levels[open] < 0
return 1
unclosed: key for key, value of levels when value > 0
throw "unclosed " + unclosed[0] if unclosed.length
throw new Error("unclosed " + unclosed[0]) if unclosed.length
# We'd like to support syntax like this:
# el.click((event) ->

View File

@ -14,4 +14,15 @@ puts list.join(' ') is "8 6 4 2"
i: 5
list: (i * 3 while i -= 1)
puts list.join(' ') is "12 9 6 3"
puts list.join(' ') is "12 9 6 3"
i: 5
func: (num) -> i -= num
assert: -> puts i < 5 > 0
results: while func 1
assert()
i
puts results.join(' ') is '4 3 2 1'