diff --git a/Rakefile b/Rakefile index 796c9649..acc9c2ea 100644 --- a/Rakefile +++ b/Rakefile @@ -6,7 +6,7 @@ require 'yui/compressor' HEADER = <<-EOS /** - * CoffeeScript Compiler v0.7.2 + * CoffeeScript Compiler v0.9.0 * http://coffeescript.org * * Copyright 2010, Jeremy Ashkenas diff --git a/documentation/coffee/block_comment.coffee b/documentation/coffee/block_comment.coffee index 85cae37f..f1cce0b8 100644 --- a/documentation/coffee/block_comment.coffee +++ b/documentation/coffee/block_comment.coffee @@ -1,4 +1,4 @@ ### -CoffeeScript Compiler v0.7.2 +CoffeeScript Compiler v0.9.0 Released under the MIT License ### \ No newline at end of file diff --git a/documentation/docs/cake.html b/documentation/docs/cake.html index 7bcd8bdb..6ae44bcd 100644 --- a/documentation/docs/cake.html +++ b/documentation/docs/cake.html @@ -4,42 +4,40 @@ for CoffeeScript. You define tasks with names and descriptions in a Cakefile, and can call them from the command line, or invoke them from other tasks.

Running cake with no arguments will print out a list of all the tasks in the -current directory's Cakefile.

#

External dependencies.

fs:           require 'fs'
-path:         require 'path'
-helpers:      require('./helpers').helpers
-optparse:     require './optparse'
-CoffeeScript: require './coffee-script'
#

Keep track of the list of defined tasks, the accepted options, and so on.

tasks: {}
-options: {}
-switches: []
-oparse: null
#

Mixin the top-level Cake functions for Cakefiles to use directly.

helpers.extend global, {
#

Define a Cake task with a short name, an optional sentence description, +current directory's Cakefile.

#

External dependencies.

fs           = require 'fs'
+path         = require 'path'
+helpers      = require('./helpers').helpers
+optparse     = require './optparse'
+CoffeeScript = require './coffee-script'
#

Keep track of the list of defined tasks, the accepted options, and so on.

tasks     = {}
+options   = {}
+switches  = []
+oparse    = null
#

Mixin the top-level Cake functions for Cakefiles to use directly.

helpers.extend global,
#

Define a Cake task with a short name, an optional sentence description, and the function to run as the action itself.

  task: (name, description, action) ->
-    [action, description]: [description, action] unless action
-    tasks[name]: {name, description, action}
#

Define an option that the Cakefile accepts. The parsed options hash, + [action, description] = [description, action] unless action + tasks[name] = {name, description, action}

#

Define an option that the Cakefile accepts. The parsed options hash, containing all of the command-line options passed, will be made available as the first argument to the action.

  option: (letter, flag, description) ->
     switches.push [letter, flag, description]
#

Invoke another task in the current Cakefile.

  invoke: (name) ->
     missingTask name unless tasks[name]
-    tasks[name].action options
-
-}
#

Run cake. Executes all of the tasks you pass, in order. Note that Node's + tasks[name].action options

#

Run cake. Executes all of the tasks you pass, in order. Note that Node's asynchrony may cause tasks to execute in a different order than you'd expect. -If no tasks are passed, print the help screen.

exports.run: ->
+If no tasks are passed, print the help screen.

exports.run = ->
   path.exists 'Cakefile', (exists) ->
-    throw new Error("Cakefile not found in ${process.cwd()}") unless exists
-    args: process.argv[2...process.argv.length]
-    CoffeeScript.run fs.readFileSync('Cakefile').toString(), {source: 'Cakefile'}
-    oparse: new optparse.OptionParser switches
+    throw new Error("Cakefile not found in #{process.cwd()}") unless exists
+    args = process.argv[2...process.argv.length]
+    CoffeeScript.run fs.readFileSync('Cakefile').toString(), fileName: 'Cakefile'
+    oparse = new optparse.OptionParser switches
     return printTasks() unless args.length
-    options: oparse.parse(args)
-    invoke arg for arg in options.arguments
#

Display the list of Cake tasks in a format similar to rake -T

printTasks: ->
+    options = oparse.parse(args)
+    invoke arg for arg in options.arguments
#

Display the list of Cake tasks in a format similar to rake -T

printTasks = ->
   puts ''
-  for name, task of tasks
-    spaces: 20 - name.length
-    spaces: if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
-    desc:   if task.description then "# $task.description" else ''
-    puts "cake $name$spaces $desc"
-  puts oparse.help() if switches.length
#

Print an error and exit when attempting to all an undefined task.

missingTask: (task) ->
-  puts "No such task: \"$task\""
+  for all name, task of tasks
+    spaces = 20 - name.length
+    spaces = if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
+    desc   = if task.description then "# #task.description" else ''
+    puts "cake #name#spaces #desc"
+  puts oparse.help() if switches.length
#

Print an error and exit when attempting to all an undefined task.

missingTask = (task) ->
+  puts "No such task: \"#task\""
   process.exit 1
 
 
\ No newline at end of file diff --git a/documentation/docs/coffee-script.html b/documentation/docs/coffee-script.html index eedfcfc6..03a9738e 100644 --- a/documentation/docs/coffee-script.html +++ b/documentation/docs/coffee-script.html @@ -5,51 +5,50 @@ CoffeeScript into JavaScript.

If included on a webpage, it will automatically sniff out, compile, and execute all scripts present in text/coffeescript tags.

#

Set up dependencies correctly for both the server and the browser.

if process?
-  path:         require 'path'
-  Lexer:        require('./lexer').Lexer
-  parser:       require('./parser').parser
-  helpers:      require('./helpers').helpers
+  path    = require 'path'
+  Lexer   = require('./lexer').Lexer
+  parser  = require('./parser').parser
+  helpers = require('./helpers').helpers
   helpers.extend global, require './nodes'
   if require.registerExtension
     require.registerExtension '.coffee', (content) -> compile content
 else
-  this.exports: this.CoffeeScript: {}
-  Lexer:        this.Lexer
-  parser:       this.parser
-  helpers:      this.helpers
#

The current CoffeeScript version number.

exports.VERSION: '0.7.2'
#

Instantiate a Lexer for our use here.

lexer: new Lexer
#

Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison -compiler.

exports.compile: compile: (code, options) ->
-  options: or {}
+  this.exports = this.CoffeeScript = {}
+  Lexer        = this.Lexer
+  parser       = this.parser
+  helpers      = this.helpers
#

The current CoffeeScript version number.

exports.VERSION = '0.9.0'
#

Instantiate a Lexer for our use here.

lexer = new Lexer
#

Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison +compiler.

exports.compile = compile = (code, options) ->
+  options or= {}
   try
     (parser.parse lexer.tokenize code).compile options
   catch err
-    err.message: "In $options.source, $err.message" if options.source
-    throw err
#

Tokenize a string of CoffeeScript code, and return the array of tokens.

exports.tokens: (code) ->
+    err.message = "In #options.fileName, #err.message" if options.fileName
+    throw err
#

Tokenize a string of CoffeeScript code, and return the array of tokens.

exports.tokens = (code) ->
   lexer.tokenize code
#

Tokenize and parse a string of CoffeeScript code, and return the AST. You can then compile it by calling .compile() on the root, or traverse it by using -.traverse() with a callback.

exports.nodes: (code) ->
+.traverse() with a callback.

exports.nodes = (code) ->
   parser.parse lexer.tokenize code
#

Compile and execute a string of CoffeeScript (on the server), correctly -setting __filename, __dirname, and relative require().

exports.run: ((code, options) ->
-  module.filename: __filename: options.source
-  __dirname: path.dirname(__filename)
+setting __filename, __dirname, and relative require().

exports.run = ((code, options) ->
+  module.filename = __filename = options.fileName
+  __dirname = path.dirname __filename
   eval exports.compile code, options
 )
#

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: {
+directly as a "Jison lexer".

parser.lexer =
   lex: ->
-    token: @tokens[@pos] or [""]
-    @pos: + 1
-    this.yylineno: token[2]
-    this.yytext:   token[1]
+    token = @tokens[@pos] or [""]
+    @pos += 1
+    this.yylineno = token[2]
+    this.yytext   = token[1]
     token[0]
   setInput: (tokens) ->
-    @tokens: tokens
-    @pos: 0
-  upcomingInput: -> ""
-}
#

Activate CoffeeScript in the browser by having it compile and evaluate + @tokens = tokens + @pos = 0 + upcomingInput: -> ""

#

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. Unfortunately, the text contents of remote scripts cannot be accessed from the browser, so only inline script tags will work.

if document? and document.getElementsByTagName
-  processScripts: ->
+  processScripts = ->
     for tag in document.getElementsByTagName('script') when tag.type is 'text/coffeescript'
       eval exports.compile tag.innerHTML
   if window.addEventListener
diff --git a/documentation/docs/command.html b/documentation/docs/command.html
index 30665e00..38792892 100644
--- a/documentation/docs/command.html
+++ b/documentation/docs/command.html
@@ -2,16 +2,16 @@
 into various forms: saved into .js files or printed to stdout, piped to
 JSLint or recompiled every time the source is
 saved, printed as a token stream or as the syntax tree, or launch an
-interactive REPL.

#

External dependencies.

fs:            require 'fs'
-path:          require 'path'
-optparse:      require './optparse'
-CoffeeScript:  require './coffee-script'
-{spawn, exec}: require('child_process')
#

The help banner that is printed when coffee is called without arguments.

BANNER: '''
+interactive REPL.

#

External dependencies.

fs            = require 'fs'
+path          = require 'path'
+optparse      = require './optparse'
+CoffeeScript  = require './coffee-script'
+{spawn, exec} = require 'child_process'
#

The help banner that is printed when coffee is called without arguments.

BANNER = '''
   coffee compiles CoffeeScript source files into JavaScript.
 
   Usage:
     coffee path/to/script.coffee
-        '''
#

The list of all the valid option flags that coffee knows how to handle.

SWITCHES: [
+         '''
#

The list of all the valid option flags that coffee knows how to handle.

SWITCHES = [
   ['-c', '--compile',       'compile to JavaScript and save as .js files']
   ['-i', '--interactive',   'run an interactive CoffeeScript REPL']
   ['-o', '--output [DIR]',  'set the directory for compiled JavaScript']
@@ -25,11 +25,11 @@ interactive REPL.

['-n', '--nodes', 'print the parse tree that Jison produces'] ['-v', '--version', 'display CoffeeScript version'] ['-h', '--help', 'display this help message'] -]
#

Top-level objects shared by all the functions.

options: {}
-sources: []
-optionParser: null
#

Run coffee by parsing passed options and determining what action to take. +]

#

Top-level objects shared by all the functions.

options      = {}
+sources      = []
+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: ->
+-- will be passed verbatim to your script as arguments in process.argv

exports.run = ->
   parseOptions()
   return usage()                              if options.help
   return version()                            if options.version
@@ -37,23 +37,23 @@ Many flags cause us to divert before compiling anything. Flags passed after
   return compileStdio()                       if options.stdio
   return compileScript 'console', sources[0]  if options.eval
   return require './repl'                     unless sources.length
-  separator: sources.indexOf '--'
-  flags: []
+  separator = sources.indexOf '--'
+  flags = []
   if separator >= 0
-    flags:   sources[(separator + 1)...sources.length]
-    sources: sources[0...separator]
+    flags   = sources[(separator + 1)...sources.length]
+    sources = sources[0...separator]
   if options.run
-    flags:   sources[1..sources.length].concat flags
-    sources: [sources[0]]
-  process.ARGV: process.argv: flags
+    flags   = sources[1..sources.length].concat flags
+    sources = [sources[0]]
+  process.ARGV = process.argv = flags
   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: ->
+'.coffee' extension source files in it and all subdirectories.

compileScripts = ->
   for source in sources
-    base: source
-    compile: (source, topLevel) ->
+    base = source
+    compile = (source, topLevel) ->
       path.exists source, (exists) ->
-        throw new Error "File not found: $source" unless exists
+        throw new Error "File not found: #source" unless exists
         fs.stat source, (err, stats) ->
           if stats.isDirectory()
             fs.readdir source, (err, files) ->
@@ -64,68 +64,69 @@ compile them. If a directory is passed, recursively compile all
             watch source, base if options.watch
     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: (source, code, base) ->
-  o: options
-  codeOpts: compileOptions source
+__dirname and module.filename to be correct relative to the script's path.

compileScript = (source, code, base) ->
+  o = options
+  codeOpts = compileOptions source
   try
     if      o.tokens      then printTokens CoffeeScript.tokens code
     else if o.nodes       then puts CoffeeScript.nodes(code).toString()
     else if o.run         then CoffeeScript.run code, codeOpts
     else
-      js: CoffeeScript.compile code, codeOpts
+      js = CoffeeScript.compile code, codeOpts
       if o.print          then print js
       else if o.compile   then writeJs source, js, base
       else if o.lint      then lint js
   catch err
     error(err.stack) and process.exit 1 unless o.watch
     puts err.message
#

Attach the appropriate listeners to compile scripts incoming over stdin, -and write them back to stdout.

compileStdio: ->
-  code: ''
-  stdin: process.openStdin()
-  stdin.addListener 'data', (buffer) ->
-    code: + buffer.toString() if buffer
-  stdin.addListener 'end', ->
+and write them back to stdout.

compileStdio = ->
+  code = ''
+  stdin = process.openStdin()
+  stdin.on 'data', (buffer) ->
+    code += buffer.toString() if buffer
+  stdin.on 'end', ->
     compileScript 'stdio', code
#

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) ->
+such as --lint or --print.

watch = (source, base) ->
   fs.watchFile source, {persistent: true, interval: 500}, (curr, prev) ->
     return if curr.mtime.getTime() is prev.mtime.getTime()
     fs.readFile source, (err, code) -> compileScript(source, code.toString(), base)
#

Write out a JavaScript source file with the compiled code. By default, files are written out in cwd as .js files with the same name, but the output -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
-  dir:      if options.output then path.join options.output, baseDir else srcDir
-  jsPath:   path.join dir, filename
-  compile:  ->
+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
+  dir       = if options.output then path.join options.output, baseDir else srcDir
+  jsPath    = path.join dir, filename
+  compile   = ->
     fs.writeFile jsPath, js, (err) ->
-      puts "Compiled $source" if options.compile and options.watch
+      puts "Compiled #source" if options.compile and options.watch
   path.exists dir, (exists) ->
-    if exists then compile() else exec "mkdir -p $dir", compile
#

Pipe compiled JS through JSLint (requires a working jsl command), printing -any errors or warnings that arise.

lint: (js) ->
-  printIt: (buffer) -> print buffer.toString()
-  jsl: spawn 'jsl', ['-nologo', '-stdin']
-  jsl.stdout.addListener 'data', printIt
-  jsl.stderr.addListener 'data', printIt
+    if exists then compile() else exec "mkdir -p #dir", compile
#

Pipe compiled JS through JSLint (requires a working jsl command), printing +any errors or warnings that arise.

lint = (js) ->
+  printIt = (buffer) -> print buffer.toString()
+  jsl = spawn 'jsl', ['-nologo', '-stdin']
+  jsl.stdout.on 'data', printIt
+  jsl.stderr.on 'data', printIt
   jsl.stdin.write js
-  jsl.stdin.end()
#

Pretty-print a stream of tokens.

printTokens: (tokens) ->
-  strings: for token in tokens
-    [tag, value]: [token[0], token[1].toString().replace(/\n/, '\\n')]
-    "[$tag $value]"
+  jsl.stdin.end()
#

Pretty-print a stream of tokens.

printTokens = (tokens) ->
+  strings = for token in tokens
+    [tag, value] = [token[0], token[1].toString().replace(/\n/, '\\n')]
+    "[#tag #value]"
   puts strings.join(' ')
#

Use the OptionParser module to extract all options from -process.argv that are specified in SWITCHES.

parseOptions: ->
-  optionParser:  new optparse.OptionParser SWITCHES, BANNER
-  o: options:    optionParser.parse(process.argv[2...process.argv.length])
-  options.run:   not (o.compile or o.print or o.lint)
-  options.print: !!  (o.print or (o.eval or o.stdio and o.compile))
-  sources:       options.arguments
#

The compile-time options to pass to the CoffeeScript compiler.

compileOptions: (source) ->
-  o: {source}
-  o.noWrap: options['no-wrap']
-  o
#

Print the --help usage message and exit.

usage: ->
+process.argv that are specified in SWITCHES.

parseOptions = ->
+  optionParser    = new optparse.OptionParser SWITCHES, BANNER
+  o = options     = optionParser.parse(process.argv[2...process.argv.length])
+  options.compile or=  !!o.output
+  options.run     = not (o.compile or o.print or o.lint)
+  options.print   = !!  (o.print or (o.eval or o.stdio and o.compile))
+  sources         = options.arguments
#

The compile-time options to pass to the CoffeeScript compiler.

compileOptions = (fileName) ->
+  o = {fileName}
+  o.noWrap = options['no-wrap']
+  o
#

Print the --help usage message and exit.

usage = ->
   puts optionParser.help()
-  process.exit 0
#

Print the --version message and exit.

version: ->
-  puts "CoffeeScript version $CoffeeScript.VERSION"
+  process.exit 0
#

Print the --version message and exit.

version = ->
+  puts "CoffeeScript version #CoffeeScript.VERSION"
   process.exit 0
 
 
\ No newline at end of file diff --git a/documentation/docs/grammar.html b/documentation/docs/grammar.html index 59b980fa..ec8143e4 100644 --- a/documentation/docs/grammar.html +++ b/documentation/docs/grammar.html @@ -12,16 +12,16 @@ reduces into the

The only dependency is on the Jison.Parser.

Parser: require('jison').Parser
#

Jison DSL

#

Since we're going to be wrapped in a function by Jison in any case, if our +from our rules and saves it into lib/parser.js.

#

The only dependency is on the Jison.Parser.

Parser = require('jison').Parser
#

Jison DSL

#

Since we're going to be wrapped in a function by Jison in any case, if our action immediately returns a value, we can optimize by removing the function -wrapper and just returning the value directly.

unwrap: /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
#

Our handy DSL for Jison grammar generation, thanks to +wrapper and just returning the value directly.

unwrap = /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
#

Our handy DSL for Jison grammar generation, thanks to Tim Caswell. For every rule in the grammar, we pass the pattern-defining string, the action to run, and extra options, optionally. If no action is specified, we simply pass the value of the -previous nonterminal.

o: (patternString, action, options) ->
+previous nonterminal.

o = (patternString, action, options) ->
   return [patternString, '$$ = $1;', options] unless action
-  action: if match: (action + '').match(unwrap) then match[1] else "($action())"
-  [patternString, "$$ = $action;", options]
#

Grammatical Rules

#

In all of the rules that follow, you'll see the name of the nonterminal as + action = if match = (action + '').match(unwrap) then match[1] else "(#action())" + [patternString, "$$ = #action;", options]

#

Grammatical Rules

#

In all of the rules that follow, you'll see the name of the nonterminal as the key to a list of alternative matches. With each match's action, the dollar-sign variables are provided by Jison as references to the value of their numeric position, so in this rule:

@@ -31,7 +31,7 @@ their numeric position, so in this rule:

$1 would be the value of the first Expression, $2 would be the token for the UNLESS terminal, and $3 would be the value of the second -Expression.

grammar: {
#

The Root is the top-level node in the syntax tree. Since we parse bottom-up, +Expression.

grammar =
#

The Root is the top-level node in the syntax tree. Since we parse bottom-up, all parsing must end here.

  Root: [
     o "",                                       -> new Expressions
     o "TERMINATOR",                             -> new Expressions
@@ -92,13 +92,16 @@ through and printed to JavaScript.

o "ON", -> new LiteralNode true o "OFF", -> new LiteralNode false ]
#

Assignment of a variable, property, or index to a value.

  Assign: [
-    o "Assignable ASSIGN Expression",           -> new AssignNode $1, $3
+    o "Assignable = Expression",                -> new AssignNode $1, $3
+    o "Assignable = INDENT Expression OUTDENT", -> new AssignNode $1, $4
   ]
#

Assignment when it happens within an object literal. The difference from the ordinary Assign is that these allow numbers and strings as keys.

  AssignObj: [
     o "Identifier",                             -> new ValueNode $1
     o "AlphaNumeric"
-    o "Identifier ASSIGN Expression",           -> new AssignNode new ValueNode($1), $3, 'object'
-    o "AlphaNumeric ASSIGN Expression",         -> new AssignNode new ValueNode($1), $3, 'object'
+    o "Identifier : Expression",                -> new AssignNode new ValueNode($1), $3, 'object'
+    o "AlphaNumeric : Expression",              -> new AssignNode new ValueNode($1), $3, 'object'
+    o "Identifier : INDENT Expression OUTDENT", -> new AssignNode new ValueNode($1), $4, 'object'
+    o "AlphaNumeric : INDENT Expression OUTDENT", -> new AssignNode new ValueNode($1), $4, 'object'
     o "Comment"
   ]
#

A return statement from a function body.

  Return: [
     o "RETURN Expression",                      -> new ReturnNode $2
@@ -126,7 +129,9 @@ functions, and => is for functions bound to the current value of
   ]
#

A single parameter in a function definition can be ordinary, or a splat that hoovers up the remaining arguments.

  Param: [
     o "PARAM",                                  -> new LiteralNode $1
-    o "Param . . .",                            -> new SplatNode $1
+    o "@ PARAM",                                -> new ParamNode $2, true
+    o "PARAM . . .",                            -> new ParamNode $1, false, true
+    o "@ PARAM . . .",                          -> new ParamNode $2, true, true
   ]
#

A splat that occurs outside of a parameter list.

  Splat: [
     o "Expression . . .",                       -> new SplatNode $1
   ]
#

Variables and properties that can be assigned to.

  SimpleAssignable: [
@@ -156,8 +161,8 @@ or by array index or slice.

o "Slice", -> new SliceNode $1 ]
#

Indexing into an object or array using bracket notation.

  Index: [
     o "INDEX_START Expression INDEX_END",       -> new IndexNode $2
-    o "INDEX_SOAK Index",                       -> $2.soakNode: yes; $2
-    o "INDEX_PROTO Index",                      -> $2.proto: yes; $2
+    o "INDEX_SOAK Index",                       -> $2.soakNode = yes; $2
+    o "INDEX_PROTO Index",                      -> $2.proto = yes; $2
   ]
#

In CoffeeScript, an object literal is simply a list of assignments.

  Object: [
     o "{ AssignList OptComma }",                -> new ObjectNode $2
   ]
#

Assignment of properties within an object literal can be separated by @@ -173,13 +178,15 @@ and optional references to the superclass.

o "CLASS SimpleAssignable EXTENDS Value", -> new ClassNode $2, $4 o "CLASS SimpleAssignable INDENT ClassBody OUTDENT", -> new ClassNode $2, null, $4 o "CLASS SimpleAssignable EXTENDS Value INDENT ClassBody OUTDENT", -> new ClassNode $2, $4, $6 + o "CLASS INDENT ClassBody OUTDENT", -> new ClassNode '__temp__', null, $3 ]
#

Assignments that can happen directly inside a class declaration.

  ClassAssign: [
     o "AssignObj",                              -> $1
-    o "ThisProperty ASSIGN Expression",         -> new AssignNode new ValueNode($1), $3, 'this'
+    o "ThisProperty : Expression",              -> new AssignNode new ValueNode($1), $3, 'this'
   ]
#

A list of assignments to a class.

  ClassBody: [
     o "",                                       -> []
     o "ClassAssign",                            -> [$1]
     o "ClassBody TERMINATOR ClassAssign",       -> $1.concat $3
+    o "{ ClassBody }",                          -> $2
   ]
#

The three flavors of function call: normal, object instantiation with new, and calling super()

  Call: [
     o "Invocation"
@@ -195,6 +202,7 @@ object.

]
#

The list of arguments to a function call.

  Arguments: [
     o "CALL_START ArgList OptComma CALL_END",   -> $2
   ]
#

Calling super.

  Super: [
+    o "SUPER",                                  -> new CallNode 'super', [new SplatNode(new LiteralNode('arguments'))]
     o "SUPER Arguments",                        -> new CallNode 'super', $2
   ]
#

A reference to the this current object.

  This: [
     o "THIS",                                   -> new ValueNode new LiteralNode 'this'
@@ -238,9 +246,9 @@ the trick.

o "( Line )", -> new ParentheticalNode $2 ]
#

The condition portion of a while loop.

  WhileSource: [
     o "WHILE Expression",                       -> new WhileNode $2
-    o "WHILE Expression WHEN Expression",       -> new WhileNode $2, {guard : $4}
-    o "UNTIL Expression",                       -> new WhileNode $2, {invert: true}
-    o "UNTIL Expression WHEN Expression",       -> new WhileNode $2, {invert: true, guard: $4}
+    o "WHILE Expression WHEN Expression",       -> new WhileNode $2, guard: $4
+    o "UNTIL Expression",                       -> new WhileNode $2, invert: true
+    o "UNTIL Expression WHEN Expression",       -> new WhileNode $2, invert: true, guard: $4
   ]
#

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
@@ -255,9 +263,19 @@ 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 FOR ForVariables ForSource",   -> new ForNode $1, $4, $3[0], $3[1]
-    o "Expression FOR ForVariables ForSource",  -> new ForNode $1, $4, $3[0], $3[1]
-    o "FOR ForVariables ForSource Block",       -> new ForNode $4, $3, $2[0], $2[1]
+    o "Statement ForBody",                      -> new ForNode $1, $2, $2.vars[0], $2.vars[1]
+    o "Expression ForBody",                     -> new ForNode $1, $2, $2.vars[0], $2.vars[1]
+    o "ForBody Block",                          -> new ForNode $2, $1, $1.vars[0], $1.vars[1]
+  ]
+
+  ForBody: [
+    o "FOR Range",                              -> source: new ValueNode($2), vars: []
+    o "ForStart ForSource",                     -> $2.raw = $1.raw; $2.vars = $1; $2
+  ]
+
+  ForStart: [
+    o "FOR ForVariables",                       -> $2
+    o "FOR ALL ForVariables",                   -> $3.raw = true; $3
   ]
#

An array of all accepted values for a variable inside the loop. This enables support for pattern matching.

  ForValue: [
     o "Identifier"
@@ -271,13 +289,13 @@ of object comprehensions.

]
#

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 "IN Expression",                               -> {source: $2}
-    o "OF Expression",                               -> {source: $2, object: true}
-    o "IN Expression WHEN Expression",               -> {source: $2, guard: $4}
-    o "OF Expression WHEN Expression",               -> {source: $2, guard: $4, object: true}
-    o "IN Expression BY Expression",                 -> {source: $2, step:   $4}
-    o "IN Expression WHEN Expression BY Expression", -> {source: $2, guard: $4, step:   $6}
-    o "IN Expression BY Expression WHEN Expression", -> {source: $2, step:   $4, guard: $6}
+    o "IN Expression",                               -> source: $2
+    o "OF Expression",                               -> source: $2, object: true
+    o "IN Expression WHEN Expression",               -> source: $2, guard: $4
+    o "OF Expression WHEN Expression",               -> source: $2, guard: $4, object: true
+    o "IN Expression BY Expression",                 -> source: $2, step:  $4
+    o "IN Expression WHEN Expression BY Expression", -> source: $2, guard: $4, step:   $6
+    o "IN Expression BY Expression WHEN Expression", -> source: $2, step:  $4, guard: $6
   ]
#

The CoffeeScript switch/when/else block replaces the JavaScript switch/case/default by compiling into an if-else chain.

  Switch: [
     o "SWITCH Expression INDENT Whens OUTDENT", -> $4.switchesOver $2
@@ -289,22 +307,22 @@ IfNode will rewrite them into a proper chain.

o "When" o "Whens When", -> $1.addElse $2 ]
#

An individual When clause, with action.

  When: [
-    o "LEADING_WHEN SimpleArgs Block",            -> new IfNode $2, $3, {statement: true}
-    o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode $2, $3, {statement: true}
+    o "LEADING_WHEN SimpleArgs Block",            -> new IfNode $2, $3, statement: true
+    o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode $2, $3, statement: true
   ]
#

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 IfNode $2, $3
-    o "UNLESS Expression Block",                -> new IfNode $2, $3, {invert: true}
+    o "UNLESS Expression Block",                -> new IfNode $2, $3, invert: true
     o "IfBlock ELSE IF Expression Block",       -> $1.addElse (new IfNode($4, $5)).forceStatement()
     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 IF Expression",                -> new IfNode $3, Expressions.wrap([$1]), {statement: true}
-    o "Expression IF Expression",               -> new IfNode $3, Expressions.wrap([$1]), {statement: true}
-    o "Statement UNLESS Expression",            -> new IfNode $3, Expressions.wrap([$1]), {statement: true, invert: true}
-    o "Expression UNLESS Expression",           -> new IfNode $3, Expressions.wrap([$1]), {statement: true, invert: true}
+    o "Statement IF Expression",                -> new IfNode $3, Expressions.wrap([$1]), statement: true
+    o "Expression IF Expression",               -> new IfNode $3, Expressions.wrap([$1]), statement: true
+    o "Statement UNLESS Expression",            -> new IfNode $3, Expressions.wrap([$1]), statement: true, invert: true
+    o "Expression UNLESS Expression",           -> new IfNode $3, Expressions.wrap([$1]), statement: true, invert: 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 @@ -363,9 +381,7 @@ rules are necessary.

o "Expression OF Expression", -> new OpNode 'in', $1, $3 o "Expression ! IN Expression", -> new OpNode '!', new InNode $1, $4 o "Expression ! OF Expression", -> new OpNode '!', new ParentheticalNode new OpNode 'in', $1, $4 - ] - -}
#

Precedence

#

Operators at the top of this list have higher precedence than the ones lower + ]

#

Precedence

#

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)
@@ -374,7 +390,7 @@ down. Following these rules is what makes 2 + 3 * 4 parse as:

And not:

(2 + 3) * 4
-
operators: [
+
operators = [
   ["left",      '?']
   ["nonassoc",  'UMINUS', 'UPLUS', '!', '!!', '~', '++', '--']
   ["left",      '*', '/', '%']
@@ -392,21 +408,21 @@ down. Following these rules is what makes 2 + 3 * 4 parse as:

["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW'] ["right", 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'NEW', 'SUPER', 'CLASS'] ["left", 'EXTENDS'] - ["right", 'ASSIGN', 'RETURN'] + ["right", '=', ':', 'RETURN'] ["right", '->', '=>', 'UNLESS', 'IF', 'ELSE'] ]
#

Wrapping Up

#

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) -as "tokens".

tokens: []
+as "tokens".

tokens = []
 for name, alternatives of grammar
-  grammar[name]: for alt in alternatives
+  grammar[name] = for alt in alternatives
     for token in alt[0].split ' '
       tokens.push token unless grammar[token]
-    alt[1] = "return ${alt[1]}" if name is 'Root'
+    alt[1] = "return #{alt[1]}" if name is 'Root'
     alt
#

Initialize the Parser with our list of terminal tokens, our grammar 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 {
+(as in Yacc).

exports.parser = new Parser {
   tokens:       tokens.join ' '
   bnf:          grammar
   operators:    operators.reverse()
diff --git a/documentation/docs/helpers.html b/documentation/docs/helpers.html
index ec7d7a99..46df9235 100644
--- a/documentation/docs/helpers.html
+++ b/documentation/docs/helpers.html
@@ -1,35 +1,37 @@
       helpers.coffee           
< Recursively traverses down the children of the nodes, yielding to a block and returning true when the block finds a match. contains does not cross scope boundaries.

returniffunc(child)isfalsecollectChildren: -> - nodes: [] + nodes=[]@eachChild(node)->nodes.pushnodenodes @@ -90,22 +93,22 @@ This is what coffee --nodes prints out.

@eachChild(child)->func.apply(this,arguments)child.traverseChildren(crossScope,func)ifchildinstanceofBaseNode 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.

@variableinstanceofValueNodemakeReturn: -> - returnnewExpressions[this,newReturnNode(@variable)] + if@isStatement() + returnnewExpressions[this,newReturnNode(@variable)] + else + super()isStatement: -> - @isValue()and(@variable.isArray()or@variable.isObject())constructor: (condition,opts)->ifoptsandopts.invert - condition: newParentheticalNodeconditionifconditioninstanceofOpNode - condition: newOpNode('!',condition) - @condition: condition - @guard: optsandopts.guard + condition=newParentheticalNodeconditionifconditioninstanceofOpNode + condition=newOpNode('!',condition) + @condition=condition + @guard=optsandopts.guardaddBody: (body)-> - @body: body + @body=bodythismakeReturn: -> - @returns: true + @returns=truethistopSensitive: -> - truesuper(idt,@class+' '+@operator)compileNode: (o)-> - o.operation: true + o.operation=truereturn@compileChain(o)if@isChainable()and@first.unwrap()instanceofOpNodeand@first.unwrap().isChainable()return@compileAssignment(o)ifindexOf(@ASSIGNMENT,@operator)>=0return@compileUnary(o)if@isUnary()return@compileExistence(o)if@operatoris'?' - [@first.compile(o),@operator,@second.compile(o)].join' 'makeReturn: ->if@isStatement() - @body: and@ensureExpressions(@body.makeReturn()) - @elseBody: and@ensureExpressions(@elseBody.makeReturn()) + @bodyand=@ensureExpressions(@body.makeReturn()) + @elseBodyand=@ensureExpressions(@elseBody.makeReturn())thiselsenewReturnNodethisensureExpressions: (node)-> - ifnodeinstanceofExpressionsthennodeelsenewExpressions[node]

helpers.coffee

#

This file contains the common helper functions that we'd like to share among the Lexer, Rewriter, and the Nodes. Merge objects, flatten -arrays, count characters, that sort of thing.

#

Set up exported variables for both Node.js and the browser.

this.exports: this unless process?
-helpers: exports.helpers: {}
#

Cross-browser indexOf, so that IE can join the party.

helpers.indexOf: indexOf: (array, item, from) ->
+arrays, count characters, that sort of thing.

#

Set up exported variables for both Node.js and the browser.

this.exports = this unless process?
+helpers = exports.helpers = {}
#

Cross-browser indexOf, so that IE can join the party.

helpers.indexOf = indexOf = (array, item, from) ->
   return array.indexOf item, from if array.indexOf
   for other, index in array
     if other is item and (not from or (from <= index))
       return index
-  -1
#

Does a list include a value?

helpers.include: include: (list, value) ->
-  indexOf(list, value) >= 0
#

Peek at the beginning of a given string to see if it matches a sequence.

helpers.starts: starts: (string, literal, start) ->
-  string.substring(start, (start or 0) + literal.length) is literal
#

Trim out all falsy values from an array.

helpers.compact: compact: (array) -> item for item in array when item
#

Count the number of occurences of a character in a string.

helpers.count: count: (string, letter) ->
-  num: 0
-  pos: indexOf string, letter
+  -1
#

Does a list include a value?

helpers.include = include = (list, value) ->
+  indexOf(list, value) >= 0
#

Peek at the beginning of a given string to see if it matches a sequence.

helpers.starts = starts = (string, literal, start) ->
+  string.substring(start, (start or 0) + literal.length) is literal
#

Peek at the end of a given string to see if it matches a sequence.

helpers.ends = ends = (string, literal, back) ->
+  start = string.length - literal.length - (back ? 0)
+  string.substring(start, start + literal.length) is literal
#

Trim out all falsy values from an array.

helpers.compact = compact = (array) -> item for item in array when item
#

Count the number of occurences of a character in a string.

helpers.count = count = (string, letter) ->
+  num = 0
+  pos = indexOf string, letter
   while pos isnt -1
-    num: + 1
-    pos: indexOf string, letter, pos + 1
-  num
#

Merge objects, returning a fresh copy with attributes from both sides. + num += 1 + pos = indexOf string, letter, pos + 1 + num

#

Merge objects, returning a fresh copy with attributes from both sides. Used every time BaseNode#compile is called, to allow properties in the -options hash to propagate down the tree without polluting other branches.

helpers.merge: merge: (options, overrides) ->
-  fresh: {}
-  (fresh[key]: val) for key, val of options
-  (fresh[key]: val) for key, val of overrides if overrides
-  fresh
#

Extend a source object with the properties of another object (shallow copy). -We use this to simulate Node's deprecated process.mixin

helpers.extend: extend: (object, properties) ->
-  (object[key]: val) for key, val of properties
#

Return a completely flattened version of an array. Handy for getting a -list of children from the nodes.

helpers.flatten: flatten: (array) ->
-  memo: []
+options hash to propagate down the tree without polluting other branches.

helpers.merge = merge = (options, overrides) ->
+  fresh = {}
+  (fresh[key] = val) for all key, val of options
+  (fresh[key] = val) for all key, val of overrides if overrides
+  fresh
#

Extend a source object with the properties of another object (shallow copy). +We use this to simulate Node's deprecated process.mixin

helpers.extend = extend = (object, properties) ->
+  (object[key] = val) for all key, val of properties
#

Return a completely flattened version of an array. Handy for getting a +list of children from the nodes.

helpers.flatten = flatten = (array) ->
+  memo = []
   for item in array
-    if item instanceof Array then memo: memo.concat(item) else memo.push(item)
-  memo
#

Delete a key from an object, returning the value. Useful when a node is -looking for a particular method in an options hash.

helpers.del: del: (obj, key) ->
-  val: obj[key]
+    if item instanceof Array then memo = memo.concat(item) else memo.push(item)
+  memo
#

Delete a key from an object, returning the value. Useful when a node is +looking for a particular method in an options hash.

helpers.del = del = (obj, key) ->
+  val = obj[key]
   delete obj[key]
   val
 
diff --git a/documentation/docs/index.html b/documentation/docs/index.html
index b002853d..ea8f8bbf 100644
--- a/documentation/docs/index.html
+++ b/documentation/docs/index.html
@@ -1,3 +1,3 @@
-      index.coffee           
"delete","instanceof","typeof","switch","super","extends","class","this","null" -]

index.coffee

#

Loader for CoffeeScript as a Node.js library.

(exports[key]: val) for key, val of require './coffee-script'
+      index.coffee              
\ No newline at end of file
diff --git a/documentation/docs/lexer.html b/documentation/docs/lexer.html
index 37be4ea1..38189afc 100644
--- a/documentation/docs/lexer.html
+++ b/documentation/docs/lexer.html
@@ -7,14 +7,14 @@ form:

Which is a format that can be fed directly into Jison.

#

Set up the Lexer for both Node.js and the browser, depending on where we are.

if process?
-  {Rewriter}: require('./rewriter')
-  {helpers}:  require('./helpers')
+  {Rewriter} = require('./rewriter')
+  {helpers}  = require('./helpers')
 else
-  this.exports: this
-  Rewriter:     this.Rewriter
-  helpers:      this.helpers
#

Import the helpers we need.

{include, count, starts, compact}: helpers
#

The Lexer Class

#

The Lexer class reads a stream of CoffeeScript and divvys it up into tagged + this.exports = this + Rewriter = this.Rewriter + helpers = this.helpers

#

Import the helpers we need.

{include, count, starts, compact} = helpers
#

The Lexer Class

#

The Lexer class reads a stream of CoffeeScript and divvys it up into tagged tokens. Some potential ambiguity in the grammar has been avoided by -pushing some extra smarts into the Lexer.

exports.Lexer: class Lexer
#

tokenize is the Lexer's main method. Scan by attempting to match tokens +pushing some extra smarts into the Lexer.

exports.Lexer = class Lexer
#

tokenize is the Lexer's main method. Scan by attempting to match tokens one at a time, using a regular expression anchored at the start of the remaining code, or a custom recursive token-matching method (for interpolations). When the next token has been recorded, we move forward @@ -26,17 +26,17 @@ of source.

Before returning the token stream, run it through the Rewriter unless explicitly asked not to.

  tokenize: (code, options) ->
-    code     : code.replace /(\r|\s+$)/g, ''
-    o        : options or {}
-    @code    : code         # The remainder of the source code.
-    @i       : 0            # Current character position we're parsing.
-    @line    : o.line or 0  # The current line.
-    @indent  : 0            # The current indentation level.
-    @outdebt : 0            # The under-outdentation of the last outdent.
-    @indents : []           # The stack of all current indentation levels.
-    @tokens  : []           # Stream of parsed tokens in the form ['TYPE', value, line]
+    code     = code.replace /(\r|\s+$)/g, ''
+    o        = options or {}
+    @code    = code         # The remainder of the source code.
+    @i       = 0            # Current character position we're parsing.
+    @line    = o.line or 0  # The current line.
+    @indent  = 0            # The current indentation level.
+    @outdebt = 0            # The under-outdentation of the last outdent.
+    @indents = []           # The stack of all current indentation levels.
+    @tokens  = []           # Stream of parsed tokens in the form ['TYPE', value, line]
     while @i < @code.length
-      @chunk: @code.slice @i
+      @chunk = @code.slice @i
       @extractNextToken()
     @closeIndentation()
     return @tokens if o.rewrite is off
@@ -58,81 +58,82 @@ identifiers. Because CoffeeScript reserves a handful of keywords that are
 allowed in JavaScript, we're careful not to tag them as keywords when
 referenced as property names here, so you can still do jQuery.is() even
 though is means === otherwise.

  identifierToken: ->
-    return false unless id: @match IDENTIFIER, 1
-    @i: + id.length
-    forcedIdentifier: @tagAccessor() or @match ASSIGNED, 1
-    tag: 'IDENTIFIER'
-    tag: id.toUpperCase() if include(JS_KEYWORDS, id) or (not forcedIdentifier and include(COFFEE_KEYWORDS, id))
-    tag: 'LEADING_WHEN'   if tag is 'WHEN' and include LINE_BREAK, @tag()
+    return false unless id = @match IDENTIFIER, 1
+    @i += id.length
+    forcedIdentifier = @tagAccessor() or @match ASSIGNED, 1
+    tag = 'IDENTIFIER'
+    tag = id.toUpperCase() if include(JS_KEYWORDS, id) or (not forcedIdentifier and include(COFFEE_KEYWORDS, id))
+    tag = 'LEADING_WHEN'   if tag is 'WHEN' and include LINE_BREAK, @tag()
+    tag = 'ALL'            if id is 'all' and @tag() is 'FOR'
     if include(JS_FORBIDDEN, id)
       if forcedIdentifier
-        tag: 'STRING'
-        id:  "'$id'"
+        tag = 'STRING'
+        id  = "\"#id\""
         if forcedIdentifier is 'accessor'
-          close_index: true
+          close_index = true
           @tokens.pop() if @tag() isnt '@'
           @token 'INDEX_START', '['
       else if include(RESERVED, id)
         @identifierError id
     unless forcedIdentifier
-      tag: id: CONVERSIONS[id]      if include COFFEE_ALIASES, id
-      return @tagHalfAssignment tag if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
+      tag = id = CONVERSIONS[id] if include COFFEE_ALIASES, id
     @token tag, id
     @token ']', ']' if close_index
     true
#

Matches numbers, including decimals, hex, and exponential notation. Be careful not to interfere with ranges-in-progress.

  numberToken: ->
-    return false unless number: @match NUMBER, 1
+    return false unless number = @match NUMBER, 1
     return false if @tag() is '.' and starts number, '.'
-    @i: + number.length
+    @i += number.length
     @token 'NUMBER', number
     true
#

Matches strings, including multi-line strings. Ensures that quotation marks are balanced within the string's contents, and within nested interpolations.

  stringToken: ->
     return false unless starts(@chunk, '"') or starts(@chunk, "'")
-    return false unless string:
-      @balancedToken(['"', '"'], ['${', '}']) or
+    return false unless string =
+      @balancedToken(['"', '"'], ['#{', '}']) or
       @balancedToken ["'", "'"]
     @interpolateString string.replace STRING_NEWLINES, " \\\n"
-    @line: + count string, "\n"
-    @i: + string.length
+    @line += count string, "\n"
+    @i += string.length
     true
#

Matches heredocs, adjusting indentation to the correct level, as heredocs preserve whitespace, but ignore indentation to the left.

  heredocToken: ->
-    return false unless match: @chunk.match(HEREDOC)
-    quote: match[1].substr 0, 1
-    doc: @sanitizeHeredoc match[2] or match[4], {quote}
-    @interpolateString "$quote$doc$quote"
-    @line: + count match[1], "\n"
-    @i: + match[1].length
+    return false unless match = @chunk.match(HEREDOC)
+    quote = match[1].substr 0, 1
+    doc = @sanitizeHeredoc match[2] or match[4], {quote}
+    @interpolateString "#quote#doc#quote", heredoc: yes
+    @line += count match[1], "\n"
+    @i += match[1].length
     true
#

Matches and conumes comments.

  commentToken: ->
-    return false unless match: @chunk.match(COMMENT)
-    @line: + count match[1], "\n"
-    @i: + match[1].length
+    return false unless match = @chunk.match(COMMENT)
+    @line += count match[1], "\n"
+    @i += match[1].length
     if match[2]
-      comment: @sanitizeHeredoc match[2], {herecomment: true}
+      comment = @sanitizeHeredoc match[2], herecomment: true
       @token 'HERECOMMENT', comment.split MULTILINER
       @token 'TERMINATOR', '\n'
     true
#

Matches JavaScript interpolated directly into the source via backticks.

  jsToken: ->
     return false unless starts @chunk, '`'
-    return false unless script: @balancedToken ['`', '`']
+    return false unless script = @balancedToken ['`', '`']
     @token 'JS', script.replace JS_CLEANER, ''
-    @i: + script.length
+    @i += script.length
     true
#

Matches regular expression literals. Lexing regular expressions is difficult to distinguish from division, so we borrow some basic heuristics from JavaScript and Ruby, borrow slash balancing from @balancedToken, and borrow interpolation from @interpolateString.

  regexToken: ->
     return false unless @chunk.match REGEX_START
     return false if     include NOT_REGEX, @tag()
-    return false unless regex: @balancedToken ['/', '/']
-    return false unless end: @chunk.substr(regex.length).match REGEX_END
-    regex: + flags: end[2] if end[2]
+    return false unless regex = @balancedToken ['/', '/']
+    return false unless end = @chunk.substr(regex.length).match REGEX_END
+    regex += flags = end[2] if end[2]
     if regex.match REGEX_INTERPOLATION
-      str: regex.substring(1).split('/')[0]
-      str: str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped
-      @tokens: @tokens.concat [['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]
-      @interpolateString "\"$str\"", yes
-      @tokens: @tokens.concat [[',', ','], ['STRING', "\"$flags\""], [')', ')'], [')', ')']]
+      str = regex.substring(1).split('/')[0]
+      str = str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped
+      @tokens = @tokens.concat [['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]
+      @interpolateString "\"#str\"", escapeQuotes: yes
+      @tokens.splice @tokens.length, 0, [',', ','], ['STRING', "\"#flags\""] if flags
+      @tokens.splice @tokens.length, 0, [')', ')'], [')', ')']
     else
       @token 'REGEX', regex
-    @i: + regex.length
+    @i += regex.length
     true
#

Matches a token in which which the passed delimiter pairs must be correctly balanced (ie. strings, JS literals).

  balancedToken: (delimited...) ->
     @balancedString @chunk, delimited
#

Matches newlines, indents, and outdents, and determines which is which. @@ -146,41 +147,51 @@ then the newline is suppressed:

Keeps track of the level of indentation, because a single outdent token can close multiple indents, so we need to know how far in we happen to be.

  lineToken: ->
-    return false unless indent: @match MULTI_DENT, 1
-    @line: + count indent, "\n"
-    @i   : + indent.length
-    prev: @prev(2)
-    size: indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length
-    nextCharacter: @match NEXT_CHARACTER, 1
-    noNewlines: nextCharacter is '.' or nextCharacter is ',' or @unfinished()
+    return false unless indent = @match MULTI_DENT, 1
+    @line += count indent, "\n"
+    @i    += indent.length
+    prev = @prev(2)
+    size = indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length
+    nextCharacter = @match NEXT_CHARACTER, 1
+    noNewlines = nextCharacter is '.' or nextCharacter is ',' or @unfinished()
     if size is @indent
       return @suppressNewlines() if noNewlines
       return @newlineToken indent
     else if size > @indent
       return @suppressNewlines() if noNewlines
-      diff: size - @indent
+      @outdebt = 0
+      diff = size - @indent
       @token 'INDENT', diff
       @indents.push diff
     else
       @outdentToken @indent - size, noNewlines
-    @indent: size
+    @indent = size
     true
#

Record an outdent token or multiple tokens, if we happen to be moving back -inwards past several recorded indents.

  outdentToken: (moveOut, noNewlines) ->
-    if moveOut > -@outdebt
-      while moveOut > 0 and @indents.length
-        lastIndent: @indents.pop()
-        @token 'OUTDENT', lastIndent
-        moveOut: - lastIndent
-    else
-      @outdebt: + moveOut
-    @outdebt: moveOut unless noNewlines
+inwards past several recorded indents.

  outdentToken: (moveOut, noNewlines, close) ->
+    while moveOut > 0
+      len = @indents.length - 1
+      if @indents[len] is undefined
+        moveOut = 0
+      else if @indents[len] is @outdebt
+        moveOut -= @outdebt
+        @outdebt = 0
+      else if @indents[len] < @outdebt
+        @outdebt -= @indents[len]
+        moveOut -= @indents[len]
+      else
+        dent = @indents.pop()
+        dent -= @outdebt
+        moveOut -= dent
+        @outdebt = 0
+        @token 'OUTDENT', dent
+    @outdebt -= moveOut if dent
     @token 'TERMINATOR', "\n" unless @tag() is 'TERMINATOR' or noNewlines
     true
#

Matches and consumes non-meaningful whitespace. Tag the previous token as being "spaced", because there are some cases where it makes a difference.

  whitespaceToken: ->
-    return false unless space: @match WHITESPACE, 1
-    prev: @prev()
-    prev.spaced: true if prev
-    @i: + space.length
+    return false unless space = @match WHITESPACE, 1
+    prev = @prev()
+    prev.spaced = true if prev
+    @i += space.length
     true
#

Generate a newline token. Consecutive newlines get merged together.

  newlineToken: (newlines) ->
     @token 'TERMINATOR', "\n" unless @tag() is 'TERMINATOR'
     true
#

Use a \ at a line-ending to suppress the newline. @@ -191,35 +202,35 @@ Multi-character operators are also literal tokens, so that Jison can assign the proper order of operations. There are some symbols that we tag specially here. ; and newlines are both treated as a TERMINATOR, we distinguish parentheses that indicate a method call from regular parentheses, and so on.

  literalToken: ->
-    match: @chunk.match OPERATOR
-    value: match and match[1]
-    space: match and match[2]
+    match = @chunk.match OPERATOR
+    value = match and match[1]
+    space = match and match[2]
     @tagParameters() if value and value.match CODE
-    value: or @chunk.substr 0, 1
-    prevSpaced: @prev() and @prev().spaced
-    tag: value
-    if value.match ASSIGNMENT
-      tag: 'ASSIGN'
-      @assignmentError() if include JS_FORBIDDEN, @value
-    else if value is ';'
-      tag: 'TERMINATOR'
+    value or= @chunk.substr 0, 1
+    @i += value.length
+    prevSpaced = @prev() and @prev().spaced
+    tag = value
+    if value is '='
+      @assignmentError() if include JS_FORBIDDEN, @value()
+      if @value() in ['or', 'and']
+        return @tag 1, CONVERSIONS[@value()] + '='
+    if value is ';'
+      tag = 'TERMINATOR'
     else if value is '?' and prevSpaced
-      tag: 'OP?'
+      tag = 'OP?'
     else if include(CALLABLE, @tag()) and not prevSpaced
       if value is '('
-        tag: 'CALL_START'
+        tag = 'CALL_START'
       else if value is '['
-        tag: 'INDEX_START'
+        tag = 'INDEX_START'
         @tag 1, 'INDEX_SOAK'  if @tag() is '?'
         @tag 1, 'INDEX_PROTO' if @tag() is '::'
-    @i: + value.length
-    return @tagHalfAssignment tag if space and prevSpaced and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
     @token tag, value
     true
#

Token Manipulators

#

As we consume a new IDENTIFIER, look at the previous token to determine if it's a special kind of accessor. Return true if any type of accessor is the previous token.

  tagAccessor: ->
-    return false if (not prev: @prev()) or (prev and prev.spaced)
-    accessor: if prev[1] is '::'
+    return false if (not prev = @prev()) or (prev and prev.spaced)
+    accessor = if prev[1] is '::'
       @tag 1, 'PROTOTYPE_ACCESS'
     else if prev[1] is '.' and not (@value(2) is '.')
       if @tag(2) is '?'
@@ -231,134 +242,132 @@ is the previous token.

prev[0] is '@' if accessor then 'accessor' else false
#

Sanitize a heredoc or herecomment by escaping internal double quotes and erasing all external indentation on the left-hand side.

  sanitizeHeredoc: (doc, options) ->
-    while match: HEREDOC_INDENT.exec doc
-      attempt: if match[2]? then match[2] else match[3]
-      indent: attempt if not indent or attempt.length < indent.length
-    doc: doc.replace(new RegExp("^" +indent, 'gm'), '')
+    while (match = HEREDOC_INDENT.exec(doc))
+      attempt = if match[2]? then match[2] else match[3]
+      indent = attempt if not indent or attempt.length < indent.length
+    doc = doc.replace(new RegExp("^" +indent, 'gm'), '')
     return doc if options.herecomment
     doc.replace(MULTILINER, "\\n")
-       .replace(new RegExp(options.quote, 'g'), "\\$options.quote")
#

Tag a half assignment.

  tagHalfAssignment: (tag) ->
-    tag:  '?' if tag is 'OP?'
-    last: @tokens.pop()
-    @tokens.push ["$tag=", "$tag=", last[2]]
-    true
#

A source of ambiguity in our grammar used to be parameter lists in function + .replace(new RegExp(options.quote, 'g'), "\\#options.quote")

#

A source of ambiguity in our grammar used to be parameter lists in function definitions versus argument lists in function calls. Walk backwards, tagging parameters specially in order to make things easier for the parser.

  tagParameters: ->
     return if @tag() isnt ')'
-    i: 0
+    i = 0
     loop
-      i: + 1
-      tok: @prev i
+      i += 1
+      tok = @prev i
       return if not tok
       switch tok[0]
-        when 'IDENTIFIER'       then tok[0]: 'PARAM'
-        when ')'                then tok[0]: 'PARAM_END'
-        when '(', 'CALL_START'  then return tok[0]: 'PARAM_START'
-    true
#

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 + when 'IDENTIFIER' then tok[0] = 'PARAM' + when ')' then tok[0] = 'PARAM_END' + when '(', 'CALL_START' then return tok[0] = 'PARAM_START' + true

#

Close up all remaining open blocks at the end of the file.

  closeIndentation: ->
+    @outdentToken @indent
#

The error for when you try to use a forbidden word in JavaScript as an identifier.

  identifierError: (word) ->
-    throw new Error "SyntaxError: Reserved word \"$word\" on line ${@line + 1}"
#

The error for when you try to assign to a reserved word in JavaScript, + throw new Error "SyntaxError: Reserved word \"#word\" on line #{@line + 1}"

#

The error for when you try to assign to a reserved word in JavaScript, like "function" or "default".

  assignmentError: ->
-    throw new Error "SyntaxError: Reserved word \"${@value()}\" on line ${@line + 1} can't be assigned"
#

Matches a balanced group such as a single or double-quoted string. Pass in + throw new Error "SyntaxError: Reserved word \"#{@value()}\" on line #{@line + 1} can't be assigned"

#

Matches a balanced group such as a single or double-quoted string. Pass in a series of delimiters, all of which must be nested correctly within the contents of the string. This method allows us to have strings within interpolations within strings, ad infinitum.

  balancedString: (str, delimited, options) ->
-    options: or {}
-    slash: delimited[0][0] is '/'
-    levels: []
-    i: 0
+    options or= {}
+    slash = delimited[0][0] is '/'
+    levels = []
+    i = 0
     while i < str.length
       if levels.length and starts str, '\\', i
-        i: + 1
+        i += 1
       else
         for pair in delimited
-          [open, close]: pair
+          [open, close] = pair
           if levels.length and starts(str, close, i) and levels[levels.length - 1] is pair
             levels.pop()
-            i: + close.length - 1
-            i: + 1 unless levels.length
+            i += close.length - 1
+            i += 1 unless levels.length
             break
           else if starts str, open, i
             levels.push(pair)
-            i: + open.length - 1
+            i += open.length - 1
             break
       break if not levels.length or slash and starts str, '\n', i
-      i: + 1
+      i += 1
     if levels.length
       return false if slash
-      throw new Error "SyntaxError: Unterminated ${levels.pop()[0]} starting on line ${@line + 1}"
-    if not i then false else str.substring(0, i)
#

Expand variables and expressions inside double-quoted strings using + throw new Error "SyntaxError: Unterminated #{levels.pop()[0]} starting on line #{@line + 1}" + if not i then false else str.substring(0, i)

#

Expand variables and expressions inside double-quoted strings using ECMA Harmony's interpolation syntax for substitution of bare variables as well as arbitrary expressions.

-
"Hello $name."
-"Hello ${name.capitalize()}."
+
"Hello #name."
+"Hello #{name.capitalize()}."
 

If it encounters an interpolation, this method will recursively create a new Lexer, tokenize the interpolated contents, and merge them into the -token stream.

  interpolateString: (str, escapeQuotes) ->
+token stream.

  interpolateString: (str, options) ->
+    options or= {}
     if str.length < 3 or not starts str, '"'
       @token 'STRING', str
     else
-      lexer:    new Lexer
-      tokens:   []
-      quote:    str.substring 0, 1
-      [i, pi]:  [1, 1]
+      lexer =    new Lexer
+      tokens =   []
+      quote =    str.substring 0, 1
+      [i, pi] =  [1, 1]
       while i < str.length - 1
         if starts str, '\\', i
-          i: + 1
-        else if match: str.substring(i).match INTERPOLATION
-          [group, interp]: match
-          interp: "this.${ interp.substring(1) }" if starts interp, '@'
-          tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
+          i += 1
+        else if match = str.substring(i).match INTERPOLATION
+          [group, interp] = match
+          interp = "this.#{ interp.substring(1) }" if starts interp, '@'
+          tokens.push ['STRING', "#quote#{ str.substring(pi, i) }#quote"] if pi < i
           tokens.push ['IDENTIFIER', interp]
-          i: + group.length - 1
-          pi: i + 1
-        else if (expr: @balancedString str.substring(i), [['${', '}']])
-          tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
-          inner: expr.substring(2, expr.length - 1)
+          i += group.length - 1
+          pi = i + 1
+        else if (expr = @balancedString str.substring(i), [['#{', '}']])
+          tokens.push ['STRING', "#quote#{ str.substring(pi, i) }#quote"] if pi < i
+          inner = expr.substring(2, expr.length - 1)
           if inner.length
-            inner: inner.replace new RegExp('\\\\' + quote, 'g'), quote
-            nested: lexer.tokenize "($inner)", {line: @line}
-            (tok[0]: ')') for tok, idx in nested when tok[0] is 'CALL_END'
+            inner = inner.replace new RegExp('\\\\' + quote, 'g'), quote if options.heredoc
+            nested = lexer.tokenize "(#inner)", line: @line
+            (tok[0] = ')') for tok, idx in nested when tok[0] is 'CALL_END'
             nested.pop()
             tokens.push ['TOKENS', nested]
           else
-            tokens.push ['STRING', "$quote$quote"]
-          i: + expr.length - 1
-          pi: i + 1
-        i: + 1
-      tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i and pi < str.length - 1
+            tokens.push ['STRING', "#quote#quote"]
+          i += expr.length - 1
+          pi = i + 1
+        i += 1
+      tokens.push ['STRING', "#quote#{ str.substring(pi, i) }#quote"] if pi < i and pi < str.length - 1
       tokens.unshift ['STRING', '""'] unless tokens[0][0] is 'STRING'
-      interpolated: tokens.length > 1
+      interpolated = tokens.length > 1
       @token '(', '(' if interpolated
       for token, i in tokens
-        [tag, value]: token
+        [tag, value] = token
         if tag is 'TOKENS'
-          @tokens: @tokens.concat value
-        else if tag is 'STRING' and escapeQuotes
-          escaped: value.substring(1, value.length - 1).replace(/"/g, '\\"')
-          @token tag, "\"$escaped\""
+          @tokens = @tokens.concat value
+        else if tag is 'STRING' and options.escapeQuotes
+          escaped = value.substring(1, value.length - 1).replace(/"/g, '\\"')
+          @token tag, "\"#escaped\""
         else
           @token tag, value
         @token '+', '+' if i < tokens.length - 1
       @token ')', ')' if interpolated
-      tokens
#

Helpers

#

Add a token to the results, taking note of the line number.

  token: (tag, value) ->
-    @tokens.push [tag, value, @line]
#

Peek at a tag in the current token stream.

  tag: (index, newTag) ->
-    return unless tok: @prev index
-    return tok[0]: newTag if newTag?
-    tok[0]
#

Peek at a value in the current token stream.

  value: (index, val) ->
-    return unless tok: @prev index
-    return tok[1]: val if val?
-    tok[1]
#

Peek at a previous token, entire.

  prev: (index) ->
-    @tokens[@tokens.length - (index or 1)]
#

Attempt to match a string against the current chunk, returning the indexed + tokens

#

Helpers

#

Add a token to the results, taking note of the line number.

  token: (tag, value) ->
+    @tokens.push [tag, value, @line]
#

Peek at a tag in the current token stream.

  tag: (index, newTag) ->
+    return unless tok = @prev index
+    return tok[0] = newTag if newTag?
+    tok[0]
#

Peek at a value in the current token stream.

  value: (index, val) ->
+    return unless tok = @prev index
+    return tok[1] = val if val?
+    tok[1]
#

Peek at a previous token, entire.

  prev: (index) ->
+    @tokens[@tokens.length - (index or 1)]
#

Attempt to match a string against the current chunk, returning the indexed match if successful, and false otherwise.

  match: (regex, index) ->
-    return false unless m: @chunk.match regex
-    if m then m[index] else false
#

Are we in the midst of an unfinished expression?

  unfinished: ->
-    prev: @prev(2)
+    return false unless m = @chunk.match regex
+    if m then m[index] else false
#

Are we in the midst of an unfinished expression?

  unfinished: ->
+    prev = @prev(2)
     @value() and @value().match and @value().match(NO_NEWLINE) and
-      prev and (prev[0] isnt '.') and not @value().match(CODE)
#

Constants

#

Keywords that CoffeeScript shares in common with JavaScript.

JS_KEYWORDS: [
+      prev and (prev[0] isnt '.') and not @value().match(CODE) and
+      not @chunk.match ASSIGNED
#

Constants

#

Keywords that CoffeeScript shares in common with JavaScript.

JS_KEYWORDS = [
   "if", "else",
   "true", "false",
   "new", "return",
@@ -368,55 +377,54 @@ match if successful, and false otherwise.

#

CoffeeScript-only keywords, which we're more relaxed about allowing. They can't -be used standalone, but you can reference them as an attached property.

COFFEE_ALIASES:  ["and", "or", "is", "isnt", "not"]
-COFFEE_KEYWORDS: COFFEE_ALIASES.concat [
+]
#

CoffeeScript-only keywords, which we're more relaxed about allowing. They can't +be used standalone, but you can reference them as an attached property.

COFFEE_ALIASES =  ["and", "or", "is", "isnt", "not"]
+COFFEE_KEYWORDS = COFFEE_ALIASES.concat [
   "then", "unless", "until", "loop",
   "yes", "no", "on", "off",
   "of", "by", "where", "when"
-]
#

The list of keywords that are reserved by JavaScript, but not used, or are +]

#

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", "do", "function", "var", "void", "with"
-  "const", "let", "enum", "export", "import", "native"
-]
#

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
#

Token matching regexes.

IDENTIFIER    : /^([a-zA-Z\$_](\w|\$)*)/
-NUMBER        : /^(((\b0(x|X)[0-9a-fA-F]+)|((\b[0-9]+(\.[0-9]+)?|\.[0-9]+)(e[+\-]?[0-9]+)?)))\b/i
-HEREDOC       : /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\n?([ \t]*)'{3})/
-INTERPOLATION : /^\$([a-zA-Z_@]\w*(\.\w+)*)/
-OPERATOR      : /^(-[\-=>]?|\+[+=]?|[*&|\/%=<>:!?]+)([ \t]*)/
-WHITESPACE    : /^([ \t]+)/
-COMMENT       : /^(\s*#{3}(?!#)[ \t]*\n+([\s\S]*?)[ \t]*\n+[ \t]*#{3}|(\s*#(?!##[^#])[^\n]*)+)/
-CODE          : /^((-|=)>)/
-MULTI_DENT    : /^((\n([ \t]*))+)(\.)?/
-LAST_DENTS    : /\n([ \t]*)/g
-LAST_DENT     : /\n([ \t]*)/
-ASSIGNMENT    : /^[:=]$/
#

Regex-matching-regexes.

REGEX_START        : /^\/[^\/ ]/
-REGEX_INTERPOLATION: /([^\\]\$[a-zA-Z_@]|[^\\]\$\{.*[^\\]\})/
-REGEX_END          : /^(([imgy]{1,4})\b|\W|$)/
-REGEX_ESCAPE       : /\\[^\$]/g
#

Token cleaning regexes.

JS_CLEANER      : /(^`|`$)/g
-MULTILINER      : /\n/g
-STRING_NEWLINES : /\n[ \t]*/g
-NO_NEWLINE      : /^([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/
-HEREDOC_INDENT  : /(\n+([ \t]*)|^([ \t]+))/g
-ASSIGNED        : /^([a-zA-Z\$_]\w*[ \t]*?[:=][^=])/
-NEXT_CHARACTER  : /^\s*(\S)/
#

Tokens which a regular expression will never immediately follow, but which +to avoid having a JavaScript error at runtime.

RESERVED = [
+  "case", "default", "do", "function", "var", "void", "with",
+  "const", "let", "enum", "export", "import", "native",
+  "__hasProp", "__extends", "__slice"
+]
#

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
#

Token matching regexes.

IDENTIFIER    = /^([a-zA-Z\$_](\w|\$)*)/
+NUMBER        = /^(((\b0(x|X)[0-9a-fA-F]+)|((\b[0-9]+(\.[0-9]+)?|\.[0-9]+)(e[+\-]?[0-9]+)?)))\b/i
+HEREDOC       = /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\n?([ \t]*)'{3})/
+INTERPOLATION = /^#([a-zA-Z_@]\w*(\.\w+)*)/
+OPERATOR      = /^(-[\-=>]?|\+[+=]?|[*&|\/%=<>:!?]+)([ \t]*)/
+WHITESPACE    = /^([ \t]+)/
+COMMENT       = /^(\s*\#{3}(?!#)[ \t]*\n+([\s\S]*?)[ \t]*\n+[ \t]*\#{3}|(\s*#(?!##[^#])[^\n]*)+)/
+CODE          = /^((-|=)>)/
+MULTI_DENT    = /^((\n([ \t]*))+)(\.)?/
+LAST_DENTS    = /\n([ \t]*)/g
+LAST_DENT     = /\n([ \t]*)/
#

Regex-matching-regexes.

REGEX_START        = /^\/[^\/ ]/
+REGEX_INTERPOLATION= /([^\\]#[a-zA-Z_@]|[^\\]#\{.*[^\\]\})/
+REGEX_END          = /^(([imgy]{1,4})\b|\W|$)/
+REGEX_ESCAPE       = /\\[^\$]/g
#

Token cleaning regexes.

JS_CLEANER      = /(^`|`$)/g
+MULTILINER      = /\n/g
+STRING_NEWLINES = /\n[ \t]*/g
+NO_NEWLINE      = /^([+\*&|\/\-%=<>!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/
+HEREDOC_INDENT  = /(\n+([ \t]*)|^([ \t]+))/g
+ASSIGNED        = /^\s*([a-zA-Z\$_@]\w*[ \t]*?[:=][^=])/
+NEXT_CHARACTER  = /^\s*(\S)/
#

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

-

Our list is shorter, due to sans-parentheses method calls.

NOT_REGEX: [
-  'NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE', ']'
-]
#

Tokens which could legitimately be invoked or indexed. A opening +

Our list is shorter, due to sans-parentheses method calls.

NOT_REGEX = ['NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE', ']']
#

Tokens which could legitimately be invoked or indexed. A opening parentheses or bracket following these tokens will be recorded as the start -of a function invocation or indexing operation.

CALLABLE: ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::']
#

Tokens that, when immediately preceding a WHEN, indicate that the WHEN +of a function invocation or indexing operation.

CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::']
#

Tokens that, when immediately preceding a WHEN, indicate that the WHEN occurs at the start of a line. We disambiguate these from trailing whens to -avoid an ambiguity in the grammar.

LINE_BREAK: ['INDENT', 'OUTDENT', 'TERMINATOR']
#

Half-assignments...

HALF_ASSIGNMENTS: ['-', '+', '/', '*', '%', '||', '&&', '?', 'OP?']
#

Conversions from CoffeeScript operators into JavaScript ones.

CONVERSIONS: {
+avoid an ambiguity in the grammar.

LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']
#

Conversions from CoffeeScript operators into JavaScript ones.

CONVERSIONS = {
   'and':  '&&'
   'or':   '||'
   'is':   '=='
   'isnt': '!='
   'not':  '!'
+  '===':  '=='
 }
 
 
\ No newline at end of file diff --git a/documentation/docs/nodes.html b/documentation/docs/nodes.html index 167f6a6e..b3299710 100644 --- a/documentation/docs/nodes.html +++ b/documentation/docs/nodes.html @@ -3,12 +3,12 @@ nodes are created as the result of actions in the grammar but some are created by other nodes as a method of code generation. To convert the syntax tree into a string of JavaScript code, call compile() on the root.

#

Set up for both Node.js and the browser, by including the Scope class and the helper functions.

if process?
-  Scope:   require('./scope').Scope
-  helpers: require('./helpers').helpers
+  Scope =   require('./scope').Scope
+  helpers = require('./helpers').helpers
 else
-  this.exports: this
-  helpers:      this.helpers
-  Scope:        this.Scope
#

Import the helpers we plan to use.

{compact, flatten, merge, del, include, indexOf, starts}: helpers
#

BaseNode

#

The BaseNode is the abstract base class for all nodes in the syntax tree. + this.exports = this + helpers = this.helpers + Scope = this.Scope

#

Import the helpers we plan to use.

{compact, flatten, merge, del, include, indexOf, starts, ends} = helpers
#

BaseNode

#

The BaseNode is the abstract base class for all nodes in the syntax tree. Each subclass implements the compileNode method, which performs the code generation for that node. To compile a node to JavaScript, call compile on it, which wraps compileNode in some generic extra smarts, @@ -16,7 +16,7 @@ to know when the generated code needs to be wrapped up in a closure. An options hash is passed and cloned throughout, containing information about the environment from higher in the tree (such as if a returned value is being requested by the surrounding function), information about the current -scope, and indentation level.

exports.BaseNode: class BaseNode
#

Common logic for determining whether to wrap this node in a closure before +scope, and indentation level.

exports.BaseNode = class BaseNode
#

Common logic for determining whether to wrap this node in a closure before compiling it, or to compile directly. We need to wrap if this node is a statement, and it's not a pureStatement, and we're not at the top level of a block (which would be unnecessary), and we haven't @@ -26,34 +26,37 @@ return results).

If a Node is topSensitive, that means that it needs to compile differently depending on whether it's being used as part of a larger expression, or is a top-level statement within the function body.

  compile: (o) ->
-    @options: merge o or {}
-    @tab:     o.indent
+    @options = merge o or {}
+    @tab     = o.indent
     unless this instanceof ValueNode or this instanceof CallNode
       del @options, 'operation'
       del @options, 'chainRoot' unless this instanceof AccessorNode or this instanceof IndexNode
-    top:      if @topSensitive() then @options.top else del @options, 'top'
-    closure:  @isStatement() and not @isPureStatement() and not top and
+    top     = if @topSensitive() then @options.top else del @options, 'top'
+    closure = @isStatement() and not @isPureStatement() and not top and
               not @options.asStatement and not (this instanceof CommentNode) and
               not @containsPureStatement()
     if closure then @compileClosure(@options) else @compileNode(@options)
#

Statements converted into expressions via closure-wrapping share a scope object with their parent closure, to preserve the expected lexical scope.

  compileClosure: (o) ->
-    @tab: o.indent
-    o.sharedScope: o.scope
+    @tab = o.indent
+    o.sharedScope = o.scope
     ClosureNode.wrap(this).compile 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.

  compileReference: (o, options) ->
-    pair: if not (this instanceof CallNode or this instanceof ValueNode and
-        (not (@base instanceof LiteralNode) or @hasProperties()))
+    options or= {}
+    pair = if not ((this instanceof CallNode or @contains((n) -> n instanceof CallNode)) or
+                  (this instanceof ValueNode and (not (@base instanceof LiteralNode) or @hasProperties())))
       [this, this]
+    else if this instanceof ValueNode and options.assignment
+      this.cacheIndexes(o)
     else
-      reference: literal o.scope.freeVariable()
-      compiled:  new AssignNode reference, this
+      reference = literal o.scope.freeVariable()
+      compiled  = new AssignNode reference, this
       [compiled, reference]
-    return pair unless options and options.precompile
-    [pair[0].compile(o), pair[1].compile(o)]
#

Convenience method to grab the current indentation level, plus tabbing in.

  idt: (tabs) ->
-    idt: @tab or ''
-    num: (tabs or 0) + 1
-    idt: + TAB while num: - 1
+    return [pair[0].compile(o), pair[1].compile(o)] if options.precompile
+    pair
#

Convenience method to grab the current indentation level, plus tabbing in.

  idt: (tabs) ->
+    idt = @tab or ''
+    num = (tabs or 0) + 1
+    idt += TAB while num -= 1
     idt
#

Construct a node that returns the current node's result. Note that this is overridden for smarter behavior for many statement nodes (eg IfNode, ForNode)...

  makeReturn: ->
@@ -61,18 +64,18 @@ many statement nodes (eg IfNode, ForNode)...

  contains: (block) ->
-    contains: false
+    contains = false
     @traverseChildren false, (node) ->
       if block(node)
-        contains: true
+        contains = true
         return false
     contains
#

Is this node of a certain type, or does it contain the type?

  containsType: (type) ->
     this instanceof type or @contains (n) -> n instanceof type
#

Convenience for the most common use of contains. Does the node contain a pure statement?

  containsPureStatement: ->
-    @isPureStatement() or @contains (n) -> n.isPureStatement()
#

Perform an in-order traversal of the AST. Crosses scope boundaries.

  traverse: (block) -> @traverseChildren true, block
#

toString representation of the node, for inspecting the parse tree. + @isPureStatement() or @contains (n) -> n.isPureStatement and n.isPureStatement()

#

Perform an in-order traversal of the AST. Crosses scope boundaries.

  traverse: (block) -> @traverseChildren true, block
#

toString representation of the node, for inspecting the parse tree. This is what coffee --nodes prints out.

  toString: (idt, override) ->
-    idt: or ''
-    children: (child.toString idt + TAB for child in @collectChildren()).join('')
+    idt or= ''
+    children = (child.toString idt + TAB for child in @collectChildren()).join('')
     '\n' + idt + (override or @class) + children
 
   eachChild: (func) ->
@@ -82,7 +85,7 @@ This is what coffee --nodes prints out.

#

Default implementations of the common node properties and methods. Nodes -will override these with custom logic, if needed.

  class:     'BaseNode'
+will override these with custom logic, if needed.

  class:    'BaseNode'
   children: []
 
-  unwrap:           -> this
-  isStatement:      -> no
-  isPureStatement:  -> no
-  topSensitive:     -> no
#

Expressions

#

The expressions body is the list of expressions that forms the body of an + unwrap : -> this + isStatement : -> no + isPureStatement : -> no + topSensitive : -> no

#

Expressions

#

The expressions body is the list of expressions that forms the body of an indented block of code -- the implementation of a function, a clause in an -if, switch, or try, and so on...

exports.Expressions: class Expressions extends BaseNode
+if, switch, or try, and so on...

exports.Expressions = class Expressions extends BaseNode
 
-  class:         'Expressions'
+  class:        'Expressions'
   children:     ['expressions']
-  isStatement: -> yes
+  isStatement:  -> yes
 
   constructor: (nodes) ->
-    @expressions: compact flatten nodes or []
#

Tack an expression on to the end of this expression list.

  push: (node) ->
+    @expressions = compact flatten nodes or []
#

Tack an expression on to the end of this expression list.

  push: (node) ->
     @expressions.push(node)
     this
#

Add an expression at the beginning of this expression list.

  unshift: (node) ->
     @expressions.unshift(node)
@@ -114,13 +117,13 @@ it back out.

< if @expressions.length is 1 then @expressions[0] else this
#

Is this an empty block of code?

  empty: ->
     @expressions.length is 0
#

An Expressions node does not return its entire body, rather it ensures that the final expression is returned.

  makeReturn: ->
-    idx:  @expressions.length - 1
-    last: @expressions[idx]
-    last: @expressions[idx: - 1] if last instanceof CommentNode
+    idx  = @expressions.length - 1
+    last = @expressions[idx]
+    last = @expressions[idx -= 1] if last instanceof CommentNode
     return this if not last or last instanceof ReturnNode
-    @expressions[idx]: last.makeReturn()
+    @expressions[idx] = last.makeReturn()
     this
#

An Expressions is the only node that can serve as the root.

  compile: (o) ->
-    o: or {}
+    o or= {}
     if o.scope then super(o) else @compileRoot(o)
 
   compileNode: (o) ->
@@ -128,72 +131,72 @@ ensures that the final expression is returned.

  compileRoot: (o) ->
-    o.indent: @tab: if o.noWrap then '' else TAB
-    o.scope: new Scope(null, this, null)
-    code: @compileWithDeclarations(o)
-    code: code.replace(TRAILING_WHITESPACE, '')
-    code: code.replace(DOUBLE_PARENS, '($1)')
-    if o.noWrap then code else "(function(){\n$code\n})();\n"
#

Compile the expressions body for the contents of a function, with + o.indent = @tab = if o.noWrap then '' else TAB + o.scope = new Scope(null, this, null) + code = @compileWithDeclarations(o) + code = code.replace(TRAILING_WHITESPACE, '') + code = code.replace(DOUBLE_PARENS, '($1)') + if o.noWrap then code else "(function() {\n#code\n})();\n"

#

Compile the expressions body for the contents of a function, with declarations of all inner variables pushed up to the top.

  compileWithDeclarations: (o) ->
-    code: @compileNode(o)
-    code: "${@tab}var ${o.scope.compiledAssignments()};\n$code"  if o.scope.hasAssignments(this)
-    code: "${@tab}var ${o.scope.compiledDeclarations()};\n$code" if not o.globals and o.scope.hasDeclarations(this)
+    code = @compileNode(o)
+    code = "#{@tab}var #{o.scope.compiledAssignments()};\n#code"  if o.scope.hasAssignments(this)
+    code = "#{@tab}var #{o.scope.compiledDeclarations()};\n#code" if not o.globals and o.scope.hasDeclarations(this)
     code
#

Compiles a single expression within the expressions 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.

  compileExpression: (node, o) ->
-    @tab: o.indent
-    compiledNode: node.compile merge o, {top: true}
-    if node.isStatement() then compiledNode else "${@idt()}$compiledNode;"
#

Wrap up the given nodes as an Expressions, unless it already happens -to be one.

Expressions.wrap: (nodes) ->
+    @tab = o.indent
+    compiledNode = node.compile merge o, top: true
+    if node.isStatement() then compiledNode else "#{@idt()}#compiledNode;"
#

Wrap up the given nodes as an Expressions, unless it already happens +to be one.

Expressions.wrap = (nodes) ->
   return nodes[0] if nodes.length is 1 and nodes[0] instanceof Expressions
   new Expressions(nodes)
#

LiteralNode

#

Literals are static values that can be passed through directly into JavaScript without translation, such as: strings, numbers, -true, false, null...

exports.LiteralNode: class LiteralNode extends BaseNode
+true, false, null...

exports.LiteralNode = class LiteralNode extends BaseNode
 
   class: 'LiteralNode'
 
-  constructor: (value) ->
-    @value: value
#

Break and continue must be treated as pure statements -- they lose their + constructor: (@value) -> + + makeReturn: -> + if @isStatement() then this else super()

#

Break and continue must be treated as pure statements -- they lose their meaning when wrapped in a closure.

  isStatement: ->
     @value is 'break' or @value is 'continue'
   isPureStatement: LiteralNode::isStatement
 
   compileNode: (o) ->
-    idt: if @isStatement() then @idt() else ''
-    end: if @isStatement() then ';' else ''
-    "$idt$@value$end"
+    idt = if @isStatement() then @idt() else ''
+    end = if @isStatement() then ';' else ''
+    "#idt#@value#end"
 
   toString: (idt) ->
-    " \"$@value\""
#

ReturnNode

#

A return is a pureStatement -- wrapping it in a closure wouldn't -make sense.

exports.ReturnNode: class ReturnNode extends BaseNode
+    " \"#@value\""
#

ReturnNode

#

A return is a pureStatement -- wrapping it in a closure wouldn't +make sense.

exports.ReturnNode = class ReturnNode extends BaseNode
 
-  class:               'ReturnNode'
-  isStatement:       -> yes
+  class:            'ReturnNode'
+  isStatement:      -> yes
   isPureStatement:  -> yes
-  children:           ['expression']
+  children:         ['expression']
 
-  constructor: (expression) ->
-    @expression: expression
+  constructor: (@expression) ->
 
   makeReturn: ->
     this
 
   compile: (o) ->
-    expr: @expression.makeReturn()
+    expr = @expression.makeReturn()
     return expr.compile o unless expr instanceof ReturnNode
     super o
 
   compileNode: (o) ->
-    o.asStatement: true if @expression.isStatement()
-    "${@tab}return ${@expression.compile(o)};"
#

ValueNode

#

A value, variable or literal or parenthesized, indexed or dotted into, -or vanilla.

exports.ValueNode: class ValueNode extends BaseNode
+    o.asStatement = true if @expression.isStatement()
+    "#{@tab}return #{@expression.compile(o)};"
#

ValueNode

#

A value, variable or literal or parenthesized, indexed or dotted into, +or vanilla.

exports.ValueNode = class ValueNode extends BaseNode
 
   SOAK:     " == undefined ? undefined : "
 
   class:     'ValueNode'
-  children: ['base', 'properties']
#

A ValueNode has a base and a list of property accesses.

  constructor: (base, properties) ->
-    @base: base
-    @properties: (properties or [])
#

Add a property access to the list.

  push: (prop) ->
+  children: ['base', 'properties']
#

A ValueNode has a base and a list of property accesses.

  constructor: (@base, @properties) ->
+    @properties or= []
#

Add a property access to the list.

  push: (prop) ->
     @properties.push(prop)
     this
 
@@ -216,298 +219,326 @@ properties.

isNumber: -> @base instanceof LiteralNode and @base.value.match NUMBER
#

Works out if the value is the start of a chain.

  isStart: (o) ->
     return true if this is o.chainRoot and @properties[0] instanceof AccessorNode
-    node: o.chainRoot.base or o.chainRoot.variable
-    while node instanceof CallNode then node: node.variable
-    node is this
#

Override compile to unwrap the value when possible.

  compile: (o) ->
-    if not o.top or @properties.length then super(o) else @base.compile(o)
#

We compile a value to JavaScript by compiling and joining each property. + node = o.chainRoot.base or o.chainRoot.variable + while node instanceof CallNode then node = node.variable + node is this

#

If the value node has indexes containing function calls, and the value node +needs to be used twice, in compound assignment ... then we need to cache +the value of the indexes.

  cacheIndexes: (o) ->
+    copy = new ValueNode @base, @properties.slice 0
+    for prop, i in copy.properties
+      if prop instanceof IndexNode and prop.contains((n) -> n instanceof CallNode)
+        [index, indexVar] = prop.index.compileReference o
+        this.properties[i] = new IndexNode index
+        copy.properties[i] = new IndexNode indexVar
+    [this, copy]
#

Override compile to unwrap the value when possible.

  compile: (o) ->
+    if not o.top or @properties.length then super(o) else @base.compile(o)
#

We compile a value to JavaScript by compiling and joining each property. Things get much more insteresting if the chain of properties has soak operators ?. interspersed. Then we have to take care not to accidentally evaluate a anything twice when building the soak chain.

  compileNode: (o) ->
-    only:         del o, 'onlyFirst'
-    op:           del o, 'operation'
-    props:        if only then @properties[0...@properties.length - 1] else @properties
-    o.chainRoot:  or this
-    baseline:     @base.compile o
-    baseline:     "($baseline)" if @hasProperties() and (@base instanceof ObjectNode or @isNumber())
-    complete:     @last: baseline
+    only        = del o, 'onlyFirst'
+    op          = del o, 'operation'
+    props       = if only then @properties[0...@properties.length - 1] else @properties
+    o.chainRoot or= this
+    baseline    = @base.compile o
+    baseline    = "(#baseline)" if @hasProperties() and (@base instanceof ObjectNode or @isNumber())
+    complete    = @last = baseline
 
     for prop, i in props
-      @source: baseline
+      @source = baseline
       if prop.soakNode
-        if @base instanceof CallNode and i is 0
-          temp: o.scope.freeVariable()
-          complete: "(${ baseline: temp } = ($complete))"
-        complete: "typeof $complete === \"undefined\" || $baseline" if i is 0 and @isStart(o)
-        complete: + @SOAK + (baseline: + prop.compile(o))
+        if @base instanceof CallNode or @base.contains((n) -> n instanceof CallNode) and i is 0
+          temp = o.scope.freeVariable()
+          complete = "(#{ baseline = temp } = (#complete))"
+        complete = "typeof #complete === \"undefined\" || #baseline" if i is 0 and @isStart(o)
+        complete += @SOAK + (baseline += prop.compile(o))
       else
-        part: prop.compile(o)
-        baseline: + part
-        complete: + part
-        @last: part
+        part = prop.compile(o)
+        baseline += part
+        complete += part
+        @last = part
 
-    if op and @wrapped then "($complete)" else complete
#

CommentNode

#

CoffeeScript passes through block comments as JavaScript block comments -at the same position.

exports.CommentNode: class CommentNode extends BaseNode
+    if op and @wrapped then "(#complete)" else complete
#

CommentNode

#

CoffeeScript passes through block comments as JavaScript block comments +at the same position.

exports.CommentNode = class CommentNode extends BaseNode
 
   class: 'CommentNode'
   isStatement: -> yes
 
-  constructor: (lines) ->
-    @lines: lines
+  constructor: (@lines) ->
 
   makeReturn: ->
     this
 
   compileNode: (o) ->
-    sep: "\n$@tab"
-    "$@tab/*$sep${ @lines.join(sep) }\n$@tab*/"
#

CallNode

#

Node for a function invocation. Takes care of converting super() calls into -calls against the prototype's function of the same name.

exports.CallNode: class CallNode extends BaseNode
+    sep = "\n#@tab"
+    "#@tab/*#sep#{ @lines.join(sep) }\n#@tab*/"
#

CallNode

#

Node for a function invocation. Takes care of converting super() calls into +calls against the prototype's function of the same name.

exports.CallNode = class CallNode extends BaseNode
 
   class:     'CallNode'
   children: ['variable', 'args']
 
-  constructor: (variable, args) ->
-    @isNew:     false
-    @isSuper:   variable is 'super'
-    @variable:  if @isSuper then null else variable
-    @args:      (args or [])
-    @compileSplatArguments: (o) ->
-      SplatNode.compileMixedArray.call(this, @args, o)
#

Tag this invocation as creating a new instance.

  newInstance: ->
-    @isNew: true
+  constructor: (variable, @args) ->
+    @isNew    = false
+    @isSuper  = variable is 'super'
+    @variable = if @isSuper then null else variable
+    @args     or= []
+    @compileSplatArguments = (o) ->
+      SplatNode.compileSplattedArray.call(this, @args, o)
#

Tag this invocation as creating a new instance.

  newInstance: ->
+    @isNew = true
     this
 
   prefix: ->
-    if @isNew then 'new ' else ''
#

Grab the reference to the superclass' implementation of the current method.

  superReference: (o) ->
-    methname: o.scope.method.name
-    meth: if o.scope.method.proto
-      "${o.scope.method.proto}.__superClass__.$methname"
+    if @isNew then 'new ' else ''
#

Grab the reference to the superclass' implementation of the current method.

  superReference: (o) ->
+    methname = o.scope.method.name
+    meth = if o.scope.method.proto
+      "#{o.scope.method.proto}.__superClass__.#methname"
     else if methname
-      "${methname}.__superClass__.constructor"
-    else throw new Error "cannot call super on an anonymous function."
#

Compile a vanilla function call.

  compileNode: (o) ->
-    o.chainRoot: this unless o.chainRoot
+      "#{methname}.__superClass__.constructor"
+    else throw new Error "cannot call super on an anonymous function."
#

Compile a vanilla function call.

  compileNode: (o) ->
+    o.chainRoot = this unless o.chainRoot
     for arg in @args when arg instanceof SplatNode
-      compilation: @compileSplat(o)
+      compilation = @compileSplat(o)
     unless compilation
-      args: (arg.compile(o) for arg in @args).join(', ')
-      compilation: if @isSuper then @compileSuper(args, o)
-      else "${@prefix()}${@variable.compile(o)}($args)"
-    if o.operation and @wrapped then "($compilation)" else compilation
#

super() is converted into a call against the superclass's implementation + args = (arg.compile(o) for arg in @args).join(', ') + compilation = if @isSuper then @compileSuper(args, o) + else "#{@prefix()}#{@variable.compile(o)}(#args)" + if o.operation and @wrapped then "(#compilation)" else compilation

#

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 -.apply() call to allow an array of arguments to be passed.

  compileSplat: (o) ->
-    meth: if @variable then @variable.compile(o) else @superReference(o)
-    obj:  @variable and @variable.source or 'this'
+    "#{@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) ->
+    meth = if @variable then @variable.compile(o) else @superReference(o)
+    obj =  @variable and @variable.source or 'this'
     if obj.match(/\(/)
-      temp: o.scope.freeVariable()
-      obj:  temp
-      meth: "($temp = ${ @variable.source })${ @variable.last }"
-    "${@prefix()}${meth}.apply($obj, ${ @compileSplatArguments(o) })"
#

ExtendsNode

#

Node to extend an object's prototype with an ancestor object. -After goog.inherits from the -Closure Library.

exports.ExtendsNode: class ExtendsNode extends BaseNode
+      temp = o.scope.freeVariable()
+      obj =  temp
+      meth = "(#temp = #{ @variable.source })#{ @variable.last }"
+    if @isNew
+      utility 'extends'
+      """
+      (function() {
+
+#DIVIDER
+      """
+    else
+      "#{@prefix()}#{meth}.apply(#obj, #{ @compileSplatArguments(o) })"
#

{@idt(1)}var ctor = function(){}; +{@idt(1)}__extends(ctor, #meth); +{@idt(1)}return #{meth}.apply(new ctor, #{ @compileSplatArguments(o) }); +@tab}).call(this)

#

ExtendsNode

exports.ExtendsNode = class ExtendsNode extends BaseNode
 
   class:     'ExtendsNode'
   children: ['child', 'parent']
 
-  constructor: (child, parent) ->
-    @child: child
-    @parent: parent
#

Hooks one constructor into another's prototype chain.

  compileNode: (o) ->
-    ref:  new ValueNode literal utility 'extends'
-    (new CallNode ref, [@child, @parent]).compile o
#

AccessorNode

#

A . accessor into a property of a value, or the :: shorthand for -an accessor into the object's prototype.

exports.AccessorNode: class AccessorNode extends BaseNode
+  constructor: (@child, @parent) ->
#

Node to extend an object's prototype with an ancestor object. +After goog.inherits from the +Closure Library.

  compileNode: (o) ->
+    ref =  new ValueNode literal utility 'extends'
+    (new CallNode ref, [@child, @parent]).compile o
#

Hooks one constructor into another's prototype chain.

#

AccessorNode

exports.AccessorNode = class AccessorNode extends BaseNode
 
   class:     'AccessorNode'
   children: ['name']
 
-  constructor: (name, tag) ->
-    @name: name
-    @prototype: if tag is 'prototype' then '.prototype' else ''
-    @soakNode: tag is 'soak'
+  constructor: (@name, tag) ->
+    @prototype = if tag is 'prototype' then '.prototype' else ''
+    @soakNode = tag is 'soak'
 
   compileNode: (o) ->
-    name: @name.compile o
-    o.chainRoot.wrapped: or @soakNode
-    namePart: if name.match(IS_STRING) then "[$name]" else ".$name"
-    @prototype + namePart
#

IndexNode

#

A [ ... ] indexed accessor into an array or object.

exports.IndexNode: class IndexNode extends BaseNode
+    name = @name.compile o
+    o.chainRoot.wrapped or= @soakNode
+    namePart = if name.match(IS_STRING) then "[#name]" else ".#name"
+    @prototype + namePart
#

A . accessor into a property of a value, or the :: shorthand for +an accessor into the object's prototype.

#

IndexNode

exports.IndexNode = class IndexNode extends BaseNode
 
   class:     'IndexNode'
   children: ['index']
 
-  constructor: (index) ->
-    @index:     index
+  constructor: (@index) ->
 
   compileNode: (o) ->
-    o.chainRoot.wrapped: or @soakNode
-    idx: @index.compile o
-    prefix: if @proto then '.prototype' else ''
-    "$prefix[$idx]"
#

RangeNode

#

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.RangeNode: class RangeNode extends BaseNode
+    o.chainRoot.wrapped or= @soakNode
+    idx = @index.compile o
+    prefix = if @proto then '.prototype' else ''
+    "#prefix[#idx]"
#

A [ ... ] indexed accessor into an array or object.

#

RangeNode

exports.RangeNode = class RangeNode extends BaseNode
 
   class:     'RangeNode'
   children: ['from', 'to']
 
-  constructor: (from, to, exclusive) ->
-    @from: from
-    @to: to
-    @exclusive: !!exclusive
#

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) ->
-    [@from, @fromVar]: @from.compileReference o
-    [@to, @toVar]:     @to.compileReference o
-    parts: []
-    parts.push @from.compile o if @from isnt @fromVar
-    parts.push @to.compile o if @to isnt @toVar
-    if parts.length then "${parts.join('; ')};" else ''
#

When compiled normally, the range returns the contents of the for loop -needed to iterate over the values in the range. Used by comprehensions.

  compileNode: (o) ->
-    return    @compileArray(o) unless o.index
-    idx:      del o, 'index'
-    step:     del o, 'step'
-    vars:     "$idx = ${@fromVar.compile(o)}"
-    step:     if step then step.compile(o) else '1'
-    equals:   if @exclusive then '' else '='
-    op:       if starts(step, '-') then ">$equals" else "<$equals"
-    "$vars; ${idx} $op ${@toVar.compile(o)}; $idx += $step"
#

When used as a value, expand the range into the equivalent array.

  compileArray: (o) ->
-    idt:    @idt 1
-    vars:   @compileVariables(merge(o, {indent: idt}))
-    equals: if @exclusive then '' else '='
-    from:   @fromVar.compile o
-    to:     @toVar.compile o
-    result: o.scope.freeVariable()
-    i:      o.scope.freeVariable()
-    clause: "$from <= $to ?"
-    pre:    "\n${idt}${result} = []; ${vars}"
-    body:   "var $i = $from; $clause $i <$equals $to : $i >$equals $to; $clause $i += 1 : $i -= 1"
-    post:   "{ ${result}.push($i) };\n${idt}return $result;\n$o.indent"
-    "(function(){${pre}\n${idt}for ($body)$post}).call(this)"
#

SliceNode

#

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.SliceNode: class SliceNode extends BaseNode
+  constructor: (@from, @to, exclusive) ->
+    @exclusive = !!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) ->
+    o = merge(o, top: true)
+    [@from, @fromVar] =  @from.compileReference o, precompile: yes
+    [@to, @toVar] =      @to.compileReference o, precompile: yes
+    [@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
+    parts = []
+    parts.push @from if @from isnt @fromVar
+    parts.push @to if @to isnt @toVar
+    if parts.length then "#{parts.join('; ')}; " 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.

  compileNode: (o) ->
+    return    @compileArray(o)  unless o.index
+    return    @compileSimple(o) if @fromNum and @toNum
+    idx      = del o, 'index'
+    step     = del o, 'step'
+    vars     = "#idx = #@fromVar"
+    intro    = "(#@fromVar <= #@toVar ? #idx"
+    compare  = "#intro <#@equals #@toVar : #idx >#@equals #@toVar)"
+    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) ->
+    [from, to] = [parseInt(@fromNum, 10), parseInt(@toNum, 10)]
+    idx        = del o, 'index'
+    step       = del o, 'step'
+    step       and= "#idx += #{step.compile(o)}"
+    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) ->
+    idt    = @idt 1
+    vars   = @compileVariables merge o, indent: idt
+    if @fromNum and @toNum and Math.abs(+@fromNum - +@toNum) <= 20
+      range = [+@fromNum..+@toNum]
+      range.pop() if @exclusive
+      return "[#{ range.join(', ') }]"
+    i = o.scope.freeVariable()
+    result = o.scope.freeVariable()
+    pre    = "\n#{idt}#{result} = []; #{vars}"
+    if @fromNum and @toNum
+      o.index = i
+      body = @compileSimple o
+    else
+      clause = "#@fromVar <= #@toVar ?"
+      body   = "var #i = #@fromVar; #clause #i <#@equals #@toVar : #i >#@equals #@toVar; #clause #i += 1 : #i -= 1"
+    post   = "{ #{result}.push(#i); }\n#{idt}return #result;\n#o.indent"
+    "(function() {#{pre}\n#{idt}for (#body)#post}).call(this)"
#

When used as a value, expand the range into the equivalent array.

#

SliceNode

exports.SliceNode = class SliceNode extends BaseNode
 
   class:     'SliceNode'
   children: ['range']
 
-  constructor: (range) ->
-    @range: range
+  constructor: (@range) ->
 
   compileNode: (o) ->
-    from:       @range.from.compile(o)
-    to:         @range.to.compile(o)
-    plusPart:  if @range.exclusive then '' else ' + 1'
-    ".slice($from, $to$plusPart)"
#

ObjectNode

#

An object literal, nothing fancy.

exports.ObjectNode: class ObjectNode extends BaseNode
+    from     = @range.from.compile(o)
+    to       = @range.to.compile(o)
+    plusPart = if @range.exclusive then '' else ' + 1'
+    ".slice(#from, #to#plusPart)"
#

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.

#

ObjectNode

exports.ObjectNode = class ObjectNode extends BaseNode
 
   class:     'ObjectNode'
   children: ['properties']
 
   constructor: (props) ->
-    @objects: @properties: props or []
+    @objects = @properties = props or []
 
   compileNode: (o) ->
-    o.indent: @idt 1
-    nonComments: prop for prop in @properties when not (prop instanceof CommentNode)
-    lastNoncom:  nonComments[nonComments.length - 1]
-    props: for prop, i in @properties
-      join:   ",\n"
-      join:   "\n" if (prop is lastNoncom) or (prop instanceof CommentNode)
-      join:   '' if i is @properties.length - 1
-      indent: if prop instanceof CommentNode then '' else @idt 1
-      prop:   new AssignNode prop, prop, 'object' unless prop instanceof AssignNode or prop instanceof CommentNode
+    o.indent = @idt 1
+    nonComments = prop for prop in @properties when not (prop instanceof CommentNode)
+    lastNoncom =  nonComments[nonComments.length - 1]
+    props = for prop, i in @properties
+      join   = ",\n"
+      join   = "\n" if (prop is lastNoncom) or (prop instanceof CommentNode)
+      join   = '' if i is @properties.length - 1
+      indent = if prop instanceof CommentNode then '' else @idt 1
+      prop   = new AssignNode prop, prop, 'object' unless prop instanceof AssignNode or prop instanceof CommentNode
       indent + prop.compile(o) + join
-    props: props.join('')
-    inner: if props then '\n' + props + '\n' + @idt() else ''
-    "{$inner}"
#

ArrayNode

#

An array literal.

exports.ArrayNode: class ArrayNode extends BaseNode
+    props = props.join('')
+    inner = if props then '\n' + props + '\n' + @idt() else ''
+    "{#inner}"
#

An object literal, nothing fancy.

#

ArrayNode

exports.ArrayNode = class ArrayNode extends BaseNode
 
   class:     'ArrayNode'
   children: ['objects']
 
-  constructor: (objects) ->
-    @objects: objects or []
-    @compileSplatLiteral: (o) ->
-      SplatNode.compileMixedArray.call(this, @objects, o)
+  constructor: (@objects) ->
+    @objects or= []
+    @compileSplatLiteral = (o) ->
+      SplatNode.compileSplattedArray.call(this, @objects, o)
 
   compileNode: (o) ->
-    o.indent: @idt 1
-    objects: []
+    o.indent = @idt 1
+    objects = []
     for obj, i in @objects
-      code: obj.compile(o)
+      code = obj.compile(o)
       if obj instanceof SplatNode
-        return @compileSplatLiteral @objects, o
+        return @compileSplatLiteral o
       else if obj instanceof CommentNode
-        objects.push "\n$code\n$o.indent"
+        objects.push "\n#code\n#o.indent"
       else if i is @objects.length - 1
         objects.push code
       else
-        objects.push "$code, "
-    objects: objects.join('')
+        objects.push "#code, "
+    objects = objects.join('')
     if indexOf(objects, '\n') >= 0
-      "[\n${@idt(1)}$objects\n$@tab]"
+      "[\n#{@idt(1)}#objects\n#@tab]"
     else
-      "[$objects]"
#

ClassNode

#

The CoffeeScript class definition.

exports.ClassNode: class ClassNode extends BaseNode
+      "[#objects]"
#

An array literal.

#

ClassNode

exports.ClassNode = class ClassNode extends BaseNode
 
   class:        'ClassNode'
   children:     ['variable', 'parent', 'properties']
-  isStatement:  -> yes
#

Initialize a ClassNode with its name, an optional superclass, and a -list of prototype property assignments.

  constructor: (variable, parent, props) ->
-    @variable: variable
-    @parent: parent
-    @properties: props or []
-    @returns:  false
+  isStatement:  -> yes
#

The CoffeeScript class definition.

  constructor: (@variable, @parent, @properties) ->
+    @properties or= []
+    @returns    = false
 
   makeReturn: ->
-    @returns: true
-    this
#

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) ->
-    extension:  @parent and new ExtendsNode(@variable, @parent)
-    props:      new Expressions
-    o.top:      true
-    me:         null
-    className:  @variable.compile o
-    constScope: null
+    @returns = true
+    this
#

Initialize a ClassNode with its name, an optional superclass, and a +list of prototype property assignments.

  compileNode: (o) ->
+    @variable  = literal o.scope.freeVariable() if @variable is '__temp__'
+    extension  = @parent and new ExtendsNode(@variable, @parent)
+    props      = new Expressions
+    o.top      = true
+    me         = null
+    className  = @variable.compile o
+    constScope = null
 
     if @parent
-      applied: new ValueNode(@parent, [new AccessorNode(literal('apply'))])
-      constructor: new CodeNode([], new Expressions([
+      applied = new ValueNode(@parent, [new AccessorNode(literal('apply'))])
+      constructor = new CodeNode([], new Expressions([
         new CallNode(applied, [literal('this'), literal('arguments')])
       ]))
     else
-      constructor: new CodeNode
+      constructor = new CodeNode
 
     for prop in @properties
-      [pvar, func]: [prop.variable, prop.value]
+      [pvar, func] = [prop.variable, prop.value]
       if pvar and pvar.base.value is 'constructor' and func instanceof CodeNode
         throw new Error "cannot define a constructor as a bound function." if func.bound
-        func.name: className
+        func.name = className
         func.body.push new ReturnNode literal 'this'
-        @variable: new ValueNode @variable
-        @variable.namespaced: include func.name, '.'
-        constructor: func
+        @variable = new ValueNode @variable
+        @variable.namespaced = include func.name, '.'
+        constructor = func
         continue
       if func instanceof CodeNode and func.bound
-        func.bound: false
-        constScope: or new Scope(o.scope, constructor.body, constructor)
-        me: or constScope.freeVariable()
-        pname: pvar.compile(o)
+        func.bound = false
+        constScope or= new Scope(o.scope, constructor.body, constructor)
+        me or= constScope.freeVariable()
+        pname = pvar.compile(o)
         constructor.body.push    new ReturnNode literal 'this' if constructor.body.empty()
-        constructor.body.unshift literal "this.${pname} = function(){ return ${className}.prototype.${pname}.apply($me, arguments); }"
+        constructor.body.unshift literal "this.#{pname} = function(){ return #{className}.prototype.#{pname}.apply(#me, arguments); }"
       if pvar
-        access: if prop.context is 'this' then pvar.base.properties[0] else new AccessorNode(pvar, 'prototype')
-        val:    new ValueNode(@variable, [access])
-        prop:   new AssignNode(val, func)
+        access = if prop.context is 'this' then pvar.base.properties[0] else new AccessorNode(pvar, 'prototype')
+        val    = new ValueNode(@variable, [access])
+        prop   = new AssignNode(val, func)
       props.push prop
 
-    constructor.body.unshift literal "$me = this" if me
-    construct: @idt() + (new AssignNode(@variable, constructor)).compile(merge o, {sharedScope: constScope}) + ';\n'
-    props:     if props.empty() then '' else props.compile(o) + '\n'
-    extension: if extension     then @idt() + extension.compile(o) + ';\n' else ''
-    returns:   if @returns      then new ReturnNode(@variable).compile(o)  else ''
-    "$construct$extension$props$returns"
#

AssignNode

#

The AssignNode is used to assign a local variable to value, or to set the -property of an object -- including within object literals.

exports.AssignNode: class AssignNode extends BaseNode
#

Matchers for detecting prototype assignments.

  PROTO_ASSIGN: /^(\S+)\.prototype/
+    constructor.body.unshift literal "#me = this" if me
+    construct = @idt() + (new AssignNode(@variable, constructor)).compile(merge o, {sharedScope: constScope}) + ';'
+    props     = if !props.empty() then '\n' + props.compile(o)                     else ''
+    extension = if extension      then '\n' + @idt() + extension.compile(o) + ';'  else ''
+    returns   = if @returns       then '\n' + new ReturnNode(@variable).compile(o) else ''
+    "#construct#extension#props#returns"
#

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.

#

AssignNode

exports.AssignNode = class AssignNode extends BaseNode
#

The AssignNode is used to assign a local variable to value, or to set the +property of an object -- including within object literals.

  PROTO_ASSIGN: /^(\S+)\.prototype/
   LEADING_DOT:  /^\.(prototype\.)?/
 
   class:     'AssignNode'
   children: ['variable', 'value']
 
-  constructor: (variable, value, context) ->
-    @variable: variable
-    @value: value
-    @context: context
+  constructor: (@variable, @value, @context) ->
 
   topSensitive: ->
     true
@@ -516,166 +547,191 @@ property of an object -- including within object literals.

#

Compile an assignment, delegating to compilePatternMatch or + @isValue() and (@variable.isArray() or @variable.isObject())

#

Matchers for detecting prototype assignments.

  compileNode: (o) ->
+    top    = del o, 'top'
+    return   @compilePatternMatch(o) if @isStatement()
+    return   @compileSplice(o) if @isValue() and @variable.isSplice()
+    stmt   = del o, 'asStatement'
+    name   = @variable.compile(o)
+    last   = if @isValue() then @variable.last.replace(@LEADING_DOT, '') else name
+    match  = name.match(@PROTO_ASSIGN)
+    proto  = match and match[1]
+    if @value instanceof CodeNode
+      @value.name =  last  if last.match(IDENTIFIER)
+      @value.proto = proto if proto
+    val = @value.compile o
+    return "#name: #val" if @context is 'object'
+    o.scope.find name unless @isValue() and (@variable.hasProperties() or @variable.namespaced)
+    val = "#name = #val"
+    return "#@tab#val;" if stmt
+    if top then val else "(#val)"
#

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) ->
-    top:    del o, 'top'
-    return  @compilePatternMatch(o) if @isStatement()
-    return  @compileSplice(o) if @isValue() and @variable.isSplice()
-    stmt:   del o, 'asStatement'
-    name:   @variable.compile(o)
-    last:   if @isValue() then @variable.last.replace(@LEADING_DOT, '') else name
-    match:  name.match(@PROTO_ASSIGN)
-    proto:  match and match[1]
-    if @value instanceof CodeNode
-      @value.name:  last  if last.match(IDENTIFIER)
-      @value.proto: proto if proto
-    val: @value.compile o
-    return "$name: $val" if @context is 'object'
-    o.scope.find name unless @isValue() and (@variable.hasProperties() or @variable.namespaced)
-    val: "$name = $val"
-    return "$@tab$val;" if stmt
-    if top then val else "($val)"
#

Brief implementation of recursive pattern matching, when assigning array or +has not been seen yet within the current scope, declare it.

  compilePatternMatch: (o) ->
+    valVar        = o.scope.freeVariable()
+    value         = if @value.isStatement() then ClosureNode.wrap(@value) else @value
+    assigns       = ["#@tab#valVar = #{ value.compile(o) };"]
+    o.top         = true
+    o.asStatement = true
+    splat         = false
+    for obj, i in @variable.base.objects
#

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) ->
-    valVar: o.scope.freeVariable()
-    value: if @value.isStatement() then ClosureNode.wrap(@value) else @value
-    assigns: ["$@tab$valVar = ${ value.compile(o) };"]
-    o.top: true
-    o.asStatement: true
-    splat: false
-    for obj, i in @variable.base.objects
#

A regular array pattern-match.

      idx: i
+for details.

      idx = i
       if @variable.isObject()
-        if obj instanceof AssignNode
#

A regular object pattern-match.

          [obj, idx]: [obj.value, obj.variable.base]
-        else
#

A shorthand {a, b, c}: val pattern-match.

          idx: obj
+        if obj instanceof AssignNode
#

A regular array pattern-match.

          [obj, idx] = [obj.value, obj.variable.base]
+        else
#

A regular object pattern-match.

          idx = obj
       if not (obj instanceof ValueNode or obj instanceof SplatNode)
         throw new Error 'pattern matching must use only identifiers on the left-hand side.'
-      isString: idx.value and idx.value.match IS_STRING
-      accessClass: if isString or @variable.isArray() then IndexNode else AccessorNode
+      isString = idx.value and idx.value.match IS_STRING
+      accessClass = if isString or @variable.isArray() then IndexNode else AccessorNode
       if obj instanceof SplatNode and not splat
-        val: literal(obj.compileValue(o, valVar,
-          (oindex: indexOf(@variable.base.objects, obj)),
-          (olength: @variable.base.objects.length) - oindex - 1))
-        splat: true
+        val = literal(obj.compileValue(o, valVar,
+          (oindex = indexOf(@variable.base.objects, obj)),
+          (olength = @variable.base.objects.length) - oindex - 1))
+        splat = true
       else
-        idx: literal(if splat then "${valVar}.length - ${olength - idx}" else idx) if typeof idx isnt 'object'
-        val: new ValueNode(literal(valVar), [new accessClass(idx)])
+        idx = literal(if splat then "#{valVar}.length - #{olength - idx}" else idx) if typeof idx isnt 'object'
+        val = new ValueNode(literal(valVar), [new accessClass(idx)])
       assigns.push(new AssignNode(obj, val).compile(o))
-    code: assigns.join("\n")
-    code
#

Compile the assignment from an array splice literal, using JavaScript's -Array#splice method.

  compileSplice: (o) ->
-    name:   @variable.compile merge o, {onlyFirst: true}
-    l:      @variable.properties.length
-    range:  @variable.properties[l - 1].range
-    plus:   if range.exclusive then '' else ' + 1'
-    from:   range.from.compile(o)
-    to:     range.to.compile(o) + ' - ' + from + plus
-    val:    @value.compile(o)
-    "${name}.splice.apply($name, [$from, $to].concat($val))"
#

CodeNode

#

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 CodeNode -has no children -- they're within the inner scope.

exports.CodeNode: class CodeNode extends BaseNode
+    code = assigns.join("\n")
+    code
#

A shorthand {a, b, c} = val pattern-match.

  compileSplice: (o) ->
+    name  = @variable.compile merge o, onlyFirst: true
+    l     = @variable.properties.length
+    range = @variable.properties[l - 1].range
+    plus  = if range.exclusive then '' else ' + 1'
+    from  = range.from.compile(o)
+    to    = range.to.compile(o) + ' - ' + from + plus
+    val   = @value.compile(o)
+    "#{name}.splice.apply(#name, [#from, #to].concat(#val))"
#

Compile the assignment from an array splice literal, using JavaScript's +Array#splice method.

#

CodeNode

exports.CodeNode = class CodeNode extends BaseNode
 
   class:     'CodeNode'
   children: ['params', 'body']
 
-  constructor: (params, body, tag) ->
-    @params:  params or []
-    @body:    body or new Expressions
-    @bound:   tag is 'boundfunc'
#

Compilation creates a new scope unless explicitly asked to share with the + constructor: (@params, @body, tag) -> + @params or= [] + @body or= new Expressions + @bound = tag is 'boundfunc'

#

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 CodeNode +has no children -- they're within the inner scope.

  compileNode: (o) ->
+    sharedScope = del o, 'sharedScope'
+    top         = del o, 'top'
+    o.scope     = sharedScope or new Scope(o.scope, @body, this)
+    o.top       = true
+    o.indent    = @idt(1)
+    empty       = @body.expressions.length is 0
+    del o, 'noWrap'
+    del o, 'globals'
+    splat = undefined
+    params = []
+    for param, i in @params
+      if splat
+        if param.attach
+          param.assign = new AssignNode new ValueNode literal('this'), [new AccessorNode param.value]
+          @body.expressions.splice splat.index + 1, 0, param.assign
+        splat.trailings.push param
+      else
+        if param.attach
+          {value} = param
+          [param, param.splat] = [literal(o.scope.freeVariable()), param.splat]
+          @body.unshift new AssignNode new ValueNode(literal('this'), [new AccessorNode value]), param
+        if param.splat
+          splat           = new SplatNode param.value
+          splat.index     = i
+          splat.trailings = []
+          splat.arglength = @params.length
+          @body.unshift(splat)
+        else
+          params.push param
+    params = (param.compile(o) for param in params)
+    @body.makeReturn() unless empty
+    (o.scope.parameter(param)) for param in params
+    code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations(o) }\n" else ''
+    func = "function(#{ params.join(', ') }) {#code#{ code and @tab }}"
+    return "#{utility('bind')}(#func, this)" if @bound
+    if top then "(#func)" else func
+
+  topSensitive: ->
+    true
#

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) ->
-    sharedScope:  del o, 'sharedScope'
-    top:          del o, 'top'
-    o.scope:      sharedScope or new Scope(o.scope, @body, this)
-    o.top:        true
-    o.indent:     @idt(if @bound then 2 else 1)
-    del o, 'noWrap'
-    del o, 'globals'
-    i: 0
-    splat: undefined
-    params: []
-    for param in @params
-      if param instanceof SplatNode and not splat?
-        splat: param
-        splat.index: i
-        splat.trailings: []
-        splat.arglength: @params.length
-        @body.unshift(splat)
-      else if splat?
-        splat.trailings.push(param)
-      else
-        params.push(param)
-      i: + 1
-    params: (param.compile(o) for param in params)
-    @body.makeReturn()
-    (o.scope.parameter(param)) for param in params
-    code: if @body.expressions.length then "\n${ @body.compileWithDeclarations(o) }\n" else ''
-    func: "function(${ params.join(', ') }) {$code${@idt(if @bound then 1 else 0)}}"
-    func: "($func)" if top and not @bound
-    return func unless @bound
-    inner: "(function() {\n${@idt(2)}return __func.apply(__this, arguments);\n${@idt(1)}});"
-    "(function(__this) {\n${@idt(1)}var __func = $func;\n${@idt(1)}return $inner\n$@tab})(this)"
-
-  topSensitive: ->
-    true
#

Short-circuit traverseChildren method to prevent it from crossing scope boundaries -unless crossScope is true

  traverseChildren: (crossScope, func) -> super(crossScope, func) if crossScope
+a closure.

  traverseChildren: (crossScope, func) -> super(crossScope, func) if crossScope
 
   toString: (idt) ->
-    idt: or ''
-    children: (child.toString(idt + TAB) for child in @collectChildren()).join('')
-    "\n$idt$children"
#

SplatNode

#

A splat, either as a parameter to a function, an argument to a call, -or as part of a destructuring assignment.

exports.SplatNode: class SplatNode extends BaseNode
+    idt or= ''
+    children = (child.toString(idt + TAB) for child in @collectChildren()).join('')
+    "\n#idt#children"
#

Short-circuit traverseChildren method to prevent it from crossing scope boundaries +unless crossScope is true

#

ParamNode

exports.ParamNode = class ParamNode extends BaseNode
+
+  class:    'ParamNode'
+  children: ['name']
+
+  constructor: (@name, @attach, @splat) ->
+    @value = literal @name
+
+  compileNode: (o) ->
+    @value.compile o
+
+  toString: (idt) ->
+    if @attach then (literal "@#@name").toString idt else @value.toString idt
#

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.

#

SplatNode

exports.SplatNode = class SplatNode extends BaseNode
 
   class:     'SplatNode'
   children: ['name']
 
   constructor: (name) ->
-    name: literal(name) unless name.compile
-    @name: name
+    name = literal(name) unless name.compile
+    @name = name
 
   compileNode: (o) ->
-    if @index? then @compileParam(o) else @name.compile(o)
#

Compiling a parameter splat means recovering the parameters that succeed -the splat in the parameter list, by slicing the arguments object.

  compileParam: (o) ->
-    name: @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.

  compileParam: (o) ->
+    name = @name.compile(o)
     o.scope.find name
-    len: o.scope.freeVariable()
-    o.scope.assign len, "arguments.length"
-    variadic: o.scope.freeVariable()
-    o.scope.assign variadic, "$len >= $@arglength"
-    for trailing, idx in @trailings
-      pos: @trailings.length - idx
-      o.scope.assign(trailing.compile(o), "arguments[$variadic ? $len - $pos : ${@index + idx}]")
-    "$name = ${utility('slice')}.call(arguments, $@index, $len - ${@trailings.length})"
#

A compiling a splat as a destructuring assignment means slicing arguments -from the right-hand-side's corresponding array.

  compileValue: (o, name, index, trailings) ->
-    trail: if trailings then ", ${name}.length - $trailings" else ''
-    "${utility 'slice'}.call($name, $index$trail)"
#

Utility function that converts arbitrary number of elements, mixed with -splats, to a proper array

  @compileMixedArray: (list, o) ->
-    args: []
-    i: 0
-    for arg in list
-      code: arg.compile o
+    end = ''
+    if @trailings.length
+      len = o.scope.freeVariable()
+      o.scope.assign len, "arguments.length"
+      variadic = o.scope.freeVariable()
+      o.scope.assign variadic, "#len >= #@arglength"
+      end = if @trailings.length then ", #len - #{@trailings.length}"
+      for trailing, idx in @trailings
+        if trailing.attach
+          assign        = trailing.assign
+          trailing      = literal o.scope.freeVariable()
+          assign.value  = trailing
+        pos = @trailings.length - idx
+        o.scope.assign(trailing.compile(o), "arguments[#variadic ? #len - #pos : #{@index + idx}]")
+    "#name = #{utility('slice')}.call(arguments, #@index#end)"
#

Compiling a parameter splat means recovering the parameters that succeed +the splat in the parameter list, by slicing the arguments object.

  compileValue: (o, name, index, trailings) ->
+    trail = if trailings then ", #{name}.length - #trailings" else ''
+    "#{utility 'slice'}.call(#name, #index#trail)"
#

A compiling a splat as a destructuring assignment means slicing arguments +from the right-hand-side's corresponding array.

  @compileSplattedArray: (list, o) ->
+    args = []
+    for arg, i in list
+      code = arg.compile o
+      prev = args[last = args.length - 1]
       if not (arg instanceof SplatNode)
-        prev: args[i - 1]
-        if i is 1 and prev.substr(0, 1) is '[' and prev.substr(prev.length - 1, 1) is ']'
-          args[i - 1]: "${prev.substr(0, prev.length - 1)}, $code]"
+        if prev and starts(prev, '[') and ends(prev, ']')
+          args[last] = "#{prev.substr(0, prev.length - 1)}, #code]"
           continue
-        else if i > 1 and prev.substr(0, 9) is '.concat([' and prev.substr(prev.length - 2, 2) is '])'
-          args[i - 1]: "${prev.substr(0, prev.length - 2)}, $code])"
+        else if prev and starts(prev, '.concat([') and ends(prev, '])')
+          args[last] = "#{prev.substr(0, prev.length - 2)}, #code])"
           continue
         else
-          code: "[$code]"
-      args.push(if i is 0 then code else ".concat($code)")
-      i: + 1
-    args.join('')
#

WhileNode

#

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.WhileNode: class WhileNode extends BaseNode
+          code = "[#code]"
+      args.push(if i is 0 then code else ".concat(#code)")
+    args.join('')
#

Utility function that converts arbitrary number of elements, mixed with +splats, to a proper array

#

WhileNode

exports.WhileNode = class WhileNode extends BaseNode
 
   class:         'WhileNode'
   children:     ['condition', 'guard', 'body']
@@ -683,53 +739,55 @@ 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.

  compileNode: (o) ->
-    top:        del(o, 'top') and not @returns
-    o.indent:   @idt 1
-    o.top:      true
-    cond:       @condition.compile(o)
-    set:        ''
+    true
#

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) ->
+    top      =  del(o, 'top') and not @returns
+    o.indent =  @idt 1
+    o.top    =  true
+    cond     =  @condition.compile(o)
+    set      =  ''
     unless top
-      rvar:     o.scope.freeVariable()
-      set:      "$@tab$rvar = [];\n"
-      @body:    PushNode.wrap(rvar, @body) if @body
-    pre:        "$set${@tab}while ($cond)"
-    @body:      Expressions.wrap([new IfNode(@guard, @body)]) if @guard
+      rvar  = o.scope.freeVariable()
+      set   = "#@tab#rvar = [];\n"
+      @body = PushNode.wrap(rvar, @body) if @body
+    pre     = "#set#{@tab}while (#cond)"
+    @body   = Expressions.wrap([new IfNode(@guard, @body)]) if @guard
     if @returns
-      post: '\n' + new ReturnNode(literal(rvar)).compile(merge(o, {indent: @idt()}))
+      post = '\n' + new ReturnNode(literal(rvar)).compile(merge(o, indent: @idt()))
     else
-      post: ''
-    "$pre {\n${ @body.compile(o) }\n$@tab}$post"
#

OpNode

#

Simple Arithmetic and logical operations. Performs some conversion from -CoffeeScript operations into their JavaScript equivalents.

exports.OpNode: class OpNode extends BaseNode
#

The map of conversions from CoffeeScript to JavaScript symbols.

  CONVERSIONS: {
+      post = ''
+    "#pre {\n#{ @body.compile(o) }\n#@tab}#post"
#

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.

#

OpNode

exports.OpNode = class OpNode extends BaseNode
#

Simple Arithmetic and logical operations. Performs some conversion from +CoffeeScript operations into their JavaScript equivalents.

  CONVERSIONS: {
     '==': '==='
     '!=': '!=='
-  }
#

The list of operators for which we perform -Python-style comparison chaining.

  CHAINABLE:        ['<', '>', '>=', '<=', '===', '!==']
#

Our assignment operators that have no JavaScript equivalent.

  ASSIGNMENT:       ['||=', '&&=', '?=']
#

Operators must come before their operands with a space.

  PREFIX_OPERATORS: ['typeof', 'delete']
+  }
#

The map of conversions from CoffeeScript to JavaScript symbols.

  CHAINABLE:        ['<', '>', '>=', '<=', '===', '!==']
#

The list of operators for which we perform +Python-style comparison chaining.

  ASSIGNMENT:       ['||=', '&&=', '?=']
#

Our assignment operators that have no JavaScript equivalent.

  PREFIX_OPERATORS: ['typeof', 'delete']
 
   class:     'OpNode'
   children: ['first', 'second']
 
-  constructor: (operator, first, second, flip) ->
-    @first: first
-    @second: second
-    @operator: @CONVERSIONS[operator] or operator
-    @flip: !!flip
+  constructor: (@operator, @first, @second, flip) ->
+    @operator = @CONVERSIONS[@operator] or @operator
+    @flip     = !!flip
+    if @first instanceof ValueNode and @first.base instanceof ObjectNode
+      @first = new ParentheticalNode @first
 
   isUnary: ->
     not @second
@@ -741,119 +799,107 @@ CoffeeScript operations into their JavaScript equivalents.

#

Mimic Python's chained comparisons when multiple comparison operators are + [@first.compile(o), @operator, @second.compile(o)].join ' '

#

Operators must come before their operands with a space.

  compileChain: (o) ->
+    shared = @first.unwrap().second
+    [@first.second, shared] = shared.compileReference(o) if shared.containsType CallNode
+    [first, second, shared] = [@first.compile(o), @second.compile(o), shared.compile(o)]
+    "(#first) && (#shared #@operator #second)"
#

Mimic Python's chained comparisons when multiple comparison operators are used sequentially. For example:

bin/coffee -e "puts 50 < 65 > 10"
 true
-
  compileChain: (o) ->
-    shared: @first.unwrap().second
-    [@first.second, shared]: shared.compileReference(o) if shared.containsType CallNode
-    [first, second, shared]: [@first.compile(o), @second.compile(o), shared.compile(o)]
-    "($first) && ($shared $@operator $second)"
#

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.

  compileAssignment: (o) ->
-    [first, second]: [@first.compile(o), @second.compile(o)]
+
  compileAssignment: (o) ->
+    [first, firstVar] = @first.compileReference o, precompile: yes, assignment: yes
+    second = @second.compile o
     o.scope.find(first) if first.match(IDENTIFIER)
-    return "$first = ${ ExistenceNode.compileTest(o, @first) } ? $first : $second" if @operator is '?='
-    "$first = $first ${ @operator.substr(0, 2) } $second"
#

If this is an existence operator, we delegate to ExistenceNode.compileTest -to give us the safe references for the variables.

  compileExistence: (o) ->
-    [first, second]: [@first.compile(o), @second.compile(o)]
-    test: ExistenceNode.compileTest(o, @first)
-    "$test ? $first : $second"
#

Compile a unary OpNode.

  compileUnary: (o) ->
-    space: if indexOf(@PREFIX_OPERATORS, @operator) >= 0 then ' ' else ''
-    parts: [@operator, space, @first.compile(o)]
-    parts: parts.reverse() if @flip
-    parts.join('')
#

InNode

exports.InNode: class InNode extends BaseNode
+    return "#first = #{ ExistenceNode.compileTest(o, literal(firstVar)) } ? #firstVar : #second" if @operator is '?='
+    "#first = #firstVar #{ @operator.substr(0, 2) } #second"
#

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.

  compileExistence: (o) ->
+    [first, second] = [@first.compile(o), @second.compile(o)]
+    test = ExistenceNode.compileTest(o, @first)
+    "#test ? #first : #second"
#

If this is an existence operator, we delegate to ExistenceNode.compileTest +to give us the safe references for the variables.

  compileUnary: (o) ->
+    space = if indexOf(@PREFIX_OPERATORS, @operator) >= 0 then ' ' else ''
+    parts = [@operator, space, @first.compile(o)]
+    parts = parts.reverse() if @flip
+    parts.join('')
#

Compile a unary OpNode.

exports.InNode = class InNode extends BaseNode
 
   class:    'InNode'
   children: ['object', 'array']
 
-  constructor: (object, array) ->
-    @object: object
-    @array: array
+  constructor: (@object, @array) ->
 
   isArray: ->
     @array instanceof ValueNode and @array.isArray()
 
   compileNode: (o) ->
-    [@obj1, @obj2]: @object.compileReference o, {precompile: yes}
+    [@obj1, @obj2] = @object.compileReference o, precompile: yes
     if @isArray() then @compileOrTest(o) else @compileLoopTest(o)
 
   compileOrTest: (o) ->
-    tests: for item, i in @array.base.objects
-      "${item.compile(o)} === ${if i then @obj2 else @obj1}"
-    "(${tests.join(' || ')})"
+    tests = for item, i in @array.base.objects
+      "#{item.compile(o)} === #{if i then @obj2 else @obj1}"
+    "(#{tests.join(' || ')})"
 
   compileLoopTest: (o) ->
-    [@arr1, @arr2]: @array.compileReference o, {precompile: yes}
-    [i, l]: [o.scope.freeVariable(), o.scope.freeVariable()]
-    prefix: if @obj1 isnt @obj2 then @obj1 + '; ' else ''
-    "!!(function(){ ${prefix}for (var $i=0, $l=${@arr1}.length; $i<$l; $i++) if (${@arr2}[$i] === $@obj2) return true; }).call(this)"
#

TryNode

#

A classic try/catch/finally block.

exports.TryNode: class TryNode extends BaseNode
+    [@arr1, @arr2] = @array.compileReference o, precompile: yes
+    [i, l] = [o.scope.freeVariable(), o.scope.freeVariable()]
+    prefix = if @obj1 isnt @obj2 then @obj1 + '; ' else ''
+    "!!(function(){ #{prefix}for (var #i=0, #l=#{@arr1}.length; #i<#l; #i++) if (#{@arr2}[#i] === #@obj2) return true; }).call(this)"
#

InNode

#

TryNode

exports.TryNode = class TryNode extends BaseNode
 
   class:        'TryNode'
   children:     ['attempt', 'recovery', 'ensure']
   isStatement:  -> yes
 
-  constructor: (attempt, error, recovery, ensure) ->
-    @attempt: attempt
-    @recovery: recovery
-    @ensure: ensure
-    @error: error
+  constructor: (@attempt, @error, @recovery, @ensure) ->
 
   makeReturn: ->
-    @attempt: @attempt.makeReturn() if @attempt
-    @recovery: @recovery.makeReturn() if @recovery
-    this
#

Compilation is more or less as you would expect -- the finally clause -is optional, the catch is not.

  compileNode: (o) ->
-    o.indent:     @idt 1
-    o.top:        true
-    attemptPart: @attempt.compile(o)
-    errorPart:   if @error then " (${ @error.compile(o) }) " else ' '
-    catchPart:   if @recovery then " catch$errorPart{\n${ @recovery.compile(o) }\n$@tab}" else ''
-    finallyPart: (@ensure or '') and ' finally {\n' + @ensure.compile(merge(o)) + "\n$@tab}"
-    "${@tab}try {\n$attemptPart\n$@tab}$catchPart$finallyPart"
#

ThrowNode

#

Simple node to throw an exception.

exports.ThrowNode: class ThrowNode extends BaseNode
+    @attempt  = @attempt.makeReturn() if @attempt
+    @recovery = @recovery.makeReturn() if @recovery
+    this
#

A classic try/catch/finally block.

  compileNode: (o) ->
+    o.indent    = @idt 1
+    o.top       = true
+    attemptPart = @attempt.compile(o)
+    errorPart   = if @error then " (#{ @error.compile(o) }) " else ' '
+    catchPart   = if @recovery then " catch#errorPart{\n#{ @recovery.compile(o) }\n#@tab}" else ''
+    finallyPart = (@ensure or '') and ' finally {\n' + @ensure.compile(merge(o)) + "\n#@tab}"
+    "#{@tab}try {\n#attemptPart\n#@tab}#catchPart#finallyPart"
#

Compilation is more or less as you would expect -- the finally clause +is optional, the catch is not.

#

ThrowNode

exports.ThrowNode = class ThrowNode extends BaseNode
 
   class:         'ThrowNode'
   children:     ['expression']
   isStatement: -> yes
 
-  constructor: (expression) ->
-    @expression: expression
#

A ThrowNode is already a return, of sorts...

  makeReturn: ->
+  constructor: (@expression) ->
#

Simple node to throw an exception.

  makeReturn: ->
     return this
 
   compileNode: (o) ->
-    "${@tab}throw ${@expression.compile(o)};"
#

ExistenceNode

#

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.ExistenceNode: class ExistenceNode extends BaseNode
+    "#{@tab}throw #{@expression.compile(o)};"
#

A ThrowNode is already a return, of sorts...

#

ExistenceNode

exports.ExistenceNode = class ExistenceNode extends BaseNode
 
   class:     'ExistenceNode'
   children: ['expression']
 
-  constructor: (expression) ->
-    @expression: expression
+  constructor: (@expression) ->
 
   compileNode: (o) ->
-    ExistenceNode.compileTest(o, @expression)
#

The meat of the ExistenceNode is in this static compileTest method + ExistenceNode.compileTest(o, @expression)

#

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.

  @compileTest: (o, variable) ->
+    [first, second] = variable.compileReference o
+    "(typeof #{first.compile(o)} !== \"undefined\" && #{second.compile(o)} !== null)"
#

The meat of the ExistenceNode is in this static compileTest method because other nodes like to check the existence of their variables as well. -Be careful not to double-evaluate anything.

  @compileTest: (o, variable) ->
-    [first, second]: variable.compileReference o
-    "(typeof ${first.compile(o)} !== \"undefined\" && ${second.compile(o)} !== null)"
#

ParentheticalNode

#

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.ParentheticalNode: class ParentheticalNode extends BaseNode
+Be careful not to double-evaluate anything.

#

ParentheticalNode

exports.ParentheticalNode = class ParentheticalNode extends BaseNode
 
   class:     'ParentheticalNode'
   children: ['expression']
 
-  constructor: (expression) ->
-    @expression: expression
+  constructor: (@expression) ->
 
   isStatement: ->
     @expression.isStatement()
@@ -865,142 +911,142 @@ parentheses, but no longer -- you can put in as many as you please.

yes compileNode: (o) -> - top: del o, 'top' - code: @expression.compile(o) + top = del o, 'top' + code = @expression.compile(o) if @isStatement() - return (if top then "$@tab$code;" else code) - l: code.length - code: code.substr(o, l-1) if code.substr(l-1, 1) is ';' - if @expression instanceof AssignNode then code else "($code)"
#

ForNode

#

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.

+ return (if top then "#@tab#code;" else code) + l = code.length + code = code.substr(o, l-1) if code.substr(l-1, 1) is ';' + if @expression instanceof AssignNode then code else "(#code)"
#

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.

-

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.ForNode: class ForNode extends BaseNode
+

Parentheses are a good way to force any statement to become an expression.

#

ForNode

exports.ForNode = class ForNode extends BaseNode
 
   class:         'ForNode'
   children:     ['body', 'source', 'guard']
   isStatement: -> yes
 
-  constructor: (body, source, name, index) ->
-    @body:    body
-    @name:    name
-    @index:   index or null
-    @source:  source.source
-    @guard:   source.guard
-    @step:    source.step
-    @object:  !!source.object
-    [@name, @index]: [@index, @name] if @object
-    @pattern: @name instanceof ValueNode
+  constructor: (@body, source, @name, @index) ->
+    @index  or= null
+    @source = source.source
+    @guard  = source.guard
+    @step   = source.step
+    @raw    = !!source.raw
+    @object = !!source.object
+    [@name, @index] = [@index, @name] if @object
+    @pattern = @name instanceof ValueNode
     throw new Error('index cannot be a pattern matching expression') if @index instanceof ValueNode
-    @returns: false
+    @returns = false
 
   topSensitive: ->
     true
 
   makeReturn: ->
-    @returns: true
+    @returns = true
     this
 
   compileReturnValue: (val, o) ->
     return '\n' + new ReturnNode(literal(val)).compile(o) if @returns
     return '\n' + val if val
-    ''
#

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) ->
-    topLevel:       del(o, 'top') and not @returns
-    range:          @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length
-    source:         if range then @source.base else @source
-    codeInBody:     @body.contains (n) -> n instanceof CodeNode
-    scope:          o.scope
-    name:           @name and @name.compile o
-    index:          @index and @index.compile o
+    ''
#

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.

  compileNode: (o) ->
+    topLevel      = del(o, 'top') and not @returns
+    range         = @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length
+    source        = if range then @source.base else @source
+    codeInBody    = @body.contains (n) -> n instanceof CodeNode
+    scope         = o.scope
+    name          = (@name and @name.compile(o)) or scope.freeVariable()
+    index         = @index and @index.compile o
     scope.find name  if name and not @pattern and not codeInBody
     scope.find index if index
-    rvar:           scope.freeVariable() unless topLevel
-    ivar:           if range then name else if codeInBody then scope.freeVariable() else index or scope.freeVariable()
-    varPart:        ''
-    body:           Expressions.wrap([@body])
+    rvar          = scope.freeVariable() unless topLevel
+    ivar          = if range then name else if codeInBody then scope.freeVariable() else index or scope.freeVariable()
+    varPart       = ''
+    guardPart     = ''
+    body          = Expressions.wrap([@body])
     if range
-      sourcePart:   source.compileVariables(o)
-      sourcePart:   + "\n$o.indent" if sourcePart
-      forPart:      source.compile merge o, {index: ivar, step: @step}
+      sourcePart  = source.compileVariables(o)
+      forPart     = source.compile merge o, index: ivar, step: @step
     else
-      svar:         scope.freeVariable()
-      sourcePart:   "$svar = ${ @source.compile(o) };"
+      svar        = scope.freeVariable()
+      sourcePart  = "#svar = #{ @source.compile(o) };"
       if @pattern
-        namePart:   new AssignNode(@name, literal("$svar[$ivar]")).compile(merge o, {indent: @idt(1), top: true}) + '\n'
+        namePart  = new AssignNode(@name, literal("#svar[#ivar]")).compile(merge o, {indent: @idt(1), top: true}) + '\n'
       else
-        namePart:   "$name = $svar[$ivar]" if name
+        namePart  = "#name = #svar[#ivar]" if name
       unless @object
-        lvar:       scope.freeVariable()
-        stepPart:   if @step then "$ivar += ${ @step.compile(o) }" else "$ivar++"
-        forPart:    "$ivar = 0, $lvar = ${svar}.length; $ivar < $lvar; $stepPart"
-    sourcePart:     (if rvar then "$rvar = []; " else '') + sourcePart
-    sourcePart:     if sourcePart then "$@tab$sourcePart\n$@tab" else @tab
-    returnResult:   @compileReturnValue(rvar, o)
+        lvar      = scope.freeVariable()
+        stepPart  = if @step then "#ivar += #{ @step.compile(o) }" else "#ivar++"
+        forPart   = "#ivar = 0, #lvar = #{svar}.length; #ivar < #lvar; #stepPart"
+    sourcePart    = (if rvar then "#rvar = []; " else '') + sourcePart
+    sourcePart    = if sourcePart then "#@tab#sourcePart\n#@tab" else @tab
+    returnResult  = @compileReturnValue(rvar, o)
 
-    body:           PushNode.wrap(rvar, body) unless topLevel
+    body          = PushNode.wrap(rvar, body) unless topLevel
     if @guard
-      body:         Expressions.wrap([new IfNode(@guard, body)])
+      body        = Expressions.wrap([new IfNode(@guard, body)])
     if codeInBody
-      body.unshift  literal "var $namePart" if namePart
-      body.unshift  literal "var $index = $ivar" if index
-      body:         ClosureNode.wrap(body, true)
+      body.unshift  literal "var #namePart" if namePart
+      body.unshift  literal "var #index = #ivar" if index
+      body        = ClosureNode.wrap(body, true)
     else
-      varPart:      (namePart or '') and (if @pattern then namePart else "${@idt(1)}$namePart;\n")
+      varPart     = (namePart or '') and (if @pattern then namePart else "#{@idt(1)}#namePart;\n")
     if @object
-      forPart:      "$ivar in $svar) { if (${utility('hasProp')}.call($svar, $ivar)"
-    body:           body.compile(merge(o, {indent: @idt(1), top: true}))
-    vars:           if range then name else "$name, $ivar"
-    close:          if @object then '}}' else '}'
-    "${sourcePart}for ($forPart) {\n$varPart$body\n$@tab$close$returnResult"
#

IfNode

#

If/else statements. Our switch/when will be compiled into this. Acts as an -expression by pushing down requested returns to the last line of each clause.

- -

Single-expression IfNodes are compiled into ternary operators if possible, -because ternaries are already proper expressions, and don't need conversion.

exports.IfNode: class IfNode extends BaseNode
+      forPart     = "#ivar in #svar"
+      guardPart   = "\n#{@idt(1)}if (!#{utility('hasProp')}.call(#svar, #ivar)) continue;" unless @raw
+    body          = body.compile(merge(o, {indent: @idt(1), top: true}))
+    vars          = if range then name else "#name, #ivar"
+    "#{sourcePart}for (#forPart) {#guardPart\n#varPart#body\n#@tab}#returnResult"
#

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.

#

IfNode

exports.IfNode = class IfNode extends BaseNode
 
   class:     'IfNode'
   children: ['condition', 'switchSubject', 'body', 'elseBody', 'assigner']
 
-  constructor: (condition, body, tags) ->
-    @condition: condition
-    @body:      body
-    @elseBody:  null
-    @tags:      tags or {}
-    @condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert
-    @isChain:   false
+  constructor: (@condition, @body, @tags) ->
+    @tags      or= {}
+    @condition = new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert
+    @elseBody = null
+    @isChain  = false
 
   bodyNode: -> @body?.unwrap()
   elseBodyNode: -> @elseBody?.unwrap()
 
   forceStatement: ->
-    @tags.statement: true
-    this
#

Tag a chain of IfNodes with their object(s) to switch on for equality -tests. rewriteSwitch will perform the actual change at compile time.

  switchesOver: (expression) ->
-    @switchSubject: expression
-    this
#

Rewrite a chain of IfNodes with their switch condition for equality. -Ensure that the switch expression isn't evaluated more than once.

  rewriteSwitch: (o) ->
-    @assigner: @switchSubject
+    @tags.statement = true
+    this
#

If/else statements. Our switch/when will be compiled into this. Acts as an +expression by pushing down requested returns to the last line of each clause.

+ +

Single-expression IfNodes are compiled into ternary operators if possible, +because ternaries are already proper expressions, and don't need conversion.

  switchesOver: (expression) ->
+    @switchSubject = expression
+    this
#

Tag a chain of IfNodes with their object(s) to switch on for equality +tests. rewriteSwitch will perform the actual change at compile time.

  rewriteSwitch: (o) ->
+    @assigner = @switchSubject
     unless (@switchSubject.unwrap() instanceof LiteralNode)
-      variable: literal(o.scope.freeVariable())
-      @assigner: new AssignNode(variable, @switchSubject)
-      @switchSubject: variable
-    @condition: for cond, i in flatten [@condition]
-      cond: new ParentheticalNode(cond) if cond instanceof OpNode
+      variable = literal(o.scope.freeVariable())
+      @assigner = new AssignNode(variable, @switchSubject)
+      @switchSubject = variable
+    @condition = for cond, i in flatten [@condition]
+      cond = new ParentheticalNode(cond) if cond instanceof OpNode
       new OpNode('==', (if i is 0 then @assigner else @switchSubject), cond)
-    @elseBodyNode().switchesOver(@switchSubject) if @isChain
#

prevent this rewrite from happening again

    @switchSubject: undefined
-    this
#

Rewrite a chain of IfNodes to add a default case as the final else.

  addElse: (elseBody, statement) ->
+    @elseBodyNode().switchesOver(@switchSubject) if @isChain
#

Rewrite a chain of IfNodes with their switch condition for equality. +Ensure that the switch expression isn't evaluated more than once.

    @switchSubject = undefined
+    this
#

prevent this rewrite from happening again

  addElse: (elseBody, statement) ->
     if @isChain
       @elseBodyNode().addElse elseBody, statement
     else
-      @isChain: elseBody instanceof IfNode
-      @elseBody: @ensureExpressions elseBody
-    this
#

The IfNode only compiles into a statement if either of its bodies needs -to be a statement. Otherwise a ternary is safe.

  isStatement: ->
-    @statement: or !!(@tags.statement or @bodyNode().isStatement() or (@elseBody and @elseBodyNode().isStatement()))
+      @isChain = elseBody instanceof IfNode
+      @elseBody = @ensureExpressions elseBody
+    this
#

Rewrite a chain of IfNodes to add a default case as the final else.

  isStatement: ->
+    @statement or= !!(@tags.statement or @bodyNode().isStatement() or (@elseBody and @elseBodyNode().isStatement()))
 
   compileCondition: (o) ->
     (cond.compile(o) for cond in flatten([@condition])).join(' || ')
@@ -1010,82 +1056,90 @@ to be a statement. Otherwise a ternary is safe.

#

Compile the IfNode as a regular if-else statement. Flattened chains -force inner else bodies into statement form.

  compileStatement: (o) ->
+    if node instanceof Expressions then node else new Expressions [node]
#

The IfNode only compiles into a statement if either of its bodies needs +to be a statement. Otherwise a ternary is safe.

  compileStatement: (o) ->
     @rewriteSwitch(o) if @switchSubject
-    child:        del o, 'chainChild'
-    condO:       merge o
-    o.indent:     @idt 1
-    o.top:        true
-    ifDent:      if child then '' else @idt()
-    comDent:     if child then @idt() else ''
-    body:         @body.compile(o)
-    ifPart:      "${ifDent}if (${ @compileCondition(condO) }) {\n$body\n$@tab}"
+    child    = del o, 'chainChild'
+    condO    = merge o
+    o.indent = @idt 1
+    o.top    = true
+    ifDent   = if child then '' else @idt()
+    comDent  = if child then @idt() else ''
+    body     = @body.compile(o)
+    ifPart   = "#{ifDent}if (#{ @compileCondition(condO) }) {\n#body\n#@tab}"
     return ifPart unless @elseBody
-    elsePart: if @isChain
+    elsePart = if @isChain
       ' else ' + @elseBodyNode().compile(merge(o, {indent: @idt(), chainChild: true}))
     else
-      " else {\n${ @elseBody.compile(o) }\n$@tab}"
-    "$ifPart$elsePart"
#

Compile the IfNode as a ternary operator.

  compileTernary: (o) ->
-    ifPart:    @condition.compile(o) + ' ? ' + @bodyNode().compile(o)
-    elsePart:  if @elseBody then @elseBodyNode().compile(o) else 'null'
-    "$ifPart : $elsePart"
#

Faux-Nodes

#

PushNode

#

Faux-nodes are never created by the grammar, but are used during code -generation to generate other combinations of nodes. The PushNode creates -the tree for array.push(value), which is helpful for recording the result -arrays from comprehensions.

PushNode: exports.PushNode: {
+      " else {\n#{ @elseBody.compile(o) }\n#@tab}"
+    "#ifPart#elsePart"
#

Compile the IfNode as a regular if-else statement. Flattened chains +force inner else bodies into statement form.

  compileTernary: (o) ->
+    o.operation = true
+    ifPart      = @condition.compile(o) + ' ? ' + @bodyNode().compile(o)
+    elsePart    = if @elseBody then @elseBodyNode().compile(o) else 'null'
+    "#ifPart : #elsePart"
#

Compile the IfNode as a ternary operator.

#

Faux-Nodes

#

PushNode

PushNode = exports.PushNode = {
 
   wrap: (array, expressions) ->
-    expr: expressions.unwrap()
+    expr = expressions.unwrap()
     return expressions if expr.isPureStatement() or expr.containsPureStatement()
     Expressions.wrap([new CallNode(
       new ValueNode(literal(array), [new AccessorNode(literal('push'))]), [expr]
     )])
 
-}
#

ClosureNode

#

A faux-node used to wrap an expressions body in a closure.

ClosureNode: exports.ClosureNode: {
#

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) ->
+}
#

Faux-nodes are never created by the grammar, but are used during code +generation to generate other combinations of nodes. The PushNode creates +the tree for array.push(value), which is helpful for recording the result +arrays from comprehensions.

#

ClosureNode

ClosureNode = exports.ClosureNode = {
#

A faux-node used to wrap an expressions body in a closure.

  wrap: (expressions, statement) ->
     return expressions if expressions.containsPureStatement()
-    func: new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions])))
-    args: []
-    mentionsArgs: expressions.contains (n) ->
+    func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions])))
+    args = []
+    mentionsArgs = expressions.contains (n) ->
       n instanceof LiteralNode and (n.value is 'arguments')
