converted nodes.coffee. a big step
This commit is contained in:
parent
bfc7704ca1
commit
5c85bf22bb
|
@ -317,7 +317,7 @@
|
|||
exports.ValueNode = (function() {
|
||||
ValueNode = function(base, properties) {
|
||||
this.base = base;
|
||||
this.properties = (properties || []);
|
||||
this.properties = properties || [];
|
||||
return this;
|
||||
};
|
||||
__extends(ValueNode, BaseNode);
|
||||
|
|
280
src/lexer.coffee
280
src/lexer.coffee
|
@ -93,13 +93,13 @@ exports.Lexer = class Lexer
|
|||
tag = 'STRING'
|
||||
id = "'$id'"
|
||||
if forcedIdentifier is 'accessor'
|
||||
close_index: true
|
||||
close_index = true
|
||||
@tokens.pop() if @tag() isnt '@'
|
||||
@token 'INDEX_START', '['
|
||||
else if include(RESERVED, id)
|
||||
@identifierError id
|
||||
unless forcedIdentifier
|
||||
tag: id: CONVERSIONS[id] if include COFFEE_ALIASES, id
|
||||
tag = id = CONVERSIONS[id] if include COFFEE_ALIASES, id
|
||||
return @tagHalfAssignment tag if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
|
||||
@token tag, id
|
||||
@token ']', ']' if close_index
|
||||
|
@ -108,9 +108,9 @@ exports.Lexer = class Lexer
|
|||
# Matches numbers, including decimals, hex, and exponential notation.
|
||||
# Be careful not to interfere with ranges-in-progress.
|
||||
numberToken: ->
|
||||
return false unless number: @match NUMBER, 1
|
||||
return false unless number = @match NUMBER, 1
|
||||
return false if @tag() is '.' and starts number, '.'
|
||||
@i: + number.length
|
||||
@i += number.length
|
||||
@token 'NUMBER', number
|
||||
true
|
||||
|
||||
|
@ -118,32 +118,32 @@ exports.Lexer = class Lexer
|
|||
# are balanced within the string's contents, and within nested interpolations.
|
||||
stringToken: ->
|
||||
return false unless starts(@chunk, '"') or starts(@chunk, "'")
|
||||
return false unless string:
|
||||
return false unless string =
|
||||
@balancedToken(['"', '"'], ['${', '}']) or
|
||||
@balancedToken ["'", "'"]
|
||||
@interpolateString string.replace STRING_NEWLINES, " \\\n"
|
||||
@line: + count string, "\n"
|
||||
@i: + string.length
|
||||
@line += count string, "\n"
|
||||
@i += string.length
|
||||
true
|
||||
|
||||
# Matches heredocs, adjusting indentation to the correct level, as heredocs
|
||||
# preserve whitespace, but ignore indentation to the left.
|
||||
heredocToken: ->
|
||||
return false unless match: @chunk.match(HEREDOC)
|
||||
quote: match[1].substr 0, 1
|
||||
doc: @sanitizeHeredoc match[2] or match[4], {quote}
|
||||
return false unless match = @chunk.match(HEREDOC)
|
||||
quote = match[1].substr 0, 1
|
||||
doc = @sanitizeHeredoc match[2] or match[4], {quote}
|
||||
@interpolateString "$quote$doc$quote", {heredoc: yes}
|
||||
@line: + count match[1], "\n"
|
||||
@i: + match[1].length
|
||||
@line += count match[1], "\n"
|
||||
@i += match[1].length
|
||||
true
|
||||
|
||||
# Matches and conumes comments.
|
||||
commentToken: ->
|
||||
return false unless match: @chunk.match(COMMENT)
|
||||
@line: + count match[1], "\n"
|
||||
@i: + match[1].length
|
||||
return false unless match = @chunk.match(COMMENT)
|
||||
@line += count match[1], "\n"
|
||||
@i += match[1].length
|
||||
if match[2]
|
||||
comment: @sanitizeHeredoc match[2], {herecomment: true}
|
||||
comment = @sanitizeHeredoc match[2], {herecomment: true}
|
||||
@token 'HERECOMMENT', comment.split MULTILINER
|
||||
@token 'TERMINATOR', '\n'
|
||||
true
|
||||
|
@ -151,9 +151,9 @@ exports.Lexer = class Lexer
|
|||
# Matches JavaScript interpolated directly into the source via backticks.
|
||||
jsToken: ->
|
||||
return false unless starts @chunk, '`'
|
||||
return false unless script: @balancedToken ['`', '`']
|
||||
return false unless script = @balancedToken ['`', '`']
|
||||
@token 'JS', script.replace JS_CLEANER, ''
|
||||
@i: + script.length
|
||||
@i += script.length
|
||||
true
|
||||
|
||||
# Matches regular expression literals. Lexing regular expressions is difficult
|
||||
|
@ -163,19 +163,19 @@ exports.Lexer = class Lexer
|
|||
regexToken: ->
|
||||
return false unless @chunk.match REGEX_START
|
||||
return false if include NOT_REGEX, @tag()
|
||||
return false unless regex: @balancedToken ['/', '/']
|
||||
return false unless end: @chunk.substr(regex.length).match REGEX_END
|
||||
regex: + flags: end[2] if end[2]
|
||||
return false unless regex = @balancedToken ['/', '/']
|
||||
return false unless end = @chunk.substr(regex.length).match REGEX_END
|
||||
regex += flags = end[2] if end[2]
|
||||
if regex.match REGEX_INTERPOLATION
|
||||
str: regex.substring(1).split('/')[0]
|
||||
str: str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped
|
||||
@tokens: @tokens.concat [['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]
|
||||
str = regex.substring(1).split('/')[0]
|
||||
str = str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped
|
||||
@tokens = @tokens.concat [['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]
|
||||
@interpolateString "\"$str\"", {escapeQuotes: yes}
|
||||
@tokens.splice @tokens.length, 0, [',', ','], ['STRING', "\"$flags\""] if flags
|
||||
@tokens.splice @tokens.length, 0, [')', ')'], [')', ')']
|
||||
else
|
||||
@token 'REGEX', regex
|
||||
@i: + regex.length
|
||||
@i += regex.length
|
||||
true
|
||||
|
||||
# Matches a token in which which the passed delimiter pairs must be correctly
|
||||
|
@ -194,24 +194,24 @@ exports.Lexer = class Lexer
|
|||
# Keeps track of the level of indentation, because a single outdent token
|
||||
# can close multiple indents, so we need to know how far in we happen to be.
|
||||
lineToken: ->
|
||||
return false unless indent: @match MULTI_DENT, 1
|
||||
@line: + count indent, "\n"
|
||||
@i : + indent.length
|
||||
prev: @prev(2)
|
||||
size: indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length
|
||||
nextCharacter: @match NEXT_CHARACTER, 1
|
||||
noNewlines: nextCharacter is '.' or nextCharacter is ',' or @unfinished()
|
||||
return false unless indent = @match MULTI_DENT, 1
|
||||
@line += count indent, "\n"
|
||||
@i += indent.length
|
||||
prev = @prev(2)
|
||||
size = indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length
|
||||
nextCharacter = @match NEXT_CHARACTER, 1
|
||||
noNewlines = nextCharacter is '.' or nextCharacter is ',' or @unfinished()
|
||||
if size is @indent
|
||||
return @suppressNewlines() if noNewlines
|
||||
return @newlineToken indent
|
||||
else if size > @indent
|
||||
return @suppressNewlines() if noNewlines
|
||||
diff: size - @indent
|
||||
diff = size - @indent
|
||||
@token 'INDENT', diff
|
||||
@indents.push diff
|
||||
else
|
||||
@outdentToken @indent - size, noNewlines
|
||||
@indent: size
|
||||
@indent = size
|
||||
true
|
||||
|
||||
# Record an outdent token or multiple tokens, if we happen to be moving back
|
||||
|
@ -219,22 +219,22 @@ exports.Lexer = class Lexer
|
|||
outdentToken: (moveOut, noNewlines) ->
|
||||
if moveOut > -@outdebt
|
||||
while moveOut > 0 and @indents.length
|
||||
lastIndent: @indents.pop()
|
||||
lastIndent = @indents.pop()
|
||||
@token 'OUTDENT', lastIndent
|
||||
moveOut: - lastIndent
|
||||
moveOut -= lastIndent
|
||||
else
|
||||
@outdebt: + moveOut
|
||||
@outdebt: moveOut unless noNewlines
|
||||
@outdebt += moveOut
|
||||
@outdebt = moveOut unless noNewlines
|
||||
@token 'TERMINATOR', "\n" unless @tag() is 'TERMINATOR' or noNewlines
|
||||
true
|
||||
|
||||
# Matches and consumes non-meaningful whitespace. Tag the previous token
|
||||
# as being "spaced", because there are some cases where it makes a difference.
|
||||
whitespaceToken: ->
|
||||
return false unless space: @match WHITESPACE, 1
|
||||
prev: @prev()
|
||||
prev.spaced: true if prev
|
||||
@i: + space.length
|
||||
return false unless space = @match WHITESPACE, 1
|
||||
prev = @prev()
|
||||
prev.spaced = true if prev
|
||||
@i += space.length
|
||||
true
|
||||
|
||||
# Generate a newline token. Consecutive newlines get merged together.
|
||||
|
@ -254,28 +254,28 @@ exports.Lexer = class Lexer
|
|||
# here. `;` and newlines are both treated as a `TERMINATOR`, we distinguish
|
||||
# parentheses that indicate a method call from regular parentheses, and so on.
|
||||
literalToken: ->
|
||||
match: @chunk.match OPERATOR
|
||||
value: match and match[1]
|
||||
space: match and match[2]
|
||||
match = @chunk.match OPERATOR
|
||||
value = match and match[1]
|
||||
space = match and match[2]
|
||||
@tagParameters() if value and value.match CODE
|
||||
value: or @chunk.substr 0, 1
|
||||
prevSpaced: @prev() and @prev().spaced
|
||||
tag: value
|
||||
value = or @chunk.substr 0, 1
|
||||
prevSpaced = @prev() and @prev().spaced
|
||||
tag = value
|
||||
if value.match ASSIGNMENT
|
||||
tag: 'ASSIGN'
|
||||
tag = 'ASSIGN'
|
||||
@assignmentError() if include JS_FORBIDDEN, @value
|
||||
else if value is ';'
|
||||
tag: 'TERMINATOR'
|
||||
tag = 'TERMINATOR'
|
||||
else if value is '?' and prevSpaced
|
||||
tag: 'OP?'
|
||||
tag = 'OP?'
|
||||
else if include(CALLABLE, @tag()) and not prevSpaced
|
||||
if value is '('
|
||||
tag: 'CALL_START'
|
||||
tag = 'CALL_START'
|
||||
else if value is '['
|
||||
tag: 'INDEX_START'
|
||||
tag = 'INDEX_START'
|
||||
@tag 1, 'INDEX_SOAK' if @tag() is '?'
|
||||
@tag 1, 'INDEX_PROTO' if @tag() is '::'
|
||||
@i: + value.length
|
||||
@i += value.length
|
||||
return @tagHalfAssignment tag if space and prevSpaced and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
|
||||
@token tag, value
|
||||
true
|
||||
|
@ -287,8 +287,8 @@ exports.Lexer = class Lexer
|
|||
# if it's a special kind of accessor. Return `true` if any type of accessor
|
||||
# is the previous token.
|
||||
tagAccessor: ->
|
||||
return false if (not prev: @prev()) or (prev and prev.spaced)
|
||||
accessor: if prev[1] is '::'
|
||||
return false if (not prev = @prev()) or (prev and prev.spaced)
|
||||
accessor = if prev[1] is '::'
|
||||
@tag 1, 'PROTOTYPE_ACCESS'
|
||||
else if prev[1] is '.' and not (@value(2) is '.')
|
||||
if @tag(2) is '?'
|
||||
|
@ -303,18 +303,18 @@ exports.Lexer = class Lexer
|
|||
# Sanitize a heredoc or herecomment by escaping internal double quotes and
|
||||
# erasing all external indentation on the left-hand side.
|
||||
sanitizeHeredoc: (doc, options) ->
|
||||
while match: HEREDOC_INDENT.exec doc
|
||||
attempt: if match[2]? then match[2] else match[3]
|
||||
indent: attempt if not indent or attempt.length < indent.length
|
||||
doc: doc.replace(new RegExp("^" +indent, 'gm'), '')
|
||||
while match = HEREDOC_INDENT.exec doc
|
||||
attempt = if match[2]? then match[2] else match[3]
|
||||
indent = attempt if not indent or attempt.length < indent.length
|
||||
doc = doc.replace(new RegExp("^" +indent, 'gm'), '')
|
||||
return doc if options.herecomment
|
||||
doc.replace(MULTILINER, "\\n")
|
||||
.replace(new RegExp(options.quote, 'g'), "\\$options.quote")
|
||||
|
||||
# Tag a half assignment.
|
||||
tagHalfAssignment: (tag) ->
|
||||
tag: '?' if tag is 'OP?'
|
||||
last: @tokens.pop()
|
||||
tag = '?' if tag is 'OP?'
|
||||
last = @tokens.pop()
|
||||
@tokens.push ["$tag=", "$tag=", last[2]]
|
||||
true
|
||||
|
||||
|
@ -323,15 +323,15 @@ exports.Lexer = class Lexer
|
|||
# parameters specially in order to make things easier for the parser.
|
||||
tagParameters: ->
|
||||
return if @tag() isnt ')'
|
||||
i: 0
|
||||
i = 0
|
||||
loop
|
||||
i: + 1
|
||||
tok: @prev i
|
||||
i = + 1
|
||||
tok = @prev i
|
||||
return if not tok
|
||||
switch tok[0]
|
||||
when 'IDENTIFIER' then tok[0]: 'PARAM'
|
||||
when ')' then tok[0]: 'PARAM_END'
|
||||
when '(', 'CALL_START' then return tok[0]: 'PARAM_START'
|
||||
when 'IDENTIFIER' then tok[0] = 'PARAM'
|
||||
when ')' then tok[0] = 'PARAM_END'
|
||||
when '(', 'CALL_START' then return tok[0] = 'PARAM_START'
|
||||
true
|
||||
|
||||
# Close up all remaining open blocks at the end of the file.
|
||||
|
@ -353,27 +353,27 @@ exports.Lexer = class Lexer
|
|||
# contents of the string. This method allows us to have strings within
|
||||
# interpolations within strings, ad infinitum.
|
||||
balancedString: (str, delimited, options) ->
|
||||
options: or {}
|
||||
slash: delimited[0][0] is '/'
|
||||
levels: []
|
||||
i: 0
|
||||
options = or {}
|
||||
slash = delimited[0][0] is '/'
|
||||
levels = []
|
||||
i = 0
|
||||
while i < str.length
|
||||
if levels.length and starts str, '\\', i
|
||||
i: + 1
|
||||
i = + 1
|
||||
else
|
||||
for pair in delimited
|
||||
[open, close]: pair
|
||||
[open, close] = pair
|
||||
if levels.length and starts(str, close, i) and levels[levels.length - 1] is pair
|
||||
levels.pop()
|
||||
i: + close.length - 1
|
||||
i: + 1 unless levels.length
|
||||
i = + close.length - 1
|
||||
i = + 1 unless levels.length
|
||||
break
|
||||
else if starts str, open, i
|
||||
levels.push(pair)
|
||||
i: + open.length - 1
|
||||
i = + open.length - 1
|
||||
break
|
||||
break if not levels.length or slash and starts str, '\n', i
|
||||
i: + 1
|
||||
i = + 1
|
||||
if levels.length
|
||||
return false if slash
|
||||
throw new Error "SyntaxError: Unterminated ${levels.pop()[0]} starting on line ${@line + 1}"
|
||||
|
@ -390,48 +390,48 @@ exports.Lexer = class Lexer
|
|||
# new Lexer, tokenize the interpolated contents, and merge them into the
|
||||
# token stream.
|
||||
interpolateString: (str, options) ->
|
||||
options: or {}
|
||||
options = or {}
|
||||
if str.length < 3 or not starts str, '"'
|
||||
@token 'STRING', str
|
||||
else
|
||||
lexer: new Lexer
|
||||
tokens: []
|
||||
quote: str.substring 0, 1
|
||||
[i, pi]: [1, 1]
|
||||
lexer = new Lexer
|
||||
tokens = []
|
||||
quote = str.substring 0, 1
|
||||
[i, pi] = [1, 1]
|
||||
while i < str.length - 1
|
||||
if starts str, '\\', i
|
||||
i: + 1
|
||||
else if match: str.substring(i).match INTERPOLATION
|
||||
[group, interp]: match
|
||||
interp: "this.${ interp.substring(1) }" if starts interp, '@'
|
||||
i = + 1
|
||||
else if match = str.substring(i).match INTERPOLATION
|
||||
[group, interp] = match
|
||||
interp = "this.${ interp.substring(1) }" if starts interp, '@'
|
||||
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
|
||||
tokens.push ['IDENTIFIER', interp]
|
||||
i: + group.length - 1
|
||||
pi: i + 1
|
||||
else if (expr: @balancedString str.substring(i), [['${', '}']])
|
||||
i = + group.length - 1
|
||||
pi = i + 1
|
||||
else if (expr = @balancedString str.substring(i), [['${', '}']])
|
||||
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
|
||||
inner: expr.substring(2, expr.length - 1)
|
||||
inner = expr.substring(2, expr.length - 1)
|
||||
if inner.length
|
||||
inner: inner.replace new RegExp('\\\\' + quote, 'g'), quote if options.heredoc
|
||||
nested: lexer.tokenize "($inner)", {line: @line}
|
||||
(tok[0]: ')') for tok, idx in nested when tok[0] is 'CALL_END'
|
||||
inner = inner.replace new RegExp('\\\\' + quote, 'g'), quote if options.heredoc
|
||||
nested = lexer.tokenize "($inner)", {line: @line}
|
||||
(tok[0] = ')') for tok, idx in nested when tok[0] is 'CALL_END'
|
||||
nested.pop()
|
||||
tokens.push ['TOKENS', nested]
|
||||
else
|
||||
tokens.push ['STRING', "$quote$quote"]
|
||||
i: + expr.length - 1
|
||||
pi: i + 1
|
||||
i: + 1
|
||||
i = + expr.length - 1
|
||||
pi = i + 1
|
||||
i = + 1
|
||||
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i and pi < str.length - 1
|
||||
tokens.unshift ['STRING', '""'] unless tokens[0][0] is 'STRING'
|
||||
interpolated: tokens.length > 1
|
||||
interpolated = tokens.length > 1
|
||||
@token '(', '(' if interpolated
|
||||
for token, i in tokens
|
||||
[tag, value]: token
|
||||
[tag, value] = token
|
||||
if tag is 'TOKENS'
|
||||
@tokens: @tokens.concat value
|
||||
@tokens = @tokens.concat value
|
||||
else if tag is 'STRING' and options.escapeQuotes
|
||||
escaped: value.substring(1, value.length - 1).replace(/"/g, '\\"')
|
||||
escaped = value.substring(1, value.length - 1).replace(/"/g, '\\"')
|
||||
@token tag, "\"$escaped\""
|
||||
else
|
||||
@token tag, value
|
||||
|
@ -448,14 +448,14 @@ exports.Lexer = class Lexer
|
|||
|
||||
# Peek at a tag in the current token stream.
|
||||
tag: (index, newTag) ->
|
||||
return unless tok: @prev index
|
||||
return tok[0]: newTag if newTag?
|
||||
return unless tok = @prev index
|
||||
return tok[0] = newTag if newTag?
|
||||
tok[0]
|
||||
|
||||
# Peek at a value in the current token stream.
|
||||
value: (index, val) ->
|
||||
return unless tok: @prev index
|
||||
return tok[1]: val if val?
|
||||
return unless tok = @prev index
|
||||
return tok[1] = val if val?
|
||||
tok[1]
|
||||
|
||||
# Peek at a previous token, entire.
|
||||
|
@ -465,12 +465,12 @@ exports.Lexer = class Lexer
|
|||
# Attempt to match a string against the current chunk, returning the indexed
|
||||
# match if successful, and `false` otherwise.
|
||||
match: (regex, index) ->
|
||||
return false unless m: @chunk.match regex
|
||||
return false unless m = @chunk.match regex
|
||||
if m then m[index] else false
|
||||
|
||||
# Are we in the midst of an unfinished expression?
|
||||
unfinished: ->
|
||||
prev: @prev(2)
|
||||
prev = @prev(2)
|
||||
@value() and @value().match and @value().match(NO_NEWLINE) and
|
||||
prev and (prev[0] isnt '.') and not @value().match(CODE)
|
||||
|
||||
|
@ -478,7 +478,7 @@ exports.Lexer = class Lexer
|
|||
# ---------
|
||||
|
||||
# Keywords that CoffeeScript shares in common with JavaScript.
|
||||
JS_KEYWORDS: [
|
||||
JS_KEYWORDS = [
|
||||
"if", "else",
|
||||
"true", "false",
|
||||
"new", "return",
|
||||
|
@ -492,8 +492,8 @@ JS_KEYWORDS: [
|
|||
|
||||
# CoffeeScript-only keywords, which we're more relaxed about allowing. They can't
|
||||
# be used standalone, but you can reference them as an attached property.
|
||||
COFFEE_ALIASES: ["and", "or", "is", "isnt", "not"]
|
||||
COFFEE_KEYWORDS: COFFEE_ALIASES.concat [
|
||||
COFFEE_ALIASES = ["and", "or", "is", "isnt", "not"]
|
||||
COFFEE_KEYWORDS = COFFEE_ALIASES.concat [
|
||||
"then", "unless", "until", "loop",
|
||||
"yes", "no", "on", "off",
|
||||
"of", "by", "where", "when"
|
||||
|
@ -502,7 +502,7 @@ COFFEE_KEYWORDS: COFFEE_ALIASES.concat [
|
|||
# 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,
|
||||
# to avoid having a JavaScript error at runtime.
|
||||
RESERVED: [
|
||||
RESERVED = [
|
||||
"case", "default", "do", "function", "var", "void", "with",
|
||||
"const", "let", "enum", "export", "import", "native",
|
||||
"__hasProp", "__extends", "__slice"
|
||||
|
@ -510,36 +510,36 @@ RESERVED: [
|
|||
|
||||
# The superset of both JavaScript keywords and reserved words, none of which may
|
||||
# be used as identifiers or properties.
|
||||
JS_FORBIDDEN: JS_KEYWORDS.concat RESERVED
|
||||
JS_FORBIDDEN = JS_KEYWORDS.concat RESERVED
|
||||
|
||||
# Token matching regexes.
|
||||
IDENTIFIER : /^([a-zA-Z\$_](\w|\$)*)/
|
||||
NUMBER : /^(((\b0(x|X)[0-9a-fA-F]+)|((\b[0-9]+(\.[0-9]+)?|\.[0-9]+)(e[+\-]?[0-9]+)?)))\b/i
|
||||
HEREDOC : /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\n?([ \t]*)'{3})/
|
||||
INTERPOLATION : /^\$([a-zA-Z_@]\w*(\.\w+)*)/
|
||||
OPERATOR : /^(-[\-=>]?|\+[+=]?|[*&|\/%=<>:!?]+)([ \t]*)/
|
||||
WHITESPACE : /^([ \t]+)/
|
||||
COMMENT : /^(\s*#{3}(?!#)[ \t]*\n+([\s\S]*?)[ \t]*\n+[ \t]*#{3}|(\s*#(?!##[^#])[^\n]*)+)/
|
||||
CODE : /^((-|=)>)/
|
||||
MULTI_DENT : /^((\n([ \t]*))+)(\.)?/
|
||||
LAST_DENTS : /\n([ \t]*)/g
|
||||
LAST_DENT : /\n([ \t]*)/
|
||||
ASSIGNMENT : /^[:=]$/
|
||||
IDENTIFIER = /^([a-zA-Z\$_](\w|\$)*)/
|
||||
NUMBER = /^(((\b0(x|X)[0-9a-fA-F]+)|((\b[0-9]+(\.[0-9]+)?|\.[0-9]+)(e[+\-]?[0-9]+)?)))\b/i
|
||||
HEREDOC = /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\n?([ \t]*)'{3})/
|
||||
INTERPOLATION = /^\$([a-zA-Z_@]\w*(\.\w+)*)/
|
||||
OPERATOR = /^(-[\-=>]?|\+[+=]?|[*&|\/%=<>:!?]+)([ \t]*)/
|
||||
WHITESPACE = /^([ \t]+)/
|
||||
COMMENT = /^(\s*#{3}(?!#)[ \t]*\n+([\s\S]*?)[ \t]*\n+[ \t]*#{3}|(\s*#(?!##[^#])[^\n]*)+)/
|
||||
CODE = /^((-|=)>)/
|
||||
MULTI_DENT = /^((\n([ \t]*))+)(\.)?/
|
||||
LAST_DENTS = /\n([ \t]*)/g
|
||||
LAST_DENT = /\n([ \t]*)/
|
||||
ASSIGNMENT = /^[:=]$/
|
||||
|
||||
# Regex-matching-regexes.
|
||||
REGEX_START : /^\/[^\/ ]/
|
||||
REGEX_INTERPOLATION: /([^\\]\$[a-zA-Z_@]|[^\\]\$\{.*[^\\]\})/
|
||||
REGEX_END : /^(([imgy]{1,4})\b|\W|$)/
|
||||
REGEX_ESCAPE : /\\[^\$]/g
|
||||
REGEX_START = /^\/[^\/ ]/
|
||||
REGEX_INTERPOLATION= /([^\\]\$[a-zA-Z_@]|[^\\]\$\{.*[^\\]\})/
|
||||
REGEX_END = /^(([imgy]{1,4})\b|\W|$)/
|
||||
REGEX_ESCAPE = /\\[^\$]/g
|
||||
|
||||
# Token cleaning regexes.
|
||||
JS_CLEANER : /(^`|`$)/g
|
||||
MULTILINER : /\n/g
|
||||
STRING_NEWLINES : /\n[ \t]*/g
|
||||
NO_NEWLINE : /^([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/
|
||||
HEREDOC_INDENT : /(\n+([ \t]*)|^([ \t]+))/g
|
||||
ASSIGNED : /^([a-zA-Z\$_]\w*[ \t]*?[:=][^=])/
|
||||
NEXT_CHARACTER : /^\s*(\S)/
|
||||
JS_CLEANER = /(^`|`$)/g
|
||||
MULTILINER = /\n/g
|
||||
STRING_NEWLINES = /\n[ \t]*/g
|
||||
NO_NEWLINE = /^([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/
|
||||
HEREDOC_INDENT = /(\n+([ \t]*)|^([ \t]+))/g
|
||||
ASSIGNED = /^([a-zA-Z\$_]\w*[ \t]*?[:=][^=])/
|
||||
NEXT_CHARACTER = /^\s*(\S)/
|
||||
|
||||
# Tokens which a regular expression will never immediately follow, but which
|
||||
# a division operator might.
|
||||
|
@ -547,25 +547,25 @@ NEXT_CHARACTER : /^\s*(\S)/
|
|||
# See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
|
||||
#
|
||||
# Our list is shorter, due to sans-parentheses method calls.
|
||||
NOT_REGEX: [
|
||||
NOT_REGEX = [
|
||||
'NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE', ']'
|
||||
]
|
||||
|
||||
# Tokens which could legitimately be invoked or indexed. A opening
|
||||
# parentheses or bracket following these tokens will be recorded as the start
|
||||
# of a function invocation or indexing operation.
|
||||
CALLABLE: ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::']
|
||||
CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::']
|
||||
|
||||
# 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
|
||||
# avoid an ambiguity in the grammar.
|
||||
LINE_BREAK: ['INDENT', 'OUTDENT', 'TERMINATOR']
|
||||
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']
|
||||
|
||||
# Half-assignments...
|
||||
HALF_ASSIGNMENTS: ['-', '+', '/', '*', '%', '||', '&&', '?', 'OP?']
|
||||
HALF_ASSIGNMENTS = ['-', '+', '/', '*', '%', '||', '&&', '?', 'OP?']
|
||||
|
||||
# Conversions from CoffeeScript operators into JavaScript ones.
|
||||
CONVERSIONS: {
|
||||
CONVERSIONS = {
|
||||
'and': '&&'
|
||||
'or': '||'
|
||||
'is': '=='
|
||||
|
|
896
src/nodes.coffee
896
src/nodes.coffee
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue