adding a 'loop' keyword to CoffeeScript. Takes an expression or a block. Runs it until you break or return out.

This commit is contained in:
Jeremy Ashkenas 2010-06-12 12:15:53 -04:00
parent a133e018cc
commit 0222d90fa3
12 changed files with 134 additions and 103 deletions

View File

@ -21,7 +21,7 @@ match_here: (regexp, text) ->
# Search for a kleene star match at the beginning of the text.
match_star: (c, regexp, text) ->
while true
loop
return true if match_here(regexp, text)
return false unless text and (text[0] is c or c is '.')
text: text.slice(1)

View File

@ -94,7 +94,7 @@ while demand > supply
while supply > demand then buy()
while true
loop
break if broken
continue if continuing

View File

@ -14,8 +14,7 @@ print(add(2, 4))
# loop: 'quaff' print.
while true
print('quaff')
loop print 'quaff'
# ('cheese', 'bread', 'mayo') at (1) print

View File

@ -346,7 +346,7 @@
return [] if len <= 0
range: new Array len
idx: 0
while true
loop
return range if (if step > 0 then i - stop else stop - i) >= 0
range[idx]: i
idx++

View File

@ -484,6 +484,15 @@
return $2.add_body(Expressions.wrap([$1]));
}), o("Expression WhileSource", function() {
return $2.add_body(Expressions.wrap([$1]));
}), o("Loop", function() {
return $1;
})
],
Loop: [
o("LOOP Block", function() {
return new WhileNode(new LiteralNode('true')).add_body($2);
}), o("LOOP Expression", function() {
return new WhileNode(new LiteralNode('true')).add_body(Expressions.wrap([$2]));
})
],
// Array, object, and range comprehensions, at the most generic level.
@ -747,7 +756,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', 'WHILE', 'UNTIL', 'NEW', 'SUPER', 'CLASS'], ["left", 'EXTENDS'], ["right", 'ASSIGN', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'IF', 'ELSE']];
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', 'WHILE', 'UNTIL', 'LOOP', 'NEW', 'SUPER', 'CLASS'], ["left", 'EXTENDS'], ["right", 'ASSIGN', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'IF', 'ELSE']];
// Wrapping Up
// -----------
// Finally, now what we have our **grammar** and our **operators**, we can create

View File

@ -658,7 +658,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", "until", "yes", "no", "on", "off", "of", "by", "where", "when"]);
COFFEE_KEYWORDS = COFFEE_ALIASES.concat(["then", "unless", "until", "loop", "yes", "no", "on", "off", "of", "by", "where", "when"]);
// The list of keywords that are reserved by JavaScript, but not used, or are
// used by CoffeeScript internally. We throw an error when these are encountered,
// to avoid having a JavaScript error at runtime.

File diff suppressed because one or more lines are too long

View File

@ -423,7 +423,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', 'UNTIL', 'TERMINATOR', 'INDENT'].concat(EXPRESSION_END);
IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', 'UNTIL', 'LOOP', '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

@ -416,6 +416,12 @@ grammar: {
o "WhileSource Block", -> $1.add_body $2
o "Statement WhileSource", -> $2.add_body Expressions.wrap [$1]
o "Expression WhileSource", -> $2.add_body Expressions.wrap [$1]
o "Loop", -> $1
]
Loop: [
o "LOOP Block", -> new WhileNode(new LiteralNode 'true').add_body $2
o "LOOP Expression", -> new WhileNode(new LiteralNode 'true').add_body Expressions.wrap [$2]
]
# Array, object, and range comprehensions, at the most generic level.
@ -596,7 +602,7 @@ operators: [
["right", 'INDENT']
["left", 'OUTDENT']
["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW']
["right", 'FOR', 'WHILE', 'UNTIL', 'NEW', 'SUPER', 'CLASS']
["right", 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'NEW', 'SUPER', 'CLASS']
["left", 'EXTENDS']
["right", 'ASSIGN', 'RETURN']
["right", '->', '=>', 'UNLESS', 'IF', 'ELSE']

View File

@ -323,7 +323,7 @@ exports.Lexer: class Lexer
tag_parameters: ->
return if @tag() isnt ')'
i: 0
while true
loop
i: + 1
tok: @prev i
return if not tok
@ -466,7 +466,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", "until",
"then", "unless", "until", "loop",
"yes", "no", "on", "off",
"of", "by", "where", "when"
]

View File

@ -43,7 +43,7 @@ exports.Rewriter: class Rewriter
# our feet.
scan_tokens: (block) ->
i: 0
while true
loop
break unless @tokens[i]
move: block @tokens[i - 1], @tokens[i], @tokens[i + 1], i
i: + move
@ -162,7 +162,7 @@ exports.Rewriter: class Rewriter
@tokens.splice i + 1, 0, indent
idx: i + 1
parens: 0
while true
loop
idx: + 1
tok: @tokens[idx]
pre: @tokens[idx - 1]
@ -286,7 +286,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', 'UNTIL', 'TERMINATOR', 'INDENT'].concat EXPRESSION_END
IMPLICIT_END: ['IF', 'UNLESS', 'FOR', 'WHILE', 'UNTIL', 'LOOP', '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

@ -36,3 +36,14 @@ results: until value
i += 1
ok i is 6
# And, the loop form of while.
i: 5
list: []
loop
i: - 1
break if i is 0
list.push i * 2
ok list.join(' ') is '8 6 4 2'