coffee-script 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/coffee-script.gemspec +3 -3
- data/examples/code.coffee +43 -43
- data/examples/documents.coffee +17 -17
- data/examples/poignant.coffee +22 -22
- data/examples/underscore.coffee +457 -416
- data/lib/coffee-script.rb +2 -1
- data/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage +23 -4
- data/lib/coffee_script/command_line.rb +5 -4
- data/lib/coffee_script/grammar.y +99 -78
- data/lib/coffee_script/lexer.rb +91 -47
- data/lib/coffee_script/narwhal/coffee-script.coffee +16 -15
- data/lib/coffee_script/narwhal/{js → lib}/coffee-script.js +22 -17
- data/lib/coffee_script/narwhal/{js → lib/coffee-script}/loader.js +6 -4
- data/lib/coffee_script/narwhal/loader.coffee +3 -3
- data/lib/coffee_script/nodes.rb +307 -255
- data/lib/coffee_script/parse_error.rb +3 -2
- data/lib/coffee_script/parser.output +10284 -9773
- data/lib/coffee_script/parser.rb +1286 -1141
- data/lib/coffee_script/rewriter.rb +208 -0
- data/lib/coffee_script/scope.rb +15 -10
- data/package.json +9 -0
- metadata +6 -6
- data/lib/coffee_script/narwhal/js/launcher.js +0 -3
- data/lib/coffee_script/narwhal/launcher.coffee +0 -1
    
        data/lib/coffee_script/lexer.rb
    CHANGED
    
    | @@ -12,69 +12,80 @@ module CoffeeScript | |
| 12 12 | 
             
                              "new", "return",
         | 
| 13 13 | 
             
                              "try", "catch", "finally", "throw",
         | 
| 14 14 | 
             
                              "break", "continue",
         | 
| 15 | 
            -
                              "for", "in", "while",
         | 
| 15 | 
            +
                              "for", "in", "by", "where", "while",
         | 
| 16 16 | 
             
                              "switch", "when",
         | 
| 17 17 | 
             
                              "super", "extends",
         | 
| 18 18 | 
             
                              "delete", "instanceof", "typeof"]
         | 
| 19 19 |  | 
| 20 20 | 
             
                # Token matching regexes.
         | 
| 21 21 | 
             
                IDENTIFIER = /\A([a-zA-Z$_]\w*)/
         | 
| 22 | 
            -
                NUMBER     = /\A( | 
| 22 | 
            +
                NUMBER     = /\A(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
         | 
| 23 23 | 
             
                STRING     = /\A(""|''|"(.*?)[^\\]"|'(.*?)[^\\]')/m
         | 
| 24 24 | 
             
                JS         = /\A(``|`(.*?)[^\\]`)/m
         | 
| 25 25 | 
             
                OPERATOR   = /\A([+\*&|\/\-%=<>:!]+)/
         | 
| 26 | 
            -
                WHITESPACE = /\A([ \t | 
| 27 | 
            -
                 | 
| 28 | 
            -
                COMMENT    = /\A((#[^\n]*\s*)+)/m
         | 
| 26 | 
            +
                WHITESPACE = /\A([ \t]+)/
         | 
| 27 | 
            +
                COMMENT    = /\A(((\n?[ \t]*)?#.*$)+)/
         | 
| 29 28 | 
             
                CODE       = /\A(=>)/
         | 
| 30 29 | 
             
                REGEX      = /\A(\/(.*?)[^\\]\/[imgy]{0,4})/
         | 
| 30 | 
            +
                MULTI_DENT = /\A((\n([ \t]*)?)+)/
         | 
| 31 | 
            +
                LAST_DENT  = /\n([ \t]*)/
         | 
| 32 | 
            +
                ASSIGNMENT = /\A(:|=)\Z/
         | 
| 31 33 |  | 
| 32 34 | 
             
                # Token cleaning regexes.
         | 
| 33 35 | 
             
                JS_CLEANER = /(\A`|`\Z)/
         | 
| 34 36 | 
             
                MULTILINER = /\n/
         | 
| 35 37 | 
             
                COMMENT_CLEANER = /(^\s*#|\n\s*$)/
         | 
| 38 | 
            +
                NO_NEWLINE = /\A([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)\Z/
         | 
| 36 39 |  | 
| 37 | 
            -
                # Tokens  | 
| 38 | 
            -
                 | 
| 39 | 
            -
             | 
| 40 | 
            -
                 | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
                 | 
| 40 | 
            +
                # Tokens which a regular expression will never immediately follow, but which
         | 
| 41 | 
            +
                # a division operator might.
         | 
| 42 | 
            +
                # See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
         | 
| 43 | 
            +
                NOT_REGEX  = [
         | 
| 44 | 
            +
                  :IDENTIFIER, :NUMBER, :REGEX, :STRING,
         | 
| 45 | 
            +
                  ')', '++', '--', ']', '}',
         | 
| 46 | 
            +
                  :FALSE, :NULL, :THIS, :TRUE
         | 
| 47 | 
            +
                ]
         | 
| 45 48 |  | 
| 46 49 | 
             
                # Scan by attempting to match tokens one character at a time. Slow and steady.
         | 
| 47 50 | 
             
                def tokenize(code)
         | 
| 48 51 | 
             
                  @code = code.chomp  # Cleanup code by remove extra line breaks
         | 
| 49 52 | 
             
                  @i = 0              # Current character position we're parsing
         | 
| 50 53 | 
             
                  @line = 1           # The current line.
         | 
| 54 | 
            +
                  @indent = 0         # The current indent level.
         | 
| 55 | 
            +
                  @indents = []       # The stack of all indent levels we are currently within.
         | 
| 51 56 | 
             
                  @tokens = []        # Collection of all parsed tokens in the form [:TOKEN_TYPE, value]
         | 
| 52 57 | 
             
                  while @i < @code.length
         | 
| 53 58 | 
             
                    @chunk = @code[@i..-1]
         | 
| 54 59 | 
             
                    extract_next_token
         | 
| 55 60 | 
             
                  end
         | 
| 56 | 
            -
                  @tokens
         | 
| 61 | 
            +
                  puts "original stream: #{@tokens.inspect}" if ENV['VERBOSE']
         | 
| 62 | 
            +
                  close_indentation
         | 
| 63 | 
            +
                  Rewriter.new.rewrite(@tokens)
         | 
| 57 64 | 
             
                end
         | 
| 58 65 |  | 
| 59 | 
            -
                # At every position, run this list of  | 
| 60 | 
            -
                # any of them succeed.
         | 
| 66 | 
            +
                # At every position, run through this list of attempted matches,
         | 
| 67 | 
            +
                # short-circuiting if any of them succeed.
         | 
| 61 68 | 
             
                def extract_next_token
         | 
| 62 69 | 
             
                  return if identifier_token
         | 
| 63 70 | 
             
                  return if number_token
         | 
| 64 71 | 
             
                  return if string_token
         | 
| 65 72 | 
             
                  return if js_token
         | 
| 66 73 | 
             
                  return if regex_token
         | 
| 74 | 
            +
                  return if indent_token
         | 
| 67 75 | 
             
                  return if comment_token
         | 
| 68 76 | 
             
                  return if whitespace_token
         | 
| 69 77 | 
             
                  return    literal_token
         | 
| 70 78 | 
             
                end
         | 
| 71 79 |  | 
| 80 | 
            +
                # Tokenizers ==========================================================
         | 
| 81 | 
            +
             | 
| 72 82 | 
             
                # Matches identifying literals: variables, keywords, method names, etc.
         | 
| 73 83 | 
             
                def identifier_token
         | 
| 74 84 | 
             
                  return false unless identifier = @chunk[IDENTIFIER, 1]
         | 
| 75 | 
            -
                  # Keywords are special identifiers tagged with their own name, | 
| 76 | 
            -
                  # in an [:IF, "if"] token
         | 
| 85 | 
            +
                  # Keywords are special identifiers tagged with their own name,
         | 
| 86 | 
            +
                  # 'if' will result in an [:IF, "if"] token.
         | 
| 77 87 | 
             
                  tag = KEYWORDS.include?(identifier) ? identifier.upcase.to_sym : :IDENTIFIER
         | 
| 88 | 
            +
                  tag = :LEADING_WHEN if tag == :WHEN && [:OUTDENT, :INDENT, "\n"].include?(last_tag)
         | 
| 78 89 | 
             
                  @tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2][1] == '.')
         | 
| 79 90 | 
             
                  token(tag, identifier)
         | 
| 80 91 | 
             
                  @i += identifier.length
         | 
| @@ -108,6 +119,7 @@ module CoffeeScript | |
| 108 119 | 
             
                # Matches regular expression literals.
         | 
| 109 120 | 
             
                def regex_token
         | 
| 110 121 | 
             
                  return false unless regex = @chunk[REGEX, 1]
         | 
| 122 | 
            +
                  return false if NOT_REGEX.include?(last_tag)
         | 
| 111 123 | 
             
                  token(:REGEX, regex)
         | 
| 112 124 | 
             
                  @i += regex.length
         | 
| 113 125 | 
             
                end
         | 
| @@ -121,73 +133,105 @@ module CoffeeScript | |
| 121 133 | 
             
                  @i += comment.length
         | 
| 122 134 | 
             
                end
         | 
| 123 135 |  | 
| 136 | 
            +
                # Record tokens for indentation differing from the previous line.
         | 
| 137 | 
            +
                def indent_token
         | 
| 138 | 
            +
                  return false unless indent = @chunk[MULTI_DENT, 1]
         | 
| 139 | 
            +
                  @line += indent.scan(MULTILINER).size
         | 
| 140 | 
            +
                  @i += indent.size
         | 
| 141 | 
            +
                  return suppress_newlines(indent) if last_value.to_s.match(NO_NEWLINE) && last_value != "=>"
         | 
| 142 | 
            +
                  size = indent.scan(LAST_DENT).last.last.length
         | 
| 143 | 
            +
                  return newline_token(indent) if size == @indent
         | 
| 144 | 
            +
                  if size > @indent
         | 
| 145 | 
            +
                    token(:INDENT, size - @indent)
         | 
| 146 | 
            +
                    @indents << (size - @indent)
         | 
| 147 | 
            +
                  else
         | 
| 148 | 
            +
                    outdent_token(@indent - size)
         | 
| 149 | 
            +
                  end
         | 
| 150 | 
            +
                  @indent = size
         | 
| 151 | 
            +
                end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                # Record an oudent token or tokens, if we're moving back inwards past
         | 
| 154 | 
            +
                # multiple recorded indents.
         | 
| 155 | 
            +
                def outdent_token(move_out)
         | 
| 156 | 
            +
                  while move_out > 0 && !@indents.empty?
         | 
| 157 | 
            +
                    last_indent = @indents.pop
         | 
| 158 | 
            +
                    token(:OUTDENT, last_indent)
         | 
| 159 | 
            +
                    move_out -= last_indent
         | 
| 160 | 
            +
                  end
         | 
| 161 | 
            +
                  token("\n", "\n")
         | 
| 162 | 
            +
                end
         | 
| 163 | 
            +
             | 
| 124 164 | 
             
                # Matches and consumes non-meaningful whitespace.
         | 
| 125 165 | 
             
                def whitespace_token
         | 
| 126 166 | 
             
                  return false unless whitespace = @chunk[WHITESPACE, 1]
         | 
| 127 167 | 
             
                  @i += whitespace.length
         | 
| 128 168 | 
             
                end
         | 
| 129 169 |  | 
| 170 | 
            +
                # Multiple newlines get merged together.
         | 
| 171 | 
            +
                # Use a trailing \ to escape newlines.
         | 
| 172 | 
            +
                def newline_token(newlines)
         | 
| 173 | 
            +
                  token("\n", "\n") unless last_value == "\n"
         | 
| 174 | 
            +
                  true
         | 
| 175 | 
            +
                end
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                # Tokens to explicitly escape newlines are removed once their job is done.
         | 
| 178 | 
            +
                def suppress_newlines(newlines)
         | 
| 179 | 
            +
                  @tokens.pop if last_value == "\\"
         | 
| 180 | 
            +
                  true
         | 
| 181 | 
            +
                end
         | 
| 182 | 
            +
             | 
| 130 183 | 
             
                # We treat all other single characters as a token. Eg.: ( ) , . !
         | 
| 131 184 | 
             
                # Multi-character operators are also literal tokens, so that Racc can assign
         | 
| 132 | 
            -
                # the proper order of operations. | 
| 133 | 
            -
                # Use a trailing \ to escape newlines.
         | 
| 185 | 
            +
                # the proper order of operations.
         | 
| 134 186 | 
             
                def literal_token
         | 
| 135 | 
            -
                  value = @chunk[NEWLINE, 1]
         | 
| 136 | 
            -
                  if value
         | 
| 137 | 
            -
                    @line += value.length
         | 
| 138 | 
            -
                    token("\n", "\n") unless ["\n", "\\"].include?(last_value)
         | 
| 139 | 
            -
                    @tokens.pop if last_value == "\\"
         | 
| 140 | 
            -
                    return @i += value.length
         | 
| 141 | 
            -
                  end
         | 
| 142 187 | 
             
                  value = @chunk[OPERATOR, 1]
         | 
| 143 188 | 
             
                  tag_parameters if value && value.match(CODE)
         | 
| 144 189 | 
             
                  value ||= @chunk[0,1]
         | 
| 145 | 
            -
                   | 
| 146 | 
            -
                  remove_leading_newlines if EXP_END.include?(value)
         | 
| 147 | 
            -
                  tag = ASSIGN.include?(value) ? :ASSIGN : value
         | 
| 190 | 
            +
                  tag = value.match(ASSIGNMENT) ? :ASSIGN : value
         | 
| 148 191 | 
             
                  token(tag, value)
         | 
| 149 192 | 
             
                  @i += value.length
         | 
| 150 193 | 
             
                end
         | 
| 151 194 |  | 
| 195 | 
            +
                # Helpers ==========================================================
         | 
| 196 | 
            +
             | 
| 152 197 | 
             
                # Add a token to the results, taking note of the line number, and
         | 
| 153 198 | 
             
                # immediately-preceding comment.
         | 
| 154 199 | 
             
                def token(tag, value)
         | 
| 155 200 | 
             
                  @tokens << [tag, Value.new(value, @line)]
         | 
| 156 201 | 
             
                end
         | 
| 157 202 |  | 
| 158 | 
            -
                # Peek at the previous token.
         | 
| 203 | 
            +
                # Peek at the previous token's value.
         | 
| 159 204 | 
             
                def last_value
         | 
| 160 205 | 
             
                  @tokens.last && @tokens.last[1]
         | 
| 161 206 | 
             
                end
         | 
| 162 207 |  | 
| 208 | 
            +
                # Peek at the previous token's tag.
         | 
| 209 | 
            +
                def last_tag
         | 
| 210 | 
            +
                  @tokens.last && @tokens.last[0]
         | 
| 211 | 
            +
                end
         | 
| 212 | 
            +
             | 
| 163 213 | 
             
                # A source of ambiguity in our grammar was parameter lists in function
         | 
| 164 214 | 
             
                # definitions (as opposed to argument lists in function calls). Tag
         | 
| 165 | 
            -
                # parameter identifiers in order to avoid this.
         | 
| 215 | 
            +
                # parameter identifiers in order to avoid this. Also, parameter lists can
         | 
| 216 | 
            +
                # make use of splats.
         | 
| 166 217 | 
             
                def tag_parameters
         | 
| 167 | 
            -
                   | 
| 218 | 
            +
                  i = 0
         | 
| 168 219 | 
             
                  loop do
         | 
| 169 | 
            -
                     | 
| 220 | 
            +
                    i -= 1
         | 
| 221 | 
            +
                    tok = @tokens[i]
         | 
| 170 222 | 
             
                    return if !tok
         | 
| 171 223 | 
             
                    next if tok[0] == ','
         | 
| 224 | 
            +
                    next tok[0] = :PARAM_SPLAT if tok[0] == '*'
         | 
| 172 225 | 
             
                    return if tok[0] != :IDENTIFIER
         | 
| 173 226 | 
             
                    tok[0] = :PARAM
         | 
| 174 227 | 
             
                  end
         | 
| 175 228 | 
             
                end
         | 
| 176 229 |  | 
| 177 | 
            -
                #  | 
| 178 | 
            -
                 | 
| 179 | 
            -
             | 
| 180 | 
            -
                   | 
| 181 | 
            -
                    @line += newlines.length
         | 
| 182 | 
            -
                    @i += newlines.length
         | 
| 183 | 
            -
                  end
         | 
| 184 | 
            -
                end
         | 
| 185 | 
            -
             | 
| 186 | 
            -
                # Discard newlines immediately before this point.
         | 
| 187 | 
            -
                def remove_leading_newlines
         | 
| 188 | 
            -
                  @tokens.pop if last_value == "\n"
         | 
| 230 | 
            +
                # Close up all remaining open blocks. IF the first token is an indent,
         | 
| 231 | 
            +
                # axe it.
         | 
| 232 | 
            +
                def close_indentation
         | 
| 233 | 
            +
                  outdent_token(@indent)
         | 
| 189 234 | 
             
                end
         | 
| 190 235 |  | 
| 191 236 | 
             
              end
         | 
| 192 | 
            -
             | 
| 193 237 | 
             
            end
         | 
| @@ -15,15 +15,16 @@ coffeePath: File.path(module.path).dirname().dirname().dirname().dirname().dirna | |
| 15 15 | 
             
            checkForErrors: coffeeProcess =>
         | 
| 16 16 | 
             
              return true if coffeeProcess.wait() is 0
         | 
| 17 17 | 
             
              system.stderr.print(coffeeProcess.stderr.read())
         | 
| 18 | 
            -
              throw new Error("CoffeeScript compile error") | 
| 18 | 
            +
              throw new Error("CoffeeScript compile error")
         | 
| 19 19 |  | 
| 20 20 | 
             
            # Run a simple REPL, round-tripping to the CoffeeScript compiler for every
         | 
| 21 21 | 
             
            # command.
         | 
| 22 22 | 
             
            exports.run: args =>
         | 
| 23 | 
            -
              args.shift()
         | 
| 24 23 | 
             
              if args.length
         | 
| 25 | 
            -
                 | 
| 26 | 
            -
             | 
| 24 | 
            +
                for path, i in args
         | 
| 25 | 
            +
                  exports.evalCS(File.read(path))
         | 
| 26 | 
            +
                  delete args[i]
         | 
| 27 | 
            +
                return true
         | 
| 27 28 |  | 
| 28 29 | 
             
              while true
         | 
| 29 30 | 
             
                try
         | 
| @@ -31,31 +32,31 @@ exports.run: args => | |
| 31 32 | 
             
                  result: exports.evalCS(Readline.readline())
         | 
| 32 33 | 
             
                  print(result) if result isnt undefined
         | 
| 33 34 | 
             
                catch e
         | 
| 34 | 
            -
                  print(e) | 
| 35 | 
            +
                  print(e)
         | 
| 35 36 |  | 
| 36 37 | 
             
            # Compile a given CoffeeScript file into JavaScript.
         | 
| 37 38 | 
             
            exports.compileFile: path =>
         | 
| 38 39 | 
             
              coffee: OS.popen([coffeePath, "--print", "--no-wrap", path])
         | 
| 39 40 | 
             
              checkForErrors(coffee)
         | 
| 40 | 
            -
              coffee.stdout.read() | 
| 41 | 
            +
              coffee.stdout.read()
         | 
| 41 42 |  | 
| 42 43 | 
             
            # Compile a string of CoffeeScript into JavaScript.
         | 
| 43 44 | 
             
            exports.compile: source =>
         | 
| 44 45 | 
             
              coffee: OS.popen([coffeePath, "--eval", "--no-wrap"])
         | 
| 45 46 | 
             
              coffee.stdin.write(source).flush().close()
         | 
| 46 47 | 
             
              checkForErrors(coffee)
         | 
| 47 | 
            -
              coffee.stdout.read() | 
| 48 | 
            +
              coffee.stdout.read()
         | 
| 48 49 |  | 
| 49 50 | 
             
            # Evaluating a string of CoffeeScript first compiles it externally.
         | 
| 50 51 | 
             
            exports.evalCS: source =>
         | 
| 51 | 
            -
              eval(exports.compile(source)) | 
| 52 | 
            +
              eval(exports.compile(source))
         | 
| 52 53 |  | 
| 53 54 | 
             
            # Make a factory for the CoffeeScript environment.
         | 
| 54 55 | 
             
            exports.makeNarwhalFactory: path =>
         | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 56 | 
            +
              code: exports.compileFile(path)
         | 
| 57 | 
            +
              factoryText: "function(require,exports,module,system,print){" + code + "/**/\n}"
         | 
| 58 | 
            +
              if system.engine is "rhino"
         | 
| 59 | 
            +
                Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null)
         | 
| 60 | 
            +
              else
         | 
| 61 | 
            +
                # eval requires parentheses, but parentheses break compileFunction.
         | 
| 62 | 
            +
                eval("(" + factoryText + ")")
         | 
| @@ -1,14 +1,16 @@ | |
| 1 1 | 
             
            (function(){
         | 
| 2 2 | 
             
              var File, OS, Readline, checkForErrors, coffeePath;
         | 
| 3 | 
            -
              // This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee | 
| 4 | 
            -
              //  | 
| 3 | 
            +
              // This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee
         | 
| 4 | 
            +
              // Executes the `coffee` Ruby program to convert from CoffeeScript
         | 
| 5 | 
            +
              // to Javascript. Eventually this will hopefully happen entirely within JS.
         | 
| 6 | 
            +
              // Require external dependencies.
         | 
| 5 7 | 
             
              OS = require('os');
         | 
| 6 8 | 
             
              File = require('file');
         | 
| 7 9 | 
             
              Readline = require('readline');
         | 
| 8 10 | 
             
              // The path to the CoffeeScript Compiler.
         | 
| 9 11 | 
             
              coffeePath = File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee');
         | 
| 10 12 | 
             
              // Our general-purpose error handler.
         | 
| 11 | 
            -
              checkForErrors = function(coffeeProcess) {
         | 
| 13 | 
            +
              checkForErrors = function checkForErrors(coffeeProcess) {
         | 
| 12 14 | 
             
                if (coffeeProcess.wait() === 0) {
         | 
| 13 15 | 
             
                  return true;
         | 
| 14 16 | 
             
                }
         | 
| @@ -17,17 +19,20 @@ | |
| 17 19 | 
             
              };
         | 
| 18 20 | 
             
              // Run a simple REPL, round-tripping to the CoffeeScript compiler for every
         | 
| 19 21 | 
             
              // command.
         | 
| 20 | 
            -
              exports.run = function(args) {
         | 
| 21 | 
            -
                var __a, __b, __c,  | 
| 22 | 
            -
                args.shift();
         | 
| 22 | 
            +
              exports.run = function run(args) {
         | 
| 23 | 
            +
                var __a, __b, __c, i, path, result;
         | 
| 23 24 | 
             
                if (args.length) {
         | 
| 24 25 | 
             
                  __a = args;
         | 
| 25 | 
            -
                   | 
| 26 | 
            -
                  for ( | 
| 27 | 
            -
                     | 
| 28 | 
            -
             | 
| 26 | 
            +
                  __b = [];
         | 
| 27 | 
            +
                  for (i in __a) {
         | 
| 28 | 
            +
                    if (__a.hasOwnProperty(i)) {
         | 
| 29 | 
            +
                      path = __a[i];
         | 
| 30 | 
            +
                      exports.evalCS(File.read(path));
         | 
| 31 | 
            +
                      __c = delete args[i];
         | 
| 32 | 
            +
                      __b.push(__c);
         | 
| 33 | 
            +
                    }
         | 
| 29 34 | 
             
                  }
         | 
| 30 | 
            -
                   | 
| 35 | 
            +
                  __b;
         | 
| 31 36 | 
             
                  return true;
         | 
| 32 37 | 
             
                }
         | 
| 33 38 | 
             
                while (true) {
         | 
| @@ -43,14 +48,14 @@ | |
| 43 48 | 
             
                }
         | 
| 44 49 | 
             
              };
         | 
| 45 50 | 
             
              // Compile a given CoffeeScript file into JavaScript.
         | 
| 46 | 
            -
              exports.compileFile = function(path) {
         | 
| 51 | 
            +
              exports.compileFile = function compileFile(path) {
         | 
| 47 52 | 
             
                var coffee;
         | 
| 48 53 | 
             
                coffee = OS.popen([coffeePath, "--print", "--no-wrap", path]);
         | 
| 49 54 | 
             
                checkForErrors(coffee);
         | 
| 50 55 | 
             
                return coffee.stdout.read();
         | 
| 51 56 | 
             
              };
         | 
| 52 57 | 
             
              // Compile a string of CoffeeScript into JavaScript.
         | 
| 53 | 
            -
              exports.compile = function(source) {
         | 
| 58 | 
            +
              exports.compile = function compile(source) {
         | 
| 54 59 | 
             
                var coffee;
         | 
| 55 60 | 
             
                coffee = OS.popen([coffeePath, "--eval", "--no-wrap"]);
         | 
| 56 61 | 
             
                coffee.stdin.write(source).flush().close();
         | 
| @@ -58,18 +63,18 @@ | |
| 58 63 | 
             
                return coffee.stdout.read();
         | 
| 59 64 | 
             
              };
         | 
| 60 65 | 
             
              // Evaluating a string of CoffeeScript first compiles it externally.
         | 
| 61 | 
            -
              exports.evalCS = function(source) {
         | 
| 66 | 
            +
              exports.evalCS = function evalCS(source) {
         | 
| 62 67 | 
             
                return eval(exports.compile(source));
         | 
| 63 68 | 
             
              };
         | 
| 64 69 | 
             
              // Make a factory for the CoffeeScript environment.
         | 
| 65 | 
            -
              exports.makeNarwhalFactory = function(path) {
         | 
| 70 | 
            +
              exports.makeNarwhalFactory = function makeNarwhalFactory(path) {
         | 
| 66 71 | 
             
                var code, factoryText;
         | 
| 67 72 | 
             
                code = exports.compileFile(path);
         | 
| 68 | 
            -
                factoryText = "function(require,exports,module,system,print){  | 
| 73 | 
            +
                factoryText = "function(require,exports,module,system,print){" + code + "/**/\n}";
         | 
| 69 74 | 
             
                if (system.engine === "rhino") {
         | 
| 70 75 | 
             
                  return Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null);
         | 
| 71 76 | 
             
                } else {
         | 
| 72 | 
            -
                  // eval requires  | 
| 77 | 
            +
                  // eval requires parentheses, but parentheses break compileFunction.
         | 
| 73 78 | 
             
                  return eval("(" + factoryText + ")");
         | 
| 74 79 | 
             
                }
         | 
| 75 80 | 
             
              };
         | 
| @@ -6,12 +6,14 @@ | |
| 6 6 | 
             
              };
         | 
| 7 7 | 
             
              loader = {
         | 
| 8 8 | 
             
                // Reload the coffee-script environment from source.
         | 
| 9 | 
            -
                reload: function(topId, path) {
         | 
| 10 | 
            -
                  coffeescript = coffeescript || require(' | 
| 11 | 
            -
                  return (factories[topId] =  | 
| 9 | 
            +
                reload: function reload(topId, path) {
         | 
| 10 | 
            +
                  coffeescript = coffeescript || require('coffee-script');
         | 
| 11 | 
            +
                  return (factories[topId] = function() {
         | 
| 12 | 
            +
                    return coffeescript.makeNarwhalFactory(path);
         | 
| 13 | 
            +
                  });
         | 
| 12 14 | 
             
                },
         | 
| 13 15 | 
             
                // Ensure that the coffee-script environment is loaded.
         | 
| 14 | 
            -
                load: function(topId, path) {
         | 
| 16 | 
            +
                load: function load(topId, path) {
         | 
| 15 17 | 
             
                  return factories[topId] = factories[topId] || this.reload(topId, path);
         | 
| 16 18 | 
             
                }
         | 
| 17 19 | 
             
              };
         | 
| @@ -7,12 +7,12 @@ loader: { | |
| 7 7 |  | 
| 8 8 | 
             
              # Reload the coffee-script environment from source.
         | 
| 9 9 | 
             
              reload: topId, path =>
         | 
| 10 | 
            -
                coffeescript ||= require(' | 
| 11 | 
            -
                factories[topId]: coffeescript.makeNarwhalFactory(path) | 
| 10 | 
            +
                coffeescript ||= require('coffee-script')
         | 
| 11 | 
            +
                factories[topId]: => coffeescript.makeNarwhalFactory(path)
         | 
| 12 12 |  | 
| 13 13 | 
             
              # Ensure that the coffee-script environment is loaded.
         | 
| 14 14 | 
             
              load: topId, path =>
         | 
| 15 | 
            -
                factories[topId] ||= this.reload(topId, path) | 
| 15 | 
            +
                factories[topId] ||= this.reload(topId, path)
         | 
| 16 16 |  | 
| 17 17 | 
             
            }
         | 
| 18 18 |  | 
    
        data/lib/coffee_script/nodes.rb
    CHANGED
    
    | @@ -11,17 +11,11 @@ module CoffeeScript | |
| 11 11 | 
             
                  class_eval "def statement?; true; end"
         | 
| 12 12 | 
             
                end
         | 
| 13 13 |  | 
| 14 | 
            -
                # Tag this node as  | 
| 15 | 
            -
                #  | 
| 16 | 
            -
                def self. | 
| 17 | 
            -
                   | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
                # Tag this node as having a custom assignment, meaning that instead of
         | 
| 21 | 
            -
                # assigning it to a variable name from the outside, you pass it the variable
         | 
| 22 | 
            -
                # name and let it take care of it.
         | 
| 23 | 
            -
                def self.custom_assign
         | 
| 24 | 
            -
                  class_eval "def custom_assign?; true; end"
         | 
| 14 | 
            +
                # Tag this node as a statement that cannot be transformed into an expression.
         | 
| 15 | 
            +
                # (break, continue, etc.) It doesn't make sense to try to transform it.
         | 
| 16 | 
            +
                def self.statement_only
         | 
| 17 | 
            +
                  statement
         | 
| 18 | 
            +
                  class_eval "def statement_only?; true; end"
         | 
| 25 19 | 
             
                end
         | 
| 26 20 |  | 
| 27 21 | 
             
                def write(code)
         | 
| @@ -29,16 +23,27 @@ module CoffeeScript | |
| 29 23 | 
             
                  code
         | 
| 30 24 | 
             
                end
         | 
| 31 25 |  | 
| 26 | 
            +
                # This is extremely important -- we convert JS statements into expressions
         | 
| 27 | 
            +
                # by wrapping them in a closure, only if it's possible, and we're not at
         | 
| 28 | 
            +
                # the top level of a block (which would be unnecessary), and we haven't
         | 
| 29 | 
            +
                # already been asked to return the result.
         | 
| 32 30 | 
             
                def compile(o={})
         | 
| 33 31 | 
             
                  @options = o.dup
         | 
| 32 | 
            +
                  top = self.is_a?(ForNode) ? @options[:top] : @options.delete(:top)
         | 
| 33 | 
            +
                  closure = statement? && !statement_only? && !top && !@options[:return]
         | 
| 34 | 
            +
                  closure ? compile_closure(@options) : compile_node(@options)
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def compile_closure(o={})
         | 
| 38 | 
            +
                  indent = o[:indent]
         | 
| 39 | 
            +
                  o[:indent] += TAB
         | 
| 40 | 
            +
                  "(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()"
         | 
| 34 41 | 
             
                end
         | 
| 35 42 |  | 
| 36 43 | 
             
                # Default implementations of the common node methods.
         | 
| 37 | 
            -
                def unwrap; | 
| 38 | 
            -
                def  | 
| 39 | 
            -
                def  | 
| 40 | 
            -
                def custom_return?;                         false;  end
         | 
| 41 | 
            -
                def custom_assign?;                         false;  end
         | 
| 44 | 
            +
                def unwrap;           self;   end
         | 
| 45 | 
            +
                def statement?;       false;  end
         | 
| 46 | 
            +
                def statement_only?;  false;  end
         | 
| 42 47 | 
             
              end
         | 
| 43 48 |  | 
| 44 49 | 
             
              # A collection of nodes, each one representing an expression.
         | 
| @@ -49,12 +54,13 @@ module CoffeeScript | |
| 49 54 | 
             
                STRIP_TRAILING_WHITESPACE = /\s+$/
         | 
| 50 55 |  | 
| 51 56 | 
             
                # Wrap up a node as an Expressions, unless it already is.
         | 
| 52 | 
            -
                def self.wrap( | 
| 53 | 
            -
                   | 
| 57 | 
            +
                def self.wrap(*nodes)
         | 
| 58 | 
            +
                  return nodes[0] if nodes.length == 1 && nodes[0].is_a?(Expressions)
         | 
| 59 | 
            +
                  Expressions.new(*nodes)
         | 
| 54 60 | 
             
                end
         | 
| 55 61 |  | 
| 56 | 
            -
                def initialize(nodes)
         | 
| 57 | 
            -
                  @expressions = nodes
         | 
| 62 | 
            +
                def initialize(*nodes)
         | 
| 63 | 
            +
                  @expressions = nodes.flatten
         | 
| 58 64 | 
             
                end
         | 
| 59 65 |  | 
| 60 66 | 
             
                # Tack an expression onto the end of this node.
         | 
| @@ -63,6 +69,11 @@ module CoffeeScript | |
| 63 69 | 
             
                  self
         | 
| 64 70 | 
             
                end
         | 
| 65 71 |  | 
| 72 | 
            +
                def unshift(node)
         | 
| 73 | 
            +
                  @expressions.unshift(node)
         | 
| 74 | 
            +
                  self
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 66 77 | 
             
                # If this Expressions consists of a single node, pull it back out.
         | 
| 67 78 | 
             
                def unwrap
         | 
| 68 79 | 
             
                  @expressions.length == 1 ? @expressions.first : self
         | 
| @@ -74,47 +85,48 @@ module CoffeeScript | |
| 74 85 | 
             
                  node == @expressions[@last_index]
         | 
| 75 86 | 
             
                end
         | 
| 76 87 |  | 
| 77 | 
            -
                 | 
| 78 | 
            -
             | 
| 79 | 
            -
                  indent = o[:no_wrap] ? '' : TAB
         | 
| 80 | 
            -
                  code = compile(o.merge(:indent => indent, :scope => Scope.new), o[:no_wrap] ? nil : :code)
         | 
| 81 | 
            -
                  code.gsub!(STRIP_TRAILING_WHITESPACE, '')
         | 
| 82 | 
            -
                  o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
         | 
| 88 | 
            +
                def compile(o={})
         | 
| 89 | 
            +
                  o[:scope] ? super(o) : compile_root(o)
         | 
| 83 90 | 
             
                end
         | 
| 84 91 |  | 
| 85 | 
            -
                # The extra fancy is to handle pushing down returns  | 
| 86 | 
            -
                #  | 
| 87 | 
            -
                #  | 
| 88 | 
            -
                 | 
| 89 | 
            -
                def compile(options={}, parent=nil)
         | 
| 90 | 
            -
                  return root_compile(options) unless options[:scope]
         | 
| 92 | 
            +
                # The extra fancy is to handle pushing down returns to the final lines of
         | 
| 93 | 
            +
                # inner statements. Variables first defined within the Expressions body
         | 
| 94 | 
            +
                # have their declarations pushed up top of the closest scope.
         | 
| 95 | 
            +
                def compile_node(options={})
         | 
| 91 96 | 
             
                  compiled = @expressions.map do |node|
         | 
| 92 | 
            -
                    o =  | 
| 93 | 
            -
                     | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
                          "#{o[:indent]}return #{node.compile(o)}#{node.line_ending}"
         | 
| 100 | 
            -
                        end
         | 
| 101 | 
            -
                      elsif o[:assign]
         | 
| 102 | 
            -
                        if node.statement? || node.custom_assign?
         | 
| 103 | 
            -
                          "#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
         | 
| 104 | 
            -
                        else
         | 
| 105 | 
            -
                          "#{o[:indent]}#{AssignNode.new(o[:assign], node).compile(o)};"
         | 
| 106 | 
            -
                        end
         | 
| 97 | 
            +
                    o = options.dup
         | 
| 98 | 
            +
                    returns = o.delete(:return)
         | 
| 99 | 
            +
                    if last?(node) && returns && !node.statement_only?
         | 
| 100 | 
            +
                      if node.statement?
         | 
| 101 | 
            +
                        node.compile(o.merge(:return => true))
         | 
| 102 | 
            +
                      else
         | 
| 103 | 
            +
                        "#{o[:indent]}return #{node.compile(o)};"
         | 
| 107 104 | 
             
                      end
         | 
| 108 105 | 
             
                    else
         | 
| 109 | 
            -
                       | 
| 110 | 
            -
                       | 
| 106 | 
            +
                      ending = node.statement? ? '' : ';'
         | 
| 107 | 
            +
                      indent = node.statement? ? '' : o[:indent]
         | 
| 108 | 
            +
                      "#{indent}#{node.compile(o.merge(:top => true))}#{ending}"
         | 
| 111 109 | 
             
                    end
         | 
| 112 110 | 
             
                  end
         | 
| 113 | 
            -
                   | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 111 | 
            +
                  write(compiled.join("\n"))
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                # If this is the top-level Expressions, wrap everything in a safety closure.
         | 
| 115 | 
            +
                def compile_root(o={})
         | 
| 116 | 
            +
                  indent = o[:no_wrap] ? '' : TAB
         | 
| 117 | 
            +
                  o.merge!(:indent => indent, :scope => Scope.new(nil, self))
         | 
| 118 | 
            +
                  code = o[:no_wrap] ? compile_node(o) : compile_with_declarations(o)
         | 
| 119 | 
            +
                  code.gsub!(STRIP_TRAILING_WHITESPACE, '')
         | 
| 120 | 
            +
                  o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                def compile_with_declarations(o={})
         | 
| 124 | 
            +
                  code  = compile_node(o)
         | 
| 125 | 
            +
                  decls = ''
         | 
| 126 | 
            +
                  decls = "#{o[:indent]}var #{o[:scope].declared_variables.join(', ')};\n" if o[:scope].declarations?(self)
         | 
| 127 | 
            +
                  decls + code
         | 
| 117 128 | 
             
                end
         | 
| 129 | 
            +
             | 
| 118 130 | 
             
              end
         | 
| 119 131 |  | 
| 120 132 | 
             
              # Literals are static values that have a Ruby representation, eg.: a string, a number,
         | 
| @@ -131,21 +143,18 @@ module CoffeeScript | |
| 131 143 | 
             
                def statement?
         | 
| 132 144 | 
             
                  STATEMENTS.include?(@value.to_s)
         | 
| 133 145 | 
             
                end
         | 
| 146 | 
            +
                alias_method :statement_only?, :statement?
         | 
| 134 147 |  | 
| 135 | 
            -
                def  | 
| 136 | 
            -
                   | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
                def compile(o={})
         | 
| 140 | 
            -
                  o = super(o)
         | 
| 141 | 
            -
                  write(@value.to_s)
         | 
| 148 | 
            +
                def compile_node(o)
         | 
| 149 | 
            +
                  indent = statement? ? o[:indent] : ''
         | 
| 150 | 
            +
                  ending = statement? ? ';' : ''
         | 
| 151 | 
            +
                  write(indent + @value.to_s + ending)
         | 
| 142 152 | 
             
                end
         | 
| 143 153 | 
             
              end
         | 
| 144 154 |  | 
| 145 155 | 
             
              # Try to return your expression, or tell it to return itself.
         | 
| 146 156 | 
             
              class ReturnNode < Node
         | 
| 147 | 
            -
                 | 
| 148 | 
            -
                custom_return
         | 
| 157 | 
            +
                statement_only
         | 
| 149 158 |  | 
| 150 159 | 
             
                attr_reader :expression
         | 
| 151 160 |  | 
| @@ -153,32 +162,23 @@ module CoffeeScript | |
| 153 162 | 
             
                  @expression = expression
         | 
| 154 163 | 
             
                end
         | 
| 155 164 |  | 
| 156 | 
            -
                def  | 
| 157 | 
            -
                  @expression. | 
| 158 | 
            -
                end
         | 
| 159 | 
            -
             | 
| 160 | 
            -
                def compile(o={})
         | 
| 161 | 
            -
                  o = super(o)
         | 
| 162 | 
            -
                  return write(@expression.compile(o.merge(:return => true))) if @expression.custom_return?
         | 
| 165 | 
            +
                def compile_node(o)
         | 
| 166 | 
            +
                  return write(@expression.compile(o.merge(:return => true))) if @expression.statement?
         | 
| 163 167 | 
             
                  compiled = @expression.compile(o)
         | 
| 164 | 
            -
                  write(@expression.statement? ? "#{compiled}\n#{indent}return null" : "return #{compiled}")
         | 
| 168 | 
            +
                  write(@expression.statement? ? "#{compiled}\n#{o[:indent]}return null;" : "#{o[:indent]}return #{compiled};")
         | 
| 165 169 | 
             
                end
         | 
| 166 170 | 
             
              end
         | 
| 167 171 |  | 
| 168 172 | 
             
              # Pass through CoffeeScript comments into JavaScript comments at the
         | 
| 169 173 | 
             
              # same position.
         | 
| 170 174 | 
             
              class CommentNode < Node
         | 
| 171 | 
            -
                 | 
| 175 | 
            +
                statement_only
         | 
| 172 176 |  | 
| 173 177 | 
             
                def initialize(lines)
         | 
| 174 178 | 
             
                  @lines = lines.value
         | 
| 175 179 | 
             
                end
         | 
| 176 180 |  | 
| 177 | 
            -
                def  | 
| 178 | 
            -
                  ''
         | 
| 179 | 
            -
                end
         | 
| 180 | 
            -
             | 
| 181 | 
            -
                def compile(o={})
         | 
| 181 | 
            +
                def compile_node(o={})
         | 
| 182 182 | 
             
                  delimiter = "\n#{o[:indent]}//"
         | 
| 183 183 | 
             
                  comment   = "#{delimiter}#{@lines.join(delimiter)}"
         | 
| 184 184 | 
             
                  write(comment)
         | 
| @@ -189,8 +189,6 @@ module CoffeeScript | |
| 189 189 | 
             
              # Node for a function invocation. Takes care of converting super() calls into
         | 
| 190 190 | 
             
              # calls against the prototype's function of the same name.
         | 
| 191 191 | 
             
              class CallNode < Node
         | 
| 192 | 
            -
                LEADING_DOT = /\A\./
         | 
| 193 | 
            -
             | 
| 194 192 | 
             
                attr_reader :variable, :arguments
         | 
| 195 193 |  | 
| 196 194 | 
             
                def initialize(variable, arguments=[])
         | 
| @@ -206,42 +204,65 @@ module CoffeeScript | |
| 206 204 | 
             
                  @variable == :super
         | 
| 207 205 | 
             
                end
         | 
| 208 206 |  | 
| 209 | 
            -
                def  | 
| 210 | 
            -
                   | 
| 207 | 
            +
                def prefix
         | 
| 208 | 
            +
                  @new ? "new " : ''
         | 
| 209 | 
            +
                end
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                def splat?
         | 
| 212 | 
            +
                  @arguments.any? {|a| a.is_a?(ArgSplatNode) }
         | 
| 213 | 
            +
                end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                def <<(argument)
         | 
| 216 | 
            +
                  @arguments << argument
         | 
| 217 | 
            +
                end
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                def compile_node(o)
         | 
| 220 | 
            +
                  return write(compile_splat(o)) if splat?
         | 
| 211 221 | 
             
                  args = @arguments.map{|a| a.compile(o) }.join(', ')
         | 
| 212 222 | 
             
                  return write(compile_super(args, o)) if super?
         | 
| 213 | 
            -
                  prefix = @new ? "new " : ''
         | 
| 214 223 | 
             
                  write("#{prefix}#{@variable.compile(o)}(#{args})")
         | 
| 215 224 | 
             
                end
         | 
| 216 225 |  | 
| 217 226 | 
             
                def compile_super(args, o)
         | 
| 218 | 
            -
                  methname = o[:last_assign] | 
| 227 | 
            +
                  methname = o[:last_assign]
         | 
| 219 228 | 
             
                  arg_part = args.empty? ? '' : ", #{args}"
         | 
| 220 229 | 
             
                  "#{o[:proto_assign]}.__superClass__.#{methname}.call(this#{arg_part})"
         | 
| 221 230 | 
             
                end
         | 
| 231 | 
            +
             | 
| 232 | 
            +
                def compile_splat(o)
         | 
| 233 | 
            +
                  meth = @variable.compile(o)
         | 
| 234 | 
            +
                  obj  = @variable.source || 'this'
         | 
| 235 | 
            +
                  args = @arguments.map do |arg|
         | 
| 236 | 
            +
                    code = arg.compile(o)
         | 
| 237 | 
            +
                    code = arg.is_a?(ArgSplatNode) ? code : "[#{code}]"
         | 
| 238 | 
            +
                    arg.equal?(@arguments.first) ? code : ".concat(#{code})"
         | 
| 239 | 
            +
                  end
         | 
| 240 | 
            +
                  "#{prefix}#{meth}.apply(#{obj}, #{args.join('')})"
         | 
| 241 | 
            +
                end
         | 
| 222 242 | 
             
              end
         | 
| 223 243 |  | 
| 224 244 | 
             
              # Node to extend an object's prototype with an ancestor object.
         | 
| 225 245 | 
             
              # After goog.inherits from the Closure Library.
         | 
| 226 246 | 
             
              class ExtendsNode < Node
         | 
| 247 | 
            +
                statement
         | 
| 227 248 | 
             
                attr_reader :sub_object, :super_object
         | 
| 228 249 |  | 
| 229 250 | 
             
                def initialize(sub_object, super_object)
         | 
| 230 251 | 
             
                  @sub_object, @super_object = sub_object, super_object
         | 
| 231 252 | 
             
                end
         | 
| 232 253 |  | 
| 233 | 
            -
                def  | 
| 254 | 
            +
                def compile_node(o={})
         | 
| 234 255 | 
             
                  sub, sup = @sub_object.compile(o), @super_object.compile(o)
         | 
| 235 | 
            -
                  "#{sub}.__superClass__ = #{sup}.prototype;\n#{o[:indent]}" +
         | 
| 256 | 
            +
                  "#{o[:indent]}#{sub}.__superClass__ = #{sup}.prototype;\n#{o[:indent]}" +
         | 
| 236 257 | 
             
                  "#{sub}.prototype = new #{sup}();\n#{o[:indent]}" +
         | 
| 237 | 
            -
                  "#{sub}.prototype.constructor = #{sub}"
         | 
| 258 | 
            +
                  "#{sub}.prototype.constructor = #{sub};"
         | 
| 238 259 | 
             
                end
         | 
| 239 260 |  | 
| 240 261 | 
             
              end
         | 
| 241 262 |  | 
| 242 263 | 
             
              # A value, indexed or dotted into, or vanilla.
         | 
| 243 264 | 
             
              class ValueNode < Node
         | 
| 244 | 
            -
                attr_reader :literal, :properties, :last
         | 
| 265 | 
            +
                attr_reader :literal, :properties, :last, :source
         | 
| 245 266 |  | 
| 246 267 | 
             
                def initialize(literal, properties=[])
         | 
| 247 268 | 
             
                  @literal, @properties = literal, properties
         | 
| @@ -260,20 +281,14 @@ module CoffeeScript | |
| 260 281 | 
             
                  @literal.is_a?(Node) && @literal.statement? && !properties?
         | 
| 261 282 | 
             
                end
         | 
| 262 283 |  | 
| 263 | 
            -
                def  | 
| 264 | 
            -
                   | 
| 265 | 
            -
             | 
| 266 | 
            -
             | 
| 267 | 
            -
                def custom_return?
         | 
| 268 | 
            -
                  @literal.is_a?(Node) && @literal.custom_return? && !properties?
         | 
| 269 | 
            -
                end
         | 
| 270 | 
            -
             | 
| 271 | 
            -
                def compile(o={})
         | 
| 272 | 
            -
                  o = super(o)
         | 
| 273 | 
            -
                  parts = [@literal, @properties].flatten.map do |val|
         | 
| 284 | 
            +
                def compile_node(o)
         | 
| 285 | 
            +
                  only  = o.delete(:only_first)
         | 
| 286 | 
            +
                  props = only ? @properties[0...-1] : @properties
         | 
| 287 | 
            +
                  parts = [@literal, props].flatten.map do |val|
         | 
| 274 288 | 
             
                    val.respond_to?(:compile) ? val.compile(o) : val.to_s
         | 
| 275 289 | 
             
                  end
         | 
| 276 290 | 
             
                  @last = parts.last
         | 
| 291 | 
            +
                  @source = parts.length > 1 ? parts[0...-1].join('') : nil
         | 
| 277 292 | 
             
                  write(parts.join(''))
         | 
| 278 293 | 
             
                end
         | 
| 279 294 | 
             
              end
         | 
| @@ -286,8 +301,7 @@ module CoffeeScript | |
| 286 301 | 
             
                  @name = name
         | 
| 287 302 | 
             
                end
         | 
| 288 303 |  | 
| 289 | 
            -
                def  | 
| 290 | 
            -
                  o = super(o)
         | 
| 304 | 
            +
                def compile_node(o)
         | 
| 291 305 | 
             
                  write(".#{@name}")
         | 
| 292 306 | 
             
                end
         | 
| 293 307 | 
             
              end
         | 
| @@ -300,15 +314,14 @@ module CoffeeScript | |
| 300 314 | 
             
                  @index = index
         | 
| 301 315 | 
             
                end
         | 
| 302 316 |  | 
| 303 | 
            -
                def  | 
| 304 | 
            -
                  o = super(o)
         | 
| 317 | 
            +
                def compile_node(o)
         | 
| 305 318 | 
             
                  write("[#{@index.compile(o)}]")
         | 
| 306 319 | 
             
                end
         | 
| 307 320 | 
             
              end
         | 
| 308 321 |  | 
| 309 322 | 
             
              # A range literal. Ranges can be used to extract portions (slices) of arrays,
         | 
| 310 323 | 
             
              # or to specify a range for array comprehensions.
         | 
| 311 | 
            -
              class RangeNode
         | 
| 324 | 
            +
              class RangeNode < Node
         | 
| 312 325 | 
             
                attr_reader :from, :to
         | 
| 313 326 |  | 
| 314 327 | 
             
                def initialize(from, to, exclusive=false)
         | 
| @@ -327,12 +340,21 @@ module CoffeeScript | |
| 327 340 | 
             
                  @exclusive ? '>' : '>='
         | 
| 328 341 | 
             
                end
         | 
| 329 342 |  | 
| 330 | 
            -
                def  | 
| 331 | 
            -
                   | 
| 332 | 
            -
                   | 
| 333 | 
            -
                   | 
| 334 | 
            -
                   | 
| 335 | 
            -
             | 
| 343 | 
            +
                def compile_variables(o)
         | 
| 344 | 
            +
                  idt = o[:indent]
         | 
| 345 | 
            +
                  @from_var, @to_var = o[:scope].free_variable, o[:scope].free_variable
         | 
| 346 | 
            +
                  from_val,  to_val  = @from.compile(o), @to.compile(o)
         | 
| 347 | 
            +
                  write("#{idt}#{@from_var} = #{from_val};\n#{idt}#{@to_var} = #{to_val};\n#{idt}")
         | 
| 348 | 
            +
                end
         | 
| 349 | 
            +
             | 
| 350 | 
            +
                def compile_node(o)
         | 
| 351 | 
            +
                  idx, step = o.delete(:index), o.delete(:step)
         | 
| 352 | 
            +
                  raise SyntaxError, "unexpected range literal" unless idx
         | 
| 353 | 
            +
                  vars     = "#{idx}=#{@from_var}"
         | 
| 354 | 
            +
                  step     = step ? step.compile(o) : '1'
         | 
| 355 | 
            +
                  compare  = "(#{@from_var} <= #{@to_var} ? #{idx} #{less_operator} #{@to_var} : #{idx} #{greater_operator} #{@to_var})"
         | 
| 356 | 
            +
                  incr     = "(#{@from_var} <= #{@to_var} ? #{idx} += #{step} : #{idx} -= #{step})"
         | 
| 357 | 
            +
                  write("#{vars}; #{compare}; #{incr}")
         | 
| 336 358 | 
             
                end
         | 
| 337 359 |  | 
| 338 360 | 
             
              end
         | 
| @@ -347,8 +369,7 @@ module CoffeeScript | |
| 347 369 | 
             
                  @range = range
         | 
| 348 370 | 
             
                end
         | 
| 349 371 |  | 
| 350 | 
            -
                def  | 
| 351 | 
            -
                  o         = super(o)
         | 
| 372 | 
            +
                def compile_node(o)
         | 
| 352 373 | 
             
                  from      = @range.from.compile(o)
         | 
| 353 374 | 
             
                  to        = @range.to.compile(o)
         | 
| 354 375 | 
             
                  plus_part = @range.exclusive? ? '' : ' + 1'
         | 
| @@ -359,8 +380,7 @@ module CoffeeScript | |
| 359 380 | 
             
              # Setting the value of a local variable, or the value of an object property.
         | 
| 360 381 | 
             
              class AssignNode < Node
         | 
| 361 382 | 
             
                PROTO_ASSIGN = /\A(\S+)\.prototype/
         | 
| 362 | 
            -
             | 
| 363 | 
            -
                custom_return
         | 
| 383 | 
            +
                LEADING_DOT = /\A\./
         | 
| 364 384 |  | 
| 365 385 | 
             
                attr_reader :variable, :value, :context
         | 
| 366 386 |  | 
| @@ -368,21 +388,26 @@ module CoffeeScript | |
| 368 388 | 
             
                  @variable, @value, @context = variable, value, context
         | 
| 369 389 | 
             
                end
         | 
| 370 390 |  | 
| 371 | 
            -
                def  | 
| 372 | 
            -
                  @ | 
| 373 | 
            -
                end
         | 
| 374 | 
            -
             | 
| 375 | 
            -
                def compile(o={})
         | 
| 376 | 
            -
                  o = super(o)
         | 
| 391 | 
            +
                def compile_node(o)
         | 
| 392 | 
            +
                  return compile_splice(o) if @variable.properties.last.is_a?(SliceNode)
         | 
| 377 393 | 
             
                  name      = @variable.compile(o)
         | 
| 378 | 
            -
                  last      = @variable.last.to_s
         | 
| 394 | 
            +
                  last      = @variable.last.to_s.sub(LEADING_DOT, '')
         | 
| 379 395 | 
             
                  proto     = name[PROTO_ASSIGN, 1]
         | 
| 380 | 
            -
                  o         = o.merge(: | 
| 396 | 
            +
                  o         = o.merge(:last_assign => last, :proto_assign => proto)
         | 
| 397 | 
            +
                  o[:immediate_assign] = last if @value.is_a?(CodeNode) && last.match(Lexer::IDENTIFIER)
         | 
| 381 398 | 
             
                  return write("#{name}: #{@value.compile(o)}") if @context == :object
         | 
| 382 399 | 
             
                  o[:scope].find(name) unless @variable.properties?
         | 
| 383 | 
            -
                  return write(@value.compile(o)) if @value.custom_assign?
         | 
| 384 400 | 
             
                  val = "#{name} = #{@value.compile(o)}"
         | 
| 385 | 
            -
                  write(o[:return]  | 
| 401 | 
            +
                  write(o[:return] ? "#{o[:indent]}return (#{val})" : val)
         | 
| 402 | 
            +
                end
         | 
| 403 | 
            +
             | 
| 404 | 
            +
                def compile_splice(o)
         | 
| 405 | 
            +
                  var   = @variable.compile(o.merge(:only_first => true))
         | 
| 406 | 
            +
                  range = @variable.properties.last.range
         | 
| 407 | 
            +
                  plus  = range.exclusive? ? '' : ' + 1'
         | 
| 408 | 
            +
                  from  = range.from.compile(o)
         | 
| 409 | 
            +
                  to    = "#{range.to.compile(o)} - #{from}#{plus}"
         | 
| 410 | 
            +
                  write("#{var}.splice.apply(#{var}, [#{from}, #{to}].concat(#{@value.compile(o)}))")
         | 
| 386 411 | 
             
                end
         | 
| 387 412 | 
             
              end
         | 
| 388 413 |  | 
| @@ -390,31 +415,30 @@ module CoffeeScript | |
| 390 415 | 
             
              # CoffeeScript operations into their JavaScript equivalents.
         | 
| 391 416 | 
             
              class OpNode < Node
         | 
| 392 417 | 
             
                CONVERSIONS = {
         | 
| 393 | 
            -
                   | 
| 394 | 
            -
                   | 
| 395 | 
            -
                   | 
| 396 | 
            -
                   | 
| 397 | 
            -
                   | 
| 398 | 
            -
                   | 
| 399 | 
            -
                   | 
| 418 | 
            +
                  :==     => "===",
         | 
| 419 | 
            +
                  :'!='   => "!==",
         | 
| 420 | 
            +
                  :and    => '&&',
         | 
| 421 | 
            +
                  :or     => '||',
         | 
| 422 | 
            +
                  :is     => '===',
         | 
| 423 | 
            +
                  :isnt   => "!==",
         | 
| 424 | 
            +
                  :not    => '!'
         | 
| 400 425 | 
             
                }
         | 
| 401 | 
            -
                CONDITIONALS     = ['||=', '&&=']
         | 
| 402 | 
            -
                PREFIX_OPERATORS = [ | 
| 426 | 
            +
                CONDITIONALS     = [:'||=', :'&&=']
         | 
| 427 | 
            +
                PREFIX_OPERATORS = [:typeof, :delete]
         | 
| 403 428 |  | 
| 404 429 | 
             
                attr_reader :operator, :first, :second
         | 
| 405 430 |  | 
| 406 431 | 
             
                def initialize(operator, first, second=nil, flip=false)
         | 
| 407 432 | 
             
                  @first, @second, @flip = first, second, flip
         | 
| 408 | 
            -
                  @operator = CONVERSIONS[operator] || operator
         | 
| 433 | 
            +
                  @operator = CONVERSIONS[operator.to_sym] || operator
         | 
| 409 434 | 
             
                end
         | 
| 410 435 |  | 
| 411 436 | 
             
                def unary?
         | 
| 412 437 | 
             
                  @second.nil?
         | 
| 413 438 | 
             
                end
         | 
| 414 439 |  | 
| 415 | 
            -
                def  | 
| 416 | 
            -
                  o  | 
| 417 | 
            -
                  return write(compile_conditional(o)) if CONDITIONALS.include?(@operator)
         | 
| 440 | 
            +
                def compile_node(o)
         | 
| 441 | 
            +
                  return write(compile_conditional(o)) if CONDITIONALS.include?(@operator.to_sym)
         | 
| 418 442 | 
             
                  return write(compile_unary(o)) if unary?
         | 
| 419 443 | 
             
                  write("#{@first.compile(o)} #{@operator} #{@second.compile(o)}")
         | 
| 420 444 | 
             
                end
         | 
| @@ -426,7 +450,7 @@ module CoffeeScript | |
| 426 450 | 
             
                end
         | 
| 427 451 |  | 
| 428 452 | 
             
                def compile_unary(o)
         | 
| 429 | 
            -
                  space = PREFIX_OPERATORS.include?(@operator. | 
| 453 | 
            +
                  space = PREFIX_OPERATORS.include?(@operator.to_sym) ? ' ' : ''
         | 
| 430 454 | 
             
                  parts = [@operator.to_s, space, @first.compile(o)]
         | 
| 431 455 | 
             
                  parts.reverse! if @flip
         | 
| 432 456 | 
             
                  parts.join('')
         | 
| @@ -442,18 +466,53 @@ module CoffeeScript | |
| 442 466 | 
             
                  @body = body
         | 
| 443 467 | 
             
                end
         | 
| 444 468 |  | 
| 445 | 
            -
                def  | 
| 446 | 
            -
                   | 
| 447 | 
            -
                   | 
| 448 | 
            -
                  o[: | 
| 449 | 
            -
                   | 
| 450 | 
            -
                  o[: | 
| 451 | 
            -
                  o | 
| 469 | 
            +
                def compile_node(o)
         | 
| 470 | 
            +
                  shared_scope = o.delete(:shared_scope)
         | 
| 471 | 
            +
                  indent       = o[:indent]
         | 
| 472 | 
            +
                  o[:scope]    = shared_scope || Scope.new(o[:scope], @body)
         | 
| 473 | 
            +
                  o[:return]   = true
         | 
| 474 | 
            +
                  o[:top]      = true
         | 
| 475 | 
            +
                  o[:indent]  += TAB
         | 
| 452 476 | 
             
                  o.delete(:no_wrap)
         | 
| 477 | 
            +
                  name = o.delete(:immediate_assign)
         | 
| 478 | 
            +
                  if @params.last.is_a?(ParamSplatNode)
         | 
| 479 | 
            +
                    splat = @params.pop
         | 
| 480 | 
            +
                    splat.index = @params.length
         | 
| 481 | 
            +
                    @body.unshift(splat)
         | 
| 482 | 
            +
                  end
         | 
| 453 483 | 
             
                  @params.each {|id| o[:scope].parameter(id.to_s) }
         | 
| 454 | 
            -
                  code = @body. | 
| 455 | 
            -
                   | 
| 484 | 
            +
                  code = @body.compile_with_declarations(o)
         | 
| 485 | 
            +
                  name_part = name ? " #{name}" : ''
         | 
| 486 | 
            +
                  write("function#{name_part}(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
         | 
| 487 | 
            +
                end
         | 
| 488 | 
            +
              end
         | 
| 489 | 
            +
             | 
| 490 | 
            +
              # A parameter splat in a function definition.
         | 
| 491 | 
            +
              class ParamSplatNode < Node
         | 
| 492 | 
            +
                attr_accessor :index
         | 
| 493 | 
            +
                attr_reader :name
         | 
| 494 | 
            +
             | 
| 495 | 
            +
                def initialize(name)
         | 
| 496 | 
            +
                  @name = name
         | 
| 497 | 
            +
                end
         | 
| 498 | 
            +
             | 
| 499 | 
            +
                def compile_node(o={})
         | 
| 500 | 
            +
                  o[:scope].find(@name)
         | 
| 501 | 
            +
                  write("#{@name} = Array.prototype.slice.call(arguments, #{@index})")
         | 
| 502 | 
            +
                end
         | 
| 503 | 
            +
              end
         | 
| 504 | 
            +
             | 
| 505 | 
            +
              class ArgSplatNode < Node
         | 
| 506 | 
            +
                attr_reader :value
         | 
| 507 | 
            +
             | 
| 508 | 
            +
                def initialize(value)
         | 
| 509 | 
            +
                  @value = value
         | 
| 510 | 
            +
                end
         | 
| 511 | 
            +
             | 
| 512 | 
            +
                def compile_node(o={})
         | 
| 513 | 
            +
                  write(@value.compile(o))
         | 
| 456 514 | 
             
                end
         | 
| 515 | 
            +
             | 
| 457 516 | 
             
              end
         | 
| 458 517 |  | 
| 459 518 | 
             
              # An object literal.
         | 
| @@ -464,13 +523,20 @@ module CoffeeScript | |
| 464 523 | 
             
                  @properties = properties
         | 
| 465 524 | 
             
                end
         | 
| 466 525 |  | 
| 467 | 
            -
                 | 
| 468 | 
            -
             | 
| 526 | 
            +
                # All the mucking about with commas is to make sure that CommentNodes and
         | 
| 527 | 
            +
                # AssignNodes get interleaved correctly, with no trailing commas or
         | 
| 528 | 
            +
                # commas affixed to comments. TODO: Extract this and add it to ArrayNode.
         | 
| 529 | 
            +
                def compile_node(o)
         | 
| 469 530 | 
             
                  indent = o[:indent]
         | 
| 470 531 | 
             
                  o[:indent] += TAB
         | 
| 471 | 
            -
                   | 
| 472 | 
            -
             | 
| 473 | 
            -
             | 
| 532 | 
            +
                  joins = Hash.new("\n")
         | 
| 533 | 
            +
                  non_comments = @properties.select {|p| !p.is_a?(CommentNode) }
         | 
| 534 | 
            +
                  non_comments.each {|p| joins[p] = p == non_comments.last ? "\n" : ",\n" }
         | 
| 535 | 
            +
                  props  = @properties.map { |prop|
         | 
| 536 | 
            +
                    join = joins[prop]
         | 
| 537 | 
            +
                    join = '' if prop == @properties.last
         | 
| 538 | 
            +
                    idt  = prop.is_a?(CommentNode) ? '' : o[:indent]
         | 
| 539 | 
            +
                    "#{idt}#{prop.compile(o)}#{join}"
         | 
| 474 540 | 
             
                  }.join('')
         | 
| 475 541 | 
             
                  write("{\n#{props}\n#{indent}}")
         | 
| 476 542 | 
             
                end
         | 
| @@ -484,13 +550,15 @@ module CoffeeScript | |
| 484 550 | 
             
                  @objects = objects
         | 
| 485 551 | 
             
                end
         | 
| 486 552 |  | 
| 487 | 
            -
                def  | 
| 488 | 
            -
                   | 
| 553 | 
            +
                def compile_node(o)
         | 
| 554 | 
            +
                  indent = o[:indent]
         | 
| 555 | 
            +
                  o[:indent] += TAB
         | 
| 489 556 | 
             
                  objects = @objects.map { |obj|
         | 
| 490 | 
            -
                     | 
| 491 | 
            -
                    obj. | 
| 557 | 
            +
                    code = obj.compile(o)
         | 
| 558 | 
            +
                    obj.is_a?(CommentNode) ? "\n#{code}\n#{o[:indent]}" :
         | 
| 559 | 
            +
                    obj == @objects.last   ? code : "#{code}, "
         | 
| 492 560 | 
             
                  }.join('')
         | 
| 493 | 
            -
                  ending = objects.include?("\n") ? "\n#{ | 
| 561 | 
            +
                  ending = objects.include?("\n") ? "\n#{indent}]" : ']'
         | 
| 494 562 | 
             
                  write("[#{objects}#{ending}")
         | 
| 495 563 | 
             
                end
         | 
| 496 564 | 
             
              end
         | 
| @@ -506,16 +574,14 @@ module CoffeeScript | |
| 506 574 | 
             
                  @condition, @body = condition, body
         | 
| 507 575 | 
             
                end
         | 
| 508 576 |  | 
| 509 | 
            -
                def  | 
| 510 | 
            -
                   | 
| 511 | 
            -
             | 
| 512 | 
            -
             | 
| 513 | 
            -
             | 
| 514 | 
            -
                   | 
| 515 | 
            -
                   | 
| 516 | 
            -
                  indent  | 
| 517 | 
            -
                  cond = @condition.compile(o)
         | 
| 518 | 
            -
                  write("while (#{cond}) {\n#{@body.compile(o.merge(:indent => indent))}\n#{o[:indent]}}")
         | 
| 577 | 
            +
                def compile_node(o)
         | 
| 578 | 
            +
                  returns     = o.delete(:return)
         | 
| 579 | 
            +
                  indent      = o[:indent]
         | 
| 580 | 
            +
                  o[:indent] += TAB
         | 
| 581 | 
            +
                  o[:top]     = true
         | 
| 582 | 
            +
                  cond        = @condition.compile(o)
         | 
| 583 | 
            +
                  post        = returns ? "\n#{indent}return null;" : ''
         | 
| 584 | 
            +
                  write("#{indent}while (#{cond}) {\n#{@body.compile(o)}\n#{indent}}#{post}")
         | 
| 519 585 | 
             
                end
         | 
| 520 586 | 
             
              end
         | 
| 521 587 |  | 
| @@ -525,74 +591,70 @@ module CoffeeScript | |
| 525 591 | 
             
              # the current index of the loop as a second parameter.
         | 
| 526 592 | 
             
              class ForNode < Node
         | 
| 527 593 | 
             
                statement
         | 
| 528 | 
            -
                custom_return
         | 
| 529 | 
            -
                custom_assign
         | 
| 530 | 
            -
             | 
| 531 | 
            -
                attr_reader :body, :source, :name, :filter, :index
         | 
| 532 594 |  | 
| 533 | 
            -
                 | 
| 534 | 
            -
                  @body, @source, @name, @filter, @index = body, source, name, filter, index
         | 
| 535 | 
            -
                end
         | 
| 595 | 
            +
                attr_reader :body, :source, :name, :index, :filter, :step
         | 
| 536 596 |  | 
| 537 | 
            -
                def  | 
| 538 | 
            -
                   | 
| 597 | 
            +
                def initialize(body, source, name, index=nil)
         | 
| 598 | 
            +
                  @body, @name, @index = body, name, index
         | 
| 599 | 
            +
                  @source = source[:source]
         | 
| 600 | 
            +
                  @filter = source[:filter]
         | 
| 601 | 
            +
                  @step   = source[:step]
         | 
| 539 602 | 
             
                end
         | 
| 540 603 |  | 
| 541 | 
            -
                def  | 
| 542 | 
            -
                  o  | 
| 604 | 
            +
                def compile_node(o)
         | 
| 605 | 
            +
                  top_level     = o.delete(:top) && !o[:return]
         | 
| 543 606 | 
             
                  range         = @source.is_a?(RangeNode)
         | 
| 544 607 | 
             
                  scope         = o[:scope]
         | 
| 545 608 | 
             
                  name_found    = scope.find(@name)
         | 
| 546 609 | 
             
                  index_found   = @index && scope.find(@index)
         | 
| 547 610 | 
             
                  svar          = scope.free_variable
         | 
| 548 | 
            -
                  ivar          = range ? name : scope.free_variable
         | 
| 549 | 
            -
                   | 
| 550 | 
            -
                   | 
| 551 | 
            -
                  index_name    = @index ? @index : nil
         | 
| 611 | 
            +
                  ivar          = range ? name : @index ? @index : scope.free_variable
         | 
| 612 | 
            +
                  rvar          = scope.free_variable unless top_level
         | 
| 613 | 
            +
                  tvar          = scope.free_variable
         | 
| 552 614 | 
             
                  if range
         | 
| 553 | 
            -
                     | 
| 554 | 
            -
                    var_part | 
| 555 | 
            -
                    index_part  = ''
         | 
| 615 | 
            +
                    body_dent   = o[:indent] + TAB
         | 
| 616 | 
            +
                    var_part, pre_cond, post_cond = '', '', ''
         | 
| 556 617 | 
             
                    index_var   = scope.free_variable
         | 
| 557 | 
            -
                     | 
| 618 | 
            +
                    source_part = @source.compile_variables(o)
         | 
| 619 | 
            +
                    for_part    = "#{index_var}=0, #{@source.compile(o.merge(:index => ivar, :step => @step))}, #{index_var}++"
         | 
| 558 620 | 
             
                  else
         | 
| 559 621 | 
             
                    index_var   = nil
         | 
| 560 | 
            -
                     | 
| 561 | 
            -
                     | 
| 562 | 
            -
                     | 
| 563 | 
            -
                     | 
| 622 | 
            +
                    body_dent   = o[:indent] + TAB + TAB
         | 
| 623 | 
            +
                    source_part = "#{o[:indent]}#{svar} = #{@source.compile(o)};\n#{o[:indent]}"
         | 
| 624 | 
            +
                    for_part    = "#{ivar} in #{svar}"
         | 
| 625 | 
            +
                    pre_cond    = "\n#{o[:indent] + TAB}if (#{svar}.hasOwnProperty(#{ivar})) {"
         | 
| 626 | 
            +
                    var_part    = "\n#{body_dent}#{@name} = #{svar}[#{ivar}];"
         | 
| 627 | 
            +
                    post_cond   = "\n#{o[:indent] + TAB}}"
         | 
| 564 628 | 
             
                  end
         | 
| 565 629 | 
             
                  body          = @body
         | 
| 566 | 
            -
                   | 
| 567 | 
            -
                   | 
| 568 | 
            -
                   | 
| 569 | 
            -
                   | 
| 570 | 
            -
             | 
| 571 | 
            -
                   | 
| 572 | 
            -
                     | 
| 630 | 
            +
                  set_result    = rvar ? "#{rvar} = [];\n#{o[:indent]}" : ''
         | 
| 631 | 
            +
                  return_result = rvar || ''
         | 
| 632 | 
            +
                  temp_var      = ValueNode.new(LiteralNode.new(tvar))
         | 
| 633 | 
            +
                  if top_level
         | 
| 634 | 
            +
                    body = Expressions.wrap(body)
         | 
| 635 | 
            +
                  else
         | 
| 636 | 
            +
                    body = Expressions.wrap(
         | 
| 637 | 
            +
                      AssignNode.new(temp_var, @body.unwrap),
         | 
| 638 | 
            +
                      CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [temp_var])
         | 
| 639 | 
            +
                    )
         | 
| 640 | 
            +
                  end
         | 
| 641 | 
            +
                  if o[:return]
         | 
| 573 642 | 
             
                    return_result = "return #{return_result}" if o[:return]
         | 
| 574 | 
            -
                     | 
| 575 | 
            -
             | 
| 576 | 
            -
                      body = IfNode.new(@filter, body, nil, :statement => true)
         | 
| 577 | 
            -
                      save_result = ''
         | 
| 578 | 
            -
                      suffix = ''
         | 
| 579 | 
            -
                    end
         | 
| 643 | 
            +
                    o.delete(:return)
         | 
| 644 | 
            +
                    body = IfNode.new(@filter, body, nil, :statement => true) if @filter
         | 
| 580 645 | 
             
                  elsif @filter
         | 
| 581 | 
            -
                    body = IfNode.new(@filter, @body)
         | 
| 646 | 
            +
                    body = Expressions.wrap(IfNode.new(@filter, @body))
         | 
| 582 647 | 
             
                  end
         | 
| 583 648 |  | 
| 584 | 
            -
                  return_result = "\n#{o[:indent]}#{return_result};"
         | 
| 585 | 
            -
                   | 
| 586 | 
            -
                   | 
| 587 | 
            -
                  write("#{source_part}#{set_result}for (#{for_part}) {#{var_part}#{index_part}\n#{indent}#{save_result}#{body}#{suffix}\n#{o[:indent]}}#{return_result}")
         | 
| 649 | 
            +
                  return_result = "\n#{o[:indent]}#{return_result};" unless top_level
         | 
| 650 | 
            +
                  body = body.compile(o.merge(:indent => body_dent, :top => true))
         | 
| 651 | 
            +
                  write("#{source_part}#{set_result}for (#{for_part}) {#{pre_cond}#{var_part}\n#{body}#{post_cond}\n#{o[:indent]}}#{return_result}")
         | 
| 588 652 | 
             
                end
         | 
| 589 653 | 
             
              end
         | 
| 590 654 |  | 
| 591 655 | 
             
              # A try/catch/finally block.
         | 
| 592 656 | 
             
              class TryNode < Node
         | 
| 593 657 | 
             
                statement
         | 
| 594 | 
            -
                custom_return
         | 
| 595 | 
            -
                custom_assign
         | 
| 596 658 |  | 
| 597 659 | 
             
                attr_reader :try, :error, :recovery, :finally
         | 
| 598 660 |  | 
| @@ -600,24 +662,20 @@ module CoffeeScript | |
| 600 662 | 
             
                  @try, @error, @recovery, @finally = try, error, recovery, finally
         | 
| 601 663 | 
             
                end
         | 
| 602 664 |  | 
| 603 | 
            -
                def  | 
| 604 | 
            -
                  ''
         | 
| 605 | 
            -
                end
         | 
| 606 | 
            -
             | 
| 607 | 
            -
                def compile(o={})
         | 
| 608 | 
            -
                  o = super(o)
         | 
| 665 | 
            +
                def compile_node(o)
         | 
| 609 666 | 
             
                  indent = o[:indent]
         | 
| 610 667 | 
             
                  o[:indent] += TAB
         | 
| 668 | 
            +
                  o[:top] = true
         | 
| 611 669 | 
             
                  error_part = @error ? " (#{@error}) " : ' '
         | 
| 612 670 | 
             
                  catch_part = @recovery &&  " catch#{error_part}{\n#{@recovery.compile(o)}\n#{indent}}"
         | 
| 613 | 
            -
                  finally_part = @finally && " finally {\n#{@finally.compile(o.merge(: | 
| 614 | 
            -
                  write("try {\n#{@try.compile(o)}\n#{indent}}#{catch_part}#{finally_part}")
         | 
| 671 | 
            +
                  finally_part = @finally && " finally {\n#{@finally.compile(o.merge(:return => nil))}\n#{indent}}"
         | 
| 672 | 
            +
                  write("#{indent}try {\n#{@try.compile(o)}\n#{indent}}#{catch_part}#{finally_part}")
         | 
| 615 673 | 
             
                end
         | 
| 616 674 | 
             
              end
         | 
| 617 675 |  | 
| 618 676 | 
             
              # Throw an exception.
         | 
| 619 677 | 
             
              class ThrowNode < Node
         | 
| 620 | 
            -
                 | 
| 678 | 
            +
                statement_only
         | 
| 621 679 |  | 
| 622 680 | 
             
                attr_reader :expression
         | 
| 623 681 |  | 
| @@ -625,9 +683,22 @@ module CoffeeScript | |
| 625 683 | 
             
                  @expression = expression
         | 
| 626 684 | 
             
                end
         | 
| 627 685 |  | 
| 628 | 
            -
                def  | 
| 629 | 
            -
                  o  | 
| 630 | 
            -
             | 
| 686 | 
            +
                def compile_node(o)
         | 
| 687 | 
            +
                  write("#{o[:indent]}throw #{@expression.compile(o)};")
         | 
| 688 | 
            +
                end
         | 
| 689 | 
            +
              end
         | 
| 690 | 
            +
             | 
| 691 | 
            +
              # Check an expression for existence (meaning not null or undefined).
         | 
| 692 | 
            +
              class ExistenceNode < Node
         | 
| 693 | 
            +
                attr_reader :expression
         | 
| 694 | 
            +
             | 
| 695 | 
            +
                def initialize(expression)
         | 
| 696 | 
            +
                  @expression = expression
         | 
| 697 | 
            +
                end
         | 
| 698 | 
            +
             | 
| 699 | 
            +
                def compile_node(o)
         | 
| 700 | 
            +
                  val = @expression.compile(o)
         | 
| 701 | 
            +
                  write("(typeof #{val} !== \"undefined\" && #{val} !== null)")
         | 
| 631 702 | 
             
                end
         | 
| 632 703 | 
             
              end
         | 
| 633 704 |  | 
| @@ -637,25 +708,12 @@ module CoffeeScript | |
| 637 708 | 
             
              class ParentheticalNode < Node
         | 
| 638 709 | 
             
                attr_reader :expressions
         | 
| 639 710 |  | 
| 640 | 
            -
                def initialize(expressions)
         | 
| 711 | 
            +
                def initialize(expressions, line=nil)
         | 
| 641 712 | 
             
                  @expressions = expressions.unwrap
         | 
| 713 | 
            +
                  @line = line
         | 
| 642 714 | 
             
                end
         | 
| 643 715 |  | 
| 644 | 
            -
                def  | 
| 645 | 
            -
                  @expressions.unwrap.statement?
         | 
| 646 | 
            -
                end
         | 
| 647 | 
            -
             | 
| 648 | 
            -
                def custom_assign?
         | 
| 649 | 
            -
                  @expressions.custom_assign?
         | 
| 650 | 
            -
                end
         | 
| 651 | 
            -
             | 
| 652 | 
            -
                def custom_return?
         | 
| 653 | 
            -
                  @expressions.custom_return?
         | 
| 654 | 
            -
                end
         | 
| 655 | 
            -
             | 
| 656 | 
            -
                def compile(o={})
         | 
| 657 | 
            -
                  raise SyntaxError, "parentheses can't be wrapped around a statement" if statement?
         | 
| 658 | 
            -
                  o = super(o)
         | 
| 716 | 
            +
                def compile_node(o)
         | 
| 659 717 | 
             
                  compiled = @expressions.compile(o)
         | 
| 660 718 | 
             
                  compiled = compiled[0...-1] if compiled[-1..-1] == ';'
         | 
| 661 719 | 
             
                  write("(#{compiled})")
         | 
| @@ -683,6 +741,11 @@ module CoffeeScript | |
| 683 741 | 
             
                  self
         | 
| 684 742 | 
             
                end
         | 
| 685 743 |  | 
| 744 | 
            +
                def force_statement
         | 
| 745 | 
            +
                  @tags[:statement] = true
         | 
| 746 | 
            +
                  self
         | 
| 747 | 
            +
                end
         | 
| 748 | 
            +
             | 
| 686 749 | 
             
                # Rewrite a chain of IfNodes with their switch condition for equality.
         | 
| 687 750 | 
             
                def rewrite_condition(expression)
         | 
| 688 751 | 
             
                  @condition = OpNode.new("is", expression, @condition)
         | 
| @@ -691,8 +754,8 @@ module CoffeeScript | |
| 691 754 | 
             
                end
         | 
| 692 755 |  | 
| 693 756 | 
             
                # Rewrite a chain of IfNodes to add a default case as the final else.
         | 
| 694 | 
            -
                def add_else( | 
| 695 | 
            -
                  chain? ? @else_body.add_else( | 
| 757 | 
            +
                def add_else(exprs)
         | 
| 758 | 
            +
                  chain? ? @else_body.add_else(exprs) : @else_body = (exprs && exprs.unwrap)
         | 
| 696 759 | 
             
                  self
         | 
| 697 760 | 
             
                end
         | 
| 698 761 |  | 
| @@ -707,20 +770,7 @@ module CoffeeScript | |
| 707 770 | 
             
                  @is_statement ||= !!(@tags[:statement] || @body.statement? || (@else_body && @else_body.statement?))
         | 
| 708 771 | 
             
                end
         | 
| 709 772 |  | 
| 710 | 
            -
                def  | 
| 711 | 
            -
                  statement?
         | 
| 712 | 
            -
                end
         | 
| 713 | 
            -
             | 
| 714 | 
            -
                def custom_assign?
         | 
| 715 | 
            -
                  statement?
         | 
| 716 | 
            -
                end
         | 
| 717 | 
            -
             | 
| 718 | 
            -
                def line_ending
         | 
| 719 | 
            -
                  statement? ? '' : ';'
         | 
| 720 | 
            -
                end
         | 
| 721 | 
            -
             | 
| 722 | 
            -
                def compile(o={})
         | 
| 723 | 
            -
                  o = super(o)
         | 
| 773 | 
            +
                def compile_node(o)
         | 
| 724 774 | 
             
                  write(statement? ? compile_statement(o) : compile_ternary(o))
         | 
| 725 775 | 
             
                end
         | 
| 726 776 |  | 
| @@ -728,14 +778,16 @@ module CoffeeScript | |
| 728 778 | 
             
                # force sub-else bodies into statement form.
         | 
| 729 779 | 
             
                def compile_statement(o)
         | 
| 730 780 | 
             
                  indent = o[:indent]
         | 
| 781 | 
            +
                  child  = o.delete(:chain_child)
         | 
| 731 782 | 
             
                  cond_o = o.dup
         | 
| 732 | 
            -
                  cond_o.delete(:assign)
         | 
| 733 783 | 
             
                  cond_o.delete(:return)
         | 
| 734 784 | 
             
                  o[:indent] += TAB
         | 
| 735 | 
            -
                   | 
| 785 | 
            +
                  o[:top] = true
         | 
| 786 | 
            +
                  if_dent = child ? '' : indent
         | 
| 787 | 
            +
                  if_part = "#{if_dent}if (#{@condition.compile(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}"
         | 
| 736 788 | 
             
                  return if_part unless @else_body
         | 
| 737 789 | 
             
                  else_part = chain? ?
         | 
| 738 | 
            -
                    " else #{@else_body.compile(o.merge(:indent => indent))}" :
         | 
| 790 | 
            +
                    " else #{@else_body.compile(o.merge(:indent => indent, :chain_child => true))}" :
         | 
| 739 791 | 
             
                    " else {\n#{Expressions.wrap(@else_body).compile(o)}\n#{indent}}"
         | 
| 740 792 | 
             
                  if_part + else_part
         | 
| 741 793 | 
             
                end
         |