-    mentionsThis: expressions.contains (n) ->
+    mentionsThis = expressions.contains (n) ->
       (n instanceof LiteralNode and (n.value is 'this')) or
       (n instanceof CodeNode and n.bound)
     if mentionsArgs or mentionsThis
-      meth: literal(if mentionsArgs then 'apply' else 'call')
-      args: [literal('this')]
+      meth = literal(if mentionsArgs then 'apply' else 'call')
+      args = [literal('this')]
       args.push literal 'arguments' if mentionsArgs
-      func: new ValueNode func, [new AccessorNode(meth)]
-    call: new CallNode(func, args)
+      func = new ValueNode func, [new AccessorNode(meth)]
+    call = new CallNode(func, args)
     if statement then Expressions.wrap([call]) else call
 
-}
#

Utility Functions

UTILITIES: {
#

Correctly set up a prototype chain for inheritance, including a reference +}

#

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.

UTILITIES = {
#

Utility Functions

  extends:  """
+            function(child, parent) {
+                var ctor = function(){};
+                ctor.prototype = parent.prototype;
+                child.prototype = new ctor();
+                child.prototype.constructor = child;
+                if (typeof parent.extended === "function") parent.extended(child);
+                child.__superClass__ = parent.prototype;
+              }
+            """
#

Correctly set up a prototype chain for inheritance, including a reference to the superclass for super() calls. See: -goog.inherits.

  __extends:  """
-              function(child, parent) {
-                  var ctor = function(){ };
-                  ctor.prototype = parent.prototype;
-                  child.__superClass__ = parent.prototype;
-                  child.prototype = new ctor();
-                  child.prototype.constructor = child;
-                }
-              """
#

Shortcuts to speed up the lookup time for native functions.

  __hasProp: 'Object.prototype.hasOwnProperty'
-  __slice:   'Array.prototype.slice'
+goog.inherits.

  bind: """
+        function(func, context) {
+            return function(){ return func.apply(context, arguments); };
+          }
+        """
#

Create a function bound to the current value of "this".

  hasProp: 'Object.prototype.hasOwnProperty'
+  slice:   'Array.prototype.slice'
 
-}
#

