Add support for standalone yield

This breaks compatibility with
->
  yield for i in [1..3]
    i * 2
and
->
  yield
    i * 2

yield's behaviour now mirrors that of return in that it can be used stand alone as well as with expressions. Thus, it currently also inherits the above limitations.
This commit is contained in:
Andreas Lubbe 2015-09-13 12:30:59 +02:00
parent 1739c9d720
commit c1a9cfa044
6 changed files with 175 additions and 174 deletions

View File

@ -47,7 +47,18 @@
return new Literal($1);
})
],
Expression: [o('Value'), o('Invocation'), o('Code'), o('Operation'), o('Assign'), o('If'), o('Try'), o('While'), o('For'), o('Switch'), o('Class'), o('Throw')],
Expression: [o('Value'), o('Invocation'), o('Code'), o('Operation'), o('Assign'), o('If'), o('Try'), o('While'), o('For'), o('Switch'), o('Class'), o('Throw'), o('Yield')],
Yield: [
o('YIELD', function() {
return new Op($1, new Value(new Literal('')));
}), o('YIELD Statement', function() {
return new Op($1, $2);
}), o('YIELD Expression', function() {
return new Op($1, $2);
}), o('YIELD FROM Expression', function() {
return new Op($1.concat($2), $3);
})
],
Block: [
o('INDENT OUTDENT', function() {
return new Block;
@ -580,12 +591,6 @@
return new Op('+', $2);
}), {
prec: 'UNARY_MATH'
}), o('YIELD Statement', function() {
return new Op($1, $2);
}), o('YIELD Expression', function() {
return new Op($1, $2);
}), o('YIELD FROM Expression', function() {
return new Op($1.concat($2), $3);
}), o('-- SimpleAssignable', function() {
return new Op('--', $2);
}), o('++ SimpleAssignable', function() {

View File

@ -794,7 +794,7 @@
Lexer.prototype.unfinished = function() {
var ref2;
return LINE_CONTINUER.test(this.chunk) || ((ref2 = this.tag()) === '\\' || ref2 === '.' || ref2 === '?.' || ref2 === '?::' || ref2 === 'UNARY' || ref2 === 'MATH' || ref2 === 'UNARY_MATH' || ref2 === '+' || ref2 === '-' || ref2 === 'YIELD' || ref2 === '**' || ref2 === 'SHIFT' || ref2 === 'RELATION' || ref2 === 'COMPARE' || ref2 === 'LOGIC' || ref2 === 'THROW' || ref2 === 'EXTENDS');
return LINE_CONTINUER.test(this.chunk) || ((ref2 = this.tag()) === '\\' || ref2 === '.' || ref2 === '?.' || ref2 === '?::' || ref2 === 'UNARY' || ref2 === 'MATH' || ref2 === 'UNARY_MATH' || ref2 === '+' || ref2 === '-' || ref2 === '**' || ref2 === 'SHIFT' || ref2 === 'RELATION' || ref2 === 'COMPARE' || ref2 === 'LOGIC' || ref2 === 'THROW' || ref2 === 'EXTENDS');
};
Lexer.prototype.formatString = function(str) {

File diff suppressed because one or more lines are too long

View File

@ -113,6 +113,14 @@ grammar =
o 'Switch'
o 'Class'
o 'Throw'
o 'Yield'
]
Yield: [
o 'YIELD', -> new Op $1, new Value new Literal ''
o 'YIELD Statement', -> new Op $1, $2
o 'YIELD Expression', -> new Op $1, $2
o 'YIELD FROM Expression', -> new Op $1.concat($2), $3
]
# An indented block of expressions. Note that the [Rewriter](rewriter.html)
@ -558,9 +566,6 @@ grammar =
o 'UNARY_MATH Expression', -> new Op $1 , $2
o '- Expression', (-> new Op '-', $2), prec: 'UNARY_MATH'
o '+ Expression', (-> new Op '+', $2), prec: 'UNARY_MATH'
o 'YIELD Statement', -> new Op $1 , $2
o 'YIELD Expression', -> new Op $1 , $2
o 'YIELD FROM Expression', -> new Op $1.concat($2) , $3
o '-- SimpleAssignable', -> new Op '--', $2
o '++ SimpleAssignable', -> new Op '++', $2

View File

@ -682,7 +682,7 @@ exports.Lexer = class Lexer
# Are we in the midst of an unfinished expression?
unfinished: ->
LINE_CONTINUER.test(@chunk) or
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-', 'YIELD',
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-',
'**', 'SHIFT', 'RELATION', 'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
formatString: (str) ->

View File

@ -4,7 +4,7 @@
# * Generator Definition
test "most basic generator support", ->
ok -> yield 0
ok -> yield
test "empty generator", ->
x = do -> yield return
@ -15,19 +15,21 @@ test "empty generator", ->
test "generator iteration", ->
x = do ->
yield 0
yield 1
yield
yield 2
3
y = x.next()
ok y.value is 0 and y.done is false
y = x.next()
ok y.value is 1 and y.done is false
ok y.value is undefined and y.done is false
y = x.next()
ok y.value is 2 and y.done is false
y = x.next()
ok y.value is undefined and y.done is true
ok y.value is 3 and y.done is true
test "last line yields are returned", ->
x = do ->
@ -71,9 +73,6 @@ test "bound generator", ->
test "error if `yield` occurs outside of a function", ->
throws -> CoffeeScript.compile 'yield 1'
test "`yield` by itself not at the end of a function errors", ->
throws -> CoffeeScript.compile 'x = -> yield; return'
test "`yield from` support", ->
x = do ->
yield from do ->
@ -131,18 +130,6 @@ test "yield in for loop expressions", ->
arrayEq z.value, [10, 20, 30]
ok z.done is true
test "yielding for loop expressions", ->
x = do ->
yield for i in [1..3]
i * 2
y = x.next()
arrayEq y.value, [2, 4, 6]
ok y.done is false
y = x.next 42
ok y.value is 42 and y.done is true
test "yield in switch expressions", ->
x = do ->
y = switch yield 1
@ -223,8 +210,9 @@ test "yield handles 'this' correctly", ->
x = ->
yield switch
when true then yield => this
yield for item in [1]
array = for item in [1]
yield => this
yield array
yield if true then yield => this
yield try throw yield => this
throw yield => this