Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ->* and yield #462

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion Slakefile
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,13 @@ function runTests global.LiveScript
else tint message
if failedTests
process.exit 1
dir(\test)forEach (file) ->

files = dir \test
unless '--harmony' in process.execArgv or '--harmony-generators' in process.execArgv
say "Skipping --harmony tests"
files.splice (files.indexOf 'generators.ls'), 1

files.forEach (file) ->
return unless /\.ls$/i.test file
code = slurp filename = path.join \test file
try LiveScript.run code, {filename} catch
Expand Down
15 changes: 12 additions & 3 deletions lib/ast.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/grammar.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions lib/lexer.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions lib/parser.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"scripts": {
"pretest": "bin/slake build && bin/slake build:parser && bin/slake build",
"test": "bin/slake test",
"test-harmony": "node --harmony ./bin/slake test",
"posttest": "git checkout -- lib"
},
"preferGlobal": true,
Expand Down
1 change: 1 addition & 0 deletions package.json.ls
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ bin :
scripts:
pretest: "bin/slake build && bin/slake build:parser && bin/slake build"
test: "bin/slake test"
\test-harmony : "node --harmony ./bin/slake test"
posttest: "git checkout -- lib"

preferGlobal: true
Expand Down
11 changes: 8 additions & 3 deletions src/ast.ls
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,8 @@ class exports.Unary extends Node
#{ it.compile o, LEVEL_LIST }).slice(8, -1)"
code = it.compile o, LEVEL_OP + PREC.unary
if @post then code += op else
op += ' ' if op in <[ new typeof delete ]>
op = 'yield* ' if op is \yieldfrom
op += ' ' if op in <[ new typeof delete yield ]>
or op in <[ + - ]> and op is code.charAt!
code = op + code
if o.level < LEVEL_CALL then code else "(#code)"
Expand Down Expand Up @@ -1591,7 +1592,7 @@ class exports.Existence extends Node implements Negatable
#### Fun
# A function definition. This is the only node that creates a `new Scope`.
class exports.Fun extends Node
(@params or [], @body or Block!, @bound and \this$, @curried or false, @hushed = false) ~>
(@params or [], @body or Block!, @bound and \this$, @curried or false, @hushed = false, @generator = false) ~>

children: <[ params body ]>

Expand Down Expand Up @@ -1623,6 +1624,10 @@ class exports.Fun extends Node
o.indent += TAB
{body, name, tab} = this
code = \function
if @generator
@ctor and @carp "a constructor can't be a generator"
@hushed and @carp "a generator is hushed by default"
code += \*
if @bound is \this$
if @ctor
scope.assign \this$ 'this instanceof ctor$ ? this : new ctor$'
Expand All @@ -1637,7 +1642,7 @@ class exports.Fun extends Node
pscope.add name, \function, this
if @statement or name and @labeled
code += ' ' + scope.add name, \function, this
@hushed or @ctor or @newed or body.makeReturn!
@hushed or @ctor or @newed or @generator or body.makeReturn!
code += "(#{ @compileParams o, scope }){"
code += "\n#that\n#tab" if body.compileWithDeclarations o
code += \}
Expand Down
2 changes: 1 addition & 1 deletion src/grammar.ls
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ bnf =

# The function literal can be either anonymous with `->`,
o 'PARAM( ArgList OptComma )PARAM -> Block'
, -> L Fun $2, $6, /~/.test($5), /--|~~/.test($5), /!/.test($5)
, -> L Fun $2, $6, /~/.test($5), /--|~~/.test($5), /!/.test($5), /\*/.test($5)
# or named with `function`.
o 'FUNCTION CALL( ArgList OptComma )CALL Block' -> L Fun($3, $6)named $1

Expand Down
15 changes: 11 additions & 4 deletions src/lexer.ls
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ exports import
switch id
case <[ true false on off yes no null void arguments debugger ]>
tag = \LITERAL
case \new \do \typeof \delete then tag = \UNARY
case <[ new do typeof delete yield ]> then tag = \UNARY
case \return \throw then tag = \HURL
case \break \continue then tag = \JUMP
case \this \eval \super then return @token(\LITERAL id, true)length
Expand Down Expand Up @@ -211,7 +211,11 @@ exports import
or last.1 is \import and \All
last.1 += that
return 3
case \from then @forange! and tag = \FROM
case \from
if last.1 is \yield
last.1 += \from
return 4
@forange! and tag = \FROM
case \to \til
@forange! and @tokens.push [\FROM '' @line] [\STRNUM \0 @line]
if @fget \from
Expand Down Expand Up @@ -539,6 +543,9 @@ exports import
@token \IMPORT \<<
return sym.length
case \*
if @last.0 is \->
@last.1 += \* if @last.0 is \->
return 1
if @last.0 in <[ NEWLINE INDENT THEN => ]> and
(INLINEDENT <<< lastIndex: index+1)exec code .0.length
@tokens.push [\LITERAL \void @line] [\ASSIGN \= @line]
Expand Down Expand Up @@ -1215,13 +1222,13 @@ KEYWORDS_SHARED = <[
true false null this void super return throw break continue
if else for while switch case default try catch finally
function class extends implements new do delete typeof in instanceof
let with var const import export debugger
let with var const import export debugger yield
]>

# The list of keywords that are reserved by JavaScript, but not used.
# We throw a syntax error for these to avoid runtime errors.
KEYWORDS_UNUSED =
<[ enum interface package private protected public static yield ]>
<[ enum interface package private protected public static ]>

JS_KEYWORDS = KEYWORDS_SHARED ++ KEYWORDS_UNUSED

Expand Down
9 changes: 8 additions & 1 deletion test/compilation.ls
Original file line number Diff line number Diff line change
Expand Up @@ -197,5 +197,12 @@ eq "var a, b, c;\na = require('a');\nb = require('b');\nc = require('c');" bare
# JS literal
eq 'some js code!' bare '``some js code!``'

# generators
compileThrows "a constructor can't be a generator" 1 'class => ->*'
compileThrows "a generator is hushed by default" 1 '!->*'

# https://github.com/jashkenas/coffee-script/pull/3240#issuecomment-38344281
eq '(function*(){\n var body;\n body = (yield fn).body;\n});' bare '->* {body} = yield fn'

# [livescript#279](https://github.com/gkz/LiveScript/issues/279)
################################################################
################################################################
68 changes: 68 additions & 0 deletions test/generators.ls
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Generators
# -----------------
#
# * Generator Definition

# generator as argument
ok ->* 1

# named generator function
ok <| :fn ->* 2

# generator definition
x = ->*
yield 0
yield 1
yield 2

y = x!
z = y.next!
eq z.value, 0
eq z.done, false
z = y.next!
eq z.value, 1
eq z.done, false
z = y.next!
eq z.value, 2
eq z.done, false
z = y.next!
eq z.value, void
eq z.done, true

# yield from
first = ->*
i = 0
loop => yield i++
second = ->*
yield from first!
list = second!
for i to 3
{value} = list.next!
eq value, i

# curried bound generators
class A
val: 5
curried: (x, y) ~~>*
yield @val + x + y
fn = (new A).curried
yield-add = fn 2
y = yield-add 3
z = y.next!
eq z.value, 10
eq z.done, false
z = y.next!
eq z.value, void
eq z.done, true

# bound generator
obj =
bound: ->
do ~>*
return this
unbound: ->
do ->*
return this

eq obj, obj.bound().next().value
ok obj isnt obj.unbound().next().value