Constants

#

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
#

Obvious redundant parentheses should be removed.

DOUBLE_PARENS: /\(\(([^\(\)\n]*)\)\)/g
#

Keep these identifier regexes in sync with the Lexer.

IDENTIFIER: /^[a-zA-Z\$_](\w|\$)*$/
-NUMBER    : /^(((\b0(x|X)[0-9a-fA-F]+)|((\b[0-9]+(\.[0-9]+)?|\.[0-9]+)(e[+\-]?[0-9]+)?)))\b$/i
#

Is a literal value a string?

IS_STRING: /^['"]/
#

Utility Functions

#

Handy helper for a generating LiteralNode.

literal: (name) ->
-  new LiteralNode(name)
#

Helper for ensuring that utility functions are assigned at the top level.

utility: (name) ->
-  ref: "__$name"
-  Scope.root.assign ref, UTILITIES[ref]
+}
#

Shortcuts to speed up the lookup time for native functions.

#

Constants

TAB = '  '
#

Tabs are two spaces for pretty printing.

TRAILING_WHITESPACE = /[ \t]+$/gm
#

Trim out all trailing whitespace, so that the generated code plays nice +with Git.

DOUBLE_PARENS = /\(\(([^\(\)\n]*)\)\)/g
#

Obvious redundant parentheses should be removed.

