Skip to content

Commit a78cbe7

Browse files
committed
Merge pull request #3240 from alubbe/master
using 'yield' automatically turns functions into generators
2 parents b407a59 + efca286 commit a78cbe7

13 files changed

+212
-45
lines changed

Cakefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,12 @@ runTests = (CoffeeScript) ->
276276

277277
# Run every test in the `test` folder, recording failures.
278278
files = fs.readdirSync 'test'
279+
280+
# Ignore generators test file if generators are not available
281+
generatorsAreAvailable = '--harmony' in process.execArgv or
282+
'--harmony-generators' in process.execArgv
283+
files.splice files.indexOf('generators.coffee'), 1 if not generatorsAreAvailable
284+
279285
for file in files when helpers.isCoffee file
280286
literate = helpers.isLiterate file
281287
currentFile = filename = path.join 'test', file

lib/coffee-script/grammar.js

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/coffee-script/lexer.js

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/coffee-script/nodes.js

Lines changed: 36 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/coffee-script/parser.js

Lines changed: 28 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/coffee-script/rewriter.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
},
2424
"preferGlobal": true,
2525
"scripts": {
26-
"test": "node ./bin/cake test"
26+
"test": "node ./bin/cake test",
27+
"test-harmony": "node --harmony ./bin/cake test"
2728
},
2829
"homepage": "http://coffeescript.org",
2930
"bugs": "https://github.com/jashkenas/coffeescript/issues",

src/grammar.coffee

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,9 @@ grammar =
536536
o 'UNARY_MATH Expression', -> new Op $1 , $2
537537
o '- Expression', (-> new Op '-', $2), prec: 'UNARY_MATH'
538538
o '+ Expression', (-> new Op '+', $2), prec: 'UNARY_MATH'
539+
o 'YIELD Statement', -> new Op $1 , $2
540+
o 'YIELD Expression', -> new Op $1 , $2
541+
o 'YIELD FROM Expression', -> new Op $1.concat($2) , $3
539542

