-marked = require 'marked'
From 2ec1a1851d90ec940de41f6b0fef366f6cfcff09 Mon Sep 17 00:00:00 2001
From: Geoffrey Booth
-marked = require 'marked'
marked.setOptions - renderer: new marked.Renderer() - gfm: true - tables: true - breaks: false - pedantic: false - sanitize: true - smartLists: true - smartypants: false
- - - - - - -Peek at the beginning of a given string to see if it matches a sequence.
Peek at the end of a given string to see if it matches a sequence.
@@ -181,11 +160,11 @@ marked = require 'm -Repeat a string n
times.
Use clever algorithm to have O(log(n)) string concatenation operations.
@@ -216,11 +195,11 @@ marked = require 'm -Trim out all falsy values from an array.
@@ -232,11 +211,11 @@ marked = require 'm -Count the number of occurrences of a string in a string.
@@ -251,11 +230,11 @@ marked = require 'm -Merge objects, returning a fresh copy with attributes from both sides.
Used every time Base#compile
is called, to allow properties in the
@@ -269,11 +248,11 @@ options hash to propagate down the tree without polluting other branches.
Extend a source object with the properties of another object (shallow copy).
@@ -287,11 +266,11 @@ options hash to propagate down the tree without polluting other branches. -Return a flattened version of an array.
Handy for getting a list of children
from the nodes.
children
from the nodes.
- Delete a key from an object, returning the value. Useful when a node is looking for a particular method in an options hash.
@@ -329,11 +308,11 @@ looking for a particular method in an options hash. -Typical Array::some
@@ -346,19 +325,43 @@ looking for a particular method in an options hash. +Simple function for extracting code from Literate CoffeeScript by stripping +out all non-code blocks, producing a string of CoffeeScript code that can +be compiled “normally.” Uses MarkdownIt +to tell the difference between Markdown and code blocks.
+ +exports.invertLiterate = (code) ->
+ out = []
+ md.renderer.rules =
Simple function for extracting code from Literate CoffeeScript by stripping -out all non-code blocks, producing a string of CoffeeScript code that can -be compiled “normally.”
+Delete all other rules, since all we want are the code blocks.
exports.invertLiterate = (code) ->
code_block: (tokens, idx, options, env, slf) ->
+ startLine = tokens[idx].map[0]
+ lines = tokens[idx].content.split '\n'
+ for line, i in lines
+ out[startLine + i] = line
+ md.render code
+ out.join '\n'
Create a placeholder for tabs, that isn’t used anywhere in code
, and then
-re-insert the tabs after code extraction.
generateRandomToken = ->
- "#{Math.random() * Date.now()}"
- while token is undefined or code.indexOf(token) isnt -1
- token = generateRandomToken()
-
- code = code.replace "\t", token
Parse as markdown, discard everything except code blocks.
- - out = ""
- for item in marked.lexer code, {}
- out += "#{item.text}\n" if item.type is 'code'
Put the tabs back in.
- - out.replace token, "\t"
- out
Merge two jison-style location data objects together.
If last
is not provided, this will simply return first
.
last
is not provided, this will simply return first
- This returns a function which takes an object as a parameter, and if that object is an AST node, updates that object’s locationData. @@ -462,11 +411,11 @@ The object is returned either way.
-Convert jison location data to a string.
obj
can be a token, or a locationData.
A .coffee.md
compatible version of basename
, that returns the file sans-extension.
Determine if a filename represents a CoffeeScript file.
@@ -524,11 +473,11 @@ The object is returned either way. -Determine if a filename represents a Literate CoffeeScript file.
@@ -539,11 +488,11 @@ The object is returned either way. -Throws a SyntaxError from a given location.
The error’s toString
will return an error message following the “standard”
@@ -560,11 +509,11 @@ marker showing where the error is.
Instead of showing the compiler’s stacktrace, show our custom error message (this is useful when the error bubbles up in Node.js applications that @@ -579,11 +528,11 @@ compile CoffeeScript for example).
-Update a compiler SyntaxError with source code information if it didn’t have it already.
@@ -595,11 +544,11 @@ it already. -Avoid screwing up the stack
property of other errors (i.e. possible bugs).
Show only the first line on multi-line errors.
@@ -641,11 +590,11 @@ it already. -Check to see if we’re running on a color-enabled TTY.
diff --git a/docs/v2/annotated-source/lexer.html b/docs/v2/annotated-source/lexer.html index 8afec0e7..e16bee93 100644 --- a/docs/v2/annotated-source/lexer.html +++ b/docs/v2/annotated-source/lexer.html @@ -220,6 +220,7 @@ it has consumed. @seenFor = no # Used to recognize FORIN, FOROF and FORFROM tokens. @seenImport = no # Used to recognize IMPORT FROM? AS? tokens. @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? ... @chunkLine = @@ -381,14 +382,14 @@ thoughis
means ===
otherwise.
if @tag() in ['DEFAULT', 'IMPORT_ALL', 'IDENTIFIER']
@token 'AS', id
return id.length
- if id is 'as' and @seenExport and @tag() is 'IDENTIFIER'
+ if id is 'as' and @seenExport and @tag() in ['IDENTIFIER', 'DEFAULT']
@token 'AS', id
return id.length
- if id is 'default' and @seenExport
+ if id is 'default' and @seenExport and @tag() in ['EXPORT', 'AS']
@token 'DEFAULT', id
return id.length
- [..., prev] = @tokens
+ prev = @prev()
tag =
if colon or prev? and
@@ -425,7 +426,30 @@ though is
means ===
otherwise.
else if tag is 'IDENTIFIER' and @seenFor and id is 'from' and
isForFrom(prev)
tag = 'FORFROM'
- @seenFor = no
+ @seenFor = noThrow an error on attempts to use get
or set
as keywords, or
+what CoffeeScript would normally interpret as calls to functions named
+get
or set
, i.e. get({foo: function () {}})
else if tag is 'PROPERTY' and prev
+ if prev.spaced and prev[0] in CALLABLE and /^[gs]et$/.test(prev[1])
+ @error "'#{prev[1]}' cannot be used as a keyword, or as a function call without parentheses", prev[2]
+ else
+ prevprev = @tokens[@tokens.length - 2]
+ if prev[0] in ['@', 'THIS'] and prevprev and prevprev.spaced and /^[gs]et$/.test(prevprev[1])
+ @error "'#{prevprev[1]}' cannot be used as a keyword, or as a function call without parentheses", prevprev[2]
if tag is 'IDENTIFIER' and id in RESERVED
@error "reserved word '#{id}'", length: id.length
@@ -457,11 +481,11 @@ though is
means ===
otherwise.
-
Matches numbers, including decimals, hex, and exponential notation. Be careful not to interfere with ranges-in-progress.
@@ -500,11 +524,11 @@ Be careful not to interfere with ranges-in-progress. -Matches strings, including multi-line strings, as well as heredocs, with or without interpolation.
@@ -518,19 +542,20 @@ interpolation. -If the preceding token is from
and this is an import or export statement,
properly tag the from
.
if @tokens.length and @value() is 'from' and (@seenImport or @seenExport)
- @tokens[@tokens.length - 1][0] = 'FROM'
+ prev = @prev()
+ if prev and @value() is 'from' and (@seenImport or @seenExport)
+ prev[0] = 'FROM'
regex = switch quote
when "'" then STRING_SINGLE
@@ -548,11 +573,11 @@ properly tag the from
.
-
+
Find the smallest indentation. It will be removed from all lines later.
@@ -586,11 +611,11 @@ properly tag the from
.
-
+
Matches and consumes comments.
@@ -611,11 +636,11 @@ properly tag the from
.
-
+
Matches JavaScript interpolated directly into the source via backticks.
@@ -628,11 +653,11 @@ properly tag the from
.
-
+
Convert escaped backticks to backticks, and escaped backslashes
just before escaped backticks to backslashes
@@ -644,11 +669,11 @@ just before escaped backticks to backslashes
-
+
string
is always a value like ‘`‘, ‘\`‘, ‘\\`‘, etc.
By reducing it to its latter half, we turn ‘`‘ to ‘', '\\\
‘ to ‘`‘, etc.
@@ -662,11 +687,11 @@ By reducing it to its latter half, we turn ‘`‘ to ‘', '\\\
-
+
Matches regular expression literals, as well as multiline extended ones.
Lexing regular expressions is difficult to distinguish from division, so we
@@ -685,7 +710,7 @@ borrow some basic heuristics from JavaScript and Ruby.
[regex, body, closed] = match
@validateEscapes body, isRegex: yes, offsetInChunk: 1
index = regex.length
- [..., prev] = @tokens
+ prev = @prev()
if prev
if prev.spaced and prev[0] in CALLABLE
return 0 if not closed or POSSIBLY_DIVISION.test regex
@@ -720,11 +745,11 @@ borrow some basic heuristics from JavaScript and Ruby.
-
+
Matches newlines, indents, and outdents, and determines which is which.
If we can detect that the current line is continued onto the next line,
@@ -742,6 +767,8 @@ can close multiple indents, so we need to know how far in we happen to be.
indent = match[0]
@seenFor = no
+ @seenImport = no unless @importSpecifierList
+ @seenExport = no unless @exportSpecifierList
size = indent.length - 1 - indent.lastIndexOf '\n'
noNewlines = @unfinished()
@@ -786,11 +813,11 @@ can close multiple indents, so we need to know how far in we happen to be.
-
+
Record an outdent token or multiple tokens, if we happen to be moving back
inwards past several recorded indents. Sets new @indent value.
@@ -803,12 +830,9 @@ inwards past several recorded indents. Sets new @indent value.
lastIndent = @indents[@indents.length - 1]
if not lastIndent
moveOut = 0
- else if lastIndent is @outdebt
- moveOut -= @outdebt
- @outdebt = 0
- else if lastIndent < @outdebt
- @outdebt -= lastIndent
- moveOut -= lastIndent
+ else if @outdebt and moveOut <= @outdebt
+ @outdebt -= moveOut
+ moveOut = 0
else
dent = @indents.pop() + @outdebt
if outdentLength and @chunk[outdentLength] in INDENTABLE_CLOSERS
@@ -819,11 +843,11 @@ inwards past several recorded indents. Sets new @indent value.
-
+
pair might call outdentToken, so preserve decreasedIndent
@@ -843,11 +867,11 @@ inwards past several recorded indents. Sets new @indent value.
-
+
Matches and consumes non-meaningful whitespace. Tag the previous token
as being “spaced”, because there are some cases where it makes a difference.
@@ -857,18 +881,18 @@ as being “spaced”, because there are some cases where it makes a difference.
whitespaceToken: ->
return 0 unless (match = WHITESPACE.exec @chunk) or
(nline = @chunk.charAt(0) is '\n')
- [..., prev] = @tokens
+ prev = @prev()
prev[if match then 'spaced' else 'newLine'] = true if prev
if match then match[0].length else 0
-
+
Generate a newline token. Consecutive newlines get merged together.
@@ -882,11 +906,11 @@ as being “spaced”, because there are some cases where it makes a difference.
-
+
Use a \
at a line-ending to suppress the newline.
The slash is removed here once its job is done.
@@ -900,11 +924,11 @@ The slash is removed here once its job is done.
-
+
We treat all other single characters as a token. E.g.: ( ) , . !
Multi-character operators are also literal tokens, so that Jison can assign
@@ -921,7 +945,7 @@ parentheses that indicate a method call from regular parentheses, and so on.
else
value = @chunk.charAt 0
tag = value
- [..., prev] = @tokens
+ prev = @prev()
if prev and value in ['=', COMPOUND_ASSIGN...]
skipToken = false
@@ -936,7 +960,11 @@ parentheses that indicate a method call from regular parentheses, and so on.
@error message, origin[2] if message
return value.length if skipToken
- if value is '{' and prev?[0] is 'EXPORT'
+ if value is '{' and @seenImport
+ @importSpecifierList = yes
+ else if @importSpecifierList and value is '}'
+ @importSpecifierList = no
+ else if value is '{' and prev?[0] is 'EXPORT'
@exportSpecifierList = yes
else if @exportSpecifierList and value is '}'
@exportSpecifierList = no
@@ -971,26 +999,14 @@ parentheses that indicate a method call from regular parentheses, and so on.
-
-
-
-
- ¶
-
- Token Manipulators
-
-
-
-
-
-
-
+ Token Manipulators
+
@@ -1002,6 +1018,18 @@ 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
parameters specially in order to make things easier for the parser.
@@ -1029,11 +1057,11 @@ parameters specially in order to make things easier for the parser.
-
+
Close up all remaining open blocks at the end of the file.
@@ -1045,11 +1073,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
@@ -1081,11 +1109,11 @@ ad infinitum.
-
+
Push a fake ‘NEOSTRING’ token, which will get turned into a real string later.
@@ -1101,11 +1129,11 @@ ad infinitum.
-
+
The 1
s are to remove the #
in #{
.
@@ -1118,11 +1146,11 @@ ad infinitum.
-
+
Skip the trailing }
.
@@ -1133,11 +1161,11 @@ ad infinitum.
-
+
Turn the leading and trailing {
and }
into parentheses. Unnecessary
parentheses will be removed later.
@@ -1152,11 +1180,11 @@ parentheses will be removed later.
-
+
Remove leading ‘TERMINATOR’ (if any).
@@ -1167,11 +1195,11 @@ parentheses will be removed later.
-
+
Push a fake ‘TOKENS’ token, which will get turned into real tokens later.
@@ -1199,11 +1227,11 @@ parentheses will be removed later.
-
+
Merge the array tokens
of the fake token types ‘TOKENS’ and ‘NEOSTRING’
(as returned by matchWithInterpolations
) into the token stream. The value
@@ -1225,11 +1253,11 @@ of ‘NEOSTRING’s are converted using fn
and turned into strings
-
+
Optimize out empty interpolations (an empty pair of parentheses).
@@ -1240,11 +1268,11 @@ of ‘NEOSTRING’s are converted using fn
and turned into strings
-
+
Push all the tokens in the fake ‘TOKENS’ token. These already have
sane location data.
@@ -1258,11 +1286,11 @@ sane location data.
-
+
Convert ‘NEOSTRING’ into ‘STRING’.
@@ -1273,11 +1301,11 @@ sane location data.
-
+
Optimize out empty strings. We ensure that the tokens stream always
starts with a string token, though, to make sure that the result
@@ -1294,11 +1322,11 @@ really is a string.
-
+
However, there is one case where we can optimize away a starting
empty string.
@@ -1316,11 +1344,11 @@ empty string.
-
+
Create a 0-length “+” token.
@@ -1352,11 +1380,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.
@@ -1371,11 +1399,11 @@ correctly balanced throughout the course of the token stream.
-
+
Auto-close INDENT to support syntax like this:
el.click((event) ->
@@ -1391,26 +1419,14 @@ correctly balanced throughout the course of the token stream.
-
-
-
-
- ¶
-
- Helpers
-
-
-
-
-
-
-
+ Helpers
+
@@ -1422,6 +1438,18 @@ 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.
@@ -1450,11 +1478,11 @@ correctly balanced throughout the course of the token stream.
-
+
Same as “token”, exception this just returns the token without adding it
to the results.
@@ -1469,11 +1497,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.
@@ -1491,11 +1519,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.
@@ -1514,11 +1542,11 @@ not specified, the length of value
will be used.
-
+
Peek at the last tag in the token stream.
@@ -1531,11 +1559,11 @@ not specified, the length of value
will be used.
-
+
Peek at the last value in the token stream.
@@ -1548,11 +1576,27 @@ not specified, the length of value
will be used.
-
+
+
+ prev: ->
+ @tokens[@tokens.length - 1]
+
+
+
+
+
+
+
+
+ ¶
Are we in the midst of an unfinished expression?
@@ -1573,21 +1617,25 @@ not specified, the length of value
will be used.
-
+
validateEscapes: (str, options = {}) ->
- match = INVALID_ESCAPE.exec str
+ invalidEscapeRegex =
+ if options.isRegex
+ REGEX_INVALID_ESCAPE
+ else
+ STRING_INVALID_ESCAPE
+ match = invalidEscapeRegex.exec str
return unless match
[[], before, octal, hex, unicode] = match
- return if options.isRegex and octal and octal.charAt(0) isnt '0'
message =
if octal
"octal escape sequences are not allowed"
@@ -1601,11 +1649,11 @@ not specified, the length of value
will be used.
-
+
Constructs a string or regex by escaping certain characters.
@@ -1625,11 +1673,11 @@ not specified, the length of value
will be used.
-
+
Ignore escaped backslashes.
@@ -1648,11 +1696,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]
).
@@ -1671,11 +1719,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
@@ -1728,11 +1776,11 @@ loop. Try to detect when from
is a variable identifier and when it
-
+
for i from from
, for from from iterable
@@ -1745,11 +1793,11 @@ loop. Try to detect when from
is a variable identifier and when it
-
+
for i from iterable
@@ -1760,11 +1808,11 @@ loop. Try to detect when from
is a variable identifier and when it
-
+
for from…
@@ -1776,11 +1824,11 @@ loop. Try to detect when from
is a variable identifier and when it
-
+
for {from}…
, for [from]…
, for {a, from}…
, for {a: from}…
@@ -1794,11 +1842,11 @@ loop. Try to detect when from
is a variable identifier and when it
-
+
Constants
@@ -1807,11 +1855,11 @@ loop. Try to detect when from
is a variable identifier and when it
-
+
@@ -1819,11 +1867,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,
@@ -1896,11 +1944,11 @@ STRICT_PROSCRIBED = ['arguments',
+
The superset of both JavaScript keywords and reserved words, none of which may
be used as identifiers or properties.
@@ -1912,11 +1960,11 @@ be used as identifiers or properties.
-
+
The character code of the nasty Microsoft madness otherwise known as the BOM.
@@ -1927,11 +1975,11 @@ be used as identifiers or properties.
-
+
Token matching regexes.
@@ -1974,11 +2022,11 @@ HERE_JSTOKEN = ///^ ``` ((?: [^`\\] | \\[\s\S] | `
-
+
String-matching-regexes.
@@ -2001,11 +2049,11 @@ HEREDOC_INDENT = /\n+([^\n\S]*)(?=\S)/g
-
+
Regex-matching-regexes.
@@ -2039,11 +2087,11 @@ POSSIBLY_DIVISION = /// ^ /=?\s ///
-
+
Other regexes.
@@ -2053,7 +2101,7 @@ POSSIBLY_DIVISION = /// ^ /=?\s ////// ^ \s* (?: , | \??\.(?![.\d]) | :: ) ///
-INVALID_ESCAPE = ///
+STRING_INVALID_ESCAPE = ///
( (?:^|[^\\]) (?:\\\\)* ) # make sure the escape isn’t escaped
\\ (
?: (0[0-7]|[1-7]) # octal escape
@@ -2061,6 +2109,14 @@ INVALID_ESCAPE = ///
| (u(?![\da-fA-F]{4}).{0,4}) # unicode escape
)
///
+REGEX_INVALID_ESCAPE = ///
+ ( (?:^|[^\\]) (?:\\\\)* ) # make sure the escape isn’t escaped
+ \\ (
+ ?: (0[0-7]) # octal escape
+ | (x(?![\da-fA-F]{2}).{0,2}) # hex escape
+ | (u(?![\da-fA-F]{4}).{0,4}) # unicode escape
+ )
+///
LEADING_BLANK_LINE = /^[^\n\S]*\n/
TRAILING_BLANK_LINE = /\n[^\n\S]*$/
@@ -2070,11 +2126,11 @@ TRAILING_SPACES = /\s+$/
-
+
-
+
Unary tokens.
@@ -2105,11 +2161,11 @@ UNARY_MATH = ['!', '~
-
+
Bit-shifting tokens.
@@ -2120,11 +2176,11 @@ UNARY_MATH = ['!', '~
-
+
Comparison tokens.
@@ -2135,11 +2191,11 @@ UNARY_MATH = ['!', '~
-
+
Mathematical tokens.
@@ -2150,11 +2206,11 @@ UNARY_MATH = ['!', '~
-
+
Relational tokens that are negatable with not
prefix.
@@ -2165,11 +2221,11 @@ UNARY_MATH = ['!', '~
-
+
Boolean tokens.
@@ -2180,11 +2236,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
@@ -2201,11 +2257,11 @@ INDEXABLE = CALLABLE.concat [
-
+
Tokens which a regular expression will never immediately follow (except spaced
CALLABLEs in some cases), but which a division operator can.
@@ -2218,11 +2274,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
@@ -2235,11 +2291,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 ef68f01e..7a2ee572 100644
--- a/docs/v2/annotated-source/nodes.html
+++ b/docs/v2/annotated-source/nodes.html
@@ -706,7 +706,7 @@ if the location data is not already set.
makeCode: (code) ->
new CodeFragment this, code
- wrapInBraces: (fragments) ->
+ wrapInParentheses: (fragments) ->
[].concat @makeCode('('), fragments, @makeCode(')')
@@ -1100,7 +1100,7 @@ our own
answer = @joinFragmentArrays(compiledNodes, ', ')
else
answer = [@makeCode "void 0"]
- if compiledNodes.length > 1 and o.level >= LEVEL_LIST then @wrapInBraces answer else answer
+ if compiledNodes.length > 1 and o.level >= LEVEL_LIST then @wrapInParentheses answer else answer
@@ -1272,7 +1272,7 @@ exports.NaNLiteral = class
compileNode: (o) ->
code = [@makeCode '0/0']
- if o.level >= LEVEL_OP then @wrapInBraces code else code
+ if o.level >= LEVEL_OP then @wrapInParentheses code else code
exports.StringLiteral = class StringLiteral extends Literal
@@ -1283,6 +1283,9 @@ exports.PassthroughLiteral = class IdentifierLiteral extends Literal
isAssignable: YES
+ eachName: (iterator) ->
+ iterator @
+
exports.PropertyName = class PropertyName extends Literal
isAssignable: YES
@@ -1341,8 +1344,7 @@ exports.BooleanLiteral = cla
- A return
is a pureStatement – wrapping it in a closure wouldn’t
-make sense.
+ A return
is a pureStatement—wrapping it in a closure wouldn’t make sense.
@@ -1437,14 +1439,15 @@ or vanilla.
exports.Value = class Value extends Base
- constructor: (base, props, tag) ->
+ constructor: (base, props, tag, isDefaultValue = no) ->
return base if not props and base instanceof Value
super()
- @base = base
- @properties = props or []
- @[tag] = true if tag
+ @base = base
+ @properties = props or []
+ @[tag] = yes if tag
+ @isDefaultValue = isDefaultValue
return this
children: ['base', 'properties']
@@ -1620,7 +1623,15 @@ evaluate anything twice when building the soak chain.
fst = new Parens new Assign ref, fst
snd.base = ref
return new If new Existence(fst), snd, soak: on
- no
+ no
+
+ eachName: (iterator) ->
+ if @hasProperties()
+ iterator @
+ else if @base.isAssignable()
+ @base.eachName iterator
+ else
+ @error 'tried to assign to unassignable value'
@@ -1692,7 +1703,7 @@ at the same position.
constructor: (@variable, @args = [], @soak) ->
super()
- @isNew = false
+ @isNew = no
if @variable instanceof Value and @variable.isNotCallable()
@variable.error "literal is not a function"
@@ -2402,53 +2413,15 @@ is the index of the beginning.
exports.Obj = class Obj extends Base
- constructor: (props, @generated = false) ->
+ constructor: (props, @generated = no, @lhs = no) ->
super()
@objects = @properties = props or []
children: ['properties']
- 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'
- idt = o.indent += TAB
- lastNoncom = @lastNonComment @properties
- answer = []
- answer.push @makeCode "{#{if props.length is 0 then '}' else '\n'}"
- for prop, i in props
- join = if i is props.length - 1
- ''
- else if prop is lastNoncom or prop instanceof Comment
- '\n'
- else
- ',\n'
- indent = if prop instanceof Comment then '' else idt
- if prop instanceof Assign
- if prop.context isnt 'object'
- prop.operatorToken.error "unexpected #{prop.operatorToken.value}"
- if prop.variable instanceof Value and prop.variable.hasProperties()
- prop.variable.error 'invalid object key'
- if prop instanceof Value and prop.this
- prop = new Assign prop.properties[0].name, prop, 'object'
- if prop not instanceof Comment and prop not instanceof Assign
- if prop.shouldCache()
- [key, value] = prop.base.cache o
- key = new PropertyName key.value if key instanceof IdentifierLiteral
- prop = new Assign key, value, 'object'
- else
- prop = new Assign prop, prop, 'object'
- if indent then answer.push @makeCode indent
- answer.push prop.compileToFragments(o, LEVEL_TOP)...
- if join then answer.push @makeCode join
- answer.push @makeCode "\n#{@tab}}" unless props.length is 0
- if @front then @wrapInBraces answer else answer
-
- assigns: (name) ->
- for prop in @properties when prop.assigns name then return yes
- no
+ isAssignable: ->
+ for prop in @properties
@@ -2459,10 +2432,83 @@ is the index of the beginning.
- Arr
+ Check for reserved words.
+ message = isUnassignable prop.unwrapAll().value
+ prop.error message if message
+
+ prop = prop.value if prop instanceof Assign and prop.context is 'object'
+ return no unless prop.isAssignable()
+ yes
+
+ shouldCache: ->
+ not @isAssignable()
+
+ 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'
+ idt = o.indent += TAB
+ lastNoncom = @lastNonComment @properties
+
+ isCompact = yes
+ for prop in @properties
+ if prop instanceof Comment or (prop instanceof Assign and prop.context is 'object')
+ isCompact = no
+
+ answer = []
+ answer.push @makeCode "{#{if isCompact then '' else '\n'}"
+ for prop, i in props
+ join = if i is props.length - 1
+ ''
+ else if isCompact
+ ', '
+ else if prop is lastNoncom or prop instanceof Comment
+ '\n'
+ else
+ ',\n'
+ indent = if isCompact or prop instanceof Comment then '' else idt
+
+ key = if prop instanceof Assign and prop.context is 'object'
+ prop.variable
+ else if prop instanceof Assign
+ prop.operatorToken.error "unexpected #{prop.operatorToken.value}" unless @lhs
+ 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
+ key = new PropertyName key.value if key instanceof IdentifierLiteral
+ 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
+ answer.push prop.compileToFragments(o, LEVEL_TOP)...
+ if join then answer.push @makeCode join
+ answer.push @makeCode "#{if isCompact then '' else "\n#{@tab}"}}"
+ if @front then @wrapInParentheses answer else answer
+
+ assigns: (name) ->
+ for prop in @properties when prop.assigns name then return yes
+ no
+
+ eachName: (iterator) ->
+ for prop in @properties
+ prop = prop.value if prop instanceof Assign and prop.context is 'object'
+ prop = prop.unwrapAll()
+ prop.eachName iterator if prop.eachName?
+
@@ -2472,23 +2518,66 @@ is the index of the beginning.
+ Arr
+
+
+
+
+
+
+
+
+
+
+ ¶
+
An array literal.
exports.Arr = class Arr extends Base
- constructor: (objs) ->
+ constructor: (objs, @lhs = no) ->
super()
@objects = objs or []
children: ['objects']
+ isAssignable: ->
+ return no unless @objects.length
+
+ for obj, i in @objects
+ return no if obj instanceof Splat and i + 1 isnt @objects.length
+ return no unless obj.isAssignable() and (not obj.isAtomic or obj.isAtomic())
+ yes
+
+ shouldCache: ->
+ not @isAssignable()
+
compileNode: (o) ->
return [@makeCode '[]'] unless @objects.length
o.indent += TAB
- answer = []
+ answer = []
+
+
+
+
+
+
+
+
+ ¶
+
+ If this array is the left-hand side of an assignment, all its children
+are too.
+
+
+
+ if @lhs
+ for obj in @objects
+ unwrappedObj = obj.unwrapAll()
+ unwrappedObj.lhs = yes if unwrappedObj instanceof Arr or unwrappedObj instanceof Obj
compiledObjs = (obj.compileToFragments o, LEVEL_LIST for obj in @objects)
for fragments, index in compiledObjs
if index
@@ -2504,16 +2593,21 @@ is the index of the beginning.
assigns: (name) ->
for obj in @objects when obj.assigns name then return yes
- no
+ no
+
+ eachName: (iterator) ->
+ for obj in @objects
+ obj = obj.unwrapAll()
+ obj.eachName iterator
-
+
Class
@@ -2522,11 +2616,11 @@ is the index of the beginning.
-
+
The CoffeeScript class definition.
Initialize a Class with its name, an optional superclass, and a body.
@@ -2547,18 +2641,18 @@ exports.Class = class
-
+
parentName = @parent.base.value if @parent instanceof Value and not @parent.hasProperties()
- @hasNameClash = @name? and @name == parentName
+ @hasNameClash = @name? and @name is parentName
if executableBody or @hasNameClash
@compileNode = @compileClassDeclaration
@@ -2570,17 +2664,17 @@ exports.Class = class
-
+
- result = @wrapInBraces result if not @name? and o.level is LEVEL_TOP
+ result = @wrapInParentheses result if not @name? and o.level is LEVEL_TOP
if @variable
assign = new Assign @variable, new Literal(''), null, { @moduleDeclaration }
@@ -2614,11 +2708,11 @@ exports.Class = class
-
+
Figure out the appropriate name for this class
@@ -2667,11 +2761,11 @@ exports.Class = class
-
+
Try to keep comments with their subsequent assign
@@ -2694,11 +2788,11 @@ exports.Class = class
-
+
Try to keep comments with their subsequent assign
@@ -2717,18 +2811,18 @@ exports.Class = class
@boundMethods.push method.name
method.bound = false
- if initializer.length != expressions.length
+ if initializer.length isnt expressions.length
@body.expressions = (expression.hoist() for expression in initializer)
new Block expressions
-
+
Add an expression to the class initializer
NOTE Currently, only comments, methods and static methods are valid in ES class initializers.
@@ -2748,11 +2842,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.
@@ -2766,11 +2860,11 @@ When additional expressions become valid, this method should be updated to handl
-
+
Returns a configured class initializer method
@@ -2853,7 +2947,7 @@ exports.ExecutableClassBody = if @name != @class.name
+ if @name isnt @class.name
@body.expressions.unshift new Assign (new IdentifierLiteral @name), @class
else
@body.expressions.unshift @class
@@ -2865,11 +2959,11 @@ exports.ExecutableClassBody =
+
Traverse the class’s children and:
@@ -2917,11 +3011,11 @@ exports.ExecutableClassBody =
+ -
Make class/prototype assignments for invalid ES properties
@@ -2939,11 +3033,11 @@ exports.ExecutableClassBody =
+ -
Passthrough
@@ -2956,11 +3050,11 @@ exports.ExecutableClassBody =
+ -
The class scope is not available yet, so return the assignment to update later
@@ -2982,11 +3076,11 @@ exports.ExecutableClassBody =
+ -
Import and Export
@@ -3060,11 +3154,11 @@ exports.ExportDeclaration =
- -
+
-
Prevent exporting an anonymous class; all exported members must be named
@@ -3123,11 +3217,11 @@ exports.ModuleSpecifier = cl
- -
+
-
The name of the variable entering the local scope
@@ -3153,11 +3247,11 @@ exports.ImportSpecifier = cl
- -
+
-
Per the spec, symbols can’t be imported multiple times
(e.g. import { foo, foo } from 'lib'
is invalid)
@@ -3181,11 +3275,11 @@ exports.ExportSpecifier = cl
- -
+
-
Assign
@@ -3194,11 +3288,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.
@@ -3212,6 +3306,8 @@ property of an object – including within object literals.
children: ['variable', 'value']
+ isAssignable: YES
+
isStatement: (o) ->
o?.level is LEVEL_TOP and @context? and (@moduleDeclaration or "?" in @context)
@@ -3229,13 +3325,13 @@ property of an object – including within object literals.
- -
+
-
-
Compile an assignment, delegating to compilePatternMatch
or
+
Compile an assignment, delegating to compileDestructuring
or
compileSplice
if appropriate. Keep track of the name of the base object
we’ve been assigned to, for correct internal references. If the variable
has not been seen yet within the current scope, declare it.
@@ -3243,47 +3339,102 @@ has not been seen yet within the current scope, declare it.
compileNode: (o) ->
- if isValue = @variable instanceof Value
- return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
+ isValue = @variable instanceof Value
+ if isValue
+
+
+
+
+ -
+
+
+
+ ¶
+
+
When compiling @variable
, remember if it is part of a function parameter.
+
+
+
+ @variable.param = @param
+
+
+
+
+ -
+
+
+
+ ¶
+
+
If @variable
is an array or an object, we’re destructuring;
+if it’s also isAssignable()
, the destructuring syntax is supported
+in ES and we can output it as is; otherwise we @compileDestructuring
+and convert this ES-unsupported destructuring into acceptable output.
+
+
+
+ if @variable.isArray() or @variable.isObject()
+
+
+
+
+ -
+
+
+
+ ¶
+
+
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
+destructured variables.
+
+
+
+ @variable.base.lhs = yes
+ return @compileDestructuring o unless @variable.isAssignable()
+
return @compileSplice o if @variable.isSplice()
return @compileConditional o if @context in ['||=', '&&=', '?=']
return @compileSpecialMath o if @context in ['**=', '//=', '%%=']
+
+ unless @context
+ varBase = @variable.unwrapAll()
+ unless varBase.isAssignable()
+ @variable.error "'#{@variable.compile o}' can't be assigned"
+
+ varBase.eachName (name) =>
+ return if name.hasProperties?()
+
+ message = isUnassignable name.value
+ name.error message if message
+
+
+
+
+ -
+
+
+
+ ¶
+
+
moduleDeclaration
can be 'import'
or 'export'
+
+
+
+ @checkAssignability o, name
+ if @moduleDeclaration
+ o.scope.add name.value, @moduleDeclaration
+ else
+ o.scope.find name.value
+
if @value instanceof Code
if @value.isStatic
@value.name = @variable.properties[0]
else if @variable.properties?.length >= 2
[properties..., prototype, name] = @variable.properties
@value.name = name if prototype.name?.value is 'prototype'
- unless @context
- varBase = @variable.unwrapAll()
- unless varBase.isAssignable()
- @variable.error "'#{@variable.compile o}' can't be assigned"
- unless varBase.hasProperties?()
-
-
-
-
- -
-
-
-
- ¶
-
-
moduleDeclaration
can be 'import'
or 'export'
-
-
-
- if @moduleDeclaration
- @checkAssignability o, varBase
- o.scope.add varBase.value, @moduleDeclaration
- else if @param
- o.scope.add varBase.value, 'var'
- else
- @checkAssignability o, varBase
- o.scope.find varBase.value
val = @value.compileToFragments o, LEVEL_LIST
- @variable.front = true if isValue and @variable.base instanceof Obj
compiledName = @variable.compileToFragments o, LEVEL_LIST
if @context is 'object'
@@ -3295,61 +3446,125 @@ has not been seen yet within the current scope, declare it.
compiledName.push @makeCode '"'
return compiledName.concat @makeCode(": "), val
- answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val
- if o.level <= LEVEL_LIST then answer else @wrapInBraces answer
+ 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)
+ @wrapInParentheses answer
+ else
+ answer
+
+
+
+
+ -
+
+
+
+ ¶
Brief implementation of recursive pattern matching, when assigning array or
object literals to a value. Peeks at their properties to assign inner names.
- compilePatternMatch: (o) ->
+ compileDestructuring: (o) ->
top = o.level is LEVEL_TOP
{value} = this
{objects} = @variable.base
- unless olen = objects.length
- code = value.compileToFragments o
- return if o.level >= LEVEL_OP then @wrapInBraces code else code
- [obj] = objects
- if olen is 1 and obj instanceof Expansion
- obj.error 'Destructuring assignment has no target'
- isObject = @variable.isObject()
- if top and olen is 1 and obj not instanceof Splat
+ olen = objects.length
- -
+
-
+
+
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
+
+
+
+ if top and olen is 1 and obj not instanceof Splat
+
+
+
+
+ -
+
+
+
+ ¶
Pick the property straight off the value when there’s just one to pick
(no need to cache the value into a variable).
- defaultValue = null
+ defaultValue = undefined
if obj instanceof Assign and obj.context is 'object'
-
-
+
-
A regular object pattern-match.
@@ -3368,11 +3583,11 @@ object literals to a value. Peeks at their properties to assign inner names.
- -
+
-
A shorthand {a, b, @c} = val
pattern-match.
@@ -3387,11 +3602,11 @@ object literals to a value. Peeks at their properties to assign inner names.
- -
+
-
A regular array pattern-match.
@@ -3403,8 +3618,11 @@ object literals to a value. Peeks at their properties to assign inner names.
value.properties.push new (if acc then Access else Index) idx
message = isUnassignable obj.unwrap().value
obj.error message if message
- value = new Op '?', value, defaultValue if defaultValue
+ if defaultValue
+ defaultValue.isDefaultValue = yes
+ value = new Op '?', value, defaultValue
return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP
+
vvar = value.compileToFragments o, LEVEL_LIST
vvarText = fragmentsToText vvar
assigns = []
@@ -3413,27 +3631,54 @@ object literals to a value. Peeks at their properties to assign inner names.
- -
+
-
-
Make vvar into a simple variable if it isn’t already.
+ 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
+variable if it isn’t already.
if value.unwrap() not instanceof IdentifierLiteral or @variable.assigns(vvarText)
- assigns.push [@makeCode("#{ ref = o.scope.freeVariable 'ref' } = "), vvar...]
+ ref = o.scope.freeVariable 'ref'
+ assigns.push [@makeCode(ref + ' = '), vvar...]
vvar = [@makeCode ref]
- vvarText = ref
- for obj, i in objects
+ vvarText = ref
+
+
+
+
+ -
+
+
+
+ ¶
+
+
And here comes the big loop that handles all of these cases:
+[a, b] = c
+[a..., b] = c
+[..., a, b] = c
+[@a, b] = c
+[a = 1, b] = c
+{a, b} = c
+{@a, b} = c
+{a = 1, b} = c
+etc.
+
+
+
+ for obj, i in objects
idx = i
if not expandedIdx and obj instanceof Splat
name = obj.name.unwrap().value
obj = obj.unwrap()
val = "#{olen} <= #{vvarText}.length ? #{ utility 'slice', o }.call(#{vvarText}, #{i}"
- if rest = olen - i - 1
+ rest = olen - i - 1
+ if rest isnt 0
ivar = o.scope.freeVariable 'i', single: true
val += ", #{ivar} = #{vvarText}.length - #{rest}) : (#{ivar} = #{i}, [])"
else
@@ -3441,7 +3686,8 @@ object literals to a value. Peeks at their properties to assign inner names.
val = new Literal val
expandedIdx = "#{ivar}++"
else if not expandedIdx and obj instanceof Expansion
- if rest = olen - i - 1
+ rest = olen - i - 1
+ if rest isnt 0
if rest is 1
expandedIdx = "#{vvarText}.length - 1"
else
@@ -3453,17 +3699,17 @@ object literals to a value. Peeks at their properties to assign inner names.
else
if obj instanceof Splat or obj instanceof Expansion
obj.error "multiple splats/expansions are disallowed in an assignment"
- defaultValue = null
+ defaultValue = undefined
if obj instanceof Assign and obj.context is 'object'
- -
+
-
A regular object pattern-match.
@@ -3482,11 +3728,11 @@ object literals to a value. Peeks at their properties to assign inner names.
- -
+
-
A shorthand {a, b, @c} = val
pattern-match.
@@ -3501,11 +3747,11 @@ object literals to a value. Peeks at their properties to assign inner names.
- -
+
-
A regular array pattern-match.
@@ -3515,23 +3761,26 @@ object literals to a value. Peeks at their properties to assign inner names.
name = obj.unwrap().value
acc = idx.unwrap() instanceof PropertyName
val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx]
- val = new Op '?', val, defaultValue if defaultValue
+ if defaultValue
+ defaultValue.isDefaultValue = yes
+ val = new Op '?', val, defaultValue
if name?
message = isUnassignable name
obj.error message if message
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
+
assigns.push vvar unless top or @subpattern
fragments = @joinFragmentArrays assigns, ', '
- if o.level < LEVEL_LIST then fragments else @wrapInBraces fragments
+ if o.level < LEVEL_LIST then fragments else @wrapInParentheses fragments
- -
+
-
When compiling a conditional assignment, take care to ensure that the
operands are only evaluated once, even though we have to reference them
@@ -3545,11 +3794,11 @@ more than once.
- -
+
-
Disallow conditional assignment of undefined variables.
@@ -3563,16 +3812,16 @@ more than once.
new If(new Existence(left), right, type: 'if').addElse(new Assign(right, @value, '=')).compileToFragments o
else
fragments = new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o
- if o.level <= LEVEL_LIST then fragments else @wrapInBraces fragments
+ if o.level <= LEVEL_LIST then fragments else @wrapInParentheses fragments
- -
+
-
Convert special math assignment operators like a **= b
to the equivalent
extended form a = a ** b
and then compiles that.
@@ -3586,11 +3835,11 @@ extended form a = a ** b
and then compiles that.
- -
+
-
Compile the assignment from an array splice literal, using JavaScript’s
Array#splice
method.
@@ -3615,16 +3864,19 @@ extended form a = a ** b
and then compiles that.
to = "9e9"
[valDef, valRef] = @value.cache o, LEVEL_LIST
answer = [].concat @makeCode("[].splice.apply(#{name}, [#{fromDecl}, #{to}].concat("), valDef, @makeCode(")), "), valRef
- if o.level > LEVEL_TOP then @wrapInBraces answer else answer
+ if o.level > LEVEL_TOP then @wrapInParentheses answer else answer
+
+ eachName: (iterator) ->
+ @variable.unwrapAll().eachName iterator
- -
+
-
Code
@@ -3633,11 +3885,11 @@ extended form a = a ** b
and then compiles that.
- -
+
-
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
@@ -3675,11 +3927,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
@@ -3714,11 +3966,11 @@ function body.
- -
+
-
Check for duplicate parameters and separate this
assignments
@@ -3739,11 +3991,11 @@ function body.
- -
+
-
Parse the parameters, adding them to the list of parameters to put in the
function definition; and dealing with splats or expansions, including
@@ -3761,11 +4013,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,
@@ -3781,27 +4033,32 @@ so we need not worry about that.
haveSplatParam = yes
if param.splat
- params.push ref = param.asReference o
- splatParamName = fragmentsToText ref.compileNode o
- if param.shouldCache()
- exprs.push new Assign new Value(param.name), ref, '=', param: yes
+ if param.name instanceof Arr
- -
+
-
-
TODO: output destructured parameters as is, and fix destructuring
-of objects with default values to work in this context (see
-Obj.compileNode if prop.context isnt 'object'
).
+ Splat arrays are treated oddly by ES; deal with them the legacy
+way in the function body. TODO: Should this be handled in the
+function parameter list, and if so, how?
- else # `param` is an Expansion
+ splatParamName = o.scope.freeVariable 'arg'
+ params.push ref = new Value new IdentifierLiteral splatParamName
+ exprs.push new Assign new Value(param.name), ref, null, param: yes
+ else
+ params.push ref = param.asReference o
+ splatParamName = fragmentsToText ref.compileNode o
+ if param.shouldCache()
+ exprs.push new Assign new Value(param.name), ref, null, param: yes
+ else # `param` is an Expansion
splatParamName = o.scope.freeVariable 'args'
params.push new Value new IdentifierLiteral splatParamName
@@ -3810,11 +4067,11 @@ Obj.compileNode if prop.context isnt 'object'
).
-
-
+
-
Parse all other parameters; if a splat paramater has not yet been
encountered, add these other parameters to the list to be output in
@@ -3830,11 +4087,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
@@ -3844,20 +4101,20 @@ to the function body assigning it, e.g.
if param.value?
- condition = new Op '==', param, new UndefinedLiteral
- ifTrue = new Assign new Value(param.name), param.value, '=', param: yes
+ condition = new Op '===', param, new UndefinedLiteral
+ ifTrue = new Assign new Value(param.name), param.value, null, param: yes
exprs.push new If condition, ifTrue
else
- exprs.push new Assign new Value(param.name), param.asReference(o), '=', param: yes
+ exprs.push new Assign new Value(param.name), param.asReference(o), null, param: yes
- -
+
-
If this parameter comes before the splat or expansion, it will go
in the function definition parameter list.
@@ -3869,11 +4126,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
@@ -3886,24 +4143,43 @@ so we can’t define its default value in the parameter list.
ref = param.asReference o
else
if param.value? and not param.assignedInBody
- ref = new Assign new Value(param.name), param.value, '='
+ ref = new Assign new Value(param.name), param.value, null, param: yes
else
ref = param
- -
+
-
-
Add this parameter’s reference to the function scope
+ Add this parameter’s reference(s) to the function scope.
- o.scope.parameter fragmentsToText (if param.value? then param else ref).compileToFragments o
+ if param.name instanceof Arr or param.name instanceof Obj
+
+
+
+
+
-
+
+
+
+ ¶
+
+
This parameter is destructured.
+
+
+
+ param.name.lhs = yes
+ param.name.eachName (prop) ->
+ o.scope.parameter prop.value
+ else
+ o.scope.parameter fragmentsToText (if param.value? then param else ref).compileToFragments o
params.push ref
else
paramsAfterSplat.push param
@@ -3911,11 +4187,11 @@ so we can’t define its default value in the parameter list.
- -
+
-
If this parameter had a default value, since it’s no longer in the
function parameter list we need to assign its default value
@@ -3924,18 +4200,18 @@ function parameter list we need to assign its default value
if param.value? and not param.shouldCache()
- condition = new Op '==', param, new UndefinedLiteral
- ifTrue = new Assign new Value(param.name), param.value, '='
+ condition = new Op '===', param, new UndefinedLiteral
+ ifTrue = new Assign new Value(param.name), param.value
exprs.push new If condition, ifTrue
- -
+
-
Add this parameter to the scope, since it wouldn’t have been added yet since it was skipped earlier.
@@ -3946,11 +4222,11 @@ function parameter list we need to assign its default value
- -
+
-
If there were parameters after the splat or expansion parameter, those
parameters need to be assigned in the body of the function.
@@ -3962,11 +4238,11 @@ parameters need to be assigned in the body of the function.
- -
+
-
Create a destructured assignment, e.g. [a, b, c] = [args..., b, c]
@@ -3979,11 +4255,11 @@ parameters need to be assigned in the body of the function.
- -
+
-
Add new expressions to the function body
@@ -3997,11 +4273,11 @@ parameters need to be assigned in the body of the function.
- -
+
-
Assemble the output
@@ -4027,11 +4303,11 @@ parameters need to be assigned in the body of the function.
- -
+
-
We need to compile the body before method names to ensure super references are handled
@@ -4053,7 +4329,7 @@ parameters need to be assigned in the body of the function.
answer.push @makeCode '}'
return [@makeCode(@tab), answer...] if @isMethod
- if @front or (o.level >= LEVEL_ACCESS) then @wrapInBraces answer else answer
+ if @front or (o.level >= LEVEL_ACCESS) then @wrapInParentheses answer else answer
eachParamName: (iterator) ->
param.eachName iterator for param in @params
@@ -4061,11 +4337,11 @@ parameters need to be assigned in the body of the function.
- -
+
-
Short-circuit traverseChildren
method to prevent it from crossing scope
boundaries unless crossScope
is true
.
@@ -4078,11 +4354,11 @@ boundaries unless crossScope
is true
.
- -
+
-
Short-circuit replaceInContext
method to prevent it from crossing context boundaries. Bound
functions have the same context.
@@ -4105,7 +4381,7 @@ functions have the same context.
superCall.error "'super' is only allowed in derived class constructors" if @ctor is 'base'
superCall.expressions = thisAssignments
- haveThisParam = thisAssignments.length and thisAssignments.length != @thisAssignments?.length
+ haveThisParam = thisAssignments.length and thisAssignments.length isnt @thisAssignments?.length
if @ctor is 'derived' and not seenSuper and haveThisParam
param = thisAssignments[0].variable
param.error "Can't use @params in derived class constructors without calling super"
@@ -4115,11 +4391,11 @@ functions have the same context.
- -
+
-
Find all super calls in the given context node
Returns true
if iterator
is called
@@ -4139,11 +4415,11 @@ Returns true
if iterator
is called
- -
+
-
super
has the same target in bound (arrow) functions, so check them too
@@ -4156,11 +4432,11 @@ Returns true
if iterator
is called
- -
+
-
Param
@@ -4169,11 +4445,11 @@ Returns true
if iterator
is called
- -
+
-
A parameter in a function definition. Beyond a typical JavaScript parameter,
these parameters can also attach themselves to the context of the function,
@@ -4215,11 +4491,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
@@ -4236,11 +4512,11 @@ to that name.
- -
+
-
- simple literals
foo
@@ -4253,11 +4529,11 @@ to that name.
- -
+
-
- at-params
@foo
@@ -4271,11 +4547,11 @@ to that name.
- -
+
-
- destructured parameter with default value
@@ -4289,11 +4565,11 @@ to that name.
- -
+
-