mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
* Computed property keys * refactor * improvements * refactor tests * fix comments
This commit is contained in:
parent
7f5ab7eb0d
commit
5eb9dded52
7 changed files with 455 additions and 241 deletions
|
@ -282,7 +282,15 @@
|
|||
});
|
||||
})
|
||||
],
|
||||
SimpleObjAssignable: [o('Identifier'), o('Property'), o('ThisProperty')],
|
||||
SimpleObjAssignable: [
|
||||
o('Identifier'),
|
||||
o('Property'),
|
||||
o('ThisProperty'),
|
||||
o('[ Expression ]',
|
||||
function() {
|
||||
return new Value(new ComputedPropertyName($2));
|
||||
})
|
||||
],
|
||||
ObjAssignable: [o('SimpleObjAssignable'), o('AlphaNumeric')],
|
||||
// Object literal spread properties.
|
||||
ObjRestValue: [
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// nodes are created as the result of actions in the [grammar](grammar.html),
|
||||
// but some are created by other nodes as a method of code generation. To convert
|
||||
// the syntax tree into a string of JavaScript code, call `compile()` on the root.
|
||||
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXTag, Call, Class, Code, CodeFragment, Elision, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncGlyph, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, hasLineComments, indentInitial, isLiteralArguments, isLiteralThis, isUnassignable, locationDataToString, merge, moveComments, multident, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility,
|
||||
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXTag, Call, Class, Code, CodeFragment, ComputedPropertyName, Elision, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncGlyph, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, hasLineComments, indentInitial, isLiteralArguments, isLiteralThis, isUnassignable, locationDataToString, merge, moveComments, multident, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility,
|
||||
indexOf = [].indexOf,
|
||||
splice = [].splice,
|
||||
slice = [].slice;
|
||||
|
@ -1107,6 +1107,13 @@
|
|||
|
||||
})();
|
||||
|
||||
exports.ComputedPropertyName = ComputedPropertyName = class ComputedPropertyName extends PropertyName {
|
||||
compileNode(o) {
|
||||
return [this.makeCode('['), ...this.value.compileToFragments(o, LEVEL_LIST), this.makeCode(']')];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
exports.StatementLiteral = StatementLiteral = (function() {
|
||||
class StatementLiteral extends Literal {
|
||||
jumps(o) {
|
||||
|
@ -2214,6 +2221,18 @@
|
|||
key = new PropertyName(key.value);
|
||||
}
|
||||
prop = new Assign(key, value, 'object');
|
||||
} else if (key instanceof Value && key.base instanceof ComputedPropertyName) {
|
||||
// `{ [foo()] }` output as `{ [ref = foo()]: ref }`.
|
||||
if (prop.base.value.shouldCache()) {
|
||||
[key, value] = prop.base.value.cache(o);
|
||||
if (key instanceof IdentifierLiteral) {
|
||||
key = new ComputedPropertyName(key.value);
|
||||
}
|
||||
prop = new Assign(key, value, 'object');
|
||||
} else {
|
||||
// `{ [expression] }` output as `{ [expression]: expression }`.
|
||||
prop = new Assign(key, prop.base.value, 'object');
|
||||
}
|
||||
} else if (!(typeof prop.bareLiteral === "function" ? prop.bareLiteral(IdentifierLiteral) : void 0)) {
|
||||
prop = new Assign(prop, prop, 'object');
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -206,6 +206,7 @@ grammar =
|
|||
o 'Identifier'
|
||||
o 'Property'
|
||||
o 'ThisProperty'
|
||||
o '[ Expression ]', -> new Value new ComputedPropertyName $2
|
||||
]
|
||||
|
||||
ObjAssignable: [
|
||||
|
|
|
@ -765,6 +765,10 @@ exports.CSXTag = class CSXTag extends IdentifierLiteral
|
|||
exports.PropertyName = class PropertyName extends Literal
|
||||
isAssignable: YES
|
||||
|
||||
exports.ComputedPropertyName = class ComputedPropertyName extends PropertyName
|
||||
compileNode: (o) ->
|
||||
[@makeCode('['), @value.compileToFragments(o, LEVEL_LIST)..., @makeCode(']')]
|
||||
|
||||
exports.StatementLiteral = class StatementLiteral extends Literal
|
||||
isStatement: YES
|
||||
|
||||
|
@ -1501,6 +1505,15 @@ exports.Obj = class Obj extends Base
|
|||
[key, value] = prop.base.cache o
|
||||
key = new PropertyName key.value if key instanceof IdentifierLiteral
|
||||
prop = new Assign key, value, 'object'
|
||||
else if key instanceof Value and key.base instanceof ComputedPropertyName
|
||||
# `{ [foo()] }` output as `{ [ref = foo()]: ref }`.
|
||||
if prop.base.value.shouldCache()
|
||||
[key, value] = prop.base.value.cache o
|
||||
key = new ComputedPropertyName key.value if key instanceof IdentifierLiteral
|
||||
prop = new Assign key, value, 'object'
|
||||
else
|
||||
# `{ [expression] }` output as `{ [expression]: expression }`.
|
||||
prop = new Assign key, prop.base.value, 'object'
|
||||
else if not prop.bareLiteral?(IdentifierLiteral)
|
||||
prop = new Assign prop, prop, 'object'
|
||||
if indent then answer.push @makeCode indent
|
||||
|
|
|
@ -183,9 +183,9 @@ test "#1096: unexpected generated tokens", ->
|
|||
for i in [1]:
|
||||
1
|
||||
''', '''
|
||||
[stdin]:1:10: error: unexpected [
|
||||
for i in [1]:
|
||||
^
|
||||
[stdin]:2:4: error: unexpected end of input
|
||||
1
|
||||
^
|
||||
'''
|
||||
# Unexpected regex
|
||||
assertErrorFormat '{/a/i: val}', '''
|
||||
|
@ -808,29 +808,8 @@ test "invalid numbers", ->
|
|||
^^^
|
||||
'''
|
||||
|
||||
test "unexpected object keys", ->
|
||||
assertErrorFormat '''
|
||||
{[[]]}
|
||||
''', '''
|
||||
[stdin]:1:2: error: unexpected [
|
||||
{[[]]}
|
||||
^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
{[[]]: 1}
|
||||
''', '''
|
||||
[stdin]:1:2: error: unexpected [
|
||||
{[[]]: 1}
|
||||
^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
[[]]: 1
|
||||
''', '''
|
||||
[stdin]:1:1: error: unexpected [
|
||||
[[]]: 1
|
||||
^
|
||||
'''
|
||||
|
||||
test "unexpected object keys", ->
|
||||
assertErrorFormat '''
|
||||
{(a + "b")}
|
||||
''', '''
|
||||
|
@ -852,20 +831,6 @@ test "unexpected object keys", ->
|
|||
(a + "b"): 1
|
||||
^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
a: 1, [[]]: 2
|
||||
''', '''
|
||||
[stdin]:1:7: error: unexpected [
|
||||
a: 1, [[]]: 2
|
||||
^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
{a: 1, [[]]: 2}
|
||||
''', '''
|
||||
[stdin]:1:8: error: unexpected [
|
||||
{a: 1, [[]]: 2}
|
||||
^
|
||||
'''
|
||||
|
||||
test "invalid object keys", ->
|
||||
assertErrorFormat '''
|
||||
|
|
|
@ -453,8 +453,7 @@ test 'inline implicit object literals within multiline implicit object literals'
|
|||
eq 0, x.b
|
||||
eq 0, x.a.aa
|
||||
|
||||
test "object keys with interpolations", ->
|
||||
# Simple cases.
|
||||
test "object keys with interpolations: simple cases", ->
|
||||
a = 'a'
|
||||
obj = "#{a}": yes
|
||||
eq obj.a, yes
|
||||
|
@ -465,7 +464,7 @@ test "object keys with interpolations", ->
|
|||
obj = {"#{5}"}
|
||||
eq obj[5], '5' # Note that the value is a string, just like the key.
|
||||
|
||||
# Commas in implicit object.
|
||||
test "object keys with interpolations: commas in implicit object", ->
|
||||
obj = "#{'a'}": 1, b: 2
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
obj = a: 1, "#{'b'}": 2
|
||||
|
@ -473,7 +472,7 @@ test "object keys with interpolations", ->
|
|||
obj = "#{'a'}": 1, "#{'b'}": 2
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
|
||||
# Commas in explicit object.
|
||||
test "object keys with interpolations: commas in explicit object", ->
|
||||
obj = {"#{'a'}": 1, b: 2}
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
obj = {a: 1, "#{'b'}": 2}
|
||||
|
@ -481,7 +480,7 @@ test "object keys with interpolations", ->
|
|||
obj = {"#{'a'}": 1, "#{'b'}": 2}
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
|
||||
# Commas after key with interpolation.
|
||||
test "object keys with interpolations: commas after key with interpolation", ->
|
||||
obj = {"#{'a'}": yes,}
|
||||
eq obj.a, yes
|
||||
obj = {
|
||||
|
@ -504,17 +503,17 @@ test "object keys with interpolations", ->
|
|||
"#{'c'}": 3, "#{'d'}": 4,
|
||||
deepEqual obj, {a: 1, b: 2, c: 3, d: 4}
|
||||
|
||||
# Key with interpolation mixed with `@prop`.
|
||||
test "object keys with interpolations: key with interpolation mixed with `@prop`", ->
|
||||
deepEqual (-> {@a, "#{'b'}": 2}).call(a: 1), {a: 1, b: 2}
|
||||
|
||||
# Evaluate only once.
|
||||
test "object keys with interpolations: evaluate only once", ->
|
||||
count = 0
|
||||
b = -> count++; 'b'
|
||||
obj = {"#{b()}"}
|
||||
eq obj.b, 'b'
|
||||
a = -> count++; 'a'
|
||||
obj = {"#{a()}"}
|
||||
eq obj.a, 'a'
|
||||
eq count, 1
|
||||
|
||||
# Evaluation order.
|
||||
test "object keys with interpolations: evaluation order", ->
|
||||
arr = []
|
||||
obj =
|
||||
a: arr.push 1
|
||||
|
@ -527,13 +526,13 @@ test "object keys with interpolations", ->
|
|||
arrayEq arr, [1..7]
|
||||
deepEqual obj, {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7}
|
||||
|
||||
# Object starting with dynamic key.
|
||||
test "object keys with interpolations: object starting with dynamic key", ->
|
||||
obj =
|
||||
"#{'a'}": 1
|
||||
b: 2
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
|
||||
# Comments in implicit object.
|
||||
test "object keys with interpolations: comments in implicit object", ->
|
||||
obj =
|
||||
### leading comment ###
|
||||
"#{'a'}": 1
|
||||
|
@ -564,7 +563,7 @@ test "object keys with interpolations", ->
|
|||
}
|
||||
deepEqual obj, {a: 1, b: 2, c: 3, d: 4, e: 5}
|
||||
|
||||
# A more complicated case.
|
||||
test "object keys with interpolations: more complicated case", ->
|
||||
obj = {
|
||||
"#{'interpolated'}":
|
||||
"""
|
||||
|
@ -582,6 +581,212 @@ test "#4324: Shorthand after interpolated key", ->
|
|||
eq 1, obj[1]
|
||||
eq 2, obj.a
|
||||
|
||||
test "computed property keys: simple cases", ->
|
||||
a = 'a'
|
||||
obj = [a]: yes
|
||||
eq obj.a, yes
|
||||
obj = {[a]: yes}
|
||||
eq obj.a, yes
|
||||
obj = {[a]}
|
||||
eq obj.a, 'a'
|
||||
obj = {[5]}
|
||||
eq obj[5], 5
|
||||
obj = {['5']}
|
||||
eq obj['5'], '5'
|
||||
|
||||
test "computed property keys: commas in implicit object", ->
|
||||
obj = ['a']: 1, b: 2
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
obj = a: 1, ['b']: 2
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
obj = ['a']: 1, ['b']: 2
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
|
||||
test "computed property keys: commas in explicit object", ->
|
||||
obj = {['a']: 1, b: 2}
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
obj = {a: 1, ['b']: 2}
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
obj = {['a']: 1, ['b']: 2}
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
|
||||
test "computed property keys: commas after key with interpolation", ->
|
||||
obj = {['a']: yes,}
|
||||
eq obj.a, yes
|
||||
obj = {
|
||||
['a']: 1,
|
||||
['b']: 2,
|
||||
### herecomment ###
|
||||
['c']: 3,
|
||||
}
|
||||
deepEqual obj, {a: 1, b: 2, c: 3}
|
||||
obj =
|
||||
['a']: 1,
|
||||
['b']: 2,
|
||||
### herecomment ###
|
||||
['c']: 3,
|
||||
deepEqual obj, {a: 1, b: 2, c: 3}
|
||||
obj =
|
||||
['a']: 1,
|
||||
['b']: 2,
|
||||
### herecomment ###
|
||||
['c']: 3, ['d']: 4,
|
||||
deepEqual obj, {a: 1, b: 2, c: 3, d: 4}
|
||||
|
||||
test "computed property keys: key with interpolation mixed with `@prop`", ->
|
||||
deepEqual (-> {@a, ['b']: 2}).call(a: 1), {a: 1, b: 2}
|
||||
|
||||
test "computed property keys: evaluate only once", ->
|
||||
count = 0
|
||||
a = -> count++; 'a'
|
||||
obj = {[a()]}
|
||||
eq obj.a, 'a'
|
||||
eq count, 1
|
||||
|
||||
test "computed property keys: evaluation order", ->
|
||||
arr = []
|
||||
obj =
|
||||
a: arr.push 1
|
||||
b: arr.push 2
|
||||
['c']: arr.push 3
|
||||
['d']: arr.push 4
|
||||
e: arr.push 5
|
||||
['f']: arr.push 6
|
||||
g: arr.push 7
|
||||
arrayEq arr, [1..7]
|
||||
deepEqual obj, {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7}
|
||||
|
||||
test "computed property keys: object starting with dynamic key", ->
|
||||
obj =
|
||||
['a']: 1
|
||||
b: 2
|
||||
deepEqual obj, {a: 1, b: 2}
|
||||
|
||||
test "computed property keys: comments in implicit object", ->
|
||||
obj =
|
||||
### leading comment ###
|
||||
['a']: 1
|
||||
|
||||
### middle ###
|
||||
|
||||
['b']: 2
|
||||
# regular comment
|
||||
'c': 3
|
||||
### foo ###
|
||||
d: 4
|
||||
['e']: 5
|
||||
deepEqual obj, {a: 1, b: 2, c: 3, d: 4, e: 5}
|
||||
|
||||
obj = {
|
||||
### leading comment ###
|
||||
['a']: 1
|
||||
|
||||
### middle ###
|
||||
|
||||
['b']: 2
|
||||
# regular comment
|
||||
'c': 3
|
||||
### foo ###
|
||||
d: 4
|
||||
['e']: 5
|
||||
}
|
||||
deepEqual obj, {a: 1, b: 2, c: 3, d: 4, e: 5}
|
||||
|
||||
test "computed property keys: more complicated case", ->
|
||||
obj = {
|
||||
['interpolated']:
|
||||
['nested']:
|
||||
123: 456
|
||||
}
|
||||
deepEqual obj,
|
||||
interpolated:
|
||||
nested:
|
||||
123: 456
|
||||
|
||||
test "computed property keys: empty array as key", ->
|
||||
o1 = { [[]] }
|
||||
deepEqual o1, { [[]]: [] }
|
||||
arrayEq o1[[]], []
|
||||
o2 = { [[]]: 1 }
|
||||
deepEqual o2, { [[]]: 1 }
|
||||
eq o2[[]], 1
|
||||
o3 = [[]]: 1
|
||||
deepEqual o3, { [[]]: 1 }
|
||||
deepEqual o3, { [[]]: 1 }
|
||||
eq o3[[]], 1
|
||||
o4 = a: 1, [[]]: 2
|
||||
deepEqual o4, { a: 1, [[]]: 2 }
|
||||
eq o4.a, 1,
|
||||
eq o4[[]], 2
|
||||
o5 = { a: 1, [[]]: 2 }
|
||||
deepEqual o5, { a: 1, [[]]: 2 }
|
||||
eq o5.a, 1,
|
||||
eq o5[[]], 2
|
||||
|
||||
test "computed property keys: shorthand after computed property key", ->
|
||||
a = 2
|
||||
obj = {[1]: 1, a}
|
||||
eq 1, obj[1]
|
||||
eq 2, obj.a
|
||||
|
||||
test "computed property keys: shorthand computed property key", ->
|
||||
a = 'b'
|
||||
o = {[a]}
|
||||
p = {a}
|
||||
r = {['a']}
|
||||
eq o.b, 'b'
|
||||
eq p.a, o.b
|
||||
eq r.a, 'a'
|
||||
|
||||
foo = -> "a"
|
||||
obj = { [foo()] }
|
||||
eq obj.a, 'a'
|
||||
|
||||
test "computed property keys: arrays", ->
|
||||
b = 'b'
|
||||
f = (c) -> "#{c}1"
|
||||
obj =
|
||||
['a']: [1, 2, 3]
|
||||
[b]: [4, 5, 6]
|
||||
[f(b)]: [7, 8, 9]
|
||||
arrayEq obj.a, [1, 2, 3]
|
||||
arrayEq obj.b, [4, 5, 6]
|
||||
arrayEq obj.b1, [7, 8, 9]
|
||||
|
||||
test "computed property keys: examples from developer.mozilla.org (Object initializer)", ->
|
||||
i = 0
|
||||
obj =
|
||||
['foo' + ++i]: i
|
||||
['foo' + ++i]: i
|
||||
['foo' + ++i]: i
|
||||
eq obj.foo1, 1
|
||||
eq obj.foo2, 2
|
||||
eq obj.foo3, 3
|
||||
|
||||
param = 'size'
|
||||
config =
|
||||
[param]: 12,
|
||||
['mobile' + param.charAt(0).toUpperCase() + param.slice(1)]: 4
|
||||
deepEqual config, {size: 12, mobileSize: 4}
|
||||
|
||||
test "computed property keys: [Symbol.iterator]", ->
|
||||
obj =
|
||||
[Symbol.iterator]: ->
|
||||
yield "hello"
|
||||
yield "world"
|
||||
arrayEq [obj...], ['hello', 'world']
|
||||
|
||||
test "computed property keys: Class property", ->
|
||||
increment_method = "increment"
|
||||
decrement_method = "decrement"
|
||||
class Obs
|
||||
constructor: (@count) ->
|
||||
[increment_method]: -> @count += 1
|
||||
[decrement_method]: -> @count -= 1
|
||||
ob = new Obs 2
|
||||
eq ob.increment(), 3
|
||||
eq ob.decrement(), 2
|
||||
|
||||
test "#1263: Braceless object return", ->
|
||||
fn = ->
|
||||
return
|
||||
|
|
Loading…
Reference in a new issue