1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

Fixed lingering CoffeeScript Compiler running live in Internet Explorer bugs. Implemented helpers.index_of and removed named functions. Ticket #366

This commit is contained in:
Jeremy Ashkenas 2010-05-14 23:40:04 -04:00
parent f84eb9ed47
commit dfb3a13246
16 changed files with 319 additions and 298 deletions

File diff suppressed because one or more lines are too long

View file

@ -22,7 +22,7 @@
helpers.extend(global, { helpers.extend(global, {
// Define a Cake task with a short name, an optional sentence description, // Define a Cake task with a short name, an optional sentence description,
// and the function to run as the action itself. // and the function to run as the action itself.
task: function task(name, description, action) { task: function(name, description, action) {
var _a; var _a;
if (!(action)) { if (!(action)) {
_a = [description, action]; _a = [description, action];
@ -39,11 +39,11 @@
// Define an option that the Cakefile accepts. The parsed options hash, // Define an option that the Cakefile accepts. The parsed options hash,
// containing all of the command-line options passed, will be made available // containing all of the command-line options passed, will be made available
// as the first argument to the action. // as the first argument to the action.
option: function option(letter, flag, description) { option: function(letter, flag, description) {
return switches.push([letter, flag, description]); return switches.push([letter, flag, description]);
}, },
// Invoke another task in the current Cakefile. // Invoke another task in the current Cakefile.
invoke: function invoke(name) { invoke: function(name) {
if (!(tasks[name])) { if (!(tasks[name])) {
no_such_task(name); no_such_task(name);
} }
@ -53,7 +53,7 @@
// Run `cake`. Executes all of the tasks you pass, in order. Note that Node's // Run `cake`. Executes all of the tasks you pass, in order. Note that Node's
// asynchrony may cause tasks to execute in a different order than you'd expect. // asynchrony may cause tasks to execute in a different order than you'd expect.
// If no tasks are passed, print the help screen. // If no tasks are passed, print the help screen.
exports.run = function run() { exports.run = function() {
return path.exists('Cakefile', function(exists) { return path.exists('Cakefile', function(exists) {
var _a, _b, _c, _d, arg, args; var _a, _b, _c, _d, arg, args;
if (!(exists)) { if (!(exists)) {
@ -77,7 +77,7 @@
}); });
}; };
// Display the list of Cake tasks in a format similar to `rake -T` // Display the list of Cake tasks in a format similar to `rake -T`
print_tasks = function print_tasks() { print_tasks = function() {
var _a, _b, _c, _d, desc, i, name, spaces, task; var _a, _b, _c, _d, desc, i, name, spaces, task;
puts(''); puts('');
_a = tasks; _a = tasks;
@ -99,7 +99,7 @@
} }
}; };
// Print an error and exit when attempting to all an undefined task. // Print an error and exit when attempting to all an undefined task.
no_such_task = function no_such_task(task) { no_such_task = function(task) {
puts(("No such task: \"" + task + "\"")); puts(("No such task: \"" + task + "\""));
return process.exit(1); return process.exit(1);
}; };

View file

@ -28,7 +28,7 @@
lexer = new Lexer(); lexer = new Lexer();
// Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison // Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison
// compiler. // compiler.
exports.compile = (compile = function compile(code, options) { exports.compile = (compile = function(code, options) {
options = options || {}; options = options || {};
try { try {
return (parser.parse(lexer.tokenize(code))).compile(options); return (parser.parse(lexer.tokenize(code))).compile(options);
@ -40,13 +40,13 @@
} }
}); });
// Tokenize a string of CoffeeScript code, and return the array of tokens. // Tokenize a string of CoffeeScript code, and return the array of tokens.
exports.tokens = function tokens(code) { exports.tokens = function(code) {
return lexer.tokenize(code); return lexer.tokenize(code);
}; };
// Tokenize and parse a string of CoffeeScript code, and return the AST. You can // Tokenize and parse a string of CoffeeScript code, and return the AST. You can
// then compile it by calling `.compile()` on the root, or traverse it by using // then compile it by calling `.compile()` on the root, or traverse it by using
// `.traverse()` with a callback. // `.traverse()` with a callback.
exports.nodes = function nodes(code) { exports.nodes = function(code) {
return parser.parse(lexer.tokenize(code)); return parser.parse(lexer.tokenize(code));
}; };
// Compile and execute a string of CoffeeScript (on the server), correctly // Compile and execute a string of CoffeeScript (on the server), correctly
@ -61,14 +61,14 @@
// the **Lexer** (as a peer of any of the lexer's tokenizing methods), and // the **Lexer** (as a peer of any of the lexer's tokenizing methods), and
// push a token on to the stack that contains a **Node** as the value (as a // push a token on to the stack that contains a **Node** as the value (as a
// peer of the nodes in [nodes.coffee](nodes.html)). // peer of the nodes in [nodes.coffee](nodes.html)).
exports.extend = function extend(func) { exports.extend = function(func) {
return Lexer.extensions.push(func); return Lexer.extensions.push(func);
}; };
// The real Lexer produces a generic stream of tokens. This object provides a // The real Lexer produces a generic stream of tokens. This object provides a
// thin wrapper around it, compatible with the Jison API. We can then pass it // thin wrapper around it, compatible with the Jison API. We can then pass it
// directly as a "Jison lexer". // directly as a "Jison lexer".
parser.lexer = { parser.lexer = {
lex: function lex() { lex: function() {
var token; var token;
token = this.tokens[this.pos] || [""]; token = this.tokens[this.pos] || [""];
this.pos += 1; this.pos += 1;
@ -76,15 +76,15 @@
this.yytext = token[1]; this.yytext = token[1];
return token[0]; return token[0];
}, },
setInput: function setInput(tokens) { setInput: function(tokens) {
this.tokens = tokens; this.tokens = tokens;
this.pos = 0; this.pos = 0;
return this.pos; return this.pos;
}, },
upcomingInput: function upcomingInput() { upcomingInput: function() {
return ""; return "";
}, },
showPosition: function showPosition() { showPosition: function() {
return this.pos; return this.pos;
} }
}; };
@ -93,7 +93,7 @@
// on page load. Unfortunately, the text contents of remote scripts cannot be // on page load. Unfortunately, the text contents of remote scripts cannot be
// accessed from the browser, so only inline script tags will work. // accessed from the browser, so only inline script tags will work.
if ((typeof document !== "undefined" && document !== null) && document.getElementsByTagName) { if ((typeof document !== "undefined" && document !== null) && document.getElementsByTagName) {
process_scripts = function process_scripts() { process_scripts = function() {
var _a, _b, _c, _d, tag; var _a, _b, _c, _d, tag;
_a = []; _c = document.getElementsByTagName('script'); _a = []; _c = document.getElementsByTagName('script');
for (_b = 0, _d = _c.length; _b < _d; _b++) { for (_b = 0, _d = _c.length; _b < _d; _b++) {

View file

@ -24,7 +24,7 @@
// Run `coffee` by parsing passed options and determining what action to take. // Run `coffee` by parsing passed options and determining what action to take.
// Many flags cause us to divert before compiling anything. Flags passed after // Many flags cause us to divert before compiling anything. Flags passed after
// `--` will be passed verbatim to your script as arguments in `process.argv` // `--` will be passed verbatim to your script as arguments in `process.argv`
exports.run = function run() { exports.run = function() {
var flags, separator; var flags, separator;
parse_options(); parse_options();
if (options.help) { if (options.help) {
@ -57,14 +57,14 @@
// Asynchronously read in each CoffeeScript in a list of source files and // Asynchronously read in each CoffeeScript in a list of source files and
// compile them. If a directory is passed, recursively compile all source // compile them. If a directory is passed, recursively compile all source
// files in it and all subdirectories. // files in it and all subdirectories.
compile_scripts = function compile_scripts() { compile_scripts = function() {
var _b, _c, _d, _e, base, compile, source; var _b, _c, _d, _e, base, compile, source;
_b = []; _d = sources; _b = []; _d = sources;
for (_c = 0, _e = _d.length; _c < _e; _c++) { for (_c = 0, _e = _d.length; _c < _e; _c++) {
source = _d[_c]; source = _d[_c];
_b.push((function() { _b.push((function() {
base = source; base = source;
compile = function compile(source) { compile = function(source) {
return path.exists(source, function(exists) { return path.exists(source, function(exists) {
if (!(exists)) { if (!(exists)) {
throw new Error(("File not found: " + source)); throw new Error(("File not found: " + source));
@ -99,7 +99,7 @@
// Compile a single source script, containing the given code, according to the // Compile a single source script, containing the given code, according to the
// requested options. If evaluating the script directly sets `__filename`, // requested options. If evaluating the script directly sets `__filename`,
// `__dirname` and `module.filename` to be correct relative to the script's path. // `__dirname` and `module.filename` to be correct relative to the script's path.
compile_script = function compile_script(source, code, base) { compile_script = function(source, code, base) {
var code_opts, js, o; var code_opts, js, o;
o = options; o = options;
code_opts = compile_options(source); code_opts = compile_options(source);
@ -130,7 +130,7 @@
}; };
// Attach the appropriate listeners to compile scripts incoming over **stdin**, // Attach the appropriate listeners to compile scripts incoming over **stdin**,
// and write them back to **stdout**. // and write them back to **stdout**.
compile_stdio = function compile_stdio() { compile_stdio = function() {
var code, stdin; var code, stdin;
code = ''; code = '';
stdin = process.openStdin(); stdin = process.openStdin();
@ -146,7 +146,7 @@
// Watch a source CoffeeScript file using `fs.watchFile`, recompiling it every // Watch a source CoffeeScript file using `fs.watchFile`, recompiling it every
// time the file is updated. May be used in combination with other options, // time the file is updated. May be used in combination with other options,
// such as `--lint` or `--print`. // such as `--lint` or `--print`.
watch = function watch(source, base) { watch = function(source, base) {
return fs.watchFile(source, { return fs.watchFile(source, {
persistent: true, persistent: true,
interval: 500 interval: 500
@ -165,14 +165,14 @@
// Write out a JavaScript source file with the compiled code. By default, files // Write out a JavaScript source file with the compiled code. By default, files
// are written out in `cwd` as `.js` files with the same name, but the output // are written out in `cwd` as `.js` files with the same name, but the output
// directory can be customized with `--output`. // directory can be customized with `--output`.
write_js = function write_js(source, js, base) { write_js = function(source, js, base) {
var base_dir, compile, dir, filename, js_path, src_dir; var base_dir, compile, dir, filename, js_path, src_dir;
filename = path.basename(source, path.extname(source)) + '.js'; filename = path.basename(source, path.extname(source)) + '.js';
src_dir = path.dirname(source); src_dir = path.dirname(source);
base_dir = src_dir.substring(base.length); base_dir = src_dir.substring(base.length);
dir = options.output ? path.join(options.output, base_dir) : src_dir; dir = options.output ? path.join(options.output, base_dir) : src_dir;
js_path = path.join(dir, filename); js_path = path.join(dir, filename);
compile = function compile() { compile = function() {
return fs.writeFile(js_path, js); return fs.writeFile(js_path, js);
}; };
return path.exists(dir, function(exists) { return path.exists(dir, function(exists) {
@ -185,9 +185,9 @@
}; };
// Pipe compiled JS through JSLint (requires a working `jsl` command), printing // Pipe compiled JS through JSLint (requires a working `jsl` command), printing
// any errors or warnings that arise. // any errors or warnings that arise.
lint = function lint(js) { lint = function(js) {
var jsl, print_it; var jsl, print_it;
print_it = function print_it(buffer) { print_it = function(buffer) {
return print(buffer.toString()); return print(buffer.toString());
}; };
jsl = spawn('jsl', ['-nologo', '-stdin']); jsl = spawn('jsl', ['-nologo', '-stdin']);
@ -197,7 +197,7 @@
return jsl.stdin.end(); return jsl.stdin.end();
}; };
// Pretty-print a stream of tokens. // Pretty-print a stream of tokens.
print_tokens = function print_tokens(tokens) { print_tokens = function(tokens) {
var _b, _c, _d, _e, _f, strings, tag, token, value; var _b, _c, _d, _e, _f, strings, tag, token, value;
strings = (function() { strings = (function() {
_b = []; _d = tokens; _b = []; _d = tokens;
@ -216,7 +216,7 @@
}; };
// Use the [OptionParser module](optparse.html) to extract all options from // Use the [OptionParser module](optparse.html) to extract all options from
// `process.argv` that are specified in `SWITCHES`. // `process.argv` that are specified in `SWITCHES`.
parse_options = function parse_options() { parse_options = function() {
var o; var o;
option_parser = new optparse.OptionParser(SWITCHES, BANNER); option_parser = new optparse.OptionParser(SWITCHES, BANNER);
o = (options = option_parser.parse(process.argv)); o = (options = option_parser.parse(process.argv));
@ -226,7 +226,7 @@
return sources; return sources;
}; };
// The compile-time options to pass to the CoffeeScript compiler. // The compile-time options to pass to the CoffeeScript compiler.
compile_options = function compile_options(source) { compile_options = function(source) {
var o; var o;
o = { o = {
source: source source: source
@ -235,12 +235,12 @@
return o; return o;
}; };
// Print the `--help` usage message and exit. // Print the `--help` usage message and exit.
usage = function usage() { usage = function() {
puts(option_parser.help()); puts(option_parser.help());
return process.exit(0); return process.exit(0);
}; };
// Print the `--version` message and exit. // Print the `--version` message and exit.
version = function version() { version = function() {
puts(("CoffeeScript version " + CoffeeScript.VERSION)); puts(("CoffeeScript version " + CoffeeScript.VERSION));
return process.exit(0); return process.exit(0);
}; };

View file

@ -28,7 +28,7 @@
// we pass the pattern-defining string, the action to run, and extra options, // we pass the pattern-defining string, the action to run, and extra options,
// optionally. If no action is specified, we simply pass the value of the // optionally. If no action is specified, we simply pass the value of the
// previous nonterminal. // previous nonterminal.
o = function o(pattern_string, action, options) { o = function(pattern_string, action, options) {
var match; var match;
if (!(action)) { if (!(action)) {
return [pattern_string, '$$ = $1;', options]; return [pattern_string, '$$ = $1;', options];

View file

@ -1,5 +1,5 @@
(function(){ (function(){
var balanced_string, compact, count, del, extend, flatten, helpers, include, merge, starts; var balanced_string, compact, count, del, extend, flatten, helpers, include, index_of, merge, starts;
var __hasProp = Object.prototype.hasOwnProperty; var __hasProp = Object.prototype.hasOwnProperty;
// This file contains the common helper functions that we'd like to share among // This file contains the common helper functions that we'd like to share among
// the **Lexer**, **Rewriter**, and the **Nodes**. Merge objects, flatten // the **Lexer**, **Rewriter**, and the **Nodes**. Merge objects, flatten
@ -9,16 +9,31 @@
this.exports = this; this.exports = this;
} }
helpers = (exports.helpers = {}); helpers = (exports.helpers = {});
// Cross-browser indexOf, so that IE can join the party.
helpers.index_of = (index_of = function(array, item, from) {
var _a, _b, index, other;
if (array.indexOf) {
return array.indexOf(item, from);
}
_a = array;
for (index = 0, _b = _a.length; index < _b; index++) {
other = _a[index];
if (other === item && (!from || (from <= index))) {
return index;
}
}
return -1;
});
// Does a list include a value? // Does a list include a value?
helpers.include = (include = function include(list, value) { helpers.include = (include = function(list, value) {
return list.indexOf(value) >= 0; return index_of(list, value) >= 0;
}); });
// Peek at the beginning of a given string to see if it matches a sequence. // Peek at the beginning of a given string to see if it matches a sequence.
helpers.starts = (starts = function starts(string, literal, start) { helpers.starts = (starts = function(string, literal, start) {
return string.substring(start, (start || 0) + literal.length) === literal; return string.substring(start, (start || 0) + literal.length) === literal;
}); });
// Trim out all falsy values from an array. // Trim out all falsy values from an array.
helpers.compact = (compact = function compact(array) { helpers.compact = (compact = function(array) {
var _a, _b, _c, _d, item; var _a, _b, _c, _d, item;
_a = []; _c = array; _a = []; _c = array;
for (_b = 0, _d = _c.length; _b < _d; _b++) { for (_b = 0, _d = _c.length; _b < _d; _b++) {
@ -28,20 +43,20 @@
return _a; return _a;
}); });
// Count the number of occurences of a character in a string. // Count the number of occurences of a character in a string.
helpers.count = (count = function count(string, letter) { helpers.count = (count = function(string, letter) {
var num, pos; var num, pos;
num = 0; num = 0;
pos = string.indexOf(letter); pos = index_of(string, letter);
while (pos !== -1) { while (pos !== -1) {
num += 1; num += 1;
pos = string.indexOf(letter, pos + 1); pos = index_of(string, letter, pos + 1);
} }
return num; return num;
}); });
// Merge objects, returning a fresh copy with attributes from both sides. // Merge objects, returning a fresh copy with attributes from both sides.
// Used every time `BaseNode#compile` is called, to allow properties in the // Used every time `BaseNode#compile` is called, to allow properties in the
// options hash to propagate down the tree without polluting other branches. // options hash to propagate down the tree without polluting other branches.
helpers.merge = (merge = function merge(options, overrides) { helpers.merge = (merge = function(options, overrides) {
var _a, _b, fresh, key, val; var _a, _b, fresh, key, val;
fresh = {}; fresh = {};
_a = options; _a = options;
@ -60,7 +75,7 @@
}); });
// Extend a source object with the properties of another object (shallow copy). // Extend a source object with the properties of another object (shallow copy).
// We use this to simulate Node's deprecated `process.mixin` // We use this to simulate Node's deprecated `process.mixin`
helpers.extend = (extend = function extend(object, properties) { helpers.extend = (extend = function(object, properties) {
var _a, _b, key, val; var _a, _b, key, val;
_a = []; _b = properties; _a = []; _b = properties;
for (key in _b) { if (__hasProp.call(_b, key)) { for (key in _b) { if (__hasProp.call(_b, key)) {
@ -71,7 +86,7 @@
}); });
// Return a completely flattened version of an array. Handy for getting a // Return a completely flattened version of an array. Handy for getting a
// list of `children` from the nodes. // list of `children` from the nodes.
helpers.flatten = (flatten = function flatten(array) { helpers.flatten = (flatten = function(array) {
var _a, _b, _c, item, memo; var _a, _b, _c, item, memo;
memo = []; memo = [];
_b = array; _b = array;
@ -83,7 +98,7 @@
}); });
// Delete a key from an object, returning the value. Useful when a node is // Delete a key from an object, returning the value. Useful when a node is
// looking for a particular method in an options hash. // looking for a particular method in an options hash.
helpers.del = (del = function del(obj, key) { helpers.del = (del = function(obj, key) {
var val; var val;
val = obj[key]; val = obj[key];
delete obj[key]; delete obj[key];
@ -93,7 +108,7 @@
// a series of delimiters, all of which must be nested correctly within the // a series of delimiters, all of which must be nested correctly within the
// contents of the string. This method allows us to have strings within // contents of the string. This method allows us to have strings within
// interpolations within strings, ad infinitum. // interpolations within strings, ad infinitum.
helpers.balanced_string = (balanced_string = function balanced_string(str, delimited, options) { helpers.balanced_string = (balanced_string = function(str, delimited, options) {
var _a, _b, _c, _d, close, i, levels, open, pair, slash; var _a, _b, _c, _d, close, i, levels, open, pair, slash;
options = options || {}; options = options || {};
slash = delimited[0][0] === '/'; slash = delimited[0][0] === '/';

View file

@ -31,7 +31,7 @@
// tokens. Some potential ambiguity in the grammar has been avoided by // tokens. Some potential ambiguity in the grammar has been avoided by
// pushing some extra smarts into the Lexer. // pushing some extra smarts into the Lexer.
exports.Lexer = (function() { exports.Lexer = (function() {
Lexer = function Lexer() { }; Lexer = function() { };
// **tokenize** is the Lexer's main method. Scan by attempting to match tokens // **tokenize** is the Lexer's main method. Scan by attempting to match tokens
// one at a time, using a regular expression anchored at the start of the // one at a time, using a regular expression anchored at the start of the
// remaining code, or a custom recursive token-matching method // remaining code, or a custom recursive token-matching method
@ -42,7 +42,7 @@
// of source. // of source.
// Before returning the token stream, run it through the [Rewriter](rewriter.html) // Before returning the token stream, run it through the [Rewriter](rewriter.html)
// unless explicitly asked not to. // unless explicitly asked not to.
Lexer.prototype.tokenize = function tokenize(code, options) { Lexer.prototype.tokenize = function(code, options) {
var o; var o;
code = code.replace(/(\r|\s+$)/g, ''); code = code.replace(/(\r|\s+$)/g, '');
o = options || {}; o = options || {};
@ -71,7 +71,7 @@
// At every position, run through this list of attempted matches, // At every position, run through this list of attempted matches,
// short-circuiting if any of them succeed. Their order determines precedence: // short-circuiting if any of them succeed. Their order determines precedence:
// `@literal_token` is the fallback catch-all. // `@literal_token` is the fallback catch-all.
Lexer.prototype.extract_next_token = function extract_next_token() { Lexer.prototype.extract_next_token = function() {
if (this.extension_token()) { if (this.extension_token()) {
return null; return null;
} }
@ -108,7 +108,7 @@
// ---------- // ----------
// Language extensions get the highest priority, first chance to tag tokens // Language extensions get the highest priority, first chance to tag tokens
// as something else. // as something else.
Lexer.prototype.extension_token = function extension_token() { Lexer.prototype.extension_token = function() {
var _d, _e, _f, extension; var _d, _e, _f, extension;
_e = Lexer.extensions; _e = Lexer.extensions;
for (_d = 0, _f = _e.length; _d < _f; _d++) { for (_d = 0, _f = _e.length; _d < _f; _d++) {
@ -125,7 +125,7 @@
// allowed in JavaScript, we're careful not to tag them as keywords when // allowed in JavaScript, we're careful not to tag them as keywords when
// referenced as property names here, so you can still do `jQuery.is()` even // referenced as property names here, so you can still do `jQuery.is()` even
// though `is` means `===` otherwise. // though `is` means `===` otherwise.
Lexer.prototype.identifier_token = function identifier_token() { Lexer.prototype.identifier_token = function() {
var accessed, id, tag; var accessed, id, tag;
if (!(id = this.match(IDENTIFIER, 1))) { if (!(id = this.match(IDENTIFIER, 1))) {
return false; return false;
@ -155,7 +155,7 @@
return true; return true;
}; };
// Matches numbers, including decimals, hex, and exponential notation. // Matches numbers, including decimals, hex, and exponential notation.
Lexer.prototype.number_token = function number_token() { Lexer.prototype.number_token = function() {
var number; var number;
if (!(number = this.match(NUMBER, 1))) { if (!(number = this.match(NUMBER, 1))) {
return false; return false;
@ -166,7 +166,7 @@
}; };
// Matches strings, including multi-line strings. Ensures that quotation marks // Matches strings, including multi-line strings. Ensures that quotation marks
// are balanced within the string's contents, and within nested interpolations. // are balanced within the string's contents, and within nested interpolations.
Lexer.prototype.string_token = function string_token() { Lexer.prototype.string_token = function() {
var string; var string;
if (!(starts(this.chunk, '"') || starts(this.chunk, "'"))) { if (!(starts(this.chunk, '"') || starts(this.chunk, "'"))) {
return false; return false;
@ -181,7 +181,7 @@
}; };
// Matches heredocs, adjusting indentation to the correct level, as heredocs // Matches heredocs, adjusting indentation to the correct level, as heredocs
// preserve whitespace, but ignore indentation to the left. // preserve whitespace, but ignore indentation to the left.
Lexer.prototype.heredoc_token = function heredoc_token() { Lexer.prototype.heredoc_token = function() {
var doc, match, quote; var doc, match, quote;
if (!(match = this.chunk.match(HEREDOC))) { if (!(match = this.chunk.match(HEREDOC))) {
return false; return false;
@ -197,7 +197,7 @@
}; };
// Matches and conumes comments. We pass through comments into JavaScript, // Matches and conumes comments. We pass through comments into JavaScript,
// so they're treated as real tokens, like any other part of the language. // so they're treated as real tokens, like any other part of the language.
Lexer.prototype.comment_token = function comment_token() { Lexer.prototype.comment_token = function() {
var comment, i, lines, match; var comment, i, lines, match;
if (!(match = this.chunk.match(COMMENT))) { if (!(match = this.chunk.match(COMMENT))) {
return false; return false;
@ -222,7 +222,7 @@
return true; return true;
}; };
// Matches JavaScript interpolated directly into the source via backticks. // Matches JavaScript interpolated directly into the source via backticks.
Lexer.prototype.js_token = function js_token() { Lexer.prototype.js_token = function() {
var script; var script;
if (!(starts(this.chunk, '`'))) { if (!(starts(this.chunk, '`'))) {
return false; return false;
@ -238,7 +238,7 @@
// to distinguish from division, so we borrow some basic heuristics from // to distinguish from division, so we borrow some basic heuristics from
// JavaScript and Ruby, borrow slash balancing from `@balanced_token`, and // JavaScript and Ruby, borrow slash balancing from `@balanced_token`, and
// borrow interpolation from `@interpolate_string`. // borrow interpolation from `@interpolate_string`.
Lexer.prototype.regex_token = function regex_token() { Lexer.prototype.regex_token = function() {
var end, flags, regex, str; var end, flags, regex, str;
if (!(this.chunk.match(REGEX_START))) { if (!(this.chunk.match(REGEX_START))) {
return false; return false;
@ -271,7 +271,7 @@
}; };
// Matches a token in which which the passed delimiter pairs must be correctly // Matches a token in which which the passed delimiter pairs must be correctly
// balanced (ie. strings, JS literals). // balanced (ie. strings, JS literals).
Lexer.prototype.balanced_token = function balanced_token() { Lexer.prototype.balanced_token = function() {
var delimited; var delimited;
var _d = arguments.length, _e = _d >= 1; var _d = arguments.length, _e = _d >= 1;
delimited = __slice.call(arguments, 0, _d - 0); delimited = __slice.call(arguments, 0, _d - 0);
@ -285,7 +285,7 @@
// .map( ... ) // .map( ... )
// Keeps track of the level of indentation, because a single outdent token // Keeps track of the level of indentation, because a single outdent token
// can close multiple indents, so we need to know how far in we happen to be. // can close multiple indents, so we need to know how far in we happen to be.
Lexer.prototype.line_token = function line_token() { Lexer.prototype.line_token = function() {
var diff, indent, next_character, no_newlines, prev, size; var diff, indent, next_character, no_newlines, prev, size;
if (!(indent = this.match(MULTI_DENT, 1))) { if (!(indent = this.match(MULTI_DENT, 1))) {
return false; return false;
@ -316,7 +316,7 @@
}; };
// Record an outdent token or multiple tokens, if we happen to be moving back // Record an outdent token or multiple tokens, if we happen to be moving back
// inwards past several recorded indents. // inwards past several recorded indents.
Lexer.prototype.outdent_token = function outdent_token(move_out, no_newlines) { Lexer.prototype.outdent_token = function(move_out, no_newlines) {
var last_indent; var last_indent;
while (move_out > 0 && this.indents.length) { while (move_out > 0 && this.indents.length) {
last_indent = this.indents.pop(); last_indent = this.indents.pop();
@ -330,7 +330,7 @@
}; };
// Matches and consumes non-meaningful whitespace. Tag the previous token // Matches and consumes non-meaningful whitespace. Tag the previous token
// as being "spaced", because there are some cases where it makes a difference. // as being "spaced", because there are some cases where it makes a difference.
Lexer.prototype.whitespace_token = function whitespace_token() { Lexer.prototype.whitespace_token = function() {
var prev, space; var prev, space;
if (!(space = this.match(WHITESPACE, 1))) { if (!(space = this.match(WHITESPACE, 1))) {
return false; return false;
@ -343,7 +343,7 @@
return true; return true;
}; };
// Generate a newline token. Consecutive newlines get merged together. // Generate a newline token. Consecutive newlines get merged together.
Lexer.prototype.newline_token = function newline_token(newlines) { Lexer.prototype.newline_token = function(newlines) {
if (!(this.tag() === 'TERMINATOR')) { if (!(this.tag() === 'TERMINATOR')) {
this.token('TERMINATOR', "\n"); this.token('TERMINATOR', "\n");
} }
@ -351,7 +351,7 @@
}; };
// Use a `\` at a line-ending to suppress the newline. // Use a `\` at a line-ending to suppress the newline.
// The slash is removed here once its job is done. // The slash is removed here once its job is done.
Lexer.prototype.suppress_newlines = function suppress_newlines() { Lexer.prototype.suppress_newlines = function() {
if (this.value() === "\\") { if (this.value() === "\\") {
this.tokens.pop(); this.tokens.pop();
} }
@ -362,7 +362,7 @@
// the proper order of operations. There are some symbols that we tag specially // the proper order of operations. There are some symbols that we tag specially
// here. `;` and newlines are both treated as a `TERMINATOR`, we distinguish // here. `;` and newlines are both treated as a `TERMINATOR`, we distinguish
// parentheses that indicate a method call from regular parentheses, and so on. // parentheses that indicate a method call from regular parentheses, and so on.
Lexer.prototype.literal_token = function literal_token() { Lexer.prototype.literal_token = function() {
var match, prev_spaced, space, tag, value; var match, prev_spaced, space, tag, value;
match = this.chunk.match(OPERATOR); match = this.chunk.match(OPERATOR);
value = match && match[1]; value = match && match[1];
@ -406,7 +406,7 @@
// ------------------ // ------------------
// As we consume a new `IDENTIFIER`, look at the previous token to determine // As we consume a new `IDENTIFIER`, look at the previous token to determine
// if it's a special kind of accessor. // if it's a special kind of accessor.
Lexer.prototype.name_access_type = function name_access_type() { Lexer.prototype.name_access_type = function() {
if (this.value() === '::') { if (this.value() === '::') {
this.tag(1, 'PROTOTYPE_ACCESS'); this.tag(1, 'PROTOTYPE_ACCESS');
} }
@ -421,7 +421,7 @@
}; };
// Sanitize a heredoc or herecomment by escaping internal double quotes and // Sanitize a heredoc or herecomment by escaping internal double quotes and
// erasing all external indentation on the left-hand side. // erasing all external indentation on the left-hand side.
Lexer.prototype.sanitize_heredoc = function sanitize_heredoc(doc, options) { Lexer.prototype.sanitize_heredoc = function(doc, options) {
var attempt, indent, match; var attempt, indent, match;
while (match = HEREDOC_INDENT.exec(doc)) { while (match = HEREDOC_INDENT.exec(doc)) {
attempt = match[2] || match[3]; attempt = match[2] || match[3];
@ -436,7 +436,7 @@
return doc.replace(MULTILINER, "\\n").replace(new RegExp(options.quote, 'g'), '\\"'); return doc.replace(MULTILINER, "\\n").replace(new RegExp(options.quote, 'g'), '\\"');
}; };
// Tag a half assignment. // Tag a half assignment.
Lexer.prototype.tag_half_assignment = function tag_half_assignment(tag) { Lexer.prototype.tag_half_assignment = function(tag) {
var last; var last;
last = this.tokens.pop(); last = this.tokens.pop();
this.tokens.push([("" + tag + "="), ("" + tag + "="), last[2]]); this.tokens.push([("" + tag + "="), ("" + tag + "="), last[2]]);
@ -445,7 +445,7 @@
// A source of ambiguity in our grammar used to be parameter lists in function // A source of ambiguity in our grammar used to be parameter lists in function
// definitions versus argument lists in function calls. Walk backwards, tagging // definitions versus argument lists in function calls. Walk backwards, tagging
// parameters specially in order to make things easier for the parser. // parameters specially in order to make things easier for the parser.
Lexer.prototype.tag_parameters = function tag_parameters() { Lexer.prototype.tag_parameters = function() {
var _d, i, tok; var _d, i, tok;
if (this.tag() !== ')') { if (this.tag() !== ')') {
return null; return null;
@ -469,17 +469,17 @@
return true; return true;
}; };
// Close up all remaining open blocks at the end of the file. // Close up all remaining open blocks at the end of the file.
Lexer.prototype.close_indentation = function close_indentation() { Lexer.prototype.close_indentation = function() {
return this.outdent_token(this.indent); return this.outdent_token(this.indent);
}; };
// The error for when you try to use a forbidden word in JavaScript as // The error for when you try to use a forbidden word in JavaScript as
// an identifier. // an identifier.
Lexer.prototype.identifier_error = function identifier_error(word) { Lexer.prototype.identifier_error = function(word) {
throw new Error(("SyntaxError: Reserved word \"" + word + "\" on line " + (this.line + 1))); throw new Error(("SyntaxError: Reserved word \"" + word + "\" on line " + (this.line + 1)));
}; };
// The error for when you try to assign to a reserved word in JavaScript, // The error for when you try to assign to a reserved word in JavaScript,
// like "function" or "default". // like "function" or "default".
Lexer.prototype.assignment_error = function assignment_error() { Lexer.prototype.assignment_error = function() {
throw new Error(("SyntaxError: Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned")); throw new Error(("SyntaxError: Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned"));
}; };
// Expand variables and expressions inside double-quoted strings using // Expand variables and expressions inside double-quoted strings using
@ -490,7 +490,7 @@
// If it encounters an interpolation, this method will recursively create a // If it encounters an interpolation, this method will recursively create a
// new Lexer, tokenize the interpolated contents, and merge them into the // new Lexer, tokenize the interpolated contents, and merge them into the
// token stream. // token stream.
Lexer.prototype.interpolate_string = function interpolate_string(str, escape_quotes) { Lexer.prototype.interpolate_string = function(str, escape_quotes) {
var _d, _e, _f, _g, _h, _i, _j, escaped, expr, group, i, idx, inner, interp, interpolated, lexer, match, nested, pi, quote, tag, tok, token, tokens, value; var _d, _e, _f, _g, _h, _i, _j, escaped, expr, group, i, idx, inner, interp, interpolated, lexer, match, nested, pi, quote, tag, tok, token, tokens, value;
if (str.length < 3 || !starts(str, '"')) { if (str.length < 3 || !starts(str, '"')) {
return this.token('STRING', str); return this.token('STRING', str);
@ -578,11 +578,11 @@
// Helpers // Helpers
// ------- // -------
// Add a token to the results, taking note of the line number. // Add a token to the results, taking note of the line number.
Lexer.prototype.token = function token(tag, value) { Lexer.prototype.token = function(tag, value) {
return this.tokens.push([tag, value, this.line]); return this.tokens.push([tag, value, this.line]);
}; };
// Peek at a tag in the current token stream. // Peek at a tag in the current token stream.
Lexer.prototype.tag = function tag(index, new_tag) { Lexer.prototype.tag = function(index, new_tag) {
var tok; var tok;
if (!(tok = this.prev(index))) { if (!(tok = this.prev(index))) {
return null; return null;
@ -594,7 +594,7 @@
return tok[0]; return tok[0];
}; };
// Peek at a value in the current token stream. // Peek at a value in the current token stream.
Lexer.prototype.value = function value(index, val) { Lexer.prototype.value = function(index, val) {
var tok; var tok;
if (!(tok = this.prev(index))) { if (!(tok = this.prev(index))) {
return null; return null;
@ -606,12 +606,12 @@
return tok[1]; return tok[1];
}; };
// Peek at a previous token, entire. // Peek at a previous token, entire.
Lexer.prototype.prev = function prev(index) { Lexer.prototype.prev = function(index) {
return this.tokens[this.tokens.length - (index || 1)]; return this.tokens[this.tokens.length - (index || 1)];
}; };
// Attempt to match a string against the current chunk, returning the indexed // Attempt to match a string against the current chunk, returning the indexed
// match if successful, and `false` otherwise. // match if successful, and `false` otherwise.
Lexer.prototype.match = function match(regex, index) { Lexer.prototype.match = function(regex, index) {
var m; var m;
if (!(m = this.chunk.match(regex))) { if (!(m = this.chunk.match(regex))) {
return false; return false;
@ -623,7 +623,7 @@
} }
}; };
// Are we in the midst of an unfinished expression? // Are we in the midst of an unfinished expression?
Lexer.prototype.unfinished = function unfinished() { Lexer.prototype.unfinished = function() {
var prev; var prev;
prev = this.prev(2); prev = this.prev(2);
return this.value() && this.value().match && this.value().match(NO_NEWLINE) && prev && (prev[0] !== '.') && !this.value().match(CODE); return this.value() && this.value().match && this.value().match(NO_NEWLINE) && prev && (prev[0] !== '.') && !this.value().match(CODE);

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
// parser: new OptionParser switches, help_banner // parser: new OptionParser switches, help_banner
// options: parser.parse process.argv // options: parser.parse process.argv
exports.OptionParser = (function() { exports.OptionParser = (function() {
OptionParser = function OptionParser(rules, banner) { OptionParser = function(rules, banner) {
this.banner = banner; this.banner = banner;
this.rules = build_rules(rules); this.rules = build_rules(rules);
return this; return this;
@ -18,7 +18,7 @@
// containing the remaning non-option arguments. This is a simpler API than // containing the remaning non-option arguments. This is a simpler API than
// many option parsers that allow you to attach callback actions for every // many option parsers that allow you to attach callback actions for every
// flag. Instead, you're responsible for interpreting the options object. // flag. Instead, you're responsible for interpreting the options object.
OptionParser.prototype.parse = function parse(args) { OptionParser.prototype.parse = function(args) {
var _a, _b, _c, arg, is_option, matched_rule, options, rule; var _a, _b, _c, arg, is_option, matched_rule, options, rule;
options = { options = {
arguments: [] arguments: []
@ -47,7 +47,7 @@
}; };
// Return the help text for this **OptionParser**, listing and describing all // Return the help text for this **OptionParser**, listing and describing all
// of the valid options, for `--help` and such. // of the valid options, for `--help` and such.
OptionParser.prototype.help = function help() { OptionParser.prototype.help = function() {
var _a, _b, _c, _d, _e, _f, i, let_part, lines, rule, spaces; var _a, _b, _c, _d, _e, _f, i, let_part, lines, rule, spaces;
lines = ['Available options:']; lines = ['Available options:'];
if (this.banner) { if (this.banner) {
@ -80,7 +80,7 @@
OPTIONAL = /\[(.+)\]/; OPTIONAL = /\[(.+)\]/;
// Build and return the list of option rules. If the optional *short-flag* is // Build and return the list of option rules. If the optional *short-flag* is
// unspecified, leave it out by padding with `null`. // unspecified, leave it out by padding with `null`.
build_rules = function build_rules(rules) { build_rules = function(rules) {
var _a, _b, _c, _d, tuple; var _a, _b, _c, _d, tuple;
_a = []; _c = rules; _a = []; _c = rules;
for (_b = 0, _d = _c.length; _b < _d; _b++) { for (_b = 0, _d = _c.length; _b < _d; _b++) {
@ -96,7 +96,7 @@
}; };
// Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the // Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the
// description of what the option does. // description of what the option does.
build_rule = function build_rule(short_flag, long_flag, description) { build_rule = function(short_flag, long_flag, description) {
var match; var match;
match = long_flag.match(OPTIONAL); match = long_flag.match(OPTIONAL);
long_flag = long_flag.match(LONG_FLAG)[1]; long_flag = long_flag.match(LONG_FLAG)[1];
@ -110,7 +110,7 @@
}; };
// Normalize arguments by expanding merged flags into multiple flags. This allows // Normalize arguments by expanding merged flags into multiple flags. This allows
// you to have `-wl` be the same as `--watch --lint`. // you to have `-wl` be the same as `--watch --lint`.
normalize_arguments = function normalize_arguments(args) { normalize_arguments = function(args) {
var _a, _b, _c, _d, _e, _f, arg, l, match, result; var _a, _b, _c, _d, _e, _f, arg, l, match, result;
args = args.slice(0); args = args.slice(0);
result = []; result = [];

View file

@ -11,14 +11,14 @@
prompt = 'coffee> '; prompt = 'coffee> ';
// Quick alias for quitting the REPL. // Quick alias for quitting the REPL.
helpers.extend(global, { helpers.extend(global, {
quit: function quit() { quit: function() {
return process.exit(0); return process.exit(0);
} }
}); });
// The main REPL function. **run** is called every time a line of code is entered. // 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 // Attempt to evaluate the command. If there's an exception, print it out instead
// of exiting. // of exiting.
run = function run(buffer) { run = function(buffer) {
var val; var val;
try { try {
val = CoffeeScript.run(buffer.toString(), { val = CoffeeScript.run(buffer.toString(), {

View file

@ -25,13 +25,13 @@
// The **Rewriter** class is used by the [Lexer](lexer.html), directly against // The **Rewriter** class is used by the [Lexer](lexer.html), directly against
// its internal array of tokens. // its internal array of tokens.
exports.Rewriter = (function() { exports.Rewriter = (function() {
Rewriter = function Rewriter() { }; Rewriter = function() { };
// Rewrite the token stream in multiple passes, one logical filter at // Rewrite the token stream in multiple passes, one logical filter at
// a time. This could certainly be changed into a single pass through the // a time. This could certainly be changed into a single pass through the
// stream, with a big ol' efficient switch, but it's much nicer to work with // stream, with a big ol' efficient switch, but it's much nicer to work with
// like this. The order of these passes matters -- indentation must be // like this. The order of these passes matters -- indentation must be
// corrected before implicit parentheses can be wrapped around blocks of code. // corrected before implicit parentheses can be wrapped around blocks of code.
Rewriter.prototype.rewrite = function rewrite(tokens) { Rewriter.prototype.rewrite = function(tokens) {
this.tokens = tokens; this.tokens = tokens;
this.adjust_comments(); this.adjust_comments();
this.remove_leading_newlines(); this.remove_leading_newlines();
@ -48,7 +48,7 @@
// forwards (or backwards) in the stream, to make sure we don't miss anything // forwards (or backwards) in the stream, to make sure we don't miss anything
// as tokens are inserted and removed, and the stream changes length under // as tokens are inserted and removed, and the stream changes length under
// our feet. // our feet.
Rewriter.prototype.scan_tokens = function scan_tokens(block) { Rewriter.prototype.scan_tokens = function(block) {
var i, move; var i, move;
i = 0; i = 0;
while (true) { while (true) {
@ -62,7 +62,7 @@
}; };
// Massage newlines and indentations so that comments don't have to be // Massage newlines and indentations so that comments don't have to be
// correctly indented, or appear on a line of their own. // correctly indented, or appear on a line of their own.
Rewriter.prototype.adjust_comments = function adjust_comments() { Rewriter.prototype.adjust_comments = function() {
return this.scan_tokens(__bind(function(prev, token, post, i) { return this.scan_tokens(__bind(function(prev, token, post, i) {
var _c, after, before; var _c, after, before;
if (!(token[0] === 'COMMENT')) { if (!(token[0] === 'COMMENT')) {
@ -85,7 +85,7 @@
}; };
// Leading newlines would introduce an ambiguity in the grammar, so we // Leading newlines would introduce an ambiguity in the grammar, so we
// dispatch them here. // dispatch them here.
Rewriter.prototype.remove_leading_newlines = function remove_leading_newlines() { Rewriter.prototype.remove_leading_newlines = function() {
var _c; var _c;
_c = []; _c = [];
while (this.tokens[0] && this.tokens[0][0] === 'TERMINATOR') { while (this.tokens[0] && this.tokens[0][0] === 'TERMINATOR') {
@ -95,7 +95,7 @@
}; };
// Some blocks occur in the middle of expressions -- when we're expecting // Some blocks occur in the middle of expressions -- when we're expecting
// this, remove their trailing newlines. // this, remove their trailing newlines.
Rewriter.prototype.remove_mid_expression_newlines = function remove_mid_expression_newlines() { Rewriter.prototype.remove_mid_expression_newlines = function() {
return this.scan_tokens(__bind(function(prev, token, post, i) { return this.scan_tokens(__bind(function(prev, token, post, i) {
if (!(post && include(EXPRESSION_CLOSE, post[0]) && token[0] === 'TERMINATOR')) { if (!(post && include(EXPRESSION_CLOSE, post[0]) && token[0] === 'TERMINATOR')) {
return 1; return 1;
@ -107,7 +107,7 @@
// The lexer has tagged the opening parenthesis of a method call, and the // The lexer has tagged the opening parenthesis of a method call, and the
// opening bracket of an indexing operation. Match them with their paired // opening bracket of an indexing operation. Match them with their paired
// close. // close.
Rewriter.prototype.close_open_calls_and_indexes = function close_open_calls_and_indexes() { Rewriter.prototype.close_open_calls_and_indexes = function() {
var brackets, parens; var brackets, parens;
parens = [0]; parens = [0];
brackets = [0]; brackets = [0];
@ -142,7 +142,7 @@
// Methods may be optionally called without parentheses, for simple cases. // Methods may be optionally called without parentheses, for simple cases.
// Insert the implicit parentheses here, so that the parser doesn't have to // Insert the implicit parentheses here, so that the parser doesn't have to
// deal with them. // deal with them.
Rewriter.prototype.add_implicit_parentheses = function add_implicit_parentheses() { Rewriter.prototype.add_implicit_parentheses = function() {
var close_calls, stack; var close_calls, stack;
stack = [0]; stack = [0];
close_calls = __bind(function(i) { close_calls = __bind(function(i) {
@ -207,7 +207,7 @@
// expressions that lack ending delimiters. The **Rewriter** adds the implicit // expressions that lack ending delimiters. The **Rewriter** adds the implicit
// blocks, so it doesn't need to. ')' can close a single-line block, // blocks, so it doesn't need to. ')' can close a single-line block,
// but we need to make sure it's balanced. // but we need to make sure it's balanced.
Rewriter.prototype.add_implicit_indentation = function add_implicit_indentation() { Rewriter.prototype.add_implicit_indentation = function() {
return this.scan_tokens(__bind(function(prev, token, post, i) { return this.scan_tokens(__bind(function(prev, token, post, i) {
var idx, indent, insertion, outdent, parens, pre, starter, tok; var idx, indent, insertion, outdent, parens, pre, starter, tok;
if (!(include(SINGLE_LINERS, token[0]) && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) { if (!(include(SINGLE_LINERS, token[0]) && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) {
@ -246,7 +246,7 @@
}; };
// Ensure that all listed pairs of tokens are correctly balanced throughout // Ensure that all listed pairs of tokens are correctly balanced throughout
// the course of the token stream. // the course of the token stream.
Rewriter.prototype.ensure_balance = function ensure_balance(pairs) { Rewriter.prototype.ensure_balance = function(pairs) {
var _c, _d, key, levels, line, open, open_line, unclosed, value; var _c, _d, key, levels, line, open, open_line, unclosed, value;
levels = {}; levels = {};
open_line = {}; open_line = {};
@ -301,7 +301,7 @@
// up balanced in the end. // up balanced in the end.
// 4. Be careful not to alter array or parentheses delimiters with overzealous // 4. Be careful not to alter array or parentheses delimiters with overzealous
// rewriting. // rewriting.
Rewriter.prototype.rewrite_closing_parens = function rewrite_closing_parens() { Rewriter.prototype.rewrite_closing_parens = function() {
var _c, debt, key, stack, val; var _c, debt, key, stack, val;
stack = []; stack = [];
debt = {}; debt = {};

View file

@ -12,7 +12,7 @@
this.exports = this; this.exports = this;
} }
exports.Scope = (function() { exports.Scope = (function() {
Scope = function Scope(parent, expressions, method) { Scope = function(parent, expressions, method) {
var _a; var _a;
_a = [parent, expressions, method]; _a = [parent, expressions, method];
this.parent = _a[0]; this.parent = _a[0];
@ -35,7 +35,7 @@
// it wraps. // it wraps.
// Look up a variable name in lexical scope, and declare it if it does not // Look up a variable name in lexical scope, and declare it if it does not
// already exist. // already exist.
Scope.prototype.find = function find(name) { Scope.prototype.find = function(name) {
if (this.check(name)) { if (this.check(name)) {
return true; return true;
} }
@ -43,7 +43,7 @@
return false; return false;
}; };
// Test variables and return true the first time fn(v, k) returns true // Test variables and return true the first time fn(v, k) returns true
Scope.prototype.any = function any(fn) { Scope.prototype.any = function(fn) {
var _a, k, v; var _a, k, v;
_a = this.variables; _a = this.variables;
for (v in _a) { if (__hasProp.call(_a, v)) { for (v in _a) { if (__hasProp.call(_a, v)) {
@ -56,12 +56,12 @@
}; };
// Reserve a variable name as originating from a function parameter for this // Reserve a variable name as originating from a function parameter for this
// scope. No `var` required for internal references. // scope. No `var` required for internal references.
Scope.prototype.parameter = function parameter(name) { Scope.prototype.parameter = function(name) {
this.variables[name] = 'param'; this.variables[name] = 'param';
return this.variables[name]; return this.variables[name];
}; };
// Just check to see if a variable has already been declared, without reserving. // Just check to see if a variable has already been declared, without reserving.
Scope.prototype.check = function check(name) { Scope.prototype.check = function(name) {
if (this.variables[name]) { if (this.variables[name]) {
return true; return true;
} }
@ -69,7 +69,7 @@
}; };
// If we need to store an intermediate result, find an available name for a // If we need to store an intermediate result, find an available name for a
// compiler-generated variable. `_a`, `_b`, and so on... // compiler-generated variable. `_a`, `_b`, and so on...
Scope.prototype.free_variable = function free_variable() { Scope.prototype.free_variable = function() {
var ordinal; var ordinal;
while (this.check(this.temp_var)) { while (this.check(this.temp_var)) {
ordinal = 1 + parseInt(this.temp_var.substr(1), 36); ordinal = 1 + parseInt(this.temp_var.substr(1), 36);
@ -80,7 +80,7 @@
}; };
// Ensure that an assignment is made at the top of this scope // Ensure that an assignment is made at the top of this scope
// (or at the top-level scope, if requested). // (or at the top-level scope, if requested).
Scope.prototype.assign = function assign(name, value) { Scope.prototype.assign = function(name, value) {
this.variables[name] = { this.variables[name] = {
value: value, value: value,
assigned: true assigned: true
@ -89,20 +89,20 @@
}; };
// Does this scope reference any variables that need to be declared in the // Does this scope reference any variables that need to be declared in the
// given function body? // given function body?
Scope.prototype.has_declarations = function has_declarations(body) { Scope.prototype.has_declarations = function(body) {
return body === this.expressions && this.any(function(k, val) { return body === this.expressions && this.any(function(k, val) {
return val === 'var'; return val === 'var';
}); });
}; };
// Does this scope reference any assignments that need to be declared at the // Does this scope reference any assignments that need to be declared at the
// top of the given function body? // top of the given function body?
Scope.prototype.has_assignments = function has_assignments(body) { Scope.prototype.has_assignments = function(body) {
return body === this.expressions && this.any(function(k, val) { return body === this.expressions && this.any(function(k, val) {
return val.assigned; return val.assigned;
}); });
}; };
// Return the list of variables first declared in this scope. // Return the list of variables first declared in this scope.
Scope.prototype.declared_variables = function declared_variables() { Scope.prototype.declared_variables = function() {
var _a, _b, key, val; var _a, _b, key, val;
return (function() { return (function() {
_a = []; _b = this.variables; _a = []; _b = this.variables;
@ -115,7 +115,7 @@
}; };
// Return the list of assignments that are supposed to be made at the top // Return the list of assignments that are supposed to be made at the top
// of this scope. // of this scope.
Scope.prototype.assigned_variables = function assigned_variables() { Scope.prototype.assigned_variables = function() {
var _a, _b, key, val; var _a, _b, key, val;
_a = []; _b = this.variables; _a = []; _b = this.variables;
for (key in _b) { if (__hasProp.call(_b, key)) { for (key in _b) { if (__hasProp.call(_b, key)) {
@ -125,11 +125,11 @@
return _a; return _a;
}; };
// Compile the JavaScript for all of the variable declarations in this scope. // Compile the JavaScript for all of the variable declarations in this scope.
Scope.prototype.compiled_declarations = function compiled_declarations() { Scope.prototype.compiled_declarations = function() {
return this.declared_variables().join(', '); return this.declared_variables().join(', ');
}; };
// Compile the JavaScript for all of the variable assignments in this scope. // Compile the JavaScript for all of the variable assignments in this scope.
Scope.prototype.compiled_assignments = function compiled_assignments() { Scope.prototype.compiled_assignments = function() {
return this.assigned_variables().join(', '); return this.assigned_variables().join(', ');
}; };
return Scope; return Scope;

View file

@ -6,9 +6,17 @@
this.exports: this unless process? this.exports: this unless process?
helpers: exports.helpers: {} helpers: exports.helpers: {}
# Cross-browser indexOf, so that IE can join the party.
helpers.index_of: index_of: (array, item, from) ->
return array.indexOf item, from if array.indexOf
for other, index in array
if other is item and (not from or (from <= index))
return index
-1
# Does a list include a value? # Does a list include a value?
helpers.include: include: (list, value) -> helpers.include: include: (list, value) ->
list.indexOf(value) >= 0 index_of(list, value) >= 0
# Peek at the beginning of a given string to see if it matches a sequence. # Peek at the beginning of a given string to see if it matches a sequence.
helpers.starts: starts: (string, literal, start) -> helpers.starts: starts: (string, literal, start) ->
@ -20,10 +28,10 @@ helpers.compact: compact: (array) -> item for item in array when item
# Count the number of occurences of a character in a string. # Count the number of occurences of a character in a string.
helpers.count: count: (string, letter) -> helpers.count: count: (string, letter) ->
num: 0 num: 0
pos: string.indexOf(letter) pos: index_of string, letter
while pos isnt -1 while pos isnt -1
num: + 1 num: + 1
pos: string.indexOf(letter, pos + 1) pos: index_of string, letter, pos + 1
num num
# Merge objects, returning a fresh copy with attributes from both sides. # Merge objects, returning a fresh copy with attributes from both sides.

View file

@ -14,7 +14,7 @@ else
Scope: this.Scope Scope: this.Scope
# Import the helpers we plan to use. # Import the helpers we plan to use.
{compact, flatten, merge, del}: helpers {compact, flatten, merge, del, index_of}: helpers
# Helper function that marks a node as a JavaScript *statement*, or as a # Helper function that marks a node as a JavaScript *statement*, or as a
# *pure_statement*. Statements must be wrapped in a closure when used as an # *pure_statement*. Statements must be wrapped in a closure when used as an
@ -627,7 +627,7 @@ exports.ArrayNode: class ArrayNode extends BaseNode
else else
objects.push "$code, " objects.push "$code, "
objects: objects.join('') objects: objects.join('')
if objects.indexOf('\n') >= 0 if index_of(objects, '\n') >= 0
"[\n${@idt(1)}$objects\n$@tab]" "[\n${@idt(1)}$objects\n$@tab]"
else else
"[$objects]" "[$objects]"
@ -767,7 +767,7 @@ exports.AssignNode: class AssignNode extends BaseNode
access_class: if is_string or @variable.is_array() then IndexNode else AccessorNode access_class: if is_string or @variable.is_array() then IndexNode else AccessorNode
if obj instanceof SplatNode and not splat if obj instanceof SplatNode and not splat
val: literal(obj.compile_value(o, val_var, val: literal(obj.compile_value(o, val_var,
(oindex: @variable.base.objects.indexOf(obj)), (oindex: index_of(@variable.base.objects, obj)),
(olength: @variable.base.objects.length) - oindex - 1)) (olength: @variable.base.objects.length) - oindex - 1))
splat: true splat: true
else else
@ -835,8 +835,7 @@ exports.CodeNode: class CodeNode extends BaseNode
@body.make_return() @body.make_return()
(o.scope.parameter(param)) for param in params (o.scope.parameter(param)) for param in params
code: if @body.expressions.length then "\n${ @body.compile_with_declarations(o) }\n" else '' code: if @body.expressions.length then "\n${ @body.compile_with_declarations(o) }\n" else ''
name_part: if @name then ' ' + @name else '' func: "function(${ params.join(', ') }) {$code${@idt(if @bound then 1 else 0)}}"
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 func: "($func)" if top and not @bound
return func unless @bound return func unless @bound
utility 'slice' utility 'slice'
@ -993,12 +992,12 @@ exports.OpNode: class OpNode extends BaseNode
not @second not @second
is_chainable: -> is_chainable: ->
@CHAINABLE.indexOf(@operator) >= 0 index_of(@CHAINABLE, @operator) >= 0
compile_node: (o) -> compile_node: (o) ->
o.operation: true o.operation: true
return @compile_chain(o) if @is_chainable() and @first.unwrap() instanceof OpNode and @first.unwrap().is_chainable() return @compile_chain(o) if @is_chainable() and @first.unwrap() instanceof OpNode and @first.unwrap().is_chainable()
return @compile_assignment(o) if @ASSIGNMENT.indexOf(@operator) >= 0 return @compile_assignment(o) if index_of(@ASSIGNMENT, @operator) >= 0
return @compile_unary(o) if @is_unary() return @compile_unary(o) if @is_unary()
return @compile_existence(o) if @operator is '?' return @compile_existence(o) if @operator is '?'
[@first.compile(o), @operator, @second.compile(o)].join ' ' [@first.compile(o), @operator, @second.compile(o)].join ' '
@ -1032,7 +1031,7 @@ exports.OpNode: class OpNode extends BaseNode
# Compile a unary **OpNode**. # Compile a unary **OpNode**.
compile_unary: (o) -> compile_unary: (o) ->
space: if @PREFIX_OPERATORS.indexOf(@operator) >= 0 then ' ' else '' space: if index_of(@PREFIX_OPERATORS, @operator) >= 0 then ' ' else ''
parts: [@operator, space, @first.compile(o)] parts: [@operator, space, @first.compile(o)]
parts: parts.reverse() if @flip parts: parts.reverse() if @flip
parts.join('') parts.join('')

View file

@ -23,7 +23,7 @@ ok x is 100
# This-assignment. # This-assignment.
tester: -> tester: ->
@example: -> puts 'example function' @example: -> 'example function'
this this
ok tester().example.name is 'example' ok tester().example() is 'example function'

View file

@ -6,7 +6,6 @@ ok x is 1
ok typeof(y.x) is 'function' ok typeof(y.x) is 'function'
ok y.x instanceof Function ok y.x instanceof Function
ok y.x() is 3 ok y.x() is 3
ok y.x.name is 'x'
# The empty function should not cause a syntax error. # The empty function should not cause a syntax error.