IDENTIFIER = /^[a-zA-Z\$_](\w|\$)*$/
+NUMBER     = /^(((\b0(x|X)[0-9a-fA-F]+)|((\b[0-9]+(\.[0-9]+)?|\.[0-9]+)(e[+\-]?[0-9]+)?)))\b$/i
+SIMPLENUM  = /^-?\d+/
#

Keep these identifier regexes in sync with the Lexer.

IS_STRING = /^['"]/
#

Is a literal value a string?

#

Utility Functions

literal = (name) ->
+  new LiteralNode(name)
#

Handy helper for a generating LiteralNode.

utility = (name) ->
+  ref = "__#name"
+  Scope.root.assign ref, UTILITIES[name]
   ref
 
-
\ No newline at end of file +
#

Helper for ensuring that utility functions are assigned at the top level.

undefined
\ No newline at end of file diff --git a/documentation/docs/optparse.html b/documentation/docs/optparse.html index dc583786..4c6d02b7 100644 --- a/documentation/docs/optparse.html +++ b/documentation/docs/optparse.html @@ -1,69 +1,69 @@ optparse.coffee

optparse.coffee

#

A simple OptionParser class to parse option flags from the command-line. Use it like so:

-
parser:  new OptionParser switches, helpBanner
-options: parser.parse process.argv
+
parser  = new OptionParser switches, helpBanner
+options = parser.parse process.argv
 

The first non-option is considered to be the start of the file (and file -option) list, and all subsequent arguments are left unparsed.

exports.OptionParser: class OptionParser
#

Initialize with a list of valid options, in the form:

+option) list, and all subsequent arguments are left unparsed.

