diff --git a/documentation/coffee/block_comment.coffee b/documentation/coffee/block_comment.coffee
index ed949f10..6b56a182 100644
--- a/documentation/coffee/block_comment.coffee
+++ b/documentation/coffee/block_comment.coffee
@@ -1,5 +1,5 @@
###
-CoffeeScript Compiler v0.9.6
+CoffeeScript Compiler v1.0.0
Released under the MIT License
###
diff --git a/documentation/coffee/parallel_assignment.coffee b/documentation/coffee/parallel_assignment.coffee
index b300e443..2b3fd348 100644
--- a/documentation/coffee/parallel_assignment.coffee
+++ b/documentation/coffee/parallel_assignment.coffee
@@ -3,3 +3,4 @@ theSwitch = 0
[theBait, theSwitch] = [theSwitch, theBait]
+
diff --git a/documentation/css/docs.css b/documentation/css/docs.css
index f859d753..b84c9575 100644
--- a/documentation/css/docs.css
+++ b/documentation/css/docs.css
@@ -64,6 +64,7 @@ code, pre, tt, textarea {
word-wrap: break-word;
}
tt {
+ display: inline-block;
background: #fff;
border: 1px solid #dedede;
padding: 0px 0.2em;
@@ -86,9 +87,10 @@ code, pre, tt, textarea {
div.code {
position: relative;
background: #fff;
- -webkit-box-shadow: 0px 0px 5px rgba(0,0,0,0.3);
- -moz-box-shadow: 0px 0px 5px rgba(0,0,0,0.3);
- box-shadow: 0px 0px 5px rgba(0,0,0,0.3);
+ border: 1px solid #d8d8d8;
+ -webkit-box-shadow: 0px 0px 4px rgba(0,0,0,0.23);
+ -moz-box-shadow: 0px 0px 4px rgba(0,0,0,0.23);
+ box-shadow: 0px 0px 4px rgba(0,0,0,0.23);
zoom: 1;
}
div.code .minibutton {
@@ -313,20 +315,23 @@ div.code {
width: 47%; right: 50%;
float: left;
}
- #repl_source {
- padding-left: 5px;
- width: 100%;
- height: 100%;
- border: 0;
- overflow-y: auto;
- resize: none;
- }
- #repl_results {
- text-transform: none;
- overflow-y: auto;
- left: 50%;
- border-left-color: #555;
+ #repl_source {
+ padding-left: 5px;
+ width: 100%;
+ height: 100%;
+ border: 0;
+ overflow-y: auto;
+ resize: none;
+ }
+ #repl_results_wrap {
+ white-space: pre;
}
+ #repl_results {
+ text-transform: none;
+ overflow-y: auto;
+ left: 50%;
+ border-left-color: #555;
+ }
/*----------------------------- Mini Buttons ---------------------------------*/
.minibutton {
diff --git a/documentation/docs/coffee-script.html b/documentation/docs/coffee-script.html
index 76c512b3..b694722c 100644
--- a/documentation/docs/coffee-script.html
+++ b/documentation/docs/coffee-script.html
@@ -12,7 +12,7 @@ execute all scripts present in text/coffeescript
tags.
The current CoffeeScript version number.
exports.VERSION = '0.9.6'
Words that cannot be used as identifiers in CoffeeScript code
exports.RESERVED = RESERVED
Expose helpers for testing.
exports.helpers = require './helpers'
Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison + require.registerExtension '.coffee', (content) -> compile content
The current CoffeeScript version number.
exports.VERSION = '1.0.0'
Words that cannot be used as identifiers in CoffeeScript code
exports.RESERVED = RESERVED
Expose helpers for testing.
exports.helpers = require './helpers'
Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
exports.compile = compile = (code, options = {}) ->
try
(parser.parse lexer.tokenize code).compile options
diff --git a/documentation/docs/command.html b/documentation/docs/command.html
index b0dddb0a..e5feb940 100644
--- a/documentation/docs/command.html
+++ b/documentation/docs/command.html
@@ -18,6 +18,7 @@ interactive REPL.
Switches that are still supported, but will cause a warning message.
DEPRECATED_SWITCHES = [
- ['--no-wrap', 'compile without the top-level function wrapper']
-]
-
-ALL_SWITCHES = SWITCHES.concat DEPRECATED_SWITCHES
Top-level objects shared by all the functions.
opts = {}
+]
Top-level objects shared by all the functions.
opts = {}
sources = []
-optionParser = null
Run coffee
by parsing passed options and determining what action to take.
+contents = []
+optionParser = null
Run coffee
by parsing passed options and determining what action to take.
Many flags cause us to divert before compiling anything. Flags passed after
--
will be passed verbatim to your script as arguments in process.argv
exports.run = ->
parseOptions()
- return usage() if opts.help
- return version() if opts.version
- return require './repl' if opts.interactive
- return compileStdio() if opts.stdio
- return compileScript null, sources[0] if opts.eval
- return require './repl' unless sources.length
- separator = sources.indexOf '--'
- flags = []
- if separator >= 0
- flags = sources.splice separator + 1
- sources.pop()
+ return forkNode() if opts.nodejs
+ return usage() if opts.help
+ return version() if opts.version
+ return require './repl' if opts.interactive
+ return compileStdio() if opts.stdio
+ return compileScript null, sources[0] if opts.eval
+ return require './repl' unless sources.length
if opts.run
- flags = sources.splice(1).concat flags
- process.ARGV = process.argv = process.argv.slice(0, 2).concat flags
- compileScripts()
Asynchronously read in each CoffeeScript in a list of source files and + opts.literals = sources.splice(1).concat opts.literals + process.ARGV = process.argv = process.argv.slice(0, 2).concat opts.literals + compileScripts()
Asynchronously read in each CoffeeScript in a list of source files and compile them. If a directory is passed, recursively compile all '.coffee' extension source files in it and all subdirectories.
compileScripts = ->
for source in sources
@@ -67,9 +62,14 @@ compile them. If a directory is passed, recursively compile all
for file in files
compile path.join(source, file)
else if topLevel or path.extname(source) is '.coffee'
- fs.readFile source, (err, code) -> compileScript(source, code.toString(), base)
- watch source, base if opts.watch
- compile source, true
Compile a single source script, containing the given code, according to the + fs.readFile source, (err, code) -> + if opts.join + contents[sources.indexOf source] = code.toString() + compileJoin() if helpers.compact(contents).length is sources.length + else + compileScript(source, code.toString(), base) + watch source, base if opts.watch and not opts.join + compile source, true
Compile a single source script, containing the given code, according to the
requested options. If evaluating the script directly sets __filename
,
__dirname
and module.filename
to be correct relative to the script's path.
compileScript = (file, input, base) ->
o = opts
@@ -93,14 +93,17 @@ requested options. If evaluating the script directly sets __filename
return if CoffeeScript.listeners('failure').length
return printLine err.message if o.watch
printWarn err.stack
- process.exit 1
Attach the appropriate listeners to compile scripts incoming over stdin, + process.exit 1
Attach the appropriate listeners to compile scripts incoming over stdin, and write them back to stdout.
compileStdio = ->
code = ''
stdin = process.openStdin()
stdin.on 'data', (buffer) ->
code += buffer.toString() if buffer
stdin.on 'end', ->
- compileScript null, code
Watch a source CoffeeScript file using fs.watchFile
, recompiling it every
+ compileScript null, code
After all of the source files are done being read, concatenate and compile +them together.
compileJoin = ->
+ code = contents.join '\n'
+ compileScript "concatenation", code, "concatenation"
Watch a source CoffeeScript file using fs.watchFile
, recompiling it every
time the file is updated. May be used in combination with other options,
such as --lint
or --print
.
watch = (source, base) ->
fs.watchFile source, {persistent: true, interval: 500}, (curr, prev) ->
@@ -112,7 +115,7 @@ are written out in cwd
as .js
files with the same name
directory can be customized with --output
.
writeJs = (source, js, base) ->
filename = path.basename(source, path.extname(source)) + '.js'
srcDir = path.dirname source
- baseDir = srcDir.substring base.length
+ baseDir = if base is '.' then srcDir else srcDir.substring base.length
dir = if opts.output then path.join opts.output, baseDir else srcDir
jsPath = path.join dir, filename
compile = ->
@@ -135,17 +138,23 @@ any errors or warnings that arise.
Use the OptionParser module to extract all options from
process.argv
that are specified in SWITCHES
.
parseOptions = ->
- optionParser = new optparse.OptionParser ALL_SWITCHES, BANNER
+ optionParser = new optparse.OptionParser SWITCHES, BANNER
o = opts = optionParser.parse process.argv.slice 2
o.compile or= !!o.output
o.run = not (o.compile or o.print or o.lint)
o.print = !! (o.print or (o.eval or o.stdio and o.compile))
- sources = o.arguments
- if opts['no-wrap']
- printWarn '--no-wrap is deprecated; please use --bare instead.'
The compile-time options to pass to the CoffeeScript compiler.
compileOptions = (fileName) -> {fileName, bare: opts.bare or opts['no-wrap']}
Print the --help
usage message and exit. Deprecated switches are not
+ sources = o.arguments
The compile-time options to pass to the CoffeeScript compiler.
compileOptions = (fileName) -> {fileName, bare: opts.bare}
Start up a new Node.js instance with the arguments in --nodejs
passed to
+the node
binary, preserving the other options.
forkNode = ->
+ nodeArgs = opts.nodejs.split /\s+/
+ args = process.argv[1..]
+ args.splice args.indexOf('--nodejs'), 2
+ spawn process.execPath, nodeArgs.concat(args),
+ cwd: process.cwd()
+ env: process.env
+ customFds: [0, 1, 2]
Print the --help
usage message and exit. Deprecated switches are not
shown.
usage = ->
printLine (new optparse.OptionParser SWITCHES, BANNER).help()
- process.exit 0
Print the --version
message and exit.
version = ->
+ process.exit 0
Print the --version
message and exit.
version = ->
printLine "CoffeeScript version #{CoffeeScript.VERSION}"
process.exit 0
diff --git a/documentation/docs/grammar.html b/documentation/docs/grammar.html
index 261d0d4d..09c70add 100644
--- a/documentation/docs/grammar.html
+++ b/documentation/docs/grammar.html
@@ -69,8 +69,8 @@ them somewhat circular.
An indented block of expressions. Note that the Rewriter will convert some postfix forms into blocks for us, by adjusting the token stream.
Block: [
- o 'INDENT Body OUTDENT', -> $2
o 'INDENT OUTDENT', -> new Expressions
+ o 'INDENT Body OUTDENT', -> $2
]
A literal identifier, a variable name or property.
Identifier: [
o 'IDENTIFIER', -> new Literal $1
]
Alphanumerics are separated from the other Literal matchers because @@ -185,10 +185,8 @@ and optional references to the superclass.
Ordinary function invocation, or a chained series of calls.
Invocation: [
o 'Value OptFuncExist Arguments', -> new Call $1, $3, $2
o 'Invocation OptFuncExist Arguments', -> new Call $1, $3, $2
- o 'SUPER', ->
- new Call 'super', [new Splat new Literal 'arguments']
- o 'SUPER Arguments', ->
- new Call 'super', $2
+ o 'SUPER', -> new Call 'super', [new Splat new Literal 'arguments']
+ o 'SUPER Arguments', -> new Call 'super', $2
]
An optional existence check on a function.
OptFuncExist: [
o '', -> no
o 'FUNC_EXIST', -> yes
@@ -262,14 +260,14 @@ or postfix, with a single expression. There is no do..while. ]
Array, object, and range comprehensions, at the most generic level. Comprehensions can either be normal, with a block of expressions to execute, or postfix, with a single expression.
For: [
- o 'Statement ForBody', -> new For $1, $2, $2.vars[0], $2.vars[1]
- o 'Expression ForBody', -> new For $1, $2, $2.vars[0], $2.vars[1]
- o 'ForBody Block', -> new For $2, $1, $1.vars[0], $1.vars[1]
+ o 'Statement ForBody', -> new For $1, $2
+ o 'Expression ForBody', -> new For $1, $2
+ o 'ForBody Block', -> new For $2, $1
]
ForBody: [
- o 'FOR Range', -> source: new Value($2), vars: []
- o 'ForStart ForSource', -> $2.own = $1.own; $2.vars = $1; $2
+ o 'FOR Range', -> source: new Value($2)
+ o 'ForStart ForSource', -> $2.own = $1.own; $2.name = $1[0]; $2.index = $1[1]; $2
]
ForStart: [
@@ -313,17 +311,14 @@ in fixed-size increments.
The most basic form of if is a condition and an action. The following if-related rules are broken up along these lines in order to avoid ambiguity.
IfBlock: [
- o 'IF Expression Block', -> new If $2, $3
- o 'UNLESS Expression Block', -> new If $2, $3, invert: true
- o 'IfBlock ELSE IF Expression Block', -> $1.addElse new If $4, $5
+ o 'IF Expression Block', -> new If $2, $3, type: $1
+ o 'IfBlock ELSE IF Expression Block', -> $1.addElse new If $4, $5, type: $3
o 'IfBlock ELSE Block', -> $1.addElse $3
]
The full complement of if expressions, including postfix one-liner if and unless.
If: [
o 'IfBlock'
- o 'Statement POST_IF Expression', -> new If $3, Expressions.wrap([$1]), statement: true
- o 'Expression POST_IF Expression', -> new If $3, Expressions.wrap([$1]), statement: true
- o 'Statement POST_UNLESS Expression', -> new If $3, Expressions.wrap([$1]), statement: true, invert: true
- o 'Expression POST_UNLESS Expression', -> new If $3, Expressions.wrap([$1]), statement: true, invert: true
+ o 'Statement POST_IF Expression', -> new If $3, Expressions.wrap([$1]), type: $2, statement: true
+ o 'Expression POST_IF Expression', -> new If $3, Expressions.wrap([$1]), type: $2, statement: true
]
Arithmetic and logical operators, working on one or more operands.
Here they are grouped by order of precedence. The actual precedence rules
are defined at the bottom of the page. It would be shorter if we could
@@ -381,8 +376,8 @@ down. Following these rules is what makes 2 + 3 * 4
parse as:
Finally, now what we have our grammar and our operators, we can create our Jison.Parser. We do this by processing all of our rules, recording all terminals (every symbol which does not appear as the name of a rule above) diff --git a/documentation/docs/helpers.html b/documentation/docs/helpers.html index 7d3fd988..ab32df89 100644 --- a/documentation/docs/helpers.html +++ b/documentation/docs/helpers.html @@ -4,9 +4,10 @@ arrays, count characters, that sort of thing.
Peek at the end of a given string to see if it matches a sequence.
exports.ends = (string, literal, back) ->
len = literal.length
literal is string.substr string.length - len - (back or 0), len
Trim out all falsy values from an array.
exports.compact = (array) ->
- item for item in array when item
Count the number of occurrences of a character in a string.
exports.count = (string, letter) ->
+ item for item in array when item
Count the number of occurrences of a string in a string.
exports.count = (string, substr) ->
num = pos = 0
- num++ while pos = 1 + string.indexOf letter, pos
+ return 1/0 unless substr.length
+ num++ while pos = 1 + string.indexOf substr, pos
num
Merge objects, returning a fresh copy with attributes from both sides.
Used every time Base#compile
is called, to allow properties in the
options hash to propagate down the tree without polluting other branches.
exports.merge = (options, overrides) ->
diff --git a/documentation/docs/lexer.html b/documentation/docs/lexer.html
index 6013e87a..2321da18 100644
--- a/documentation/docs/lexer.html
+++ b/documentation/docs/lexer.html
@@ -67,12 +67,14 @@ though is
means ===
otherwise.
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 contents of the string. This method allows us to have strings within -interpolations within strings, ad infinitum.
balancedString: (str, delimited, options = {}) ->
- stack = [delimited[0]]
+interpolations within strings, ad infinitum.
balancedString: (str, end) ->
+ stack = [end]
for i in [1...str.length]
- switch str.charAt i
+ switch letter = str.charAt i
when '\\'
i++
continue
- when stack[stack.length - 1][1]
+ when end
stack.pop()
- return str.slice 0, i + 1 unless stack.length
+ unless stack.length
+ return str.slice 0, i + 1
+ end = stack[stack.length - 1]
continue
- for pair in delimited when (open = pair[0]) is str.substr i, open.length
- stack.push pair
- i += open.length - 1
- break
- throw new Error "unterminated #{ stack.pop()[0] } on line #{ @line + 1 }"
Expand variables and expressions inside double-quoted strings using + if end is '}' and letter in ['"', "'"] + stack.push end = letter + else if end is '}' and letter is '{' + stack.push end = '}' + else if end is '"' and prev is '#' and letter is '{' + stack.push end = '}' + prev = letter + throw new Error "missing #{ stack.pop() }, starting on line #{ @line + 1 }"
Expand variables and expressions inside double-quoted strings using Ruby-like notation for substitution of arbitrary expressions.
"Hello #{name.capitalize()}."
@@ -339,7 +346,7 @@ token stream.
CoffeeScript-only keywords.
COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when']
COFFEE_KEYWORDS.push op for op of COFFEE_ALIASES =
@@ -397,9 +404,9 @@ token stream.
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 = [
- 'case', 'default', 'function', 'var', 'void', 'with', 'do'
+ 'case', 'default', 'function', 'var', 'void', 'with'
'const', 'let', 'enum', 'export', 'import', 'native'
- '__hasProp', '__extends', '__slice'
+ '__hasProp', '__extends', '__slice', '__bind', '__indexOf'
]
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
@@ -427,7 +434,7 @@ be used as identifiers or properties.
Compound assignment tokens.
COMPOUND_ASSIGN = [
'-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='
-]
Unary tokens.
UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE']
Logical tokens.
LOGIC = ['&&', '||', '&', '|', '^']
Bit-shifting tokens.
SHIFT = ['<<', '>>', '>>>']
Comparison tokens.
COMPARE = ['==', '!=', '<', '>', '<=', '>=']
Mathematical tokens.
MATH = ['*', '/', '%']
Relational tokens that are negatable with not
prefix.
RELATION = ['IN', 'OF', 'INSTANCEOF']
Boolean tokens.
BOOL = ['TRUE', 'FALSE', 'NULL', 'UNDEFINED']
Tokens which a regular expression will never immediately follow, but which +]
Unary tokens.
UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO']
Logical tokens.
LOGIC = ['&&', '||', '&', '|', '^']
Bit-shifting tokens.
SHIFT = ['<<', '>>', '>>>']
Comparison tokens.
COMPARE = ['==', '!=', '<', '>', '<=', '>=']
Mathematical tokens.
MATH = ['*', '/', '%']
Relational tokens that are negatable with not
prefix.
RELATION = ['IN', 'OF', 'INSTANCEOF']
Boolean tokens.
BOOL = ['TRUE', 'FALSE', 'NULL', 'UNDEFINED']
Tokens which a regular expression will never immediately follow, but which a division operator might.
See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
diff --git a/documentation/docs/nodes.html b/documentation/docs/nodes.html index efed2923..064354c0 100644 --- a/documentation/docs/nodes.html +++ b/documentation/docs/nodes.html @@ -24,14 +24,14 @@ return results).Statements converted into expressions via closure-wrapping share a scope object with their parent closure, to preserve the expected lexical scope.
compileClosure: (o) ->
- if @containsPureStatement()
- throw SyntaxError 'cannot include a pure statement in an expression.'
- o.sharedScope = o.scope
+ if @jumps()
+ throw SyntaxError 'cannot use a pure statement in an expression.'
+ o.sharedScope = yes
Closure.wrap(this).compileNode o
If the code generation wishes to use the result of a complex expression in multiple places, ensure that the expression is only ever evaluated once, by assigning it to a temporary variable. Pass a level to precompile.
cache: (o, level, reused) ->
@@ -58,9 +58,10 @@ scope boundaries.
Is this node of a certain type, or does it contain the type?
containsType: (type) ->
- this instanceof type or @contains (node) -> node instanceof type
Convenience for the most common use of contains. Does the node contain -a pure statement?
containsPureStatement: ->
- @isPureStatement() or @contains (node) -> node.isPureStatement()
toString
representation of the node, for inspecting the parse tree.
+ this instanceof type or @contains (node) -> node instanceof type
Pull out the last non-comment node of a node list.
lastNonComment: (list) ->
+ i = list.length
+ return list[i] while i-- when list[i] not instanceof Comment
+ null
toString
representation of the node, for inspecting the parse tree.
This is what coffee --nodes
prints out.
toString: (idt = '', name = @constructor.name) ->
tree = '\n' + idt + name
tree += '?' if @soak
@@ -87,7 +88,7 @@ This is what coffee --nodes
prints out.
children: []
isStatement : NO
- isPureStatement : NO
+ jumps : NO
isComplex : YES
isChainable : NO
isAssignable : NO
@@ -110,9 +111,13 @@ it back out.
An Expressions node does not return its entire body, rather it + no + + jumps: (o) -> + for exp in @expressions + return exp if exp.jumps o
An Expressions node does not return its entire body, rather it ensures that the final expression is returned.
makeReturn: ->
len = @expressions.length
while len--
@@ -153,7 +158,7 @@ declarations of all inner variables pushed up to the top.
Break and continue must be treated as pure statements -- they lose their -meaning when wrapped in a closure.
isPureStatement: ->
- @value in ['break', 'continue', 'debugger']
+ if @isStatement() then this else new Return this
isAssignable: ->
IDENTIFIER.test @value
+ isStatement: ->
+ @value in ['break', 'continue', 'debugger']
+
isComplex: NO
assigns: (name) ->
name is @value
- compile: ->
- if @value.reserved then "\"#{@value}\"" else @value
+ jumps: (o) ->
+ return no unless @isStatement()
+ if not (o and (o.loop or o.block and (@value isnt 'continue'))) then this else no
+
+ compileNode: (o) ->
+ code = if @value.reserved then "\"#{@value}\"" else @value
+ if @isStatement() then "#{@tab}#{code};" else code
toString: ->
- ' "' + @value + '"'
A return
is a pureStatement -- wrapping it in a closure wouldn't
+ ' "' + @value + '"'
A return
is a pureStatement -- wrapping it in a closure wouldn't
make sense.
exports.Return = class Return extends Base
constructor: (@expression) ->
children: ['expression']
isStatement: YES
- isPureStatement: YES
makeReturn: THIS
+ jumps: THIS
compile: (o, level) ->
expr = @expression?.makeReturn()
if expr and expr not instanceof Return then expr.compile o, level else super o, level
compileNode: (o) ->
- o.level = LEVEL_PAREN
- @tab + "return#{ if @expression then ' ' + @expression.compile o else '' };"
A value, variable or literal or parenthesized, indexed or dotted into, + @tab + "return#{ if @expression then ' ' + @expression.compile(o, LEVEL_PAREN) else '' };"
A value, variable or literal or parenthesized, indexed or dotted into, or vanilla.
exports.Value = class Value extends Base
constructor: (base, props, tag) ->
return base if not props and base instanceof Value
@@ -215,12 +225,12 @@ or vanilla.
Add a property access to the list.
push: (prop) ->
+ children: ['base', 'properties']
Add a property access to the list.
push: (prop) ->
@properties.push prop
this
hasProperties: ->
- !!@properties.length
Some boolean checks for the benefit of other nodes.
isArray : -> not @properties.length and @base instanceof Arr
+ !!@properties.length
Some boolean checks for the benefit of other nodes.
isArray : -> not @properties.length and @base instanceof Arr
isComplex : -> @hasProperties() or @base.isComplex()
isAssignable : -> @hasProperties() or @base.isAssignable()
isSimpleNumber : -> @base instanceof Literal and SIMPLENUM.test @base.value
@@ -231,6 +241,7 @@ or vanilla.
The value can be unwrapped as its inner node, if there are no attached + if @properties.length then super() else @base.makeReturn()
The value can be unwrapped as its inner node, if there are no attached properties.
unwrap: ->
- if @properties.length then this else @base
A reference has base part (this
value) and name part.
+ if @properties.length then this else @base
A reference has base part (this
value) and name part.
We cache them separately for compiling complex expressions.
a()[b()] ?= c
-> (_base = a())[_name = b()] ? _base[_name] = c
cacheReference: (o) ->
name = last @properties
@@ -257,7 +268,7 @@ We cache them separately for compiling complex expressions.
nref = new Literal o.scope.freeVariable 'name'
name = new Index new Assign nref, name.index
nref = new Index nref
- [base.push(name), new Value(bref or base.base, [nref or name])]
We compile a value to JavaScript by compiling and joining each property. + [base.push(name), new Value(bref or base.base, [nref or name])]
We compile a value to JavaScript by compiling and joining each property.
Things get much more interesting if the chain of properties has soak
operators ?.
interspersed. Then we have to take care not to accidentally
evaluate anything twice when building the soak chain.
compileNode: (o) ->
@@ -266,7 +277,7 @@ evaluate anything twice when building the soak chain.
Unfold a soak into an If
: a?.b
-> a.b if a?
unfoldSoak: (o) ->
+ code
Unfold a soak into an If
: a?.b
-> a.b if a?
unfoldSoak: (o) ->
if ifn = @base.unfoldSoak o
Array::push.apply ifn.body.properties, @properties
return ifn
@@ -279,31 +290,30 @@ evaluate anything twice when building the soak chain.
CoffeeScript passes through block comments as JavaScript block comments + null
CoffeeScript passes through block comments as JavaScript block comments at the same position.
exports.Comment = class Comment extends Base
constructor: (@comment) ->
- isPureStatement: YES
isStatement: YES
makeReturn: THIS
compileNode: (o, level) ->
code = '/*' + multident(@comment, @tab) + '*/'
code = o.indent + code if (level or o.level) is LEVEL_TOP
- code
Node for a function invocation. Takes care of converting super()
calls into
+ code
Node for a function invocation. Takes care of converting super()
calls into
calls against the prototype's function of the same name.
exports.Call = class Call extends Base
constructor: (variable, @args = [], @soak) ->
@isNew = false
@isSuper = variable is 'super'
@variable = if @isSuper then null else variable
- children: ['variable', 'args']
Tag this invocation as creating a new instance.
newInstance: ->
+ children: ['variable', 'args']
Tag this invocation as creating a new instance.
newInstance: ->
base = @variable.base or @variable
if base instanceof Call
base.newInstance()
else
@isNew = true
- this
Grab the reference to the superclass's implementation of the current + this
Grab the reference to the superclass's implementation of the current method.
superReference: (o) ->
{method} = o.scope
throw SyntaxError 'cannot call super outside of a function.' unless method
@@ -312,7 +322,7 @@ method.
Soaked chained invocations unfold into if/else ternary structures.
unfoldSoak: (o) ->
+ "#{name}.__super__.constructor"
Soaked chained invocations unfold into if/else ternary structures.
unfoldSoak: (o) ->
if @soak
if @variable
return ifn if ifn = unfoldSoak o, this, 'variable'
@@ -341,7 +351,7 @@ method.
Compile a vanilla function call.
compileNode: (o) ->
+ ifn
Compile a vanilla function call.
compileNode: (o) ->
@variable?.front = @front
if code = Splat.compileSplattedArray o, @args, true
return @compileSplat o, code
@@ -349,9 +359,9 @@ method.
super()
is converted into a call against the superclass's implementation
+ (if @isNew then 'new ' else '') + @variable.compile(o, LEVEL_ACCESS) + "(#{args})"
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})"
If you call a function with a splat, it's converted into a JavaScript + "#{@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.
If it's a constructor, then things get real tricky. We have to inject an
inner constructor in order to be able to pass the varargs.
compileSplat: (o, splatArgs) ->
@@ -360,8 +370,10 @@ inner constructor in order to be able to pass the varargs.
{idt}ctor.prototype = func.prototype; -{idt}var child = new ctor, result = func.apply(child, args); -{idt}return typeof result === "object" ? result : child; -{@tab}})(#{ @variable.compile o, LEVEL_LIST }, #{splatArgs}, function() {})
exports.Extends = class Extends extends Base
+ "#{fun}.apply(#{ref}, #{splatArgs})"
Node to extend an object's prototype with an ancestor object.
+After goog.inherits
from the
+Closure Library.
exports.Extends = class Extends extends Base
constructor: (@child, @parent) ->
- children: ['child', 'parent']
Node to extend an object's prototype with an ancestor object.
-After goog.inherits
from the
-Closure Library.
compile: (o) ->
+ children: ['child', 'parent']
Hooks one constructor into another's prototype chain.
compile: (o) ->
utility 'hasProp'
- new Call(new Value(new Literal utility 'extends'), [@child, @parent]).compile o
Hooks one constructor into another's prototype chain.
exports.Access = class Access extends Base
+ new Call(new Value(new Literal utility 'extends'), [@child, @parent]).compile o
A .
access into a property of a value, or the ::
shorthand for
+an access into the object's prototype.
exports.Access = class Access extends Base
constructor: (@name, tag) ->
+ @name.asKey = yes
@proto = if tag is 'proto' then '.prototype' else ''
@soak = tag is 'soak'
@@ -395,8 +406,7 @@ After goog.inherits
from the
name = @name.compile o
@proto + if IS_STRING.test name then "[#{name}]" else ".#{name}"
- isComplex: NO
A .
access into a property of a value, or the ::
shorthand for
-an access into the object's prototype.
exports.Index = class Index extends Base
+ isComplex: NO
A [ ... ]
indexed access into an array or object.
exports.Index = class Index extends Base
constructor: (@index) ->
children: ['index']
@@ -405,23 +415,24 @@ an access into the object's prototype.
A [ ... ]
indexed access into an array or object.
exports.Range = class Range extends Base
+ @index.isComplex()
A range literal. Ranges can be used to extract portions (slices) of arrays, +to specify a range for comprehensions, or as a value, to be expanded into the +corresponding array of integers at runtime.
exports.Range = class Range extends Base
children: ['from', 'to']
constructor: (@from, @to, tag) ->
@exclusive = tag is 'exclusive'
- @equals = if @exclusive then '' else '='
A range literal. Ranges can be used to extract portions (slices) of arrays, -to specify a range for comprehensions, or as a value, to be expanded into the -corresponding array of integers at runtime.
compileVariables: (o) ->
+ @equals = if @exclusive then '' else '='
Compiles the range's source variables -- where it starts and where it ends. +But only if they need to be cached to avoid double evaluation.
compileVariables: (o) ->
o = merge(o, top: true)
[@from, @fromVar] = @from.cache o, LEVEL_LIST
[@to, @toVar] = @to.cache o, LEVEL_LIST
[@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
parts = []
parts.push @from if @from isnt @fromVar
- parts.push @to if @to isnt @toVar
Compiles the range's source variables -- where it starts and where it ends. -But only if they need to be cached to avoid double evaluation.
compileNode: (o) ->
+ parts.push @to if @to isnt @toVar
When compiled normally, the range returns the contents of the for loop +needed to iterate over the values in the range. Used by comprehensions.
compileNode: (o) ->
@compileVariables o
return @compileArray(o) unless o.index
return @compileSimple(o) if @fromNum and @toNum
@@ -432,8 +443,7 @@ But only if they need to be cached to avoid double evaluation. <
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}"
When compiled normally, the range returns the contents of the for loop -needed to iterate over the values in the range. Used by comprehensions.
compileSimple: (o) ->
+ "#{vars}; #{compare}; #{incr}"
Compile a simple range comprehension, with integers.
compileSimple: (o) ->
[from, to] = [+@fromNum, +@toNum]
idx = del o, 'index'
step = del o, 'step'
@@ -441,7 +451,7 @@ needed to iterate over the values in the range. Used by comprehensions.
if from <= to
"#{idx} = #{from}; #{idx} <#{@equals} #{to}; #{step or "#{idx}++"}"
else
- "#{idx} = #{from}; #{idx} >#{@equals} #{to}; #{step or "#{idx}--"}"
Compile a simple range comprehension, with integers.
compileArray: (o) ->
+ "#{idx} = #{from}; #{idx} >#{@equals} #{to}; #{step or "#{idx}--"}"
When used as a value, expand the range into the equivalent array.
compileArray: (o) ->
if @fromNum and @toNum and Math.abs(@fromNum - @toNum) <= 20
range = [+@fromNum..+@toNum]
range.pop() if @exclusive
@@ -458,21 +468,27 @@ needed to iterate over the values in the range. Used by comprehensions.
clause = "#{@fromVar} <= #{@toVar} ?"
body = "var #{vars}; #{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)"
When used as a value, expand the range into the equivalent array.
exports.Slice = class Slice extends Base
+ "(function() {#{pre}\n#{idt}for (#{body})#{post}}).call(this)"
An array slice literal. Unlike JavaScript's Array#slice
, the second parameter
+specifies the index of the end of the slice, just as the first parameter
+is the index of the beginning.
exports.Slice = class Slice extends Base
children: ['range']
constructor: (@range) ->
- super()
-
- compileNode: (o) ->
- from = if @range.from then @range.from.compile(o) else '0'
- to = if @range.to then @range.to.compile(o) else ''
- to += if not to or @range.exclusive then '' else ' + 1'
- to = ', ' + to if to
- ".slice(#{from}#{to})"
An array slice literal. Unlike JavaScript's Array#slice
, the second parameter
-specifies the index of the end of the slice, just as the first parameter
-is the index of the beginning.
exports.Obj = class Obj extends Base
+ super()
We have to be careful when trying to slice through the end of the array,
+9e9
is used because not all implementations respect undefined
or 1/0
.
+9e9
should be safe because 9e9
> 2**32
, the max array length.
compileNode: (o) ->
+ {to, from} = @range
+ fromStr = from and from.compile(o, LEVEL_PAREN) or '0'
+ compiled = to and to.compile o, LEVEL_PAREN
+ if to and not (not @range.exclusive and +compiled is -1)
+ toStr = ', ' + if @range.exclusive
+ compiled
+ else if SIMPLENUM.test compiled
+ (+compiled + 1).toString()
+ else
+ "(#{compiled} + 1) || 9e9"
+ ".slice(#{ fromStr }#{ toStr or '' })"
An object literal, nothing fancy.
exports.Obj = class Obj extends Base
constructor: (props, @generated = false) ->
@objects = @properties = props or []
@@ -482,8 +498,7 @@ is the index of the beginning.
An object literal, nothing fancy.
exports.Arr = class Arr extends Base
+ no
An array literal.
exports.Arr = class Arr extends Base
constructor: (objs) ->
@objects = objs or []
@@ -521,31 +538,34 @@ is the index of the beginning.
An array literal.
exports.Class = class Class extends Base
+ no
The CoffeeScript class definition. +Initialize a Class with its name, an optional superclass, and a +list of prototype property assignments.
exports.Class = class Class extends Base
constructor: (@variable, @parent, @body = new Expressions) ->
@boundFuncs = []
+ @body.classBody = yes
- children: ['variable', 'parent', 'body']
The CoffeeScript class definition. -Initialize a Class with its name, an optional superclass, and a -list of prototype property assignments.
determineName: ->
+ children: ['variable', 'parent', 'body']
Figure out the appropriate name for the constructor function of this class.
determineName: ->
return null unless @variable
decl = if tail = last @variable.properties
tail instanceof Access and tail.name.value
else
@variable.base.value
- decl and= IDENTIFIER.test(decl) and decl
Figure out the appropriate name for the constructor function of this class.
setContext: (name) ->
+ decl and= IDENTIFIER.test(decl) and decl
For all this
-references and bound functions in the class definition,
+this
is the Class being constructed.
setContext: (name) ->
@body.traverseChildren false, (node) ->
+ return false if node.classBody
if node instanceof Literal and node.value is 'this'
node.value = name
else if node instanceof Code
node.klass = name
- node.context = name if node.bound
For all this
-references and bound functions in the class definition,
-this
is the Class being constructed.
addBoundFunctions: (o) ->
+ node.context = name if node.bound
Ensure that all functions bound to the instance are proxied in the +constructor.
addBoundFunctions: (o) ->
if @boundFuncs.length
for bvar in @boundFuncs
bname = bvar.compile o
- @ctor.body.unshift new Literal "this.#{bname} = #{utility 'bind'}(this.#{bname}, this);"
Ensure that all functions bound to the instance are proxied in the -constructor.
addProperties: (node, name) ->
+ @ctor.body.unshift new Literal "this.#{bname} = #{utility 'bind'}(this.#{bname}, this);"
Merge the properties from a top-level object as prototypal properties +on the class.
addProperties: (node, name) ->
props = node.base.properties.slice 0
while assign = props.shift()
if assign instanceof Assign
@@ -567,23 +587,24 @@ constructor.
Merge the properties from a top-level object as prototypal properties -on the class.
walkBody: (name) ->
+ assign
Walk the body of the class, looking for prototype properties to be converted.
walkBody: (name) ->
@traverseChildren false, (child) =>
return false if child instanceof Class
if child instanceof Expressions
for node, i in exps = child.expressions
if node instanceof Value and node.isObject(true)
exps[i] = @addProperties node, name
- child.expressions = exps = flatten exps
Walk the body of the class, looking for prototype properties to be converted.
ensureConstructor: (name) ->
+ child.expressions = exps = flatten exps
Make sure that a constructor is defined for the class, and properly +configured.
ensureConstructor: (name) ->
if not @ctor
@ctor = new Code
@ctor.body.push new Call 'super', [new Splat new Literal 'arguments'] if @parent
@body.expressions.unshift @ctor
@ctor.ctor = @ctor.name = name
@ctor.klass = null
- @ctor.noReturn = yes
Make sure that a constructor is defined for the class, and properly -configured.
compileNode: (o) ->
+ @ctor.noReturn = yes
Instead of generating the JavaScript string directly, we build up the +equivalent syntax tree and compile that, in pieces. You can see the +constructor, property assignments, and inheritance getting built out below.
compileNode: (o) ->
decl = @determineName()
name = decl or @name or '_Class'
lname = new Literal name
@@ -595,14 +616,12 @@ configured.
Instead of generating the JavaScript string directly, we build up the -equivalent syntax tree and compile that, in pieces. You can see the -constructor, property assignments, and inheritance getting built out below.
exports.Assign = class Assign extends Base
+ klass.compile o
The Assign is used to assign a local variable to value, or to set the +property of an object -- including within object literals.
exports.Assign = class Assign extends Base
constructor: (@variable, @value, @context, options) ->
- @param = options and options.param
The Assign is used to assign a local variable to value, or to set the -property of an object -- including within object literals.
METHOD_DEF: /^(?:(\S+)\.prototype\.|\S+?)?\b([$A-Za-z_][$\w]*)$/
+ @param = options and options.param
Matchers for detecting class/method names
METHOD_DEF: /^(?:(\S+)\.prototype\.|\S+?)?\b([$A-Za-z_][$\w]*)$/
children: ['variable', 'value']
@@ -610,7 +629,10 @@ property of an object -- including within object literals.
Matchers for detecting class/method names
compileNode: (o) ->
+ unfoldSoak o, this, 'variable'
Compile an assignment, delegating to compilePatternMatch
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.
compileNode: (o) ->
if isValue = @variable instanceof Value
return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
return @compileSplice o if @variable.isSplice()
@@ -629,19 +651,16 @@ property of an object -- including within object literals.
Compile an assignment, delegating to compilePatternMatch
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.
compilePatternMatch: (o) ->
+ if o.level <= LEVEL_LIST 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. +See the ECMAScript Harmony Wiki +for details.
compilePatternMatch: (o) ->
top = o.level is LEVEL_TOP
{value} = this
{objects} = @variable.base
return value.compile o unless olen = objects.length
isObject = @variable.isObject()
- if top and olen is 1 and (obj = objects[0]) not instanceof Splat
Brief implementation of recursive pattern matching, when assigning array or -object literals to a value. Peeks at their properties to assign inner names. -See the ECMAScript Harmony Wiki -for details.
if obj instanceof Assign
+ if top and olen is 1 and (obj = objects[0]) not instanceof Splat
Unroll simplest cases: {v} = x
-> v = x.v
if obj instanceof Assign
{variable: {base: idx}, value: obj} = obj
else
if obj.base instanceof Parens
@@ -661,10 +680,10 @@ for details.
Unroll simplest cases: {v} = x
-> v = x.v
idx = i
+ for obj, i in objects
A regular array pattern-match.
idx = i
if isObject
- if obj instanceof Assign
A regular array pattern-match.
{variable: {base: idx}, value: obj} = obj
- else
A regular object pattern-match.
if obj.base instanceof Parens
+ if obj instanceof Assign
A regular object pattern-match.
{variable: {base: idx}, value: obj} = obj
+ else
A shorthand {a, b, @c} = val
pattern-match.
if obj.base instanceof Parens
[obj, idx] = new Value(obj.unwrapAll()).cacheReference o
else
idx = if obj.this then obj.properties[0].name else obj
@@ -691,26 +710,29 @@ for details.
A shorthand {a, b, @c} = val
pattern-match.
compileConditional: (o) ->
- [left, rite] = @variable.cacheReference o
- new Op(@context.slice(0, -1), left, new Assign(rite, @value, '=')).compile o
When compiling a conditional assignment, take care to ensure that the + if o.level < LEVEL_LIST then code else "(#{code})"
When compiling a conditional assignment, take care to ensure that the operands are only evaluated once, even though we have to reference them -more than once.
compileSplice: (o) ->
- {range} = @variable.properties.pop()
- name = @variable.compile o
- excl = range.exclusive
- from = if range.from then range.from.compile(o) else '0'
- to = "#{name}.length" unless range.to
- unless to
- if range.from and range.from.isSimpleNumber() and range.to.isSimpleNumber()
- to = +range.to.compile(o) - +from
- to += 1 unless excl
+more than once.
compileConditional: (o) ->
+ [left, rite] = @variable.cacheReference o
+ new Op(@context.slice(0, -1), left, new Assign(rite, @value, '=')).compile o
Compile the assignment from an array splice literal, using JavaScript's
+Array#splice
method.
compileSplice: (o) ->
+ {range: {from, to, exclusive}} = @variable.properties.pop()
+ name = @variable.compile o
+ [fromDecl, fromRef] = from?.cache(o, LEVEL_OP) or ['0', '0']
+ if to
+ if from?.isSimpleNumber() and to.isSimpleNumber()
+ to = +to.compile(o) - +fromRef
+ to += 1 unless exclusive
else
- to = range.to.compile(o) + ' - ' + from
- to += ' + 1' unless excl
- val = @value.compile(o)
- "[].splice.apply(#{name}, [#{from}, #{to}].concat(#{val}))"
Compile the assignment from an array splice literal, using JavaScript's
-Array#splice
method.
exports.Code = class Code extends Base
+ to = to.compile(o) + ' - ' + fromRef
+ to += ' + 1' unless exclusive
+ else
+ to = "9e9"
+ [valDef, valRef] = @value.cache o, LEVEL_LIST
+ code = "[].splice.apply(#{name}, [#{fromDecl}, #{to}].concat(#{valDef})), #{valRef}"
+ if o.level > LEVEL_TOP then "(#{code})" else code
A function definition. This is the only node that creates a new Scope. +When for the purposes of walking the contents of a function body, the Code +has no children -- they're within the inner scope.
exports.Code = class Code extends Base
constructor: (params, body, tag) ->
@params = params or []
@body = body or new Expressions
@@ -719,12 +741,15 @@ more than once.
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 -has no children -- they're within the inner scope.
compileNode: (o) ->
- sharedScope = del o, 'sharedScope'
- o.scope = sharedScope or new Scope o.scope, @body, this
- o.scope.shared = yes if sharedScope
+ isStatement: -> !!@ctor
+
+ jumps: NO
Compilation creates a new scope unless explicitly asked to share with the
+outer scope. Handles splat parameters in the parameter list by peeking at
+the JavaScript arguments
objects. If the function is bound with the =>
+arrow, generates a wrapper that saves the current value of this
through
+a closure.
compileNode: (o) ->
+ o.scope = new Scope o.scope, @body, this
+ o.scope.shared = del o, 'sharedScope'
o.indent += TAB
delete o.bare
delete o.globals
@@ -759,13 +784,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 peeking at
-the JavaScript arguments
objects. If the function is bound with the =>
-arrow, generates a wrapper that saves the current value of this
through
-a closure.
traverseChildren: (crossScope, func) ->
- super(crossScope, func) if crossScope
Short-circuit traverseChildren
method to prevent it from crossing scope boundaries
-unless crossScope
is true
.
exports.Param = class Param extends Base
+ if @front or (o.level >= LEVEL_ACCESS) then "(#{code})" else code
Short-circuit traverseChildren
method to prevent it from crossing scope boundaries
+unless crossScope
is true
.
traverseChildren: (crossScope, func) ->
+ super(crossScope, func) if crossScope
A parameter in a function definition. Beyond a typical Javascript parameter, +these parameters can also attach themselves to the context of the function, +as well as be a splat, gathering up a group of parameters into an array.
exports.Param = class Param extends Base
constructor: (@name, @value, @splat) ->
children: ['name', 'value']
@@ -786,9 +809,8 @@ unless crossScope
is true
.
A parameter in a function definition. Beyond a typical Javascript parameter, -these parameters can also attach themselves to the context of the function, -as well as be a splat, gathering up a group of parameters into an array.
exports.Splat = class Splat extends Base
+ @name.isComplex()
A splat, either as a parameter to a function, an argument to a call, +or as part of a destructuring assignment.
exports.Splat = class Splat extends Base
children: ['name']
@@ -801,8 +823,8 @@ as well as be a splat, gathering up a group of parameters into an array.
@name.assigns name
compile: (o) ->
- if @index? then @compileParam o else @name.compile o
A splat, either as a parameter to a function, an argument to a call, -or as part of a destructuring assignment.
@compileSplattedArray: (o, list, apply) ->
+ if @index? then @compileParam o else @name.compile o
Utility function that converts arbitrary number of elements, mixed with +splats, to a proper array.
@compileSplattedArray: (o, list, apply) ->
index = -1
continue while (node = list[++index]) and node not instanceof Splat
return '' if index >= list.length
@@ -818,8 +840,9 @@ or as part of a destructuring assignment.
Utility function that converts arbitrary number of elements, mixed with -splats, to a proper array.
exports.While = class While extends Base
+ "[#{ base.join ', ' }].concat(#{ args.join ', ' })"
A while loop, the only sort of low-level loop exposed by CoffeeScript. From +it, all other loops can be manufactured. Useful in cases where you need more +flexibility or more speed than a comprehension can provide.
exports.While = class While extends Base
constructor: (condition, options) ->
@condition = if options?.invert then condition.invert() else condition
@guard = options?.guard
@@ -835,15 +858,14 @@ splats, to a proper array.
A while loop, the only sort of low-level loop exposed by CoffeeScript. From -it, all other loops can be manufactured. Useful in cases where you need more -flexibility or more speed than a comprehension can provide.
compileNode: (o) ->
+ return no unless expressions.length
+ for node in expressions
+ return node if node.jumps loop: yes
+ no
The main difference from a JavaScript while is that the CoffeeScript +while can be used as a part of a larger expression -- while loops may +return an array containing the computed result of each iteration.
compileNode: (o) ->
o.indent += TAB
set = ''
{body} = this
@@ -858,13 +880,12 @@ flexibility or more speed than a comprehension can provide.
The main difference from a JavaScript while is that the CoffeeScript -while can be used as a part of a larger expression -- while loops may -return an array containing the computed result of each iteration.
exports.Op = class Op extends Base
+ code += "\n#{@tab}return #{rvar};"
+ code
Simple Arithmetic and logical operations. Performs some conversion from +CoffeeScript operations into their JavaScript equivalents.
exports.Op = class Op extends Base
constructor: (op, first, second, flip) ->
return new In first, second if op is 'in'
+ return new Call first, first.params or [] if op is 'do'
if op is 'new'
return first.newInstance() if first instanceof Call
first = new Parens first if first instanceof Code and first.bound
@@ -872,18 +893,20 @@ return an array containing the computed result of each iteration.
@first = first
@second = second
@flip = !!flip
- return this
Simple Arithmetic and logical operations. Performs some conversion from -CoffeeScript operations into their JavaScript equivalents.
CONVERSIONS =
+ return this
The map of conversions from CoffeeScript to JavaScript symbols.
CONVERSIONS =
'==': '==='
'!=': '!=='
- 'of': 'in'
The map of conversions from CoffeeScript to JavaScript symbols.
INVERSIONS =
+ 'of': 'in'
The map of invertible operators.
INVERSIONS =
'!==': '==='
'===': '!=='
children: ['first', 'second']
+ isSimpleNumber: NO
+
isUnary: ->
- not @second
The map of invertible operators.
isChainable: ->
+ not @second
Am I capable of +Python-style comparison chaining?
isChainable: ->
@operator in ['<', '>', '>=', '<=', '===', '!==']
invert: ->
@@ -923,8 +946,12 @@ CoffeeScript operations into their JavaScript equivalents.
Am I capable of -Python-style comparison chaining?
compileChain: (o) ->
+ if o.level <= LEVEL_OP then code else "(#{code})"
Mimic Python's chained comparisons when multiple comparison operators are +used sequentially. For example:
+ +bin/coffee -e 'console.log 50 < 65 > 10'
+true
+
compileChain: (o) ->
[@first.second, shared] = @first.second.cache o
fst = @first.compile o, LEVEL_OP
code = "#{fst} #{if @invert then '&&' else '||'} #{ shared.compile o } #{@operator} #{ @second.compile o, LEVEL_OP }"
@@ -937,12 +964,7 @@ CoffeeScript operations into their JavaScript equivalents.
Mimic Python's chained comparisons when multiple comparison operators are -used sequentially. For example:
- -bin/coffee -e 'console.log 50 < 65 > 10'
-true
-
compileUnary: (o) ->
+ new Existence(fst).compile(o) + " ? #{ref} : #{ @second.compile o, LEVEL_LIST }"
Compile a unary Op.
compileUnary: (o) ->
parts = [op = @operator]
parts.push ' ' if op in ['new', 'typeof', 'delete'] or
op in ['+', '-'] and @first instanceof Op and @first.operator is op
@@ -951,7 +973,7 @@ true
parts.join ''
toString: (idt) ->
- super idt, @constructor.name + ' ' + @operator
Compile a unary Op.
exports.In = class In extends Base
+ super idt, @constructor.name + ' ' + @operator
exports.In = class In extends Base
constructor: (@object, @array) ->
children: ['object', 'array']
@@ -981,17 +1003,20 @@ true
if o.level < LEVEL_LIST then code else "(#{code})"
toString: (idt) ->
- super idt, @constructor.name + if @negated then '!' else ''
exports.Try = class Try extends Base
+ super idt, @constructor.name + if @negated then '!' else ''
A classic try/catch/finally block.
exports.Try = class Try extends Base
constructor: (@attempt, @error, @recovery, @ensure) ->
children: ['attempt', 'recovery', 'ensure']
isStatement: YES
+ jumps: (o) -> @attempt.jumps(o) or @recovery?.jumps(o)
+
makeReturn: ->
@attempt = @attempt .makeReturn() if @attempt
@recovery = @recovery.makeReturn() if @recovery
- this
A classic try/catch/finally block.
compileNode: (o) ->
+ this
Compilation is more or less as you would expect -- the finally clause +is optional, the catch is not.
compileNode: (o) ->
o.indent += TAB
errorPart = if @error then " (#{ @error.compile o }) " else ' '
catchPart = if @recovery
@@ -999,20 +1024,21 @@ true
else unless @ensure or @recovery
' catch (_e) {}'
"""
-
-#DIVIDER
- """ + if @ensure then " finally {\n#{ @ensure.compile o, LEVEL_TOP }\n#{@tab}}" else ''
Compilation is more or less as you would expect -- the finally clause -is optional, the catch is not.
{@tab}try { -{ @attempt.compile o, LEVEL_TOP } -{@tab}}#{ catchPart or '' }
exports.Throw = class Throw extends Base
+ #{@tab}try {
+ #{ @attempt.compile o, LEVEL_TOP }
+ #{@tab}}#{ catchPart or '' }
+ """ + if @ensure then " finally {\n#{ @ensure.compile o, LEVEL_TOP }\n#{@tab}}" else ''
Simple node to throw an exception.
exports.Throw = class Throw extends Base
constructor: (@expression) ->
children: ['expression']
- isStatement: YES
makeReturn: THIS
+ isStatement: YES
+ jumps: NO
A Throw is already a return, of sorts...
makeReturn: THIS
compileNode: (o) ->
- @tab + "throw #{ @expression.compile o };"
Simple node to throw an exception.
A Throw is already a return, of sorts...
exports.Existence = class Existence extends Base
+ @tab + "throw #{ @expression.compile o };"
Checks a variable for existence -- not null and not undefined. This is
+similar to .nil?
in Ruby, and avoids having to consult a JavaScript truth
+table.
exports.Existence = class Existence extends Base
constructor: (@expression) ->
children: ['expression']
@@ -1029,9 +1055,11 @@ is optional, the catch is not.
Checks a variable for existence -- not null and not undefined. This is
-similar to .nil?
in Ruby, and avoids having to consult a JavaScript truth
-table.
exports.Parens = class Parens extends Base
+ if o.level <= LEVEL_COND then code else "(#{code})"
An extra set of parentheses, specified explicitly in the source. At one time +we tried to clean up the results by detecting and removing redundant +parentheses, but no longer -- you can put in as many as you please.
+ +Parentheses are a good way to force any statement to become an expression.
exports.Parens = class Parens extends Base
constructor: (@body) ->
children: ['body']
@@ -1045,15 +1073,18 @@ table.
An extra set of parentheses, specified explicitly in the source. At one time -we tried to clean up the results by detecting and removing redundant -parentheses, but no longer -- you can put in as many as you please.
+ bare = o.level < LEVEL_OP and (expr instanceof Op or expr instanceof Call or + (expr instanceof For and expr.returns)) + if bare then code else "(#{code})"CoffeeScript's replacement for the for loop is our array and object +comprehensions, that compile into for loops here. They also act as an +expression, able to return the result of each filtered iteration.
-Parentheses are a good way to force any statement to become an expression.
exports.For = class For extends Base
- constructor: (body, source, @name, @index) ->
- {@source, @guard, @step} = source
+Unlike Python array comprehensions, they can be multi-line, and you can pass
+the current index of the loop as a second parameter. Unlike Ruby blocks,
+you can map and filter in a single pass.
exports.For = class For extends Base
+ constructor: (body, source) ->
+ {@source, @guard, @step, @name, @index} = source
@body = Expressions.wrap [body]
@own = !!source.own
@object = !!source.object
@@ -1069,22 +1100,26 @@ parentheses, but no longer -- you can put in as many as you please.
isStatement: YES
+ jumps: While::jumps
+
makeReturn: ->
@returns = yes
- this
-
- containsPureStatement: While::containsPureStatement
compileNode: (o) ->
+ this
Welcome to the hairiest method in all of CoffeeScript. Handles the inner +loop, filtering, stepping, and result saving for array, object, and range +comprehensions. Some of the generated code can be shared in common, and +some cannot.
compileNode: (o) ->
body = Expressions.wrap [@body]
- hasCode = body.contains (node) -> node instanceof Code
- hasPure = last(body.expressions)?.containsPureStatement()
+ lastJumps = last(body.expressions)?.jumps()
+ @returns = no if lastJumps and lastJumps instanceof Return
source = if @range then @source.base else @source
scope = o.scope
name = @name and @name.compile o, LEVEL_LIST
index = @index and @index.compile o, LEVEL_LIST
scope.find(name, immediate: yes) if name and not @pattern
scope.find(index, immediate: yes) if index
- rvar = scope.freeVariable 'results' if @returns and not hasPure
+ rvar = scope.freeVariable 'results' if @returns
ivar = (if @range then name else index) or scope.freeVariable 'i'
+ name = ivar if @pattern
varPart = ''
guardPart = ''
defPart = ''
@@ -1096,35 +1131,32 @@ parentheses, but no longer -- you can put in as many as you please.
if (name or @own) and not IDENTIFIER.test svar
defPart = "#{@tab}#{ref = scope.freeVariable 'ref'} = #{svar};\n"
svar = ref
- namePart = if @pattern
- new Assign(@name, new Literal "#{svar}[#{ivar}]").compile o, LEVEL_TOP
- else if name
- "#{name} = #{svar}[#{ivar}]"
+ if name and not @pattern
+ namePart = "#{name} = #{svar}[#{ivar}]"
unless @object
lvar = scope.freeVariable 'len'
stepPart = if @step then "#{ivar} += #{ @step.compile(o, LEVEL_OP) }" else "#{ivar}++"
forPart = "#{ivar} = 0, #{lvar} = #{svar}.length; #{ivar} < #{lvar}; #{stepPart}"
- if @returns and not hasPure
+ if @returns
resultPart = "#{@tab}#{rvar} = [];\n"
- returnResult = '\n' + (new Return(new Literal(rvar)).compile o, LEVEL_PAREN)
+ returnResult = "\n#{@tab}return #{rvar};"
body = Push.wrap rvar, body
if @guard
body = Expressions.wrap [new If @guard, body]
- if hasCode
- body = Closure.wrap(body, yes, not @returns)
+ if @pattern
+ body.expressions.unshift new Assign @name, new Literal "#{svar}[#{ivar}]"
+ defPart += @pluckDirectCall o, body
varPart = "\n#{idt1}#{namePart};" if namePart
if @object
forPart = "#{ivar} in #{svar}"
guardPart = "\n#{idt1}if (!#{utility('hasProp')}.call(#{svar}, #{ivar})) continue;" if @own
- defPart += @pluckDirectCall o, body, name, index unless @pattern
body = body.compile merge(o, indent: idt1), LEVEL_TOP
body = '\n' + body + '\n' if body
"""
-
-#DIVIDER
+ #{defPart}#{resultPart or ''}#{@tab}for (#{forPart}) {#{guardPart}#{varPart}#{body}#{@tab}}#{returnResult or ''}
"""
- pluckDirectCall: (o, body, name, index) ->
+ pluckDirectCall: (o, body) ->
defs = ''
for expr, idx in body.expressions
expr = expr.unwrapAll()
@@ -1138,32 +1170,23 @@ parentheses, but no longer -- you can put in as many as you please.
fn = val.base?.unwrapAll() or val
ref = new Literal o.scope.freeVariable 'fn'
base = new Value ref
- args = compact [name, index]
- args.reverse() if @object
- for arg, i in args
- fn.params.push new Param args[i] = new Literal arg
if val.base
[val.base, base] = [base, val]
args.unshift new Literal 'this'
- body.expressions[idx] = new Call base, args
- o.sharedScope = o.scope
+ body.expressions[idx] = new Call base, expr.args
defs += @tab + new Assign(ref, fn).compile(o, LEVEL_TOP) + ';\n'
- defs
CoffeeScript's replacement for the for loop is our array and object -comprehensions, that compile into for loops here. They also act as an -expression, able to return the result of each filtered iteration.
- -Unlike Python array comprehensions, they can be multi-line, and you can pass -the current index of the loop as a second parameter. Unlike Ruby blocks, -you can map and filter in a single pass.
Welcome to the hairiest method in all of CoffeeScript. Handles the inner -loop, filtering, stepping, and result saving for array, object, and range -comprehensions. Some of the generated code can be shared in common, and -some cannot.
exports.Switch = class Switch extends Base
+ defs
A JavaScript switch statement. Converts into a returnable expression on-demand.
exports.Switch = class Switch extends Base
constructor: (@subject, @cases, @otherwise) ->
children: ['subject', 'cases', 'otherwise']
isStatement: YES
+ jumps: (o = {block: yes}) ->
+ for [conds, block] in @cases
+ return block if block.jumps o
+ @otherwise?.jumps o
+
makeReturn: ->
pair[1].makeReturn() for pair in @cases
@otherwise?.makeReturn()
@@ -1179,13 +1202,18 @@ some cannot.
{defPart}#{resultPart or ''}#{@tab}for (#{forPart}) {#{guardPart}#{varPart}#{body}#{@tab}}#{returnResult or ''}
exports.If = class If extends Base
+ expr = @lastNonComment block.expressions
+ jumper = expr.jumps()
+ if not expr or not jumper or (jumper instanceof Literal and jumper.value is 'debugger')
+ code += idt2 + 'break;\n'
+ code += idt1 + "default:\n#{ @otherwise.compile o, LEVEL_TOP }\n" if @otherwise and @otherwise.expressions.length
+ code + @tab + '}'
If/else statements. Acts as an expression by pushing down requested returns +to the last line of each clause.
+ +Single-expression Ifs are compiled into conditional operators if possible, +because ternaries are already proper expressions, and don't need conversion.
exports.If = class If extends Base
constructor: (condition, @body, options = {}) ->
- @condition = if options.invert then condition.invert() else condition
+ @condition = if options.type is 'unless' then condition.invert() else condition
@elseBody = null
@isChain = false
{@soak} = options
@@ -1193,16 +1221,19 @@ some cannot.
A JavaScript switch statement. Converts into a returnable expression on-demand.
addElse: (elseBody) ->
+ elseBodyNode: -> @elseBody?.unwrap()
Rewrite a chain of Ifs to add a default case as the final else.
addElse: (elseBody) ->
if @isChain
@elseBodyNode().addElse elseBody
else
@isChain = elseBody instanceof If
@elseBody = @ensureExpressions elseBody
- this
isStatement: (o) ->
+ this
The If only compiles into a statement if either of its bodies needs +to be a statement. Otherwise a conditional operator is safe.
isStatement: (o) ->
o?.level is LEVEL_TOP or
@bodyNode().isStatement(o) or @elseBodyNode()?.isStatement(o)
+ jumps: (o) -> @body.jumps(o) or @elseBody?.jumps(o)
+
compileNode: (o) ->
if @isStatement o then @compileStatement o else @compileExpression o
@@ -1212,11 +1243,8 @@ some cannot.
If/else statements. Acts as an expression by pushing down requested returns -to the last line of each clause.
- -Single-expression Ifs are compiled into conditional operators if possible, -because ternaries are already proper expressions, and don't need conversion.
compileStatement: (o) ->
+ if node instanceof Expressions then node else new Expressions [node]
Compile the If as a regular if-else statement. Flattened chains +force inner else bodies into statement form.
compileStatement: (o) ->
child = del o, 'chainChild'
cond = @condition.compile o, LEVEL_PAREN
o.indent += TAB
@@ -1230,7 +1258,7 @@ because ternaries are already proper expressions, and don't need conversion.
o.chainChild = yes
@elseBody.unwrap().compile o, LEVEL_TOP
else
- "{\n#{ @elseBody.compile o, LEVEL_TOP }\n#{@tab}}"
Rewrite a chain of Ifs to add a default case as the final else.
compileExpression: (o) ->
+ "{\n#{ @elseBody.compile o, LEVEL_TOP }\n#{@tab}}"
Compile the If as a conditional operator.
compileExpression: (o) ->
cond = @condition.compile o, LEVEL_COND
body = @bodyNode().compile o, LEVEL_LIST
alt = if @elseBodyNode() then @elseBodyNode().compile(o, LEVEL_LIST) else 'void 0'
@@ -1238,17 +1266,17 @@ because ternaries are already proper expressions, and don't need conversion.
if o.level >= LEVEL_COND then "(#{code})" else code
unfoldSoak: ->
- @soak and this
The If only compiles into a statement if either of its bodies needs -to be a statement. Otherwise a conditional operator is safe.
Compile the If as a regular if-else statement. Flattened chains -force inner else bodies into statement form.
Compile the If as a conditional operator.
Push =
- wrap: (name, exps) ->
- return exps if exps.isEmpty() or last(exps.expressions).containsPureStatement()
- exps.push new Call new Value(new Literal(name), [new Access new Literal 'push']), [exps.pop()]
Faux-nodes are never created by the grammar, but are used during code -generation to generate other combinations of nodes.
Closure =
The Push creates the tree for array.push(value)
,
-which is helpful for recording the result arrays from comprehensions.
wrap: (expressions, statement, noReturn) ->
- return expressions if expressions.containsPureStatement()
+generation to generate other combinations of nodes.
The Push creates the tree for array.push(value)
,
+which is helpful for recording the result arrays from comprehensions.
Push =
+ wrap: (name, exps) ->
+ return exps if exps.isEmpty() or last(exps.expressions).jumps()
+ exps.push new Call new Value(new Literal(name), [new Access new Literal 'push']), [exps.pop()]
A faux-node used to wrap an expressions body in a closure.
Closure =
Wrap the expressions body, unless it contains a pure statement,
+in which case, no dice. If the body mentions this
or arguments
,
+then make sure that the closure wrapper preserves the original values.
wrap: (expressions, statement, noReturn) ->
+ return expressions if expressions.jumps()
func = new Code [], Expressions.wrap [expressions]
args = []
if (mentionsArgs = expressions.contains @literalArgs) or
@@ -1261,15 +1289,16 @@ which is helpful for recording the result arrays from comprehensions.
call = new Call func, args
if statement then Expressions.wrap [call] else call
- literalArgs: (node) -> node instanceof Literal and node.value is 'arguments'
- literalThis: (node) -> node instanceof Literal and node.value is 'this' or
- node instanceof Code and node.bound
unfoldSoak = (o, parent, name) ->
+ literalArgs: (node) ->
+ node instanceof Literal and node.value is 'arguments' and not node.asKey
+ literalThis: (node) ->
+ (node instanceof Literal and node.value is 'this' and not node.asKey) or
+ (node instanceof Code and node.bound)
Unfold a node's child if soak, then tuck the node under created If
unfoldSoak = (o, parent, name) ->
return unless ifn = parent[name].unfoldSoak o
parent[name] = ifn.body
ifn.body = new Value parent
- ifn
A faux-node used to wrap an expressions body in a closure.
UTILITIES =
Wrap the expressions body, unless it contains a pure statement,
-in which case, no dice. If the body mentions this
or arguments
,
-then make sure that the closure wrapper preserves the original values.
extends: '''
+ ifn
UTILITIES =
Correctly set up a prototype chain for inheritance, including a reference
+to the superclass for super()
calls, and copies of any static properties.
extends: '''
function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
@@ -1278,28 +1307,27 @@ then make sure that the closure wrapper preserves the original values.
child.__super__ = parent.prototype;
return child;
}
- '''
Unfold a node's child if soak, then tuck the node under created If
bind: '''
+ '''
Create a function bound to the current value of "this".
bind: '''
function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
- '''
indexOf: '''
+ '''
Discover if an item is in an array.
indexOf: '''
Array.prototype.indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (this[i] === item) return i;
}
return -1;
}
- '''
Correctly set up a prototype chain for inheritance, including a reference
-to the superclass for super()
calls, and copies of any static properties.
hasProp: 'Object.prototype.hasOwnProperty'
- slice : 'Array.prototype.slice'
Create a function bound to the current value of "this".
LEVEL_TOP = 1 # ...;
+ '''
Shortcuts to speed up the lookup time for native functions.
hasProp: 'Object.prototype.hasOwnProperty'
+ slice : 'Array.prototype.slice'
Levels indicates a node's position in the AST. Useful for knowing if +parens are necessary or superfluous.
LEVEL_TOP = 1 # ...;
LEVEL_PAREN = 2 # (...)
LEVEL_LIST = 3 # [...]
LEVEL_COND = 4 # ... ? x : y
LEVEL_OP = 5 # !...
-LEVEL_ACCESS = 6 # ...[0]
Discover if an item is in an array.
TAB = ' '
Shortcuts to speed up the lookup time for native functions.
TRAILING_WHITESPACE = /[ \t]+$/gm
+LEVEL_ACCESS = 6 # ...[0]
Tabs are two spaces for pretty printing.
TAB = ' '
Trim out all trailing whitespace, so that the generated code plays nice +with Git.
TRAILING_WHITESPACE = /[ \t]+$/gm
IDENTIFIER = /^[$A-Za-z_][$\w]*$/
-SIMPLENUM = /^[+-]?\d+$/
Levels indicates a node's position in the AST. Useful for knowing if -parens are necessary or superfluous.
IS_STRING = /^['"]/
Tabs are two spaces for pretty printing.
Trim out all trailing whitespace, so that the generated code plays nice -with Git.
utility = (name) ->
+SIMPLENUM = /^[+-]?\d+$/
Is a literal value a string?
IS_STRING = /^['"]/
Helper for ensuring that utility functions are assigned at the top level.
utility = (name) ->
ref = "__#{name}"
Scope.root.assign ref, UTILITIES[name]
ref
@@ -1307,4 +1335,4 @@ with Git.
Is a literal value a string?
undefined
undefined
Helper for ensuring that utility functions are assigned at the top level.
undefined