objDestructAnswer = @compileObjectDestruct(o) if @variable.isObject() and hasSplat
- return objDestructAnswer if objDestructAnswer
+ @variable.base.lhs = yes
+ unless @variable.isAssignable()
+ if @variable.isObject() and @variable.base.hasSplat()
+ return @compileObjectDestruct o
+ else
+ return @compileDestructuring o
return @compileSplice o if @variable.isSplice()
return @compileConditional o if @context in ['||=', '&&=', '?=']
- return @compileSpecialMath o if @context in ['**=', '//=', '%%=']
+ return @compileSpecialMath o if @context in ['//=', '%%=']
- unless @context
+ if not @context or @context is '**='
varBase = @variable.unwrapAll()
unless varBase.isAssignable()
@variable.error "'#{@variable.compile o}' can't be assigned"
@@ -4431,11 +4363,11 @@ destructured variables.
-
+
moduleDeclaration
can be 'import'
or 'export'
.
@@ -4456,11 +4388,11 @@ destructured variables.
-
+
If this assignment identifier has one or more herecomments
attached, output them as part of the declarations line (unless
@@ -4502,11 +4434,11 @@ the comment to be between the class name and the {
.
-
+
Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration,
if we’re destructuring without declaring, the destructuring assignment must be wrapped in parentheses.
@@ -4523,249 +4455,36 @@ The assignment is wrapped in parentheses if ‘o.level’ has lower precedence t
-
+
-
Check object destructuring variable for rest elements;
-can be removed once ES proposal hits Stage 4.
+
Object rest property is not assignable: {{a}...}
- compileObjectDestruct: (o) ->
+ compileObjectDestruct: (o) ->
+ @variable.base.reorderProperties()
+ {properties: props} = @variable.base
+ [..., splat] = props
+ splatProp = splat.name
+ assigns = []
+ refVal = new Value new IdentifierLiteral o.scope.freeVariable 'ref'
+ props.splice -1, 1, new Splat refVal
+ assigns.push new Assign(new Value(new Obj props), @value).compileToFragments o, LEVEL_LIST
+ assigns.push new Assign(new Value(splatProp), refVal).compileToFragments o, LEVEL_LIST
+ @joinFragmentArrays assigns, ', '
-
+
-
Returns a safe (cached) reference to the key for a given property
-
-
-
- getPropKey = (prop) ->
- if prop instanceof Assign
- [prop.variable, key] = prop.variable.cache o
- key
- else
- prop
-
-
-
-
-
-
-
-
-
Returns the name of a given property for use with excludeProps
-Property names are quoted (e.g. a: b
-> ‘a’), and everything else uses the key reference
-(e.g. 'a': b -> 'a'
, "#{a}": b
-> `)
-
-
-
- getPropName = (prop) ->
- key = getPropKey prop
- cached = prop instanceof Assign and prop.variable isnt key
- if cached or not key.isAssignable()
- key
- else
- new Literal "'#{key.compileWithoutComments o}'"
-
-
-
-
-
-
-
-
-
Recursive function for searching and storing rest elements in objects.
-e.g. {[properties...]} = source
.
-
-
-
- traverseRest = (properties, source) =>
- restElements = []
- restIndex = undefined
- source = new Value source unless source.properties?
-
- for prop, index in properties
- nestedSourceDefault = nestedSource = nestedProperties = null
- if prop instanceof Assign
-
-
-
-
-
-
-
-
-
prop is k: expr
, we need to check expr
for nested splats
-
-
-
- if prop.value.isObject?()
-
-
-
-
-
-
-
-
-
prop is k = {...}
-
-
-
- continue unless prop.context is 'object'
-
-
-
-
-
-
-
-
-
prop is k: {...}
-
-
-
- nestedProperties = prop.value.base.properties
- else if prop.value instanceof Assign and prop.value.variable.isObject()
-
-
-
-
-
-
-
-
-
prop is k: {...} = default
-
-
-
- nestedProperties = prop.value.variable.base.properties
- [prop.value.value, nestedSourceDefault] = prop.value.value.cache o
- if nestedProperties
- nestedSource = new Value source.base, source.properties.concat [new Access getPropKey prop]
- nestedSource = new Value new Op '?', nestedSource, nestedSourceDefault if nestedSourceDefault
- restElements.push traverseRest(nestedProperties, nestedSource)...
- else if prop instanceof Splat
- prop.error "multiple rest elements are disallowed in object destructuring" if restIndex?
- restIndex = index
- restElements.push {
- name: prop.name.unwrapAll()
- source
- excludeProps: new Arr (getPropName p for p in properties when p isnt prop)
- }
-
- if restIndex?
-
-
-
-
-
-
-
-
-
Remove rest element from the properties after iteration
-
-
-
- properties.splice restIndex, 1
-
- restElements
-
-
-
-
-
-
-
-
-
Cache the value for reuse with rest elements.
-
-
-
- valueRefTemp =
- if @value.shouldCache()
- new IdentifierLiteral o.scope.freeVariable 'ref', reserve: false
- else
- @value.base
-
-
-
-
-
-
-
-
-
Find all rest elements.
-
-
-
- restElements = traverseRest @variable.base.properties, valueRefTemp
- return no unless restElements and restElements.length > 0
-
- [@value, valueRef] = @value.cache o
- result = new Block [@]
-
- for restElement in restElements
- value = new Call new Value(new Literal utility 'objectWithoutKeys', o), [restElement.source, restElement.excludeProps]
- result.push new Assign new Value(restElement.name), value, null, param: if @param then 'alwaysDeclare' else null
-
- fragments = result.compileToFragments o
- if o.level is LEVEL_TOP
-
-
-
-
-
-
-
-
-
Remove leading tab and trailing semicolon
-
-
-
- fragments.shift()
- fragments.pop()
-
- fragments
-
-
-
-
-
-
-
-
Brief implementation of recursive pattern matching, when assigning array or
object literals to a value. Peeks at their properties to assign inner names.
@@ -4781,11 +4500,11 @@ object literals to a value. Peeks at their properties to assign inner names.
-
+
Special-case for {} = a
and [] = a
(empty patterns).
Compile to simply a
.
@@ -4800,11 +4519,11 @@ Compile to simply
a
.
-
+
Disallow [...] = a
for some reason. (Could be equivalent to [] = a
?)
@@ -4816,11 +4535,11 @@ Compile to simply
a
.
-
+
Count all Splats
: [a, b, c…, d, e]
@@ -4831,11 +4550,11 @@ Compile to simply
a
.
-
+
Count all Expansions
: [a, b, …, c, d]
@@ -4846,11 +4565,11 @@ Compile to simply
a
.
-
+
Combine splats and expansions.
@@ -4861,11 +4580,11 @@ Compile to simply
a
.
-
+
Show error if there is more than one Splat
, or Expansion
.
Examples: [a, b, c…, d, e, f…], [a, b, …, c, d, …], [a, b, …, c, d, e…]
@@ -4877,11 +4596,11 @@ Examples: [a, b, c…, d, e, f…], [a, b, …, c, d, …], [a, b, …, c, d, e
-
+
Sort ‘splatsAndExpans’ so we can show error at first disallowed token.
@@ -4891,21 +4610,28 @@ Examples: [a, b, c…, d, e, f…], [a, b, …, c, d, …], [a, b, …, c, d, e
isSplat = splats?.length >
0
isExpans = expans?.length >
0
- isObject = @variable.isObject()
- isArray = @variable.isArray()
vvar = value.compileToFragments o, LEVEL_LIST
vvarText = fragmentsToText vvar
- assigns = []
+ assigns = []
+ pushAssign = (variable, val) =>
+ assigns.push new Assign(variable, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
+
+ if isSplat
+ splatVar = objects[splats[0]].name.unwrap()
+ if splatVar instanceof Arr or splatVar instanceof Obj
+ splatVarRef = new IdentifierLiteral o.scope.freeVariable 'ref'
+ objects[splats[0]].name = splatVarRef
+ splatVarAssign = -> pushAssign new Value(splatVar), splatVarRef
-
+
At this point, there are several things to destructure. So the fn()
in
{a, b} = fn()
must be cached, for example. Make vvar into a simple
@@ -4929,11 +4655,11 @@ variable if it isn’t already.
-
+
Helper which outputs [].slice
code.
@@ -4944,11 +4670,11 @@ variable if it isn’t already.
-
+
Helper which outputs [].splice
code.
@@ -4959,27 +4685,11 @@ variable if it isn’t already.
-
+
-
Check if objects
array contains object spread ({a, r...}
), e.g. [a, b, {c, r...}]
.
-
-
-
- hasObjSpreads = (objs) ->
- (i for obj, i in objs when obj.base instanceof Obj and obj.base.hasSplat())
-
-
-
-
-
-
-
-
Check if objects
array contains any instance of Assign
, e.g. {a:1}.
@@ -4991,11 +4701,11 @@ variable if it isn’t already.
-
+
Check if objects
array contains any unassignable object.
@@ -5008,28 +4718,28 @@ variable if it isn’t already.
-
+
-
objects
are complex when there is object spread ({a…}), object assign ({a:1}),
+
objects
are complex when there is object assign ({a:1}),
unassignable object, or just a single node.
complexObjects = (objs) ->
- hasObjSpreads(objs).length or hasObjAssigns(objs).length or objIsUnassignable(objs) or olen is 1
+ hasObjAssigns(objs).length or objIsUnassignable(objs) or olen is 1
-
+
“Complex” objects
are processed in a loop.
Examples: [a, b, {c, r…}, d], [a, …, {b, r…}, c, d]
@@ -5037,17 +4747,16 @@ Examples: [a, b, {c, r…}, d], [a, …, {b, r…}, c, d]
loopObjects = (objs, vvar, vvarTxt) =>
- objSpreads = hasObjSpreads objs
for obj, i in objs
-
+
Elision
can be skipped.
@@ -5058,11 +4767,11 @@ Examples: [a, b, {c, r…}, d], [a, …, {b, r…}, c, d]
-
+
If obj
is {a: 1}
@@ -5083,11 +4792,11 @@ Examples: [a, b, {c, r…}, d], [a, …, {b, r…}, c, d]
-
+
obj
is [a…], {a…} or a
@@ -5095,23 +4804,22 @@ Examples: [a, b, {c, r…}, d], [a, …, {b, r…}, c, d]
vvar = switch
when obj instanceof Splat then new Value obj.name
- when i in objSpreads then new Value obj.base
else obj
vval = switch
when obj instanceof Splat then compSlice(vvarTxt, i)
else new Value new Literal(vvarTxt), [new Index new NumberLiteral i]
message = isUnassignable vvar.unwrap().value
vvar.error message if message
- assigns.push new Assign(vvar, vval, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
+ pushAssign vvar, vval
-
+
“Simple” objects
can be split and compiled to arrays, [a, b, c] = arr, [a, b, c…] = arr
@@ -5120,7 +4828,7 @@ Examples: [a, b, {c, r…}, d], [a, …, {b, r…}, c, d]
assignObjects = (objs, vvar, vvarTxt) =>
vvar = new Value new Arr(objs, yes)
vval = if vvarTxt instanceof Value then vvarTxt else new Value new Literal(vvarTxt)
- assigns.push new Assign(vvar, vval, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
+ pushAssign vvar, vval
processObjects = (objs, vvar, vvarTxt) ->
if complexObjects objs
@@ -5131,11 +4839,11 @@ Examples: [a, b, {c, r…}, d], [a, …, {b, r…}, c, d]
-
+
In case there is Splat
or Expansion
in objects
,
we can split array in two simple subarrays.
@@ -5161,11 +4869,11 @@ b) Expansion
-
+
Slice or splice objects
.
@@ -5184,17 +4892,18 @@ b)
Expansion
-
+
There is no Splat
or Expansion
in objects
.
processObjects objects, vvar, vvarText
+ splatVarAssign?()
assigns.push vvar unless top or @subpattern
fragments = @joinFragmentArrays assigns, ', '
if o.level < LEVEL_LIST then fragments else @wrapInParentheses fragments
@@ -5202,11 +4911,11 @@ b) Expansion
-
+
When compiling a conditional assignment, take care to ensure that the
operands are only evaluated once, even though we have to reference them
@@ -5220,11 +4929,11 @@ more than once.
-
+
Disallow conditional assignment of undefined variables.
@@ -5243,13 +4952,13 @@ more than once.
-
+
-
Convert special math assignment operators like a **= b
to the equivalent
+
Convert special math assignment operators like a //= b
to the equivalent
extended form a = a ** b
and then compiles that.
@@ -5261,11 +4970,11 @@ extended form a = a ** b
and then compiles that.
-
+
Compile the assignment from an array splice literal, using JavaScript’s
Array#splice
method.
@@ -5302,11 +5011,11 @@ extended form
a = a ** b
and then compiles that.
-
+
FuncGlyph
@@ -5320,11 +5029,11 @@ exports.FuncGlyph =
class
-
+
Code
@@ -5333,11 +5042,11 @@ exports.FuncGlyph =
class
-
+
A function definition. This is the only node that creates a new Scope.
When for the purposes of walking the contents of a function body, the Code
@@ -5361,8 +5070,8 @@ has no children – they’re within the inner scope.
@isGenerator =
yes
if (node
instanceof Op
and node.isAwait())
or node
instanceof AwaitReturn
@isAsync =
yes
-
if @isGenerator
and @isAsync
- node.error
"function can't contain both yield and await"
+
if node
instanceof For
and node.isAwait()
+ @isAsync =
yes
children: [
'params',
'body']
@@ -5375,11 +5084,11 @@ has no
children – they’re within the inner scope.
-
+
Compilation creates a new scope unless explicitly asked to share with the
outer scope. Handles splat parameters in the parameter list by setting
@@ -5414,11 +5123,11 @@ function body.
-
+
Check for duplicate parameters and separate this
assignments.
@@ -5437,11 +5146,11 @@ function body.
-
+
Param
is object destructuring with a default value: ({@prop = 1}) ->
In a case when the variable name is already reserved, we have to assign
@@ -5461,11 +5170,11 @@ a new variable name to the destructured variable: ({prop:prop1 = 1}) ->
-
+
Parse the parameters, adding them to the list of parameters to put in the
function definition; and dealing with splats or expansions, including
@@ -5483,11 +5192,11 @@ any non-idempotent parameters are evaluated in the correct order.
-
+
Was ...
used with this parameter? (Only one such parameter is allowed
per function.) Splat/expansion parameters cannot have default values,
@@ -5502,16 +5211,16 @@ so we need not worry about that.
param.error
'an expansion parameter cannot be the only parameter in a function definition'
haveSplatParam =
yes
if param.splat
-
if param.name
instanceof Arr
+ if param.name instanceof Arr or param.name instanceof Obj
-
+
Splat arrays are treated oddly by ES; deal with them the legacy
way in the function body. TODO: Should this be handled in the
@@ -5536,11 +5245,11 @@ function parameter list, and if so, how?
-
+
Parse all other parameters; if a splat paramater has not yet been
encountered, add these other parameters to the list to be output in
@@ -5556,11 +5265,11 @@ the function definition.
-
+
This parameter cannot be declared or assigned in the parameter
list. So put a reference in the parameter list and add a statement
@@ -5579,11 +5288,11 @@ to the function body assigning it, e.g.
-
+
If this parameter comes before the splat or expansion, it will go
in the function definition parameter list.
@@ -5595,11 +5304,11 @@ in the function definition parameter list.
-
+
If this parameter has a default value, and it hasn’t already been
set by the shouldCache()
block above, define it as a statement in
@@ -5619,11 +5328,11 @@ so we can’t define its default value in the parameter list.
-
+
Add this parameter’s reference(s) to the function scope.
@@ -5634,54 +5343,18 @@ so we can’t define its default value in the parameter list.
-
+
This parameter is destructured.
-
-
-
-
-
-
-
-
-
-
Compile foo({a, b...}) ->
to foo(arg) -> {a, b...} = arg
.
-Can be removed once ES proposal hits Stage 4.
-
-
-
- if param.name instanceof Obj and param.name.hasSplat()
- splatParamName = o.scope.freeVariable 'arg'
- o.scope.parameter splatParamName
- ref = new Value new IdentifierLiteral splatParamName
- exprs.push new Assign new Value(param.name), ref, null, param: 'alwaysDeclare'
-
-
-
-
-
-
-
-
-
Compile foo({a, b...} = {}) ->
to foo(arg = {}) -> {a, b...} = arg
.
-
-
-
- if param.value? and not param.assignedInBody
- ref = new Assign ref, param.value, null, param: yes
- else unless param.shouldCache()
+ param.name.lhs = yes
+ unless param.shouldCache()
param.name.eachName (prop) ->
o.scope.parameter prop.value
else
@@ -5689,11 +5362,11 @@ Can be removed once ES proposal hits Stage 4.
-
+
This compilation of the parameter is only to get its name to add
to the scope name tracking; since the compilation output here
@@ -5712,11 +5385,11 @@ is compiled.
-
+
If this parameter had a default value, since it’s no longer in the
function parameter list we need to assign its default value
@@ -5732,11 +5405,11 @@ function parameter list we need to assign its default value
-
+
Add this parameter to the scope, since it wouldn’t have been added
yet since it was skipped earlier.
@@ -5748,11 +5421,11 @@ yet since it was skipped earlier.
-
+
If there were parameters after the splat or expansion parameter, those
parameters need to be assigned in the body of the function.
@@ -5764,11 +5437,11 @@ parameters need to be assigned in the body of the function.
-
+
Create a destructured assignment, e.g. [a, b, c] = [args..., b, c]
@@ -5781,11 +5454,11 @@ parameters need to be assigned in the body of the function.
-
+
Add new expressions to the function body
@@ -5802,11 +5475,11 @@ parameters need to be assigned in the body of the function.
-
+
JavaScript doesn’t allow bound (=>
) functions to also be generators.
This is usually caught via Op::compileContinuation
, but double-check:
@@ -5820,11 +5493,11 @@ This is usually caught via
Op::compileContinuation
, but double-chec
-
+
Assemble the output
@@ -5843,11 +5516,11 @@ This is usually caught via
Op::compileContinuation
, but double-chec
-
+
Block comments between a function name and (
get output between
function
and (
.
@@ -5863,11 +5536,11 @@ This is usually caught via
Op::compileContinuation
, but double-chec
-
+
Compile this parameter, but if any generated variables get created
(e.g. ref
), shift those into the parent scope since we can’t put a
@@ -5885,11 +5558,11 @@ This is usually caught via Op::compileContinuation
, but double-chec
-
+
Block comments between )
and ->
/=>
get output between )
and {
.
@@ -5904,11 +5577,11 @@ This is usually caught via
Op::compileContinuation
, but double-chec
-
+
We need to compile the body before method names to ensure super
references are handled.
@@ -5939,11 +5612,11 @@ references are handled.
-
+
Short-circuit traverseChildren
method to prevent it from crossing scope
boundaries unless crossScope
is true
.
@@ -5956,11 +5629,11 @@ boundaries unless
crossScope
is
true
.
-
+
Short-circuit replaceInContext
method to prevent it from crossing context boundaries. Bound
functions have the same context.
@@ -5993,11 +5666,11 @@ functions have the same context.
-
+
Find all super calls in the given context node;
returns true
if iterator
is called.
@@ -6013,11 +5686,11 @@ returns
true
if
iterator
is called.
-
+
super
in a constructor (the only super
without an accessor)
cannot be given an argument with a reference to this
, as that would
@@ -6038,11 +5711,11 @@ be referencing this
before calling super
.
-
+
super
has the same target in bound (arrow) functions, so check them too
@@ -6055,11 +5728,11 @@ be referencing
this
before calling
super
.
-
+
Param
@@ -6068,11 +5741,11 @@ be referencing
this
before calling
super
.
-
+
A parameter in a function definition. Beyond a typical JavaScript parameter,
these parameters can also attach themselves to the context of the function,
@@ -6117,11 +5790,11 @@ as well as be a splat, gathering up a group of parameters into an array.
-
+
Iterates the name or names of a Param
.
In a sense, a destructured parameter represents multiple JS parameters. This
@@ -6138,11 +5811,11 @@ to that name.
-
+
- simple literals
foo
@@ -6155,11 +5828,11 @@ to that name.
- -
+
-
- at-params
@foo
@@ -6173,11 +5846,11 @@ to that name.
- -
+
-
Save original obj.
@@ -6188,11 +5861,11 @@ to that name.
-
-
+
-
- destructured parameter with default value
@@ -6206,11 +5879,11 @@ to that name.
- -
+
-
- assignments within destructured parameters
{foo:bar}
@@ -6223,11 +5896,11 @@ to that name.
- -
+
-
… possibly with a default value
@@ -6242,11 +5915,11 @@ to that name.
-
-
+
-
- splats within destructured parameters
[xs...]
@@ -6262,11 +5935,11 @@ to that name.
- -
+
-
- destructured parameters within destructured parameters
[{a}]
@@ -6280,11 +5953,11 @@ to that name.
- -
+
-
- at-params within destructured parameters
{@foo}
@@ -6298,11 +5971,11 @@ to that name.
- -
+
-
- simple destructured parameters {foo}
@@ -6320,11 +5993,11 @@ to that name.
- -
+
-
Rename a param by replacing the given AST node for a name with a new node.
This needs to ensure that the the source for object destructuring does not change.
@@ -6341,11 +6014,11 @@ This needs to ensure that the the source for object destructuring does not chang
-
-
+
-
No need to assign a new variable for the destructured variable if the variable isn’t reserved.
Examples:
@@ -6366,11 +6039,11 @@ Examples:
-
-
+
-
Splat
@@ -6379,11 +6052,11 @@ Examples:
-
-
+
-
A splat, either as a parameter to a function, an argument to a call,
or as part of a destructuring assignment.
@@ -6397,7 +6070,10 @@ or as part of a destructuring assignment.
children: [
'name']
+ shouldCache:
-> no
+
isAssignable:
->
+
return no if @name
instanceof Obj
or @name
instanceof Parens
@name.isAssignable()
and (
not @name.isAtomic
or @name.isAtomic())
assigns:
(name) ->
@@ -6411,11 +6087,11 @@ or as part of a destructuring assignment.
-
-
+
-
Expansion
@@ -6424,11 +6100,11 @@ or as part of a destructuring assignment.
-
-
+
-
Used to skip values inside an array destructuring (pattern matching) or
parameter list.
@@ -6450,11 +6126,11 @@ parameter list.
-
-
+
-
Elision
@@ -6463,11 +6139,11 @@ parameter list.
-
-
+
-
Array elision element (for example, [,a, , , b, , c, ,]).
@@ -6495,11 +6171,11 @@ parameter list.
-
-
+
-
While
@@ -6508,11 +6184,11 @@ parameter list.
-
-
+
-
A while loop, the only sort of low-level loop exposed by CoffeeScript. From
it, all other loops can be manufactured. Useful in cases where you need more
@@ -6551,11 +6227,11 @@ flexibility or more speed than a comprehension can provide.
-
-
+
-
The main difference from a JavaScript while is that the CoffeeScript
while can be used as a part of a larger expression – while loops may
@@ -6588,11 +6264,11 @@ return an array containing the computed result of each iteration.
-
-
+
-
Op
@@ -6601,11 +6277,11 @@ return an array containing the computed result of each iteration.
-
-
+
-
Simple Arithmetic and logical operations. Performs some conversion from
CoffeeScript operations into their JavaScript equivalents.
@@ -6633,11 +6309,11 @@ CoffeeScript operations into their JavaScript equivalents.
-
-
+
-
The map of conversions from CoffeeScript to JavaScript symbols.
@@ -6652,11 +6328,11 @@ CoffeeScript operations into their JavaScript equivalents.
-
-
+
-
The map of invertible operators.
@@ -6687,11 +6363,11 @@ CoffeeScript operations into their JavaScript equivalents.
-
-
+
-
Am I capable of
Python-style comparison chaining?
@@ -6753,11 +6429,11 @@ CoffeeScript operations into their JavaScript equivalents.
-
-
+
-
In chains, there’s no need to wrap bare obj literals in parens,
as the chained expression is wrapped.
@@ -6775,7 +6451,6 @@ as the chained expression is wrapped.
return @compileChain o
if isChain
switch @operator
when '?' then @compileExistence o, @second.isDefaultValue
-
when '**' then @compilePower o
when '//' then @compileFloorDivision o
when '%%' then @compileModulo o
else
@@ -6787,11 +6462,11 @@ as the chained expression is wrapped.
-
-
+
-