exports.OptionParser = class OptionParser
#

Initialize with a list of valid options, in the form:

[short-flag, long-flag, description]
 

Along with an an optional banner for the usage help.

  constructor: (rules, banner) ->
-    @banner:  banner
-    @rules:   buildRules(rules)
#

Parse the list of arguments, populating an options object with all of the + @banner = banner + @rules = buildRules rules

#

Parse the list of arguments, populating an options object with all of the specified options, and returning it. options.arguments will be an array containing the remaning non-option arguments. This is a simpler API than many option parsers that allow you to attach callback actions for every flag. Instead, you're responsible for interpreting the options object.

  parse: (args) ->
-    options: {arguments: []}
-    args: normalizeArguments args
+    options = arguments: []
+    args    = normalizeArguments args
     for arg, i in args
-      isOption: !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
-      matchedRule: no
+      isOption = !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
+      matchedRule = no
       for rule in @rules
         if rule.shortFlag is arg or rule.longFlag is arg
-          options[rule.name]: if rule.hasArgument then args[i: + 1] else true
-          matchedRule: yes
+          options[rule.name] = if rule.hasArgument then args[i += 1] else true
+          matchedRule = yes
           break
-      throw new Error "unrecognized option: $arg" if isOption and not matchedRule
+      throw new Error "unrecognized option: #arg" if isOption and not matchedRule
       if not isOption
