1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

786: implemented dynamic object keys

This commit is contained in:
satyr 2010-10-24 14:05:31 +09:00
parent 291f377d40
commit 331036f86d
6 changed files with 117 additions and 53 deletions

View file

@ -77,18 +77,16 @@
})
],
AssignObj: [
o('Identifier', function() {
o('ObjAssignable', function() {
return new Value($1);
}), o('AlphaNumeric'), o('ThisProperty'), o('Identifier : Expression', function() {
}), o('ObjAssignable : Expression', function() {
return new Assign(new Value($1), $3, 'object');
}), o('AlphaNumeric : Expression', function() {
return new Assign(new Value($1), $3, 'object');
}), o('Identifier : INDENT Expression OUTDENT', function() {
}), o('ObjAssignable :\
INDENT Expression OUTDENT', function() {
return new Assign(new Value($1), $4, 'object');
}), o('AlphaNumeric : INDENT Expression OUTDENT', function() {
return new Assign(new Value($1), $4, 'object');
}), o('Comment')
}), o('ThisProperty'), o('Comment')
],
ObjAssignable: [o('Identifier'), o('AlphaNumeric'), o('Parenthetical')],
Return: [
o("RETURN Expression", function() {
return new Return($2);
@ -156,9 +154,9 @@
}), o("ThisProperty")
],
Assignable: [
o("SimpleAssignable"), o("Array", function() {
o('SimpleAssignable'), o('Array', function() {
return new Value($1);
}), o("Object", function() {
}), o('Object', function() {
return new Value($1);
})
],

View file

@ -47,15 +47,15 @@
o.sharedScope = o.scope;
return Closure.wrap(this).compileNode(o);
};
Base.prototype.cache = function(o, lvl) {
Base.prototype.cache = function(o, level, reused) {
var ref, sub;
if (!this.isComplex()) {
ref = lvl ? this.compile(o, lvl) : this;
ref = level ? this.compile(o, level) : this;
return [ref, ref];
} else {
ref = new Literal(o.scope.freeVariable('ref'));
ref = new Literal(reused || o.scope.freeVariable('ref'));
sub = new Assign(ref, this);
return lvl ? [sub.compile(o, lvl), ref.value] : [sub, ref];
return level ? [sub.compile(o, level), ref.value] : [sub, ref];
}
};
Base.prototype.compileLoopReference = function(o, name) {
@ -199,9 +199,9 @@
}
return this;
};
Expressions.prototype.compile = function(o, lvl) {
Expressions.prototype.compile = function(o, level) {
o || (o = {});
return o.scope ? Expressions.__super__.compile.call(this, o, lvl) : this.compileRoot(o);
return o.scope ? Expressions.__super__.compile.call(this, o, level) : this.compileRoot(o);
};
Expressions.prototype.compileNode = function(o) {
var _i, _len, _ref2, _result, node;
@ -299,10 +299,10 @@
Return.prototype.isStatement = YES;
Return.prototype.isPureStatement = YES;
Return.prototype.makeReturn = THIS;
Return.prototype.compile = function(o, lvl) {
Return.prototype.compile = function(o, level) {
var _ref2, expr;
expr = (_ref2 = this.expression) != null ? _ref2.makeReturn() : undefined;
return expr && !(expr instanceof Return) ? expr.compile(o, lvl) : Return.__super__.compile.call(this, o, lvl);
return expr && !(expr instanceof Return) ? expr.compile(o, level) : Return.__super__.compile.call(this, o, level);
};
Return.prototype.compileNode = function(o) {
o.level = LEVEL_PAREN;
@ -652,13 +652,20 @@
__extends(ObjectLiteral, Base);
ObjectLiteral.prototype.children = ['properties'];
ObjectLiteral.prototype.compileNode = function(o) {
var _i, _len, _ref2, _result, i, indent, join, lastNoncom, nonComments, obj, prop, props;
var _i, _len, _len2, _ref2, _ref3, _result, i, indent, join, lastNoncom, nonComments, obj, prop, props;
_ref2 = this.properties;
for (i = 0, _len = _ref2.length; i < _len; i++) {
prop = _ref2[i];
if ((prop.variable || prop).base instanceof Parens) {
return this.compileDynamic(o, i);
}
}
o.indent = this.idt(1);
nonComments = (function() {
_ref2 = this.properties;
_ref3 = this.properties;
_result = [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
prop = _ref2[_i];
for (_i = 0, _len2 = _ref3.length; _i < _len2; _i++) {
prop = _ref3[_i];
if (!(prop instanceof Comment)) {
_result.push(prop);
}
@ -667,10 +674,10 @@
}).call(this);
lastNoncom = last(nonComments);
props = (function() {
_ref2 = this.properties;
_ref3 = this.properties;
_result = [];
for (i = 0, _len = _ref2.length; i < _len; i++) {
prop = _ref2[i];
for (i = 0, _len2 = _ref3.length; i < _len2; i++) {
prop = _ref3[i];
_result.push((function() {
join = i === this.properties.length - 1 ? '' : prop === lastNoncom || prop instanceof Comment ? '\n' : ',\n';
indent = prop instanceof Comment ? '' : this.idt(1);
@ -688,6 +695,28 @@
obj = "{" + (props ? '\n' + props + '\n' + this.idt() : '') + "}";
return this.tags.front ? "(" + obj + ")" : obj;
};
ObjectLiteral.prototype.compileDynamic = function(o, idx) {
var _len, _ref2, _ref3, code, i, key, obj, prop, ref, sub;
obj = o.scope.freeVariable('obj');
code = "" + obj + " = " + (new ObjectLiteral(this.properties.slice(0, idx)).compile(o)) + ", ";
_ref2 = this.properties.slice(idx);
for (i = 0, _len = _ref2.length; i < _len; i++) {
prop = _ref2[i];
if (prop instanceof Assign) {
key = prop.variable.compile(o, LEVEL_PAREN);
code += "" + obj + "[" + key + "] = " + (prop.value.compile(o, LEVEL_LIST)) + ", ";
continue;
}
if (prop instanceof Comment) {
code += prop.compile(o) + ' ';
continue;
}
_ref3 = prop.base.cache(o, LEVEL_LIST, ref), sub = _ref3[0], ref = _ref3[1];
code += "" + obj + "[" + sub + "] = " + ref + ", ";
}
code += obj;
return o.level <= LEVEL_PAREN ? code : "(" + code + ")";
};
ObjectLiteral.prototype.assigns = function(name) {
var _i, _len, _ref2, prop;
_ref2 = this.properties;

File diff suppressed because one or more lines are too long

View file

@ -144,16 +144,20 @@ grammar =
# Assignment when it happens within an object literal. The difference from
# the ordinary **Assign** is that these allow numbers and strings as keys.
AssignObj: [
o 'Identifier', -> new Value $1
o 'AlphaNumeric'
o 'ObjAssignable', -> new Value $1
o 'ObjAssignable : Expression', -> new Assign new Value($1), $3, 'object'
o 'ObjAssignable :
INDENT Expression OUTDENT', -> new Assign new Value($1), $4, 'object'
o 'ThisProperty'
o 'Identifier : Expression', -> new Assign new Value($1), $3, 'object'
o 'AlphaNumeric : Expression', -> new Assign new Value($1), $3, 'object'
o 'Identifier : INDENT Expression OUTDENT', -> new Assign new Value($1), $4, 'object'
o 'AlphaNumeric : INDENT Expression OUTDENT', -> new Assign new Value($1), $4, 'object'
o 'Comment'
]
ObjAssignable: [
o 'Identifier'
o 'AlphaNumeric'
o 'Parenthetical'
]
# A return statement from a function body.
Return: [
o "RETURN Expression", -> new Return $2
@ -222,9 +226,9 @@ grammar =
# Everything that can be assigned to.
Assignable: [
o "SimpleAssignable"
o "Array", -> new Value $1
o "Object", -> new Value $1
o 'SimpleAssignable'
o 'Array', -> new Value $1
o 'Object', -> new Value $1
]
# The types of things that can be treated as values -- assigned to, invoked

View file

@ -58,14 +58,14 @@ exports.Base = class Base
# If the code generation wishes to use the result of a complex expression
# in multiple places, ensure that the expression is only ever evaluated once,
# by assigning it to a temporary variable. Pass a level to precompile.
cache: (o, lvl) ->
cache: (o, level, reused) ->
unless @isComplex()
ref = if lvl then @compile o, lvl else this
ref = if level then @compile o, level else this
[ref, ref]
else
ref = new Literal o.scope.freeVariable 'ref'
ref = new Literal reused or o.scope.freeVariable 'ref'
sub = new Assign ref, this
if lvl then [sub.compile(o, lvl), ref.value] else [sub, ref]
if level then [sub.compile(o, level), ref.value] else [sub, ref]
# Compile to a source/variable pair suitable for looping.
compileLoopReference: (o, name) ->
@ -193,9 +193,9 @@ exports.Expressions = class Expressions extends Base
this
# An **Expressions** is the only node that can serve as the root.
compile: (o, lvl) ->
compile: (o, level) ->
o or= {}
if o.scope then super o, lvl else @compileRoot o
if o.scope then super o, level else @compileRoot o
compileNode: (o) ->
@tab = o.indent
@ -279,9 +279,9 @@ exports.Return = class Return extends Base
makeReturn: THIS
compile: (o, lvl) ->
compile: (o, level) ->
expr = @expression?.makeReturn()
if expr and expr not instanceof Return then expr.compile o, lvl else super o, lvl
if expr and expr not instanceof Return then expr.compile o, level else super o, level
compileNode: (o) ->
o.level = LEVEL_PAREN
@ -572,6 +572,8 @@ exports.ObjectLiteral = class ObjectLiteral extends Base
@objects = @properties = props or []
compileNode: (o) ->
for prop, i in @properties when (prop.variable or prop).base instanceof Parens
return @compileDynamic o, i
o.indent = @idt 1
nonComments = prop for prop in @properties when prop not instanceof Comment
lastNoncom = last nonComments
@ -592,6 +594,22 @@ exports.ObjectLiteral = class ObjectLiteral extends Base
obj = "{#{ if props then '\n' + props + '\n' + @idt() else '' }}"
if @tags.front then "(#{obj})" else obj
compileDynamic: (o, idx) ->
obj = o.scope.freeVariable 'obj'
code = "#{obj} = #{ new ObjectLiteral(@properties.slice 0, idx).compile o }, "
for prop, i in @properties.slice idx
if prop instanceof Assign
key = prop.variable.compile o, LEVEL_PAREN
code += "#{obj}[#{key}] = #{ prop.value.compile o, LEVEL_LIST }, "
continue
if prop instanceof Comment
code += prop.compile(o) + ' '
continue
[sub, ref] = prop.base.cache o, LEVEL_LIST, ref
code += "#{obj}[#{sub}] = #{ref}, "
code += obj
if o.level <= LEVEL_PAREN then code else "(#{code})"
assigns: (name) ->
for prop in @properties when prop.assigns name then return yes
no

View file

@ -231,6 +231,21 @@ eq result.two, 2
eq result.two, obj.list()[1]
# Dynamic object keys.
i = 0
obj = {
### leading comment ###
"interpolated#{'key'}": 123
(4 * 2): 8
### cached shorthand ###
(++i)
### traling comment ###
}
eq obj.interpolatedkey, 123
eq obj[8], 8
eq obj[1], 1
#542: Objects leading expression statement should be parenthesized.
{f: -> ok yes }.f() + 1