exports.run = ->
+ optionParser = buildCSOptionParser()
parseOptions()diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f50fb5dfc8..22936f4d3f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,4 +6,4 @@ * Use the same coding style as the rest of the [codebase](https://github.com/jashkenas/coffeescript/tree/master/src). If you’re just getting started with CoffeeScript, there’s a nice [style guide](https://github.com/polarmobile/coffeescript-style-guide). -* In your pull request, do not add documentation to `index.html` or re-build the minified `coffeescript.js` file. We’ll do those things before cutting a new release. +* In your pull request, do not add documentation to `index.html` or re-build the minified `coffeescript.js` file. We’ll do those things before cutting a new release. You _should,_ however, commit the updated compiled JavaScript files in `lib`. \ No newline at end of file diff --git a/docs/v2/annotated-source/command.html b/docs/v2/annotated-source/command.html index d61c417c11..ac073b48b1 100644 --- a/docs/v2/annotated-source/command.html +++ b/docs/v2/annotated-source/command.html @@ -238,7 +238,10 @@
exports.run = ->
+ optionParser = buildCSOptionParser()
parseOptions()parseOptions = ->
- optionParser = new optparse.OptionParser SWITCHES, BANNER
o = opts = optionParser.parse process.argv[2..]
o.compile or= !!o.output
o.run = not (o.compile or o.print or o.map)
@@ -856,7 +859,7 @@ command.coffee
usage = ->
- printLine (new optparse.OptionParser SWITCHES, BANNER).help() AssignObj: [
o 'ObjAssignable', -> new Value $1
+ o 'ObjRestValue'
o 'ObjAssignable : Expression', -> new Assign LOC(1)(new Value $1), $3, 'object',
operatorToken: LOC(2)(new Literal $2)
o 'ObjAssignable :
@@ -574,6 +576,40 @@ Grammatical Rules
+ Object literal spread properties.
+
+ ObjRestValue: [
+ o 'SimpleObjAssignable ...', -> new Splat new Value $1
+ o 'ObjSpreadExpr ...', -> new Splat $1
+ ]
+
+ ObjSpreadExpr: [
+ o 'ObjSpreadIdentifier'
+ o 'Object'
+ o 'Parenthetical'
+ o 'Super'
+ o 'This'
+ o 'SUPER Arguments', -> new SuperCall LOC(1)(new Super), $2
+ o 'SimpleObjAssignable Arguments', -> new Call (new Value $1), $2
+ o 'ObjSpreadExpr Arguments', -> new Call $1, $2
+ ]
+
+ ObjSpreadIdentifier: [
+ o 'SimpleObjAssignable . Property', -> (new Value $1).add(new Access $3)
+ o 'SimpleObjAssignable INDEX_START IndexValue INDEX_END', -> (new Value $1).add($3)
+ ]A return statement from a function body.
A block comment.
@@ -613,11 +649,11 @@The Code node is the function literal. It’s defined by an indented block of Block preceded by a function arrow, with an optional parameter list.
@@ -632,11 +668,11 @@CoffeeScript has two different symbols for functions. -> is for ordinary
functions, and => is for functions bound to the current value of this.
An optional, trailing comma.
@@ -669,11 +705,11 @@The list of parameters that a function accepts can be of any length.
@@ -690,11 +726,11 @@A single parameter in a function definition can be ordinary, or a splat that hoovers up the remaining arguments.
@@ -711,11 +747,11 @@Function Parameters
@@ -731,11 +767,11 @@A splat that occurs outside of a parameter list.
@@ -748,11 +784,11 @@Variables and properties that can be assigned to.
@@ -768,11 +804,11 @@Everything that can be assigned to.
@@ -787,11 +823,11 @@The types of things that can be treated as values – assigned to, invoked as functions, indexed into, named as a class, etc.
@@ -810,11 +846,11 @@A super-based expression that can be used as a value.
The general group of accessors into an object, by property, by prototype or by array index or slice.
@@ -851,11 +887,11 @@Indexing into an object or array using bracket notation.
@@ -874,11 +910,11 @@In CoffeeScript, an object literal is simply a list of assignments.
@@ -891,11 +927,11 @@Assignment of properties within an object literal can be separated by comma, as in JavaScript, or simply by newline.
@@ -913,11 +949,11 @@Class definitions have optional bodies of prototype property assignments, and optional references to the superclass.
@@ -1002,11 +1038,11 @@Ordinary function invocation, or a chained series of calls.
@@ -1022,11 +1058,11 @@An optional existence check on a function.
@@ -1040,11 +1076,11 @@The list of arguments to a function call.
@@ -1058,11 +1094,11 @@A reference to the this current object.
@@ -1076,11 +1112,11 @@A reference to a property on this.
@@ -1093,11 +1129,11 @@The array literal.
@@ -1111,11 +1147,11 @@Inclusive and exclusive range dots.
@@ -1129,11 +1165,11 @@The CoffeeScript range literal.
@@ -1146,11 +1182,11 @@Array slice literals.
@@ -1166,11 +1202,11 @@The ArgList is both the list of objects passed into a function call, as well as the contents of an array literal @@ -1189,11 +1225,11 @@
Valid arguments are Blocks or Splats.
@@ -1208,11 +1244,11 @@Just simple, comma-separated, required arguments (no fancy syntax). We need this to be separate from the ArgList for use in Switch blocks, where @@ -1228,11 +1264,11 @@
The variants of try/catch/finally exception handling blocks.
@@ -1248,11 +1284,11 @@A catch clause names its error and runs a block of code.
@@ -1267,11 +1303,11 @@Throw an exception object.
@@ -1284,11 +1320,11 @@Parenthetical expressions. Note that the Parenthetical is a Value, not an Expression, so if you need to use an expression in a place @@ -1305,11 +1341,11 @@
The condition portion of a while loop.
@@ -1325,11 +1361,11 @@The while loop can either be normal, with a block of expressions to execute, or postfix, with a single expression. There is no do..while.
@@ -1351,11 +1387,11 @@Array, object, and range comprehensions, at the most generic level. Comprehensions can either be normal, with a block of expressions to execute, @@ -1383,11 +1419,11 @@
An array of all accepted values for a variable inside the loop. This enables support for pattern matching.
@@ -1404,11 +1440,11 @@An array or range comprehension has variables for the current element and (optional) reference to the current index. Or, key, value, in the case @@ -1424,11 +1460,11 @@
The source of a comprehension is an array or object with an optional guard clause. If it’s an array comprehension, you can also choose to step through @@ -1463,11 +1499,11 @@
An individual When clause, with action.
@@ -1481,11 +1517,11 @@The most basic form of if is a condition and an action. The following if-related rules are broken up along these lines in order to avoid @@ -1501,11 +1537,11 @@
The full complement of if expressions, including postfix one-liner if and unless.
@@ -1522,11 +1558,11 @@Arithmetic and logical operators, working on one or more operands. Here they are grouped by order of precedence. The actual precedence rules @@ -1553,11 +1589,11 @@
Operators at the top of this list have higher precedence than the ones lower
down. Following these rules is what makes 2 + 3 * 4 parse as:
Finally, now that we have our grammar and our operators, we can create our Jison.Parser. We do this by processing all of our rules, recording all @@ -1714,11 +1750,11 @@
Initialize the Parser with our list of terminal tokens, our grammar rules, and the name of the root. Reverse the operators because Jison orders diff --git a/docs/v2/annotated-source/lexer.html b/docs/v2/annotated-source/lexer.html index 4e253f72fd..12aab1e4c4 100644 --- a/docs/v2/annotated-source/lexer.html +++ b/docs/v2/annotated-source/lexer.html @@ -222,6 +222,7 @@
identifierToken: ->
- return 0 unless match = IDENTIFIER.exec @chunk
+ inCSXTag = @atCSXTag()
+ regex = if inCSXTag then CSX_ATTRIBUTE else IDENTIFIER
+ return 0 unless match = regex.exec @chunk
[input, id, colon] = matchCSX is like JSX but for CoffeeScript.
+ + csxToken: ->
+ firstChar = @chunk[0]
+ if firstChar is '<'
+ match = CSX_IDENTIFIER.exec @chunk[1...]
+ return 0 unless match and (
+ @csxDepth > 0 orNot the right hand side of an unspaced comparison (i.e. a<b).
not (prev = @prev()) or
+ prev.spaced or
+ prev[0] not in COMPARABLE_LEFT_SIDE
+ )
+ [input, id, colon] = match
+ origin = @token 'CSX_TAG', id, 1, id.length
+ @token 'CALL_START', '('
+ @token '{', '{'
+ @ends.push tag: '/>', origin: origin, name: id
+ @csxDepth++
+ return id.length + 1
+ else if csxTag = @atCSXTag()
+ if @chunk[...2] is '/>'
+ @pair '/>'
+ @token '}', '}', 0, 2
+ @token 'CALL_END', ')', 0, 2
+ @csxDepth--
+ return 2
+ else if firstChar is '{'
+ token = @token '(', '('
+ @ends.push {tag: '}', origin: token}
+ return 1
+ else if firstChar is '>'Ignore terminators inside a tag.
+ + @pair '/>' # As if the current tag was self-closing.
+ origin = @token '}', '}'
+ @token ',', ','
+ {tokens, index: end} =
+ @matchWithInterpolations INSIDE_CSX, '>', '</', CSX_INTERPOLATION
+ @mergeInterpolationTokens tokens, {delimiter: '"'}, (value, i) =>
+ @formatString value, delimiter: '>'
+ match = CSX_IDENTIFIER.exec @chunk[end...]
+ if not match or match[0] isnt csxTag.name
+ @error "expected corresponding CSX closing tag for #{csxTag.name}",
+ csxTag.origin[2]
+ afterTag = end + csxTag.name.length
+ if @chunk[afterTag] isnt '>'
+ @error "missing closing > after tag name", offset: afterTag, length: 1+1 for the closing >.
@token 'CALL_END', ')', end, csxTag.name.length + 1
+ @csxDepth--
+ return afterTag + 1
+ else
+ return 0
+ else if @atCSXTag 1
+ if firstChar is '}'
+ @pair firstChar
+ @token ')', ')'
+ @token ',', ','
+ return 1
+ else
+ return 0
+ else
+ return 0
+
+ atCSXTag: (depth = 0) ->
+ return no if @csxDepth is 0
+ i = @ends.length - 1
+ i-- while @ends[i]?.tag is 'OUTDENT' or depth-- > 0 # Ignore indents.
+ last = @ends[i]
+ last?.tag is '/>' and lastWe treat all other single characters as a token. E.g.: ( ) , . !
Multi-character operators are also literal tokens, so that Jison can assign
the proper order of operations. There are some symbols that we tag specially
@@ -998,17 +1129,17 @@
A source of ambiguity in our grammar used to be parameter lists in function definitions versus argument lists in function calls. Walk backwards, tagging @@ -1046,7 +1177,8 @@
Close up all remaining open blocks at the end of the file.
@@ -1078,11 +1212,11 @@Match the contents of a delimited token and expand variables and expressions inside it using Ruby-like notation for substitution of arbitrary @@ -1095,13 +1229,19 @@
#{ if interpolations are desired).
delimiter is the delimiter of the token. Examples are ', ", ''',
""" and ///.closingDelimiter is different from delimiter only in CSXinterpolators matches the start of an interpolation, for CSX it’s both
+{ and < (i.e. nested CSX tag)This method allows us to have strings within interpolations within strings, ad infinitum.
matchWithInterpolations: (regex, delimiter) ->
+ matchWithInterpolations: (regex, delimiter, closingDelimiter, interpolators) ->
+ closingDelimiter ?= delimiter
+ interpolators ?= /^#\{/
+
tokens = []
offsetInChunk = delimiter.length
return null unless @chunk[...offsetInChunk] is delimiter
@@ -1114,11 +1254,11 @@ Token Manipulators
-
+
+ break unless match = interpolators.exec str
+ [interpolator] = match [line, column] = @getLineAndColumnFromChunk offsetInChunk + 1
+ interpolationOffset = interpolator.length - 1
+ [line, column] = @getLineAndColumnFromChunk offsetInChunk + interpolationOffset
+ rest = str[interpolationOffset..]
{tokens: nested, index} =
- new Lexer().tokenize str[1..], line: line, column: column, untilBalanced: on
+ new Lexer().tokenize rest, line: line, column: column, untilBalanced: on index += 1 index += interpolationOffset
+
+ braceInterpolator = str[index - 1] is '}'
+ if braceInterpolatorTurn the leading and trailing { and } into parentheses. Unnecessary
parentheses will be removed later.
[open, ..., close] = nested
- open[0] = open[1] = '('
- close[0] = close[1] = ')'
- close.origin = ['', 'end of interpolation', close[2]] [open, ..., close] = nested
+ open[0] = open[1] = '('
+ close[0] = close[1] = ')'
+ close.origin = ['', 'end of interpolation', close[2]] nested.splice 1, 1 if nested[1]?[0] is 'TERMINATOR' nested.splice 1, 1 if nested[1]?[0] is 'TERMINATOR'
+
+ unless braceInterpolator open = @makeToken '(', '(', offsetInChunk, 0
+ close = @makeToken ')', ')', offsetInChunk + index, 0
+ nested = [open, nested..., close]Push a fake 'TOKENS' token, which will get turned into real tokens later.
Merge the array tokens of the fake token types 'TOKENS' and 'NEOSTRING'
(as returned by matchWithInterpolations) into the token stream. The value
@@ -1258,11 +1423,11 @@
Optimize out empty interpolations (an empty pair of parentheses).
@@ -1273,11 +1438,11 @@Push all the tokens in the fake 'TOKENS' token. These already have
sane location data.
Convert 'NEOSTRING' into 'STRING'.
Optimize out empty strings. We ensure that the tokens stream always starts with a string token, though, to make sure that the result @@ -1327,11 +1492,11 @@
However, there is one case where we can optimize away a starting empty string.
@@ -1349,11 +1514,11 @@Create a 0-length “+” token.
@@ -1385,11 +1550,11 @@Pairs up a closing token, ensuring that all listed pairs of tokens are correctly balanced throughout the course of the token stream.
@@ -1404,11 +1569,11 @@Auto-close INDENT to support syntax like this:
el.click((event) ->
@@ -1424,11 +1589,11 @@ Token Manipulators
-
+
Helpers
@@ -1437,11 +1602,11 @@ Helpers
-
+
@@ -1449,11 +1614,11 @@ Helpers
-
+
Returns the line and column number from an offset into the current chunk.
offset is a number of characters into @chunk.
@@ -1483,11 +1648,11 @@ Helpers
-
+
Same as token, except this just returns the token without adding it
to the results.
@@ -1502,11 +1667,11 @@ Helpers
-
+
Use length - 1 for the final offset - we’re supplying the last_line and the last_column,
so if last_column == first_column, then we’re looking at a character of length 1.
@@ -1524,11 +1689,11 @@ Helpers
-
+
Add a token to the results.
offset is the offset into the current @chunk where the token starts.
@@ -1547,11 +1712,11 @@
Helpers
-
+
Peek at the last tag in the token stream.
@@ -1564,11 +1729,11 @@ Helpers
-
+
Peek at the last value in the token stream.
@@ -1581,11 +1746,11 @@ Helpers
-
+
Get the previous token in the token stream.
@@ -1597,11 +1762,11 @@ Helpers
-
+
Are we in the midst of an unfinished expression?
@@ -1631,11 +1796,11 @@ Helpers
-
+
surrogate pair
@@ -1648,11 +1813,11 @@ Helpers
-
+
Replace \u{...} with \uxxxx[\uxxxx] in regexes without u flag
@@ -1675,11 +1840,11 @@ Helpers
-
+
Validates escapes in strings and regexes.
@@ -1707,11 +1872,11 @@ Helpers
-
+
Constructs a string or regex by escaping certain characters.
@@ -1731,11 +1896,11 @@ Helpers
-
+
Ignore escaped backslashes.
@@ -1754,11 +1919,11 @@ Helpers
-
+
Throws an error at either a given offset from the current chunk or at the
location of a token (token[2]).
@@ -1777,11 +1942,11 @@ Helpers
-
+
Helper functions
@@ -1790,11 +1955,11 @@ Helper functions
-
+
@@ -1815,11 +1980,11 @@ Helper functions
-
+
from isn’t a CoffeeScript keyword, but it behaves like one in import and
export statements (handled above) and in the declaration line of a for
@@ -1834,11 +1999,11 @@
Helper functions
-
+
for i from from, for from from iterable
@@ -1851,11 +2016,11 @@ Helper functions
-
+
for i from iterable
@@ -1866,11 +2031,11 @@ Helper functions
-
+
for from…
@@ -1882,11 +2047,11 @@ Helper functions
-
+
for {from}…, for [from]…, for {a, from}…, for {a: from}…
@@ -1900,11 +2065,11 @@ Helper functions
-
+
Constants
@@ -1913,11 +2078,11 @@ Constants
-
+
@@ -1925,11 +2090,11 @@ Constants
-
+
Keywords that CoffeeScript shares in common with JavaScript.
@@ -1947,11 +2112,11 @@ Constants
-
+
CoffeeScript-only keywords.
@@ -1979,11 +2144,11 @@ Constants
-
+
The list of keywords that are reserved by JavaScript, but not used, or are
used by CoffeeScript internally. We throw an error when these are encountered,
@@ -2002,11 +2167,11 @@
Constants
-
+
The superset of both JavaScript keywords and reserved words, none of which may
be used as identifiers or properties.
@@ -2018,11 +2183,11 @@ Constants
-
+
The character code of the nasty Microsoft madness otherwise known as the BOM.
@@ -2033,11 +2198,11 @@ Constants
-
+
Token matching regexes.
@@ -2049,6 +2214,17 @@ Constants
( [^\n\S]* : (?!:) )? # Is this a property name?
///
+CSX_IDENTIFIER = /// ^
+ (?![\d<]) # Must not start with `<`.
+ ( (?: (?!\s)[\.\-$\w\x7f-\uffff] )+ ) # Like `IDENTIFIER`, but includes `-`s and `.`s.
+///
+
+CSX_ATTRIBUTE = /// ^
+ (?!\d)
+ ( (?: (?!\s)[\-$\w\x7f-\uffff] )+ ) # Like `IDENTIFIER`, but includes `-`s.
+ ( [^\S]* = (?!=) )? # Is this an attribute with a value?
+///
+
NUMBER = ///
^ 0b[01]+ | # binary
^ 0o[0-7]+ | # octal
@@ -2080,11 +2256,11 @@ Constants
-
+
String-matching-regexes.
@@ -2097,6 +2273,17 @@ Constants
HEREDOC_SINGLE = /// ^(?: [^\\'] | \\[\s\S] | '(?!'') )* ///
HEREDOC_DOUBLE = /// ^(?: [^\\"#] | \\[\s\S] | "(?!"") | \#(?!\{) )* ///
+INSIDE_CSX = /// ^(?:
+ [^
+ \{ # Start of CoffeeScript interpolation.
+ < # Maybe CSX tag (`<` not allowed even if bare).
+ ]
+ )* /// # Similar to `HEREDOC_DOUBLE` but there is no escaping.
+CSX_INTERPOLATION = /// ^(?:
+ \{ # CoffeeScript interpolation.
+ | <(?!/) # CSX opening tag.
+ )///
+
STRING_OMIT = ///
((?:\\\\)+) # Consume (and preserve) an even number of backslashes.
| \\[^\S\n]*\n\s* # Remove escaped newlines.
@@ -2107,11 +2294,11 @@ Constants
-
+
Regex-matching-regexes.
@@ -2145,11 +2332,11 @@ Constants
-
+
Other regexes.
@@ -2192,11 +2379,11 @@ Constants
-
+
Compound assignment tokens.
@@ -2210,11 +2397,11 @@ Constants
-
+
Unary tokens.
@@ -2227,11 +2414,11 @@ Constants
-
+
Bit-shifting tokens.
@@ -2242,11 +2429,11 @@ Constants
-
+
Comparison tokens.
@@ -2257,11 +2444,11 @@ Constants
-
+
Mathematical tokens.
@@ -2272,11 +2459,11 @@ Constants
-
+
Relational tokens that are negatable with not prefix.
@@ -2287,11 +2474,11 @@ Constants
-
+
Boolean tokens.
@@ -2302,11 +2489,11 @@ Constants
-
+
Tokens which could legitimately be invoked or indexed. An opening
parentheses or bracket following these tokens will be recorded as the start
@@ -2323,11 +2510,26 @@
Constants
-
+
+
+ COMPARABLE_LEFT_SIDE = ['IDENTIFIER', ')', ']', 'NUMBER']
+
+
+
+
+
+
+
+
+ ¶
Tokens which a regular expression will never immediately follow (except spaced
CALLABLEs in some cases), but which a division operator can.
@@ -2340,11 +2542,11 @@ Constants
-
+
Tokens that, when immediately preceding a WHEN, indicate that the WHEN
occurs at the start of a line. We disambiguate these from trailing whens to
@@ -2357,11 +2559,11 @@
Constants
-
+
Additional indent in front of these is ignored.
diff --git a/docs/v2/annotated-source/nodes.html b/docs/v2/annotated-source/nodes.html
index 8f18bdfa72..83e2fe2b13 100644
--- a/docs/v2/annotated-source/nodes.html
+++ b/docs/v2/annotated-source/nodes.html
@@ -707,7 +707,10 @@ Base
new CodeFragment this, code
wrapInParentheses: (fragments) ->
- [].concat @makeCode('('), fragments, @makeCode(')')
+ [@makeCode('('), fragments..., @makeCode(')')]
+
+ wrapInBraces: (fragments) ->
+ [@makeCode('{'), fragments..., @makeCode('}')]
@@ -1274,6 +1277,16 @@ Literal
if o.level >= LEVEL_OP then @wrapInParentheses code else code
exports.StringLiteral = class StringLiteral extends Literal
+ compileNode: (o) ->
+ res = if @csx then [@makeCode @unquote yes] else super()
+
+ unquote: (literal) ->
+ unquoted = @value[1...-1]
+ if literal
+ unquoted.replace /\\n/g, '\n'
+ .replace /\\"/g, '"'
+ else
+ unquoted
exports.RegexLiteral = class RegexLiteral extends Literal
@@ -1285,6 +1298,8 @@ Literal
eachName: (iterator) ->
iterator @
+exports.CSXTag = class CSXTag extends IdentifierLiteral
+
exports.PropertyName = class PropertyName extends Literal
isAssignable: YES
@@ -1706,6 +1721,8 @@ Call
if @variable instanceof Value and @variable.isNotCallable()
@variable.error "literal is not a function"
+ @csx = @variable.base instanceof CSXTag
+
children: ['variable', 'args']
@@ -1816,6 +1833,7 @@ Call
compileNode: (o) ->
+ return @compileCSX o if @csx
@variable?.front = @front
compiledArgs = []
for arg, argIndex in @args
@@ -1828,6 +1846,21 @@ Call
fragments.push @makeCode 'new '
fragments.push @variable.compileToFragments(o, LEVEL_ACCESS)...
fragments.push @makeCode('('), compiledArgs..., @makeCode(')')
+ fragments
+
+ compileCSX: (o) ->
+ [attributes, content] = @args
+ attributes.base.csx = yes
+ content?.base.csx = yes
+ fragments = [@makeCode('<')]
+ fragments.push (tag = @variable.compileToFragments(o, LEVEL_ACCESS))...
+ fragments.push attributes.compileToFragments(o, LEVEL_PAREN)...
+ if content
+ fragments.push @makeCode('>')
+ fragments.push content.compileNode(o, LEVEL_LIST)...
+ fragments.push [@makeCode('</'), tag..., @makeCode('>')]...
+ else
+ fragments.push @makeCode(' />')
fragments
@@ -2440,29 +2473,87 @@ Obj
yes
shouldCache: ->
- not @isAssignable()
+ not @isAssignable()
+
+
+
+
+
+
+
+
+ ¶
+
+ Check if object contains splat.
+
+
+
+ hasSplat: ->
+ splat = yes for prop in @properties when prop instanceof Splat
+ splat ? no
compileNode: (o) ->
props = @properties
if @generated
for node in props when node instanceof Value
- node.error 'cannot have an implicit value in an implicit object'
+ node.error 'cannot have an implicit value in an implicit object'
+
+
+
+
+
+
+
+
+ ¶
+
+ Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
+
+
+
+ return @compileSpread o if @hasSplat()
+
idt = o.indent += TAB
- lastNoncom = @lastNonComment @properties
+ lastNoncom = @lastNonComment @properties
+
+
+
+
+
+
+
+
+ ¶
+
+ If this object is the left-hand side of an assignment, all its children
+are too.
+
+
+
+ if @lhs
+ for prop in props when prop instanceof Assign
+ {value} = prop
+ unwrappedVal = value.unwrapAll()
+ if unwrappedVal instanceof Arr or unwrappedVal instanceof Obj
+ unwrappedVal.lhs = yes
+ else if unwrappedVal instanceof Assign
+ unwrappedVal.nestedLhs = yes
isCompact = yes
for prop in @properties
- if prop instanceof Comment or (prop instanceof Assign and prop.context is 'object')
+ if prop instanceof Comment or (prop instanceof Assign and prop.context is 'object' and not @csx)
isCompact = no
answer = []
- answer.push @makeCode "{#{if isCompact then '' else '\n'}"
+ answer.push @makeCode if isCompact then '' else '\n'
for prop, i in props
join = if i is props.length - 1
''
+ else if isCompact and @csx
+ ' '
else if isCompact
', '
- else if prop is lastNoncom or prop instanceof Comment
+ else if prop is lastNoncom or prop instanceof Comment or @csx
'\n'
else
',\n'
@@ -2475,12 +2566,10 @@ Obj
prop.variable
else if prop not instanceof Comment
prop
-
if key instanceof Value and key.hasProperties()
key.error 'invalid object key' if prop.context is 'object' or not key.this
key = key.properties[0].name
prop = new Assign key, prop, 'object'
-
if key is prop
if prop.shouldCache()
[key, value] = prop.base.cache o
@@ -2488,11 +2577,13 @@ Obj
prop = new Assign key, value, 'object'
else if not prop.bareLiteral?(IdentifierLiteral)
prop = new Assign prop, prop, 'object'
-
if indent then answer.push @makeCode indent
+ prop.csx = yes if @csx
+ answer.push @makeCode ' ' if @csx and i is 0
answer.push prop.compileToFragments(o, LEVEL_TOP)...
if join then answer.push @makeCode join
- answer.push @makeCode "#{if isCompact then '' else "\n#{@tab}"}}"
+ answer.push @makeCode if isCompact then '' else "\n#{@tab}"
+ answer = @wrapInBraces answer if not @csx
if @front then @wrapInParentheses answer else answer
assigns: (name) ->
@@ -2508,11 +2599,59 @@ Obj
-
+
+ Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
+obj2 = {a: 1, obj..., c: 3, d: 4} → obj2 = Object.assign({}, {a: 1}, obj, {c: 3, d: 4})
+
+
+
+ compileSpread: (o) ->
+ props = @properties
+
+
+
+
+
+
+
+
+ ¶
+
+ Store object spreads.
+
+
+
+ splatSlice = []
+ propSlices = []
+ slices = []
+ addSlice = ->
+ slices.push new Obj propSlices if propSlices.length
+ slices.push splatSlice... if splatSlice.length
+ splatSlice = []
+ propSlices = []
+ for prop in props
+ if prop instanceof Splat
+ splatSlice.push new Value prop.name
+ addSlice()
+ else
+ propSlices.push prop
+ addSlice()
+ slices.unshift new Obj unless slices[0] instanceof Obj
+ (new Call new Literal('Object.assign'), slices).compileToFragments o
+
+
+
+
+
+
+
+
+ ¶
Arr
@@ -2521,11 +2660,11 @@ Arr
-
+
An array literal.
@@ -2559,11 +2698,11 @@ Arr
-
+
If this array is the left-hand side of an assignment, all its children
are too.
@@ -2600,11 +2739,11 @@ Arr
-
+
Class
@@ -2613,11 +2752,11 @@ Class
-
+
The CoffeeScript class definition.
Initialize a Class with its name, an optional superclass, and a body.
@@ -2638,11 +2777,11 @@ Class
-
+
Special handling to allow class expr.A extends A declarations
@@ -2651,38 +2790,46 @@ Class
parentName = @parent.base.value if @parent instanceof Value and not @parent.hasProperties()
@hasNameClash = @name? and @name is parentName
+ node = @
+
if executableBody or @hasNameClash
- @compileNode = @compileClassDeclaration
- result = new ExecutableClassBody(@, executableBody).compileToFragments o
- @compileNode = @constructor::compileNode
- else
- result = @compileClassDeclaration o
+ node = new ExecutableClassBody node, executableBody
+ else if not @name? and o.level is LEVEL_TOP
-
+
- result = @wrapInParentheses result if not @name? and o.level is LEVEL_TOP
+ node = new Parens node
+
+ if @boundMethods.length and @parent
+ @variable ?= new IdentifierLiteral o.scope.freeVariable '_class'
+ [@variable, @variableRef] = @variable.cache o unless @variableRef?
if @variable
- assign = new Assign @variable, new Literal(''), null, { @moduleDeclaration }
- [ assign.compileToFragments(o)..., result... ]
- else
- result
+ node = new Assign @variable, node, null, { @moduleDeclaration }
+
+ @compileNode = @compileClassDeclaration
+ try
+ return node.compileToFragments o
+ finally
+ delete @compileNode
compileClassDeclaration: (o) ->
- @ctor ?= @makeDefaultConstructor() if @externalCtor
+ @ctor ?= @makeDefaultConstructor() if @externalCtor or @boundMethods.length
@ctor?.noReturn = true
+ @proxyBoundMethods() if @boundMethods.length
+
o.indent += TAB
result = []
@@ -2703,11 +2850,11 @@ Class
-
+
Figure out the appropriate name for this class
@@ -2730,6 +2877,7 @@ Class
walkBody: ->
@ctor = null
+ @boundMethods = []
executableBody = null
initializer = []
@@ -2755,11 +2903,11 @@ Class
-
+
Try to keep comments with their subsequent assign
@@ -2782,11 +2930,11 @@ Class
-
+
Try to keep comments with their subsequent assign
@@ -2801,6 +2949,8 @@ Class
@ctor = method
else if method.isStatic and method.bound
method.context = @name
+ else if method.bound
+ @boundMethods.push method
if initializer.length isnt expressions.length
@body.expressions = (expression.hoist() for expression in initializer)
@@ -2809,11 +2959,11 @@ Class
-
+
Add an expression to the class initializer
NOTE Currently, only comments, methods and static methods are valid in ES class initializers.
@@ -2833,11 +2983,11 @@
Class
-
+
Checks if the given node is a valid ES class initializer method.
@@ -2851,11 +3001,11 @@ Class
-
+
Returns a configured class initializer method
@@ -2873,7 +3023,7 @@ Class
method.name = new (if methodName.shouldCache() then Index else Access) methodName
method.name.updateLocationDataIfMissing methodName.locationData
method.ctor = (if @parent then 'derived' else 'base') if methodName.value is 'constructor'
- method.error 'Methods cannot be bound functions' if method.bound
+ method.error 'Cannot define a constructor as a bound (fat arrow) function' if method.bound and method.ctor
method
@@ -2892,6 +3042,15 @@ Class
ctor
+ proxyBoundMethods: ->
+ @ctor.thisAssignments = for method in @boundMethods
+ method.classVariable = @variableRef if @parent
+
+ name = new Value(new ThisLiteral, [ method.name ])
+ new Assign name, new Call(new Value(name, [new Access new PropertyName 'bind']), [new ThisLiteral])
+
+ null
+
exports.ExecutableClassBody = class ExecutableClassBody extends Base
children: [ 'class', 'body' ]
@@ -2943,11 +3102,11 @@ Class
-
+
-
+
Make class/prototype assignments for invalid ES properties
@@ -3017,11 +3176,11 @@ Class
-
+
Passthrough
@@ -3034,11 +3193,11 @@ Class
-
+
The class scope is not available yet, so return the assignment to update later
@@ -3060,11 +3219,11 @@ Class
-
+
Import and Export
@@ -3138,11 +3297,11 @@ Import and Export
-
+
Prevent exporting an anonymous class; all exported members must be named
@@ -3201,11 +3360,11 @@ Import and Export
-
+
The name of the variable entering the local scope
@@ -3231,11 +3390,11 @@ Import and Export
-
+
Per the spec, symbols can’t be imported multiple times
(e.g. import { foo, foo } from 'lib' is invalid)
@@ -3259,11 +3418,11 @@ Import and Export
-
+
Assign
@@ -3272,11 +3431,11 @@ Assign
-
+
The Assign is used to assign a local variable to value, or to set the
property of an object – including within object literals.
@@ -3309,11 +3468,11 @@ Assign
-
+
Compile an assignment, delegating to compileDestructuring or
compileSplice if appropriate. Keep track of the name of the base object
@@ -3329,11 +3488,11 @@
Assign
-
+
When compiling @variable, remember if it is part of a function parameter.
@@ -3344,11 +3503,11 @@ Assign
-
+
If @variable is an array or an object, we’re destructuring;
if it’s also isAssignable(), the destructuring syntax is supported
@@ -3362,11 +3521,11 @@
Assign
-
+
This is the left-hand side of an assignment; let Arr and Obj
know that, so that those nodes know that they’re assignable as
@@ -3375,7 +3534,23 @@
Assign
@variable.base.lhs = yes
- return @compileDestructuring o unless @variable.isAssignable()
+ return @compileDestructuring o unless @variable.isAssignable()
+
+
+
+
+
+
+
+
+ ¶
+
+ Object destructuring. Can be removed once ES proposal hits Stage 4.
+
+
+
+ return @compileObjectDestruct(o) if @variable.isObject() and @variable.contains (node) ->
+ node instanceof Obj and node.hasSplat()
return @compileSplice o if @variable.isSplice()
return @compileConditional o if @context in ['||=', '&&=', '?=']
@@ -3395,11 +3570,11 @@ Assign
-
+
moduleDeclaration can be 'import' or 'export'
@@ -3418,6 +3593,7 @@ Assign
[properties..., prototype, name] = @variable.properties
@value.name = name if prototype.name?.value is 'prototype'
+ @value.base.csxAttribute = yes if @csx
val = @value.compileToFragments o, LEVEL_LIST
compiledName = @variable.compileToFragments o, LEVEL_LIST
@@ -3425,25 +3601,25 @@ Assign
if @variable.shouldCache()
compiledName.unshift @makeCode '['
compiledName.push @makeCode ']'
- return compiledName.concat @makeCode(": "), val
+ return compiledName.concat @makeCode(if @csx then '=' else ': '), val
answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val
-
+
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.
- if o.level > LEVEL_LIST or (isValue and @variable.base instanceof Obj and not @param)
+ if o.level > LEVEL_LIST or (o.level is LEVEL_TOP and isValue and @variable.base instanceof Obj and not @nestedLhs and not @param)
@wrapInParentheses answer
else
answer
@@ -3451,68 +3627,312 @@ Assign
-
+
- Brief implementation of recursive pattern matching, when assigning array or
-object literals to a value. Peeks at their properties to assign inner names.
+ Check object destructuring variable for rest elements;
+can be removed once ES proposal hits Stage 4.
- compileDestructuring: (o) ->
- top = o.level is LEVEL_TOP
- {value} = this
- {objects} = @variable.base
- olen = objects.length
+ compileObjectDestruct: (o) ->
-
+
- Special-case for {} = a and [] = a (empty patterns).
-Compile to simply a.
+ 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: ({a, b} = obj). Helper function
+setScopeVar() declares variables a and b at the top of the
+current scope.
- if olen is 0
- code = value.compileToFragments o
- return if o.level >= LEVEL_OP then @wrapInParentheses code else code
- [obj] = objects
+ setScopeVar = (prop) ->
+ newVar = false
+ return if prop instanceof Assign and prop.value.base instanceof Obj
+ if prop instanceof Assign
+ if prop.value.base instanceof IdentifierLiteral
+ newVar = prop.value.base.compile o
+ else
+ newVar = prop.variable.base.compile o
+ else
+ newVar = prop.compile o
+ o.scope.add(newVar, 'var', true) if newVar
-
+
- Disallow [...] = a for some reason. (Could be equivalent to [] = a?)
+ Returns a safe (cached) reference to the key for a given property
- if olen is 1 and obj instanceof Expansion
- obj.error 'Destructuring assignment has no target'
-
- isObject = @variable.isObject()
+ 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 != key
+ if cached or not key.isAssignable()
+ key
+ else
+ new Literal "'#{key.compile o}'"
+
+
+
+
+
+
+
+
+ ¶
+
+ Recursive function for searching and storing rest elements in objects.
+e.g. {[properties...]} = source.
+
+
+
+ traverseRest = (properties, source) =>
+ restElements = []
+ restIndex = undefined
+
+ for prop, index in properties
+ setScopeVar prop.unwrap()
+ if prop instanceof Assign
+
+
+
+
+
+
+
+
+ ¶
+
+ prop is k: expr, we need to check expr for nested splats
+
+
+
+ if prop.value.isObject?()
+
+
+
+
+
+
+
+
+ ¶
+
+ 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 = restElements.concat 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
+
+
+
+ [@value, valueRef] = @value.cache o
+
+
+
+
+
+
+
+
+ ¶
+
+ Find all rest elements.
+
+
+
+ restElements = traverseRest @variable.base.properties, valueRef
+
+ 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 restElement.name, value
+
+ 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.
+
+
+
+ compileDestructuring: (o) ->
+ top = o.level is LEVEL_TOP
+ {value} = this
+ {objects} = @variable.base
+ olen = objects.length
+
+
+
+
+
+
+
+
+ ¶
+
+ Special-case for {} = a and [] = a (empty patterns).
+Compile to simply a.
+
+
+
+ if olen is 0
+ code = value.compileToFragments o
+ return if o.level >= LEVEL_OP then @wrapInParentheses code else code
+ [obj] = objects
+
+
+
+
+
+
+
+
+ ¶
+
+ Disallow [...] = a for some reason. (Could be equivalent to [] = a?)
+
+
+
+ if olen is 1 and obj instanceof Expansion
+ obj.error 'Destructuring assignment has no target'
+
+ isObject = @variable.isObject()
+
+
+
+
+
+
+
+
+ ¶
Special case for when there’s only one thing destructured off of
something. {a} = b, [a] = b, {a: b} = c
@@ -3524,11 +3944,11 @@ Assign
-
+
Pick the property straight off the value when there’s just one to pick
(no need to cache the value into a variable).
@@ -3541,11 +3961,11 @@ Assign
-
+
A regular object pattern-match.
@@ -3564,11 +3984,11 @@ Assign
-
+
A shorthand {a, b, @c} = val pattern-match.
@@ -3583,11 +4003,11 @@ Assign
-
+
A regular array pattern-match.
@@ -3612,11 +4032,11 @@ Assign
-
+
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
@@ -3633,11 +4053,11 @@
Assign
-
+
And here comes the big loop that handles all of these cases:
[a, b] = c
@@ -3686,11 +4106,11 @@
Assign
-
+
A regular object pattern-match.
@@ -3709,11 +4129,11 @@ Assign
-
+
A shorthand {a, b, @c} = val pattern-match.
@@ -3728,11 +4148,11 @@ Assign
-
+
A regular array pattern-match.
@@ -3757,11 +4177,11 @@ Assign
-
+
When compiling a conditional assignment, take care to ensure that the
operands are only evaluated once, even though we have to reference them
@@ -3775,11 +4195,11 @@
Assign
-
+
Disallow conditional assignment of undefined variables.
@@ -3798,11 +4218,11 @@ Assign
-
+
Convert special math assignment operators like a **= b to the equivalent
extended form a = a ** b and then compiles that.
@@ -3816,11 +4236,11 @@ Assign
-
+
Compile the assignment from an array splice literal, using JavaScript’s
Array#splice method.
@@ -3853,11 +4273,11 @@ Assign
-
+
Code
@@ -3866,11 +4286,11 @@ Code
-
+
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
@@ -3908,11 +4328,11 @@
Code
-
+