-        options.arguments: args[i...args.length]
+        options.arguments = args[i...args.length]
         break
     options
#

Return the help text for this OptionParser, listing and describing all of the valid options, for --help and such.

  help: ->
-    lines: ['Available options:']
-    lines.unshift "$@banner\n" if @banner
+    lines = ['Available options:']
+    lines.unshift "#@banner\n" if @banner
     for rule in @rules
-      spaces:   15 - rule.longFlag.length
-      spaces:   if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
-      letPart: if rule.shortFlag then rule.shortFlag + ', ' else '    '
-      lines.push "  $letPart$rule.longFlag$spaces$rule.description"
-    "\n${ lines.join('\n') }\n"
#

Helpers

#

Regex matchers for option flags.

LONG_FLAG:  /^(--\w[\w\-]+)/
-SHORT_FLAG: /^(-\w)/
-MULTI_FLAG: /^-(\w{2,})/
-OPTIONAL:   /\[(.+)\]/
#

Build and return the list of option rules. If the optional short-flag is -unspecified, leave it out by padding with null.

buildRules: (rules) ->
+      spaces  = 15 - rule.longFlag.length
+      spaces  = if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
+      letPart = if rule.shortFlag then rule.shortFlag + ', ' else '    '
+      lines.push "  #letPart#rule.longFlag#spaces#rule.description"
+    "\n#{ lines.join('\n') }\n"
#

Helpers

#

Regex matchers for option flags.

LONG_FLAG  = /^(--\w[\w\-]+)/
+SHORT_FLAG = /^(-\w)/
+MULTI_FLAG = /^-(\w{2,})/
+OPTIONAL   = /\[(.+)\]/
#

Build and return the list of option rules. If the optional short-flag is +unspecified, leave it out by padding with null.

buildRules = (rules) ->
   for tuple in rules
     tuple.unshift null if tuple.length < 3
     buildRule tuple...
#

Build a rule from a -o short flag, a --output [DIR] long flag, and the -description of what the option does.

buildRule: (shortFlag, longFlag, description) ->
-  match:      longFlag.match(OPTIONAL)
-  longFlag:  longFlag.match(LONG_FLAG)[1]
+description of what the option does.

buildRule = (shortFlag, longFlag, description) ->
+  match    = longFlag.match(OPTIONAL)
+  longFlag = longFlag.match(LONG_FLAG)[1]
   {
     name:         longFlag.substr 2
-    shortFlag:   shortFlag
-    longFlag:    longFlag
+    shortFlag:    shortFlag
+    longFlag:     longFlag
     description:  description
-    hasArgument: !!(match and match[1])
+    hasArgument:  !!(match and match[1])
   }
#

Normalize arguments by expanding merged flags into multiple flags. This allows -you to have -wl be the same as --watch --lint.

normalizeArguments: (args) ->
-  args: args.slice 0
-  result: []
+you to have -wl be the same as --watch --lint.

normalizeArguments = (args) ->
+  args = args.slice 0
+  result = []
   for arg in args
-    if match: arg.match MULTI_FLAG
+    if match = arg.match MULTI_FLAG
       result.push '-' + l for l in match[1].split ''
     else
       result.push arg
diff --git a/documentation/docs/repl.html b/documentation/docs/repl.html
index 9be24f83..c240f64f 100644
--- a/documentation/docs/repl.html
+++ b/documentation/docs/repl.html
@@ -2,24 +2,22 @@
 and evaluates it. Good for simple tests, or poking around the Node.js API.
 Using it looks like this:

-
coffee> puts "$num bottles of beer" for num in [99..1]
-
#

Require the coffee-script module to get access to the compiler.

CoffeeScript: require './coffee-script'
-helpers:      require('./helpers').helpers
-readline:     require 'readline'
#

Start by opening up stdio.

stdio: process.openStdin()
#

Quick alias for quitting the REPL.

helpers.extend global, {
-  quit: -> process.exit(0)
-}
#

The main REPL function. run is called every time a line of code is entered. +

coffee> puts "#num bottles of beer" for num in [99..1]
+
#

Require the coffee-script module to get access to the compiler.

CoffeeScript = require './coffee-script'
+helpers      = require('./helpers').helpers
+readline     = require 'readline'
#

Start by opening up stdio.

stdio = process.openStdin()
#

Quick alias for quitting the REPL.

helpers.extend global, quit: -> process.exit(0)
#

The main REPL function. run is called every time a line of code is entered. Attempt to evaluate the command. If there's an exception, print it out instead -of exiting.

run: (buffer) ->
+of exiting.

run = (buffer) ->
   try
-    val: CoffeeScript.run buffer.toString(), {noWrap: true, globals: true, source: 'repl'}
+    val = CoffeeScript.run buffer.toString(), noWrap: true, globals: true, fileName: 'repl'
     puts inspect val if val isnt undefined
   catch err
     puts err.stack or err.toString()
-  repl.prompt()
#

Create the REPL by listening to stdin.

repl: readline.createInterface stdio
+  repl.prompt()
#

Create the REPL by listening to stdin.

repl = readline.createInterface stdio
 repl.setPrompt 'coffee> '
-stdio.addListener 'data',   (buffer) -> repl.write buffer
-repl.addListener  'close',  -> stdio.destroy()
-repl.addListener  'line',   run
+stdio.on 'data',   (buffer) -> repl.write buffer
+repl.on  'close',  -> stdio.destroy()
+repl.on  'line',   run
 repl.prompt()
 
 
\ No newline at end of file diff --git a/documentation/docs/rewriter.html b/documentation/docs/rewriter.html index 636c4d45..ea7cf43c 100644 --- a/documentation/docs/rewriter.html +++ b/documentation/docs/rewriter.html @@ -4,39 +4,41 @@ the resulting parse table. Instead of making the parser handle it all, we take a series of passes over the token stream, using this Rewriter to convert shorthand into the unambiguous long form, add implicit indentation and parentheses, balance incorrect nestings, and generally clean things up.

#

Set up exported variables for both Node.js and the browser.

if process?
-  {helpers}: require('./helpers')
+  {helpers} = require('./helpers')
 else
-  this.exports: this
-  helpers:      this.helpers
#

Import the helpers we need.

{include}: helpers
#

The Rewriter class is used by the Lexer, directly against -its internal array of tokens.

exports.Rewriter: class Rewriter
#

Rewrite the token stream in multiple passes, one logical filter at + this.exports = this + helpers = this.helpers

#

Import the helpers we need.

{include} = helpers
#

The Rewriter class is used by the Lexer, directly against +its internal array of tokens.

exports.Rewriter = class Rewriter
#

Helpful snippet for debugging: + puts (t[0] + '/' + t[1] for t in @tokens).join ' '

#

Rewrite the token stream in multiple passes, one logical filter at a time. This could certainly be changed into a single pass through the stream, with a big ol' efficient switch, but it's much nicer to work with like this. The order of these passes matters -- indentation must be corrected before implicit parentheses can be wrapped around blocks of code.

  rewrite: (tokens) ->
-    @tokens: tokens
+    @tokens = tokens
     @adjustComments()
     @removeLeadingNewlines()
     @removeMidExpressionNewlines()
     @closeOpenCallsAndIndexes()
     @addImplicitIndentation()
+    @addImplicitBraces()
     @addImplicitParentheses()
     @ensureBalance BALANCED_PAIRS
     @rewriteClosingParens()
-    @tokens
#

Rewrite the token stream, looking one token ahead and behind. + @tokens

#

Rewrite the token stream, looking one token ahead and behind. Allow the return value of the block to tell us how many tokens to move forwards (or backwards) in the stream, to make sure we don't miss anything as tokens are inserted and removed, and the stream changes length under our feet.

  scanTokens: (block) ->
-    i: 0
+    i = 0
     loop
       break unless @tokens[i]
-      move: block @tokens[i - 1], @tokens[i], @tokens[i + 1], i
-      i: + move
-    true
#

Massage newlines and indentations so that comments don't have to be + move = block @tokens[i - 1], @tokens[i], @tokens[i + 1], i + i += move + true

#

Massage newlines and indentations so that comments don't have to be correctly indented, or appear on a line of their own.

  adjustComments: ->
     @scanTokens (prev, token, post, i) =>
       return 1 unless token[0] is 'HERECOMMENT'
-      [before, after]: [@tokens[i - 2], @tokens[i + 2]]
+      [before, after] = [@tokens[i - 2], @tokens[i + 2]]
       if after and after[0] is 'INDENT'
         @tokens.splice i + 2, 1
         if before and before[0] is 'OUTDENT' and post and prev[0] is post[0] is 'TERMINATOR'
@@ -45,80 +47,119 @@ correctly indented, or appear on a line of their own.

@tokens.splice i, 0, after else if prev and prev[0] not in ['TERMINATOR', 'INDENT', 'OUTDENT'] if post and post[0] is 'TERMINATOR' and after and after[0] is 'OUTDENT' - @tokens.splice(i + 3, 0, @tokens.splice(i, 2)...) - @tokens.splice(i + 3, 0, ['TERMINATOR', "\n", prev[2]]) + @tokens.splice(i + 2, 0, @tokens.splice(i, 2)...) + if @tokens[i + 2][0] isnt 'TERMINATOR' + @tokens.splice i + 2, 0, ['TERMINATOR', "\n", prev[2]] else @tokens.splice i, 0, ['TERMINATOR', "\n", prev[2]] return 2 - return 1
#

Leading newlines would introduce an ambiguity in the grammar, so we + return 1

#

Leading newlines would introduce an ambiguity in the grammar, so we dispatch them here.

  removeLeadingNewlines: ->
-    @tokens.shift() while @tokens[0] and @tokens[0][0] is 'TERMINATOR'
#

Some blocks occur in the middle of expressions -- when we're expecting + @tokens.shift() while @tokens[0] and @tokens[0][0] is 'TERMINATOR'

#

Some blocks occur in the middle of expressions -- when we're expecting this, remove their trailing newlines.

  removeMidExpressionNewlines: ->
     @scanTokens (prev, token, post, i) =>
       return 1 unless post and include(EXPRESSION_CLOSE, post[0]) and token[0] is 'TERMINATOR'
       @tokens.splice i, 1
-      return 0
#

The lexer has tagged the opening parenthesis of a method call, and the + return 0

#

The lexer has tagged the opening parenthesis of a method call, and the opening bracket of an indexing operation. Match them with their paired close.

  closeOpenCallsAndIndexes: ->
-    parens:   [0]
-    brackets: [0]
+    parens   = [0]
+    brackets = [0]
     @scanTokens (prev, token, post, i) =>
       switch token[0]
         when 'CALL_START'  then parens.push 0
         when 'INDEX_START' then brackets.push 0
-        when '('           then parens[parens.length - 1]: + 1
-        when '['           then brackets[brackets.length - 1]: + 1
+        when '('           then parens[parens.length - 1] += 1
+        when '['           then brackets[brackets.length - 1] += 1
         when ')'
           if parens[parens.length - 1] is 0
             parens.pop()
-            token[0]: 'CALL_END'
+            token[0] = 'CALL_END'
           else
-            parens[parens.length - 1]: - 1
+            parens[parens.length - 1] -= 1
         when ']'
           if brackets[brackets.length - 1] == 0
             brackets.pop()
-            token[0]: 'INDEX_END'
+            token[0] = 'INDEX_END'
           else
-            brackets[brackets.length - 1]: - 1
-      return 1
#

Methods may be optionally called without parentheses, for simple cases. -Insert the implicit parentheses here, so that the parser doesn't have to -deal with them.

  addImplicitParentheses: ->
-    stack: [0]
-    closeCalls: (i) =>
-      for tmp in [0...stack[stack.length - 1]]
-        @tokens.splice(i, 0, ['CALL_END', ')', @tokens[i][2]])
-      size: stack[stack.length - 1] + 1
-      stack[stack.length - 1]: 0
+            brackets[brackets.length - 1] -= 1
+      return 1
#

Object literals may be written with implicit braces, for simple cases. +Insert the missing braces here, so that the parser doesn't have to.

  addImplicitBraces: ->
+    stack   = [0]
+    running = no
+    closeBrackets = (i) =>
+      len = stack.length - 1
+      for tmp in [0...stack[len]]
+        @tokens.splice(i, 0, ['}', '}', @tokens[i][2]])
+      size = stack[len] + 1
+      stack[len] = 0
       size
     @scanTokens (prev, token, post, i) =>
-      tag: token[0]
-      stack[stack.length - 2]: + stack.pop() if tag is 'OUTDENT'
-      open: stack[stack.length - 1] > 0
+      tag    = token[0]
+      len    = stack.length - 1
+      before = @tokens[i - 2]
+      after  = @tokens[i + 2]
+      open   = stack[len] > 0
+      if (tag is 'TERMINATOR' and not ((after and after[0] is ':') or (post and post[0] is '@' and @tokens[i + 3] and @tokens[i + 3][0] is ':'))) or
+          (running and tag is ',' and post and (post[0] not in ['IDENTIFIER', 'STRING', '@']))
+        running = no
+        return closeBrackets(i)
+      else if include EXPRESSION_START, tag
+        stack.push(if tag is '{' then 1 else 0)
+        return 2 if tag is '{' and post and post[0] is 'INDENT'
+      else if include EXPRESSION_END, tag
+        return 1 if tag is 'OUTDENT' and post and post[0] is '}'
+        size = closeBrackets(i) if tag is 'OUTDENT'
+        stack[len - 1] += stack.pop()
+        stack[len - 1] -= 1 if tag is '}'
+        return size if tag is 'OUTDENT'
+      else if tag is ':' and not open
+        idx = if before and before[0] is '@' then i - 2 else i - 1
+        @tokens.splice idx, 0, ['{', '{', token[2]]
+        stack[stack.length - 1] += 1
+        running = yes
+        return 2
+      return 1
#

Methods may be optionally called without parentheses, for simple cases. +Insert the implicit parentheses here, so that the parser doesn't have to +deal with them.

  addImplicitParentheses: ->
+    stack = [0]
+    closeCalls = (i) =>
+      for tmp in [0...stack[stack.length - 1]]
+        @tokens.splice(i, 0, ['CALL_END', ')', @tokens[i][2]])
+      size = stack[stack.length - 1] + 1
+      stack[stack.length - 1] = 0
+      size
+    @scanTokens (prev, token, post, i) =>
+      tag = token[0]
+      before = @tokens[i - 2] and @tokens[i - 2][0]
+      stack[stack.length - 2] += stack.pop() if tag is 'OUTDENT'
+      open = stack[stack.length - 1] > 0
       if prev and prev.spaced and include(IMPLICIT_FUNC, prev[0]) and include(IMPLICIT_CALL, tag) and
           not (tag is '!' and (post[0] in ['IN', 'OF']))
         @tokens.splice i, 0, ['CALL_START', '(', token[2]]
-        stack[stack.length - 1]: + 1
+        stack[stack.length - 1] += 1
         stack.push 0 if include(EXPRESSION_START, tag)
         return 2
       if include(EXPRESSION_START, tag)
-        if tag is 'INDENT' and !token.generated and open and not (prev and include(IMPLICIT_BLOCK, prev[0]))
-          size: closeCalls(i)
+        if tag is 'INDENT' and !token.generated and open and not
+            ((prev and include(IMPLICIT_BLOCK, prev[0])) or before and before is 'CLASS')
+          size = closeCalls(i)
           stack.push 0
           return size
         stack.push 0
         return 1
       if open and !token.generated and prev[0] isnt ',' and (!post or include(IMPLICIT_END, tag))
-        j: 1; j++ while (nx: @tokens[i + j])? and include(IMPLICIT_END, nx[0])
-        if nx? and nx[0] is ','
+        j = 1; j++ while (nx = @tokens[i + j])? and include(IMPLICIT_END, nx[0])
+        if nx? and nx[0] is ',' and @tokens[i + j - 1][0] is 'OUTDENT'
           @tokens.splice(i, 1) if tag is 'TERMINATOR'
         else
-          size: closeCalls(i)
+          size = closeCalls(i)
           stack.pop() if tag isnt 'OUTDENT' and include EXPRESSION_END, tag
           return size
       if tag isnt 'OUTDENT' and include EXPRESSION_END, tag
-        stack[stack.length - 2]: + stack.pop()
+        stack[stack.length - 2] += stack.pop()
         return 1
-      return 1
#

Because our grammar is LALR(1), it can't handle some single-line + return 1

#

Because our grammar is LALR(1), it can't handle some single-line expressions that lack ending delimiters. The Rewriter adds the implicit blocks, so it doesn't need to. ')' can close a single-line block, but we need to make sure it's balanced.

  addImplicitIndentation: ->
@@ -126,52 +167,53 @@ but we need to make sure it's balanced.

if token[0] is 'ELSE' and prev[0] isnt 'OUTDENT' @tokens.splice i, 0, @indentation(token)... return 2 - if token[0] is 'CATCH' and @tokens[i + 2][0] is 'TERMINATOR' + if token[0] is 'CATCH' and + (@tokens[i + 2][0] is 'TERMINATOR' or @tokens[i + 2][0] is 'FINALLY') @tokens.splice i + 2, 0, @indentation(token)... return 4 return 1 unless include(SINGLE_LINERS, token[0]) and post[0] isnt 'INDENT' and not (token[0] is 'ELSE' and post[0] is 'IF') - starter: token[0] - [indent, outdent]: @indentation token - indent.generated: outdent.generated: true + starter = token[0] + [indent, outdent] = @indentation token + indent.generated = outdent.generated = true @tokens.splice i + 1, 0, indent - idx: i + 1 - parens: 0 + idx = i + 1 + parens = 0 loop - idx: + 1 - tok: @tokens[idx] - pre: @tokens[idx - 1] + idx += 1 + tok = @tokens[idx] + pre = @tokens[idx - 1] if (not tok or (include(SINGLE_CLOSERS, tok[0]) and tok[1] isnt ';' and parens is 0) or (tok[0] is ')' and parens is 0)) and not (tok[0] is 'ELSE' and starter not in ['IF', 'THEN']) - insertion: if pre[0] is "," then idx - 1 else idx + insertion = if pre[0] is "," then idx - 1 else idx @tokens.splice insertion, 0, outdent break - parens: + 1 if tok[0] is '(' - parens: - 1 if tok[0] is ')' + parens += 1 if tok[0] is '(' + parens -= 1 if tok[0] is ')' return 1 unless token[0] is 'THEN' @tokens.splice i, 1 - return 0
#

Ensure that all listed pairs of tokens are correctly balanced throughout + return 0

#

Ensure that all listed pairs of tokens are correctly balanced throughout the course of the token stream.

  ensureBalance: (pairs) ->
-    levels: {}
-    openLine: {}
+    levels   = {}
+    openLine = {}
     @scanTokens (prev, token, post, i) =>
       for pair in pairs
-        [open, close]: pair
-        levels[open]: or 0
+        [open, close] = pair
+        levels[open] or= 0
         if token[0] is open
-          openLine[open]: token[2] if levels[open] == 0
-          levels[open]: + 1
-        levels[open]: - 1 if token[0] is close
-        throw new Error("too many ${token[1]} on line ${token[2] + 1}") if levels[open] < 0
+          openLine[open] = token[2] if levels[open] == 0
+          levels[open] += 1
+        levels[open] -= 1 if token[0] is close
+        throw new Error("too many #{token[1]} on line #{token[2] + 1}") if levels[open] < 0
       return 1
-    unclosed: key for key, value of levels when value > 0
+    unclosed = key for key, value of levels when value > 0
     if unclosed.length
-      open: unclosed[0]
-      line: openLine[open] + 1
-      throw new Error "unclosed $open on line $line"
#

We'd like to support syntax like this:

+ open = unclosed[0] + line = openLine[open] + 1 + throw new Error "unclosed #open on line #line"
#

We'd like to support syntax like this:

el.click((event) ->
   el.hide())
@@ -190,27 +232,27 @@ up balanced in the end.
 
  • Be careful not to alter array or parentheses delimiters with overzealous rewriting.
  •   rewriteClosingParens: ->
    -    stack: []
    -    debt:  {}
    -    (debt[key]: 0) for key, val of INVERSES
    +    stack = []
    +    debt  = {}
    +    (debt[key] = 0) for key, val of INVERSES
         @scanTokens (prev, token, post, i) =>
    -      tag: token[0]
    -      inv: INVERSES[token[0]]
    +      tag = token[0]
    +      inv = INVERSES[token[0]]
           if include EXPRESSION_START, tag
             stack.push token
             return 1
           else if include EXPRESSION_END, tag
             if debt[inv] > 0
    -          debt[inv]: - 1
    +          debt[inv] -= 1
               @tokens.splice i, 1
               return 0
             else
    -          match: stack.pop()
    -          mtag:  match[0]
    -          oppos: INVERSES[mtag]
    +          match = stack.pop()
    +          mtag  = match[0]
    +          oppos = INVERSES[mtag]
               return 1 if tag is oppos
    -          debt[mtag]: + 1
    -          val: [oppos, if mtag is 'INDENT' then match[1] else oppos]
    +          debt[mtag] += 1
    +          val = [oppos, if mtag is 'INDENT' then match[1] else oppos]
               if @tokens[i + 2]?[0] is mtag
                 @tokens.splice i + 3, 0, val
                 stack.push(match)
    @@ -218,18 +260,19 @@ rewriting.
                 @tokens.splice i, 0, val
               return 1
           else
    -        return 1
    #

    Generate the indentation tokens, based on another token on the same line.

      indentation: (token) ->
    -    [['INDENT', 2, token[2]], ['OUTDENT', 2, token[2]]]
    #

    Constants

    #

    List of the token pairs that must be balanced.

    BALANCED_PAIRS: [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'],
    -  ['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END']]
    #

    The inverse mappings of BALANCED_PAIRS we're trying to fix up, so we can -look things up from either end.

    INVERSES: {}
    +        return 1
    #

    Generate the indentation tokens, based on another token on the same line.

      indentation: (token) ->
    +    [['INDENT', 2, token[2]], ['OUTDENT', 2, token[2]]]
    #

    Constants

    #

    List of the token pairs that must be balanced.

    BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'],
    +  ['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END']]
    #

    The inverse mappings of BALANCED_PAIRS we're trying to fix up, so we can +look things up from either end.

    INVERSES = {}
     for pair in BALANCED_PAIRS
    -  INVERSES[pair[0]]: pair[1]
    -  INVERSES[pair[1]]: pair[0]
    #

    The tokens that signal the start of a balanced pair.

    EXPRESSION_START: pair[0] for pair in BALANCED_PAIRS
    #

    The tokens that signal the end of a balanced pair.

    EXPRESSION_END:   pair[1] for pair in BALANCED_PAIRS
    #

    Tokens that indicate the close of a clause of an expression.

    EXPRESSION_CLOSE: ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END
    #

    Tokens that, if followed by an IMPLICIT_CALL, indicate a function invocation.

    IMPLICIT_FUNC:  ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@']
    #

    If preceded by an IMPLICIT_FUNC, indicates a function invocation.

    IMPLICIT_CALL:  ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START',
    -                 'TRY', 'DELETE', 'TYPEOF', 'SWITCH',
    -                 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!',
    -                 'THIS', 'NULL',
    -                 '@', '->', '=>', '[', '(', '{']
    #

    Tokens indicating that the implicit call must enclose a block of expressions.

    IMPLICIT_BLOCK: ['->', '=>', '{', '[', ',']
    #

    Tokens that always mark the end of an implicit call for single-liners.

    IMPLICIT_END:   ['IF', 'UNLESS', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'TERMINATOR', 'INDENT'].concat EXPRESSION_END
    #

    Single-line flavors of block expressions that have unclosed endings. -The grammar can't disambiguate them, so we insert the implicit indentation.

    SINGLE_LINERS: ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN']
    -SINGLE_CLOSERS: ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN']
    +  INVERSES[pair[0]] = pair[1]
    +  INVERSES[pair[1]] = pair[0]
    #

    The tokens that signal the start of a balanced pair.

    EXPRESSION_START = pair[0] for pair in BALANCED_PAIRS
    #

    The tokens that signal the end of a balanced pair.

    EXPRESSION_END   = pair[1] for pair in BALANCED_PAIRS
    #

    Tokens that indicate the close of a clause of an expression.

    EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END
    #

    Tokens that, if followed by an IMPLICIT_CALL, indicate a function invocation.

    IMPLICIT_FUNC    = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@']
    #

    If preceded by an IMPLICIT_FUNC, indicates a function invocation.

    IMPLICIT_CALL    = [
    +  'IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS',
    +  'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'THIS', 'NULL',
    +  'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF',
    +  '!', '!!', '@', '->', '=>', '[', '(', '{'
    +]
    #

    Tokens indicating that the implicit call must enclose a block of expressions.

    IMPLICIT_BLOCK   = ['->', '=>', '{', '[', ',']
    #

    Tokens that always mark the end of an implicit call for single-liners.

    IMPLICIT_END     = ['IF', 'UNLESS', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'TERMINATOR', 'INDENT'].concat EXPRESSION_END
    #

    Single-line flavors of block expressions that have unclosed endings. +The grammar can't disambiguate them, so we insert the implicit indentation.

    SINGLE_LINERS    = ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN']
    +SINGLE_CLOSERS   = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN']
     
     
    \ No newline at end of file diff --git a/documentation/docs/scope.html b/documentation/docs/scope.html index 15d85f93..ac719264 100644 --- a/documentation/docs/scope.html +++ b/documentation/docs/scope.html @@ -3,45 +3,45 @@ generate code, you create a tree of scopes in the same shape as the nested function bodies. Each scope knows about the variables declared within it, and has a reference to its parent enclosing scope. In this way, we know which variables are new and need to be declared with var, and which are shared -with the outside.

    #

    Set up exported variables for both Node.js and the browser.

    this.exports: this unless process?
    +with the outside.

    #

    Set up exported variables for both Node.js and the browser.

    this.exports = this unless process?
     
    -exports.Scope: class Scope
    #

    The top-level Scope object.

      @root: null
    #

    Initialize a scope with its parent, for lookups up the chain, +exports.Scope = class Scope

    #

    The top-level Scope object.

      @root: null
    #

    Initialize a scope with its parent, for lookups up the chain, as well as a reference to the Expressions node is belongs to, which is where it should declare its variables, and a reference to the function that it wraps.

      constructor: (parent, expressions, method) ->
    -    [@parent, @expressions, @method]: [parent, expressions, method]
    -    @variables: {}
    +    [@parent, @expressions, @method] = [parent, expressions, method]
    +    @variables = {}
         if @parent
    -      @tempVar: @parent.tempVar
    +      @tempVar = @parent.tempVar
         else
    -      Scope.root: this
    -      @tempVar: '_a'
    #

    Look up a variable name in lexical scope, and declare it if it does not + Scope.root = this + @tempVar = '_a'

    #

    Look up a variable name in lexical scope, and declare it if it does not already exist.

      find: (name) ->
         return true if @check name
    -    @variables[name]: 'var'
    +    @variables[name] = 'var'
         false
    #

    Test variables and return true the first time fn(v, k) returns true

      any: (fn) ->
         for v, k of @variables when fn(v, k)
           return true
         return false
    #

    Reserve a variable name as originating from a function parameter for this scope. No var required for internal references.

      parameter: (name) ->
    -    @variables[name]: 'param'
    #

    Just check to see if a variable has already been declared, without reserving.

      check: (name) ->
    +    @variables[name] = 'param'
    #

    Just check to see if a variable has already been declared, without reserving.

      check: (name) ->
         return true if @variables.hasOwnProperty name
         !!(@parent and @parent.check(name))
    #

    If we need to store an intermediate result, find an available name for a compiler-generated variable. _a, _b, and so on...

      freeVariable: ->
         while @check @tempVar
    -      ordinal: 1 + parseInt @tempVar.substr(1), 36
    -      @tempVar: '_' + ordinal.toString(36).replace(/\d/g, 'a')
    -    @variables[@tempVar]: 'var'
    +      ordinal = 1 + parseInt @tempVar.substr(1), 36
    +      @tempVar = '_' + ordinal.toString(36).replace(/\d/g, 'a')
    +    @variables[@tempVar] = 'var'
         @tempVar
    #

    Ensure that an assignment is made at the top of this scope (or at the top-level scope, if requested).

      assign: (name, value) ->
    -    @variables[name]: {value: value, assigned: true}
    #

    Does this scope reference any variables that need to be declared in the + @variables[name] = value: value, assigned: true

    #

    Does this scope reference any variables that need to be declared in the given function body?

      hasDeclarations: (body) ->
         body is @expressions and @any (k, val) -> val is 'var'
    #

    Does this scope reference any assignments that need to be declared at the top of the given function body?

      hasAssignments: (body) ->
         body is @expressions and @any (k, val) -> val.assigned
    #

    Return the list of variables first declared in this scope.

      declaredVariables: ->
         (key for key, val of @variables when val is 'var').sort()
    #

    Return the list of assignments that are supposed to be made at the top of this scope.

      assignedVariables: ->
    -    "$key = $val.value" for key, val of @variables when val.assigned
    #

    Compile the JavaScript for all of the variable declarations in this scope.

      compiledDeclarations: ->
    +    "#key = #val.value" for key, val of @variables when val.assigned
    #

    Compile the JavaScript for all of the variable declarations in this scope.

      compiledDeclarations: ->
         @declaredVariables().join ', '
    #

    Compile the JavaScript for all of the variable assignments in this scope.

      compiledAssignments: ->
         @assignedVariables().join ', '
     
    diff --git a/documentation/index.html.erb b/documentation/index.html.erb
    index 1ec16f46..68cfad8c 100644
    --- a/documentation/index.html.erb
    +++ b/documentation/index.html.erb
    @@ -72,7 +72,7 @@
           
    -
    @@ -128,7 +128,7 @@ alert reverse '.eeffoC yrT'

    Latest Version: - 0.7.2 + 0.9.0

    @@ -174,7 +174,7 @@ alert reverse '.eeffoC yrT'

    Then clone the CoffeeScript source repository from GitHub, or download the latest - release: 0.7.2. + release: 0.9.0. To install the CoffeeScript compiler system-wide under /usr/local, open the directory and run:

    @@ -1237,15 +1237,15 @@ coffee --print app/scripts/*.coffee > concatenation.js