540543
o '-- SimpleAssignable', -> new Op '--', $2
541544
o '++ SimpleAssignable', -> new Op '++', $2
@@ -586,6 +589,7 @@ operators = [
586589
['nonassoc', '++', '--']
587590
['left', '?']
588591
['right', 'UNARY']
592+
['right', 'YIELD']
589593
['right', '**']
590594
['right', 'UNARY_MATH']
591595
['left', 'MATH']

src/lexer.coffee

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ exports.Lexer = class Lexer
109109
if id is 'own' and @tag() is 'FOR'
110110
@token 'OWN', id
111111
return id.length
112+
if id is 'from' and @tag() is 'YIELD'
113+
@token 'FROM', id
114+
return id.length
112115
forcedIdentifier = colon or
113116
(prev = last @tokens) and (prev[0] in ['.', '?.', '::', '?::'] or
114117
not prev.spaced and prev[0] is '@')
@@ -688,7 +691,7 @@ exports.Lexer = class Lexer
688691
# Are we in the midst of an unfinished expression?
689692
unfinished: ->
690693
LINE_CONTINUER.test(@chunk) or
691-
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-',
694+
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-', 'YIELD',
692695
'**', 'SHIFT', 'RELATION', 'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
693696

694697
# Remove newlines from beginning and (non escaped) from end of string literals.
@@ -729,7 +732,7 @@ exports.Lexer = class Lexer
729732
JS_KEYWORDS = [
730733
'true', 'false', 'null', 'this'
731734
'new', 'delete', 'typeof', 'in', 'instanceof'
732-
'return', 'throw', 'break', 'continue', 'debugger'
735+
'return', 'throw', 'break', 'continue', 'debugger', 'yield'
733736
'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally'
734737
'class', 'extends', 'super'
735738
]
@@ -758,10 +761,10 @@ RESERVED = [
758761
'case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum'
759762
'export', 'import', 'native', '__hasProp', '__extends', '__slice', '__bind'
760763
'__indexOf', 'implements', 'interface', 'package', 'private', 'protected'
761-
'public', 'static', 'yield'
764+
'public', 'static'
762765
]
763766

764-
STRICT_PROSCRIBED = ['arguments', 'eval']
767+
STRICT_PROSCRIBED = ['arguments', 'eval', 'yield*']
765768

766769
# The superset of both JavaScript keywords and reserved words, none of which may
767770
# be used as identifiers or properties.

src/nodes.coffee

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,6 @@ exports.Return = class Return extends Base
455455
answer.push @makeCode ";"
456456
return answer
457457

458-
459458
#### Value
460459

461460
# A value, variable or literal or parenthesized, indexed or dotted into,
@@ -1315,9 +1314,11 @@ exports.Assign = class Assign extends Base
13151314
# has no *children* -- they're within the inner scope.
13161315
exports.Code = class Code extends Base
13171316
constructor: (params, body, tag) ->
1318-
@params = params or []
1319-
@body = body or new Block
1320-
@bound = tag is 'boundfunc'
1317+
@params = params or []
1318+
@body = body or new Block
1319+
@bound = tag is 'boundfunc'
1320+
@isGenerator = @body.contains (node) ->
1321+
node instanceof Op and node.operator in ['yield', 'yield*']
13211322

13221323
children: ['params', 'body']
13231324

@@ -1384,9 +1385,10 @@ exports.Code = class Code extends Base
13841385
node.error "multiple parameters named '#{name}'" if name in uniqs
13851386
uniqs.push name
13861387
@body.makeReturn() unless wasEmpty or @noReturn
1387-
code = 'function'
1388-
code += ' ' + @name if @ctor
1389-
code += '('
1388+
code = 'function'
1389+
code += '*' if @isGenerator
1390+
code += ' ' + @name if @ctor
1391+
code += '('
13901392
answer = [@makeCode(code)]
13911393
for p, i in params
13921394
if i then answer.push @makeCode ", "
@@ -1612,9 +1614,10 @@ exports.Op = class Op extends Base
16121614

16131615
# The map of conversions from CoffeeScript to JavaScript symbols.
16141616
CONVERSIONS =
1615-
'==': '==='
1616-
'!=': '!=='
1617-
'of': 'in'
1617+
'==': '==='
1618+
'!=': '!=='
1619+
'of': 'in'
1620+
'yieldfrom': 'yield*'
16181621

16191622
# The map of invertible operators.
16201623
INVERSIONS =
@@ -1625,6 +1628,9 @@ exports.Op = class Op extends Base
16251628

16261629
isSimpleNumber: NO
16271630

1631+
isYield: ->
1632+
@operator in ['yield', 'yield*']
1633+
16281634
isUnary: ->
16291635
not @second
16301636

@@ -1691,6 +1697,7 @@ exports.Op = class Op extends Base
16911697
@error 'delete operand may not be argument or var'
16921698
if @operator in ['--', '++'] and @first.unwrapAll().value in STRICT_PROSCRIBED
16931699
@error "cannot increment/decrement \"#{@first.unwrapAll().value}\""
1700+
return @compileYield o if @isYield()
16941701
return @compileUnary o if @isUnary()
16951702
return @compileChain o if isChain
16961703
switch @operator
@@ -1745,6 +1752,19 @@ exports.Op = class Op extends Base
17451752
parts.reverse() if @flip
17461753
@joinFragmentArrays parts, ''
17471754

1755+
compileYield: (o) ->
1756+
parts = []
1757+
op = @operator
1758+
if not o.scope.parent?
1759+
@error 'yield statements must occur within a function generator.'
1760+
if 'expression' in Object.keys @first
1761+
parts.push @first.expression.compileToFragments o, LEVEL_OP if @first.expression?
1762+
else
1763+
parts.push [@makeCode "(#{op} "]
1764+
parts.push @first.compileToFragments o, LEVEL_OP
1765+
parts.push [@makeCode ")"]
1766+
@joinFragmentArrays parts, ''
1767+
17481768
compilePower: (o) ->
17491769
# Make a Math.pow call
17501770
pow = new Value new Literal('Math'), [new Access new Literal 'pow']

src/rewriter.coffee

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@
467467
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
468468
IMPLICIT_CALL = [
469469
'IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS'
470-
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY',
470+
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'YIELD'
471471
'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'
472472
]
473473

0 commit comments

Comments
 (0)