From 11d17fcf928608520634a79c8de97a4c0d4d1cf6 Mon Sep 17 00:00:00 2001 From: summivox Date: Sun, 31 Jan 2016 23:40:06 -0500 Subject: [PATCH] ES7 async await impl with concise syntax see http://wiki.ecmascript.org/doku.php?id=strawman:async_functions async function: `->>`, `~>>`, `-->>`, `~~>>`, `async (arglist) ->...`, `async function name (arglist)` await: `await` => `await`; `await all` => `await*` (proposed to translate to `Promise.all`, not final) backcall does NOT work, but this should be fine because they were invented to create the illusion of async function anyway --- src/ast.ls | 32 +++++++++++++++++++------------- src/grammar.ls | 8 ++++++-- src/lexer.ls | 14 +++++++++++--- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/ast.ls b/src/ast.ls index 794baf368..9cf9eb1ee 100644 --- a/src/ast.ls +++ b/src/ast.ls @@ -1122,21 +1122,23 @@ class exports.Yield extends Node children: <[ it ]> - show: -> if @op is 'yieldfrom' then 'from' else '' + show: -> switch @op + | 'yield' => '' + | 'yieldfrom' => 'from' + | 'await' => 'await' + | 'awaitall' => 'await all' + | _ => '' ::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' + | 'awaitall' => 'await*' + )] + if @it then code.push " #{@it.compile o, LEVEL_OP + PREC.unary}" sn(this, "(", ...code, ")") #### Unary operators @@ -1797,7 +1799,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 ]> @@ -1829,7 +1831,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-generator = true + code.unshift 'async ' + else if @generator @ctor and @carp "a constructor can't be a generator" o.in-generator = true code.push \* diff --git a/src/grammar.ls b/src/grammar.ls index dc4e8f2a0..a0b2714e9 100644 --- a/src/grammar.ls +++ b/src/grammar.ls @@ -257,11 +257,15 @@ 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) + o 'ASYNC PARAM( ArgList OptComma )PARAM -> Block' + , -> Fun $3, $7, /~/.test($6), /--|~~/.test($6), /!/.test($6), false, true # 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 diff --git a/src/lexer.ls b/src/lexer.ls index bc8c4e823..6a4d283ce 100644 --- a/src/lexer.ls +++ b/src/lexer.ls @@ -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' @@ -239,6 +239,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' @@ -657,7 +660,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 = '<-' @@ -1022,6 +1025,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 + | 'PARAM(', 'FUNCTION' => token.0 = 'ASYNC' + | 'GENERATOR' => carp 'named generator cannot be async' line prev = token continue @@ -1336,7 +1344,7 @@ SYMBOL = // | \.{1,3} # dot / cascade / splat/placeholder/yada*3 | \^\^ # clone | \*?<(?:--?|~~?)!? # backcall -| !?(?:--?|~~?)>\*? # function, bound function +| !?(?:--?|~~?)>(?:>|\*)? # function, bound function | ([-+&|:])\1 # crement / logic / `prototype` | %% # mod | & # arguments