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. 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. 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. 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
trueMatches 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. 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
trueMatches 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.
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
trueAs 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'
trueClose 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. 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 falseAre 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: []Keywords that CoffeeScript shares in common with JavaScript.
JS_KEYWORDS: [
+ prev and (prev[0] isnt '.') and not @value().match(CODE)There are no exensions to the core lexer by default.
@extensions: []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. 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_KEYWORDSThe 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_KEYWORDSThe 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 RESERVEDToken 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 RESERVEDToken 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. Regex-matching-regexes.
REGEX_START : /^\/[^\/ ]/
+ASSIGNMENT : /^(:|=)$/Regex-matching-regexes.
REGEX_START : /^\/[^\/ ]/
REGEX_INTERPOLATION: /([^\\]\$[a-zA-Z_@]|[^\\]\$\{.*[^\\]\})/
REGEX_FLAGS : /^[imgy]{0,4}/
-REGEX_ESCAPE : /\\[^\$]/gToken cleaning regexes.
JS_CLEANER : /(^`|`$)/g
+REGEX_ESCAPE : /\\[^\$]/gToken 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]+/mgTokens 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?
Scope: require('./scope').Scope
helpers: require('./helpers').helpers
else
this.exports: this
helpers: this.helpers
- Scope: this.ScopeImport the helpers we need.
compact: helpers.compact
+ Scope: this.ScopeImport the helpers we plan to use.
compact: helpers.compact
flatten: helpers.flatten
merge: helpers.merge
del: helpers.delHelper function that marks a node as a JavaScript statement, or as a @@ -72,7 +72,7 @@ a pure statement?
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. 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. 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: valueBreak and continue must be treated as pure statements -- they lose their @@ -143,7 +141,6 @@ meaning when wrapped in a closure.
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. 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. 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. 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. 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. 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
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)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
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)}"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. 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. 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. 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}"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"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"
The CoffeeScript class definition.
exports.ClassNode: class ClassNode extends BaseNodeInitialize 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 ClassNodeThe 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 BaseNodeMatchers for detecting prototype assignments.
PROTO_ASSIGN: /^(\S+)\.prototype/
LEADING_DOT: /^\.(prototype\.)?/
constructor: (variable, value, context) ->
@@ -508,7 +479,7 @@ for details. 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. 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. When traversing (for printing or inspecting), return the real children of @@ -572,7 +543,6 @@ the function -- the parameters and body of expressions.
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('')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('')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. 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 WhileNodeSimple 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 BaseNodeThe 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. 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. 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. 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. 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. 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)"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)"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. 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. 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+$/gmKeep this identifier regex in sync with the Lexer.
IDENTIFIER: /^[a-zA-Z\$_](\w|\$)*$/Handy helper for a generating LiteralNode.
literal: (name) ->
- new LiteralNode(name)
+}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'
+
+}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+$/gmKeep this identifier regex in sync with the Lexer.
IDENTIFIER: /^[a-zA-Z\$_](\w|\$)*$/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