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:
parent
291f377d40
commit
331036f86d
6 changed files with 117 additions and 53 deletions
|
@ -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);
|
||||
})
|
||||
],
|
||||
|
|
59
lib/nodes.js
59
lib/nodes.js
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue