diff --git a/documentation/docs/grammar.html b/documentation/docs/grammar.html index 5d79eea5..5728ab41 100644 --- a/documentation/docs/grammar.html +++ b/documentation/docs/grammar.html @@ -96,6 +96,7 @@ through and printed to JavaScript.

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

A return statement from a function body.

  Return: [
     o "RETURN Expression",                      -> new ReturnNode $2
@@ -125,14 +126,12 @@ that hoovers up the remaining arguments.

o "Param . . .", -> new SplatNode $1 ]
#

A splat that occurs outside of a parameter list.

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

Variables and properties that can be assigned to.

  SimpleAssignable: [
+  ]
#

Variables and properties that can be assigned to.

  SimpleAssignable: [
     o "Identifier",                             -> new ValueNode $1
     o "Value Accessor",                         -> $1.push $2
     o "Invocation Accessor",                    -> new ValueNode $1, [$2]
     o "ThisProperty"
-  ]
-  
#

Everything that can be assigned to.

  Assignable: [
+  ]
#

Everything that can be assigned to.

  Assignable: [
     o "SimpleAssignable"
     o "Array",                                  -> new ValueNode $1
     o "Object",                                 -> new ValueNode $1
@@ -198,8 +197,7 @@ object.

]
#

A reference to the this current object.

  This: [
     o "THIS",                                   -> new ValueNode new LiteralNode 'this'
     o "@",                                      -> new ValueNode new LiteralNode 'this'
-  ]
-  
#

A reference to a property on this.

  ThisProperty: [
+  ]
#

A reference to a property on this.

  ThisProperty: [
     o "@ Identifier",                           -> new ValueNode new LiteralNode('this'), [new AccessorNode($2)]
   ]
#

The CoffeeScript range literal.

  Range: [
     o "[ Expression . . Expression ]",          -> new RangeNode $2, $5
diff --git a/documentation/docs/lexer.html b/documentation/docs/lexer.html
index 47b1c3f6..ff840ce8 100644
--- a/documentation/docs/lexer.html
+++ b/documentation/docs/lexer.html
@@ -30,7 +30,7 @@ of source.

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

  tokenize: (code, options) ->
-    code     : code.replace(/(\r|\s+$)/g, '')
+    code     : code.replace /(\r|\s+$)/g, ''
     o        : options or {}
     @code    : code         # The remainder of the source code.
     @i       : 0            # Current character position we're parsing.
@@ -39,7 +39,7 @@ unless explicitly asked not to.

@indents : [] # The stack of all current indentation levels. @tokens : [] # Stream of parsed tokens in the form ['TYPE', value, line] while @i < @code.length - @chunk: @code.slice(@i) + @chunk: @code.slice @i @extract_next_token() @close_indentation() return @tokens if o.rewrite is off @@ -68,7 +68,7 @@ referenced as property names here, so you can still do jQuery.is() though is means === otherwise.

  identifier_token: ->
     return false unless id: @match IDENTIFIER, 1
     @name_access_type()
-    accessed: include ACCESSORS, @tag(0)
+    accessed: include ACCESSORS, @tag 0
     tag: 'IDENTIFIER'
     tag: id.toUpperCase()     if not accessed and include(KEYWORDS, id)
     @identifier_error id      if include RESERVED, id
@@ -76,8 +76,8 @@ though is means === otherwise.

@i: + id.length if not accessed tag: id: CONVERSIONS[id] if include COFFEE_ALIASES, id - return @tag_half_assignment(tag) if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag - @token(tag, id) + return @tag_half_assignment tag if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag + @token tag, id true
#

Matches numbers, including decimals, hex, and exponential notation.

  number_token: ->
     return false unless number: @match NUMBER, 1
     @token 'NUMBER', number
@@ -88,13 +88,13 @@ are balanced within the string's contents, and within nested interpolations.

return false unless string: @balanced_token(['"', '"'], ['${', '}']) or @balanced_token ["'", "'"] - @interpolate_string string.replace(STRING_NEWLINES, " \\\n") + @interpolate_string string.replace STRING_NEWLINES, " \\\n" @line: + count string, "\n" @i: + string.length true
#

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

  heredoc_token: ->
     return false unless match: @chunk.match(HEREDOC)
-    quote: match[1].substr(0, 1)
+    quote: match[1].substr 0, 1
     doc: @sanitize_heredoc match[2] or match[4], quote
     @interpolate_string "$quote$doc$quote"
     @line: + count match[1], "\n"
@@ -102,7 +102,7 @@ preserve whitespace, but ignore indentation to the left.

true
#

Matches JavaScript interpolated directly into the source via backticks.

  js_token: ->
     return false unless starts @chunk, '`'
     return false unless script: @balanced_token ['`', '`']
-    @token 'JS', script.replace(JS_CLEANER, '')
+    @token 'JS', script.replace JS_CLEANER, ''
     @i: + script.length
     true
#

Matches regular expression literals. Lexing regular expressions is difficult to distinguish from division, so we borrow some basic heuristics from @@ -111,7 +111,7 @@ borrow interpolation from @interpolate_string.

return false unless @chunk.match REGEX_START return false if include NOT_REGEX, @tag() return false unless regex: @balanced_token ['/', '/'] - regex: + (flags: @chunk.substr(regex.length).match(REGEX_FLAGS)) + regex: + (flags: @chunk.substr(regex.length).match REGEX_FLAGS) if regex.match REGEX_INTERPOLATION str: regex.substring(1).split('/')[0] str: str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped @@ -127,7 +127,7 @@ balanced (ie. strings, JS literals).

  comment_token: ->
     return false unless comment: @match COMMENT, 1
     @line: + (comment.match(MULTILINER) or []).length
-    lines: compact comment.replace(COMMENT_CLEANER, '').split(MULTILINER)
+    lines: compact comment.replace(COMMENT_CLEANER, '').split MULTILINER
     i: @tokens.length - 1
     if @unfinished()
       i: - 1 while @tokens[i] and not include LINE_BREAK, @tokens[i][0]
@@ -153,7 +153,7 @@ can close multiple indents, so we need to know how far in we happen to be.

no_newlines: next_character is '.' or @unfinished() if size is @indent return @suppress_newlines() if no_newlines - return @newline_token(indent) + return @newline_token indent else if size > @indent return @suppress_newlines() if no_newlines diff: size - @indent @@ -185,14 +185,14 @@ Multi-character operators are also literal tokens, so that Jison can assign the proper order of operations. There are some symbols that we tag specially here. ; and newlines are both treated as a TERMINATOR, we distinguish parentheses that indicate a method call from regular parentheses, and so on.

  literal_token: ->
-    match: @chunk.match(OPERATOR)
+    match: @chunk.match OPERATOR
     value: match and match[1]
     space: match and match[2]
-    @tag_parameters() if value and value.match(CODE)
-    value: or @chunk.substr(0, 1)
+    @tag_parameters() if value and value.match CODE
+    value: or @chunk.substr 0, 1
     prev_spaced: @prev() and @prev().spaced
     tag: value
-    if value.match(ASSIGNMENT)
+    if value.match ASSIGNMENT
       tag: 'ASSIGN'
       @assignment_error() if include JS_FORBIDDEN, @value
     else if value is ';'
@@ -208,7 +208,7 @@ parentheses that indicate a method call from regular parentheses, and so on.

tag: 'CALL_START' if value is '(' tag: 'INDEX_START' if value is '[' @i: + value.length - return @tag_half_assignment(tag) if space and prev_spaced and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag + return @tag_half_assignment tag if space and prev_spaced and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag @token tag, value true
#

Token Manipulators

#

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

  name_access_type: ->
@@ -233,14 +233,14 @@ parameters specially in order to make things easier for the parser.

i: 0 while true i: + 1 - tok: @prev(i) + tok: @prev i return if not tok switch tok[0] when 'IDENTIFIER' then tok[0]: 'PARAM' when ')' then tok[0]: 'PARAM_END' when '(' then return tok[0]: 'PARAM_START' true
#

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

  close_indentation: ->
-    @outdent_token(@indent)
#

The error for when you try to use a forbidden word in JavaScript as + @outdent_token @indent

#

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

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

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

  assignment_error: ->
@@ -260,7 +260,7 @@ token stream.

else lexer: new Lexer() tokens: [] - quote: str.substring(0, 1) + quote: str.substring 0, 1 [i, pi]: [1, 1] while i < str.length - 1 if starts str, '\\', i @@ -297,20 +297,20 @@ token stream.

@token tag, value @token '+', '+' if i < tokens.length - 1 tokens
#

Helpers

#

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

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

Peek at a tag in the current token stream.

  tag: (index, tag) ->
-    return unless tok: @prev(index)
+    @tokens.push [tag, value, @line]
#

Peek at a tag in the current token stream.

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

Peek at a value in the current token stream.

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

Peek at a previous token, entire.

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

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

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

Are we in the midst of an unfinished expression?

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

There are no exensions to the core lexer by default.

Lexer.extensions: []
#

Constants

#

Keywords that CoffeeScript shares in common with JavaScript.

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

Lexer Properties

#

There are no exensions to the core lexer by default.

  @extensions: []
#

Constants

#

Keywords that CoffeeScript shares in common with JavaScript.

JS_KEYWORDS: [
   "if", "else",
   "true", "false",
   "new", "return",
@@ -320,21 +320,20 @@ match if successful, and false otherwise.

"delete", "instanceof", "typeof", "switch", "super", "extends", "class", "this", "null" -]
#

CoffeeScript-only keywords, which we're more relaxed about allowing. They can't +]

