Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First stab at getting await blocks to act as expressions, almost work…

…ing, except for nested guys.
  • Loading branch information...
commit 9b3d7a89b3154d14628650abc36eb9e5f6e6b33f 1 parent efde659
@maxtaco authored
View
4 lib/coffee-script/grammar.js
@@ -33,7 +33,7 @@
Statement: [
o('Return'), o('Comment'), o('STATEMENT', function() {
return new Literal($1);
- }), o('Await'), o('Require')
+ }), o('Require')
],
Require: [
o('TAMEREQUIRE Arguments', function() {
@@ -47,7 +47,7 @@
return new Await(Block.wrap([$2]));
})
],
- Expression: [o('Value'), o('Invocation'), o('Code'), o('Operation'), o('Assign'), o('If'), o('Try'), o('While'), o('For'), o('Switch'), o('Class'), o('Throw'), o('Defer')],
+ Expression: [o('Value'), o('Invocation'), o('Code'), o('Operation'), o('Assign'), o('If'), o('Try'), o('While'), o('For'), o('Switch'), o('Class'), o('Throw'), o('Defer'), o('Await')],
Block: [
o('INDENT OUTDENT', function() {
return new Block;
View
141 lib/coffee-script/nodes.js
@@ -242,6 +242,29 @@
return _results;
};
+ Base.prototype.tameAssignDefersToAwait = function(aw) {
+ var child, _i, _len, _ref2, _results;
+ _ref2 = this.flattenChildren();
+ _results = [];
+ for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
+ child = _ref2[_i];
+ _results.push(child.tameAssignDefersToAwait(aw));
+ }
+ return _results;
+ };
+
+ Base.prototype.tameExtractExpressions = function(inBlock) {
+ var child, lst, v, _i, _len, _ref2;
+ lst = [];
+ _ref2 = this.flattenChildren();
+ for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
+ child = _ref2[_i];
+ v = child.tameExtractExpressions(false);
+ lst = lst.concat(v);
+ }
+ return lst;
+ };
+
Base.prototype.tameWalkAst = function() {
var child, _i, _len, _ref2;
_ref2 = this.flattenChildren();
@@ -345,6 +368,7 @@
function Block(nodes) {
this.expressions = compact(flatten(nodes || []));
+ this.awaitExpressions = [];
}
Block.prototype.children = ['expressions'];
@@ -436,13 +460,18 @@
};
Block.prototype.compileNode = function(o) {
- var code, codes, node, top, _i, _len, _ref2;
+ var a, code, codes, node, top, _i, _j, _len, _len2, _ref2, _ref3;
this.tab = o.indent;
top = o.level === LEVEL_TOP;
codes = [];
- _ref2 = this.expressions;
+ _ref2 = this.awaitExpressions;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
- node = _ref2[_i];
+ a = _ref2[_i];
+ a.allocateTemporary(o);
+ }
+ _ref3 = this.expressions;
+ for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
+ node = _ref3[_j];
node = node.unwrapAll();
node = node.unfoldSoak(o) || node;
if (node instanceof Block) {
@@ -588,7 +617,26 @@
return this.expressions.unshift(new TameRequire());
};
+ Block.prototype.tameExtractExpressions = function(inBlock) {
+ var e, l, newExpressions, _i, _len, _ref2;
+ if (this.parens) {
+ return Block.__super__.tameExtractExpressions.apply(this, arguments);
+ }
+ newExpressions = [];
+ _ref2 = this.expressions;
+ for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
+ e = _ref2[_i];
+ l = e.tameExtractExpressions(true);
+ newExpressions = newExpressions.concat(l.concat([e]));
+ this.awaitExpressions = this.awaitExpressions.concat(l);
+ }
+ this.expressions = newExpressions;
+ return [];
+ };
+
Block.prototype.tameTransform = function() {
+ this.tameExtractExpressions(true);
+ this.tameAssignDefersToAwait();
this.tameWalkAst();
if (this.tameNeedsRuntime() && !this.tameFindRequire()) {
this.tameAddRuntime();
@@ -1863,6 +1911,10 @@
return this.tameCpsPivotFlag = false;
};
+ Code.prototype.tameAssignDefersToAwait = function(aw) {
+ return Code.__super__.tameAssignDefersToAwait.call(this, null);
+ };
+
return Code;
})(Base);
@@ -2363,6 +2415,7 @@
})();
this.params = [];
this.vars = [];
+ this.finalVal = null;
}
Defer.prototype.children = ['slots'];
@@ -2375,12 +2428,19 @@
return v;
};
- Defer.prototype.makeAssignFn = function(o) {
- var a, args, assign, assignments, block, call, func, i, i_lit, inner_fn, outer_block, outer_fn, prop, s, slot, _i, _len, _ref2;
- if (this.slots.length === 0) return null;
+ Defer.prototype.makeAssignFn = function() {
+ var a, args, assign, assignments, block, call, func, i, i_lit, inner_fn, lhs, outer_block, outer_fn, prop, rhs, s, slot, _i, _len, _ref2;
+ if (this.slots.length === 0 && !this.finalVal) return null;
assignments = [];
args = [];
i = 0;
+ if (this.finalVal) {
+ rhs = new Value(new Literal("arguments"));
+ rhs.add(new Index(new Value(new Literal(0))));
+ lhs = new Value(new Literal(this.finalVal));
+ assign = new Assign(lhs, rhs);
+ assignments.push(assign);
+ }
_ref2 = this.slots;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
s = _ref2[_i];
@@ -2435,11 +2495,12 @@
};
Defer.prototype.compileNode = function(o) {
- var call, name, scope, v, _i, _len, _ref2;
+ var call, name, scope, v, _i, _len, _ref2, _ref3;
+ this.finalVal = (_ref2 = this.myAwait) != null ? _ref2.getFinalVal() : void 0;
call = this.transform();
- _ref2 = this.vars;
- for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
- v = _ref2[_i];
+ _ref3 = this.vars;
+ for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
+ v = _ref3[_i];
name = v.compile(o, LEVEL_LIST);
scope = o.scope;
scope.add(name, 'var');
@@ -2451,6 +2512,11 @@
return true;
};
+ Defer.prototype.tameAssignDefersToAwait = function(aw) {
+ this.myAwait = aw;
+ return Defer.__super__.tameAssignDefersToAwait.call(this, aw);
+ };
+
return Defer;
})(Base);
@@ -2461,8 +2527,21 @@
function Await(body) {
this.body = body;
+ this.temporary = null;
+ this.used = false;
+ this.parent = null;
+ this.displaced = false;
}
+ Await.prototype.displace = function() {
+ var ret;
+ ret = new Await(this.body);
+ this.body = null;
+ this.parent = ret;
+ ret.displaced = true;
+ return ret;
+ };
+
Await.prototype.transform = function(o) {
var assign, body, call, cls, lhs, meth, name, rhs;
body = this.body;
@@ -2483,18 +2562,51 @@
Await.prototype.children = ['body'];
- Await.prototype.isStatement = YES;
+ Await.prototype.isStatement = function() {
+ return !this.parent;
+ };
Await.prototype.makeReturn = THIS;
+ Await.prototype.allocateTemporary = function(o) {
+ if (this.displaced) return this.temporary = o.scope.freeVariable('_await');
+ };
+
Await.prototype.compileNode = function(o) {
- this.transform(o);
- return this.body.compile(o);
+ if (this.parent) {
+ return this.parent.temporary;
+ } else {
+ this.transform(o);
+ return this.body.compile(o);
+ }
+ };
+
+ Await.prototype.getFinalVal = function() {
+ if (this.temporary && !this.used) {
+ this.used = true;
+ return this.temporary;
+ } else {
+ return null;
+ }
};
Await.prototype.tameWalkAst = function() {
Await.__super__.tameWalkAst.call(this);
- return this.tameNodeFlag = true;
+ return this.tameNodeFlag = !this.parent;
+ };
+
+ Await.prototype.tameExtractExpressions = function(inBlock) {
+ var children;
+ children = Await.__super__.tameExtractExpressions.call(this, false);
+ if (inBlock && !children.length) {
+ return [];
+ } else {
+ return [this.displace()].concat(children);
+ }
+ };
+
+ Await.prototype.tameAssignDefersToAwait = function(aw) {
+ return Await.__super__.tameAssignDefersToAwait.call(this, this);
};
return Await;
@@ -2657,6 +2769,7 @@
function Parens(body) {
this.body = body;
+ this.body.parens = true;
}
Parens.prototype.children = ['body'];
View
2  src/grammar.coffee
@@ -79,7 +79,6 @@ grammar =
o 'Return'
o 'Comment'
o 'STATEMENT', -> new Literal $1
- o 'Await'
o 'Require'
]
@@ -111,6 +110,7 @@ grammar =
o 'Class'
o 'Throw'
o 'Defer'
+ o 'Await'
]
# An indented block of expressions. Note that the [Rewriter](rewriter.html)
View
98 src/nodes.coffee
@@ -199,11 +199,25 @@ exports.Base = class Base
#
# AST Walking Routines for CPS Pivots, etc.
#
- # There are three passes:
- # 1. Find await's and trace upward.
- # 2. Find loops found in #1, and flood downward
- # 3. Find break/continue found in #2, and trace upward
+ # There are five passes:
+ # 1. Extract expresions
+ # 2. Assign Defers to their awaits
+ # 3. Find await's and trace upward.
+ # 4. Find loops found in #1, and flood downward
+ # 5. Find break/continue found in #2, and trace upward
#
+ #
+ tameAssignDefersToAwait : (aw) ->
+ for child in @flattenChildren()
+ child.tameAssignDefersToAwait aw
+
+ tameExtractExpressions : (inBlock) ->
+ lst = []
+ for child in @flattenChildren()
+ v = child.tameExtractExpressions(false)
+ lst = lst.concat v
+ lst
+
# tameWalkAst
# Walk the AST looking for taming. Mark a node as with tame flags
# if any of its children are tamed, but don't cross scope boundary
@@ -283,6 +297,7 @@ exports.Base = class Base
exports.Block = class Block extends Base
constructor: (nodes) ->
@expressions = compact flatten nodes or []
+ @awaitExpressions = []
children: ['expressions']
@@ -359,6 +374,8 @@ exports.Block = class Block extends Base
@tab = o.indent
top = o.level is LEVEL_TOP
codes = []
+ for a in @awaitExpressions
+ a.allocateTemporary o
for node in @expressions
node = node.unwrapAll()
node = (node.unfoldSoak(o) or node)
@@ -510,8 +527,20 @@ exports.Block = class Block extends Base
tameAddRuntime : ->
@expressions.unshift new TameRequire()
+ tameExtractExpressions : (inBlock) ->
+ return super if @parens
+ newExpressions = []
+ for e in @expressions
+ l = e.tameExtractExpressions(true)
+ newExpressions = newExpressions.concat l.concat [e]
+ @awaitExpressions = @awaitExpressions.concat l
+ @expressions = newExpressions
+ []
+
# Perform all steps of the Tame transform
tameTransform : ->
+ @tameExtractExpressions(true)
+ @tameAssignDefersToAwait()
@tameWalkAst()
@tameAddRuntime() if @tameNeedsRuntime() and not @tameFindRequire()
@tameWalkAstLoops(false)
@@ -1522,6 +1551,9 @@ exports.Code = class Code extends Base
super()
@tameCpsPivotFlag = false
+ tameAssignDefersToAwait : (aw) ->
+ super null
+
#### Param
# A parameter in a function definition. Beyond a typical Javascript parameter,
@@ -1935,6 +1967,7 @@ exports.Defer = class Defer extends Base
@slots = (a.toSlot() for a in args)
@params = []
@vars = []
+ @finalVal = null
children : ['slots' ]
@@ -1965,11 +1998,17 @@ exports.Defer = class Defer extends Base
# Case 4 -- defer(rest...) -- rest is an array, assign it to all
# leftover arguments.
#
- makeAssignFn : (o) ->
- return null if @slots.length is 0
+ makeAssignFn : () ->
+ return null if @slots.length is 0 and not @finalVal
assignments = []
args = []
i = 0
+ if @finalVal
+ rhs = new Value new Literal "arguments"
+ rhs.add new Index new Value new Literal 0
+ lhs = new Value new Literal @finalVal
+ assign = new Assign lhs, rhs
+ assignments.push assign
for s in @slots
a = new Value new Literal "arguments"
i_lit = new Value new Literal i
@@ -1997,6 +2036,7 @@ exports.Defer = class Defer extends Base
assign = new Assign slot, a
assignments.push assign
i++
+
block = new Block assignments
inner_fn = new Code [], block, 'tamegen'
outer_block = new Block [ new Return inner_fn ]
@@ -2024,6 +2064,7 @@ exports.Defer = class Defer extends Base
new Call fn, [ new Value o ]
compileNode : (o) ->
+ @finalVal = @myAwait?.getFinalVal()
call = @transform()
for v in @vars
name = v.compile o, LEVEL_LIST
@@ -2033,11 +2074,26 @@ exports.Defer = class Defer extends Base
tameNeedsRuntime : -> true
+ tameAssignDefersToAwait : (aw) ->
+ @myAwait = aw
+ super aw
+
#### Await
exports.Await = class Await extends Base
constructor : (body) ->
@body = body
+ @temporary = null
+ @used = false
+ @parent = null
+ @displaced = false
+
+ displace : () ->
+ ret = new Await @body
+ @body = null
+ @parent = ret
+ ret.displaced = true
+ ret
transform : (o) ->
body = @body
@@ -2057,20 +2113,41 @@ exports.Await = class Await extends Base
children: ['body']
- isStatement: YES
+ isStatement: -> not @parent
makeReturn : THIS
+ allocateTemporary : (o) ->
+ if @displaced
+ @temporary = o.scope.freeVariable '_await'
+
compileNode: (o) ->
- @transform(o)
- @body.compile o
+ if @parent
+ @parent.temporary
+ else
+ @transform(o)
+ @body.compile o
+
+ getFinalVal : () ->
+ if @temporary and not @used
+ @used = true
+ @temporary
+ else
+ null
# We still need to walk our children to see if there are any embedded
# function which might also be tamed. But we're always going to report
# to our parent that we are tamed, since we are!
tameWalkAst : ->
super()
- @tameNodeFlag = true
+ @tameNodeFlag = not @parent
+
+ tameExtractExpressions : (inBlock) ->
+ children = super false
+ if inBlock and not children.length then []
+ else [ @displace() ].concat children
+ tameAssignDefersToAwait : (aw) ->
+ super this
#### tameRequire
#
@@ -2209,6 +2286,7 @@ exports.Existence = class Existence extends Base
# Parentheses are a good way to force any statement to become an expression.
exports.Parens = class Parens extends Base
constructor: (@body) ->
+ @body.parens = true
children: ['body']
Please sign in to comment.
Something went wrong with that request. Please try again.