First step of the general rewriter refactor. Added a generic 'detectEnd' function which is expression-pair sensitive. Use it to reimplement closeOpenCallsAndIndexes

This commit is contained in:
Jeremy Ashkenas
2010-08-08 17:37:28 -04:00
parent d286b33601
commit 18d6fd72de
6 changed files with 89 additions and 65 deletions

View File

@@ -19,7 +19,8 @@
this.adjustComments();
this.removeLeadingNewlines();
this.removeMidExpressionNewlines();
this.closeOpenCallsAndIndexes();
this.closeOpenCalls();
this.closeOpenIndexes();
this.addImplicitIndentation();
this.addImplicitBraces();
this.addImplicitParentheses();
@@ -39,6 +40,26 @@
}
return true;
};
Rewriter.prototype.detectEnd = function(i, condition, action) {
var _c, _d, _e, _f, _g, _h, levels, token;
levels = 0;
while (true) {
if (!(token = this.tokens[i])) {
break;
}
if (!levels && condition(token, i)) {
return action(token, i);
}
if ((function(){ (_c = token[0]); for (var _d=0, _e=EXPRESSION_START.length; _d<_e; _d++) { if (EXPRESSION_START[_d] === _c) return true; } return false; }).call(this)) {
levels += 1;
}
if ((function(){ (_f = token[0]); for (var _g=0, _h=EXPRESSION_END.length; _g<_h; _g++) { if (EXPRESSION_END[_g] === _f) return true; } return false; }).call(this)) {
levels -= 1;
}
i += 1;
}
return i - 1;
};
Rewriter.prototype.adjustComments = function() {
return this.scanTokens(__bind(function(prev, token, post, i) {
var _c, _d, after, before;
@@ -80,34 +101,34 @@
return 0;
}, this));
};
Rewriter.prototype.closeOpenCallsAndIndexes = function() {
var brackets, parens;
parens = [0];
brackets = [0];
Rewriter.prototype.closeOpenCalls = function() {
return this.scanTokens(__bind(function(prev, token, post, i) {
var _c;
if ((_c = token[0]) === 'CALL_START') {
parens.push(0);
} else if (_c === 'INDEX_START') {
brackets.push(0);
} else if (_c === '(') {
parens[parens.length - 1] += 1;
} else if (_c === '[') {
brackets[brackets.length - 1] += 1;
} else if (_c === ')') {
if (parens[parens.length - 1] === 0) {
parens.pop();
token[0] = 'CALL_END';
} else {
parens[parens.length - 1] -= 1;
}
} else if (_c === ']') {
if (brackets[brackets.length - 1] === 0) {
brackets.pop();
token[0] = 'INDEX_END';
} else {
brackets[brackets.length - 1] -= 1;
}
var action, condition;
condition = function(token, i) {
var _c;
return (')' === (_c = token[0]) || 'CALL_END' === _c);
};
action = function(token, i) {
return (token[0] = 'CALL_END');
};
if (token[0] === 'CALL_START') {
this.detectEnd(i + 1, condition, action);
}
return 1;
}, this));
};
Rewriter.prototype.closeOpenIndexes = function() {
return this.scanTokens(__bind(function(prev, token, post, i) {
var action, condition;
condition = function(token, i) {
var _c;
return (']' === (_c = token[0]) || 'INDEX_END' === _c);
};
action = function(token, i) {
return (token[0] = 'INDEX_END');
};
if (token[0] === 'INDEX_START') {
this.detectEnd(i + 1, condition, action);
}
return 1;
}, this));

View File

@@ -402,7 +402,7 @@ exports.Lexer = class Lexer
while i < str.length - 1
if starts str, '\\', i
i += 1
else if (expr = @balancedString str.substring(i), [['#{', '}']])
else if expr = @balancedString(str.substring(i), [['#{', '}']])
tokens.push ['STRING', quote + str.substring(pi, i) + quote] if pi < i
inner = expr.substring(2, expr.length - 1)
if inner.length

View File

@@ -821,9 +821,9 @@ exports.AssignNode = class AssignNode extends BaseNode
isString = idx.value and idx.value.match IS_STRING
accessClass = if isString or @variable.isArray() then IndexNode else AccessorNode
if obj instanceof SplatNode and not splat
val = literal(obj.compileValue(o, valVar,
val = literal obj.compileValue o, valVar,
(oindex = indexOf(@variable.base.objects, obj)),
(olength = @variable.base.objects.length) - oindex - 1))
(olength = @variable.base.objects.length) - oindex - 1
splat = true
else
idx = literal(if splat then "#{valVar}.length - #{olength - idx}" else idx) if typeof idx isnt 'object'

View File

@@ -32,7 +32,8 @@ exports.Rewriter = class Rewriter
@adjustComments()
@removeLeadingNewlines()
@removeMidExpressionNewlines()
@closeOpenCallsAndIndexes()
@closeOpenCalls()
@closeOpenIndexes()
@addImplicitIndentation()
@addImplicitBraces()
@addImplicitParentheses()
@@ -53,6 +54,16 @@ exports.Rewriter = class Rewriter
i += move
true
detectEnd: (i, condition, action) ->
levels = 0
loop
break unless token = @tokens[i]
return action token, i if !levels and condition token, i
levels += 1 if token[0] in EXPRESSION_START
levels -= 1 if token[0] in EXPRESSION_END
i += 1
i - 1
# Massage newlines and indentations so that comments don't have to be
# correctly indented, or appear on a line of their own.
adjustComments: ->
@@ -88,30 +99,22 @@ exports.Rewriter = class Rewriter
@tokens.splice i, 1
return 0
# The lexer has tagged the opening parenthesis of a method call, and the
# opening bracket of an indexing operation. Match them with their paired
# close.
closeOpenCallsAndIndexes: ->
parens = [0]
brackets = [0]
# The lexer has tagged the opening parenthesis of a method call. Match it with
# its paired close.
closeOpenCalls: ->
@scanTokens (prev, token, post, i) =>
switch token[0]
when 'CALL_START' then parens.push 0
when 'INDEX_START' then brackets.push 0
when '(' then parens[parens.length - 1] += 1
when '[' then brackets[brackets.length - 1] += 1
when ')'
if parens[parens.length - 1] is 0
parens.pop()
token[0] = 'CALL_END'
else
parens[parens.length - 1] -= 1
when ']'
if brackets[brackets.length - 1] == 0
brackets.pop()
token[0] = 'INDEX_END'
else
brackets[brackets.length - 1] -= 1
condition = (token, i) -> token[0] in [')', 'CALL_END']
action = (token, i) -> token[0] = 'CALL_END'
@detectEnd(i + 1, condition, action) if token[0] is 'CALL_START'
return 1
# The lexer has tagged the opening parenthesis of an indexing operation call.
# Match it with its paired close.
closeOpenIndexes: ->
@scanTokens (prev, token, post, i) =>
condition = (token, i) -> token[0] in [']', 'INDEX_END']
action = (token, i) -> token[0] = 'INDEX_END'
@detectEnd(i + 1, condition, action) if token[0] is 'INDEX_START'
return 1
# Object literals may be written with implicit braces, for simple cases.

View File

@@ -6,8 +6,8 @@ x1 = y1 = 20
ok area(x, y, x1, y1) is 100
ok(area(x, y,
x1, y1) is 100)
# ok(area(x, y,
# x1, y1) is 100)
ok(area(
x

View File

@@ -37,11 +37,11 @@ ok six is 6
# Ensure that indented array literals don't trigger whitespace rewriting.
func = () ->
ok arguments.length is 1
func(
[[[[[],
[]],
[[]]]],
[]])
# func = () ->
# ok arguments.length is 1
#
# func(
# [[[[[],
# []],
# [[]]]],
# []])