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

ES7 async/await implementation with concise syntax #836

Closed
wants to merge 4 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions src/ast.ls
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ SourceNode::to-string = (...args) ->
that.carp 'inconvertible statement' if @get-jump!
fun = Fun [] Block this
call = Call!
fun.async = true if o.in-async
fun.generator = true if o.in-generator
var hasArgs, hasThis
@traverse-children !->
Expand All @@ -174,6 +175,8 @@ SourceNode::to-string = (...args) ->
out = Parens(Chain fun<<<{+wrapper, @void} [call]; true)
if o.in-generator
out = new Yield 'yieldfrom', out
else if o.in-async
out = new Yield 'await', out
out.compile o

# Compiles a child node as a block statement.
Expand Down Expand Up @@ -1151,21 +1154,21 @@ class exports.Yield extends Node

children: <[ it ]>

show: -> if @op is 'yieldfrom' then 'from' else ''
show: -> switch @op
| 'yield' => ''
| 'yieldfrom' => 'from'
| 'await' => 'await'
| _ => ''

::delegate <[ isCallable ]> -> yes

compile-node: (o) ->
code = []

if @op is \yieldfrom
code.push 'yield*'
else
code.push 'yield'

if @it
code.push " #{@it.compile o, LEVEL_OP + PREC.unary}"

code = [(switch @op
| 'yield' => 'yield'
| 'yieldfrom' => 'yield*'
| 'await' => 'await'
)]
if @it then code.push " #{@it.compile o, LEVEL_OP + PREC.unary}"
sn(this, "(", ...code, ")")

#### Unary operators
Expand Down Expand Up @@ -1881,7 +1884,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, @generator = false) ~>
(@params or [], @body or Block!, @bound and \this$, @curried or false, @hushed = false, @generator = false, @async = false) ~>

children: <[ params body ]>

Expand Down Expand Up @@ -1913,7 +1916,11 @@ class exports.Fun extends Node
o.indent += TAB
{body, name, tab} = this
code = [\function]
if @generator
if @async
@ctor and @carp "a constructor can't be async"
o.in-async = true
code.unshift 'async '
else if @generator
@ctor and @carp "a constructor can't be a generator"
o.in-generator = true
code.push \*
Expand Down
6 changes: 4 additions & 2 deletions src/grammar.ls
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,13 @@ bnf =

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

# The full complement of `if` and `unless` expressions
o 'IF Expression Block Else' -> L 1 2 If $2, $3, $1 is 'unless' .add-else $4
Expand Down
14 changes: 11 additions & 3 deletions src/lexer.ls
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ exports <<<
tag = 'LITERAL'
case <[ new do typeof delete ]>
tag = 'UNARY'
case 'yield'
case 'yield' 'await'
tag = 'YIELD'
case 'return' 'throw'
tag = 'HURL'
Expand Down Expand Up @@ -240,6 +240,9 @@ exports <<<
or last.1 is 'import' and 'All'
last.1 += that
return 3
if last.0 is 'yield' and last.1 is 'await'
last.1 += 'all'
return 3
case 'from'
if last.1 is 'yield'
last.1 += 'from'
Expand Down Expand Up @@ -655,7 +658,7 @@ exports <<<
@fset 'for' false
tag = 'THEN'
default
if /^!?(?:--?|~~?)>\*?$/.test val # function arrow
if /^!?(?:--?|~~?)>(?:>|\*)?$/.test val # function arrow
@parameters tag = '->'
else if /^\*?<(?:--?|~~?)!?$/.test val # backcall
@parameters tag = '<-'
Expand Down Expand Up @@ -1030,6 +1033,11 @@ character = if not JSON? then uxxxx else ->
[')PARAM' ')' line, column]
['->' '~>' line, column]
break LOOP
case tag is 'ID' and val is 'async'
next = tokens[i + 1]
switch next.0
| 'FUNCTION' => token.0 = 'ASYNC'
| 'GENERATOR' => carp 'named generator cannot be async' line
prev = token
continue

Expand Down Expand Up @@ -1344,7 +1352,7 @@ SYMBOL = //
| \.{1,3} # dot / cascade / splat/placeholder/yada*3
| \^\^ # clone
| \*?<(?:--?|~~?)!? # backcall
| !?(?:--?|~~?)>\*? # function, bound function
| !?(?:--?|~~?)>(?:>|\*)? # function, bound function
| ([-+&|:])\1 # crement / logic / `prototype`
| %% # mod
| & # arguments
Expand Down