adding until loops as the inverse of while loops

This commit is contained in:
Jeremy Ashkenas 2010-04-28 22:08:00 -04:00
parent 17ba44056e
commit adbcd320b2
10 changed files with 133 additions and 101 deletions

View File

@ -470,6 +470,15 @@
return new WhileNode($2, {
guard: $4
});
}), o("UNTIL Expression", function() {
return new WhileNode($2, {
invert: true
});
}), o("UNTIL Expression WHEN Expression", function() {
return new WhileNode($2, {
invert: true,
guard: $4
});
})
],
// The while loop can either be normal, with a block of expressions to execute,
@ -740,7 +749,7 @@
// 2 + (3 * 4)
// And not:
// (2 + 3) * 4
operators = [["left", '?'], ["nonassoc", 'UMINUS', 'UPLUS', '!', '!!', '~', '++', '--'], ["left", '*', '/', '%'], ["left", '+', '-'], ["left", '<<', '>>', '>>>'], ["left", '&', '|', '^'], ["left", '<=', '<', '>', '>='], ["right", 'DELETE', 'INSTANCEOF', 'TYPEOF'], ["left", '==', '!='], ["left", '&&', '||'], ["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?='], ["left", '.'], ["right", 'INDENT'], ["left", 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW'], ["right", 'FOR', 'NEW', 'SUPER', 'CLASS'], ["left", 'EXTENDS'], ["right", 'ASSIGN', 'RETURN'], ["right", '->', '=>', '<-', 'UNLESS', 'IF', 'ELSE', 'WHILE']];
operators = [["left", '?'], ["nonassoc", 'UMINUS', 'UPLUS', '!', '!!', '~', '++', '--'], ["left", '*', '/', '%'], ["left", '+', '-'], ["left", '<<', '>>', '>>>'], ["left", '&', '|', '^'], ["left", '<=', '<', '>', '>='], ["right", 'DELETE', 'INSTANCEOF', 'TYPEOF'], ["left", '==', '!='], ["left", '&&', '||'], ["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?='], ["left", '.'], ["right", 'INDENT'], ["left", 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW'], ["right", 'FOR', 'NEW', 'SUPER', 'CLASS'], ["left", 'EXTENDS'], ["right", 'ASSIGN', 'RETURN'], ["right", '->', '=>', '<-', 'UNLESS', 'IF', 'ELSE', 'WHILE', 'UNTIL']];
// Wrapping Up
// -----------
// Finally, now what we have our **grammar** and our **operators**, we can create

View File

@ -615,7 +615,7 @@
// CoffeeScript-only keywords, which we're more relaxed about allowing. They can't
// be used standalone, but you can reference them as an attached property.
COFFEE_ALIASES = ["and", "or", "is", "isnt", "not"];
COFFEE_KEYWORDS = COFFEE_ALIASES.concat(["then", "unless", "yes", "no", "on", "off", "of", "by", "where", "when"]);
COFFEE_KEYWORDS = COFFEE_ALIASES.concat(["then", "unless", "until", "yes", "no", "on", "off", "of", "by", "where", "when"]);
// The combined list of keywords is the superset that gets passed verbatim to
// the parser.
KEYWORDS = JS_KEYWORDS.concat(COFFEE_KEYWORDS);

View File

@ -1176,6 +1176,9 @@
// flexibility or more speed than a comprehension can provide.
exports.WhileNode = (function() {
WhileNode = function WhileNode(condition, opts) {
if (opts && opts.invert) {
condition = new OpNode('!', condition);
}
this.children = [(this.condition = condition)];
this.guard = opts && opts.guard;
return this;

File diff suppressed because one or more lines are too long

View File

@ -375,7 +375,7 @@
// Tokens indicating that the implicit call must enclose a block of expressions.
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ','];
// Tokens that always mark the end of an implicit call for single-liners.
IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', 'TERMINATOR', 'INDENT'].concat(EXPRESSION_END);
IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', 'UNTIL', 'TERMINATOR', 'INDENT'].concat(EXPRESSION_END);
// Single-line flavors of block expressions that have unclosed endings.
// The grammar can't disambiguate them, so we insert the implicit indentation.
SINGLE_LINERS = ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN'];

View File

@ -414,6 +414,8 @@ grammar: {
WhileSource: [
o "WHILE Expression", -> new WhileNode $2
o "WHILE Expression WHEN Expression", -> new WhileNode $2, {guard : $4}
o "UNTIL Expression", -> new WhileNode $2, {invert: true}
o "UNTIL Expression WHEN Expression", -> new WhileNode $2, {invert: true, guard: $4}
]
# The while loop can either be normal, with a block of expressions to execute,
@ -603,7 +605,7 @@ operators: [
["right", 'FOR', 'NEW', 'SUPER', 'CLASS']
["left", 'EXTENDS']
["right", 'ASSIGN', 'RETURN']
["right", '->', '=>', '<-', 'UNLESS', 'IF', 'ELSE', 'WHILE']
["right", '->', '=>', '<-', 'UNLESS', 'IF', 'ELSE', 'WHILE', 'UNTIL']
]
# Wrapping Up

View File

@ -449,7 +449,7 @@ JS_KEYWORDS: [
# be used standalone, but you can reference them as an attached property.
COFFEE_ALIASES: ["and", "or", "is", "isnt", "not"]
COFFEE_KEYWORDS: COFFEE_ALIASES.concat [
"then", "unless",
"then", "unless", "until",
"yes", "no", "on", "off",
"of", "by", "where", "when"
]

View File

@ -855,6 +855,7 @@ exports.SplatNode: class SplatNode extends BaseNode
exports.WhileNode: class WhileNode extends BaseNode
constructor: (condition, opts) ->
condition: new OpNode('!', condition) if opts and opts.invert
@children:[@condition: condition]
@guard: opts and opts.guard

View File

@ -283,7 +283,7 @@ IMPLICIT_CALL: ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_
IMPLICIT_BLOCK: ['->', '=>', '{', '[', ',']
# Tokens that always mark the end of an implicit call for single-liners.
IMPLICIT_END: ['IF', 'UNLESS', 'FOR', 'WHILE', 'TERMINATOR', 'INDENT'].concat EXPRESSION_END
IMPLICIT_END: ['IF', 'UNLESS', 'FOR', 'WHILE', 'UNTIL', 'TERMINATOR', 'INDENT'].concat EXPRESSION_END
# Single-line flavors of block expressions that have unclosed endings.
# The grammar can't disambiguate them, so we insert the implicit indentation.

View File

@ -28,3 +28,11 @@ results: while i -= 1 when i % 2 is 0
ok results.join(' ') is '16 12 8 4'
value: false
i: 0
results: until value
value: true if i is 5
i += 1
ok i is 6