diff --git a/src/coffee-script.coffee b/src/coffee-script.coffee index 3abf63483c..40073278b9 100644 --- a/src/coffee-script.coffee +++ b/src/coffee-script.coffee @@ -91,10 +91,14 @@ exports.tokens = withPrettyErrors (code, options) -> # return the AST. You can then compile it by calling `.compile()` on the root, # or traverse it by using `.traverseChildren()` with a callback. exports.nodes = withPrettyErrors (source, options) -> + console.log 'uniced', options.uniced if typeof source is 'string' - iced_transform(parser.parse(lexer.tokenize(source, options)), options) + result = parser.parse lexer.tokenize(source, options) else - iced_transform(parser.parse(source),options) + result = parser.parse source + if not options.uniced + result = iced_transform result, options + return result # Compile and execute a string of CoffeeScript (on the server), correctly # setting `__filename`, `__dirname`, and relative `require()`. diff --git a/src/command.coffee b/src/command.coffee index fd33bc3c06..2d63580e08 100644 --- a/src/command.coffee +++ b/src/command.coffee @@ -51,6 +51,7 @@ SWITCHES = [ ['-s', '--stdio', 'listen for and compile scripts over stdio'] ['-l', '--literate', 'treat stdio as literate style coffee-script'] ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce'] + ['-u', '--uniced', 'print out the uniced parse tree that the parser produces'] ['-v', '--version', 'display the version number'] ['-w', '--watch', 'watch scripts for changes and rerun commands'] ['-I', '--runtime [WHICH]', "how to include the iced runtime, one of #{runtime_modes_str}; default is 'node'" ] @@ -168,8 +169,9 @@ compileScript = (file, input, base = null) -> CoffeeScript.emit 'compile', task if o.tokens printTokens CoffeeScript.tokens t.input, t.options - else if o.nodes - printLine CoffeeScript.nodes(t.input, t.options).toString().trim() + else if o.nodes or o.uniced + printLine CoffeeScript.nodes( + t.input, t.options, o.uniced).toString().trim() else if o.run CoffeeScript.register() CoffeeScript.run t.input, t.options @@ -403,6 +405,7 @@ compileOptions = (filename, base) -> sourceMap: opts.map runtime : opts.runtime runforce : opts.runforce + uniced: opts.uniced } if filename if base diff --git a/src/grammar.coffee b/src/grammar.coffee index 12f8110242..1e705fd080 100644 --- a/src/grammar.coffee +++ b/src/grammar.coffee @@ -38,7 +38,7 @@ o = (patternString, action, options) -> # All runtime functions we need are defined on "yy" action = action.replace /\bnew /g, '$&yy.' - action = action.replace /\b(?:Block\.wrap|extend)\b/g, 'yy.$&' + action = action.replace /\b(?:Block\.wrap|extend|makewait)\b/g, 'yy.$&' # Returns a function which adds location data to the first parameter passed # in, and returns the parameter. If the parameter is not a node, it will @@ -85,6 +85,7 @@ grammar = # Block and statements, which make up a line in a body. Line: [ + o 'AwaitAssign' o 'Expression' o 'Statement' ] @@ -98,8 +99,15 @@ grammar = ] Await: [ - o 'AWAIT Block', -> new Await $2 - o 'AWAIT Expression', -> new Await Block.wrap [$2 ] + o 'AWAIT Block', -> makewait false, $2, yylineno + o 'AWAIT Expression', -> makewait true, $2, yylineno + ] + + # ECMAScript 7-style x = await fn() + AwaitAssign: [ + o 'Assignable = AWAIT Expression', -> makewait $1, $4, yylineno + o 'Assignable = TERMINATOR AWAIT Expression', -> makewait $1, $5, yylineno + o 'Assignable = INDENT AWAIT Expression OUTDENT', -> makewait $1, $5, yylineno ] # All the different types of expressions in our language. The basic unit of diff --git a/src/nodes.coffee b/src/nodes.coffee index 1e5fb82191..b93b1aa04c 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -2660,6 +2660,36 @@ exports.Await = class Await extends Base super p, o @icedNodeFlag = o.foundAwaitFunc = o.foundAwait = true +#### makewait + +# The **Await.assign** is used to assign a local variable to value, +# or to set the property of an object -- including within object literals. +exports.makewait = (variable, value, lineno) -> + hasDefer = value.contains isDefer + # if this is a bare await containing a single expression (variable is true) + # then it should be treated as an iced-await if it contains 'defer'. + if variable is true and hasDefer + variable = false + value = Block.wrap [ value ] + # if this is a bare await containing a block, then it is an iced-style await. + if variable is false + result = new Await value + if not hasDefer + result.error 'await block is missing a defer statement' + return result + # if this is an await assignment, then parse it as if it uses defer notation. + # written as: x = await expr + # parses as: await (expr).then(defer x) + # TODO: consider doing this as part of the iced transformation + expr = new Value new Parens value + expr_then = expr.add new Access new Value new Literal "then" + defer_args = if variable is true then [] else [variable] + call = new Call(expr_then, [ new Defer(defer_args, lineno) ]) + result = new Await Block.wrap [ call ] + if hasDefer + result.error 'promise-style await must not have a defer statement' + return result + #### IcedRuntime # # By default, the iced libraries are require'd via nodejs' require. @@ -3606,6 +3636,9 @@ isLiteralThis = (node) -> (node instanceof Code and node.bound) or (node instanceof Call and node.isSuper) +isDefer = (node) -> + node instanceof Defer + # Unfold a node's child if soak, then tuck the node under created `If` unfoldSoak = (o, parent, name) -> return unless ifn = parent[name].unfoldSoak o