diff --git a/documentation/coffee/block_comment.coffee b/documentation/coffee/block_comment.coffee index af5f0d5e57..dba86b6cd0 100644 --- a/documentation/coffee/block_comment.coffee +++ b/documentation/coffee/block_comment.coffee @@ -1,5 +1,5 @@ ### -CoffeeScript Compiler v1.1.1 +CoffeeScript Compiler v1.1.2 Released under the MIT License ### diff --git a/documentation/css/docs.css b/documentation/css/docs.css index 8c69a00047..e004cf95de 100644 --- a/documentation/css/docs.css +++ b/documentation/css/docs.css @@ -85,6 +85,10 @@ code, pre, tt, textarea { font-weight: normal; color: black; } + .timestamp small { + font-size: 11px; + text-transform: uppercase; + } div.code { position: relative; background: #fff; diff --git a/documentation/docs/browser.html b/documentation/docs/browser.html index f1f5a297c9..cf0d6caeec 100644 --- a/documentation/docs/browser.html +++ b/documentation/docs/browser.html @@ -9,10 +9,10 @@ xhr.onreadystatechange = -> if xhr.readyState is 4 if xhr.status in [0, 200] - CoffeeScript.run xhr.responseText + CoffeeScript.run xhr.responseText else throw new Error "Could not load #{url}" - callback() if callback + callback() if callback xhr.send null
Activate CoffeeScript in the browser by having it compile and evaluate
all script tags with a content-type of text/coffeescript
.
This happens on page load.
runScripts = ->
diff --git a/documentation/docs/cake.html b/documentation/docs/cake.html
index f9f835626c..3824c8d993 100644
--- a/documentation/docs/cake.html
+++ b/documentation/docs/cake.html
@@ -36,7 +36,7 @@
spaces = if spaces > 0 then Array(spaces + 1).join(' ') else ''
desc = if task.description then "# #{task.description}" else ''
console.log "cake #{name}#{spaces} #{desc}"
- console.log oparse.help() if switches.length
Print an error and exit when attempting to all an undefined task.
missingTask = (task) ->
+ console.log oparse.help() if switches.length
Print an error and exit when attempting to call an undefined task.
missingTask = (task) ->
console.log "No such task: \"#{task}\""
process.exit 1
diff --git a/documentation/docs/coffee-script.html b/documentation/docs/coffee-script.html
index 316bef6a10..6c09a2b7e0 100644
--- a/documentation/docs/coffee-script.html
+++ b/documentation/docs/coffee-script.html
@@ -6,14 +6,15 @@
If included on a webpage, it will automatically sniff out, compile, and
execute all scripts present in text/coffeescript
tags.
fs = require 'fs'
path = require 'path'
-vm = require 'vm'
+{Script} = require 'vm'
+Module = require 'module'
{Lexer,RESERVED} = require './lexer'
{parser} = require './parser'
TODO: Remove registerExtension when fully deprecated.
if require.extensions
require.extensions['.coffee'] = (module, filename) ->
content = compile fs.readFileSync(filename, 'utf8'), {filename}
module._compile content, filename
else if require.registerExtension
- require.registerExtension '.coffee', (content) -> compile content
The current CoffeeScript version number.
exports.VERSION = '1.1.1'
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.1.2'
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
@@ -27,28 +28,36 @@
parser.parse lexer.tokenize source, options
else
parser.parse source
Compile and execute a string of CoffeeScript (on the server), correctly
-setting __filename
, __dirname
, and relative require()
.
exports.run = (code, options) ->
We want the root module.
root = module
- while root.parent
- root = root.parent
Set the filename.
root.filename = process.argv[1] =
- if options.filename then fs.realpathSync(options.filename) else '.'
Clear the module cache.
root.moduleCache = {} if root.moduleCache
Assign paths for node_modules loading
if process.binding('natives').module
+setting __filename
, __dirname
, and relative require()
.
exports.run = (code, options) ->
+ mainModule = require.main
Set the filename.
mainModule.filename = process.argv[1] =
+ if options.filename then fs.realpathSync(options.filename) else '.'
Clear the module cache.
mainModule.moduleCache and= {}
Assign paths for node_modules loading
if process.binding('natives').module
{Module} = require 'module'
- root.paths = Module._nodeModulePaths path.dirname options.filename
Compile.
if path.extname(root.filename) isnt '.coffee' or require.extensions
- root._compile compile(code, options), root.filename
+ mainModule.paths = Module._nodeModulePaths path.dirname options.filename
Compile.
if path.extname(mainModule.filename) isnt '.coffee' or require.extensions
+ mainModule._compile compile(code, options), mainModule.filename
else
- root._compile code, root.filename
Compile and evaluate a string of CoffeeScript (in a Node.js-like environment). + mainModule._compile code, mainModule.filename
Compile and evaluate a string of CoffeeScript (in a Node.js-like environment). The CoffeeScript REPL uses this to run the input.
exports.eval = (code, options = {}) ->
- sandbox = options.sandbox
- unless sandbox
- sandbox =
- require: require
- module : { exports: {} }
- sandbox[g] = global[g] for g of global
- sandbox.global = sandbox
- sandbox.global.global = sandbox.global.root = sandbox.global.GLOBAL = sandbox
+ return unless code = code.trim()
+ sandbox = Script.createContext()
+ sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox
+ if options.sandbox?
+ if options.sandbox instanceof sandbox.constructor
+ sandbox = options.sandbox
+ else
+ sandbox[k] = v for own k, v of options.sandbox
sandbox.__filename = options.filename || 'eval'
- sandbox.__dirname = path.dirname sandbox.__filename
- js = compile "_=(#{code.trim()})", options
- vm.runInNewContext js, sandbox, sandbox.__filename
Instantiate a Lexer for our use here.
lexer = new Lexer
The real Lexer produces a generic stream of tokens. This object provides a + sandbox.__dirname = path.dirname sandbox.__filename
define module/require only if they chose not to specify their own
unless sandbox.module or sandbox.require
+ Module = require 'module'
+ sandbox.module = _module = new Module(options.modulename || 'eval')
+ sandbox.require = _require = (path) -> Module._load path, _module
+ _module.filename = sandbox.__filename
+ _require[r] = require[r] for r in Object.getOwnPropertyNames require
use the same hack node currently uses for their own REPL
_require.paths = _module.paths = Module._nodeModulePaths process.cwd()
+ _require.resolve = (request) -> Module._resolveFilename request, _module
+ o = {}
+ o[k] = v for own k, v of options
+ o.bare = on # ensure return value
+ js = compile code, o
+ Script.runInContext js, sandbox
Instantiate a Lexer for our use here.
lexer = new Lexer
The real Lexer produces a generic stream of tokens. This object provides a thin wrapper around it, compatible with the Jison API. We can then pass it directly as a "Jison lexer".
parser.lexer =
lex: ->
diff --git a/documentation/docs/command.html b/documentation/docs/command.html
index c5a78290e7..955bceb44e 100644
--- a/documentation/docs/command.html
+++ b/documentation/docs/command.html
@@ -53,26 +53,44 @@
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 = ->
+ unprocessed = []
+ for source in sources
+ unprocessed[sources.indexOf(source)]=1
for source in sources
base = path.join(source)
- compile = (source, topLevel) ->
+ compile = (source, sourceIndex, topLevel) ->
+ remaining_files = ->
+ total = 0
+ total += x for x in unprocessed
+ total
path.exists source, (exists) ->
+ if topLevel and not exists and source[-7..] isnt '.coffee'
+ return compile "#{source}.coffee", sourceIndex, topLevel
+
throw new Error "File not found: #{source}" if topLevel and not exists
fs.stat source, (err, stats) ->
throw err if err
if stats.isDirectory()
fs.readdir source, (err, files) ->
+ throw err if err
+ unprocessed[sourceIndex] += files.length
for file in files
- compile path.join(source, file)
+ compile path.join(source, file), sourceIndex
+ unprocessed[sourceIndex] -= 1
else if topLevel or path.extname(source) is '.coffee'
fs.readFile source, (err, code) ->
+ throw err if err
+ unprocessed[sourceIndex] -= 1
if opts.join
- contents[sources.indexOf source] = code.toString()
- compileJoin() if helpers.compact(contents).length > 0
+ contents[sourceIndex] = helpers.compact([contents[sourceIndex], code.toString()]).join('\n')
+ if helpers.compact(contents).length > 0 and remaining_files() == 0
+ compileJoin()
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 + else + unprocessed[sourceIndex] -= 1 + compile source, sources.indexOf(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
diff --git a/documentation/docs/grammar.html b/documentation/docs/grammar.html
index 57b7012474..c956c6d652 100644
--- a/documentation/docs/grammar.html
+++ b/documentation/docs/grammar.html
@@ -53,7 +53,7 @@
o 'STATEMENT', -> new Literal $1
]
All the different types of expressions in our language. The basic unit of CoffeeScript is the Expression -- everything that can be an expression -is one. Block serve as the building blocks of many other rules, making +is one. Blocks serve as the building blocks of many other rules, making them somewhat circular.
Expression: [
o 'Value'
o 'Invocation'
@@ -77,7 +77,7 @@
they can also serve as keys in object literals.
AlphaNumeric: [
o 'NUMBER', -> new Literal $1
o 'STRING', -> new Literal $1
- ]
All of our immediate values. These can (in general), be passed straight + ]
All of our immediate values. Generally these can be passed straight through and printed to JavaScript.
Literal: [
o 'AlphaNumeric'
o 'JS', -> new Literal $1
@@ -128,57 +128,55 @@
o 'ParamVar', -> new Param $1
o 'ParamVar ...', -> new Param $1, null, on
o 'ParamVar = Expression', -> new Param $1, $3
- ]
-
- ParamVar: [
+ ]
Function Parameters
ParamVar: [
o 'Identifier'
o 'ThisProperty'
o 'Array'
o 'Object'
- ]
A splat that occurs outside of a parameter list.
Splat: [
+ ]
A splat that occurs outside of a parameter list.
Splat: [
o 'Expression ...', -> new Splat $1
- ]
Variables and properties that can be assigned to.
SimpleAssignable: [
+ ]
Variables and properties that can be assigned to.
SimpleAssignable: [
o 'Identifier', -> new Value $1
o 'Value Accessor', -> $1.push $2
o 'Invocation Accessor', -> new Value $1, [$2]
o 'ThisProperty'
- ]
Everything that can be assigned to.
Assignable: [
+ ]
Everything that can be assigned to.
Assignable: [
o 'SimpleAssignable'
o 'Array', -> new Value $1
o 'Object', -> new Value $1
- ]
The types of things that can be treated as values -- assigned to, invoked + ]
The types of things that can be treated as values -- assigned to, invoked as functions, indexed into, named as a class, etc.
Value: [
o 'Assignable'
o 'Literal', -> new Value $1
o 'Parenthetical', -> new Value $1
o 'Range', -> new Value $1
o 'This'
- ]
The general group of accessors into an object, by property, by prototype + ]
The general group of accessors into an object, by property, by prototype or by array index or slice.
Accessor: [
o '. Identifier', -> new Access $2
o '?. Identifier', -> new Access $2, 'soak'
o ':: Identifier', -> new Access $2, 'proto'
o '::', -> new Access new Literal 'prototype'
o 'Index'
- ]
Indexing into an object or array using bracket notation.
Index: [
+ ]
Indexing into an object or array using bracket notation.
Index: [
o 'INDEX_START IndexValue INDEX_END', -> $2
o 'INDEX_SOAK Index', -> extend $2, soak : yes
o 'INDEX_PROTO Index', -> extend $2, proto: yes
]
-
+
IndexValue: [
o 'Expression', -> new Index $1
o 'Slice', -> new Slice $1
- ]
In CoffeeScript, an object literal is simply a list of assignments.
Object: [
+ ]
In CoffeeScript, an object literal is simply a list of assignments.
Object: [
o '{ AssignList OptComma }', -> new Obj $2, $1.generated
- ]
Assignment of properties within an object literal can be separated by + ]
Assignment of properties within an object literal can be separated by comma, as in JavaScript, or simply by newline.
AssignList: [
o '', -> []
o 'AssignObj', -> [$1]
o 'AssignList , AssignObj', -> $1.concat $3
o 'AssignList OptComma TERMINATOR AssignObj', -> $1.concat $4
o 'AssignList OptComma INDENT AssignList OptComma OUTDENT', -> $1.concat $4
- ]
Class definitions have optional bodies of prototype property assignments, + ]
Class definitions have optional bodies of prototype property assignments, and optional references to the superclass.
Class: [
o 'CLASS', -> new Class
o 'CLASS Block', -> new Class null, null, $2
@@ -188,35 +186,35 @@
o 'CLASS SimpleAssignable Block', -> new Class $2, null, $3
o 'CLASS SimpleAssignable EXTENDS Value', -> new Class $2, $4
o 'CLASS SimpleAssignable EXTENDS Value Block', -> new Class $2, $4, $5
- ]
Ordinary function invocation, or a chained series of calls.
Invocation: [
+ ]
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
- ]
An optional existence check on a function.
OptFuncExist: [
+ ]
An optional existence check on a function.
OptFuncExist: [
o '', -> no
o 'FUNC_EXIST', -> yes
- ]
The list of arguments to a function call.
Arguments: [
+ ]
The list of arguments to a function call.
Arguments: [
o 'CALL_START CALL_END', -> []
o 'CALL_START ArgList OptComma CALL_END', -> $2
- ]
A reference to the this current object.
This: [
+ ]
A reference to the this current object.
This: [
o 'THIS', -> new Value new Literal 'this'
o '@', -> new Value new Literal 'this'
- ]
A reference to a property on this.
ThisProperty: [
+ ]
A reference to a property on this.
ThisProperty: [
o '@ Identifier', -> new Value new Literal('this'), [new Access($2)], 'this'
- ]
The array literal.
Array: [
+ ]
The array literal.
Array: [
o '[ ]', -> new Arr []
o '[ ArgList OptComma ]', -> new Arr $2
- ]
Inclusive and exclusive range dots.
RangeDots: [
+ ]
Inclusive and exclusive range dots.
RangeDots: [
o '..', -> 'inclusive'
o '...', -> 'exclusive'
- ]
The CoffeeScript range literal.
Range: [
+ ]
The CoffeeScript range literal.
Range: [
o '[ Expression RangeDots Expression ]', -> new Range $2, $4, $3
- ]
Array slice literals.
Slice: [
+ ]
Array slice literals.
Slice: [
o 'Expression RangeDots Expression', -> new Range $1, $3, $2
o 'Expression RangeDots', -> new Range $1, null, $2
o 'RangeDots Expression', -> new Range null, $2, $1
- ]
The ArgList is both the list of objects passed into a function call, + ]
The ArgList is both the list of objects passed into a function call, as well as the contents of an array literal (i.e. comma-separated expressions). Newlines work as well.
ArgList: [
o 'Arg', -> [$1]
@@ -224,35 +222,35 @@
o 'ArgList OptComma TERMINATOR Arg', -> $1.concat $4
o 'INDENT ArgList OptComma OUTDENT', -> $2
o 'ArgList OptComma INDENT ArgList OptComma OUTDENT', -> $1.concat $4
- ]
Valid arguments are Block or Splats.
Arg: [
+ ]
Valid arguments are Blocks or Splats.
Arg: [
o 'Expression'
o 'Splat'
- ]
Just simple, comma-separated, required arguments (no fancy syntax). We need + ]
Just simple, comma-separated, required arguments (no fancy syntax). We need this to be separate from the ArgList for use in Switch blocks, where having the newlines wouldn't make sense.
SimpleArgs: [
o 'Expression'
o 'SimpleArgs , Expression', -> [].concat $1, $3
- ]
The variants of try/catch/finally exception handling blocks.
Try: [
+ ]
The variants of try/catch/finally exception handling blocks.
Try: [
o 'TRY Block', -> new Try $2
o 'TRY Block Catch', -> new Try $2, $3[0], $3[1]
o 'TRY Block FINALLY Block', -> new Try $2, null, null, $4
o 'TRY Block Catch FINALLY Block', -> new Try $2, $3[0], $3[1], $5
- ]
A catch clause names its error and runs a block of code.
Catch: [
+ ]
A catch clause names its error and runs a block of code.
Catch: [
o 'CATCH Identifier Block', -> [$2, $3]
- ]
Throw an exception object.
Throw: [
+ ]
Throw an exception object.
Throw: [
o 'THROW Expression', -> new Throw $2
- ]
Parenthetical expressions. Note that the Parenthetical is a Value, + ]
Parenthetical expressions. Note that the Parenthetical is a Value, not an Expression, so if you need to use an expression in a place where only values are accepted, wrapping it in parentheses will always do the trick.
Parenthetical: [
o '( Body )', -> new Parens $2
o '( INDENT Body OUTDENT )', -> new Parens $3
- ]
The condition portion of a while loop.
WhileSource: [
+ ]
The condition portion of a while loop.
WhileSource: [
o 'WHILE Expression', -> new While $2
o 'WHILE Expression WHEN Expression', -> new While $2, guard: $4
o 'UNTIL Expression', -> new While $2, invert: true
o 'UNTIL Expression WHEN Expression', -> new While $2, invert: true, guard: $4
- ]
The while loop can either be normal, with a block of expressions to execute, + ]
The while loop can either be normal, with a block of expressions to execute, or postfix, with a single expression. There is no do..while.
While: [
o 'WhileSource Block', -> $1.addBody $2
o 'Statement WhileSource', -> $2.addBody Block.wrap [$1]
@@ -263,7 +261,7 @@
Loop: [
o 'LOOP Block', -> new While(new Literal 'true').addBody $2
o 'LOOP Expression', -> new While(new Literal 'true').addBody Block.wrap [$2]
- ]
Array, object, and range comprehensions, at the most generic level. + ]
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
@@ -279,17 +277,17 @@
ForStart: [
o 'FOR ForVariables', -> $2
o 'FOR OWN ForVariables', -> $3.own = yes; $3
- ]
An array of all accepted values for a variable inside the loop. + ]
An array of all accepted values for a variable inside the loop. This enables support for pattern matching.
ForValue: [
o 'Identifier'
o 'Array', -> new Value $1
o 'Object', -> new Value $1
- ]
An array or range comprehension has variables for the current element + ]
An array or range comprehension has variables for the current element and (optional) reference to the current index. Or, key, value, in the case of object comprehensions.
ForVariables: [
o 'ForValue', -> [$1]
o 'ForValue , ForValue', -> [$1, $3]
- ]
The source of a comprehension is an array or object with an optional guard + ]
The source of a comprehension is an array or object with an optional guard clause. If it's an array comprehension, you can also choose to step through in fixed-size increments.
ForSource: [
o 'FORIN Expression', -> source: $2
@@ -311,21 +309,21 @@
Whens: [
o 'When'
o 'Whens When', -> $1.concat $2
- ]
An individual When clause, with action.
When: [
+ ]
An individual When clause, with action.
When: [
o 'LEADING_WHEN SimpleArgs Block', -> [[$2, $3]]
o 'LEADING_WHEN SimpleArgs Block TERMINATOR', -> [[$2, $3]]
- ]
The most basic form of if is a condition and an action. The following + ]
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, type: $1
o 'IfBlock ELSE IF Expression Block', -> $1.addElse new If $4, $5, type: $3
- ]
The full complement of if expressions, including postfix one-liner + ]
The full complement of if expressions, including postfix one-liner if and unless.
If: [
o 'IfBlock'
o 'IfBlock ELSE Block', -> $1.addElse $3
o 'Statement POST_IF Expression', -> new If $3, Block.wrap([$1]), type: $2, statement: true
o 'Expression POST_IF Expression', -> new If $3, Block.wrap([$1]), type: $2, statement: true
- ]
Arithmetic and logical operators, working on one or more operands. + ]
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 combine most of these rules into a single generic Operand OpSymbol Operand @@ -338,7 +336,7 @@ o '-- SimpleAssignable', -> new Op '--', $2 o '++ SimpleAssignable', -> new Op '++', $2 o 'SimpleAssignable --', -> new Op '--', $1, null, true - o 'SimpleAssignable ++', -> new Op '++', $1, null, true
o 'Expression ?', -> new Existence $1
+ o 'SimpleAssignable ++', -> new Op '++', $1, null, true
o 'Expression ?', -> new Existence $1
o 'Expression + Expression', -> new Op '+' , $1, $3
o 'Expression - Expression', -> new Op '-' , $1, $3
@@ -358,7 +356,7 @@
o 'SimpleAssignable COMPOUND_ASSIGN
INDENT Expression OUTDENT', -> new Assign $1, $4, $2
o 'SimpleAssignable EXTENDS Expression', -> new Extends $1, $3
- ]
Operators at the top of this list have higher precedence than the ones lower + ]
Operators at the top of this list have higher precedence than the ones lower
down. Following these rules is what makes 2 + 3 * 4
parse as:
2 + (3 * 4)
@@ -384,7 +382,7 @@
['right', 'FORIN', 'FOROF', 'BY', 'WHEN']
['right', 'IF', 'ELSE', 'FOR', 'DO', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
['right', 'POST_IF']
-]
Finally, now what we have our grammar and our operators, we can create +]
Finally, now that we have our grammar and our operators, we can create our Jison.Parser. We do this by processing all of our rules, recording all terminals (every symbol which does not appear as the name of a rule above) as "tokens".
tokens = []
@@ -393,7 +391,7 @@
for token in alt[0].split ' '
tokens.push token unless grammar[token]
alt[1] = "return #{alt[1]}" if name is 'Root'
- alt
Initialize the Parser with our list of terminal tokens, our grammar + alt
Initialize the Parser with our list of terminal tokens, our grammar rules, and the name of the root. Reverse the operators because Jison orders precedence from low to high, and we have it high to low (as in Yacc).
exports.parser = new Parser
diff --git a/documentation/docs/lexer.html b/documentation/docs/lexer.html
index 4bb17c1589..7819070168 100644
--- a/documentation/docs/lexer.html
+++ b/documentation/docs/lexer.html
@@ -62,8 +62,7 @@
not prev.spaced and prev[0] is '@')
tag = 'IDENTIFIER'
- if id in JS_KEYWORDS or
- not forcedIdentifier and id in COFFEE_KEYWORDS
+ if not forcedIdentifier and (id in JS_KEYWORDS or id in COFFEE_KEYWORDS)
tag = id.toUpperCase()
if tag is 'WHEN' and @tag() in LINE_BREAK
tag = 'LEADING_WHEN'
@@ -149,7 +148,11 @@
to distinguish from division, so we borrow some basic heuristics from
JavaScript and Ruby.
regexToken: ->
return 0 if @chunk.charAt(0) isnt '/'
- return @heregexToken match if match = HEREGEX.exec @chunk
+ if match = HEREGEX.exec @chunk
+ length = @heregexToken match
+ @line += count match[0], '\n'
+ return length
+
prev = last @tokens
return 0 if prev and (prev[0] in (if prev.spaced then NOT_REGEX else NOT_SPACED_REGEX))
return 0 unless match = REGEX.exec @chunk
@@ -305,6 +308,7 @@
else if tok[0] is '('
tok[0] = 'PARAM_START'
return this
+ else return this
this
Close up all remaining open blocks at the end of the file.
closeIndentation: ->
@outdentToken @indent
The error for when you try to use a forbidden word in JavaScript as an identifier.
identifierError: (word) ->
@@ -328,6 +332,8 @@
continue
if end is '}' and letter in ['"', "'"]
stack.push end = letter
+ else if end is '}' and letter is '/' and match = (HEREGEX.exec(str.slice i) or REGEX.exec(str.slice i))
+ i += match[0].length - 1
else if end is '}' and letter is '{'
stack.push end = '}'
else if end is '"' and prev is '#' and letter is '{'
@@ -409,7 +415,7 @@
no : 'false'
on : 'true'
off : 'false'
-
+
COFFEE_ALIASES = (key for key of COFFEE_ALIAS_MAP)
COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat COFFEE_ALIASES
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, @@ -427,7 +433,7 @@ NUMBER = /// ^ 0x[\da-f]+ | # hex - ^ (?: \d+(\.\d+)? | \.\d+ ) (?:e[+-]?\d+)? # decimal + ^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal ///i HEREDOC = /// ^ ("""|''') ([\s\S]*?) (?:\n[^\n\S]*)? \1 /// @@ -492,7 +498,7 @@
See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
Our list is shorter, due to sans-parentheses method calls.
NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', '++', '--', ']']
If the previous token is not spaced, there are more preceding tokens that -force a division parse:
NOT_SPACED_REGEX = NOT_REGEX.concat ')', '}', 'THIS', 'IDENTIFIER', 'STRING'
Tokens which could legitimately be invoked or indexed. A opening +force a division parse:
NOT_SPACED_REGEX = NOT_REGEX.concat ')', '}', 'THIS', 'IDENTIFIER', 'STRING'
Tokens which could legitimately be invoked or indexed. An opening parentheses or bracket following these tokens will be recorded as the start of a function invocation or indexing operation.
CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER']
INDEXABLE = CALLABLE.concat 'NUMBER', 'BOOL'
Tokens that, when immediately preceding a WHEN
, indicate that the WHEN
diff --git a/documentation/docs/nodes.html b/documentation/docs/nodes.html
index d17a3c09f6..42916c811a 100644
--- a/documentation/docs/nodes.html
+++ b/documentation/docs/nodes.html
@@ -117,7 +117,7 @@
jumps: (o) ->
for exp in @expressions
- return exp if exp.jumps o
An Block node does not return its entire body, rather it + return exp if exp.jumps o
A Block node does not return its entire body, rather it ensures that the final expression is returned.
makeReturn: ->
len = @expressions.length
while len--
@@ -126,7 +126,7 @@
@expressions[len] = expr.makeReturn()
@expressions.splice(len, 1) if expr instanceof Return and not expr.expression
break
- this
An Block is the only node that can serve as the root.
compile: (o = {}, level) ->
+ this
A Block is the only node that can serve as the root.
compile: (o = {}, level) ->
if o.scope then super o, level else @compileRoot o
Compile all expressions within the Block body. If we need to return the result, and it's an expression, simply return it. If it's a statement, ask the statement to do so.
compileNode: (o) ->
@@ -136,7 +136,10 @@
for node in @expressions
node = node.unwrapAll()
node = (node.unfoldSoak(o) or node)
- if top
+ if node instanceof Block
This is a nested block. We don't do anything special here like enclose +it in a new scope; we just compile the statements in this block along with +our own
codes.push node.compileNode o
+ else if top
node.front = true
code = node.compile o
codes.push if node.isStatement o then code else @tab + code + ';'
@@ -144,7 +147,7 @@
codes.push node.compile o, LEVEL_LIST
return codes.join '\n' if top
code = codes.join(', ') or 'void 0'
- if codes.length > 1 and o.level >= LEVEL_LIST then "(#{code})" else code
If we happen to be the top-level Block, wrap everything in + if codes.length > 1 and o.level >= LEVEL_LIST then "(#{code})" else code
If we happen to be the top-level Block, wrap everything in a safety closure, unless requested not to. It would be better not to generate them in the first place, but for now, clean up obvious double-parentheses.
compileRoot: (o) ->
@@ -152,7 +155,7 @@
o.scope = new Scope null, this, null
o.level = LEVEL_TOP
code = @compileWithDeclarations o
- if o.bare then code else "(function() {\n#{code}\n}).call(this);\n"
Compile the expressions body for the contents of a function, with + if o.bare then code else "(function() {\n#{code}\n}).call(this);\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 = post = ''
for exp, i in @expressions
@@ -161,19 +164,23 @@
o = merge(o, level: LEVEL_TOP)
if i
rest = @expressions.splice i, @expressions.length
- code = @compileNode o
+ code = @compileNode(o)
@expressions = rest
post = @compileNode o
{scope} = o
if scope.expressions is this
- if o.scope.hasDeclarations()
+ declars = o.scope.hasDeclarations()
+ assigns = scope.hasAssignments
+ if (declars or assigns) and i
+ code += '\n'
+ if declars
code += "#{@tab}var #{ scope.declaredVariables().join(', ') };\n"
- if scope.hasAssignments
+ if assigns
code += "#{@tab}var #{ multident scope.assignedVariables().join(', '), @tab };\n"
- code + post
Wrap up the given nodes as an Block, unless it already happens + code + post
Wrap up the given nodes as a Block, unless it already happens to be one.
@wrap: (nodes) ->
return nodes[0] if nodes.length is 1 and nodes[0] instanceof Block
- new Block nodes
Literals are static values that can be passed through directly into + new Block nodes
Literals are static values that can be passed through directly into
JavaScript without translation, such as: strings, numbers,
true
, false
, null
...
exports.Literal = class Literal extends Base
constructor: (@value) ->
@@ -206,7 +213,7 @@
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: (expr) ->
@expression = expr if expr and not expr.unwrap().isUndefined
@@ -222,7 +229,7 @@
if expr and expr not instanceof Return then expr.compile o, level else super o, level
compileNode: (o) ->
- @tab + "return#{ if @expression then ' ' + @expression.compile(o, LEVEL_PAREN) 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
@@ -231,12 +238,12 @@
@[tag] = true if tag
return this
- children: ['base', 'properties']
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
@@ -257,15 +264,15 @@
last(@properties) instanceof Slice
makeReturn: ->
- if @properties.length then super() else @base.makeReturn()
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
if @properties.length < 2 and not @base.isComplex() and not name?.isComplex()
return [this, this] # `a` `a.b`
- base = new Value @base, @properties.slice 0, -1
+ base = new Value @base, @properties[...-1]
if base.isComplex() # `a().b`
bref = new Literal o.scope.freeVariable 'base'
base = new Value new Parens new Assign bref, base
@@ -274,16 +281,16 @@
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) ->
@base.front = @front
props = @properties
code = @base.compile o, if props.length then LEVEL_ACCESS else null
- code = "(#{code})" if props[0] instanceof Access and @isSimpleNumber()
+ code = "#{code}." if (@base instanceof Parens or props.length) and SIMPLENUM.test code
code += prop.compile o for prop in props
- code
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) ->
return @unfoldedSoak if @unfoldedSoak?
result = do =>
if ifn = @base.unfoldSoak o
@@ -291,15 +298,15 @@
return ifn
for prop, i in @properties when prop.soak
prop.soak = off
- fst = new Value @base, @properties.slice 0, i
- snd = new Value @base, @properties.slice i
+ fst = new Value @base, @properties[...i]
+ snd = new Value @base, @properties[i..]
if fst.isComplex()
ref = new Literal o.scope.freeVariable 'ref'
fst = new Parens new Assign ref, fst
snd.base = ref
return new If new Existence(fst), snd, soak: on
null
- @unfoldedSoak = result or no
CoffeeScript passes through block comments as JavaScript block comments + @unfoldedSoak = result or no
CoffeeScript passes through block comments as JavaScript block comments at the same position.
exports.Comment = class Comment extends Base
constructor: (@comment) ->
@@ -309,29 +316,29 @@
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
+ if base instanceof Call and not base.isNew
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
{name} = method
- throw SyntaxError 'cannot call super on an anonymous function.' unless name
+ throw SyntaxError 'cannot call super on an anonymous function.' unless name?
if method.klass
- "#{method.klass}.__super__.#{name}"
+ (new Value (new Literal method.klass), [new Access(new Literal "__super__"), new Access new Literal name]).compile o
else
- "#{name}.__super__.constructor"
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'
@@ -360,7 +367,7 @@
else
call.variable.base = ifn
ifn = unfoldSoak o, call, 'variable'
- ifn
Walk through the objects in the arguments, moving over simple values. + ifn
Walk through the objects in the arguments, moving over simple values.
This allows syntax like call a: b, c
into call({a: b}, c);
filterImplicitObjects: (list) ->
nodes = []
for node in list
@@ -369,13 +376,13 @@
continue
obj = null
for prop in node.base.properties
- if prop instanceof Assign
+ if prop instanceof Assign or prop instanceof Comment
nodes.push obj = new Obj properties = [], true if not obj
properties.push prop
else
nodes.push prop
obj = null
- nodes
Compile a vanilla function call.
compileNode: (o) ->
+ nodes
Compile a vanilla function call.
compileNode: (o) ->
@variable?.front = @front
if code = Splat.compileSplattedArray o, @args, true
return @compileSplat o, code
@@ -384,9 +391,9 @@
if @isSuper
@superReference(o) + ".call(this#{ args and ', ' + args })"
else
- (if @isNew then 'new ' else '') + @variable.compile(o, LEVEL_ACCESS) + "(#{args})"
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) ->
@@ -412,14 +419,14 @@
fun += name.compile o
else
ref = 'null'
- "#{fun}.apply(#{ref}, #{splatArgs})"
Node to extend an object's prototype with an ancestor object. + "#{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']
Hooks one constructor into another's prototype chain.
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
A .
access into a property of a value, or the ::
shorthand for
+ 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
@@ -430,9 +437,9 @@
compile: (o) ->
name = @name.compile o
- @proto + if IS_STRING.test name then "[#{name}]" else ".#{name}"
+ @proto + if IDENTIFIER.test name then ".#{name}" else "[#{name}]"
- isComplex: NO
A [ ... ]
indexed access into an array or object.
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']
@@ -441,7 +448,7 @@
(if @proto then '.prototype' else '') + "[#{ @index.compile o, LEVEL_PAREN }]"
isComplex: ->
- @index.isComplex()
A range literal. Ranges can be used to extract portions (slices) of arrays, + @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
@@ -449,37 +456,34 @@
constructor: (@from, @to, tag) ->
@exclusive = tag is 'exclusive'
- @equals = if @exclusive then '' else '='
Compiles the range's source variables -- where it starts and where it ends. + @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
When compiled normally, the range returns the contents of the for loop + o = merge o, top: true + [@fromC, @fromVar] = @from.cache o, LEVEL_LIST + [@toC, @toVar] = @to.cache o, LEVEL_LIST + [@step, @stepVar] = step.cache o, LEVEL_LIST if step = del o, 'step' + [@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)] + @stepNum = @stepVar.match(SIMPLENUM) if @stepVar
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
+ @compileVariables o unless @fromVar
+ return @compileArray(o) unless o.index
Set up endpoints.
known = @fromNum and @toNum
idx = del o, 'index'
- step = del o, 'step'
- stepvar = o.scope.freeVariable "step" if step
- varPart = "#{idx} = #{@from}" + ( if @to isnt @toVar then ", #{@to}" else '' ) + if step then ", #{stepvar} = #{step.compile(o)}" else ''
- cond = "#{@fromVar} <= #{@toVar}"
- condPart = "#{cond} ? #{idx} <#{@equals} #{@toVar} : #{idx} >#{@equals} #{@toVar}"
- stepPart = if step then "#{idx} += #{stepvar}" else "#{cond} ? #{idx}++ : #{idx}--"
- "#{varPart}; #{condPart}; #{stepPart}"
Compile a simple range comprehension, with integers.
compileSimple: (o) ->
- [from, to] = [+@fromNum, +@toNum]
- idx = del o, 'index'
- step = del o, 'step'
- stepvar = o.scope.freeVariable "step" if step
- varPart = "#{idx} = #{from}"
- varPart += ", #{stepvar} = #{step.compile(o)}" if step
- condPart = if from <= to then "#{idx} <#{@equals} #{to}" else "#{idx} >#{@equals} #{to}"
- stepPart = "#{idx} += #{stepvar}" if step
- stepPart = ( if from <= to then "#{idx}++" else "#{idx}--" ) if not step
- "#{varPart}; #{condPart}; #{stepPart}"
When used as a value, expand the range into the equivalent array.
compileArray: (o) ->
+ varPart = "#{idx} = #{@fromC}"
+ varPart += ", #{@toC}" if @toC isnt @toVar
+ varPart += ", #{@step}" if @step isnt @stepVar
+ [lt, gt] = ["#{idx} <#{@equals}", "#{idx} >#{@equals}"]
Generate the condition.
condPart = if @stepNum
+ condPart = if +@stepNum > 0 then "#{lt} #{@toVar}" else "#{gt} #{@toVar}"
+ else if known
+ [from, to] = [+@fromNum, +@toNum]
+ condPart = if from <= to then "#{lt} #{to}" else "#{gt} #{to}"
+ else
+ cond = "#{@fromVar} <= #{@toVar}"
+ condPart = "#{cond} ? #{lt} #{@toVar} : #{gt} #{@toVar}"
Generate the step.
stepPart = if @stepVar
+ "#{idx} += #{@stepVar}"
+ else if known
+ if from <= to then "#{idx}++" else "#{idx}--"
+ else
+ "#{cond} ? #{idx}++ : #{idx}--"
The final loop body.
"#{varPart}; #{condPart}; #{stepPart}"
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
@@ -490,20 +494,22 @@
pre = "\n#{idt}#{result} = [];"
if @fromNum and @toNum
o.index = i
- body = @compileSimple o
+ body = @compileNode o
else
- vars = "#{i} = #{@from}" + if @to isnt @toVar then ", #{@to}" else ''
+ vars = "#{i} = #{@fromC}" + if @toC isnt @toVar then ", #{@toC}" else ''
cond = "#{@fromVar} <= #{@toVar}"
body = "var #{vars}; #{cond} ? #{i} <#{@equals} #{@toVar} : #{i} >#{@equals} #{@toVar}; #{cond} ? #{i}++ : #{i}--"
post = "{ #{result}.push(#{i}); }\n#{idt}return #{result};\n#{o.indent}"
- "(function() {#{pre}\n#{idt}for (#{body})#{post}}).apply(this, arguments)"
An array slice literal. Unlike JavaScript's Array#slice
, the second parameter
+ hasArgs = (node) -> node?.contains (n) -> n instanceof Literal and n.value is 'arguments' and not n.asKey
+ args = ', arguments' if hasArgs(@from) or hasArgs(@to)
+ "(function() {#{pre}\n#{idt}for (#{body})#{post}}).apply(this#{args ? ''})"
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()
We have to be careful when trying to slice through the end of the array, + 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
@@ -516,7 +522,7 @@
(+compiled + 1).toString()
else
"(#{compiled} + 1) || 9e9"
- ".slice(#{ fromStr }#{ toStr or '' })"
An object literal, nothing fancy.
exports.Obj = class Obj extends Base
+ ".slice(#{ fromStr }#{ toStr or '' })"
An object literal, nothing fancy.
exports.Obj = class Obj extends Base
constructor: (props, @generated = false) ->
@objects = @properties = props or []
@@ -551,7 +557,7 @@
assigns: (name) ->
for prop in @properties when prop.assigns name then return yes
- no
An array literal.
exports.Arr = class Arr extends Base
+ no
An array literal.
exports.Arr = class Arr extends Base
constructor: (objs) ->
@objects = objs or []
@@ -572,20 +578,20 @@
assigns: (name) ->
for obj in @objects when obj.assigns name then return yes
- no
The CoffeeScript class definition. + 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 Block) ->
@boundFuncs = []
@body.classBody = yes
- children: ['variable', 'parent', 'body']
Figure out the appropriate name for the constructor function of this class.
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
For all this
-references and bound functions in the class definition,
+ 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
@@ -593,14 +599,14 @@
node.value = name
else if node instanceof Code
node.klass = name
- node.context = name if node.bound
Ensure that all functions bound to the instance are proxied in the + 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)"
Merge the properties from a top-level object as prototypal properties + lhs = (new Value (new Literal "this"), [new Access bvar]).compile o + @ctor.body.unshift new Literal "#{lhs} = #{utility 'bind'}(#{lhs}, this)"
Merge the properties from a top-level object as prototypal properties on the class.
addProperties: (node, name, o) ->
- props = node.base.properties.slice 0
+ props = node.base.properties[0..]
exprs = while assign = props.shift()
if assign instanceof Assign
base = assign.variable.base
@@ -623,14 +629,14 @@
@boundFuncs.push base
func.bound = no
assign
- compact exprs
Walk the body of the class, looking for prototype properties to be converted.
walkBody: (name, o) ->
+ compact exprs
Walk the body of the class, looking for prototype properties to be converted.
walkBody: (name, o) ->
@traverseChildren false, (child) =>
return false if child instanceof Class
if child instanceof Block
for node, i in exps = child.expressions
if node instanceof Value and node.isObject(true)
exps[i] = @addProperties node, name, o
- child.expressions = exps = flatten exps
Make sure that a constructor is defined for the class, and properly + 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
@@ -639,7 +645,7 @@
@body.expressions.unshift @ctor
@ctor.ctor = @ctor.name = name
@ctor.klass = null
- @ctor.noReturn = yes
Instead of generating the JavaScript string directly, we build up the + @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()
@@ -656,18 +662,21 @@
klass = new Parens Closure.wrap(@body), true
klass = new Assign @variable, klass if @variable
- klass.compile o
The Assign is used to assign a local variable to value, or to set the + 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
Matchers for detecting class/method names
METHOD_DEF: /^(?:(\S+)\.prototype\.|\S+?)?\b([$A-Za-z_][$\w\x7f-\uffff]*)$/
+ @param = options and options.param
children: ['variable', 'value']
+ isStatement: (o) ->
+ o?.level is LEVEL_TOP and @context? and "?" in @context
+
assigns: (name) ->
@[if @context is 'object' then 'value' else 'variable'].assigns name
unfoldSoak: (o) ->
- unfoldSoak o, this, 'variable'
Compile an assignment, delegating to compilePatternMatch
or
+ 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) ->
@@ -683,13 +692,13 @@
o.scope.add name, 'var'
else
o.scope.find name
- if @value instanceof Code and match = @METHOD_DEF.exec name
- @value.name = match[2]
+ if @value instanceof Code and match = METHOD_DEF.exec name
@value.klass = match[1] if match[1]
+ @value.name = match[2] ? match[3] ? match[4] ? match[5]
val = @value.compile o, LEVEL_LIST
return "#{name}: #{val}" if @context is 'object'
val = name + " #{ @context or '=' } " + val
- if o.level <= LEVEL_LIST then val else "(#{val})"
Brief implementation of recursive pattern matching, when assigning array or + 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) ->
@@ -700,7 +709,7 @@
code = value.compile o
return if o.level >= LEVEL_OP then "(#{code})" else code
isObject = @variable.isObject()
- 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
+ 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
@@ -713,17 +722,17 @@
acc = IDENTIFIER.test idx.unwrap().value or 0
value = new Value value
value.properties.push new (if acc then Access else Index) idx
- return new Assign(obj, value).compile o
+ return new Assign(obj, value, null, param: @param).compile o, LEVEL_TOP
vvar = value.compile o, LEVEL_LIST
assigns = []
splat = false
if not IDENTIFIER.test(vvar) or @variable.assigns(vvar)
assigns.push "#{ ref = o.scope.freeVariable 'ref' } = #{vvar}"
vvar = ref
- for obj, i in objects
A regular array pattern-match.
idx = i
+ for obj, i in objects
A regular array pattern-match.
idx = i
if isObject
- 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
+ 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
@@ -750,11 +759,12 @@
assigns.push new Assign(obj, val, null, param: @param).compile o, LEVEL_TOP
assigns.push vvar unless top
code = assigns.join ', '
- if o.level < LEVEL_LIST then code else "(#{code})"
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.
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 + if "?" in @context then o.isExistentialEquals = true + new Op(@context[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
@@ -770,7 +780,7 @@
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. + 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) ->
@@ -783,9 +793,9 @@
isStatement: -> !!@ctor
- jumps: NO
Compilation creates a new scope unless explicitly asked to share with the + 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 =>
+the JavaScript arguments
object. 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
@@ -795,7 +805,7 @@
vars = []
exprs = []
for param in @params when param.splat
- o.scope.add param.name.value, 'var' if param.name.value
+ o.scope.add p.name.value, 'var', yes for p in @params when p.name.value
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
new Value new Literal 'arguments'
break
@@ -824,9 +834,9 @@
code += '}'
return @tab + code if @ctor
return utility('bind') + "(#{code}, #{@context})" if @bound
- if @front or (o.level >= LEVEL_ACCESS) then "(#{code})" else code
Short-circuit traverseChildren
method to prevent it from crossing scope boundaries
+ 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, + 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) ->
@@ -849,7 +859,7 @@
@reference = node
isComplex: ->
- @name.isComplex()
A splat, either as a parameter to a function, an argument to a call, + @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']
@@ -863,7 +873,7 @@
@name.assigns name
compile: (o) ->
- if @index? then @compileParam o else @name.compile o
Utility function that converts arbitrary number of elements, mixed with + if @index? then @compileParam o else @name.compile o
Utility function that converts an 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
@@ -872,15 +882,15 @@
code = list[0].compile o, LEVEL_LIST
return code if apply
return "#{ utility 'slice' }.call(#{code})"
- args = list.slice index
+ args = list[index..]
for node, i in args
code = node.compile o, LEVEL_LIST
args[i] = if node instanceof Splat
then "#{ utility 'slice' }.call(#{code})"
else "[#{code}]"
- return args[0] + ".concat(#{ args.slice(1).join ', ' })" if index is 0
- base = (node.compile o, LEVEL_LIST for node in list.slice 0, index)
- "[#{ base.join ', ' }].concat(#{ args.join ', ' })"
A while loop, the only sort of low-level loop exposed by CoffeeScript. From + return args[0] + ".concat(#{ args[1..].join ', ' })" if index is 0 + base = (node.compile o, LEVEL_LIST for node in list[0...index]) + "[#{ 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) ->
@@ -903,7 +913,7 @@
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 + 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
@@ -921,25 +931,25 @@
code = set + @tab + "while (#{ @condition.compile o, LEVEL_PAREN }) {#{body}}"
if @returns
code += "\n#{@tab}return #{rvar};"
- code
Simple Arithmetic and logical operations. Performs some conversion from + 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) ->
+ constructor: (op, first, second, flip ) ->
return new In first, second if op is 'in'
if op is 'do'
call = new Call first, first.params or []
call.do = yes
return call
if op is 'new'
- return first.newInstance() if first instanceof Call and not first.do
+ return first.newInstance() if first instanceof Call and not first.do and not first.isNew
first = new Parens first if first instanceof Code and first.bound or first.do
@operator = CONVERSIONS[op] or op
@first = first
@second = second
@flip = !!flip
- return this
The map of conversions from CoffeeScript to JavaScript symbols.
CONVERSIONS =
+ return this
The map of conversions from CoffeeScript to JavaScript symbols.
CONVERSIONS =
'==': '==='
'!=': '!=='
- 'of': 'in'
The map of invertible operators.
INVERSIONS =
+ 'of': 'in'
The map of invertible operators.
INVERSIONS =
'!==': '==='
'===': '!=='
@@ -948,7 +958,10 @@
isSimpleNumber: NO
isUnary: ->
- not @second
Am I capable of + not @second + + isComplex: -> + not (@isUnary() and (@operator in ['+', '-'])) or @first.isComplex()
Am I capable of Python-style comparison chaining?
isChainable: ->
@operator in ['<', '>', '>=', '<=', '===', '!==']
@@ -989,7 +1002,7 @@
@first.front = @front
code = @first.compile(o, LEVEL_OP) + ' ' + @operator + ' ' +
@second.compile(o, LEVEL_OP)
- if o.level <= LEVEL_OP then code else "(#{code})"
Mimic Python's chained comparisons when multiple comparison operators are + 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'
@@ -1007,7 +1020,7 @@
else
fst = @first
ref = fst
- new If(new Existence(fst), ref, type: 'if').addElse(@second).compile o
Compile a unary Op.
compileUnary: (o) ->
+ new If(new Existence(fst), ref, type: 'if').addElse(@second).compile o
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
@@ -1017,7 +1030,7 @@
parts.join ''
toString: (idt) ->
- super idt, @constructor.name + ' ' + @operator
exports.In = class In extends Base
+ super idt, @constructor.name + ' ' + @operator
exports.In = class In extends Base
constructor: (@object, @array) ->
children: ['object', 'array']
@@ -1026,9 +1039,10 @@
compileNode: (o) ->
if @array instanceof Value and @array.isArray()
- @compileOrTest o
- else
- @compileLoopTest o
+ for obj in @array.base.objects when obj instanceof Splat
+ hasSplat = yes
+ break
compileOrTest
only if we have an array literal with no splats
return @compileOrTest o unless hasSplat
+ @compileLoopTest o
compileOrTest: (o) ->
[sub, ref] = @object.cache o, LEVEL_OP
@@ -1048,7 +1062,7 @@
if o.level < LEVEL_LIST then code else "(#{code})"
toString: (idt) ->
- super idt, @constructor.name + if @negated then '!' else ''
A classic try/catch/finally block.
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']
@@ -1060,11 +1074,12 @@
makeReturn: ->
@attempt = @attempt .makeReturn() if @attempt
@recovery = @recovery.makeReturn() if @recovery
- this
Compilation is more or less as you would expect -- the finally clause + 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
+ o.scope.add @error.value, 'param'
" catch#{errorPart}{\n#{ @recovery.compile o, LEVEL_TOP }\n#{@tab}}"
else unless @ensure or @recovery
' catch (_e) {}'
@@ -1072,16 +1087,16 @@
#{@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
+ """ + 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
- jumps: NO
A Throw is already a return, of sorts...
makeReturn: THIS
+ jumps: NO
A Throw is already a return, of sorts...
makeReturn: THIS
compileNode: (o) ->
- @tab + "throw #{ @expression.compile o };"
Checks a variable for existence -- not null and not undefined. This is + @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) ->
@@ -1093,14 +1108,10 @@
compileNode: (o) ->
code = @expression.compile o, LEVEL_OP
code = if IDENTIFIER.test(code) and not o.scope.check code
- if @negated
- "typeof #{code} === \"undefined\" || #{code} === null"
- else
- "typeof #{code} !== \"undefined\" && #{code} !== null"
- else
- sym = if @negated then '==' else '!='
- "#{code} #{sym} null"
- if o.level <= LEVEL_COND then code else "(#{code})"
An extra set of parentheses, specified explicitly in the source. At one time + [cmp, cnj] = if @negated then ['===', '||'] else ['!==', '&&'] + "typeof #{code} #{cmp} \"undefined\" #{cnj} #{code} #{cmp} null" + else
do not use strict equality here; it will break existing code
"#{code} #{if @negated then '==' else '!='} null"
+ 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.
@@ -1121,7 +1132,7 @@ code = expr.compile o, LEVEL_PAREN 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 + 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.
@@ -1149,7 +1160,7 @@ makeReturn: -> @returns = yes - thisWelcome to the hairiest method in all of CoffeeScript. Handles the inner + 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) ->
@@ -1163,7 +1174,7 @@
scope.find(name, immediate: yes) if name and not @pattern
scope.find(index, immediate: yes) if index
rvar = scope.freeVariable 'results' if @returns
- ivar = (if @range then name else index) or scope.freeVariable 'i'
the _by
variable is created twice in Range
s if we don't prevent it from being declared here
stepvar = scope.freeVariable "step" if @step and not @range
+ ivar = (if @range then name else index) or scope.freeVariable 'i'
the _by
variable is created twice in Range
s if we don't prevent it from being declared here
stepvar = scope.freeVariable "step" if @step and not @range
name = ivar if @pattern
varPart = ''
guardPart = ''
@@ -1218,10 +1229,9 @@
base = new Value ref
if val.base
[val.base, base] = [base, val]
- args.unshift new Literal 'this'
body.expressions[idx] = new Call base, expr.args
defs += @tab + new Assign(ref, fn).compile(o, LEVEL_TOP) + ';\n'
- defs
A JavaScript switch statement. Converts into a returnable expression on-demand.
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']
@@ -1252,7 +1262,7 @@
continue if expr instanceof Return or (expr instanceof Literal and expr.jumps() and expr.value isnt '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 + 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, @@ -1266,13 +1276,13 @@ children: ['condition', 'body', 'elseBody'] bodyNode: -> @body?.unwrap() - elseBodyNode: -> @elseBody?.unwrap()
Rewrite a chain of Ifs to add a default case as the final else.
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 = @ensureBlock elseBody
- this
The If only compiles into a statement if either of its bodies needs + 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)
@@ -1288,9 +1298,14 @@
this
ensureBlock: (node) ->
- if node instanceof Block then node else new Block [node]
Compile the If as a regular if-else statement. Flattened chains + if node instanceof Block then node else new Block [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'
+ exeq = del o, 'isExistentialEquals'
+
+ if exeq
+ return new If(@condition.invert(), @elseBodyNode(), type: 'if').compile o
+
cond = @condition.compile o, LEVEL_PAREN
o.indent += TAB
body = @ensureBlock(@body).compile o
@@ -1303,7 +1318,7 @@
o.chainChild = yes
@elseBody.unwrap().compile o, LEVEL_TOP
else
- "{\n#{ @elseBody.compile o, LEVEL_TOP }\n#{@tab}}"
Compile the If as a conditional operator.
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'
@@ -1311,14 +1326,14 @@
if o.level >= LEVEL_COND then "(#{code})" else code
unfoldSoak: ->
- @soak and this
Faux-nodes are never created by the grammar, but are used during code -generation to generate other combinations of nodes.
The Push creates the tree for array.push(value)
,
+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, + 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()
@@ -1337,11 +1352,11 @@
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) ->
+ (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
UTILITIES =
Correctly set up a prototype chain for inheritance, including a reference + 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]; }
@@ -1351,26 +1366,46 @@
child.__super__ = parent.prototype;
return child;
}
- '''
Create a function bound to the current value of "this".
bind: '''
+ '''
Create a function bound to the current value of "this".
bind: '''
function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
- '''
Discover if an item is in an array.
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;
}
- '''
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 + '''
Shortcuts to speed up the lookup time for native functions.
hasProp: 'Object.prototype.hasOwnProperty'
+ slice : 'Array.prototype.slice'
Levels indicate 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]
Tabs are two spaces for pretty printing.
TAB = ' '
-
-IDENTIFIER = /^[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*$/
-SIMPLENUM = /^[+-]?\d+$/
Is a literal value a string?
IS_STRING = /^['"]/
Helper for ensuring that utility functions are assigned at the top level.
utility = (name) ->
+LEVEL_ACCESS = 6 # ...[0]
Tabs are two spaces for pretty printing.
TAB = ' '
+
+IDENTIFIER_STR = "[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*"
+IDENTIFIER = /// ^ #{IDENTIFIER_STR} $ ///
+SIMPLENUM = /^[+-]?\d+$/
+METHOD_DEF = ///
+ ^
+ (?:
+ (#{IDENTIFIER_STR})
+ \.prototype
+ (?:
+ \.(#{IDENTIFIER_STR})
+ | \[("(?:[^\\"\r\n]|\\.)*"|'(?:[^\\'\r\n]|\\.)*')\]
+ | \[(0x[\da-fA-F]+ | \d*\.?\d+ (?:[eE][+-]?\d+)?)\]
+ )
+ )
+ |
+ (#{IDENTIFIER_STR})
+ $
+///
+
+
+#DIVIDER
+IS_STRING = /^['"]/
Is a literal value a string?
utility = (name) ->
ref = "__#{name}"
Scope.root.assign ref, UTILITIES[name]
ref
@@ -1378,4 +1413,4 @@
multident = (code, tab) ->
code.replace /\n/g, '$&' + tab
-