mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-04-11 03:00:13 -04:00
and now with if/else statements, CoffeeScript-in-CoffeeScript is language-complete -- now for the shakedown cruise
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
(function(){
|
(function(){
|
||||||
var AccessorNode, ArrayNode, AssignNode, CallNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IndexNode, LiteralNode, Node, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThisNode, ThrowNode, TryNode, ValueNode, WhileNode, any, compact, del, dup, flatten, inherit, merge, statement;
|
var AccessorNode, ArrayNode, AssignNode, CallNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IfNode, IndexNode, LiteralNode, Node, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThisNode, ThrowNode, TryNode, ValueNode, WhileNode, any, compact, del, dup, flatten, inherit, merge, statement;
|
||||||
var __hasProp = Object.prototype.hasOwnProperty;
|
var __hasProp = Object.prototype.hasOwnProperty;
|
||||||
process.mixin(require('./scope'));
|
process.mixin(require('./scope'));
|
||||||
// The abstract base class for all CoffeeScript nodes.
|
// The abstract base class for all CoffeeScript nodes.
|
||||||
@@ -1290,4 +1290,113 @@
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
statement(ForNode);
|
statement(ForNode);
|
||||||
|
// If/else statements. Switch/whens get compiled into these. Acts as an
|
||||||
|
// expression by pushing down requested returns to the expression bodies.
|
||||||
|
// Single-expression IfNodes are compiled into ternary operators if possible,
|
||||||
|
// because ternaries are first-class returnable assignable expressions.
|
||||||
|
IfNode = (exports.IfNode = inherit(Node, {
|
||||||
|
constructor: function constructor(condition, body, else_body, tags) {
|
||||||
|
this.condition = condition;
|
||||||
|
this.body = body && body.unwrap();
|
||||||
|
this.else_body = else_body && else_body.unwrap();
|
||||||
|
this.children = [this.condition, this.body, this.else_body];
|
||||||
|
this.tags = tags || {
|
||||||
|
};
|
||||||
|
if (this.condition instanceof Array) {
|
||||||
|
this.multiple = true;
|
||||||
|
}
|
||||||
|
if (this.tags.invert) {
|
||||||
|
this.condition = new OpNode('!', new ParentheticalNode(this.condition));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
push: function push(else_body) {
|
||||||
|
var eb;
|
||||||
|
eb = else_body.unwrap();
|
||||||
|
this.else_body ? this.else_body.push(eb) : (this.else_body = eb);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
force_statement: function force_statement() {
|
||||||
|
this.tags.statement = true;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
// Rewrite a chain of IfNodes with their switch condition for equality.
|
||||||
|
rewrite_condition: function rewrite_condition(expression) {
|
||||||
|
var __a, __b, __c, cond;
|
||||||
|
this.condition = (function() {
|
||||||
|
if (this.multiple) {
|
||||||
|
__a = []; __b = this.condition;
|
||||||
|
for (__c = 0; __c < __b.length; __c++) {
|
||||||
|
cond = __b[__c];
|
||||||
|
__a.push(new OpNode('is', expression, cond));
|
||||||
|
}
|
||||||
|
return __a;
|
||||||
|
} else {
|
||||||
|
return new OpNode('is', expression, this.condition);
|
||||||
|
}
|
||||||
|
}).call(this);
|
||||||
|
if (this.is_chain()) {
|
||||||
|
this.else_body.rewrite_condition(expression);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
// Rewrite a chain of IfNodes to add a default case as the final else.
|
||||||
|
add_else: function add_else(exprs) {
|
||||||
|
this.is_chain() ? this.else_body.add_else(exprs) : (this.else_body = exprs && exprs.unwrap());
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
// If the else_body is an IfNode itself, then we've got an if-else chain.
|
||||||
|
is_chain: function is_chain() {
|
||||||
|
return this.chain = this.chain || this.else_body && this.else_body instanceof IfNode;
|
||||||
|
},
|
||||||
|
// The IfNode only compiles into a statement if either of the bodies needs
|
||||||
|
// to be a statement.
|
||||||
|
is_statement: function is_statement() {
|
||||||
|
return this.statement = this.statement || !!(this.comment || this.tags.statement || this.body.is_statement() || (this.else_body && this.else_body.is_statement()));
|
||||||
|
},
|
||||||
|
compile_condition: function compile_condition(o) {
|
||||||
|
var __a, __b, __c, cond;
|
||||||
|
return ((function() {
|
||||||
|
__a = []; __b = flatten(this.condition);
|
||||||
|
for (__c = 0; __c < __b.length; __c++) {
|
||||||
|
cond = __b[__c];
|
||||||
|
__a.push(cond.compile(o));
|
||||||
|
}
|
||||||
|
return __a;
|
||||||
|
}).call(this)).join(' || ');
|
||||||
|
},
|
||||||
|
compile_node: function compile_node(o) {
|
||||||
|
return this.is_statement() ? this.compile_statement(o) : this.compile_ternary(o);
|
||||||
|
},
|
||||||
|
// Compile the IfNode as a regular if-else statement. Flattened chains
|
||||||
|
// force sub-else bodies into statement form.
|
||||||
|
compile_statement: function compile_statement(o) {
|
||||||
|
var body, child, com_dent, cond_o, else_part, if_dent, if_part, prefix;
|
||||||
|
child = del(o, 'chain_child');
|
||||||
|
cond_o = dup(o);
|
||||||
|
del(cond_o, 'returns');
|
||||||
|
o.indent = this.idt(1);
|
||||||
|
o.top = true;
|
||||||
|
if_dent = child ? '' : this.idt();
|
||||||
|
com_dent = child ? this.idt() : '';
|
||||||
|
prefix = this.comment ? this.comment.compile(cond_o) + '\n' + com_dent : '';
|
||||||
|
body = Expressions.wrap([body]).compile(o);
|
||||||
|
if_part = prefix + if_dent + 'if (' + compile_condition(cond_o) + ') {\n' + body + '\n' + this.idt() + '}';
|
||||||
|
if (!(this.else_body)) {
|
||||||
|
return if_part;
|
||||||
|
}
|
||||||
|
else_part = this.is_chain() ? ' else ' + this.else_body.compile(merge(o, {
|
||||||
|
indent: this.idt(),
|
||||||
|
chain_child: true
|
||||||
|
})) : ' else {\n' + Expressions.wrap(this.else_body).compile(o) + '\n' + this.idt() + '}';
|
||||||
|
return if_part + else_part;
|
||||||
|
},
|
||||||
|
// Compile the IfNode into a ternary operator.
|
||||||
|
compile_ternary: function compile_ternary(o) {
|
||||||
|
var else_part, if_part;
|
||||||
|
if_part = this.condition.compile(o) + ' ? ' + this.body.compile(o);
|
||||||
|
else_part = this.else_body ? this.else_body.compile(o) : 'null';
|
||||||
|
return if_part + ' : ' + else_part;
|
||||||
|
}
|
||||||
|
}));
|
||||||
})();
|
})();
|
||||||
@@ -1029,7 +1029,7 @@ module CoffeeScript
|
|||||||
# Compile the IfNode into a ternary operator.
|
# Compile the IfNode into a ternary operator.
|
||||||
def compile_ternary(o)
|
def compile_ternary(o)
|
||||||
if_part = "#{@condition.compile(o)} ? #{@body.compile(o)}"
|
if_part = "#{@condition.compile(o)} ? #{@body.compile(o)}"
|
||||||
else_part = @else_body ? "#{@else_body.compile(o)}" : 'null'
|
else_part = @else_body ? @else_body.compile(o) : 'null'
|
||||||
"#{if_part} : #{else_part}"
|
"#{if_part} : #{else_part}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -462,7 +462,7 @@
|
|||||||
statement: true
|
statement: true
|
||||||
});
|
});
|
||||||
}), o("Comment TERMINATOR When", function() {
|
}), o("Comment TERMINATOR When", function() {
|
||||||
return $3.add_comment($1);
|
return $3.comment = $1;
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
// The most basic form of "if".
|
// The most basic form of "if".
|
||||||
|
|||||||
@@ -945,7 +945,87 @@ ForNode: exports.ForNode: inherit Node, {
|
|||||||
|
|
||||||
statement ForNode
|
statement ForNode
|
||||||
|
|
||||||
|
# If/else statements. Switch/whens get compiled into these. Acts as an
|
||||||
|
# expression by pushing down requested returns to the expression bodies.
|
||||||
|
# Single-expression IfNodes are compiled into ternary operators if possible,
|
||||||
|
# because ternaries are first-class returnable assignable expressions.
|
||||||
|
IfNode: exports.IfNode: inherit Node, {
|
||||||
|
|
||||||
|
constructor: (condition, body, else_body, tags) ->
|
||||||
|
@condition: condition
|
||||||
|
@body: body and body.unwrap()
|
||||||
|
@else_body: else_body and else_body.unwrap()
|
||||||
|
@children: [@condition, @body, @else_body]
|
||||||
|
@tags: tags or {}
|
||||||
|
@multiple: true if @condition instanceof Array
|
||||||
|
@condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert
|
||||||
|
this
|
||||||
|
|
||||||
|
push: (else_body) ->
|
||||||
|
eb: else_body.unwrap()
|
||||||
|
if @else_body then @else_body.push(eb) else @else_body: eb
|
||||||
|
this
|
||||||
|
|
||||||
|
force_statement: ->
|
||||||
|
@tags.statement: true
|
||||||
|
this
|
||||||
|
|
||||||
|
# Rewrite a chain of IfNodes with their switch condition for equality.
|
||||||
|
rewrite_condition: (expression) ->
|
||||||
|
@condition: if @multiple
|
||||||
|
new OpNode('is', expression, cond) for cond in @condition
|
||||||
|
else
|
||||||
|
new OpNode('is', expression, @condition)
|
||||||
|
@else_body.rewrite_condition(expression) if @is_chain()
|
||||||
|
this
|
||||||
|
|
||||||
|
# Rewrite a chain of IfNodes to add a default case as the final else.
|
||||||
|
add_else: (exprs) ->
|
||||||
|
if @is_chain() then @else_body.add_else(exprs) else @else_body: exprs and exprs.unwrap()
|
||||||
|
this
|
||||||
|
|
||||||
|
# If the else_body is an IfNode itself, then we've got an if-else chain.
|
||||||
|
is_chain: ->
|
||||||
|
@chain ||= @else_body and @else_body instanceof IfNode
|
||||||
|
|
||||||
|
# The IfNode only compiles into a statement if either of the bodies needs
|
||||||
|
# to be a statement.
|
||||||
|
is_statement: ->
|
||||||
|
@statement ||= !!(@comment or @tags.statement or @body.is_statement() or (@else_body and @else_body.is_statement()))
|
||||||
|
|
||||||
|
compile_condition: (o) ->
|
||||||
|
(cond.compile(o) for cond in flatten(@condition)).join(' || ')
|
||||||
|
|
||||||
|
compile_node: (o) ->
|
||||||
|
if @is_statement() then @compile_statement(o) else @compile_ternary(o)
|
||||||
|
|
||||||
|
# Compile the IfNode as a regular if-else statement. Flattened chains
|
||||||
|
# force sub-else bodies into statement form.
|
||||||
|
compile_statement: (o) ->
|
||||||
|
child: del o, 'chain_child'
|
||||||
|
cond_o: dup o
|
||||||
|
del cond_o, 'returns'
|
||||||
|
o.indent: @idt(1)
|
||||||
|
o.top: true
|
||||||
|
if_dent: if child then '' else @idt()
|
||||||
|
com_dent: if child then @idt() else ''
|
||||||
|
prefix: if @comment then @comment.compile(cond_o) + '\n' + com_dent else ''
|
||||||
|
body: Expressions.wrap([body]).compile(o)
|
||||||
|
if_part: prefix + if_dent + 'if (' + compile_condition(cond_o) + ') {\n' + body + '\n' + @idt() + '}'
|
||||||
|
return if_part unless @else_body
|
||||||
|
else_part: if @is_chain()
|
||||||
|
' else ' + @else_body.compile(merge(o, {indent: @idt(), chain_child: true}))
|
||||||
|
else
|
||||||
|
' else {\n' + Expressions.wrap(@else_body).compile(o) + '\n' + @idt() + '}'
|
||||||
|
if_part + else_part
|
||||||
|
|
||||||
|
# Compile the IfNode into a ternary operator.
|
||||||
|
compile_ternary: (o) ->
|
||||||
|
if_part: @condition.compile(o) + ' ? ' + @body.compile(o)
|
||||||
|
else_part: if @else_body then @else_body.compile(o) else 'null'
|
||||||
|
if_part + ' : ' + else_part
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -405,7 +405,7 @@ grammar: {
|
|||||||
When: [
|
When: [
|
||||||
o "LEADING_WHEN SimpleArgs Block", -> new IfNode($2, $3, null, {statement: true})
|
o "LEADING_WHEN SimpleArgs Block", -> new IfNode($2, $3, null, {statement: true})
|
||||||
o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode($2, $3, null, {statement: true})
|
o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode($2, $3, null, {statement: true})
|
||||||
o "Comment TERMINATOR When", -> $3.add_comment($1)
|
o "Comment TERMINATOR When", -> $3.comment: $1
|
||||||
]
|
]
|
||||||
|
|
||||||
# The most basic form of "if".
|
# The most basic form of "if".
|
||||||
|
|||||||
Reference in New Issue
Block a user