exports.run = ->
+ optionParser = buildCSOptionParser()
parseOptions()From 8234ce2712985be187dade4c8c98ae7bf3dc74ae Mon Sep 17 00:00:00 2001
From: Julian Rosse exports.run = ->
+ optionParser = buildCSOptionParser()
parseOptions().js file.
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 @@ shown.
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 @@ the ordinary Assign is that these allow numbers and strings as
+ 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 ordinary Assign is that these allow numbers and strings as -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 @@ of Block preceded by a function arrow, with an optional paramet -CoffeeScript has two different symbols for functions. -> is for ordinary
functions, and => is for functions bound to the current value of this.
=> is for functions bound to the current value of
- An optional, trailing comma.
@@ -669,11 +705,11 @@ functions, and=> is for functions bound to the current value of
- The list of parameters that a function accepts can be of any length.
@@ -690,11 +726,11 @@ functions, and=> is for functions bound to the current value of
- A single parameter in a function definition can be ordinary, or a splat that hoovers up the remaining arguments.
@@ -711,11 +747,11 @@ that hoovers up the remaining arguments. -Function Parameters
@@ -731,11 +767,11 @@ that hoovers up the remaining arguments. -A splat that occurs outside of a parameter list.
@@ -748,11 +784,11 @@ that hoovers up the remaining arguments. -Variables and properties that can be assigned to.
@@ -768,11 +804,11 @@ that hoovers up the remaining arguments. -Everything that can be assigned to.
@@ -787,11 +823,11 @@ that hoovers up the remaining arguments. -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 @@ as functions, indexed into, named as a class, etc. -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 @@ or by array index or slice. -Indexing into an object or array using bracket notation.
@@ -874,11 +910,11 @@ or by array index or slice. -In CoffeeScript, an object literal is simply a list of assignments.
@@ -891,11 +927,11 @@ or by array index or slice. -Assignment of properties within an object literal can be separated by comma, as in JavaScript, or simply by newline.
@@ -913,11 +949,11 @@ comma, as in JavaScript, or simply by newline. -Class definitions have optional bodies of prototype property assignments, and optional references to the superclass.
@@ -1002,11 +1038,11 @@ and optional references to the superclass. -Ordinary function invocation, or a chained series of calls.
@@ -1022,11 +1058,11 @@ and optional references to the superclass. -An optional existence check on a function.
@@ -1040,11 +1076,11 @@ and optional references to the superclass. -The list of arguments to a function call.
@@ -1058,11 +1094,11 @@ and optional references to the superclass. -A reference to the this current object.
@@ -1076,11 +1112,11 @@ and optional references to the superclass. -A reference to a property on this.
@@ -1093,11 +1129,11 @@ and optional references to the superclass. -The array literal.
@@ -1111,11 +1147,11 @@ and optional references to the superclass. -Inclusive and exclusive range dots.
@@ -1129,11 +1165,11 @@ and optional references to the superclass. -The CoffeeScript range literal.
@@ -1146,11 +1182,11 @@ and optional references to the superclass. -Array slice literals.
@@ -1166,11 +1202,11 @@ and optional references to the superclass. -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 @@ as well as the contents of an array literal -
Valid arguments are Blocks or Splats.
@@ -1208,11 +1244,11 @@ as well as the contents of an array literal -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 @@ having the newlines wouldn’t make sense.
-The variants of try/catch/finally exception handling blocks.
@@ -1248,11 +1284,11 @@ having the newlines wouldn’t make sense. -A catch clause names its error and runs a block of code.
@@ -1267,11 +1303,11 @@ having the newlines wouldn’t make sense. -Throw an exception object.
@@ -1284,11 +1320,11 @@ having the newlines wouldn’t make sense. -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 trick.
-The condition portion of a while loop.
@@ -1325,11 +1361,11 @@ the trick. -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 @@ or postfix, with a single expression. There is no do..while. -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 @@ or postfix, with a single expression.
-An array of all accepted values for a variable inside the loop. This enables support for pattern matching.
@@ -1404,11 +1440,11 @@ This enables support for pattern matching. -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 @@ of object comprehensions.
-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 @@ in fixed-size increments.
-An individual When clause, with action.
@@ -1481,11 +1517,11 @@ in fixed-size increments. -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 @@ ambiguity.
-The full complement of if expressions, including postfix one-liner if and unless.
@@ -1522,11 +1558,11 @@ ambiguity. -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 @@ rules are necessary.
-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:
2 + (3 * 4)
@@ -1665,26 +1701,14 @@ down. Following these rules is what makes 2 + 3 * 4 parse as:
-
-
-
-
- ¶
-
- Wrapping Up
-
-
-
-
-
-
-
+ Wrapping Up
+
@@ -1696,6 +1720,18 @@ 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 terminals (every symbol which does not appear as the name of a rule above) @@ -1714,11 +1750,11 @@ as “tokens”.
-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 4e253f72..12aab1e4 100644 --- a/docs/v2/annotated-source/lexer.html +++ b/docs/v2/annotated-source/lexer.html @@ -222,6 +222,7 @@ it has consumed.
@seenExport = no # Used to recognize EXPORT FROM? AS? tokens. @importSpecifierList = no # Used to identify when in an IMPORT {...} FROM? ... @exportSpecifierList = no # Used to identify when in an EXPORT {...} FROM? ... + @csxDepth = 0 # Used to optimize CSX checks, how deep in CSX we are. @chunkLine = opts.line or 0 # The start line for the current @chunk. @@ -253,6 +254,7 @@ short-circuiting if any of them succeed. Their order determines precedence: @lineToken() or @stringToken() or @numberToken() or + @csxToken() or @regexToken() or @jsToken() or @literalToken()is means === otherwise.
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] = matchfrom.
' '
value
+ if @atCSXTag()
+ @token ',', ',', 0, 0, @prev
+
endCSX 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 @@ parentheses that indicate a method call from regular parentheses, and so on.
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 @@ parameters specially in order to make things easier for the parser.
stack = [] {tokens} = this i = tokens.length - tokens[--i][0] = 'PARAM_END' + paramEndToken = tokens[--i] + paramEndToken[0] = 'PARAM_END' while tok = tokens[--i] switch tok[0] when ')' @@ -1056,17 +1188,19 @@ parameters specially in order to make things easier for the parser. else if tok[0] is '(' tok[0] = 'PARAM_START' return this - else return this + else + paramEndToken[0] = 'CALL_END' + return this thisClose up all remaining open blocks at the end of the file.
@@ -1078,11 +1212,11 @@ parameters specially in order to make things easier for the parser. -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 @@ Lexer and tokenize until the { of #{ is balanced with
#{ 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 @@ ad infinitum.
-
+
-
-
-
-
-
-
-
-
- ¶
-
- The 1s are to remove the # in #{.
-
-
-
- [line, column] = @getLineAndColumnFromChunk offsetInChunk + 1
- {tokens: nested, index} =
- new Lexer().tokenize str[1..], line: line, column: column, untilBalanced: on
-
-
-
-
-
-
-
-
- ¶
-
- Skip the trailing }.
-
-
-
- index += 1
-
-
-
-
-
-
-
-
- ¶
-
- Turn 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]]
-
-
-
-
-
-
-
-
- ¶
-
- Remove leading 'TERMINATOR' (if any).
-
-
-
- nested.splice 1, 1 if nested[1]?[0] is 'TERMINATOR'
+ break unless match = interpolators.exec str
+ [interpolator] = match Push a fake 'TOKENS' token, which will get turned into real tokens later.
To remove the # in #{.
tokens.push ['TOKENS', nested]
-
- str = str[index..]
- offsetInChunk += index
-
- unless str[...delimiter.length] is delimiter
- @error "missing #{delimiter}", length: delimiter.length
-
- [firstToken, ..., lastToken] = tokens
- firstToken[2].first_column -= delimiter.length
- if lastToken[1].substr(-1) is '\n'
- lastToken[2].last_line += 1
- lastToken[2].last_column = delimiter.length - 1
- else
- lastToken[2].last_column += delimiter.length
- lastToken[2].last_column -= 1 if lastToken[1].length is 0
-
- {tokens, index: offsetInChunk + delimiter.length} interpolationOffset = interpolator.length - 1
+ [line, column] = @getLineAndColumnFromChunk offsetInChunk + interpolationOffset
+ rest = str[interpolationOffset..]
+ {tokens: nested, index} =
+ new Lexer().tokenize rest, line: line, column: column, untilBalanced: onAccount for the # in #{
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]]Remove leading 'TERMINATOR' (if any).
nested.splice 1, 1 if nested[1]?[0] is 'TERMINATOR'
+
+ unless braceInterpolatorWe are not using { and }, so wrap the interpolated tokens instead.
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.
tokens.push ['TOKENS', nested]
+
+ str = str[index..]
+ offsetInChunk += index
+
+ unless str[...closingDelimiter.length] is closingDelimiter
+ @error "missing #{closingDelimiter}", length: delimiter.length
+
+ [firstToken, ..., lastToken] = tokens
+ firstToken[2].first_column -= delimiter.length
+ if lastToken[1].substr(-1) is '\n'
+ lastToken[2].last_line += 1
+ lastToken[2].last_column = closingDelimiter.length - 1
+ else
+ lastToken[2].last_column += closingDelimiter.length
+ lastToken[2].last_column -= 1 if lastToken[1].length is 0
+
+ {tokens, index: offsetInChunk + closingDelimiter.length}Merge the array tokens of the fake token types 'TOKENS' and 'NEOSTRING'
(as returned by matchWithInterpolations) into the token stream. The value
of 'NEOSTRING's are converted using fn and turned into strings using
@@ -1258,11 +1423,11 @@ of 'NEOSTRING's are converted using fn and tur
-
Optimize out empty interpolations (an empty pair of parentheses).
@@ -1273,11 +1438,11 @@ of'NEOSTRING's are converted using fn and tur
- 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 @@ really is a string.
-However, there is one case where we can optimize away a starting empty string.
@@ -1349,11 +1514,11 @@ empty string. -Create a 0-length “+” token.
@@ -1385,11 +1550,11 @@ empty string. -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 @@ correctly balanced throughout the course of the token stream. -Auto-close INDENT to support syntax like this:
el.click((event) ->
@@ -1424,11 +1589,11 @@ correctly balanced throughout the course of the token stream.
-
+
Helpers
@@ -1437,11 +1602,11 @@ correctly balanced throughout the course of the token stream.
-
+
@@ -1449,11 +1614,11 @@ correctly balanced throughout the course of the token stream.
-
+
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 @@ correctly balanced throughout the course of the token stream.
-
+
Same as token, except this just returns the token without adding it
to the results.
@@ -1502,11 +1667,11 @@ to the results.
-
+
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 @@ so if last_column == first_column, then we’re looking at a character of length
-
+
Add a token to the results.
offset is the offset into the current @chunk where the token starts.
@@ -1547,11 +1712,11 @@ not specified, the length of value will be used.
-
+
Peek at the last tag in the token stream.
@@ -1564,11 +1729,11 @@ not specified, the length of value will be used.
-
+
Peek at the last value in the token stream.
@@ -1581,11 +1746,11 @@ not specified, the length of value will be used.
-
+
Get the previous token in the token stream.
@@ -1597,11 +1762,11 @@ not specified, the length of value will be used.
-
+
Are we in the midst of an unfinished expression?
@@ -1631,11 +1796,11 @@ not specified, the length of value will be used.
-
+
surrogate pair
@@ -1648,11 +1813,11 @@ not specified, the length of value will be used.
-
+
Replace \u{...} with \uxxxx[\uxxxx] in regexes without u flag
@@ -1675,11 +1840,11 @@ not specified, the length of value will be used.
-
+
Validates escapes in strings and regexes.
@@ -1707,11 +1872,11 @@ not specified, the length of value will be used.
-
+
Constructs a string or regex by escaping certain characters.
@@ -1731,11 +1896,11 @@ not specified, the length of value will be used.
-
+
Ignore escaped backslashes.
@@ -1754,11 +1919,11 @@ not specified, the length of value will be used.
-
+
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 @@ location of a token (token[2]).
-
+
-
+
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 @@ loop. Try to detect when from is a variable identifier and when it
-
+
for i from from, for from from iterable
@@ -1851,11 +2016,11 @@ loop. Try to detect when from is a variable identifier and when it
-
+
for i from iterable
@@ -1866,11 +2031,11 @@ loop. Try to detect when from is a variable identifier and when it
-
+
for from…
@@ -1882,11 +2047,11 @@ loop. Try to detect when from is a variable identifier and when it
-
+
for {from}…, for [from]…, for {a, from}…, for {a: from}…
@@ -1900,11 +2065,11 @@ loop. Try to detect when from is a variable identifier and when it
-
+
Constants
@@ -1913,11 +2078,11 @@ loop. Try to detect when from is a variable identifier and when it
-
+
@@ -1925,11 +2090,11 @@ loop. Try to detect when from is a variable identifier and when it
-
+
-
+
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 @@ STRICT_PROSCRIBED = ['arguments',
+
The superset of both JavaScript keywords and reserved words, none of which may
be used as identifiers or properties.
@@ -2018,11 +2183,11 @@ be used as identifiers or properties.
-
+
The character code of the nasty Microsoft madness otherwise known as the BOM.
@@ -2033,11 +2198,11 @@ be used as identifiers or properties.
-
+
Token matching regexes.
@@ -2049,6 +2214,17 @@ be used as identifiers or properties.
( [^\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 @@ HERE_JSTOKEN = ///^ ``` ((?: [^`\\] | \\[\s\S] | `
-
+
String-matching-regexes.
@@ -2097,6 +2273,17 @@ STRING_DOUBLE = /// ^(?: [^\\"'] | \\[\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 @@ HEREDOC_INDENT = /\n+([^\n\S]*)(?=\S)/g
-
+
-
+
-
+
Unary tokens.
@@ -2227,11 +2414,11 @@ UNARY_MATH = ['!', '~
-
+
Bit-shifting tokens.
@@ -2242,11 +2429,11 @@ UNARY_MATH = ['!', '~
-
+
Comparison tokens.
@@ -2257,11 +2444,11 @@ UNARY_MATH = ['!', '~
-
+
Mathematical tokens.
@@ -2272,11 +2459,11 @@ UNARY_MATH = ['!', '~
-
+
Relational tokens that are negatable with not prefix.
@@ -2287,11 +2474,11 @@ UNARY_MATH = ['!', '~
-
+
Boolean tokens.
@@ -2302,11 +2489,11 @@ UNARY_MATH = ['!', '~
-
+
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 @@ INDEXABLE = CALLABLE.concat [
-
+
+
+ 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 @@ CALLABLEs in some cases), but which a division operator can.
-
+
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 @@ avoid an ambiguity in the grammar.
-
+
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 8f18bdfa..83e2fe2b 100644
--- a/docs/v2/annotated-source/nodes.html
+++ b/docs/v2/annotated-source/nodes.html
@@ -707,7 +707,10 @@ if the location data is not already set.
new CodeFragment this, code
wrapInParentheses: (fragments) ->
- [].concat @makeCode('('), fragments, @makeCode(')')
+ [@makeCode('('), fragments..., @makeCode(')')]
+
+ wrapInBraces: (fragments) ->
+ [@makeCode('{'), fragments..., @makeCode('}')]
@@ -1274,6 +1277,16 @@ exports.NaNLiteral = class
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 @@ exports.IdentifierLiteral =
eachName: (iterator) ->
iterator @
+exports.CSXTag = class CSXTag extends IdentifierLiteral
+
exports.PropertyName = class PropertyName extends Literal
isAssignable: YES
@@ -1706,6 +1721,8 @@ at the same position.
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 @@ expands the range on the left, but not the right.
compileNode: (o) ->
+ return @compileCSX o if @csx
@variable?.front = @front
compiledArgs = []
for arg, argIndex in @args
@@ -1828,6 +1846,21 @@ expands the range on the left, but not the right.
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 @@ is the index of the beginning.
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 @@ is the index of the beginning.
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 @@ is the index of the beginning.
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 @@ is the index of the beginning.
-
+
+ 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 @@ is the index of the beginning.
-
+
An array literal.
@@ -2559,11 +2698,11 @@ is the index of the beginning.
-
+
If this array is the left-hand side of an assignment, all its children
are too.
@@ -2600,11 +2739,11 @@ are too.
-
+
Class
@@ -2613,11 +2752,11 @@ are too.
-
+
The CoffeeScript class definition.
Initialize a Class with its name, an optional superclass, and a body.
@@ -2638,11 +2777,11 @@ exports.Class = class
-
+
Special handling to allow class expr.A extends A declarations
@@ -2651,38 +2790,46 @@ exports.Class = 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 @@ exports.Class = class
-
+
Figure out the appropriate name for this class
@@ -2730,6 +2877,7 @@ exports.Class = class
walkBody: ->
@ctor = null
+ @boundMethods = []
executableBody = null
initializer = []
@@ -2755,11 +2903,11 @@ exports.Class = class
-
+
Try to keep comments with their subsequent assign
@@ -2782,11 +2930,11 @@ exports.Class = class
-
+
Try to keep comments with their subsequent assign
@@ -2801,6 +2949,8 @@ exports.Class = 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 @@ exports.Class = 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 @@ When additional expressions become valid, this method should be updated to handl
-
+
Checks if the given node is a valid ES class initializer method.
@@ -2851,11 +3001,11 @@ When additional expressions become valid, this method should be updated to handl
-
+
Returns a configured class initializer method
@@ -2873,7 +3023,7 @@ When additional expressions become valid, this method should be updated to handl
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 @@ When additional expressions become valid, this method should be updated to handl
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 @@ exports.ExecutableClassBody =
+
-
+
Make class/prototype assignments for invalid ES properties
@@ -3017,11 +3176,11 @@ exports.ExecutableClassBody =
+
Passthrough
@@ -3034,11 +3193,11 @@ exports.ExecutableClassBody =
+
The class scope is not available yet, so return the assignment to update later
@@ -3060,11 +3219,11 @@ exports.ExecutableClassBody =
+
Import and Export
@@ -3138,11 +3297,11 @@ exports.ExportDeclaration =
-
+
Prevent exporting an anonymous class; all exported members must be named
@@ -3201,11 +3360,11 @@ exports.ModuleSpecifier = cl
-
+
The name of the variable entering the local scope
@@ -3231,11 +3390,11 @@ exports.ImportSpecifier = cl
-
+
Per the spec, symbols can’t be imported multiple times
(e.g. import { foo, foo } from 'lib' is invalid)
@@ -3259,11 +3418,11 @@ exports.ExportSpecifier = cl
-
+
Assign
@@ -3272,11 +3431,11 @@ exports.ExportSpecifier = cl
-
+
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 @@ property of an object – including within object literals.
-
+
Compile an assignment, delegating to compileDestructuring or
compileSplice if appropriate. Keep track of the name of the base object
@@ -3329,11 +3488,11 @@ has not been seen yet within the current scope, declare it.
-
+
When compiling @variable, remember if it is part of a function parameter.
@@ -3344,11 +3503,11 @@ has not been seen yet within the current scope, declare it.
-
+
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 @@ and convert this ES-unsupported destructuring into acceptable output.
-
+
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 @@ destructured variables.
@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 @@ destructured variables.
-
+
moduleDeclaration can be 'import' or 'export'
@@ -3418,6 +3593,7 @@ destructured variables.
[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 @@ destructured variables.
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,11 +3627,255 @@ if we’re destructuring without declaring, the destructuring assignment must be
-
+
+ Check object destructuring variable for rest elements;
+can be removed once ES proposal hits Stage 4.
+
+
+
+ compileObjectDestruct: (o) ->
+
+
+
+
+
+
+
+
+ ¶
+
+ 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.
+
+
+
+ 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
+
+
+
+
+
+
+
+
+ ¶
+
+ 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 != 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.
@@ -3471,11 +3891,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.
@@ -3490,11 +3910,11 @@ Compile to simply a.
-
+
Disallow [...] = a for some reason. (Could be equivalent to [] = a?)
@@ -3508,11 +3928,11 @@ Compile to simply a.
-
+
Special case for when there’s only one thing destructured off of
something. {a} = b, [a] = b, {a: b} = c
@@ -3524,11 +3944,11 @@ something. {a} = b, [a] = b, {a: b} = c
-
+
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 @@ something. {a} = b, [a] = b, {a: b} = c
-
+
A regular object pattern-match.
@@ -3564,11 +3984,11 @@ something. {a} = b, [a] = b, {a: b} = c
-
+
A shorthand {a, b, @c} = val pattern-match.
@@ -3583,11 +4003,11 @@ something. {a} = b, [a] = b, {a: b} = c
-
+
A regular array pattern-match.
@@ -3612,11 +4032,11 @@ something. {a} = b, [a] = b, {a: b} = c
-
+
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 @@ variable if it isn’t already.
-
+
And here comes the big loop that handles all of these cases:
[a, b] = c
@@ -3686,11 +4106,11 @@ etc.
-
+
A regular object pattern-match.
@@ -3709,11 +4129,11 @@ etc.
-
+
A shorthand {a, b, @c} = val pattern-match.
@@ -3728,11 +4148,11 @@ etc.
-
+
A regular array pattern-match.
@@ -3757,11 +4177,11 @@ etc.
-
+
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 @@ more than once.
-
+