mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-02-19 03:44:23 -05:00
Finishing off the docs for nodes.coffee -- almost ready to roll.
This commit is contained in:
@@ -940,7 +940,7 @@ statement TryNode
|
||||
|
||||
#### ThrowNode
|
||||
|
||||
# Throw an exception.
|
||||
# Simple node to throw an exception.
|
||||
exports.ThrowNode: class ThrowNode extends BaseNode
|
||||
type: 'Throw'
|
||||
|
||||
@@ -952,8 +952,11 @@ exports.ThrowNode: class ThrowNode extends BaseNode
|
||||
|
||||
statement ThrowNode
|
||||
|
||||
#### ExistenceNode
|
||||
|
||||
# Check an expression for existence (meaning not null or undefined).
|
||||
# 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'
|
||||
|
||||
@@ -963,6 +966,9 @@ exports.ExistenceNode: class ExistenceNode extends BaseNode
|
||||
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())
|
||||
@@ -970,8 +976,13 @@ ExistenceNode.compile_test: (o, variable) ->
|
||||
[first, second]: [first.compile(o), second.compile(o)]
|
||||
"(typeof $first !== \"undefined\" && $second !== null)"
|
||||
|
||||
#### ParentheticalNode
|
||||
|
||||
# An extra set of parentheses, specified explicitly in the source.
|
||||
# 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'
|
||||
|
||||
@@ -988,11 +999,15 @@ exports.ParentheticalNode: class ParentheticalNode extends BaseNode
|
||||
code: code.substr(o, l-1) if code.substr(l-1, 1) is ';'
|
||||
"($code)"
|
||||
|
||||
#### ForNode
|
||||
|
||||
# The replacement for the for loop is an array comprehension (that compiles)
|
||||
# into a for loop. Also acts as an expression, able to return the result
|
||||
# of the comprehenion. Unlike Python array comprehensions, it's able to pass
|
||||
# the current index of the loop as a second parameter.
|
||||
# CoffeeScript's replacement for the *for* loop is our array and object
|
||||
# comprehensions, that compile into *for* loops here. They also act as an
|
||||
# expression, able to return the result of each filtered iteration.
|
||||
#
|
||||
# Unlike Python array comprehensions, they can be multi-line, and you can pass
|
||||
# the current index of the loop as a second parameter. Unlike Ruby blocks,
|
||||
# you can map and filter in a single pass.
|
||||
exports.ForNode: class ForNode extends BaseNode
|
||||
type: 'For'
|
||||
|
||||
@@ -1010,6 +1025,10 @@ exports.ForNode: class ForNode extends BaseNode
|
||||
top_sensitive: ->
|
||||
true
|
||||
|
||||
# Welcome to the hairiest method in all of CoffeeScript. Handles the inner
|
||||
# loop, filtering, stepping, and result saving for array, object, and range
|
||||
# comprehensions. Some of the generated code can be shared in common, and
|
||||
# some cannot.
|
||||
compile_node: (o) ->
|
||||
top_level: del(o, 'top') and not o.returns
|
||||
range: @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length
|
||||
@@ -1059,11 +1078,13 @@ exports.ForNode: class ForNode extends BaseNode
|
||||
|
||||
statement ForNode
|
||||
|
||||
#### IfNode
|
||||
|
||||
# If/else statements. Switch/whens get compiled into these. Acts as an
|
||||
# expression by pushing down requested returns to the expression bodies.
|
||||
# Single-expression IfNodes are compiled into ternary operators if possible,
|
||||
# because ternaries are first-class returnable assignable expressions.
|
||||
# *If/else* statements. Our *switch/when* will be compiled into this. Acts as an
|
||||
# expression by pushing down requested returns to the last line of each clause.
|
||||
#
|
||||
# Single-expression **IfNodes** are compiled into ternary operators if possible,
|
||||
# because ternaries are already proper expressions, and don't need conversion.
|
||||
exports.IfNode: class IfNode extends BaseNode
|
||||
type: 'If'
|
||||
|
||||
@@ -1076,6 +1097,8 @@ exports.IfNode: class IfNode extends BaseNode
|
||||
@multiple: true if @condition instanceof Array
|
||||
@condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert
|
||||
|
||||
# Add a new *else* clause to this **IfNode**, or push it down to the bottom
|
||||
# of the chain recursively.
|
||||
push: (else_body) ->
|
||||
eb: else_body.unwrap()
|
||||
if @else_body then @else_body.push(eb) else @else_body: eb
|
||||
@@ -1085,12 +1108,14 @@ exports.IfNode: class IfNode extends BaseNode
|
||||
@tags.statement: true
|
||||
this
|
||||
|
||||
# Tag a chain of IfNodes with their switch condition for equality.
|
||||
# Tag a chain of **IfNodes** with their object(s) to switch on for equality
|
||||
# tests. `rewrite_switch` will perform the actual change at compile time.
|
||||
rewrite_condition: (expression) ->
|
||||
@switcher: expression
|
||||
this
|
||||
|
||||
# Rewrite a chain of IfNodes with their switch condition for equality.
|
||||
# Rewrite a chain of **IfNodes** with their switch condition for equality.
|
||||
# Ensure that the switch expression isn't evaluated more than once.
|
||||
rewrite_switch: (o) ->
|
||||
assigner: @switcher
|
||||
if not (@switcher.unwrap() instanceof LiteralNode)
|
||||
@@ -1105,7 +1130,7 @@ exports.IfNode: class IfNode extends BaseNode
|
||||
@else_body.rewrite_condition(@switcher) if @is_chain()
|
||||
this
|
||||
|
||||
# Rewrite a chain of IfNodes to add a default case as the final else.
|
||||
# Rewrite a chain of **IfNodes** to add a default case as the final *else*.
|
||||
add_else: (exprs, statement) ->
|
||||
if @is_chain()
|
||||
@else_body.add_else exprs, statement
|
||||
@@ -1114,12 +1139,12 @@ exports.IfNode: class IfNode extends BaseNode
|
||||
@children.push @else_body: exprs
|
||||
this
|
||||
|
||||
# If the else_body is an IfNode itself, then we've got an if-else chain.
|
||||
# If the `else_body` is an **IfNode** itself, then we've got an *if-else* chain.
|
||||
is_chain: ->
|
||||
@chain ||= @else_body and @else_body instanceof IfNode
|
||||
|
||||
# The IfNode only compiles into a statement if either of the bodies needs
|
||||
# to be a statement.
|
||||
# The **IfNode** only compiles into a statement if either of its bodies needs
|
||||
# to be a statement. Otherwise a ternary is safe.
|
||||
is_statement: ->
|
||||
@statement ||= !!(@comment or @tags.statement or @body.is_statement() or (@else_body and @else_body.is_statement()))
|
||||
|
||||
@@ -1129,8 +1154,8 @@ exports.IfNode: class IfNode extends BaseNode
|
||||
compile_node: (o) ->
|
||||
if @is_statement() then @compile_statement(o) else @compile_ternary(o)
|
||||
|
||||
# Compile the IfNode as a regular if-else statement. Flattened chains
|
||||
# force sub-else bodies into statement form.
|
||||
# Compile the **IfNode** as a regular *if-else* statement. Flattened chains
|
||||
# force inner *else* bodies into statement form.
|
||||
compile_statement: (o) ->
|
||||
@rewrite_switch(o) if @switcher
|
||||
child: del o, 'chain_child'
|
||||
@@ -1150,7 +1175,7 @@ exports.IfNode: class IfNode extends BaseNode
|
||||
" else {\n${ Expressions.wrap([@else_body]).compile(o) }\n$@tab}"
|
||||
"$if_part$else_part"
|
||||
|
||||
# Compile the IfNode into a ternary operator.
|
||||
# Compile the IfNode as a ternary operator.
|
||||
compile_ternary: (o) ->
|
||||
if_part: @condition.compile(o) + ' ? ' + @body.compile(o)
|
||||
else_part: if @else_body then @else_body.compile(o) else 'null'
|
||||
@@ -1161,15 +1186,20 @@ exports.IfNode: class IfNode extends BaseNode
|
||||
|
||||
# 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 the identifier regex in sync with the Lexer.
|
||||
IDENTIFIER: /^[a-zA-Z$_](\w|\$)*$/
|
||||
# Keep this identifier regex in sync with the Lexer.
|
||||
IDENTIFIER: /^[a-zA-Z$_](\w|\$)*$/
|
||||
|
||||
# Utility Functions
|
||||
# -----------------
|
||||
|
||||
# Merge objects.
|
||||
# Merge objects, returning a fresh copy with attributes from both sides.
|
||||
# Used every time `compile` is called, to allow properties in the options hash
|
||||
# to propagate down the tree without polluting other branches.
|
||||
merge: (options, overrides) ->
|
||||
fresh: {}
|
||||
(fresh[key]: val) for key, val of options
|
||||
@@ -1179,19 +1209,21 @@ merge: (options, overrides) ->
|
||||
# Trim out all falsy values from an array.
|
||||
compact: (array) -> item for item in array when item
|
||||
|
||||
# Return a completely flattened version of an array.
|
||||
# Return a completely flattened version of an array. Handy for getting a
|
||||
# list of `children`.
|
||||
flatten: (array) ->
|
||||
memo: []
|
||||
for item in array
|
||||
if item instanceof Array then memo: memo.concat(item) else memo.push(item)
|
||||
memo
|
||||
|
||||
# Delete a key from an object, returning the value.
|
||||
# Delete a key from an object, returning the value. Useful when a node is
|
||||
# looking for a particular method in an options hash.
|
||||
del: (obj, key) ->
|
||||
val: obj[key]
|
||||
delete obj[key]
|
||||
val
|
||||
|
||||
# Quickie helper for a generated LiteralNode.
|
||||
# Handy helper for a generating LiteralNode.
|
||||
literal: (name) ->
|
||||
new LiteralNode(name)
|
||||
|
||||
Reference in New Issue
Block a user