mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
waypoint commit with both # and $ performing interpolation. Issue #544
This commit is contained in:
parent
9c8a22832c
commit
d624310be1
12 changed files with 200 additions and 200 deletions
|
@ -129,7 +129,7 @@
|
|||
if (!(starts(this.chunk, '"') || starts(this.chunk, "'"))) {
|
||||
return false;
|
||||
}
|
||||
if (!(string = this.balancedToken(['"', '"'], ['${', '}']) || this.balancedToken(["'", "'"]))) {
|
||||
if (!(string = this.balancedToken(['"', '"'], ['${', '}'], ['#{', '}']) || this.balancedToken(["'", "'"]))) {
|
||||
return false;
|
||||
}
|
||||
this.interpolateString(string.replace(STRING_NEWLINES, " \\\n"));
|
||||
|
@ -471,7 +471,7 @@
|
|||
tokens.push(['IDENTIFIER', interp]);
|
||||
i += group.length - 1;
|
||||
pi = i + 1;
|
||||
} else if ((expr = this.balancedString(str.substring(i), [['${', '}']]))) {
|
||||
} else if ((expr = this.balancedString(str.substring(i), [['${', '}'], ['#{', '}']]))) {
|
||||
if (pi < i) {
|
||||
tokens.push(['STRING', ("" + quote + (str.substring(pi, i)) + quote)]);
|
||||
}
|
||||
|
@ -580,7 +580,7 @@
|
|||
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+)*)/;
|
||||
INTERPOLATION = /^[$#]([a-zA-Z_@]\w*(\.\w+)*)/;
|
||||
OPERATOR = /^(-[\-=>]?|\+[+=]?|[*&|\/%=<>:!?]+)([ \t]*)/;
|
||||
WHITESPACE = /^([ \t]+)/;
|
||||
COMMENT = /^(\s*#{3}(?!#)[ \t]*\n+([\s\S]*?)[ \t]*\n+[ \t]*#{3}|(\s*#(?!##[^#])[^\n]*)+)/;
|
||||
|
|
|
@ -45,7 +45,7 @@ helpers.extend global,
|
|||
# If no tasks are passed, print the help screen.
|
||||
exports.run = ->
|
||||
path.exists 'Cakefile', (exists) ->
|
||||
throw new Error("Cakefile not found in ${process.cwd()}") unless exists
|
||||
throw new Error("Cakefile not found in #{process.cwd()}") unless exists
|
||||
args = process.argv[2...process.argv.length]
|
||||
CoffeeScript.run fs.readFileSync('Cakefile').toString(), source: 'Cakefile'
|
||||
oparse = new optparse.OptionParser switches
|
||||
|
@ -59,11 +59,11 @@ printTasks = ->
|
|||
for all name, task of tasks
|
||||
spaces = 20 - name.length
|
||||
spaces = if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
|
||||
desc = if task.description then "# $task.description" else ''
|
||||
puts "cake $name$spaces $desc"
|
||||
desc = if task.description then "# #task.description" else ''
|
||||
puts "cake #name#spaces #desc"
|
||||
puts oparse.help() if switches.length
|
||||
|
||||
# Print an error and exit when attempting to all an undefined task.
|
||||
missingTask = (task) ->
|
||||
puts "No such task: \"$task\""
|
||||
puts "No such task: \"#task\""
|
||||
process.exit 1
|
||||
|
|
|
@ -34,7 +34,7 @@ exports.compile = compile = (code, options) ->
|
|||
try
|
||||
(parser.parse lexer.tokenize code).compile options
|
||||
catch err
|
||||
err.message = "In $options.source, $err.message" if options.source
|
||||
err.message = "In #options.source, #err.message" if options.source
|
||||
throw err
|
||||
|
||||
# Tokenize a string of CoffeeScript code, and return the array of tokens.
|
||||
|
|
|
@ -71,7 +71,7 @@ compileScripts = ->
|
|||
base = source
|
||||
compile = (source, topLevel) ->
|
||||
path.exists source, (exists) ->
|
||||
throw new Error "File not found: $source" unless exists
|
||||
throw new Error "File not found: #source" unless exists
|
||||
fs.stat source, (err, stats) ->
|
||||
if stats.isDirectory()
|
||||
fs.readdir source, (err, files) ->
|
||||
|
@ -130,9 +130,9 @@ writeJs = (source, js, base) ->
|
|||
jsPath = path.join dir, filename
|
||||
compile = ->
|
||||
fs.writeFile jsPath, js, (err) ->
|
||||
puts "Compiled $source" if options.compile and options.watch
|
||||
puts "Compiled #source" if options.compile and options.watch
|
||||
path.exists dir, (exists) ->
|
||||
if exists then compile() else exec "mkdir -p $dir", compile
|
||||
if exists then compile() else exec "mkdir -p #dir", compile
|
||||
|
||||
# Pipe compiled JS through JSLint (requires a working `jsl` command), printing
|
||||
# any errors or warnings that arise.
|
||||
|
@ -148,7 +148,7 @@ lint = (js) ->
|
|||
printTokens = (tokens) ->
|
||||
strings = for token in tokens
|
||||
[tag, value] = [token[0], token[1].toString().replace(/\n/, '\\n')]
|
||||
"[$tag $value]"
|
||||
"[#tag #value]"
|
||||
puts strings.join(' ')
|
||||
|
||||
# Use the [OptionParser module](optparse.html) to extract all options from
|
||||
|
@ -174,5 +174,5 @@ usage = ->
|
|||
|
||||
# Print the `--version` message and exit.
|
||||
version = ->
|
||||
puts "CoffeeScript version $CoffeeScript.VERSION"
|
||||
puts "CoffeeScript version #CoffeeScript.VERSION"
|
||||
process.exit 0
|
||||
|
|
|
@ -32,8 +32,8 @@ unwrap = /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
|
|||
# previous nonterminal.
|
||||
o = (patternString, action, options) ->
|
||||
return [patternString, '$$ = $1;', options] unless action
|
||||
action = if match = (action + '').match(unwrap) then match[1] else "($action())"
|
||||
[patternString, "$$ = $action;", options]
|
||||
action = if match = (action + '').match(unwrap) then match[1] else "(#action())"
|
||||
[patternString, "$$ = #action;", options]
|
||||
|
||||
# Grammatical Rules
|
||||
# -----------------
|
||||
|
@ -613,7 +613,7 @@ for name, alternatives of grammar
|
|||
grammar[name] = for alt in alternatives
|
||||
for token in alt[0].split ' '
|
||||
tokens.push token unless grammar[token]
|
||||
alt[1] = "return ${alt[1]}" if name is 'Root'
|
||||
alt[1] = "return #{alt[1]}" if name is 'Root'
|
||||
alt
|
||||
|
||||
# Initialize the **Parser** with our list of terminal **tokens**, our **grammar**
|
||||
|
|
|
@ -91,7 +91,7 @@ exports.Lexer = class Lexer
|
|||
if include(JS_FORBIDDEN, id)
|
||||
if forcedIdentifier
|
||||
tag = 'STRING'
|
||||
id = "'$id'"
|
||||
id = "'#id'"
|
||||
if forcedIdentifier is 'accessor'
|
||||
close_index = true
|
||||
@tokens.pop() if @tag() isnt '@'
|
||||
|
@ -118,7 +118,7 @@ exports.Lexer = class Lexer
|
|||
stringToken: ->
|
||||
return false unless starts(@chunk, '"') or starts(@chunk, "'")
|
||||
return false unless string =
|
||||
@balancedToken(['"', '"'], ['${', '}']) or
|
||||
@balancedToken(['"', '"'], ['${', '}'], ['#{', '}']) or
|
||||
@balancedToken ["'", "'"]
|
||||
@interpolateString string.replace STRING_NEWLINES, " \\\n"
|
||||
@line += count string, "\n"
|
||||
|
@ -131,7 +131,7 @@ exports.Lexer = class Lexer
|
|||
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
|
||||
@interpolateString "#quote#doc#quote", heredoc: yes
|
||||
@line += count match[1], "\n"
|
||||
@i += match[1].length
|
||||
true
|
||||
|
@ -169,8 +169,8 @@ exports.Lexer = class Lexer
|
|||
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
|
||||
@interpolateString "\"#str\"", escapeQuotes: yes
|
||||
@tokens.splice @tokens.length, 0, [',', ','], ['STRING', "\"#flags\""] if flags
|
||||
@tokens.splice @tokens.length, 0, [')', ')'], [')', ')']
|
||||
else
|
||||
@token 'REGEX', regex
|
||||
|
@ -308,7 +308,7 @@ exports.Lexer = class Lexer
|
|||
doc = doc.replace(new RegExp("^" +indent, 'gm'), '')
|
||||
return doc if options.herecomment
|
||||
doc.replace(MULTILINER, "\\n")
|
||||
.replace(new RegExp(options.quote, 'g'), "\\$options.quote")
|
||||
.replace(new RegExp(options.quote, 'g'), "\\#options.quote")
|
||||
|
||||
# A source of ambiguity in our grammar used to be parameter lists in function
|
||||
# definitions versus argument lists in function calls. Walk backwards, tagging
|
||||
|
@ -333,12 +333,12 @@ exports.Lexer = class Lexer
|
|||
# The error for when you try to use a forbidden word in JavaScript as
|
||||
# an identifier.
|
||||
identifierError: (word) ->
|
||||
throw new Error "SyntaxError: Reserved word \"$word\" on line ${@line + 1}"
|
||||
throw new Error "SyntaxError: Reserved word \"#word\" on line #{@line + 1}"
|
||||
|
||||
# The error for when you try to assign to a reserved word in JavaScript,
|
||||
# like "function" or "default".
|
||||
assignmentError: ->
|
||||
throw new Error "SyntaxError: Reserved word \"${@value()}\" on line ${@line + 1} can't be assigned"
|
||||
throw new Error "SyntaxError: Reserved word \"#{@value()}\" on line #{@line + 1} can't be assigned"
|
||||
|
||||
# Matches a balanced group such as a single or double-quoted string. Pass in
|
||||
# a series of delimiters, all of which must be nested correctly within the
|
||||
|
@ -368,15 +368,15 @@ exports.Lexer = class Lexer
|
|||
i += 1
|
||||
if levels.length
|
||||
return false if slash
|
||||
throw new Error "SyntaxError: Unterminated ${levels.pop()[0]} starting on line ${@line + 1}"
|
||||
throw new Error "SyntaxError: Unterminated #{levels.pop()[0]} starting on line #{@line + 1}"
|
||||
if not i then false else str.substring(0, i)
|
||||
|
||||
# Expand variables and expressions inside double-quoted strings using
|
||||
# [ECMA Harmony's interpolation syntax](http://wiki.ecmascript.org/doku.php?id=strawman:string_interpolation)
|
||||
# for substitution of bare variables as well as arbitrary expressions.
|
||||
#
|
||||
# "Hello $name."
|
||||
# "Hello ${name.capitalize()}."
|
||||
# "Hello #name."
|
||||
# "Hello #{name.capitalize()}."
|
||||
#
|
||||
# If it encounters an interpolation, this method will recursively create a
|
||||
# new Lexer, tokenize the interpolated contents, and merge them into the
|
||||
|
@ -395,26 +395,26 @@ exports.Lexer = class Lexer
|
|||
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
|
||||
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), [['${', '}']])
|
||||
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
|
||||
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)
|
||||
if inner.length
|
||||
inner = inner.replace new RegExp('\\\\' + quote, 'g'), quote if options.heredoc
|
||||
nested = lexer.tokenize "($inner)", line: @line
|
||||
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"]
|
||||
tokens.push ['STRING', "#quote#quote"]
|
||||
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.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
|
||||
@token '(', '(' if interpolated
|
||||
|
@ -424,7 +424,7 @@ exports.Lexer = class Lexer
|
|||
@tokens = @tokens.concat value
|
||||
else if tag is 'STRING' and options.escapeQuotes
|
||||
escaped = value.substring(1, value.length - 1).replace(/"/g, '\\"')
|
||||
@token tag, "\"$escaped\""
|
||||
@token tag, "\"#escaped\""
|
||||
else
|
||||
@token tag, value
|
||||
@token '+', '+' if i < tokens.length - 1
|
||||
|
@ -509,7 +509,7 @@ JS_FORBIDDEN = JS_KEYWORDS.concat RESERVED
|
|||
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+)*)/
|
||||
INTERPOLATION = /^[$#]([a-zA-Z_@]\w*(\.\w+)*)/
|
||||
OPERATOR = /^(-[\-=>]?|\+[+=]?|[*&|\/%=<>:!?]+)([ \t]*)/
|
||||
WHITESPACE = /^([ \t]+)/
|
||||
COMMENT = /^(\s*#{3}(?!#)[ \t]*\n+([\s\S]*?)[ \t]*\n+[ \t]*#{3}|(\s*#(?!##[^#])[^\n]*)+)/
|
||||
|
|
214
src/nodes.coffee
214
src/nodes.coffee
|
@ -203,14 +203,14 @@ exports.Expressions = class Expressions extends BaseNode
|
|||
code = @compileWithDeclarations(o)
|
||||
code = code.replace(TRAILING_WHITESPACE, '')
|
||||
code = code.replace(DOUBLE_PARENS, '($1)')
|
||||
if o.noWrap then code else "(function() {\n$code\n})();\n"
|
||||
if o.noWrap then code else "(function() {\n#code\n})();\n"
|
||||
|
||||
# Compile the expressions body for the contents of a function, with
|
||||
# declarations of all inner variables pushed up to the top.
|
||||
compileWithDeclarations: (o) ->
|
||||
code = @compileNode(o)
|
||||
code = "${@tab}var ${o.scope.compiledAssignments()};\n$code" if o.scope.hasAssignments(this)
|
||||
code = "${@tab}var ${o.scope.compiledDeclarations()};\n$code" if not o.globals and o.scope.hasDeclarations(this)
|
||||
code = "#{@tab}var #{o.scope.compiledAssignments()};\n#code" if o.scope.hasAssignments(this)
|
||||
code = "#{@tab}var #{o.scope.compiledDeclarations()};\n#code" if not o.globals and o.scope.hasDeclarations(this)
|
||||
code
|
||||
|
||||
# Compiles a single expression within the expressions body. If we need to
|
||||
|
@ -219,7 +219,7 @@ exports.Expressions = class Expressions extends BaseNode
|
|||
compileExpression: (node, o) ->
|
||||
@tab = o.indent
|
||||
compiledNode = node.compile merge o, top: true
|
||||
if node.isStatement() then compiledNode else "${@idt()}$compiledNode;"
|
||||
if node.isStatement() then compiledNode else "#{@idt()}#compiledNode;"
|
||||
|
||||
# Wrap up the given nodes as an **Expressions**, unless it already happens
|
||||
# to be one.
|
||||
|
@ -248,10 +248,10 @@ exports.LiteralNode = class LiteralNode extends BaseNode
|
|||
compileNode: (o) ->
|
||||
idt = if @isStatement() then @idt() else ''
|
||||
end = if @isStatement() then ';' else ''
|
||||
"$idt$@value$end"
|
||||
"#idt#@value#end"
|
||||
|
||||
toString: (idt) ->
|
||||
" \"$@value\""
|
||||
" \"#@value\""
|
||||
|
||||
#### ReturnNode
|
||||
|
||||
|
@ -277,7 +277,7 @@ exports.ReturnNode = class ReturnNode extends BaseNode
|
|||
|
||||
compileNode: (o) ->
|
||||
o.asStatement = true if @expression.isStatement()
|
||||
"${@tab}return ${@expression.compile(o)};"
|
||||
"#{@tab}return #{@expression.compile(o)};"
|
||||
|
||||
#### ValueNode
|
||||
|
||||
|
@ -350,7 +350,7 @@ exports.ValueNode = class ValueNode extends BaseNode
|
|||
props = if only then @properties[0...@properties.length - 1] else @properties
|
||||
o.chainRoot or= this
|
||||
baseline = @base.compile o
|
||||
baseline = "($baseline)" if @hasProperties() and (@base instanceof ObjectNode or @isNumber())
|
||||
baseline = "(#baseline)" if @hasProperties() and (@base instanceof ObjectNode or @isNumber())
|
||||
complete = @last = baseline
|
||||
|
||||
for prop, i in props
|
||||
|
@ -358,8 +358,8 @@ exports.ValueNode = class ValueNode extends BaseNode
|
|||
if prop.soakNode
|
||||
if @base instanceof CallNode or @base.contains((n) -> n instanceof CallNode) and i is 0
|
||||
temp = o.scope.freeVariable()
|
||||
complete = "(${ baseline = temp } = ($complete))"
|
||||
complete = "typeof $complete === \"undefined\" || $baseline" if i is 0 and @isStart(o)
|
||||
complete = "(#{ baseline = temp } = (#complete))"
|
||||
complete = "typeof #complete === \"undefined\" || #baseline" if i is 0 and @isStart(o)
|
||||
complete += @SOAK + (baseline += prop.compile(o))
|
||||
else
|
||||
part = prop.compile(o)
|
||||
|
@ -367,7 +367,7 @@ exports.ValueNode = class ValueNode extends BaseNode
|
|||
complete += part
|
||||
@last = part
|
||||
|
||||
if op and @wrapped then "($complete)" else complete
|
||||
if op and @wrapped then "(#complete)" else complete
|
||||
|
||||
#### CommentNode
|
||||
|
||||
|
@ -385,8 +385,8 @@ exports.CommentNode = class CommentNode extends BaseNode
|
|||
this
|
||||
|
||||
compileNode: (o) ->
|
||||
sep = "\n$@tab"
|
||||
"$@tab/*$sep${ @lines.join(sep) }\n$@tab*/"
|
||||
sep = "\n#@tab"
|
||||
"#@tab/*#sep#{ @lines.join(sep) }\n#@tab*/"
|
||||
|
||||
#### CallNode
|
||||
|
||||
|
@ -417,9 +417,9 @@ exports.CallNode = class CallNode extends BaseNode
|
|||
superReference: (o) ->
|
||||
methname = o.scope.method.name
|
||||
meth = if o.scope.method.proto
|
||||
"${o.scope.method.proto}.__superClass__.$methname"
|
||||
"#{o.scope.method.proto}.__superClass__.#methname"
|
||||
else if methname
|
||||
"${methname}.__superClass__.constructor"
|
||||
"#{methname}.__superClass__.constructor"
|
||||
else throw new Error "cannot call super on an anonymous function."
|
||||
|
||||
# Compile a vanilla function call.
|
||||
|
@ -430,13 +430,13 @@ exports.CallNode = class CallNode extends BaseNode
|
|||
unless compilation
|
||||
args = (arg.compile(o) for arg in @args).join(', ')
|
||||
compilation = if @isSuper then @compileSuper(args, o)
|
||||
else "${@prefix()}${@variable.compile(o)}($args)"
|
||||
if o.operation and @wrapped then "($compilation)" else compilation
|
||||
else "#{@prefix()}#{@variable.compile(o)}(#args)"
|
||||
if o.operation and @wrapped then "(#compilation)" else compilation
|
||||
|
||||
# `super()` is converted into a call against the superclass's implementation
|
||||
# of the current function.
|
||||
compileSuper: (args, o) ->
|
||||
"${@superReference(o)}.call(this${ if args.length then ', ' else '' }$args)"
|
||||
"#{@superReference(o)}.call(this#{ if args.length then ', ' else '' }#args)"
|
||||
|
||||
# If you call a function with a splat, it's converted into a JavaScript
|
||||
# `.apply()` call to allow an array of arguments to be passed.
|
||||
|
@ -448,18 +448,18 @@ exports.CallNode = class CallNode extends BaseNode
|
|||
if obj.match(/\(/)
|
||||
temp = o.scope.freeVariable()
|
||||
obj = temp
|
||||
meth = "($temp = ${ @variable.source })${ @variable.last }"
|
||||
meth = "(#temp = #{ @variable.source })#{ @variable.last }"
|
||||
if @isNew
|
||||
utility 'extends'
|
||||
"""
|
||||
(function() {
|
||||
${@idt(1)}var ctor = function(){};
|
||||
${@idt(1)}__extends(ctor, $meth);
|
||||
${@idt(1)}return ${meth}.apply(new ctor, ${ @compileSplatArguments(o) });
|
||||
$@tab}).call(this)
|
||||
#{@idt(1)}var ctor = function(){};
|
||||
#{@idt(1)}__extends(ctor, #meth);
|
||||
#{@idt(1)}return #{meth}.apply(new ctor, #{ @compileSplatArguments(o) });
|
||||
#@tab}).call(this)
|
||||
"""
|
||||
else
|
||||
"${@prefix()}${meth}.apply($obj, ${ @compileSplatArguments(o) })"
|
||||
"#{@prefix()}#{meth}.apply(#obj, #{ @compileSplatArguments(o) })"
|
||||
|
||||
#### ExtendsNode
|
||||
|
||||
|
@ -497,7 +497,7 @@ exports.AccessorNode = class AccessorNode extends BaseNode
|
|||
compileNode: (o) ->
|
||||
name = @name.compile o
|
||||
o.chainRoot.wrapped or= @soakNode
|
||||
namePart = if name.match(IS_STRING) then "[$name]" else ".$name"
|
||||
namePart = if name.match(IS_STRING) then "[#name]" else ".#name"
|
||||
@prototype + namePart
|
||||
|
||||
#### IndexNode
|
||||
|
@ -515,7 +515,7 @@ exports.IndexNode = class IndexNode extends BaseNode
|
|||
o.chainRoot.wrapped or= @soakNode
|
||||
idx = @index.compile o
|
||||
prefix = if @proto then '.prototype' else ''
|
||||
"$prefix[$idx]"
|
||||
"#prefix[#idx]"
|
||||
|
||||
#### RangeNode
|
||||
|
||||
|
@ -543,7 +543,7 @@ exports.RangeNode = class RangeNode extends BaseNode
|
|||
parts = []
|
||||
parts.push @from if @from isnt @fromVar
|
||||
parts.push @to if @to isnt @toVar
|
||||
if parts.length then "${parts.join('; ')}; " else ''
|
||||
if parts.length then "#{parts.join('; ')}; " else ''
|
||||
|
||||
# When compiled normally, the range returns the contents of the *for loop*
|
||||
# needed to iterate over the values in the range. Used by comprehensions.
|
||||
|
@ -552,23 +552,23 @@ exports.RangeNode = class RangeNode extends BaseNode
|
|||
return @compileSimple(o) if @fromNum and @toNum
|
||||
idx = del o, 'index'
|
||||
step = del o, 'step'
|
||||
vars = "$idx = $@fromVar"
|
||||
intro = "($@fromVar <= $@toVar ? $idx"
|
||||
compare = "$intro <$@equals $@toVar : $idx >$@equals $@toVar)"
|
||||
vars = "#idx = #@fromVar"
|
||||
intro = "(#@fromVar <= #@toVar ? #idx"
|
||||
compare = "#intro <#@equals #@toVar : #idx >#@equals #@toVar)"
|
||||
stepPart = if step then step.compile(o) else '1'
|
||||
incr = if step then "$idx += $stepPart" else "$intro += $stepPart : $idx -= $stepPart)"
|
||||
"$vars; $compare; $incr"
|
||||
incr = if step then "#idx += #stepPart" else "#intro += #stepPart : #idx -= #stepPart)"
|
||||
"#vars; #compare; #incr"
|
||||
|
||||
# Compile a simple range comprehension, with integers.
|
||||
compileSimple: (o) ->
|
||||
[from, to] = [parseInt(@fromNum, 10), parseInt(@toNum, 10)]
|
||||
idx = del o, 'index'
|
||||
step = del o, 'step'
|
||||
step and= "$idx += ${step.compile(o)}"
|
||||
step and= "#idx += #{step.compile(o)}"
|
||||
if from <= to
|
||||
"$idx = $from; $idx <$@equals $to; ${step or "$idx++"}"
|
||||
"#idx = #from; #idx <#@equals #to; #{step or "$idx++"}"
|
||||
else
|
||||
"$idx = $from; $idx >$@equals $to; ${step or "$idx--"}"
|
||||
"#idx = #from; #idx >#@equals #to; #{step or "#idx--"}"
|
||||
|
||||
# When used as a value, expand the range into the equivalent array.
|
||||
compileArray: (o) ->
|
||||
|
@ -576,15 +576,15 @@ exports.RangeNode = class RangeNode extends BaseNode
|
|||
vars = @compileVariables merge o, indent: idt
|
||||
result = o.scope.freeVariable()
|
||||
i = o.scope.freeVariable()
|
||||
pre = "\n${idt}${result} = []; ${vars}"
|
||||
pre = "\n#{idt}#{result} = []; #{vars}"
|
||||
if @fromNum and @toNum
|
||||
o.index = i
|
||||
body = @compileSimple o
|
||||
else
|
||||
clause = "$@fromVar <= $@toVar ?"
|
||||
body = "var $i = $@fromVar; $clause $i <$@equals $@toVar : $i >$@equals $@toVar; $clause $i += 1 : $i -= 1"
|
||||
post = "{ ${result}.push($i) };\n${idt}return $result;\n$o.indent"
|
||||
"(function() {${pre}\n${idt}for ($body)$post}).call(this)"
|
||||
clause = "#@fromVar <= #@toVar ?"
|
||||
body = "var #i = #@fromVar; #clause #i <#@equals #@toVar : #i >#@equals #@toVar; #clause #i += 1 : #i -= 1"
|
||||
post = "{ #{result}.push(#i) };\n#{idt}return #result;\n#o.indent"
|
||||
"(function() {#{pre}\n#{idt}for (#body)#post}).call(this)"
|
||||
|
||||
#### SliceNode
|
||||
|
||||
|
@ -603,7 +603,7 @@ exports.SliceNode = class SliceNode extends BaseNode
|
|||
from = @range.from.compile(o)
|
||||
to = @range.to.compile(o)
|
||||
plusPart = if @range.exclusive then '' else ' + 1'
|
||||
".slice($from, $to$plusPart)"
|
||||
".slice(#from, #to#plusPart)"
|
||||
|
||||
#### ObjectNode
|
||||
|
||||
|
@ -629,7 +629,7 @@ exports.ObjectNode = class ObjectNode extends BaseNode
|
|||
indent + prop.compile(o) + join
|
||||
props = props.join('')
|
||||
inner = if props then '\n' + props + '\n' + @idt() else ''
|
||||
"{$inner}"
|
||||
"{#inner}"
|
||||
|
||||
#### ArrayNode
|
||||
|
||||
|
@ -652,16 +652,16 @@ exports.ArrayNode = class ArrayNode extends BaseNode
|
|||
if obj instanceof SplatNode
|
||||
return @compileSplatLiteral o
|
||||
else if obj instanceof CommentNode
|
||||
objects.push "\n$code\n$o.indent"
|
||||
objects.push "\n#code\n#o.indent"
|
||||
else if i is @objects.length - 1
|
||||
objects.push code
|
||||
else
|
||||
objects.push "$code, "
|
||||
objects.push "#code, "
|
||||
objects = objects.join('')
|
||||
if indexOf(objects, '\n') >= 0
|
||||
"[\n${@idt(1)}$objects\n$@tab]"
|
||||
"[\n#{@idt(1)}#objects\n#@tab]"
|
||||
else
|
||||
"[$objects]"
|
||||
"[#objects]"
|
||||
|
||||
#### ClassNode
|
||||
|
||||
|
@ -719,19 +719,19 @@ exports.ClassNode = class ClassNode extends BaseNode
|
|||
me or= constScope.freeVariable()
|
||||
pname = pvar.compile(o)
|
||||
constructor.body.push new ReturnNode literal 'this' if constructor.body.empty()
|
||||
constructor.body.unshift literal "this.${pname} = function(){ return ${className}.prototype.${pname}.apply($me, arguments); }"
|
||||
constructor.body.unshift literal "this.#{pname} = function(){ return #{className}.prototype.#{pname}.apply(#me, arguments); }"
|
||||
if pvar
|
||||
access = if prop.context is 'this' then pvar.base.properties[0] else new AccessorNode(pvar, 'prototype')
|
||||
val = new ValueNode(@variable, [access])
|
||||
prop = new AssignNode(val, func)
|
||||
props.push prop
|
||||
|
||||
constructor.body.unshift literal "$me = this" if me
|
||||
constructor.body.unshift literal "#me = this" if me
|
||||
construct = @idt() + (new AssignNode(@variable, constructor)).compile(merge o, {sharedScope: constScope}) + ';'
|
||||
props = if !props.empty() then '\n' + props.compile(o) else ''
|
||||
extension = if extension then '\n' + @idt() + extension.compile(o) + ';' else ''
|
||||
returns = if @returns then '\n' + new ReturnNode(@variable).compile(o) else ''
|
||||
"$construct$extension$props$returns"
|
||||
"#construct#extension#props#returns"
|
||||
|
||||
#### AssignNode
|
||||
|
||||
|
@ -783,11 +783,11 @@ exports.AssignNode = class AssignNode extends BaseNode
|
|||
@value.name = last if last.match(IDENTIFIER)
|
||||
@value.proto = proto if proto
|
||||
val = @value.compile o
|
||||
return "$name: $val" if @context is 'object'
|
||||
return "#name: #val" if @context is 'object'
|
||||
o.scope.find name unless @isValue() and (@variable.hasProperties() or @variable.namespaced)
|
||||
val = "$name = $val"
|
||||
return "$@tab$val;" if stmt
|
||||
if top then val else "($val)"
|
||||
val = "#name = #val"
|
||||
return "#@tab#val;" if stmt
|
||||
if top then val else "(#val)"
|
||||
|
||||
# Brief implementation of recursive pattern matching, when assigning array or
|
||||
# object literals to a value. Peeks at their properties to assign inner names.
|
||||
|
@ -796,7 +796,7 @@ exports.AssignNode = class AssignNode extends BaseNode
|
|||
compilePatternMatch: (o) ->
|
||||
valVar = o.scope.freeVariable()
|
||||
value = if @value.isStatement() then ClosureNode.wrap(@value) else @value
|
||||
assigns = ["$@tab$valVar = ${ value.compile(o) };"]
|
||||
assigns = ["#@tab#valVar = #{ value.compile(o) };"]
|
||||
o.top = true
|
||||
o.asStatement = true
|
||||
splat = false
|
||||
|
@ -820,7 +820,7 @@ exports.AssignNode = class AssignNode extends BaseNode
|
|||
(olength = @variable.base.objects.length) - oindex - 1))
|
||||
splat = true
|
||||
else
|
||||
idx = literal(if splat then "${valVar}.length - ${olength - idx}" else idx) if typeof idx isnt 'object'
|
||||
idx = literal(if splat then "#{valVar}.length - #{olength - idx}" else idx) if typeof idx isnt 'object'
|
||||
val = new ValueNode(literal(valVar), [new accessClass(idx)])
|
||||
assigns.push(new AssignNode(obj, val).compile(o))
|
||||
code = assigns.join("\n")
|
||||
|
@ -836,7 +836,7 @@ exports.AssignNode = class AssignNode extends BaseNode
|
|||
from = range.from.compile(o)
|
||||
to = range.to.compile(o) + ' - ' + from + plus
|
||||
val = @value.compile(o)
|
||||
"${name}.splice.apply($name, [$from, $to].concat($val))"
|
||||
"#{name}.splice.apply(#name, [#from, #to].concat(#val))"
|
||||
|
||||
#### CodeNode
|
||||
|
||||
|
@ -884,10 +884,10 @@ exports.CodeNode = class CodeNode extends BaseNode
|
|||
params = (param.compile(o) for param in params)
|
||||
@body.makeReturn()
|
||||
(o.scope.parameter(param)) for param in params
|
||||
code = if @body.expressions.length then "\n${ @body.compileWithDeclarations(o) }\n" else ''
|
||||
func = "function(${ params.join(', ') }) {$code${ code and @tab }}"
|
||||
return "${utility('bind')}($func, this)" if @bound
|
||||
if top then "($func)" else func
|
||||
code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations(o) }\n" else ''
|
||||
func = "function(#{ params.join(', ') }) {#code#{ code and @tab }}"
|
||||
return "#{utility('bind')}(#func, this)" if @bound
|
||||
if top then "(#func)" else func
|
||||
|
||||
topSensitive: ->
|
||||
true
|
||||
|
@ -899,7 +899,7 @@ exports.CodeNode = class CodeNode extends BaseNode
|
|||
toString: (idt) ->
|
||||
idt or= ''
|
||||
children = (child.toString(idt + TAB) for child in @collectChildren()).join('')
|
||||
"\n$idt$children"
|
||||
"\n#idt#children"
|
||||
|
||||
#### SplatNode
|
||||
|
||||
|
@ -927,18 +927,18 @@ exports.SplatNode = class SplatNode extends BaseNode
|
|||
len = o.scope.freeVariable()
|
||||
o.scope.assign len, "arguments.length"
|
||||
variadic = o.scope.freeVariable()
|
||||
o.scope.assign variadic, "$len >= $@arglength"
|
||||
end = if @trailings.length then ", $len - ${@trailings.length}"
|
||||
o.scope.assign variadic, "#len >= #@arglength"
|
||||
end = if @trailings.length then ", #len - #{@trailings.length}"
|
||||
for trailing, idx in @trailings
|
||||
pos = @trailings.length - idx
|
||||
o.scope.assign(trailing.compile(o), "arguments[$variadic ? $len - $pos : ${@index + idx}]")
|
||||
"$name = ${utility('slice')}.call(arguments, $@index$end)"
|
||||
o.scope.assign(trailing.compile(o), "arguments[#variadic ? #len - #pos : #{@index + idx}]")
|
||||
"#name = #{utility('slice')}.call(arguments, #@index#end)"
|
||||
|
||||
# A compiling a splat as a destructuring assignment means slicing arguments
|
||||
# from the right-hand-side's corresponding array.
|
||||
compileValue: (o, name, index, trailings) ->
|
||||
trail = if trailings then ", ${name}.length - $trailings" else ''
|
||||
"${utility 'slice'}.call($name, $index$trail)"
|
||||
trail = if trailings then ", #{name}.length - #trailings" else ''
|
||||
"#{utility 'slice'}.call(#name, #index#trail)"
|
||||
|
||||
# Utility function that converts arbitrary number of elements, mixed with
|
||||
# splats, to a proper array
|
||||
|
@ -949,14 +949,14 @@ exports.SplatNode = class SplatNode extends BaseNode
|
|||
prev = args[last = args.length - 1]
|
||||
if not (arg instanceof SplatNode)
|
||||
if prev and starts(prev, '[') and ends(prev, ']')
|
||||
args[last] = "${prev.substr(0, prev.length - 1)}, $code]"
|
||||
args[last] = "#{prev.substr(0, prev.length - 1)}, #code]"
|
||||
continue
|
||||
else if prev and starts(prev, '.concat([') and ends(prev, '])')
|
||||
args[last] = "${prev.substr(0, prev.length - 2)}, $code])"
|
||||
args[last] = "#{prev.substr(0, prev.length - 2)}, #code])"
|
||||
continue
|
||||
else
|
||||
code = "[$code]"
|
||||
args.push(if i is 0 then code else ".concat($code)")
|
||||
code = "[#code]"
|
||||
args.push(if i is 0 then code else ".concat(#code)")
|
||||
args.join('')
|
||||
|
||||
#### WhileNode
|
||||
|
@ -999,15 +999,15 @@ exports.WhileNode = class WhileNode extends BaseNode
|
|||
set = ''
|
||||
unless top
|
||||
rvar = o.scope.freeVariable()
|
||||
set = "$@tab$rvar = [];\n"
|
||||
set = "#@tab#rvar = [];\n"
|
||||
@body = PushNode.wrap(rvar, @body) if @body
|
||||
pre = "$set${@tab}while ($cond)"
|
||||
pre = "#set#{@tab}while (#cond)"
|
||||
@body = Expressions.wrap([new IfNode(@guard, @body)]) if @guard
|
||||
if @returns
|
||||
post = '\n' + new ReturnNode(literal(rvar)).compile(merge(o, indent: @idt()))
|
||||
else
|
||||
post = ''
|
||||
"$pre {\n${ @body.compile(o) }\n$@tab}$post"
|
||||
"#pre {\n#{ @body.compile(o) }\n#@tab}#post"
|
||||
|
||||
#### OpNode
|
||||
|
||||
|
@ -1068,7 +1068,7 @@ exports.OpNode = class OpNode extends BaseNode
|
|||
shared = @first.unwrap().second
|
||||
[@first.second, shared] = shared.compileReference(o) if shared.containsType CallNode
|
||||
[first, second, shared] = [@first.compile(o), @second.compile(o), shared.compile(o)]
|
||||
"($first) && ($shared $@operator $second)"
|
||||
"(#first) && (#shared #@operator #second)"
|
||||
|
||||
# When compiling a conditional assignment, take care to ensure that the
|
||||
# operands are only evaluated once, even though we have to reference them
|
||||
|
@ -1076,15 +1076,15 @@ exports.OpNode = class OpNode extends BaseNode
|
|||
compileAssignment: (o) ->
|
||||
[first, second] = [@first.compile(o), @second.compile(o)]
|
||||
o.scope.find(first) if first.match(IDENTIFIER)
|
||||
return "$first = ${ ExistenceNode.compileTest(o, @first) } ? $first : $second" if @operator is '?='
|
||||
"$first = $first ${ @operator.substr(0, 2) } $second"
|
||||
return "#first = #{ ExistenceNode.compileTest(o, @first) } ? #first : #second" if @operator is '?='
|
||||
"#first = #first #{ @operator.substr(0, 2) } #second"
|
||||
|
||||
# If this is an existence operator, we delegate to `ExistenceNode.compileTest`
|
||||
# to give us the safe references for the variables.
|
||||
compileExistence: (o) ->
|
||||
[first, second] = [@first.compile(o), @second.compile(o)]
|
||||
test = ExistenceNode.compileTest(o, @first)
|
||||
"$test ? $first : $second"
|
||||
"#test ? #first : #second"
|
||||
|
||||
# Compile a unary **OpNode**.
|
||||
compileUnary: (o) ->
|
||||
|
@ -1112,14 +1112,14 @@ exports.InNode = class InNode extends BaseNode
|
|||
|
||||
compileOrTest: (o) ->
|
||||
tests = for item, i in @array.base.objects
|
||||
"${item.compile(o)} === ${if i then @obj2 else @obj1}"
|
||||
"(${tests.join(' || ')})"
|
||||
"#{item.compile(o)} === #{if i then @obj2 else @obj1}"
|
||||
"(#{tests.join(' || ')})"
|
||||
|
||||
compileLoopTest: (o) ->
|
||||
[@arr1, @arr2] = @array.compileReference o, precompile: yes
|
||||
[i, l] = [o.scope.freeVariable(), o.scope.freeVariable()]
|
||||
prefix = if @obj1 isnt @obj2 then @obj1 + '; ' else ''
|
||||
"!!(function(){ ${prefix}for (var $i=0, $l=${@arr1}.length; $i<$l; $i++) if (${@arr2}[$i] === $@obj2) return true; }).call(this)"
|
||||
"!!(function(){ #{prefix}for (var #i=0, #l=#{@arr1}.length; #i<#l; #i++) if (#{@arr2}[#i] === #@obj2) return true; }).call(this)"
|
||||
|
||||
#### TryNode
|
||||
|
||||
|
@ -1147,10 +1147,10 @@ exports.TryNode = class TryNode extends BaseNode
|
|||
o.indent = @idt 1
|
||||
o.top = true
|
||||
attemptPart = @attempt.compile(o)
|
||||
errorPart = if @error then " (${ @error.compile(o) }) " else ' '
|
||||
catchPart = if @recovery then " catch$errorPart{\n${ @recovery.compile(o) }\n$@tab}" else ''
|
||||
finallyPart = (@ensure or '') and ' finally {\n' + @ensure.compile(merge(o)) + "\n$@tab}"
|
||||
"${@tab}try {\n$attemptPart\n$@tab}$catchPart$finallyPart"
|
||||
errorPart = if @error then " (#{ @error.compile(o) }) " else ' '
|
||||
catchPart = if @recovery then " catch#errorPart{\n#{ @recovery.compile(o) }\n#@tab}" else ''
|
||||
finallyPart = (@ensure or '') and ' finally {\n' + @ensure.compile(merge(o)) + "\n#@tab}"
|
||||
"#{@tab}try {\n#attemptPart\n#@tab}#catchPart#finallyPart"
|
||||
|
||||
#### ThrowNode
|
||||
|
||||
|
@ -1169,7 +1169,7 @@ exports.ThrowNode = class ThrowNode extends BaseNode
|
|||
return this
|
||||
|
||||
compileNode: (o) ->
|
||||
"${@tab}throw ${@expression.compile(o)};"
|
||||
"#{@tab}throw #{@expression.compile(o)};"
|
||||
|
||||
#### ExistenceNode
|
||||
|
||||
|
@ -1192,7 +1192,7 @@ exports.ExistenceNode = class ExistenceNode extends BaseNode
|
|||
# Be careful not to double-evaluate anything.
|
||||
@compileTest: (o, variable) ->
|
||||
[first, second] = variable.compileReference o
|
||||
"(typeof ${first.compile(o)} !== \"undefined\" && ${second.compile(o)} !== null)"
|
||||
"(typeof #{first.compile(o)} !== \"undefined\" && #{second.compile(o)} !== null)"
|
||||
|
||||
#### ParentheticalNode
|
||||
|
||||
|
@ -1222,10 +1222,10 @@ exports.ParentheticalNode = class ParentheticalNode extends BaseNode
|
|||
top = del o, 'top'
|
||||
code = @expression.compile(o)
|
||||
if @isStatement()
|
||||
return (if top then "$@tab$code;" else code)
|
||||
return (if top then "#@tab#code;" else code)
|
||||
l = code.length
|
||||
code = code.substr(o, l-1) if code.substr(l-1, 1) is ';'
|
||||
if @expression instanceof AssignNode then code else "($code)"
|
||||
if @expression instanceof AssignNode then code else "(#code)"
|
||||
|
||||
#### ForNode
|
||||
|
||||
|
@ -1292,34 +1292,34 @@ exports.ForNode = class ForNode extends BaseNode
|
|||
forPart = source.compile merge o, index: ivar, step: @step
|
||||
else
|
||||
svar = scope.freeVariable()
|
||||
sourcePart = "$svar = ${ @source.compile(o) };"
|
||||
sourcePart = "#svar = #{ @source.compile(o) };"
|
||||
if @pattern
|
||||
namePart = new AssignNode(@name, literal("$svar[$ivar]")).compile(merge o, {indent: @idt(1), top: true}) + '\n'
|
||||
namePart = new AssignNode(@name, literal("#svar[#ivar]")).compile(merge o, {indent: @idt(1), top: true}) + '\n'
|
||||
else
|
||||
namePart = "$name = $svar[$ivar]" if name
|
||||
namePart = "#name = #svar[#ivar]" if name
|
||||
unless @object
|
||||
lvar = scope.freeVariable()
|
||||
stepPart = if @step then "$ivar += ${ @step.compile(o) }" else "$ivar++"
|
||||
forPart = "$ivar = 0, $lvar = ${svar}.length; $ivar < $lvar; $stepPart"
|
||||
sourcePart = (if rvar then "$rvar = []; " else '') + sourcePart
|
||||
sourcePart = if sourcePart then "$@tab$sourcePart\n$@tab" else @tab
|
||||
stepPart = if @step then "#ivar += #{ @step.compile(o) }" else "#ivar++"
|
||||
forPart = "#ivar = 0, #lvar = #{svar}.length; #ivar < #lvar; #stepPart"
|
||||
sourcePart = (if rvar then "#rvar = []; " else '') + sourcePart
|
||||
sourcePart = if sourcePart then "#@tab#sourcePart\n#@tab" else @tab
|
||||
returnResult = @compileReturnValue(rvar, o)
|
||||
|
||||
body = PushNode.wrap(rvar, body) unless topLevel
|
||||
if @guard
|
||||
body = Expressions.wrap([new IfNode(@guard, body)])
|
||||
if codeInBody
|
||||
body.unshift literal "var $namePart" if namePart
|
||||
body.unshift literal "var $index = $ivar" if index
|
||||
body.unshift literal "var #namePart" if namePart
|
||||
body.unshift literal "var #index = #ivar" if index
|
||||
body = ClosureNode.wrap(body, true)
|
||||
else
|
||||
varPart = (namePart or '') and (if @pattern then namePart else "${@idt(1)}$namePart;\n")
|
||||
varPart = (namePart or '') and (if @pattern then namePart else "#{@idt(1)}#namePart;\n")
|
||||
if @object
|
||||
forPart = "$ivar in $svar"
|
||||
guardPart = "\n${@idt(1)}if (!${utility('hasProp')}.call($svar, $ivar)) continue;" unless @raw
|
||||
forPart = "#ivar in #svar"
|
||||
guardPart = "\n#{@idt(1)}if (!#{utility('hasProp')}.call(#svar, #ivar)) continue;" unless @raw
|
||||
body = body.compile(merge(o, {indent: @idt(1), top: true}))
|
||||
vars = if range then name else "$name, $ivar"
|
||||
"${sourcePart}for ($forPart) {$guardPart\n$varPart$body\n$@tab}$returnResult"
|
||||
vars = if range then name else "#name, #ivar"
|
||||
"#{sourcePart}for (#forPart) {#guardPart\n#varPart#body\n#@tab}#returnResult"
|
||||
|
||||
#### IfNode
|
||||
|
||||
|
@ -1412,20 +1412,20 @@ exports.IfNode = class IfNode extends BaseNode
|
|||
ifDent = if child then '' else @idt()
|
||||
comDent = if child then @idt() else ''
|
||||
body = @body.compile(o)
|
||||
ifPart = "${ifDent}if (${ @compileCondition(condO) }) {\n$body\n$@tab}"
|
||||
ifPart = "#{ifDent}if (#{ @compileCondition(condO) }) {\n#body\n#@tab}"
|
||||
return ifPart unless @elseBody
|
||||
elsePart = if @isChain
|
||||
' else ' + @elseBodyNode().compile(merge(o, {indent: @idt(), chainChild: true}))
|
||||
else
|
||||
" else {\n${ @elseBody.compile(o) }\n$@tab}"
|
||||
"$ifPart$elsePart"
|
||||
" else {\n#{ @elseBody.compile(o) }\n#@tab}"
|
||||
"#ifPart#elsePart"
|
||||
|
||||
# Compile the IfNode as a ternary operator.
|
||||
compileTernary: (o) ->
|
||||
o.operation = true
|
||||
ifPart = @condition.compile(o) + ' ? ' + @bodyNode().compile(o)
|
||||
elsePart = if @elseBody then @elseBodyNode().compile(o) else 'null'
|
||||
"$ifPart : $elsePart"
|
||||
"#ifPart : #elsePart"
|
||||
|
||||
# Faux-Nodes
|
||||
# ----------
|
||||
|
|
|
@ -33,7 +33,7 @@ exports.OptionParser = class OptionParser
|
|||
options[rule.name] = if rule.hasArgument then args[i += 1] else true
|
||||
matchedRule = yes
|
||||
break
|
||||
throw new Error "unrecognized option: $arg" if isOption and not matchedRule
|
||||
throw new Error "unrecognized option: #arg" if isOption and not matchedRule
|
||||
if not isOption
|
||||
options.arguments = args[i...args.length]
|
||||
break
|
||||
|
@ -43,13 +43,13 @@ exports.OptionParser = class OptionParser
|
|||
# of the valid options, for `--help` and such.
|
||||
help: ->
|
||||
lines = ['Available options:']
|
||||
lines.unshift "$@banner\n" if @banner
|
||||
lines.unshift "#@banner\n" if @banner
|
||||
for rule in @rules
|
||||
spaces = 15 - rule.longFlag.length
|
||||
spaces = if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
|
||||
letPart = if rule.shortFlag then rule.shortFlag + ', ' else ' '
|
||||
lines.push " $letPart$rule.longFlag$spaces$rule.description"
|
||||
"\n${ lines.join('\n') }\n"
|
||||
lines.push " #letPart#rule.longFlag#spaces#rule.description"
|
||||
"\n#{ lines.join('\n') }\n"
|
||||
|
||||
# Helpers
|
||||
# -------
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# and evaluates it. Good for simple tests, or poking around the **Node.js** API.
|
||||
# Using it looks like this:
|
||||
#
|
||||
# coffee> puts "$num bottles of beer" for num in [99..1]
|
||||
# coffee> puts "#num bottles of beer" for num in [99..1]
|
||||
|
||||
# Require the **coffee-script** module to get access to the compiler.
|
||||
CoffeeScript = require './coffee-script'
|
||||
|
|
|
@ -242,13 +242,13 @@ exports.Rewriter = class Rewriter
|
|||
openLine[open] = token[2] if levels[open] == 0
|
||||
levels[open] += 1
|
||||
levels[open] -= 1 if token[0] is close
|
||||
throw new Error("too many ${token[1]} on line ${token[2] + 1}") if levels[open] < 0
|
||||
throw new Error("too many #{token[1]} on line #{token[2] + 1}") if levels[open] < 0
|
||||
return 1
|
||||
unclosed = key for key, value of levels when value > 0
|
||||
if unclosed.length
|
||||
open = unclosed[0]
|
||||
line = openLine[open] + 1
|
||||
throw new Error "unclosed $open on line $line"
|
||||
throw new Error "unclosed #open on line #line"
|
||||
|
||||
# We'd like to support syntax like this:
|
||||
#
|
||||
|
|
|
@ -80,7 +80,7 @@ exports.Scope = class Scope
|
|||
# Return the list of assignments that are supposed to be made at the top
|
||||
# of this scope.
|
||||
assignedVariables: ->
|
||||
"$key = $val.value" for key, val of @variables when val.assigned
|
||||
"#key = #val.value" for key, val of @variables when val.assigned
|
||||
|
||||
# Compile the JavaScript for all of the variable declarations in this scope.
|
||||
compiledDeclarations: ->
|
||||
|
|
|
@ -1,80 +1,80 @@
|
|||
hello = 'Hello'
|
||||
world = 'World'
|
||||
ok '$hello $world!' is '$hello $world!'
|
||||
ok '${hello} ${world}!' is '${hello} ${world}!'
|
||||
ok "$hello $world!" is 'Hello World!'
|
||||
ok "${hello} ${world}!" is 'Hello World!'
|
||||
ok "[$hello$world]" is '[HelloWorld]'
|
||||
ok "[${hello}${world}]" is '[HelloWorld]'
|
||||
ok "$hello$$world" is 'Hello$World'
|
||||
ok "${hello}$${world}" is 'Hello$World'
|
||||
ok "Hello ${ 1 + 2 } World" is 'Hello 3 World'
|
||||
ok "$hello ${ 1 + 2 } $world" is "Hello 3 World"
|
||||
ok '#hello #world!' is '#hello #world!'
|
||||
ok '#{hello} #{world}!' is '#{hello} #{world}!'
|
||||
ok "#hello #world!" is 'Hello World!'
|
||||
ok "#{hello} #{world}!" is 'Hello World!'
|
||||
ok "[#hello#world]" is '[HelloWorld]'
|
||||
ok "[#{hello}#{world}]" is '[HelloWorld]'
|
||||
ok "#hello##world" is 'Hello#World'
|
||||
ok "#{hello}##{world}" is 'Hello#World'
|
||||
ok "Hello #{ 1 + 2 } World" is 'Hello 3 World'
|
||||
ok "#hello #{ 1 + 2 } #world" is "Hello 3 World"
|
||||
|
||||
|
||||
[s, t, r, i, n, g] = ['s', 't', 'r', 'i', 'n', 'g']
|
||||
ok "$s$t$r$i$n$g" is 'string'
|
||||
ok "${s}${t}${r}${i}${n}${g}" is 'string'
|
||||
ok "\$s\$t\$r\$i\$n\$g" is '$s$t$r$i$n$g'
|
||||
ok "\\$s\\$t\\$r\\$i\\$n\\$g" is '\\s\\t\\r\\i\\n\\g'
|
||||
ok "\${s}\${t}\${r}\${i}\${n}\${g}" is '${s}${t}${r}${i}${n}${g}'
|
||||
ok "\$string" is '$string'
|
||||
ok "\${string}" is '${string}'
|
||||
ok "#s#t#r#i#n#g" is 'string'
|
||||
ok "#{s}#{t}#{r}#{i}#{n}#{g}" is 'string'
|
||||
ok "\#s\#t\#r\#i\#n\#g" is '#s#t#r#i#n#g'
|
||||
ok "\\#s\\#t\\#r\\#i\\#n\\#g" is '\\s\\t\\r\\i\\n\\g'
|
||||
ok "\#{s}\#{t}\#{r}\#{i}\#{n}\#{g}" is '#{s}#{t}#{r}#{i}#{n}#{g}'
|
||||
ok "\#string" is '#string'
|
||||
ok "\#{string}" is '#{string}'
|
||||
|
||||
|
||||
ok "\$Escaping first" is '$Escaping first'
|
||||
ok "\${Escaping} first" is '${Escaping} first'
|
||||
ok "Escaping \$in middle" is 'Escaping $in middle'
|
||||
ok "Escaping \${in} middle" is 'Escaping ${in} middle'
|
||||
ok "Escaping \$last" is 'Escaping $last'
|
||||
ok "Escaping \${last}" is 'Escaping ${last}'
|
||||
ok "\#Escaping first" is '#Escaping first'
|
||||
ok "\#{Escaping} first" is '#{Escaping} first'
|
||||
ok "Escaping \#in middle" is 'Escaping #in middle'
|
||||
ok "Escaping \#{in} middle" is 'Escaping #{in} middle'
|
||||
ok "Escaping \#last" is 'Escaping #last'
|
||||
ok "Escaping \#{last}" is 'Escaping #{last}'
|
||||
|
||||
|
||||
ok "$$" is '$$'
|
||||
ok "${}" is ''
|
||||
ok "${}A${} ${} ${}B${}" is 'A B'
|
||||
ok "\\\\\$$" is '\\\\\$$'
|
||||
ok "\\\${}" is '\\${}'
|
||||
ok "##" is '##'
|
||||
ok "#{}" is ''
|
||||
ok "#{}A#{} #{} #{}B#{}" is 'A B'
|
||||
ok "\\\\\##" is '\\\\\##'
|
||||
ok "\\\#{}" is '\\#{}'
|
||||
|
||||
|
||||
ok "I won $20 last night." is 'I won $20 last night.'
|
||||
ok "I won $${20} last night." is 'I won $20 last night.'
|
||||
ok "I won $#20 last night." is 'I won $#20 last night.'
|
||||
ok "I won $${'#20'} last night." is 'I won $#20 last night.'
|
||||
ok "I won #20 last night." is 'I won #20 last night.'
|
||||
ok "I won ##{20} last night." is 'I won #20 last night.'
|
||||
ok "I won ##20 last night." is 'I won ##20 last night.'
|
||||
ok "I won ##{'#20'} last night." is 'I won ##20 last night.'
|
||||
|
||||
|
||||
ok "${hello + world}" is 'HelloWorld'
|
||||
ok "${hello + ' ' + world + '!'}" is 'Hello World!'
|
||||
ok "#{hello + world}" is 'HelloWorld'
|
||||
ok "#{hello + ' ' + world + '!'}" is 'Hello World!'
|
||||
|
||||
|
||||
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
ok "values: ${list.join(', ')}, length: ${list.length}." is 'values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, length: 10.'
|
||||
ok "values: ${list.join ' '}" is 'values: 0 1 2 3 4 5 6 7 8 9'
|
||||
ok "values: #{list.join(', ')}, length: #{list.length}." is 'values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, length: 10.'
|
||||
ok "values: #{list.join ' '}" is 'values: 0 1 2 3 4 5 6 7 8 9'
|
||||
|
||||
|
||||
obj = {
|
||||
name: 'Joe'
|
||||
hi: -> "Hello $@name."
|
||||
cya: -> "Hello $@name.".replace('Hello','Goodbye')
|
||||
hi: -> "Hello #@name."
|
||||
cya: -> "Hello #@name.".replace('Hello','Goodbye')
|
||||
}
|
||||
ok obj.hi() is "Hello Joe."
|
||||
ok obj.cya() is "Goodbye Joe."
|
||||
|
||||
|
||||
ok "With ${"quotes"}" is 'With quotes'
|
||||
ok 'With ${"quotes"}' is 'With ${"quotes"}'
|
||||
ok "With #{"quotes"}" is 'With quotes'
|
||||
ok 'With #{"quotes"}' is 'With #{"quotes"}'
|
||||
|
||||
ok "Where is ${obj["name"] + '?'}" is 'Where is Joe?'
|
||||
ok "Where is $obj.name?" is 'Where is Joe?'
|
||||
ok "Where is #{obj["name"] + '?'}" is 'Where is Joe?'
|
||||
ok "Where is #obj.name?" is 'Where is Joe?'
|
||||
|
||||
ok "Where is ${"the nested ${obj["name"]}"}?" is 'Where is the nested Joe?'
|
||||
ok "Hello ${world ? "$hello"}" is 'Hello World'
|
||||
ok "Where is #{"the nested #{obj["name"]}"}?" is 'Where is the nested Joe?'
|
||||
ok "Hello #{world ? "#hello"}" is 'Hello World'
|
||||
|
||||
ok "Hello ${"${"${obj["name"]}" + '!'}"}" is 'Hello Joe!'
|
||||
ok "Hello #{"#{"#{obj["name"]}" + '!'}"}" is 'Hello Joe!'
|
||||
|
||||
|
||||
a = """
|
||||
Hello ${ "Joe" }
|
||||
Hello #{ "Joe" }
|
||||
"""
|
||||
ok a is "Hello Joe"
|
||||
|
||||
|
@ -82,14 +82,14 @@ ok a is "Hello Joe"
|
|||
a = 1
|
||||
b = 2
|
||||
c = 3
|
||||
ok "$a$b$c" is '123'
|
||||
ok "#a#b#c" is '123'
|
||||
|
||||
|
||||
result = null
|
||||
stash = (str) -> result = str
|
||||
stash "a ${ ('aa').replace /a/g, 'b' } c"
|
||||
stash "a #{ ('aa').replace /a/g, 'b' } c"
|
||||
ok result is 'a bb c'
|
||||
|
||||
|
||||
foo = "hello"
|
||||
ok "${foo.replace("\"", "")}" is 'hello'
|
||||
ok "#{foo.replace("\"", "")}" is 'hello'
|
Loading…
Add table
Reference in a new issue