#

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

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

The combined list of keywords is the superset that gets passed verbatim to -the parser.

KEYWORDS: JS_KEYWORDS.concat COFFEE_KEYWORDS
#

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

#

The combined list of keywords is the superset that gets passed verbatim to +the parser.

KEYWORDS: JS_KEYWORDS.concat COFFEE_KEYWORDS
#

The list of keywords that are reserved by JavaScript, but not used, or are used by CoffeeScript internally. We throw an error when these are encountered, to avoid having a JavaScript error at runtime.

RESERVED: [
   "case", "default", "do", "function", "var", "void", "with"
-  "const", "let", "debugger", "enum", "export", "import", "native",
-  "__extends", "__hasProp"
-]
#

The superset of both JavaScript keywords and reserved words, none of which may -be used as identifiers or properties.

JS_FORBIDDEN: JS_KEYWORDS.concat RESERVED
#

Token matching regexes.

IDENTIFIER    : /^([a-zA-Z\$_](\w|\$)*)/
+  "const", "let", "debugger", "enum", "export", "import", "native"
+]
#

The superset of both JavaScript keywords and reserved words, none of which may +be used as identifiers or properties.

JS_FORBIDDEN: JS_KEYWORDS.concat RESERVED
#

Token matching regexes.

IDENTIFIER    : /^([a-zA-Z\$_](\w|\$)*)/
 NUMBER        : /^(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
 HEREDOC       : /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\n?([ \t]*)'{3})/
 INTERPOLATION : /^\$([a-zA-Z_@]\w*(\.\w+)*)/
@@ -345,27 +344,27 @@ be used as identifiers or properties.

MULTI_DENT : /^((\n([ \t]*))+)(\.)?/ LAST_DENTS : /\n([ \t]*)/g LAST_DENT : /\n([ \t]*)/ -ASSIGNMENT : /^(:|=)$/
#

Regex-matching-regexes.

REGEX_START        : /^\/[^\/ ]/
+ASSIGNMENT    : /^(:|=)$/
#

Regex-matching-regexes.

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

Token cleaning regexes.

JS_CLEANER      : /(^`|`$)/g
+REGEX_ESCAPE       : /\\[^\$]/g
#

Token cleaning regexes.

JS_CLEANER      : /(^`|`$)/g
 MULTILINER      : /\n/g
 STRING_NEWLINES : /\n[ \t]*/g
 COMMENT_CLEANER : /(^[ \t]*#|\n[ \t]*$)/mg
 NO_NEWLINE      : /^([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/
-HEREDOC_INDENT  : /^[ \t]+/mg
#

Tokens which a regular expression will never immediately follow, but which +HEREDOC_INDENT : /^[ \t]+/mg

#

Tokens which a regular expression will never immediately follow, but which a division operator might.

See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions

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

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

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

#

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

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

Tokens that indicate an access -- keywords immediately following will be -treated as identifiers.

ACCESSORS: ['PROPERTY_ACCESS', 'PROTOTYPE_ACCESS', 'SOAK_ACCESS', '@']
#

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

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

Tokens that indicate an access -- keywords immediately following will be +treated as identifiers.

ACCESSORS: ['PROPERTY_ACCESS', 'PROTOTYPE_ACCESS', 'SOAK_ACCESS', '@']
#

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

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

Half-assignments...

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

Conversions from CoffeeScript operators into JavaScript ones.

CONVERSIONS: {
+avoid an ambiguity in the grammar.

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

Half-assignments...

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

Conversions from CoffeeScript operators into JavaScript ones.

CONVERSIONS: {
   'and':  '&&'
   'or':   '||'
   'is':   '=='
diff --git a/documentation/docs/nodes.html b/documentation/docs/nodes.html
index 4d5cecca..f9935e7b 100644
--- a/documentation/docs/nodes.html
+++ b/documentation/docs/nodes.html
@@ -2,13 +2,13 @@
 nodes are created as the result of actions in the grammar,
 but some are created by other nodes as a method of code generation. To convert
 the syntax tree into a string of JavaScript code, call compile() on the root.

#

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

if process?
+including the Scope class and the helper functions.

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

Import the helpers we need.

compact: helpers.compact
+  Scope:        this.Scope
#

Import the helpers we plan to use.

compact: helpers.compact
 flatten: helpers.flatten
 merge:   helpers.merge
 del:     helpers.del
#

Helper function that marks a node as a JavaScript statement, or as a @@ -72,7 +72,7 @@ a pure statement?

node.traverse block if node.traverse
#

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

  toString: (idt) ->
     idt: or ''
-    '\n' + idt + @type + (child.toString(idt + TAB) for child in @children).join('')
#

Default implementations of the common node identification methods. Nodes + '\n' + idt + @constructor.name + (child.toString(idt + TAB) for child in @children).join('')

#

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

  unwrap:               -> this
   children:             []
   is_statement:         -> false
@@ -80,7 +80,6 @@ will override these with custom logic, if needed.

top_sensitive: -> false
#

Expressions

#

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

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

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

  push: (node) ->
@@ -127,7 +126,6 @@ to be one.

statement Expressions
#

LiteralNode

#

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

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

Break and continue must be treated as pure statements -- they lose their @@ -143,7 +141,6 @@ meaning when wrapped in a closure.

toString: (idt) -> " \"$@value\""
#

ReturnNode

#

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

exports.ReturnNode: class ReturnNode extends BaseNode
-  type: 'Return'
 
   constructor: (expression) ->
     @children: [@expression: expression]
@@ -160,7 +157,6 @@ make sense.

statement ReturnNode, true
#

ValueNode

#

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

exports.ValueNode: class ValueNode extends BaseNode
-  type: 'Value'
 
   SOAK: " == undefined ? undefined : "
#

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

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

Add a property access to the list.

  push: (prop) ->
@@ -211,7 +207,6 @@ evaluate a anything twice when building the soak chain.

if op and soaked then "($complete)" else complete
#

CommentNode

#

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

exports.CommentNode: class CommentNode extends BaseNode
-  type: 'Comment'
 
   constructor: (lines) ->
     @lines: lines
@@ -225,7 +220,6 @@ same position.

statement CommentNode
#

CallNode

#

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

exports.CallNode: class CallNode extends BaseNode
-  type: 'Call'
 
   constructor: (variable, args) ->
     @is_new:   false
@@ -260,9 +254,6 @@ of the current function.

"${@prefix()}${meth}.apply($obj, ${ @compile_splat_arguments(o) })"
#

CurryNode

#

Binds a context object and a list of arguments to a function, returning the bound function. After ECMAScript 5, Prototype.js, and Underscore's bind functions.

exports.CurryNode: class CurryNode extends CallNode
-  type: 'Curry'
-
-  body: 'func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)))'
 
   constructor: (meth, args) ->
     @children:  flatten [@meth: meth, @context: args[0], @args: (args.slice(1) or [])]
@@ -274,43 +265,27 @@ Underscore's bind functions.

(new ArrayNode(@args)).compile o compile_node: (o) -> - body: Expressions.wrap([literal @body]) - curried: new CodeNode([], body) - curry: new CodeNode([literal('func'), literal('obj'), literal('args')], Expressions.wrap([curried])) - (new ParentheticalNode(new CallNode(curry, [@meth, @context, literal(@arguments(o))]))).compile o
#

ExtendsNode

#

Node to extend an object's prototype with an ancestor object. + utility 'slice' + ref: new ValueNode literal utility 'bind' + (new CallNode(ref, [@meth, @context, literal(@arguments(o))])).compile o

#

ExtendsNode

#

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

exports.ExtendsNode: class ExtendsNode extends BaseNode
-  type: 'Extends'
-
-  code: '''
-        function(child, parent) {
-            var ctor = function(){ };
-            ctor.prototype = parent.prototype;
-            child.__superClass__ = parent.prototype;
-            child.prototype = new ctor();
-            child.prototype.constructor = child;
-          }
-        '''
 
   constructor: (child, parent) ->
     @children:  [@child: child, @parent: parent]
#

Hooks one constructor into another's prototype chain.

  compile_node: (o) ->
-    o.scope.assign('__extends', @code, true)
-    ref:  new ValueNode literal('__extends')
-    call: new CallNode ref, [@child, @parent]
-    call.compile(o)
#

AccessorNode

#

A . accessor into a property of a value, or the :: shorthand for + ref: new ValueNode literal utility 'extends' + (new CallNode ref, [@child, @parent]).compile o

#

AccessorNode

#

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

exports.AccessorNode: class AccessorNode extends BaseNode
-  type: 'Accessor'
 
   constructor: (name, tag) ->
     @children:  [@name: name]
-    @prototype: tag is 'prototype'
+    @prototype:tag is 'prototype'
     @soak_node: tag is 'soak'
     this
 
   compile_node: (o) ->
     proto_part: if @prototype then 'prototype.' else ''
     ".$proto_part${@name.compile(o)}"
#

IndexNode

#

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

exports.IndexNode: class IndexNode extends BaseNode
-  type: 'Index'
 
   constructor: (index, tag) ->
     @children:  [@index: index]
@@ -321,7 +296,6 @@ an accessor into the object's prototype.

"[$idx]"
#

RangeNode

#

A range literal. Ranges can be used to extract portions (slices) of arrays, to specify a range for comprehensions, or as a value, to be expanded into the corresponding array of integers at runtime.

exports.RangeNode: class RangeNode extends BaseNode
-  type: 'Range'
 
   constructor: (from, to, exclusive) ->
     @children:  [@from: from, @to: to]
@@ -349,7 +323,6 @@ it instead of wrapping nodes.

(new ParentheticalNode(new CallNode(new CodeNode([], arr.make_return())))).compile(o)
#

SliceNode

#

An array slice literal. Unlike JavaScript's Array#slice, the second parameter specifies the index of the end of the slice, just as the first parameter is the index of the beginning.

exports.SliceNode: class SliceNode extends BaseNode
-  type: 'Slice'
 
   constructor: (range) ->
     @children: [@range: range]
@@ -360,7 +333,6 @@ is the index of the beginning.

to: @range.to.compile(o) plus_part: if @range.exclusive then '' else ' + 1' ".slice($from, $to$plus_part)"
#

ObjectNode

#

An object literal, nothing fancy.

exports.ObjectNode: class ObjectNode extends BaseNode
-  type: 'Object'
 
   constructor: (props) ->
     @children: @objects: @properties: props or []
#

All the mucking about with commas is to make sure that CommentNodes and @@ -368,31 +340,30 @@ AssignNodes get interleaved correctly, with no trailing commas or commas affixed to comments.

TODO: Extract this and add it to ArrayNode.

  compile_node: (o) ->
-    o.indent: @idt(1)
+    o.indent: @idt 1
     non_comments: prop for prop in @properties when not (prop instanceof CommentNode)
     last_noncom:  non_comments[non_comments.length - 1]
     props: for prop, i in @properties
       join:   ",\n"
       join:   "\n" if (prop is last_noncom) or (prop instanceof CommentNode)
       join:   '' if i is @properties.length - 1
-      indent: if prop instanceof CommentNode then '' else @idt(1)
+      indent: if prop instanceof CommentNode then '' else @idt 1
       indent + prop.compile(o) + join
     props: props.join('')
     inner: if props then '\n' + props + '\n' + @idt() else ''
     "{$inner}"
#

ArrayNode

#

An array literal.

exports.ArrayNode: class ArrayNode extends BaseNode
-  type: 'Array'
 
   constructor: (objects) ->
     @children: @objects: objects or []
     @compile_splat_literal: SplatNode.compile_mixed_array <- @, @objects
 
   compile_node: (o) ->
-    o.indent: @idt(1)
+    o.indent: @idt 1
     objects: []
     for obj, i in @objects
       code: obj.compile(o)
       if obj instanceof SplatNode
-        return @compile_splat_literal(@objects, o)
+        return @compile_splat_literal @objects, o
       else if obj instanceof CommentNode
         objects.push "\n$code\n$o.indent"
       else if i is @objects.length - 1
@@ -401,8 +372,7 @@ commas affixed to comments.

objects.push "$code, " objects: objects.join('') ending: if objects.indexOf('\n') >= 0 then "\n$@tab]" else ']' - "[$objects$ending"
#

ClassNode

#

The CoffeeScript class definition.

exports.ClassNode: class ClassNode extends BaseNode
-  type: 'Class'
#

Initialize a ClassNode with its name, an optional superclass, and a + "[$objects$ending"

#

ClassNode

#

The CoffeeScript class definition.

exports.ClassNode: class ClassNode extends BaseNode
#

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

  constructor: (variable, parent, props) ->
     @children: compact flatten [@variable: variable, @parent: parent, @properties: props or []]
     @returns:  false
@@ -418,14 +388,16 @@ constructor, property assignments, and inheritance getting built out below.

o.top: true for prop in @properties - if prop.variable and prop.variable.base.value is 'constructor' + pvar: prop.variable + if pvar and pvar.base.value is 'constructor' func: prop.value func.body.push(new ReturnNode(literal('this'))) constructor: new AssignNode(@variable, func) else - if prop.variable - val: new ValueNode(@variable, [new AccessorNode(prop.variable, 'prototype')]) - prop: new AssignNode(val, prop.value) + if pvar + access: if prop.context is 'this' then pvar.base.properties[0] else new AccessorNode(pvar, 'prototype') + val: new ValueNode(@variable, [access]) + prop: new AssignNode(val, prop.value) props.push prop if not constructor @@ -444,8 +416,7 @@ constructor, property assignments, and inheritance getting built out below.

"$construct$extension$props$returns" statement ClassNode
#

AssignNode

#

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

exports.AssignNode: class AssignNode extends BaseNode
-  type: 'Assign'
#

Matchers for detecting prototype assignments.

  PROTO_ASSIGN: /^(\S+)\.prototype/
+property of an object -- including within object literals.

exports.AssignNode: class AssignNode extends BaseNode
#

Matchers for detecting prototype assignments.

  PROTO_ASSIGN: /^(\S+)\.prototype/
   LEADING_DOT:  /^\.(prototype\.)?/
 
   constructor: (variable, value, context) ->
@@ -508,7 +479,7 @@ for details.

< code: assigns.join("\n") code
#

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

  compile_splice: (o) ->
-    name:   @variable.compile(merge(o, {only_first: true}))
+    name:   @variable.compile merge o, {only_first: true}
     l:      @variable.properties.length
     range:  @variable.properties[l - 1].range
     plus:   if range.exclusive then '' else ' + 1'
@@ -518,7 +489,6 @@ for details.

< "${name}.splice.apply($name, [$from, $to].concat($val))"
#

CodeNode

#

A function definition. This is the only node that creates a new Scope. When for the purposes of walking the contents of a function body, the CodeNode has no children -- they're within the inner scope.

exports.CodeNode: class CodeNode extends BaseNode
-  type: 'Code'
 
   constructor: (params, body, tag) ->
     @params:  params or []
@@ -557,8 +527,9 @@ a closure.

func: "function${ if @bound then '' else name_part }(${ params.join(', ') }) {$code${@idt(if @bound then 1 else 0)}}" func: "($func)" if top and not @bound return func unless @bound - inner: "(function$name_part() {\n${@idt(2)}return __func.apply(__this, arguments);\n${@idt(1)}});" - "(function(__this) {\n${@idt(1)}var __func = $func;\n${@idt(1)}return $inner\n$@tab})(this)" + utility 'slice' + ref: new ValueNode literal utility 'bind' + (new CallNode ref, [literal(func), literal('this')]).compile o top_sensitive: -> true
#

When traversing (for printing or inspecting), return the real children of @@ -572,7 +543,6 @@ the function -- the parameters and body of expressions.

children: (child.toString(idt + TAB) for child in @real_children()).join('') "\n$idt$children"
#

SplatNode

#

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

exports.SplatNode: class SplatNode extends BaseNode
-  type: 'Splat'
 
   constructor: (name) ->
     name: literal(name) unless name.compile
@@ -587,31 +557,30 @@ the splat in the parameter list, by slicing the arguments object.

for trailing in @trailings o.scope.assign(trailing.compile(o), "arguments[arguments.length - $@trailings.length + $i]") i: + 1 - "$name = Array.prototype.slice.call(arguments, $@index, arguments.length - ${@trailings.length})"
#

A compiling a splat as a destructuring assignment means slicing arguments + "$name = ${utility('slice')}.call(arguments, $@index, arguments.length - ${@trailings.length})"

#

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

  compile_value: (o, name, index, trailings) ->
-    if trailings? then "Array.prototype.slice.call($name, $index, ${name}.length - $trailings)" \
-    else "Array.prototype.slice.call($name, $index)"
#

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

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

WhileNode

#

A while loop, the only sort of low-level loop exposed by CoffeeScript. From + trail: if trailings then ", ${name}.length - $trailings" else '' + "${utility 'slice'}.call($name, $index$trail)"

#

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

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

WhileNode

#

A while loop, the only sort of low-level loop exposed by CoffeeScript. From it, all other loops can be manufactured. Useful in cases where you need more flexibility or more speed than a comprehension can provide.

exports.WhileNode: class WhileNode extends BaseNode
-  type: 'While'
 
   constructor: (condition, opts) ->
     @children:[@condition: condition]
@@ -630,7 +599,7 @@ flexibility or more speed than a comprehension can provide.

while can be used as a part of a larger expression -- while loops may return an array containing the computed result of each iteration.

  compile_node: (o) ->
     top:        del(o, 'top') and not @returns
-    o.indent:   @idt(1)
+    o.indent:   @idt 1
     o.top:      true
     cond:       @condition.compile(o)
     set:        ''
@@ -648,15 +617,14 @@ return an array containing the computed result of each iteration.

"$pre {\n${ @body.compile(o) }\n$@tab}\n$post" statement WhileNode
#

OpNode

#

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

exports.OpNode: class OpNode extends BaseNode
-  type: 'Op'
#

The map of conversions from CoffeeScript to JavaScript symbols.

  CONVERSIONS: {
+CoffeeScript operations into their JavaScript equivalents.

exports.OpNode: class OpNode extends BaseNode
#

The map of conversions from CoffeeScript to JavaScript symbols.

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

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

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

Our assignment operators that have no JavaScript equivalent.

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

Operators must come before their operands with a space.

  PREFIX_OPERATORS: ['typeof', 'delete']
 
   constructor: (operator, first, second, flip) ->
-    @type: + ' ' + operator
+    @constructor.name: + ' ' + operator
     @children: compact [@first: first, @second: second]
     @operator: @CONVERSIONS[operator] or operator
     @flip: !!flip
@@ -697,7 +665,6 @@ to give us the safe references for the variables.

parts: [@operator, space, @first.compile(o)] parts: parts.reverse() if @flip parts.join('')
#

TryNode

#

A classic try/catch/finally block.

exports.TryNode: class TryNode extends BaseNode
-  type: 'Try'
 
   constructor: (attempt, error, recovery, ensure) ->
     @children: compact [@attempt: attempt, @recovery: recovery, @ensure: ensure]
@@ -709,7 +676,7 @@ to give us the safe references for the variables.

@recovery: @recovery.make_return() if @recovery this
#

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

  compile_node: (o) ->
-    o.indent:     @idt(1)
+    o.indent:     @idt 1
     o.top:        true
     attempt_part: @attempt.compile(o)
     error_part:   if @error then " (${ @error.compile(o) }) " else ' '
@@ -718,7 +685,6 @@ is optional, the catch is not.

"${@tab}try {\n$attempt_part\n$@tab}$catch_part$finally_part" statement TryNode
#

ThrowNode

#

Simple node to throw an exception.

exports.ThrowNode: class ThrowNode extends BaseNode
-  type: 'Throw'
 
   constructor: (expression) ->
     @children: [@expression: expression]
#

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

  make_return: ->
@@ -730,7 +696,6 @@ is optional, the catch is not.

statement ThrowNode
#

ExistenceNode

#

Checks a variable for existence -- not null and not undefined. This is similar to .nil? in Ruby, and avoids having to consult a JavaScript truth table.

exports.ExistenceNode: class ExistenceNode extends BaseNode
-  type: 'Existence'
 
   constructor: (expression) ->
     @children: [@expression: expression]
@@ -738,17 +703,16 @@ table.

compile_node: (o) -> ExistenceNode.compile_test(o, @expression)
#

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

ExistenceNode.compile_test: (o, variable) ->
-  [first, second]: [variable, variable]
-  if variable instanceof CallNode or (variable instanceof ValueNode and variable.has_properties())
-    [first, second]: variable.compile_reference(o)
-  [first, second]: [first.compile(o), second.compile(o)]
-  "(typeof $first !== \"undefined\" && $second !== null)"
#

ParentheticalNode

#

An extra set of parentheses, specified explicitly in the source. At one time +Be careful not to double-evaluate anything.

  @compile_test: (o, variable) ->
+    [first, second]: [variable, variable]
+    if variable instanceof CallNode or (variable instanceof ValueNode and variable.has_properties())
+      [first, second]: variable.compile_reference(o)
+    [first, second]: [first.compile(o), second.compile(o)]
+    "(typeof $first !== \"undefined\" && $second !== null)"
#

ParentheticalNode

#

An extra set of parentheses, specified explicitly in the source. At one time we tried to clean up the results by detecting and removing redundant parentheses, but no longer -- you can put in as many as you please.

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

exports.ParentheticalNode: class ParentheticalNode extends BaseNode
-  type: 'Paren'
 
   constructor: (expression) ->
     @children: [@expression: expression]
@@ -771,7 +735,6 @@ expression, able to return the result of each filtered iteration.

Unlike Python array comprehensions, they can be multi-line, and you can pass the current index of the loop as a second parameter. Unlike Ruby blocks, you can map and filter in a single pass.

exports.ForNode: class ForNode extends BaseNode
-  type: 'For'
 
   constructor: (body, source, name, index) ->
     @body:    body
@@ -806,7 +769,7 @@ some cannot.

< index: @index and @index.compile o scope.find name if name scope.find index if index - body_dent: @idt(1) + body_dent: @idt 1 rvar: scope.free_variable() unless top_level ivar: if range then name else index or scope.free_variable() var_part: '' @@ -833,8 +796,7 @@ some cannot.

< if @filter body: Expressions.wrap([new IfNode(@filter, body)]) if @object - o.scope.assign('__hasProp', 'Object.prototype.hasOwnProperty', true) - for_part: "$ivar in $svar) { if (__hasProp.call($svar, $ivar)" + for_part: "$ivar in $svar) { if (${utility('hasProp')}.call($svar, $ivar)" body: body.compile(merge(o, {indent: body_dent, top: true})) vars: if range then name else "$name, $ivar" close: if @object then '}}\n' else '}\n' @@ -845,7 +807,6 @@ expression by pushing down requested returns to the last line of each clause.

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

exports.IfNode: class IfNode extends BaseNode
-  type: 'If'
 
   constructor: (condition, body, else_body, tags) ->
     @condition: condition
@@ -903,7 +864,7 @@ force inner else bodies into statement form.

@rewrite_switch(o) if @switcher child: del o, 'chain_child' cond_o: merge o - o.indent: @idt(1) + o.indent: @idt 1 o.top: true if_dent: if child then '' else @idt() com_dent: if child then @idt() else '' @@ -937,8 +898,31 @@ in which case, no dice.

call: new CallNode(new ValueNode(func, [new AccessorNode(literal('call'))]), [literal('this')]) if statement then Expressions.wrap([call]) else call -}
#

Constants

#

Tabs are two spaces for pretty printing.

TAB: '  '
#

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

TRAILING_WHITESPACE: /\s+$/gm
#

Keep this identifier regex in sync with the Lexer.

IDENTIFIER: /^[a-zA-Z\$_](\w|\$)*$/
#

Utility Functions

#

Handy helper for a generating LiteralNode.

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

Utility Functions

UTILITIES: {
#

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

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

Bind a function to a calling context, optionally including curried arguments. +See Underscore's implementation.

  __bind:   """
+            function(func, obj, args) {
+                return function() {
+                  return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments);
+                };
+              }
+            """
#

Shortcuts to speed up the lookup time for native functions.

  __hasProp: 'Object.prototype.hasOwnProperty'
+  __slice: 'Array.prototype.slice'
+
+}
#

Constants

#

Tabs are two spaces for pretty printing.

TAB: '  '
#

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

TRAILING_WHITESPACE: /\s+$/gm
#

Keep this identifier regex in sync with the Lexer.

IDENTIFIER: /^[a-zA-Z\$_](\w|\$)*$/
#

Utility Functions

#

Handy helper for a generating LiteralNode.

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

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

utility: (name) ->
+  ref: "__$name"
+  Scope.root.assign ref, UTILITIES[ref]
+  ref
 
 
\ No newline at end of file diff --git a/documentation/docs/optparse.html b/documentation/docs/optparse.html index 8f8532d7..9878a0fe 100644 --- a/documentation/docs/optparse.html +++ b/documentation/docs/optparse.html @@ -17,7 +17,7 @@ many option parsers that allow you to attach callback actions for every flag. Instead, you're responsible for interpreting the options object.

  parse: (args) ->
     options: {arguments: []}
     args: normalize_arguments args
-    while arg: args.shift()
+    while (arg: args.shift())
       is_option: !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
       matched_rule: no
       for rule in @rules
diff --git a/documentation/docs/rewriter.html b/documentation/docs/rewriter.html
index d3281249..483e523b 100644
--- a/documentation/docs/rewriter.html
+++ b/documentation/docs/rewriter.html
@@ -20,7 +20,7 @@ corrected before implicit parentheses can be wrapped around blocks of code.

@close_open_calls_and_indexes() @add_implicit_indentation() @add_implicit_parentheses() - @ensure_balance(BALANCED_PAIRS) + @ensure_balance BALANCED_PAIRS @rewrite_closing_parens() @tokens
#

Rewrite the token stream, looking one token ahead and behind. Allow the return value of the block to tell us how many tokens to move @@ -30,7 +30,7 @@ our feet.

i: 0 while true break unless @tokens[i] - move: block(@tokens[i - 1], @tokens[i], @tokens[i + 1], i) + move: block @tokens[i - 1], @tokens[i], @tokens[i + 1], i i: + move true
#

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

  adjust_comments: ->
@@ -51,7 +51,7 @@ dispatch them here.

this, remove their trailing newlines.

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

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

  close_open_calls_and_indexes: ->
@@ -59,8 +59,8 @@ close.

brackets: [0] @scan_tokens (prev, token, post, i) => switch token[0] - when 'CALL_START' then parens.push(0) - when 'INDEX_START' then brackets.push(0) + when 'CALL_START' then parens.push 0 + when 'INDEX_START' then brackets.push 0 when '(' then parens[parens.length - 1]: + 1 when '[' then brackets[brackets.length - 1]: + 1 when ')' @@ -88,7 +88,7 @@ deal with them.

when 'CALL_END' then calls: - 1 when '(' then parens: + 1 when ')' then parens: - 1 - when 'INDENT' then stack.push(0) + when 'INDENT' then stack.push 0 when 'OUTDENT' last: stack.pop() stack[stack.length - 1]: + last @@ -104,9 +104,9 @@ deal with them.

size: stack[stack.length - stack_pointer] + 1 stack[stack.length - stack_pointer]: 0 return size - return 1 unless prev and include(IMPLICIT_FUNC, prev[0]) and include IMPLICIT_CALL, tag + return 1 unless prev and include(IMPLICIT_FUNC, prev[0]) and include(IMPLICIT_CALL, tag) calls: 0 - @tokens.splice(i, 0, ['CALL_START', '(', token[2]]) + @tokens.splice i, 0, ['CALL_START', '(', token[2]] stack[stack.length - 1]: + 1 return 2
#

Because our grammar is LALR(1), it can't handle some single-line expressions that lack ending delimiters. The Rewriter adds the implicit @@ -117,7 +117,7 @@ but we need to make sure it's balanced.

post[0] isnt 'INDENT' and not (token[0] is 'ELSE' and post[0] is 'IF') starter: token[0] - @tokens.splice(i + 1, 0, ['INDENT', 2, token[2]]) + @tokens.splice i + 1, 0, ['INDENT', 2, token[2]] idx: i + 1 parens: 0 while true @@ -131,12 +131,12 @@ but we need to make sure it's balanced.

insertion: if pre[0] is "," then idx - 1 else idx outdent: ['OUTDENT', 2, token[2]] outdent.generated: true - @tokens.splice(insertion, 0, outdent) + @tokens.splice insertion, 0, outdent break parens: + 1 if tok[0] is '(' parens: - 1 if tok[0] is ')' return 1 unless token[0] is 'THEN' - @tokens.splice(i, 1) + @tokens.splice i, 1 return 0
#

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

  ensure_balance: (pairs) ->
     levels: {}
diff --git a/documentation/docs/scope.html b/documentation/docs/scope.html
index ddb81d25..bd7751f6 100644
--- a/documentation/docs/scope.html
+++ b/documentation/docs/scope.html
@@ -5,38 +5,44 @@ and has a reference to its parent enclosing scope. In this way, we know which
 variables are new and need to be declared with var, and which are shared
 with the outside.

#

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

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

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

#

The top-level Scope object.

  @root: null
#

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

  constructor: (parent, expressions, method) ->
     [@parent, @expressions, @method]: [parent, expressions, method]
     @variables: {}
-    @temp_var: if @parent then @parent.temp_var else '_a'
#

Look up a variable name in lexical scope, and declare it if it does not + if @parent + @temp_var: @parent.temp_var + else + Scope.root: this + @temp_var: '_a'

#

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

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

Reserve a variable name as originating from a function parameter for this + false

#

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

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

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

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

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

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

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

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

If we need to store an intermediate result, find an available name for a + !!(@parent and @parent.check(name))

#

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

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

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

  assign: (name, value, top_level) ->
-    return @parent.assign(name, value, top_level) if top_level and @parent
-    @variables[name]: {value: value, assigned: true}
#

Does this scope reference any variables that need to be declared in the + @temp_var

#

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

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

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

  has_declarations: (body) ->
-    body is @expressions and @declared_variables().length
#

Does this scope reference any assignments that need to be declared at the + body is @expressions and @any (k, val) -> val is 'var'

#

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

  has_assignments: (body) ->
-    body is @expressions and @assigned_variables().length
#

Return the list of variables first declared in this scope.

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

Return the list of assignments that are supposed to be made at the top + body is @expressions and @any (k, val) -> val.assigned

#

Return the list of variables first declared in this scope.

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

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

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

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

  compiled_declarations: ->
-    @declared_variables().join ', '
#

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

  compiled_assignments: ->
+    "$key = ${val.value}" for key, val of @variables when val.assigned
#

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

  compiled_declarations: ->
+    @declared_variables().join ', '
#

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

  compiled_assignments: ->
     @assigned_variables().join ', '
 
 
\ No newline at end of file diff --git a/lib/nodes.js b/lib/nodes.js index f5af457a..60028d9e 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -16,7 +16,7 @@ // but some are created by other nodes as a method of code generation. To convert // the syntax tree into a string of JavaScript code, call `compile()` on the root. // Set up for both **Node.js** and the browser, by - // including the [Scope](scope.html) class. + // including the [Scope](scope.html) class and the [helper](helpers.html) functions. if ((typeof process !== "undefined" && process !== null)) { Scope = require('./scope').Scope; helpers = require('./helpers').helpers; @@ -25,7 +25,7 @@ helpers = this.helpers; Scope = this.Scope; } - // Import the helpers we need. + // Import the helpers we plan to use. compact = helpers.compact; flatten = helpers.flatten; merge = helpers.merge; @@ -158,7 +158,7 @@ BaseNode.prototype.toString = function toString(idt) { var _a, _b, _c, _d, child; idt = idt || ''; - return '\n' + idt + this.type + (function() { + return '\n' + idt + this.constructor.name + (function() { _a = []; _c = this.children; for (_b = 0, _d = _c.length; _b < _d; _b++) { child = _c[_b]; @@ -194,7 +194,6 @@ return this; }; __extends(Expressions, BaseNode); - Expressions.prototype.type = 'Expressions'; // Tack an expression on to the end of this expression list. Expressions.prototype.push = function push(node) { this.expressions.push(node); @@ -322,7 +321,6 @@ return this; }; __extends(LiteralNode, BaseNode); - LiteralNode.prototype.type = 'Literal'; // Break and continue must be treated as pure statements -- they lose their // meaning when wrapped in a closure. LiteralNode.prototype.is_statement = function is_statement() { @@ -349,7 +347,6 @@ return this; }; __extends(ReturnNode, BaseNode); - ReturnNode.prototype.type = 'Return'; ReturnNode.prototype.top_sensitive = function top_sensitive() { return true; }; @@ -377,7 +374,6 @@ return this; }; __extends(ValueNode, BaseNode); - ValueNode.prototype.type = 'Value'; ValueNode.prototype.SOAK = " == undefined ? undefined : "; // A **ValueNode** has a base and a list of property accesses. // Add a property access to the list. @@ -471,7 +467,6 @@ return this; }; __extends(CommentNode, BaseNode); - CommentNode.prototype.type = 'Comment'; CommentNode.prototype.make_return = function make_return() { return this; }; @@ -494,7 +489,6 @@ return this; }; __extends(CallNode, BaseNode); - CallNode.prototype.type = 'Call'; // Tag this invocation as creating a new instance. CallNode.prototype.new_instance = function new_instance() { this.is_new = true; @@ -564,7 +558,6 @@ return this; }; __extends(CurryNode, CallNode); - CurryNode.prototype.type = 'Curry'; CurryNode.prototype.arguments = function arguments(o) { var _a, _b, _c, arg; _b = this.args; @@ -594,7 +587,6 @@ return this; }; __extends(ExtendsNode, BaseNode); - ExtendsNode.prototype.type = 'Extends'; // Hooks one constructor into another's prototype chain. ExtendsNode.prototype.compile_node = function compile_node(o) { var ref; @@ -615,7 +607,6 @@ return this; }; __extends(AccessorNode, BaseNode); - AccessorNode.prototype.type = 'Accessor'; AccessorNode.prototype.compile_node = function compile_node(o) { var proto_part; proto_part = this.prototype ? 'prototype.' : ''; @@ -632,7 +623,6 @@ return this; }; __extends(IndexNode, BaseNode); - IndexNode.prototype.type = 'Index'; IndexNode.prototype.compile_node = function compile_node(o) { var idx; idx = this.index.compile(o); @@ -651,7 +641,6 @@ return this; }; __extends(RangeNode, BaseNode); - RangeNode.prototype.type = 'Range'; // Compiles the range's source variables -- where it starts and where it ends. RangeNode.prototype.compile_variables = function compile_variables(o) { var _a, _b, from, to; @@ -707,7 +696,6 @@ return this; }; __extends(SliceNode, BaseNode); - SliceNode.prototype.type = 'Slice'; SliceNode.prototype.compile_node = function compile_node(o) { var from, plus_part, to; from = this.range.from.compile(o); @@ -725,7 +713,6 @@ return this; }; __extends(ObjectNode, BaseNode); - ObjectNode.prototype.type = 'Object'; // All the mucking about with commas is to make sure that CommentNodes and // AssignNodes get interleaved correctly, with no trailing commas or // commas affixed to comments. @@ -775,7 +762,6 @@ return this; }; __extends(ArrayNode, BaseNode); - ArrayNode.prototype.type = 'Array'; ArrayNode.prototype.compile_node = function compile_node(o) { var _a, _b, code, ending, i, obj, objects; o.indent = this.idt(1); @@ -809,7 +795,6 @@ return this; }; __extends(ClassNode, BaseNode); - ClassNode.prototype.type = 'Class'; // Initialize a **ClassNode** with its name, an optional superclass, and a // list of prototype property assignments. ClassNode.prototype.make_return = function make_return() { @@ -869,7 +854,6 @@ return this; }; __extends(AssignNode, BaseNode); - AssignNode.prototype.type = 'Assign'; // Matchers for detecting prototype assignments. AssignNode.prototype.PROTO_ASSIGN = /^(\S+)\.prototype/; AssignNode.prototype.LEADING_DOT = /^\.(prototype\.)?/; @@ -993,7 +977,6 @@ return this; }; __extends(CodeNode, BaseNode); - CodeNode.prototype.type = 'Code'; // Compilation creates a new scope unless explicitly asked to share with the // outer scope. Handles splat parameters in the parameter list by peeking at // the JavaScript `arguments` objects. If the function is bound with the `=>` @@ -1099,7 +1082,6 @@ return this; }; __extends(SplatNode, BaseNode); - SplatNode.prototype.type = 'Splat'; SplatNode.prototype.compile_node = function compile_node(o) { var _a; if ((typeof (_a = this.index) !== "undefined" && _a !== null)) { @@ -1170,7 +1152,6 @@ return this; }; __extends(WhileNode, BaseNode); - WhileNode.prototype.type = 'While'; WhileNode.prototype.add_body = function add_body(body) { this.children.push((this.body = body)); return this; @@ -1219,14 +1200,13 @@ // CoffeeScript operations into their JavaScript equivalents. exports.OpNode = (function() { OpNode = function OpNode(operator, first, second, flip) { - this.type += ' ' + operator; + this.constructor.name += ' ' + operator; this.children = compact([(this.first = first), (this.second = second)]); this.operator = this.CONVERSIONS[operator] || operator; this.flip = !!flip; return this; }; __extends(OpNode, BaseNode); - OpNode.prototype.type = 'Op'; // The map of conversions from CoffeeScript to JavaScript symbols. OpNode.prototype.CONVERSIONS = { '==': '===', @@ -1327,7 +1307,6 @@ return this; }; __extends(TryNode, BaseNode); - TryNode.prototype.type = 'Try'; TryNode.prototype.make_return = function make_return() { if (this.attempt) { this.attempt = this.attempt.make_return(); @@ -1360,7 +1339,6 @@ return this; }; __extends(ThrowNode, BaseNode); - ThrowNode.prototype.type = 'Throw'; // A **ThrowNode** is already a return, of sorts... ThrowNode.prototype.make_return = function make_return() { return this; @@ -1381,7 +1359,6 @@ return this; }; __extends(ExistenceNode, BaseNode); - ExistenceNode.prototype.type = 'Existence'; ExistenceNode.prototype.compile_node = function compile_node(o) { return ExistenceNode.compile_test(o, this.expression); }; @@ -1416,7 +1393,6 @@ return this; }; __extends(ParentheticalNode, BaseNode); - ParentheticalNode.prototype.type = 'Paren'; ParentheticalNode.prototype.is_statement = function is_statement() { return this.expression.is_statement(); }; @@ -1468,7 +1444,6 @@ return this; }; __extends(ForNode, BaseNode); - ForNode.prototype.type = 'For'; ForNode.prototype.top_sensitive = function top_sensitive() { return true; }; @@ -1572,7 +1547,6 @@ return this; }; __extends(IfNode, BaseNode); - IfNode.prototype.type = 'If'; // Add a new *else* clause to this **IfNode**, or push it down to the bottom // of the chain recursively. IfNode.prototype.push = function push(else_body) { @@ -1737,11 +1711,12 @@ UTILITIES = { // Correctly set up a prototype chain for inheritance, including a reference // to the superclass for `super()` calls. See: - // [goog.inherits](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.source.html#line1206) + // [goog.inherits](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.source.html#line1206). __extends: "function(child, parent) {\n var ctor = function(){ };\n ctor.prototype = parent.prototype;\n child.__superClass__ = parent.prototype;\n child.prototype = new ctor();\n child.prototype.constructor = child;\n }", // Bind a function to a calling context, optionally including curried arguments. - // See [Underscore's implementation](http://jashkenas.github.com/coffee-script/documentation/docs/underscore.html#section-47) + // See [Underscore's implementation](http://jashkenas.github.com/coffee-script/documentation/docs/underscore.html#section-47). __bind: "function(func, obj, args) {\n return function() {\n return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments);\n };\n }", + // Shortcuts to speed up the lookup time for native functions. __hasProp: 'Object.prototype.hasOwnProperty', __slice: 'Array.prototype.slice' }; diff --git a/src/lexer.coffee b/src/lexer.coffee index f8eba6c6..87b68bc7 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -44,7 +44,7 @@ exports.Lexer: class Lexer # Before returning the token stream, run it through the [Rewriter](rewriter.html) # unless explicitly asked not to. tokenize: (code, options) -> - code : code.replace(/(\r|\s+$)/g, '') + code : code.replace /(\r|\s+$)/g, '' o : options or {} @code : code # The remainder of the source code. @i : 0 # Current character position we're parsing. @@ -53,7 +53,7 @@ exports.Lexer: class Lexer @indents : [] # The stack of all current indentation levels. @tokens : [] # Stream of parsed tokens in the form ['TYPE', value, line] while @i < @code.length - @chunk: @code.slice(@i) + @chunk: @code.slice @i @extract_next_token() @close_indentation() return @tokens if o.rewrite is off @@ -94,7 +94,7 @@ exports.Lexer: class Lexer identifier_token: -> return false unless id: @match IDENTIFIER, 1 @name_access_type() - accessed: include ACCESSORS, @tag(0) + accessed: include ACCESSORS, @tag 0 tag: 'IDENTIFIER' tag: id.toUpperCase() if not accessed and include(KEYWORDS, id) @identifier_error id if include RESERVED, id @@ -102,8 +102,8 @@ exports.Lexer: class Lexer @i: + id.length if not accessed tag: id: CONVERSIONS[id] if include COFFEE_ALIASES, id - return @tag_half_assignment(tag) if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag - @token(tag, id) + return @tag_half_assignment tag if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag + @token tag, id true # Matches numbers, including decimals, hex, and exponential notation. @@ -120,7 +120,7 @@ exports.Lexer: class Lexer return false unless string: @balanced_token(['"', '"'], ['${', '}']) or @balanced_token ["'", "'"] - @interpolate_string string.replace(STRING_NEWLINES, " \\\n") + @interpolate_string string.replace STRING_NEWLINES, " \\\n" @line: + count string, "\n" @i: + string.length true @@ -129,7 +129,7 @@ exports.Lexer: class Lexer # preserve whitespace, but ignore indentation to the left. heredoc_token: -> return false unless match: @chunk.match(HEREDOC) - quote: match[1].substr(0, 1) + quote: match[1].substr 0, 1 doc: @sanitize_heredoc match[2] or match[4], quote @interpolate_string "$quote$doc$quote" @line: + count match[1], "\n" @@ -140,7 +140,7 @@ exports.Lexer: class Lexer js_token: -> return false unless starts @chunk, '`' return false unless script: @balanced_token ['`', '`'] - @token 'JS', script.replace(JS_CLEANER, '') + @token 'JS', script.replace JS_CLEANER, '' @i: + script.length true @@ -152,7 +152,7 @@ exports.Lexer: class Lexer return false unless @chunk.match REGEX_START return false if include NOT_REGEX, @tag() return false unless regex: @balanced_token ['/', '/'] - regex: + (flags: @chunk.substr(regex.length).match(REGEX_FLAGS)) + regex: + (flags: @chunk.substr(regex.length).match REGEX_FLAGS) if regex.match REGEX_INTERPOLATION str: regex.substring(1).split('/')[0] str: str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped @@ -174,7 +174,7 @@ exports.Lexer: class Lexer comment_token: -> return false unless comment: @match COMMENT, 1 @line: + (comment.match(MULTILINER) or []).length - lines: compact comment.replace(COMMENT_CLEANER, '').split(MULTILINER) + lines: compact comment.replace(COMMENT_CLEANER, '').split MULTILINER i: @tokens.length - 1 if @unfinished() i: - 1 while @tokens[i] and not include LINE_BREAK, @tokens[i][0] @@ -202,7 +202,7 @@ exports.Lexer: class Lexer no_newlines: next_character is '.' or @unfinished() if size is @indent return @suppress_newlines() if no_newlines - return @newline_token(indent) + return @newline_token indent else if size > @indent return @suppress_newlines() if no_newlines diff: size - @indent @@ -249,14 +249,14 @@ exports.Lexer: class Lexer # here. `;` and newlines are both treated as a `TERMINATOR`, we distinguish # parentheses that indicate a method call from regular parentheses, and so on. literal_token: -> - match: @chunk.match(OPERATOR) + match: @chunk.match OPERATOR value: match and match[1] space: match and match[2] - @tag_parameters() if value and value.match(CODE) - value: or @chunk.substr(0, 1) + @tag_parameters() if value and value.match CODE + value: or @chunk.substr 0, 1 prev_spaced: @prev() and @prev().spaced tag: value - if value.match(ASSIGNMENT) + if value.match ASSIGNMENT tag: 'ASSIGN' @assignment_error() if include JS_FORBIDDEN, @value else if value is ';' @@ -272,7 +272,7 @@ exports.Lexer: class Lexer tag: 'CALL_START' if value is '(' tag: 'INDEX_START' if value is '[' @i: + value.length - return @tag_half_assignment(tag) if space and prev_spaced and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag + return @tag_half_assignment tag if space and prev_spaced and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag @token tag, value true @@ -312,7 +312,7 @@ exports.Lexer: class Lexer i: 0 while true i: + 1 - tok: @prev(i) + tok: @prev i return if not tok switch tok[0] when 'IDENTIFIER' then tok[0]: 'PARAM' @@ -322,7 +322,7 @@ exports.Lexer: class Lexer # Close up all remaining open blocks at the end of the file. close_indentation: -> - @outdent_token(@indent) + @outdent_token @indent # The error for when you try to use a forbidden word in JavaScript as # an identifier. @@ -350,7 +350,7 @@ exports.Lexer: class Lexer else lexer: new Lexer() tokens: [] - quote: str.substring(0, 1) + quote: str.substring 0, 1 [i, pi]: [1, 1] while i < str.length - 1 if starts str, '\\', i @@ -393,17 +393,17 @@ exports.Lexer: class Lexer # Add a token to the results, taking note of the line number. token: (tag, value) -> - @tokens.push([tag, value, @line]) + @tokens.push [tag, value, @line] # Peek at a tag in the current token stream. tag: (index, tag) -> - return unless tok: @prev(index) + return unless tok: @prev index return tok[0]: tag if tag? tok[0] # Peek at a value in the current token stream. value: (index, val) -> - return unless tok: @prev(index) + return unless tok: @prev index return tok[1]: val if val? tok[1] @@ -414,7 +414,7 @@ exports.Lexer: class Lexer # Attempt to match a string against the current chunk, returning the indexed # match if successful, and `false` otherwise. match: (regex, index) -> - return false unless m: @chunk.match(regex) + return false unless m: @chunk.match regex if m then m[index] else false # Are we in the midst of an unfinished expression? diff --git a/src/nodes.coffee b/src/nodes.coffee index f35bd587..4e7a17c8 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -4,7 +4,7 @@ # the syntax tree into a string of JavaScript code, call `compile()` on the root. # Set up for both **Node.js** and the browser, by -# including the [Scope](scope.html) class. +# including the [Scope](scope.html) class and the [helper](helpers.html) functions. if process? Scope: require('./scope').Scope helpers: require('./helpers').helpers @@ -13,7 +13,7 @@ else helpers: this.helpers Scope: this.Scope -# Import the helpers we need. +# Import the helpers we plan to use. compact: helpers.compact flatten: helpers.flatten merge: helpers.merge @@ -113,7 +113,7 @@ exports.BaseNode: class BaseNode # This is what `coffee --nodes` prints out. toString: (idt) -> idt: or '' - '\n' + idt + @type + (child.toString(idt + TAB) for child in @children).join('') + '\n' + idt + @constructor.name + (child.toString(idt + TAB) for child in @children).join('') # Default implementations of the common node identification methods. Nodes # will override these with custom logic, if needed. @@ -129,7 +129,6 @@ exports.BaseNode: class BaseNode # indented block of code -- the implementation of a function, a clause in an # `if`, `switch`, or `try`, and so on... exports.Expressions: class Expressions extends BaseNode - type: 'Expressions' constructor: (nodes) -> @children: @expressions: compact flatten nodes or [] @@ -214,7 +213,6 @@ statement Expressions # JavaScript without translation, such as: strings, numbers, # `true`, `false`, `null`... exports.LiteralNode: class LiteralNode extends BaseNode - type: 'Literal' constructor: (value) -> @value: value @@ -238,7 +236,6 @@ exports.LiteralNode: class LiteralNode extends BaseNode # A `return` is a *pure_statement* -- wrapping it in a closure wouldn't # make sense. exports.ReturnNode: class ReturnNode extends BaseNode - type: 'Return' constructor: (expression) -> @children: [@expression: expression] @@ -260,7 +257,6 @@ statement ReturnNode, true # A value, variable or literal or parenthesized, indexed or dotted into, # or vanilla. exports.ValueNode: class ValueNode extends BaseNode - type: 'Value' SOAK: " == undefined ? undefined : " @@ -335,7 +331,6 @@ exports.ValueNode: class ValueNode extends BaseNode # CoffeeScript passes through comments as JavaScript comments at the # same position. exports.CommentNode: class CommentNode extends BaseNode - type: 'Comment' constructor: (lines) -> @lines: lines @@ -354,7 +349,6 @@ statement CommentNode # Node for a function invocation. Takes care of converting `super()` calls into # calls against the prototype's function of the same name. exports.CallNode: class CallNode extends BaseNode - type: 'Call' constructor: (variable, args) -> @is_new: false @@ -406,7 +400,6 @@ exports.CallNode: class CallNode extends BaseNode # returning the bound function. After ECMAScript 5, Prototype.js, and # Underscore's `bind` functions. exports.CurryNode: class CurryNode extends CallNode - type: 'Curry' constructor: (meth, args) -> @children: flatten [@meth: meth, @context: args[0], @args: (args.slice(1) or [])] @@ -429,7 +422,6 @@ exports.CurryNode: class CurryNode extends CallNode # After `goog.inherits` from the # [Closure Library](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.html). exports.ExtendsNode: class ExtendsNode extends BaseNode - type: 'Extends' constructor: (child, parent) -> @children: [@child: child, @parent: parent] @@ -444,11 +436,10 @@ exports.ExtendsNode: class ExtendsNode extends BaseNode # A `.` accessor into a property of a value, or the `::` shorthand for # an accessor into the object's prototype. exports.AccessorNode: class AccessorNode extends BaseNode - type: 'Accessor' constructor: (name, tag) -> @children: [@name: name] - @prototype: tag is 'prototype' + @prototype:tag is 'prototype' @soak_node: tag is 'soak' this @@ -460,7 +451,6 @@ exports.AccessorNode: class AccessorNode extends BaseNode # A `[ ... ]` indexed accessor into an array or object. exports.IndexNode: class IndexNode extends BaseNode - type: 'Index' constructor: (index, tag) -> @children: [@index: index] @@ -476,7 +466,6 @@ exports.IndexNode: class IndexNode extends BaseNode # to specify a range for comprehensions, or as a value, to be expanded into the # corresponding array of integers at runtime. exports.RangeNode: class RangeNode extends BaseNode - type: 'Range' constructor: (from, to, exclusive) -> @children: [@from: from, @to: to] @@ -518,7 +507,6 @@ exports.RangeNode: class RangeNode extends BaseNode # specifies the index of the end of the slice, just as the first parameter # is the index of the beginning. exports.SliceNode: class SliceNode extends BaseNode - type: 'Slice' constructor: (range) -> @children: [@range: range] @@ -534,7 +522,6 @@ exports.SliceNode: class SliceNode extends BaseNode # An object literal, nothing fancy. exports.ObjectNode: class ObjectNode extends BaseNode - type: 'Object' constructor: (props) -> @children: @objects: @properties: props or [] @@ -545,14 +532,14 @@ exports.ObjectNode: class ObjectNode extends BaseNode # # *TODO: Extract this and add it to ArrayNode*. compile_node: (o) -> - o.indent: @idt(1) + o.indent: @idt 1 non_comments: prop for prop in @properties when not (prop instanceof CommentNode) last_noncom: non_comments[non_comments.length - 1] props: for prop, i in @properties join: ",\n" join: "\n" if (prop is last_noncom) or (prop instanceof CommentNode) join: '' if i is @properties.length - 1 - indent: if prop instanceof CommentNode then '' else @idt(1) + indent: if prop instanceof CommentNode then '' else @idt 1 indent + prop.compile(o) + join props: props.join('') inner: if props then '\n' + props + '\n' + @idt() else '' @@ -562,14 +549,13 @@ exports.ObjectNode: class ObjectNode extends BaseNode # An array literal. exports.ArrayNode: class ArrayNode extends BaseNode - type: 'Array' constructor: (objects) -> @children: @objects: objects or [] @compile_splat_literal: SplatNode.compile_mixed_array <- @, @objects compile_node: (o) -> - o.indent: @idt(1) + o.indent: @idt 1 objects: [] for obj, i in @objects code: obj.compile(o) @@ -589,7 +575,6 @@ exports.ArrayNode: class ArrayNode extends BaseNode # The CoffeeScript class definition. exports.ClassNode: class ClassNode extends BaseNode - type: 'Class' # Initialize a **ClassNode** with its name, an optional superclass, and a # list of prototype property assignments. @@ -645,7 +630,6 @@ statement ClassNode # The **AssignNode** is used to assign a local variable to value, or to set the # property of an object -- including within object literals. exports.AssignNode: class AssignNode extends BaseNode - type: 'Assign' # Matchers for detecting prototype assignments. PROTO_ASSIGN: /^(\S+)\.prototype/ @@ -735,7 +719,6 @@ exports.AssignNode: class AssignNode extends BaseNode # When for the purposes of walking the contents of a function body, the CodeNode # has no *children* -- they're within the inner scope. exports.CodeNode: class CodeNode extends BaseNode - type: 'Code' constructor: (params, body, tag) -> @params: params or [] @@ -804,7 +787,6 @@ exports.CodeNode: class CodeNode extends BaseNode # A splat, either as a parameter to a function, an argument to a call, # or as part of a destructuring assignment. exports.SplatNode: class SplatNode extends BaseNode - type: 'Splat' constructor: (name) -> name: literal(name) unless name.compile @@ -857,7 +839,6 @@ exports.SplatNode: class SplatNode extends BaseNode # it, all other loops can be manufactured. Useful in cases where you need more # flexibility or more speed than a comprehension can provide. exports.WhileNode: class WhileNode extends BaseNode - type: 'While' constructor: (condition, opts) -> @children:[@condition: condition] @@ -879,7 +860,7 @@ exports.WhileNode: class WhileNode extends BaseNode # return an array containing the computed result of each iteration. compile_node: (o) -> top: del(o, 'top') and not @returns - o.indent: @idt(1) + o.indent: @idt 1 o.top: true cond: @condition.compile(o) set: '' @@ -903,7 +884,6 @@ statement WhileNode # Simple Arithmetic and logical operations. Performs some conversion from # CoffeeScript operations into their JavaScript equivalents. exports.OpNode: class OpNode extends BaseNode - type: 'Op' # The map of conversions from CoffeeScript to JavaScript symbols. CONVERSIONS: { @@ -922,7 +902,7 @@ exports.OpNode: class OpNode extends BaseNode PREFIX_OPERATORS: ['typeof', 'delete'] constructor: (operator, first, second, flip) -> - @type: + ' ' + operator + @constructor.name: + ' ' + operator @children: compact [@first: first, @second: second] @operator: @CONVERSIONS[operator] or operator @flip: !!flip @@ -979,7 +959,6 @@ exports.OpNode: class OpNode extends BaseNode # A classic *try/catch/finally* block. exports.TryNode: class TryNode extends BaseNode - type: 'Try' constructor: (attempt, error, recovery, ensure) -> @children: compact [@attempt: attempt, @recovery: recovery, @ensure: ensure] @@ -994,7 +973,7 @@ exports.TryNode: class TryNode extends BaseNode # Compilation is more or less as you would expect -- the *finally* clause # is optional, the *catch* is not. compile_node: (o) -> - o.indent: @idt(1) + o.indent: @idt 1 o.top: true attempt_part: @attempt.compile(o) error_part: if @error then " (${ @error.compile(o) }) " else ' ' @@ -1008,7 +987,6 @@ statement TryNode # Simple node to throw an exception. exports.ThrowNode: class ThrowNode extends BaseNode - type: 'Throw' constructor: (expression) -> @children: [@expression: expression] @@ -1028,7 +1006,6 @@ statement ThrowNode # similar to `.nil?` in Ruby, and avoids having to consult a JavaScript truth # table. exports.ExistenceNode: class ExistenceNode extends BaseNode - type: 'Existence' constructor: (expression) -> @children: [@expression: expression] @@ -1054,7 +1031,6 @@ exports.ExistenceNode: class ExistenceNode extends BaseNode # # Parentheses are a good way to force any statement to become an expression. exports.ParentheticalNode: class ParentheticalNode extends BaseNode - type: 'Paren' constructor: (expression) -> @children: [@expression: expression] @@ -1082,7 +1058,6 @@ exports.ParentheticalNode: class ParentheticalNode extends BaseNode # the current index of the loop as a second parameter. Unlike Ruby blocks, # you can map and filter in a single pass. exports.ForNode: class ForNode extends BaseNode - type: 'For' constructor: (body, source, name, index) -> @body: body @@ -1120,7 +1095,7 @@ exports.ForNode: class ForNode extends BaseNode index: @index and @index.compile o scope.find name if name scope.find index if index - body_dent: @idt(1) + body_dent: @idt 1 rvar: scope.free_variable() unless top_level ivar: if range then name else index or scope.free_variable() var_part: '' @@ -1163,7 +1138,6 @@ statement ForNode # Single-expression **IfNodes** are compiled into ternary operators if possible, # because ternaries are already proper expressions, and don't need conversion. exports.IfNode: class IfNode extends BaseNode - type: 'If' constructor: (condition, body, else_body, tags) -> @condition: condition @@ -1242,7 +1216,7 @@ exports.IfNode: class IfNode extends BaseNode @rewrite_switch(o) if @switcher child: del o, 'chain_child' cond_o: merge o - o.indent: @idt(1) + o.indent: @idt 1 o.top: true if_dent: if child then '' else @idt() com_dent: if child then @idt() else '' @@ -1304,7 +1278,7 @@ UTILITIES: { # Correctly set up a prototype chain for inheritance, including a reference # to the superclass for `super()` calls. See: - # [goog.inherits](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.source.html#line1206) + # [goog.inherits](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.source.html#line1206). __extends: """ function(child, parent) { var ctor = function(){ }; @@ -1316,7 +1290,7 @@ UTILITIES: { """ # Bind a function to a calling context, optionally including curried arguments. - # See [Underscore's implementation](http://jashkenas.github.com/coffee-script/documentation/docs/underscore.html#section-47) + # See [Underscore's implementation](http://jashkenas.github.com/coffee-script/documentation/docs/underscore.html#section-47). __bind: """ function(func, obj, args) { return function() { @@ -1325,9 +1299,9 @@ UTILITIES: { } """ - __hasProp:'Object.prototype.hasOwnProperty' - - __slice: 'Array.prototype.slice' + # Shortcuts to speed up the lookup time for native functions. + __hasProp: 'Object.prototype.hasOwnProperty' + __slice: 'Array.prototype.slice' } diff --git a/src/rewriter.coffee b/src/rewriter.coffee index cf30f36d..7dcc0197 100644 --- a/src/rewriter.coffee +++ b/src/rewriter.coffee @@ -32,7 +32,7 @@ exports.Rewriter: class Rewriter @close_open_calls_and_indexes() @add_implicit_indentation() @add_implicit_parentheses() - @ensure_balance(BALANCED_PAIRS) + @ensure_balance BALANCED_PAIRS @rewrite_closing_parens() @tokens @@ -45,7 +45,7 @@ exports.Rewriter: class Rewriter i: 0 while true break unless @tokens[i] - move: block(@tokens[i - 1], @tokens[i], @tokens[i + 1], i) + move: block @tokens[i - 1], @tokens[i], @tokens[i + 1], i i: + move true @@ -75,7 +75,7 @@ exports.Rewriter: class Rewriter remove_mid_expression_newlines: -> @scan_tokens (prev, token, post, i) => return 1 unless post and include(EXPRESSION_CLOSE, post[0]) and token[0] is 'TERMINATOR' - @tokens.splice(i, 1) + @tokens.splice i, 1 return 0 # The lexer has tagged the opening parenthesis of a method call, and the @@ -86,8 +86,8 @@ exports.Rewriter: class Rewriter brackets: [0] @scan_tokens (prev, token, post, i) => switch token[0] - when 'CALL_START' then parens.push(0) - when 'INDEX_START' then brackets.push(0) + when 'CALL_START' then parens.push 0 + when 'INDEX_START' then brackets.push 0 when '(' then parens[parens.length - 1]: + 1 when '[' then brackets[brackets.length - 1]: + 1 when ')' @@ -118,7 +118,7 @@ exports.Rewriter: class Rewriter when 'CALL_END' then calls: - 1 when '(' then parens: + 1 when ')' then parens: - 1 - when 'INDENT' then stack.push(0) + when 'INDENT' then stack.push 0 when 'OUTDENT' last: stack.pop() stack[stack.length - 1]: + last @@ -134,9 +134,9 @@ exports.Rewriter: class Rewriter size: stack[stack.length - stack_pointer] + 1 stack[stack.length - stack_pointer]: 0 return size - return 1 unless prev and include(IMPLICIT_FUNC, prev[0]) and include IMPLICIT_CALL, tag + return 1 unless prev and include(IMPLICIT_FUNC, prev[0]) and include(IMPLICIT_CALL, tag) calls: 0 - @tokens.splice(i, 0, ['CALL_START', '(', token[2]]) + @tokens.splice i, 0, ['CALL_START', '(', token[2]] stack[stack.length - 1]: + 1 return 2 @@ -150,7 +150,7 @@ exports.Rewriter: class Rewriter post[0] isnt 'INDENT' and not (token[0] is 'ELSE' and post[0] is 'IF') starter: token[0] - @tokens.splice(i + 1, 0, ['INDENT', 2, token[2]]) + @tokens.splice i + 1, 0, ['INDENT', 2, token[2]] idx: i + 1 parens: 0 while true @@ -164,12 +164,12 @@ exports.Rewriter: class Rewriter insertion: if pre[0] is "," then idx - 1 else idx outdent: ['OUTDENT', 2, token[2]] outdent.generated: true - @tokens.splice(insertion, 0, outdent) + @tokens.splice insertion, 0, outdent break parens: + 1 if tok[0] is '(' parens: - 1 if tok[0] is ')' return 1 unless token[0] is 'THEN' - @tokens.splice(i, 1) + @tokens.splice i, 1 return 0 # Ensure that all listed pairs of tokens are correctly balanced throughout