* fix #3921: inline function without parentheses used in condition * add grammar rules * fix issue #2343 * typos * multiple 'else' in line * close 'else if'
This commit is contained in:
parent
74bd537d6c
commit
c804087ec9
|
@ -97,7 +97,7 @@
|
|||
// Block and statements, which make up a line in a body. YieldReturn is a
|
||||
// statement, but not included in Statement because that results in an ambiguous
|
||||
// grammar.
|
||||
Line: [o('Expression'), o('Statement'), o('FuncDirective')],
|
||||
Line: [o('Expression'), o('ExpressionLine'), o('Statement'), o('FuncDirective')],
|
||||
FuncDirective: [o('YieldReturn'), o('AwaitReturn')],
|
||||
// Pure statements which cannot be expressions.
|
||||
Statement: [
|
||||
|
@ -114,6 +114,10 @@
|
|||
// is one. Blocks serve as the building blocks of many other rules, making
|
||||
// them somewhat circular.
|
||||
Expression: [o('Value'), o('Code'), o('Operation'), o('Assign'), o('If'), o('Try'), o('While'), o('For'), o('Switch'), o('Class'), o('Throw'), o('Yield')],
|
||||
// Expressions which are written in single line and would otherwise require being
|
||||
// wrapped in braces: E.g `a = b if do -> f a is 1`, `if f (a) -> a*2 then ...`,
|
||||
// `for x in do (obj) -> f obj when x > 8 then f x`
|
||||
ExpressionLine: [o('CodeLine'), o('IfLine'), o('OperationLine')],
|
||||
Yield: [
|
||||
o('YIELD',
|
||||
function() {
|
||||
|
@ -407,6 +411,22 @@
|
|||
$1);
|
||||
})
|
||||
],
|
||||
// The Codeline is the **Code** node with **Line** instead of indented **Block**.
|
||||
CodeLine: [
|
||||
o('PARAM_START ParamList PARAM_END FuncGlyph Line',
|
||||
function() {
|
||||
return new Code($2,
|
||||
LOC(5)(Block.wrap([$5])),
|
||||
$4,
|
||||
LOC(1)(new Literal($1)));
|
||||
}),
|
||||
o('FuncGlyph Line',
|
||||
function() {
|
||||
return new Code([],
|
||||
LOC(2)(Block.wrap([$2])),
|
||||
$1);
|
||||
})
|
||||
],
|
||||
// CoffeeScript has two different symbols for functions. `->` is for ordinary
|
||||
// functions, and `=>` is for functions bound to the current value of *this*.
|
||||
FuncGlyph: [
|
||||
|
@ -990,6 +1010,12 @@
|
|||
return new Range($2,
|
||||
$4,
|
||||
$3);
|
||||
}),
|
||||
o('[ ExpressionLine RangeDots Expression ]',
|
||||
function() {
|
||||
return new Range($2,
|
||||
$4,
|
||||
$3);
|
||||
})
|
||||
],
|
||||
// Array slice literals.
|
||||
|
@ -1006,6 +1032,18 @@
|
|||
null,
|
||||
$2);
|
||||
}),
|
||||
o('ExpressionLine RangeDots Expression',
|
||||
function() {
|
||||
return new Range($1,
|
||||
$3,
|
||||
$2);
|
||||
}),
|
||||
o('ExpressionLine RangeDots',
|
||||
function() {
|
||||
return new Range($1,
|
||||
null,
|
||||
$2);
|
||||
}),
|
||||
o('RangeDots Expression',
|
||||
function() {
|
||||
return new Range(null,
|
||||
|
@ -1046,6 +1084,7 @@
|
|||
// Valid arguments are Blocks or Splats.
|
||||
Arg: [
|
||||
o('Expression'),
|
||||
o('ExpressionLine'),
|
||||
o('Splat'),
|
||||
o('...',
|
||||
function() {
|
||||
|
@ -1117,10 +1156,16 @@
|
|||
// having the newlines wouldn't make sense.
|
||||
SimpleArgs: [
|
||||
o('Expression'),
|
||||
o('ExpressionLine'),
|
||||
o('SimpleArgs , Expression',
|
||||
function() {
|
||||
return [].concat($1,
|
||||
$3);
|
||||
}),
|
||||
o('SimpleArgs , ExpressionLine',
|
||||
function() {
|
||||
return [].concat($1,
|
||||
$3);
|
||||
})
|
||||
],
|
||||
// The variants of *try/catch/finally* exception handling blocks.
|
||||
|
@ -1194,6 +1239,34 @@
|
|||
})
|
||||
],
|
||||
// The condition portion of a while loop.
|
||||
WhileLineSource: [
|
||||
o('WHILE ExpressionLine',
|
||||
function() {
|
||||
return new While($2);
|
||||
}),
|
||||
o('WHILE ExpressionLine WHEN ExpressionLine',
|
||||
function() {
|
||||
return new While($2,
|
||||
{
|
||||
guard: $4
|
||||
});
|
||||
}),
|
||||
o('UNTIL ExpressionLine',
|
||||
function() {
|
||||
return new While($2,
|
||||
{
|
||||
invert: true
|
||||
});
|
||||
}),
|
||||
o('UNTIL ExpressionLine WHEN ExpressionLine',
|
||||
function() {
|
||||
return new While($2,
|
||||
{
|
||||
invert: true,
|
||||
guard: $4
|
||||
});
|
||||
})
|
||||
],
|
||||
WhileSource: [
|
||||
o('WHILE Expression',
|
||||
function() {
|
||||
|
@ -1206,6 +1279,13 @@
|
|||
guard: $4
|
||||
});
|
||||
}),
|
||||
o('WHILE ExpressionLine WHEN Expression',
|
||||
function() {
|
||||
return new While($2,
|
||||
{
|
||||
guard: $4
|
||||
});
|
||||
}),
|
||||
o('UNTIL Expression',
|
||||
function() {
|
||||
return new While($2,
|
||||
|
@ -1214,6 +1294,14 @@
|
|||
});
|
||||
}),
|
||||
o('UNTIL Expression WHEN Expression',
|
||||
function() {
|
||||
return new While($2,
|
||||
{
|
||||
invert: true,
|
||||
guard: $4
|
||||
});
|
||||
}),
|
||||
o('UNTIL ExpressionLine WHEN Expression',
|
||||
function() {
|
||||
return new While($2,
|
||||
{
|
||||
|
@ -1229,6 +1317,10 @@
|
|||
function() {
|
||||
return $1.addBody($2);
|
||||
}),
|
||||
o('WhileLineSource Block',
|
||||
function() {
|
||||
return $1.addBody($2);
|
||||
}),
|
||||
o('Statement WhileSource',
|
||||
function() {
|
||||
return $2.addBody(LOC(1)(Block.wrap([$1])));
|
||||
|
@ -1270,6 +1362,11 @@
|
|||
function() {
|
||||
return new For($2,
|
||||
$1);
|
||||
}),
|
||||
o('ForLineBody Block',
|
||||
function() {
|
||||
return new For($2,
|
||||
$1);
|
||||
})
|
||||
],
|
||||
ForBody: [
|
||||
|
@ -1295,6 +1392,23 @@
|
|||
return $2;
|
||||
})
|
||||
],
|
||||
ForLineBody: [
|
||||
o('FOR Range BY ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: LOC(2)(new Value($2)),
|
||||
step: $4
|
||||
};
|
||||
}),
|
||||
o('ForStart ForLineSource',
|
||||
function() {
|
||||
$2.own = $1.own;
|
||||
$2.ownTag = $1.ownTag;
|
||||
$2.name = $1[0];
|
||||
$2.index = $1[1];
|
||||
return $2;
|
||||
})
|
||||
],
|
||||
ForStart: [
|
||||
o('FOR ForVariables',
|
||||
function() {
|
||||
|
@ -1359,6 +1473,13 @@
|
|||
guard: $4
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine WHEN Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4
|
||||
};
|
||||
}),
|
||||
o('FOROF Expression WHEN Expression',
|
||||
function() {
|
||||
return {
|
||||
|
@ -1367,6 +1488,14 @@
|
|||
object: true
|
||||
};
|
||||
}),
|
||||
o('FOROF ExpressionLine WHEN Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
object: true
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression BY Expression',
|
||||
function() {
|
||||
return {
|
||||
|
@ -1374,6 +1503,13 @@
|
|||
step: $4
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine BY Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
step: $4
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression WHEN Expression BY Expression',
|
||||
function() {
|
||||
return {
|
||||
|
@ -1382,6 +1518,30 @@
|
|||
step: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine WHEN Expression BY Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
step: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression WHEN ExpressionLine BY Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
step: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine WHEN ExpressionLine BY Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
step: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression BY Expression WHEN Expression',
|
||||
function() {
|
||||
return {
|
||||
|
@ -1390,6 +1550,30 @@
|
|||
guard: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine BY Expression WHEN Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
step: $4,
|
||||
guard: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression BY ExpressionLine WHEN Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
step: $4,
|
||||
guard: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine BY ExpressionLine WHEN Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
step: $4,
|
||||
guard: $6
|
||||
};
|
||||
}),
|
||||
o('FORFROM Expression',
|
||||
function() {
|
||||
return {
|
||||
|
@ -1398,6 +1582,160 @@
|
|||
};
|
||||
}),
|
||||
o('FORFROM Expression WHEN Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
from: true
|
||||
};
|
||||
}),
|
||||
o('FORFROM ExpressionLine WHEN Expression',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
from: true
|
||||
};
|
||||
})
|
||||
],
|
||||
ForLineSource: [
|
||||
o('FORIN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2
|
||||
};
|
||||
}),
|
||||
o('FOROF ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
object: true
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression WHEN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine WHEN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4
|
||||
};
|
||||
}),
|
||||
o('FOROF Expression WHEN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
object: true
|
||||
};
|
||||
}),
|
||||
o('FOROF ExpressionLine WHEN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
object: true
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression BY ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
step: $4
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine BY ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
step: $4
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression WHEN Expression BY ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
step: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine WHEN Expression BY ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
step: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression WHEN ExpressionLine BY ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
step: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine WHEN ExpressionLine BY ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
step: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression BY Expression WHEN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
step: $4,
|
||||
guard: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine BY Expression WHEN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
step: $4,
|
||||
guard: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN Expression BY ExpressionLine WHEN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
step: $4,
|
||||
guard: $6
|
||||
};
|
||||
}),
|
||||
o('FORIN ExpressionLine BY ExpressionLine WHEN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
step: $4,
|
||||
guard: $6
|
||||
};
|
||||
}),
|
||||
o('FORFROM ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
from: true
|
||||
};
|
||||
}),
|
||||
o('FORFROM Expression WHEN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
from: true
|
||||
};
|
||||
}),
|
||||
o('FORFROM ExpressionLine WHEN ExpressionLine',
|
||||
function() {
|
||||
return {
|
||||
source: $2,
|
||||
|
@ -1412,12 +1750,23 @@
|
|||
return new Switch($2,
|
||||
$4);
|
||||
}),
|
||||
o('SWITCH ExpressionLine INDENT Whens OUTDENT',
|
||||
function() {
|
||||
return new Switch($2,
|
||||
$4);
|
||||
}),
|
||||
o('SWITCH Expression INDENT Whens ELSE Block OUTDENT',
|
||||
function() {
|
||||
return new Switch($2,
|
||||
$4,
|
||||
$6);
|
||||
}),
|
||||
o('SWITCH ExpressionLine INDENT Whens ELSE Block OUTDENT',
|
||||
function() {
|
||||
return new Switch($2,
|
||||
$4,
|
||||
$6);
|
||||
}),
|
||||
o('SWITCH INDENT Whens OUTDENT',
|
||||
function() {
|
||||
return new Switch(null,
|
||||
|
@ -1499,12 +1848,63 @@
|
|||
});
|
||||
})
|
||||
],
|
||||
IfBlockLine: [
|
||||
o('IF ExpressionLine Block',
|
||||
function() {
|
||||
return new If($2,
|
||||
$3,
|
||||
{
|
||||
type: $1
|
||||
});
|
||||
}),
|
||||
o('IfBlockLine ELSE IF ExpressionLine Block',
|
||||
function() {
|
||||
return $1.addElse(LOC(3,
|
||||
5)(new If($4,
|
||||
$5,
|
||||
{
|
||||
type: $3
|
||||
})));
|
||||
})
|
||||
],
|
||||
IfLine: [
|
||||
o('IfBlockLine'),
|
||||
o('IfBlockLine ELSE Block',
|
||||
function() {
|
||||
return $1.addElse($3);
|
||||
}),
|
||||
o('Statement POST_IF ExpressionLine',
|
||||
function() {
|
||||
return new If($3,
|
||||
LOC(1)(Block.wrap([$1])),
|
||||
{
|
||||
type: $2,
|
||||
statement: true
|
||||
});
|
||||
}),
|
||||
o('Expression POST_IF ExpressionLine',
|
||||
function() {
|
||||
return new If($3,
|
||||
LOC(1)(Block.wrap([$1])),
|
||||
{
|
||||
type: $2,
|
||||
statement: true
|
||||
});
|
||||
})
|
||||
],
|
||||
// Arithmetic and logical operators, working on one or more operands.
|
||||
// Here they are grouped by order of precedence. The actual precedence rules
|
||||
// are defined at the bottom of the page. It would be shorter if we could
|
||||
// combine most of these rules into a single generic *Operand OpSymbol Operand*
|
||||
// -type rule, but in order to make the precedence binding possible, separate
|
||||
// rules are necessary.
|
||||
OperationLine: [
|
||||
o('UNARY ExpressionLine',
|
||||
function() {
|
||||
return new Op($1,
|
||||
$2);
|
||||
})
|
||||
],
|
||||
Operation: [
|
||||
o('UNARY Expression',
|
||||
function() {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -506,7 +506,7 @@
|
|||
}
|
||||
}
|
||||
newLine = prevTag === 'OUTDENT' || prevToken.newLine;
|
||||
if (indexOf.call(IMPLICIT_END, tag) >= 0 || indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) {
|
||||
if (indexOf.call(IMPLICIT_END, tag) >= 0 || (indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) || ((tag === '..' || tag === '...') && this.findTagsBackwards(i, ["INDEX_START"]))) {
|
||||
while (inImplicit()) {
|
||||
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop();
|
||||
// Close implicit calls when reached end of argument list
|
||||
|
@ -781,18 +781,51 @@
|
|||
// newlines within expressions are removed and the indentation tokens of empty
|
||||
// blocks are added.
|
||||
normalizeLines() {
|
||||
var action, condition, indent, outdent, starter;
|
||||
var action, closeElseTag, condition, ifThens, indent, leading_if_then, leading_switch_when, outdent, starter;
|
||||
starter = indent = outdent = null;
|
||||
leading_switch_when = null;
|
||||
leading_if_then = null;
|
||||
// Count `THEN` tags
|
||||
ifThens = [];
|
||||
condition = function(token, i) {
|
||||
var ref, ref1, ref2, ref3;
|
||||
return token[1] !== ';' && (ref = token[0], indexOf.call(SINGLE_CLOSERS, ref) >= 0) && !(token[0] === 'TERMINATOR' && (ref1 = this.tag(i + 1), indexOf.call(EXPRESSION_CLOSE, ref1) >= 0)) && !(token[0] === 'ELSE' && starter !== 'THEN') && !(((ref2 = token[0]) === 'CATCH' || ref2 === 'FINALLY') && (starter === '->' || starter === '=>')) || (ref3 = token[0], indexOf.call(CALL_CLOSERS, ref3) >= 0) && (this.tokens[i - 1].newLine || this.tokens[i - 1][0] === 'OUTDENT');
|
||||
return token[1] !== ';' && (ref = token[0], indexOf.call(SINGLE_CLOSERS, ref) >= 0) && !(token[0] === 'TERMINATOR' && (ref1 = this.tag(i + 1), indexOf.call(EXPRESSION_CLOSE, ref1) >= 0)) && !(token[0] === 'ELSE' && (starter !== 'THEN' || (leading_if_then || leading_switch_when))) && !(((ref2 = token[0]) === 'CATCH' || ref2 === 'FINALLY') && (starter === '->' || starter === '=>')) || (ref3 = token[0], indexOf.call(CALL_CLOSERS, ref3) >= 0) && (this.tokens[i - 1].newLine || this.tokens[i - 1][0] === 'OUTDENT');
|
||||
};
|
||||
action = function(token, i) {
|
||||
if (token[0] === 'ELSE' && starter === 'THEN') {
|
||||
ifThens.pop();
|
||||
}
|
||||
return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent);
|
||||
};
|
||||
closeElseTag = (tokens, i) => {
|
||||
var lastThen, outdentElse, tlen;
|
||||
tlen = ifThens.length;
|
||||
if (!(tlen > 0)) {
|
||||
return i;
|
||||
}
|
||||
lastThen = ifThens.pop();
|
||||
[, outdentElse] = this.indentation(tokens[lastThen]);
|
||||
// Insert `OUTDENT` to close inner `IF`.
|
||||
outdentElse[1] = tlen * 2;
|
||||
tokens.splice(i, 0, outdentElse);
|
||||
// Insert `OUTDENT` to close outer `IF`.
|
||||
outdentElse[1] = 2;
|
||||
tokens.splice(i + 1, 0, outdentElse);
|
||||
// Remove outdents from the end.
|
||||
this.detectEnd(i + 2, function(token, i) {
|
||||
var ref;
|
||||
return (ref = token[0]) === 'OUTDENT' || ref === 'TERMINATOR';
|
||||
}, function(token, i) {
|
||||
if (this.tag(i) === 'OUTDENT' && this.tag(i + 1) === 'OUTDENT') {
|
||||
return tokens.splice(i, 2);
|
||||
}
|
||||
});
|
||||
return i + 2;
|
||||
};
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var j, k, ref, ref1, tag;
|
||||
var conditionTag, j, k, ref, ref1, tag;
|
||||
[tag] = token;
|
||||
conditionTag = (tag === '->' || tag === '=>') && this.findTagsBackwards(i, ['IF', 'WHILE', 'FOR', 'UNTIL', 'SWITCH', 'WHEN', 'LEADING_WHEN', '[', 'INDEX_START']) && !(this.findTagsBackwards(i, ['THEN', '..', '...']));
|
||||
if (tag === 'TERMINATOR') {
|
||||
if (this.tag(i + 1) === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
|
||||
tokens.splice(i, 1, ...this.indentation());
|
||||
|
@ -817,12 +850,23 @@
|
|||
tokens.splice(i + 1, 0, indent, outdent);
|
||||
return 1;
|
||||
}
|
||||
if (indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) {
|
||||
if (indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF' && ifThens.length > 1) && !conditionTag) {
|
||||
starter = tag;
|
||||
[indent, outdent] = this.indentation(tokens[i]);
|
||||
if (starter === 'THEN') {
|
||||
indent.fromThen = true;
|
||||
}
|
||||
if (tag === 'THEN') {
|
||||
leading_switch_when = this.findTagsBackwards(i, ['LEADING_WHEN']) && this.tag(i + 1) === 'IF';
|
||||
leading_if_then = this.findTagsBackwards(i, ['IF']) && this.tag(i + 1) === 'IF';
|
||||
}
|
||||
if (tag === 'THEN' && this.findTagsBackwards(i, ['IF'])) {
|
||||
ifThens.push(i);
|
||||
}
|
||||
// `ELSE` tag is not closed.
|
||||
if (tag === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
|
||||
i = closeElseTag(tokens, i);
|
||||
}
|
||||
tokens.splice(i + 1, 0, indent);
|
||||
this.detectEnd(i + 2, condition, action);
|
||||
if (tag === 'THEN') {
|
||||
|
|
|
@ -89,6 +89,7 @@ grammar =
|
|||
# grammar.
|
||||
Line: [
|
||||
o 'Expression'
|
||||
o 'ExpressionLine'
|
||||
o 'Statement'
|
||||
o 'FuncDirective'
|
||||
]
|
||||
|
@ -125,6 +126,15 @@ grammar =
|
|||
o 'Yield'
|
||||
]
|
||||
|
||||
# Expressions which are written in single line and would otherwise require being
|
||||
# wrapped in braces: E.g `a = b if do -> f a is 1`, `if f (a) -> a*2 then ...`,
|
||||
# `for x in do (obj) -> f obj when x > 8 then f x`
|
||||
ExpressionLine: [
|
||||
o 'CodeLine'
|
||||
o 'IfLine'
|
||||
o 'OperationLine'
|
||||
]
|
||||
|
||||
Yield: [
|
||||
o 'YIELD', -> new Op $1, new Value new Literal ''
|
||||
o 'YIELD Expression', -> new Op $1, $2
|
||||
|
@ -267,6 +277,13 @@ grammar =
|
|||
o 'FuncGlyph Block', -> new Code [], $2, $1
|
||||
]
|
||||
|
||||
# The Codeline is the **Code** node with **Line** instead of indented **Block**.
|
||||
CodeLine: [
|
||||
o 'PARAM_START ParamList PARAM_END FuncGlyph Line', -> new Code $2, LOC(5)(Block.wrap [$5]), $4,
|
||||
LOC(1)(new Literal $1)
|
||||
o 'FuncGlyph Line', -> new Code [], LOC(2)(Block.wrap [$2]), $1
|
||||
]
|
||||
|
||||
# CoffeeScript has two different symbols for functions. `->` is for ordinary
|
||||
# functions, and `=>` is for functions bound to the current value of *this*.
|
||||
FuncGlyph: [
|
||||
|
@ -506,13 +523,16 @@ grammar =
|
|||
|
||||
# The CoffeeScript range literal.
|
||||
Range: [
|
||||
o '[ Expression RangeDots Expression ]', -> new Range $2, $4, $3
|
||||
o '[ Expression RangeDots Expression ]', -> new Range $2, $4, $3
|
||||
o '[ ExpressionLine RangeDots Expression ]', -> new Range $2, $4, $3
|
||||
]
|
||||
|
||||
# Array slice literals.
|
||||
Slice: [
|
||||
o 'Expression RangeDots Expression', -> new Range $1, $3, $2
|
||||
o 'Expression RangeDots', -> new Range $1, null, $2
|
||||
o 'ExpressionLine RangeDots Expression', -> new Range $1, $3, $2
|
||||
o 'ExpressionLine RangeDots', -> new Range $1, null, $2
|
||||
o 'RangeDots Expression', -> new Range null, $2, $1
|
||||
o 'RangeDots', -> new Range null, null, $1
|
||||
]
|
||||
|
@ -530,6 +550,7 @@ grammar =
|
|||
# Valid arguments are Blocks or Splats.
|
||||
Arg: [
|
||||
o 'Expression'
|
||||
o 'ExpressionLine'
|
||||
o 'Splat'
|
||||
o '...', -> new Expansion
|
||||
]
|
||||
|
@ -568,7 +589,9 @@ grammar =
|
|||
# having the newlines wouldn't make sense.
|
||||
SimpleArgs: [
|
||||
o 'Expression'
|
||||
o 'ExpressionLine'
|
||||
o 'SimpleArgs , Expression', -> [].concat $1, $3
|
||||
o 'SimpleArgs , ExpressionLine', -> [].concat $1, $3
|
||||
]
|
||||
|
||||
# The variants of *try/catch/finally* exception handling blocks.
|
||||
|
@ -602,17 +625,27 @@ grammar =
|
|||
]
|
||||
|
||||
# The condition portion of a while loop.
|
||||
WhileLineSource: [
|
||||
o 'WHILE ExpressionLine', -> new While $2
|
||||
o 'WHILE ExpressionLine WHEN ExpressionLine', -> new While $2, guard: $4
|
||||
o 'UNTIL ExpressionLine', -> new While $2, invert: true
|
||||
o 'UNTIL ExpressionLine WHEN ExpressionLine', -> new While $2, invert: true, guard: $4
|
||||
]
|
||||
|
||||
WhileSource: [
|
||||
o 'WHILE Expression', -> new While $2
|
||||
o 'WHILE Expression WHEN Expression', -> new While $2, guard: $4
|
||||
o 'WHILE ExpressionLine WHEN Expression', -> new While $2, guard: $4
|
||||
o 'UNTIL Expression', -> new While $2, invert: true
|
||||
o 'UNTIL Expression WHEN Expression', -> new While $2, invert: true, guard: $4
|
||||
o 'UNTIL ExpressionLine WHEN Expression', -> new While $2, invert: true, guard: $4
|
||||
]
|
||||
|
||||
# The while loop can either be normal, with a block of expressions to execute,
|
||||
# or postfix, with a single expression. There is no do..while.
|
||||
While: [
|
||||
o 'WhileSource Block', -> $1.addBody $2
|
||||
o 'WhileLineSource Block', -> $1.addBody $2
|
||||
o 'Statement WhileSource', -> $2.addBody LOC(1) Block.wrap([$1])
|
||||
o 'Expression WhileSource', -> $2.addBody LOC(1) Block.wrap([$1])
|
||||
o 'Loop', -> $1
|
||||
|
@ -630,6 +663,7 @@ grammar =
|
|||
o 'Statement ForBody', -> new For $1, $2
|
||||
o 'Expression ForBody', -> new For $1, $2
|
||||
o 'ForBody Block', -> new For $2, $1
|
||||
o 'ForLineBody Block', -> new For $2, $1
|
||||
]
|
||||
|
||||
ForBody: [
|
||||
|
@ -638,6 +672,11 @@ grammar =
|
|||
o 'ForStart ForSource', -> $2.own = $1.own; $2.ownTag = $1.ownTag; $2.name = $1[0]; $2.index = $1[1]; $2
|
||||
]
|
||||
|
||||
ForLineBody: [
|
||||
o 'FOR Range BY ExpressionLine', -> source: (LOC(2) new Value($2)), step: $4
|
||||
o 'ForStart ForLineSource', -> $2.own = $1.own; $2.ownTag = $1.ownTag; $2.name = $1[0]; $2.index = $1[1]; $2
|
||||
]
|
||||
|
||||
ForStart: [
|
||||
o 'FOR ForVariables', -> $2
|
||||
o 'FOR OWN ForVariables', -> $3.own = yes; $3.ownTag = (LOC(2) new Literal($2)); $3
|
||||
|
@ -664,22 +703,56 @@ grammar =
|
|||
# clause. If it's an array comprehension, you can also choose to step through
|
||||
# in fixed-size increments.
|
||||
ForSource: [
|
||||
o 'FORIN Expression', -> source: $2
|
||||
o 'FOROF Expression', -> source: $2, object: yes
|
||||
o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4
|
||||
o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes
|
||||
o 'FORIN Expression BY Expression', -> source: $2, step: $4
|
||||
o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
|
||||
o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
|
||||
o 'FORFROM Expression', -> source: $2, from: yes
|
||||
o 'FORFROM Expression WHEN Expression', -> source: $2, guard: $4, from: yes
|
||||
o 'FORIN Expression', -> source: $2
|
||||
o 'FOROF Expression', -> source: $2, object: yes
|
||||
o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4
|
||||
o 'FORIN ExpressionLine WHEN Expression', -> source: $2, guard: $4
|
||||
o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes
|
||||
o 'FOROF ExpressionLine WHEN Expression', -> source: $2, guard: $4, object: yes
|
||||
o 'FORIN Expression BY Expression', -> source: $2, step: $4
|
||||
o 'FORIN ExpressionLine BY Expression', -> source: $2, step: $4
|
||||
o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
|
||||
o 'FORIN ExpressionLine WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
|
||||
o 'FORIN Expression WHEN ExpressionLine BY Expression', -> source: $2, guard: $4, step: $6
|
||||
o 'FORIN ExpressionLine WHEN ExpressionLine BY Expression', -> source: $2, guard: $4, step: $6
|
||||
o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
|
||||
o 'FORIN ExpressionLine BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
|
||||
o 'FORIN Expression BY ExpressionLine WHEN Expression', -> source: $2, step: $4, guard: $6
|
||||
o 'FORIN ExpressionLine BY ExpressionLine WHEN Expression', -> source: $2, step: $4, guard: $6
|
||||
o 'FORFROM Expression', -> source: $2, from: yes
|
||||
o 'FORFROM Expression WHEN Expression', -> source: $2, guard: $4, from: yes
|
||||
o 'FORFROM ExpressionLine WHEN Expression', -> source: $2, guard: $4, from: yes
|
||||
]
|
||||
|
||||
ForLineSource: [
|
||||
o 'FORIN ExpressionLine', -> source: $2
|
||||
o 'FOROF ExpressionLine', -> source: $2, object: yes
|
||||
o 'FORIN Expression WHEN ExpressionLine', -> source: $2, guard: $4
|
||||
o 'FORIN ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4
|
||||
o 'FOROF Expression WHEN ExpressionLine', -> source: $2, guard: $4, object: yes
|
||||
o 'FOROF ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4, object: yes
|
||||
o 'FORIN Expression BY ExpressionLine', -> source: $2, step: $4
|
||||
o 'FORIN ExpressionLine BY ExpressionLine', -> source: $2, step: $4
|
||||
o 'FORIN Expression WHEN Expression BY ExpressionLine', -> source: $2, guard: $4, step: $6
|
||||
o 'FORIN ExpressionLine WHEN Expression BY ExpressionLine', -> source: $2, guard: $4, step: $6
|
||||
o 'FORIN Expression WHEN ExpressionLine BY ExpressionLine', -> source: $2, guard: $4, step: $6
|
||||
o 'FORIN ExpressionLine WHEN ExpressionLine BY ExpressionLine', -> source: $2, guard: $4, step: $6
|
||||
o 'FORIN Expression BY Expression WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
|
||||
o 'FORIN ExpressionLine BY Expression WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
|
||||
o 'FORIN Expression BY ExpressionLine WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
|
||||
o 'FORIN ExpressionLine BY ExpressionLine WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
|
||||
o 'FORFROM ExpressionLine', -> source: $2, from: yes
|
||||
o 'FORFROM Expression WHEN ExpressionLine', -> source: $2, guard: $4, from: yes
|
||||
o 'FORFROM ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4, from: yes
|
||||
]
|
||||
|
||||
Switch: [
|
||||
o 'SWITCH Expression INDENT Whens OUTDENT', -> new Switch $2, $4
|
||||
o 'SWITCH Expression INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, $6
|
||||
o 'SWITCH INDENT Whens OUTDENT', -> new Switch null, $3
|
||||
o 'SWITCH INDENT Whens ELSE Block OUTDENT', -> new Switch null, $3, $5
|
||||
o 'SWITCH Expression INDENT Whens OUTDENT', -> new Switch $2, $4
|
||||
o 'SWITCH ExpressionLine INDENT Whens OUTDENT', -> new Switch $2, $4
|
||||
o 'SWITCH Expression INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, $6
|
||||
o 'SWITCH ExpressionLine INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, $6
|
||||
o 'SWITCH INDENT Whens OUTDENT', -> new Switch null, $3
|
||||
o 'SWITCH INDENT Whens ELSE Block OUTDENT', -> new Switch null, $3, $5
|
||||
]
|
||||
|
||||
Whens: [
|
||||
|
@ -710,12 +783,28 @@ grammar =
|
|||
o 'Expression POST_IF Expression', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true
|
||||
]
|
||||
|
||||
IfBlockLine: [
|
||||
o 'IF ExpressionLine Block', -> new If $2, $3, type: $1
|
||||
o 'IfBlockLine ELSE IF ExpressionLine Block', -> $1.addElse LOC(3,5) new If $4, $5, type: $3
|
||||
]
|
||||
|
||||
IfLine: [
|
||||
o 'IfBlockLine'
|
||||
o 'IfBlockLine ELSE Block', -> $1.addElse $3
|
||||
o 'Statement POST_IF ExpressionLine', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true
|
||||
o 'Expression POST_IF ExpressionLine', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true
|
||||
]
|
||||
|
||||
# Arithmetic and logical operators, working on one or more operands.
|
||||
# Here they are grouped by order of precedence. The actual precedence rules
|
||||
# are defined at the bottom of the page. It would be shorter if we could
|
||||
# combine most of these rules into a single generic *Operand OpSymbol Operand*
|
||||
# -type rule, but in order to make the precedence binding possible, separate
|
||||
# rules are necessary.
|
||||
OperationLine: [
|
||||
o 'UNARY ExpressionLine', -> new Op $1, $2
|
||||
]
|
||||
|
||||
Operation: [
|
||||
o 'UNARY Expression', -> new Op $1 , $2
|
||||
o 'UNARY_MATH Expression', -> new Op $1 , $2
|
||||
|
|
|
@ -343,7 +343,9 @@ exports.Rewriter = class Rewriter
|
|||
stackItem[2].sameLine = no if isImplicitObject stackItem
|
||||
|
||||
newLine = prevTag is 'OUTDENT' or prevToken.newLine
|
||||
if tag in IMPLICIT_END or tag in CALL_CLOSERS and newLine
|
||||
if tag in IMPLICIT_END or
|
||||
(tag in CALL_CLOSERS and newLine) or
|
||||
(tag in ['..', '...'] and @findTagsBackwards(i, ["INDEX_START"]))
|
||||
while inImplicit()
|
||||
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()
|
||||
# Close implicit calls when reached end of argument list
|
||||
|
@ -544,20 +546,49 @@ exports.Rewriter = class Rewriter
|
|||
# blocks are added.
|
||||
normalizeLines: ->
|
||||
starter = indent = outdent = null
|
||||
leading_switch_when = null
|
||||
leading_if_then = null
|
||||
# Count `THEN` tags
|
||||
ifThens = []
|
||||
|
||||
condition = (token, i) ->
|
||||
token[1] isnt ';' and token[0] in SINGLE_CLOSERS and
|
||||
not (token[0] is 'TERMINATOR' and @tag(i + 1) in EXPRESSION_CLOSE) and
|
||||
not (token[0] is 'ELSE' and starter isnt 'THEN') and
|
||||
not (token[0] is 'ELSE' and
|
||||
(starter isnt 'THEN' or (leading_if_then or leading_switch_when))) and
|
||||
not (token[0] in ['CATCH', 'FINALLY'] and starter in ['->', '=>']) or
|
||||
token[0] in CALL_CLOSERS and
|
||||
(@tokens[i - 1].newLine or @tokens[i - 1][0] is 'OUTDENT')
|
||||
|
||||
action = (token, i) ->
|
||||
ifThens.pop() if token[0] is 'ELSE' and starter is 'THEN'
|
||||
@tokens.splice (if @tag(i - 1) is ',' then i - 1 else i), 0, outdent
|
||||
|
||||
closeElseTag = (tokens, i) =>
|
||||
tlen = ifThens.length
|
||||
return i unless tlen > 0
|
||||
lastThen = ifThens.pop()
|
||||
[, outdentElse] = @indentation tokens[lastThen]
|
||||
# Insert `OUTDENT` to close inner `IF`.
|
||||
outdentElse[1] = tlen*2
|
||||
tokens.splice(i, 0, outdentElse)
|
||||
# Insert `OUTDENT` to close outer `IF`.
|
||||
outdentElse[1] = 2
|
||||
tokens.splice(i + 1, 0, outdentElse)
|
||||
# Remove outdents from the end.
|
||||
@detectEnd i + 2,
|
||||
(token, i) -> token[0] in ['OUTDENT', 'TERMINATOR']
|
||||
(token, i) ->
|
||||
if @tag(i) is 'OUTDENT' and @tag(i + 1) is 'OUTDENT'
|
||||
tokens.splice i, 2
|
||||
i + 2
|
||||
|
||||
@scanTokens (token, i, tokens) ->
|
||||
[tag] = token
|
||||
conditionTag = tag in ['->', '=>'] and
|
||||
@findTagsBackwards(i, ['IF', 'WHILE', 'FOR', 'UNTIL', 'SWITCH', 'WHEN', 'LEADING_WHEN', '[', 'INDEX_START']) and
|
||||
not (@findTagsBackwards i, ['THEN', '..', '...'])
|
||||
|
||||
if tag is 'TERMINATOR'
|
||||
if @tag(i + 1) is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
|
||||
tokens.splice i, 1, @indentation()...
|
||||
|
@ -574,10 +605,18 @@ exports.Rewriter = class Rewriter
|
|||
tokens.splice i + 1, 0, indent, outdent
|
||||
return 1
|
||||
if tag in SINGLE_LINERS and @tag(i + 1) isnt 'INDENT' and
|
||||
not (tag is 'ELSE' and @tag(i + 1) is 'IF')
|
||||
not (tag is 'ELSE' and @tag(i + 1) is 'IF' and ifThens.length > 1) and
|
||||
not conditionTag
|
||||
starter = tag
|
||||
[indent, outdent] = @indentation tokens[i]
|
||||
indent.fromThen = true if starter is 'THEN'
|
||||
if tag is 'THEN'
|
||||
leading_switch_when = @findTagsBackwards(i, ['LEADING_WHEN']) and @tag(i + 1) is 'IF'
|
||||
leading_if_then = @findTagsBackwards(i, ['IF']) and @tag(i + 1) is 'IF'
|
||||
ifThens.push i if tag is 'THEN' and @findTagsBackwards(i, ['IF'])
|
||||
# `ELSE` tag is not closed.
|
||||
if tag is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
|
||||
i = closeElseTag tokens, i
|
||||
tokens.splice i + 1, 0, indent
|
||||
@detectEnd i + 2, condition, action
|
||||
tokens.splice i, 1 if tag is 'THEN'
|
||||
|
|
|
@ -485,6 +485,633 @@ test "#4267: lots of for-loops in the same scope", ->
|
|||
"""
|
||||
ok CoffeeScript.eval(code)
|
||||
|
||||
# Test for issue #2342: Lexer: Inline `else` binds to wrong `if`/`switch`
|
||||
test "#2343: if / then / if / then / else", ->
|
||||
a = b = yes
|
||||
c = e = g = no
|
||||
d = 1
|
||||
f = 2
|
||||
h = 3
|
||||
i = 4
|
||||
|
||||
s = ->
|
||||
if a
|
||||
if b
|
||||
if c
|
||||
d
|
||||
else
|
||||
if e
|
||||
f
|
||||
else
|
||||
if g
|
||||
h
|
||||
else
|
||||
i
|
||||
|
||||
t = ->
|
||||
if a then if b
|
||||
if c then d
|
||||
else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else
|
||||
i
|
||||
|
||||
u = ->
|
||||
if a then if b
|
||||
if c then d else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else i
|
||||
|
||||
v = ->
|
||||
if a then if b
|
||||
if c then d else if e then f
|
||||
else if g then h
|
||||
else i
|
||||
|
||||
w = ->
|
||||
if a then if b
|
||||
if c then d
|
||||
else if e
|
||||
f
|
||||
else
|
||||
if g then h
|
||||
else i
|
||||
|
||||
x = -> if a then if b then if c then d else if e then f else if g then h else i
|
||||
|
||||
y = -> if a then if b then (if c then d else (if e then f else (if g then h else i)))
|
||||
|
||||
eq 4, s()
|
||||
eq 4, t()
|
||||
eq 4, u()
|
||||
eq 4, v()
|
||||
eq 4, w()
|
||||
eq 4, x()
|
||||
eq 4, y()
|
||||
|
||||
c = yes
|
||||
eq 1, s()
|
||||
eq 1, t()
|
||||
eq 1, u()
|
||||
eq 1, v()
|
||||
eq 1, w()
|
||||
eq 1, x()
|
||||
eq 1, y()
|
||||
|
||||
b = no
|
||||
eq undefined, s()
|
||||
eq undefined, t()
|
||||
eq undefined, u()
|
||||
eq undefined, v()
|
||||
eq undefined, w()
|
||||
eq undefined, x()
|
||||
eq undefined, y()
|
||||
|
||||
test "#2343: if / then / if / then / else / else", ->
|
||||
a = b = yes
|
||||
c = e = g = no
|
||||
d = 1
|
||||
f = 2
|
||||
h = 3
|
||||
i = 4
|
||||
j = 5
|
||||
k = 6
|
||||
|
||||
s = ->
|
||||
if a
|
||||
if b
|
||||
if c
|
||||
d
|
||||
else
|
||||
e
|
||||
if e
|
||||
f
|
||||
else
|
||||
if g
|
||||
h
|
||||
else
|
||||
i
|
||||
else
|
||||
j
|
||||
else
|
||||
k
|
||||
|
||||
t = ->
|
||||
if a
|
||||
if b
|
||||
if c then d
|
||||
else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else
|
||||
i
|
||||
else
|
||||
j
|
||||
else
|
||||
k
|
||||
|
||||
u = ->
|
||||
if a
|
||||
if b
|
||||
if c then d else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else i
|
||||
else j
|
||||
else k
|
||||
|
||||
v = ->
|
||||
if a
|
||||
if b
|
||||
if c then d else if e then f
|
||||
else if g then h
|
||||
else i
|
||||
else j else k
|
||||
|
||||
w = ->
|
||||
if a then if b
|
||||
if c then d
|
||||
else if e
|
||||
f
|
||||
else
|
||||
if g then h
|
||||
else i
|
||||
else j else k
|
||||
|
||||
x = -> if a then if b then if c then d else if e then f else if g then h else i else j else k
|
||||
|
||||
y = -> if a then (if b then (if c then d else (if e then f else (if g then h else i))) else j) else k
|
||||
|
||||
eq 4, s()
|
||||
eq 4, t()
|
||||
eq 4, u()
|
||||
eq 4, v()
|
||||
eq 4, w()
|
||||
eq 4, x()
|
||||
eq 4, y()
|
||||
|
||||
c = yes
|
||||
eq 1, s()
|
||||
eq 1, t()
|
||||
eq 1, u()
|
||||
eq 1, v()
|
||||
eq 1, w()
|
||||
eq 1, x()
|
||||
eq 1, y()
|
||||
|
||||
b = no
|
||||
eq 5, s()
|
||||
eq 5, t()
|
||||
eq 5, u()
|
||||
eq 5, v()
|
||||
eq 5, w()
|
||||
eq 5, x()
|
||||
eq 5, y()
|
||||
|
||||
a = no
|
||||
eq 6, s()
|
||||
eq 6, t()
|
||||
eq 6, u()
|
||||
eq 6, v()
|
||||
eq 6, w()
|
||||
eq 6, x()
|
||||
eq 6, y()
|
||||
|
||||
|
||||
test "#2343: switch / when / then / if / then / else", ->
|
||||
a = b = yes
|
||||
c = e = g = no
|
||||
d = 1
|
||||
f = 2
|
||||
h = 3
|
||||
i = 4
|
||||
|
||||
s = ->
|
||||
switch
|
||||
when a
|
||||
if b
|
||||
if c
|
||||
d
|
||||
else
|
||||
if e
|
||||
f
|
||||
else
|
||||
if g
|
||||
h
|
||||
else
|
||||
i
|
||||
|
||||
|
||||
t = ->
|
||||
switch
|
||||
when a then if b
|
||||
if c then d
|
||||
else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else
|
||||
i
|
||||
|
||||
u = ->
|
||||
switch
|
||||
when a then if b then if c then d
|
||||
else if e then f
|
||||
else if g then h else i
|
||||
|
||||
v = ->
|
||||
switch
|
||||
when a then if b then if c then d else if e then f
|
||||
else if g then h else i
|
||||
|
||||
w = ->
|
||||
switch
|
||||
when a then if b then if c then d else if e then f
|
||||
else if g
|
||||
h
|
||||
else i
|
||||
|
||||
x = ->
|
||||
switch
|
||||
when a then if b then if c then d else if e then f else if g then h else i
|
||||
|
||||
y = -> switch
|
||||
when a then if b then (if c then d else (if e then f else (if g then h else i)))
|
||||
|
||||
eq 4, s()
|
||||
eq 4, t()
|
||||
eq 4, u()
|
||||
eq 4, v()
|
||||
eq 4, w()
|
||||
eq 4, x()
|
||||
eq 4, y()
|
||||
|
||||
c = yes
|
||||
eq 1, s()
|
||||
eq 1, t()
|
||||
eq 1, u()
|
||||
eq 1, v()
|
||||
eq 1, w()
|
||||
eq 1, x()
|
||||
eq 1, y()
|
||||
|
||||
b = no
|
||||
eq undefined, s()
|
||||
eq undefined, t()
|
||||
eq undefined, u()
|
||||
eq undefined, v()
|
||||
eq undefined, w()
|
||||
eq undefined, x()
|
||||
eq undefined, y()
|
||||
|
||||
test "#2343: switch / when / then / if / then / else / else", ->
|
||||
a = b = yes
|
||||
c = e = g = no
|
||||
d = 1
|
||||
f = 2
|
||||
h = 3
|
||||
i = 4
|
||||
|
||||
s = ->
|
||||
switch
|
||||
when a
|
||||
if b
|
||||
if c
|
||||
d
|
||||
else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else
|
||||
i
|
||||
else
|
||||
0
|
||||
|
||||
t = ->
|
||||
switch
|
||||
when a
|
||||
if b
|
||||
if c then d
|
||||
else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else i
|
||||
else 0
|
||||
|
||||
u = ->
|
||||
switch
|
||||
when a
|
||||
if b then if c
|
||||
d
|
||||
else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else i
|
||||
else 0
|
||||
|
||||
v = ->
|
||||
switch
|
||||
when a
|
||||
if b then if c then d
|
||||
else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else i
|
||||
else 0
|
||||
|
||||
w = ->
|
||||
switch
|
||||
when a
|
||||
if b then if c then d
|
||||
else if e then f
|
||||
else if g then h
|
||||
else i
|
||||
else 0
|
||||
|
||||
x = ->
|
||||
switch
|
||||
when a
|
||||
if b then if c then d else if e then f else if g then h else i
|
||||
else 0
|
||||
|
||||
y = -> switch
|
||||
when a
|
||||
if b then (if c then d else (if e then f else (if g then h else i)))
|
||||
else 0
|
||||
|
||||
eq 4, s()
|
||||
eq 4, t()
|
||||
eq 4, u()
|
||||
eq 4, v()
|
||||
eq 4, w()
|
||||
eq 4, x()
|
||||
eq 4, y()
|
||||
|
||||
c = yes
|
||||
eq 1, s()
|
||||
eq 1, t()
|
||||
eq 1, u()
|
||||
eq 1, v()
|
||||
eq 1, w()
|
||||
eq 1, x()
|
||||
eq 1, y()
|
||||
|
||||
b = no
|
||||
eq undefined, s()
|
||||
eq undefined, t()
|
||||
eq undefined, u()
|
||||
eq undefined, v()
|
||||
eq undefined, w()
|
||||
eq undefined, x()
|
||||
eq undefined, y()
|
||||
|
||||
b = yes
|
||||
a = no
|
||||
eq 0, s()
|
||||
eq 0, t()
|
||||
eq 0, u()
|
||||
eq 0, v()
|
||||
eq 0, w()
|
||||
eq 0, x()
|
||||
eq 0, y()
|
||||
|
||||
test "#2343: switch / when / then / if / then / else / else / else", ->
|
||||
a = b = yes
|
||||
c = e = g = no
|
||||
d = 1
|
||||
f = 2
|
||||
h = 3
|
||||
i = 4
|
||||
j = 5
|
||||
|
||||
s = ->
|
||||
switch
|
||||
when a
|
||||
if b
|
||||
if c
|
||||
d
|
||||
else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else
|
||||
i
|
||||
else
|
||||
j
|
||||
else
|
||||
0
|
||||
|
||||
t = ->
|
||||
switch
|
||||
when a
|
||||
if b
|
||||
if c then d
|
||||
else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else i
|
||||
else
|
||||
j
|
||||
else 0
|
||||
|
||||
u = ->
|
||||
switch
|
||||
when a
|
||||
if b
|
||||
if c
|
||||
d
|
||||
else if e
|
||||
f
|
||||
else if g
|
||||
h
|
||||
else i
|
||||
else j
|
||||
else 0
|
||||
|
||||
v = ->
|
||||
switch
|
||||
when a
|
||||
if b
|
||||
if c then d
|
||||
else if e
|
||||
f
|
||||
else if g then h
|
||||
else i
|
||||
else j
|
||||
else 0
|
||||
|
||||
w = ->
|
||||
switch
|
||||
when a
|
||||
if b
|
||||
if c then d
|
||||
else if e then f
|
||||
else if g then h
|
||||
else i
|
||||
else j
|
||||
else 0
|
||||
|
||||
x = ->
|
||||
switch
|
||||
when a
|
||||
if b then if c then d else if e then f else if g then h else i else j
|
||||
else 0
|
||||
|
||||
y = -> switch
|
||||
when a
|
||||
if b then (if c then d else (if e then f else (if g then h else i))) else j
|
||||
else 0
|
||||
|
||||
eq 4, s()
|
||||
eq 4, t()
|
||||
eq 4, u()
|
||||
eq 4, v()
|
||||
eq 4, w()
|
||||
eq 4, x()
|
||||
eq 4, y()
|
||||
|
||||
c = yes
|
||||
eq 1, s()
|
||||
eq 1, t()
|
||||
eq 1, u()
|
||||
eq 1, v()
|
||||
eq 1, w()
|
||||
eq 1, x()
|
||||
eq 1, y()
|
||||
|
||||
b = no
|
||||
eq 5, s()
|
||||
eq 5, t()
|
||||
eq 5, u()
|
||||
eq 5, v()
|
||||
eq 5, w()
|
||||
eq 5, x()
|
||||
eq 5, y()
|
||||
|
||||
b = yes
|
||||
a = no
|
||||
eq 0, s()
|
||||
eq 0, t()
|
||||
eq 0, u()
|
||||
eq 0, v()
|
||||
eq 0, w()
|
||||
eq 0, x()
|
||||
eq 0, y()
|
||||
|
||||
# Test for issue #3921: Inline function without parentheses used in condition fails to compile
|
||||
test "#3921: `if` & `unless`", ->
|
||||
a = {}
|
||||
eq a, if do -> no then undefined else a
|
||||
a1 = undefined
|
||||
if do -> yes
|
||||
a1 = a
|
||||
eq a, a1
|
||||
|
||||
b = {}
|
||||
eq b, unless do -> no then b else undefined
|
||||
b1 = undefined
|
||||
unless do -> no
|
||||
b1 = b
|
||||
eq b, b1
|
||||
|
||||
c = 0
|
||||
if (arg = undefined) -> yes then c++
|
||||
eq 1, c
|
||||
d = 0
|
||||
if (arg = undefined) -> yes
|
||||
d++
|
||||
eq 1, d
|
||||
|
||||
answer = 'correct'
|
||||
eq answer, if do -> 'wrong' then 'correct' else 'wrong'
|
||||
eq answer, unless do -> no then 'correct' else 'wrong'
|
||||
statm1 = undefined
|
||||
if do -> 'wrong'
|
||||
statm1 = 'correct'
|
||||
eq answer, statm1
|
||||
statm2 = undefined
|
||||
unless do -> no
|
||||
statm2 = 'correct'
|
||||
eq answer, statm2
|
||||
|
||||
test "#3921: `post if`", ->
|
||||
a = {}
|
||||
eq a, a unless do -> no
|
||||
a1 = a if do -> yes
|
||||
eq a, a1
|
||||
|
||||
c = 0
|
||||
c++ if (arg = undefined) -> yes
|
||||
eq 1, c
|
||||
d = 0
|
||||
d++ if (arg = undefined) -> yes
|
||||
eq 1, d
|
||||
|
||||
answer = 'correct'
|
||||
eq answer, 'correct' if do -> 'wrong'
|
||||
eq answer, 'correct' unless do -> not 'wrong'
|
||||
statm1 = undefined
|
||||
statm1 = 'correct' if do -> 'wrong'
|
||||
eq answer, statm1
|
||||
statm2 = undefined
|
||||
statm2 = 'correct' unless do -> not 'wrong'
|
||||
eq answer, statm2
|
||||
|
||||
test "Issue 3921: `while` & `until`", ->
|
||||
i = 5
|
||||
assert = (a) -> ok 5 > a > 0
|
||||
result1 = while do (num = 1) -> i -= num
|
||||
assert i
|
||||
i
|
||||
ok result1.join(' ') is '4 3 2 1'
|
||||
|
||||
j = 5
|
||||
result2 = until do (num = 1) -> (j -= num) < 1
|
||||
assert j
|
||||
j
|
||||
ok result2.join(' ') is '4 3 2 1'
|
||||
|
||||
test "#3921: `switch`", ->
|
||||
i = 1
|
||||
a = switch do (m = 2) -> i * m
|
||||
when 5 then "five"
|
||||
when 4 then "four"
|
||||
when 3 then "three"
|
||||
when 2 then "two"
|
||||
when 1 then "one"
|
||||
else "none"
|
||||
eq "two", a
|
||||
|
||||
j = 12
|
||||
b = switch do (m = 3) -> j / m
|
||||
when 5 then "five"
|
||||
when 4 then "four"
|
||||
when 3 then "three"
|
||||
when 2 then "two"
|
||||
when 1 then "one"
|
||||
else "none"
|
||||
eq "four", b
|
||||
|
||||
k = 20
|
||||
c = switch do (m = 4) -> k / m
|
||||
when 5 then "five"
|
||||
when 4 then "four"
|
||||
when 3 then "three"
|
||||
when 2 then "two"
|
||||
when 1 then "one"
|
||||
else "none"
|
||||
eq "five", c
|
||||
|
||||
# Issue #3441: Parentheses wrapping expression throw invalid error in `then` clause
|
||||
test "#3441: `StatementLiteral` in parentheses", ->
|
||||
i = 0
|
||||
|
|
Loading…
Reference in New Issue