mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
finished basic port to equality of the compiler
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# A simple **OptionParser** class to parse option flags from the command-line.
|
||||
# Use it like so:
|
||||
#
|
||||
# parser: new OptionParser switches, helpBanner
|
||||
# options: parser.parse process.argv
|
||||
# parser = new OptionParser switches, helpBanner
|
||||
# options = parser.parse process.argv
|
||||
#
|
||||
# The first non-option is considered to be the start of the file (and file
|
||||
# option) list, and all subsequent arguments are left unparsed.
|
||||
exports.OptionParser: class OptionParser
|
||||
exports.OptionParser = class OptionParser
|
||||
|
||||
# Initialize with a list of valid options, in the form:
|
||||
#
|
||||
@@ -14,8 +14,8 @@ exports.OptionParser: class OptionParser
|
||||
#
|
||||
# Along with an an optional banner for the usage help.
|
||||
constructor: (rules, banner) ->
|
||||
@banner: banner
|
||||
@rules: buildRules(rules)
|
||||
@banner = banner
|
||||
@rules = buildRules rules
|
||||
|
||||
# Parse the list of arguments, populating an `options` object with all of the
|
||||
# specified options, and returning it. `options.arguments` will be an array
|
||||
@@ -23,31 +23,31 @@ exports.OptionParser: class OptionParser
|
||||
# many option parsers that allow you to attach callback actions for every
|
||||
# flag. Instead, you're responsible for interpreting the options object.
|
||||
parse: (args) ->
|
||||
options: {arguments: []}
|
||||
args: normalizeArguments args
|
||||
options = {arguments: []}
|
||||
args = normalizeArguments args
|
||||
for arg, i in args
|
||||
isOption: !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
|
||||
matchedRule: no
|
||||
isOption = !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
|
||||
matchedRule = no
|
||||
for rule in @rules
|
||||
if rule.shortFlag is arg or rule.longFlag is arg
|
||||
options[rule.name]: if rule.hasArgument then args[i: + 1] else true
|
||||
matchedRule: yes
|
||||
options[rule.name] = if rule.hasArgument then args[i = + 1] else true
|
||||
matchedRule = yes
|
||||
break
|
||||
throw new Error "unrecognized option: $arg" if isOption and not matchedRule
|
||||
if not isOption
|
||||
options.arguments: args[i...args.length]
|
||||
options.arguments = args[i...args.length]
|
||||
break
|
||||
options
|
||||
|
||||
# Return the help text for this **OptionParser**, listing and describing all
|
||||
# of the valid options, for `--help` and such.
|
||||
help: ->
|
||||
lines: ['Available options:']
|
||||
lines = ['Available options:']
|
||||
lines.unshift "$@banner\n" if @banner
|
||||
for rule in @rules
|
||||
spaces: 15 - rule.longFlag.length
|
||||
spaces: if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
|
||||
letPart: if rule.shortFlag then rule.shortFlag + ', ' else ' '
|
||||
spaces = 15 - rule.longFlag.length
|
||||
spaces = if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
|
||||
letPart = if rule.shortFlag then rule.shortFlag + ', ' else ' '
|
||||
lines.push " $letPart$rule.longFlag$spaces$rule.description"
|
||||
"\n${ lines.join('\n') }\n"
|
||||
|
||||
@@ -55,38 +55,38 @@ exports.OptionParser: class OptionParser
|
||||
# -------
|
||||
|
||||
# Regex matchers for option flags.
|
||||
LONG_FLAG: /^(--\w[\w\-]+)/
|
||||
SHORT_FLAG: /^(-\w)/
|
||||
MULTI_FLAG: /^-(\w{2,})/
|
||||
OPTIONAL: /\[(.+)\]/
|
||||
LONG_FLAG = /^(--\w[\w\-]+)/
|
||||
SHORT_FLAG = /^(-\w)/
|
||||
MULTI_FLAG = /^-(\w{2,})/
|
||||
OPTIONAL = /\[(.+)\]/
|
||||
|
||||
# Build and return the list of option rules. If the optional *short-flag* is
|
||||
# unspecified, leave it out by padding with `null`.
|
||||
buildRules: (rules) ->
|
||||
buildRules = (rules) ->
|
||||
for tuple in rules
|
||||
tuple.unshift null if tuple.length < 3
|
||||
buildRule tuple...
|
||||
|
||||
# Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the
|
||||
# description of what the option does.
|
||||
buildRule: (shortFlag, longFlag, description) ->
|
||||
match: longFlag.match(OPTIONAL)
|
||||
longFlag: longFlag.match(LONG_FLAG)[1]
|
||||
buildRule = (shortFlag, longFlag, description) ->
|
||||
match = longFlag.match(OPTIONAL)
|
||||
longFlag = longFlag.match(LONG_FLAG)[1]
|
||||
{
|
||||
name: longFlag.substr 2
|
||||
shortFlag: shortFlag
|
||||
longFlag: longFlag
|
||||
shortFlag: shortFlag
|
||||
longFlag: longFlag
|
||||
description: description
|
||||
hasArgument: !!(match and match[1])
|
||||
hasArgument: !!(match and match[1])
|
||||
}
|
||||
|
||||
# Normalize arguments by expanding merged flags into multiple flags. This allows
|
||||
# you to have `-wl` be the same as `--watch --lint`.
|
||||
normalizeArguments: (args) ->
|
||||
args: args.slice 0
|
||||
result: []
|
||||
normalizeArguments = (args) ->
|
||||
args = args.slice 0
|
||||
result = []
|
||||
for arg in args
|
||||
if match: arg.match MULTI_FLAG
|
||||
if match = arg.match MULTI_FLAG
|
||||
result.push '-' + l for l in match[1].split ''
|
||||
else
|
||||
result.push arg
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
# coffee> puts "$num bottles of beer" for num in [99..1]
|
||||
|
||||
# Require the **coffee-script** module to get access to the compiler.
|
||||
CoffeeScript: require './coffee-script'
|
||||
helpers: require('./helpers').helpers
|
||||
readline: require 'readline'
|
||||
CoffeeScript = require './coffee-script'
|
||||
helpers = require('./helpers').helpers
|
||||
readline = require 'readline'
|
||||
|
||||
# Start by opening up **stdio**.
|
||||
stdio: process.openStdin()
|
||||
stdio = process.openStdin()
|
||||
|
||||
# Quick alias for quitting the REPL.
|
||||
helpers.extend global, {
|
||||
@@ -20,16 +20,16 @@ helpers.extend global, {
|
||||
# The main REPL function. **run** is called every time a line of code is entered.
|
||||
# Attempt to evaluate the command. If there's an exception, print it out instead
|
||||
# of exiting.
|
||||
run: (buffer) ->
|
||||
run = (buffer) ->
|
||||
try
|
||||
val: CoffeeScript.run buffer.toString(), {noWrap: true, globals: true, source: 'repl'}
|
||||
val = CoffeeScript.run buffer.toString(), {noWrap: true, globals: true, source: 'repl'}
|
||||
puts inspect val if val isnt undefined
|
||||
catch err
|
||||
puts err.stack or err.toString()
|
||||
repl.prompt()
|
||||
|
||||
# Create the REPL by listening to **stdin**.
|
||||
repl: readline.createInterface stdio
|
||||
repl = readline.createInterface stdio
|
||||
repl.setPrompt 'coffee> '
|
||||
stdio.on 'data', (buffer) -> repl.write buffer
|
||||
repl.on 'close', -> stdio.destroy()
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
|
||||
# Set up exported variables for both Node.js and the browser.
|
||||
if process?
|
||||
{helpers}: require('./helpers')
|
||||
{helpers} = require('./helpers')
|
||||
else
|
||||
this.exports: this
|
||||
helpers: this.helpers
|
||||
this.exports = this
|
||||
helpers = this.helpers
|
||||
|
||||
# Import the helpers we need.
|
||||
{include}: helpers
|
||||
{include} = helpers
|
||||
|
||||
# The **Rewriter** class is used by the [Lexer](lexer.html), directly against
|
||||
# its internal array of tokens.
|
||||
exports.Rewriter: class Rewriter
|
||||
exports.Rewriter = class Rewriter
|
||||
|
||||
# Rewrite the token stream in multiple passes, one logical filter at
|
||||
# a time. This could certainly be changed into a single pass through the
|
||||
@@ -25,7 +25,7 @@ exports.Rewriter: class Rewriter
|
||||
# like this. The order of these passes matters -- indentation must be
|
||||
# corrected before implicit parentheses can be wrapped around blocks of code.
|
||||
rewrite: (tokens) ->
|
||||
@tokens: tokens
|
||||
@tokens = tokens
|
||||
@adjustComments()
|
||||
@removeLeadingNewlines()
|
||||
@removeMidExpressionNewlines()
|
||||
@@ -42,11 +42,11 @@ exports.Rewriter: class Rewriter
|
||||
# as tokens are inserted and removed, and the stream changes length under
|
||||
# our feet.
|
||||
scanTokens: (block) ->
|
||||
i: 0
|
||||
i = 0
|
||||
loop
|
||||
break unless @tokens[i]
|
||||
move: block @tokens[i - 1], @tokens[i], @tokens[i + 1], i
|
||||
i: + move
|
||||
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
|
||||
@@ -54,7 +54,7 @@ exports.Rewriter: class Rewriter
|
||||
adjustComments: ->
|
||||
@scanTokens (prev, token, post, i) =>
|
||||
return 1 unless token[0] is 'HERECOMMENT'
|
||||
[before, after]: [@tokens[i - 2], @tokens[i + 2]]
|
||||
[before, after] = [@tokens[i - 2], @tokens[i + 2]]
|
||||
if after and after[0] is 'INDENT'
|
||||
@tokens.splice i + 2, 1
|
||||
if before and before[0] is 'OUTDENT' and post and prev[0] is post[0] is 'TERMINATOR'
|
||||
@@ -88,66 +88,66 @@ exports.Rewriter: class Rewriter
|
||||
# opening bracket of an indexing operation. Match them with their paired
|
||||
# close.
|
||||
closeOpenCallsAndIndexes: ->
|
||||
parens: [0]
|
||||
brackets: [0]
|
||||
parens = [0]
|
||||
brackets = [0]
|
||||
@scanTokens (prev, token, post, i) =>
|
||||
switch token[0]
|
||||
when 'CALL_START' then parens.push 0
|
||||
when 'INDEX_START' then brackets.push 0
|
||||
when '(' then parens[parens.length - 1]: + 1
|
||||
when '[' then brackets[brackets.length - 1]: + 1
|
||||
when '(' then parens[parens.length - 1] = + 1
|
||||
when '[' then brackets[brackets.length - 1] = + 1
|
||||
when ')'
|
||||
if parens[parens.length - 1] is 0
|
||||
parens.pop()
|
||||
token[0]: 'CALL_END'
|
||||
token[0] = 'CALL_END'
|
||||
else
|
||||
parens[parens.length - 1]: - 1
|
||||
parens[parens.length - 1] = - 1
|
||||
when ']'
|
||||
if brackets[brackets.length - 1] == 0
|
||||
brackets.pop()
|
||||
token[0]: 'INDEX_END'
|
||||
token[0] = 'INDEX_END'
|
||||
else
|
||||
brackets[brackets.length - 1]: - 1
|
||||
brackets[brackets.length - 1] = - 1
|
||||
return 1
|
||||
|
||||
# Methods may be optionally called without parentheses, for simple cases.
|
||||
# Insert the implicit parentheses here, so that the parser doesn't have to
|
||||
# deal with them.
|
||||
addImplicitParentheses: ->
|
||||
stack: [0]
|
||||
closeCalls: (i) =>
|
||||
stack = [0]
|
||||
closeCalls = (i) =>
|
||||
for tmp in [0...stack[stack.length - 1]]
|
||||
@tokens.splice(i, 0, ['CALL_END', ')', @tokens[i][2]])
|
||||
size: stack[stack.length - 1] + 1
|
||||
stack[stack.length - 1]: 0
|
||||
size = stack[stack.length - 1] + 1
|
||||
stack[stack.length - 1] = 0
|
||||
size
|
||||
@scanTokens (prev, token, post, i) =>
|
||||
tag: token[0]
|
||||
stack[stack.length - 2]: + stack.pop() if tag is 'OUTDENT'
|
||||
open: stack[stack.length - 1] > 0
|
||||
tag = token[0]
|
||||
stack[stack.length - 2] = + stack.pop() if tag is 'OUTDENT'
|
||||
open = stack[stack.length - 1] > 0
|
||||
if prev and prev.spaced and include(IMPLICIT_FUNC, prev[0]) and include(IMPLICIT_CALL, tag) and
|
||||
not (tag is '!' and (post[0] in ['IN', 'OF']))
|
||||
@tokens.splice i, 0, ['CALL_START', '(', token[2]]
|
||||
stack[stack.length - 1]: + 1
|
||||
stack[stack.length - 1] = + 1
|
||||
stack.push 0 if include(EXPRESSION_START, tag)
|
||||
return 2
|
||||
if include(EXPRESSION_START, tag)
|
||||
if tag is 'INDENT' and !token.generated and open and not (prev and include(IMPLICIT_BLOCK, prev[0]))
|
||||
size: closeCalls(i)
|
||||
size = closeCalls(i)
|
||||
stack.push 0
|
||||
return size
|
||||
stack.push 0
|
||||
return 1
|
||||
if open and !token.generated and prev[0] isnt ',' and (!post or include(IMPLICIT_END, tag))
|
||||
j: 1; j++ while (nx: @tokens[i + j])? and include(IMPLICIT_END, nx[0])
|
||||
j = 1; j++ while (nx = @tokens[i + j])? and include(IMPLICIT_END, nx[0])
|
||||
if nx? and nx[0] is ',' and @tokens[i + j - 1][0] is 'OUTDENT'
|
||||
@tokens.splice(i, 1) if tag is 'TERMINATOR'
|
||||
else
|
||||
size: closeCalls(i)
|
||||
size = closeCalls(i)
|
||||
stack.pop() if tag isnt 'OUTDENT' and include EXPRESSION_END, tag
|
||||
return size
|
||||
if tag isnt 'OUTDENT' and include EXPRESSION_END, tag
|
||||
stack[stack.length - 2]: + stack.pop()
|
||||
stack[stack.length - 2] = + stack.pop()
|
||||
return 1
|
||||
return 1
|
||||
|
||||
@@ -167,25 +167,25 @@ exports.Rewriter: class Rewriter
|
||||
return 1 unless include(SINGLE_LINERS, token[0]) and
|
||||
post[0] isnt 'INDENT' and
|
||||
not (token[0] is 'ELSE' and post[0] is 'IF')
|
||||
starter: token[0]
|
||||
[indent, outdent]: @indentation token
|
||||
indent.generated: outdent.generated: true
|
||||
starter = token[0]
|
||||
[indent, outdent] = @indentation token
|
||||
indent.generated = outdent.generated = true
|
||||
@tokens.splice i + 1, 0, indent
|
||||
idx: i + 1
|
||||
parens: 0
|
||||
idx = i + 1
|
||||
parens = 0
|
||||
loop
|
||||
idx: + 1
|
||||
tok: @tokens[idx]
|
||||
pre: @tokens[idx - 1]
|
||||
idx = + 1
|
||||
tok = @tokens[idx]
|
||||
pre = @tokens[idx - 1]
|
||||
if (not tok or
|
||||
(include(SINGLE_CLOSERS, tok[0]) and tok[1] isnt ';' and parens is 0) or
|
||||
(tok[0] is ')' and parens is 0)) and
|
||||
not (tok[0] is 'ELSE' and starter not in ['IF', 'THEN'])
|
||||
insertion: if pre[0] is "," then idx - 1 else idx
|
||||
insertion = if pre[0] is "," then idx - 1 else idx
|
||||
@tokens.splice insertion, 0, outdent
|
||||
break
|
||||
parens: + 1 if tok[0] is '('
|
||||
parens: - 1 if tok[0] is ')'
|
||||
parens = + 1 if tok[0] is '('
|
||||
parens = - 1 if tok[0] is ')'
|
||||
return 1 unless token[0] is 'THEN'
|
||||
@tokens.splice i, 1
|
||||
return 0
|
||||
@@ -193,22 +193,22 @@ exports.Rewriter: class Rewriter
|
||||
# Ensure that all listed pairs of tokens are correctly balanced throughout
|
||||
# the course of the token stream.
|
||||
ensureBalance: (pairs) ->
|
||||
levels: {}
|
||||
openLine: {}
|
||||
levels = {}
|
||||
openLine = {}
|
||||
@scanTokens (prev, token, post, i) =>
|
||||
for pair in pairs
|
||||
[open, close]: pair
|
||||
levels[open]: or 0
|
||||
[open, close] = pair
|
||||
levels[open] = or 0
|
||||
if token[0] is open
|
||||
openLine[open]: token[2] if levels[open] == 0
|
||||
levels[open]: + 1
|
||||
levels[open]: - 1 if token[0] is close
|
||||
openLine[open] = token[2] if levels[open] == 0
|
||||
levels[open] = + 1
|
||||
levels[open] = - 1 if token[0] is close
|
||||
throw new Error("too many ${token[1]} on line ${token[2] + 1}") if levels[open] < 0
|
||||
return 1
|
||||
unclosed: key for key, value of levels when value > 0
|
||||
unclosed = key for key, value of levels when value > 0
|
||||
if unclosed.length
|
||||
open: unclosed[0]
|
||||
line: openLine[open] + 1
|
||||
open = unclosed[0]
|
||||
line = openLine[open] + 1
|
||||
throw new Error "unclosed $open on line $line"
|
||||
|
||||
# We'd like to support syntax like this:
|
||||
@@ -228,27 +228,27 @@ exports.Rewriter: class Rewriter
|
||||
# 4. Be careful not to alter array or parentheses delimiters with overzealous
|
||||
# rewriting.
|
||||
rewriteClosingParens: ->
|
||||
stack: []
|
||||
debt: {}
|
||||
(debt[key]: 0) for key, val of INVERSES
|
||||
stack = []
|
||||
debt = {}
|
||||
(debt[key] = 0) for key, val of INVERSES
|
||||
@scanTokens (prev, token, post, i) =>
|
||||
tag: token[0]
|
||||
inv: INVERSES[token[0]]
|
||||
tag = token[0]
|
||||
inv = INVERSES[token[0]]
|
||||
if include EXPRESSION_START, tag
|
||||
stack.push token
|
||||
return 1
|
||||
else if include EXPRESSION_END, tag
|
||||
if debt[inv] > 0
|
||||
debt[inv]: - 1
|
||||
debt[inv] = - 1
|
||||
@tokens.splice i, 1
|
||||
return 0
|
||||
else
|
||||
match: stack.pop()
|
||||
mtag: match[0]
|
||||
oppos: INVERSES[mtag]
|
||||
match = stack.pop()
|
||||
mtag = match[0]
|
||||
oppos = INVERSES[mtag]
|
||||
return 1 if tag is oppos
|
||||
debt[mtag]: + 1
|
||||
val: [oppos, if mtag is 'INDENT' then match[1] else oppos]
|
||||
debt[mtag] = + 1
|
||||
val = [oppos, if mtag is 'INDENT' then match[1] else oppos]
|
||||
if @tokens[i + 2]?[0] is mtag
|
||||
@tokens.splice i + 3, 0, val
|
||||
stack.push(match)
|
||||
@@ -266,42 +266,43 @@ exports.Rewriter: class Rewriter
|
||||
# ---------
|
||||
|
||||
# List of the token pairs that must be balanced.
|
||||
BALANCED_PAIRS: [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'],
|
||||
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'],
|
||||
['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END']]
|
||||
|
||||
# The inverse mappings of `BALANCED_PAIRS` we're trying to fix up, so we can
|
||||
# look things up from either end.
|
||||
INVERSES: {}
|
||||
INVERSES = {}
|
||||
for pair in BALANCED_PAIRS
|
||||
INVERSES[pair[0]]: pair[1]
|
||||
INVERSES[pair[1]]: pair[0]
|
||||
INVERSES[pair[0]] = pair[1]
|
||||
INVERSES[pair[1]] = pair[0]
|
||||
|
||||
# The tokens that signal the start of a balanced pair.
|
||||
EXPRESSION_START: pair[0] for pair in BALANCED_PAIRS
|
||||
EXPRESSION_START = pair[0] for pair in BALANCED_PAIRS
|
||||
|
||||
# The tokens that signal the end of a balanced pair.
|
||||
EXPRESSION_END: pair[1] for pair in BALANCED_PAIRS
|
||||
EXPRESSION_END = pair[1] for pair in BALANCED_PAIRS
|
||||
|
||||
# Tokens that indicate the close of a clause of an expression.
|
||||
EXPRESSION_CLOSE: ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END
|
||||
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END
|
||||
|
||||
# Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation.
|
||||
IMPLICIT_FUNC: ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@']
|
||||
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@']
|
||||
|
||||
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
||||
IMPLICIT_CALL: ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START',
|
||||
'TRY', 'DELETE', 'TYPEOF', 'SWITCH',
|
||||
'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!',
|
||||
'THIS', 'NULL',
|
||||
'@', '->', '=>', '[', '(', '{']
|
||||
IMPLICIT_CALL = [
|
||||
'IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START',
|
||||
'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'THIS', 'NULL',
|
||||
'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF',
|
||||
'!', '!!', '@', '->', '=>', '[', '(', '{'
|
||||
]
|
||||
|
||||
# Tokens indicating that the implicit call must enclose a block of expressions.
|
||||
IMPLICIT_BLOCK: ['->', '=>', '{', '[', ',']
|
||||
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ',']
|
||||
|
||||
# Tokens that always mark the end of an implicit call for single-liners.
|
||||
IMPLICIT_END: ['IF', 'UNLESS', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'TERMINATOR', 'INDENT'].concat EXPRESSION_END
|
||||
IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'TERMINATOR', 'INDENT'].concat EXPRESSION_END
|
||||
|
||||
# Single-line flavors of block expressions that have unclosed endings.
|
||||
# The grammar can't disambiguate them, so we insert the implicit indentation.
|
||||
SINGLE_LINERS: ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN']
|
||||
SINGLE_CLOSERS: ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN']
|
||||
SINGLE_LINERS = ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN']
|
||||
SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN']
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
# with the outside.
|
||||
|
||||
# Set up exported variables for both **Node.js** and the browser.
|
||||
this.exports: this unless process?
|
||||
this.exports = this unless process?
|
||||
|
||||
exports.Scope: class Scope
|
||||
exports.Scope = class Scope
|
||||
|
||||
# The top-level **Scope** object.
|
||||
@root: null
|
||||
@@ -18,19 +18,19 @@ exports.Scope: class Scope
|
||||
# where it should declare its variables, and a reference to the function that
|
||||
# it wraps.
|
||||
constructor: (parent, expressions, method) ->
|
||||
[@parent, @expressions, @method]: [parent, expressions, method]
|
||||
@variables: {}
|
||||
[@parent, @expressions, @method] = [parent, expressions, method]
|
||||
@variables = {}
|
||||
if @parent
|
||||
@tempVar: @parent.tempVar
|
||||
@tempVar = @parent.tempVar
|
||||
else
|
||||
Scope.root: this
|
||||
@tempVar: '_a'
|
||||
Scope.root = this
|
||||
@tempVar = '_a'
|
||||
|
||||
# Look up a variable name in lexical scope, and declare it if it does not
|
||||
# already exist.
|
||||
find: (name) ->
|
||||
return true if @check name
|
||||
@variables[name]: 'var'
|
||||
@variables[name] = 'var'
|
||||
false
|
||||
|
||||
# Test variables and return true the first time fn(v, k) returns true
|
||||
@@ -42,7 +42,7 @@ exports.Scope: class Scope
|
||||
# Reserve a variable name as originating from a function parameter for this
|
||||
# scope. No `var` required for internal references.
|
||||
parameter: (name) ->
|
||||
@variables[name]: 'param'
|
||||
@variables[name] = 'param'
|
||||
|
||||
# Just check to see if a variable has already been declared, without reserving.
|
||||
check: (name) ->
|
||||
@@ -53,15 +53,15 @@ exports.Scope: class Scope
|
||||
# compiler-generated variable. `_a`, `_b`, and so on...
|
||||
freeVariable: ->
|
||||
while @check @tempVar
|
||||
ordinal: 1 + parseInt @tempVar.substr(1), 36
|
||||
@tempVar: '_' + ordinal.toString(36).replace(/\d/g, 'a')
|
||||
@variables[@tempVar]: 'var'
|
||||
ordinal = 1 + parseInt @tempVar.substr(1), 36
|
||||
@tempVar = '_' + ordinal.toString(36).replace(/\d/g, 'a')
|
||||
@variables[@tempVar] = 'var'
|
||||
@tempVar
|
||||
|
||||
# Ensure that an assignment is made at the top of this scope
|
||||
# (or at the top-level scope, if requested).
|
||||
assign: (name, value) ->
|
||||
@variables[name]: {value: value, assigned: true}
|
||||
@variables[name] = {value: value, assigned: true}
|
||||
|
||||
# Does this scope reference any variables that need to be declared in the
|
||||
# given function body?
|
||||
|
||||
Reference in New Issue
Block a user