Merge github.com:jashkenas/coffee-script

This commit is contained in:
Andreas Lubbe
2013-12-05 11:56:34 -08:00
18 changed files with 527 additions and 270 deletions

View File

@@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.6.3
(function() {
var CoffeeScript, cakefileDirectory, existsSync, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
var CoffeeScript, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
fs = require('fs');
@@ -12,8 +12,6 @@
CoffeeScript = require('./coffee-script');
existsSync = fs.existsSync || path.existsSync;
tasks = {};
options = {};
@@ -101,7 +99,7 @@
cakefileDirectory = function(dir) {
var parent;
if (existsSync(path.join(dir, 'Cakefile'))) {
if (fs.existsSync(path.join(dir, 'Cakefile'))) {
return dir;
}
parent = path.normalize(path.join(dir, '..'));

View File

@@ -1,6 +1,7 @@
// Generated by CoffeeScript 1.6.3
(function() {
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, exists, forkNode, fs, helpers, hidden, joinTimeout, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, sourceCode, sources, spawn, timeLog, unwatchDir, usage, useWinPathSep, version, wait, watch, watchDir, writeJs, _ref;
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, forkNode, fs, helpers, hidden, joinTimeout, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs, _ref,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
fs = require('fs');
@@ -18,8 +19,6 @@
EventEmitter = require('events').EventEmitter;
exists = fs.exists || path.exists;
useWinPathSep = path.sep === '\\';
helpers.extend(CoffeeScript, new EventEmitter);
@@ -48,10 +47,12 @@
notSources = {};
watchedDirs = {};
optionParser = null;
exports.run = function() {
var literals, replCliOpts, source, _i, _len, _results;
var literals, replCliOpts, source, _i, _len, _ref1, _results;
parseOptions();
replCliOpts = {
useGlobal: true
@@ -68,31 +69,39 @@
if (opts.interactive) {
return require('./repl').start(replCliOpts);
}
if (opts.watch && !fs.watch) {
return printWarn("The --watch feature depends on Node v0.6.0+. You are running " + process.version + ".");
}
if (opts.stdio) {
return compileStdio();
}
if (opts["eval"]) {
return compileScript(null, sources[0]);
return compileScript(null, opts["arguments"][0]);
}
if (!sources.length) {
if (!opts["arguments"].length) {
return require('./repl').start(replCliOpts);
}
literals = opts.run ? sources.splice(1) : [];
literals = opts.run ? opts["arguments"].splice(1) : [];
process.argv = process.argv.slice(0, 2).concat(literals);
process.argv[0] = 'coffee';
if (opts.output) {
opts.output = path.resolve(opts.output);
}
if (opts.join) {
opts.join = path.resolve(opts.join);
}
_ref1 = opts["arguments"];
_results = [];
for (_i = 0, _len = sources.length; _i < _len; _i++) {
source = sources[_i];
_results.push(compilePath(source, true, path.normalize(source)));
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
source = _ref1[_i];
source = path.resolve(source);
_results.push(compilePath(source, true, source));
}
return _results;
};
compilePath = function(source, topLevel, base) {
var code, err, file, files, index, stats, _ref1, _ref2;
var code, err, file, files, stats, _i, _len, _results;
if (__indexOf.call(sources, source) >= 0 || watchedDirs[source] || !topLevel && (notSources[source] || hidden(source))) {
return;
}
try {
stats = fs.statSync(source);
} catch (_error) {
@@ -103,7 +112,11 @@
}
throw err;
}
if (stats.isDirectory() && path.dirname(source) !== 'node_modules') {
if (stats.isDirectory()) {
if (path.basename(source) === 'node_modules') {
notSources[source] = true;
return;
}
if (opts.watch) {
watchDir(source, base);
}
@@ -117,26 +130,16 @@
throw err;
}
}
index = sources.indexOf(source);
files = files.filter(function(file) {
return !hidden(file);
});
[].splice.apply(sources, [index, index - index + 1].concat(_ref1 = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
_results.push(path.join(source, file));
}
return _results;
})())), _ref1;
[].splice.apply(sourceCode, [index, index - index + 1].concat(_ref2 = files.map(function() {
return null;
}))), _ref2;
return files.forEach(function(file) {
return compilePath(path.join(source, file), false, base);
});
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
_results.push(compilePath(path.join(source, file), false, base));
}
return _results;
} else if (topLevel || helpers.isCoffee(source)) {
sources.push(source);
sourceCode.push(null);
delete notSources[source];
if (opts.watch) {
watch(source, base);
}
@@ -152,8 +155,7 @@
}
return compileScript(source, code.toString(), base);
} else {
notSources[source] = true;
return removeSource(source, base);
return notSources[source] = true;
}
};
@@ -303,36 +305,27 @@
var e, readdirTimeout, watcher;
readdirTimeout = null;
try {
watchedDirs[source] = true;
return watcher = fs.watch(source, function() {
clearTimeout(readdirTimeout);
return readdirTimeout = wait(25, function() {
return fs.readdir(source, function(err, files) {
var file, _i, _len, _results;
if (err) {
if (err.code !== 'ENOENT') {
throw err;
}
watcher.close();
return unwatchDir(source, base);
var err, file, files, _i, _len, _results;
try {
files = fs.readdirSync(source);
} catch (_error) {
err = _error;
if (err.code !== 'ENOENT') {
throw err;
}
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
if (!(!hidden(file) && !notSources[file])) {
continue;
}
file = path.join(source, file);
if (sources.some(function(s) {
return s.indexOf(file) >= 0;
})) {
continue;
}
sources.push(file);
sourceCode.push(null);
_results.push(compilePath(file, false, base));
}
return _results;
});
watcher.close();
return removeSourceDir(source, base);
}
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
_results.push(compilePath(path.join(source, file), false, base));
}
return _results;
});
});
} catch (_error) {
@@ -343,61 +336,56 @@
}
};
unwatchDir = function(source, base) {
var file, prevSources, toRemove, _i, _len;
prevSources = sources.slice(0);
toRemove = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = sources.length; _i < _len; _i++) {
file = sources[_i];
if (file.indexOf(source) >= 0) {
_results.push(file);
}
removeSourceDir = function(source, base) {
var file, sourcesChanged, _i, _len;
delete watchedDirs[source];
sourcesChanged = false;
for (_i = 0, _len = sources.length; _i < _len; _i++) {
file = sources[_i];
if (!(source === path.dirname(file))) {
continue;
}
return _results;
})();
for (_i = 0, _len = toRemove.length; _i < _len; _i++) {
file = toRemove[_i];
removeSource(file, base, true);
sourcesChanged = true;
}
if (!sources.some(function(s, i) {
return prevSources[i] !== s;
})) {
return;
if (sourcesChanged) {
return compileJoin();
}
return compileJoin();
};
removeSource = function(source, base, removeJs) {
var index, jsPath;
var err, index, jsPath;
index = sources.indexOf(source);
sources.splice(index, 1);
sourceCode.splice(index, 1);
if (removeJs && !opts.join) {
jsPath = outputPath(source, base);
return exists(jsPath, function(itExists) {
if (itExists) {
return fs.unlink(jsPath, function(err) {
if (err && err.code !== 'ENOENT') {
throw err;
}
return timeLog("removed " + source);
});
try {
fs.unlinkSync(jsPath);
} catch (_error) {
err = _error;
if (err.code !== 'ENOENT') {
throw err;
}
});
}
return timeLog("removed " + source);
}
};
outputPath = function(source, base, extension) {
var baseDir, basename, dir, srcDir;
var basename, dir, srcDir;
if (extension == null) {
extension = ".js";
}
basename = helpers.baseFileName(source, true, useWinPathSep);
srcDir = path.dirname(source);
baseDir = base === '.' || base === './' ? srcDir : srcDir.substring(base.length);
dir = opts.output ? path.join(opts.output, baseDir) : srcDir;
if (!opts.output) {
dir = srcDir;
} else if (source === base) {
dir = opts.output;
} else {
dir = path.join(opts.output, path.relative(base, srcDir));
}
return path.join(dir, basename + extension);
};
@@ -432,7 +420,7 @@
});
}
};
return exists(jsDir, function(itExists) {
return fs.exists(jsDir, function(itExists) {
if (itExists) {
return compile();
} else {
@@ -466,17 +454,12 @@
};
parseOptions = function() {
var i, o, source, _i, _len;
var o;
optionParser = new optparse.OptionParser(SWITCHES, BANNER);
o = opts = optionParser.parse(process.argv.slice(2));
o.compile || (o.compile = !!o.output);
o.run = !(o.compile || o.print || o.map);
o.print = !!(o.print || (o["eval"] || o.stdio && o.compile));
sources = o["arguments"];
for (i = _i = 0, _len = sources.length; _i < _len; i = ++_i) {
source = sources[i];
sourceCode[i] = null;
}
return o.print = !!(o.print || (o["eval"] || o.stdio && o.compile));
};
compileOptions = function(filename, base) {

View File

@@ -96,8 +96,7 @@
return new Value($1);
}), o('ObjAssignable : Expression', function() {
return new Assign(LOC(1)(new Value($1)), $3, 'object');
}), o('ObjAssignable :\
INDENT Expression OUTDENT', function() {
}), o('ObjAssignable : INDENT Expression OUTDENT', function() {
return new Assign(LOC(1)(new Value($1)), $4, 'object');
}), o('Comment')
],
@@ -573,14 +572,11 @@
} else {
return new Op($2, $1, $3);
}
}), o('SimpleAssignable COMPOUND_ASSIGN\
Expression', function() {
}), o('SimpleAssignable COMPOUND_ASSIGN Expression', function() {
return new Assign($1, $3, $2);
}), o('SimpleAssignable COMPOUND_ASSIGN\
INDENT Expression OUTDENT', function() {
}), o('SimpleAssignable COMPOUND_ASSIGN INDENT Expression OUTDENT', function() {
return new Assign($1, $4, $2);
}), o('SimpleAssignable COMPOUND_ASSIGN TERMINATOR\
Expression', function() {
}), o('SimpleAssignable COMPOUND_ASSIGN TERMINATOR Expression', function() {
return new Assign($1, $4, $2);
}), o('SimpleAssignable EXTENDS Expression', function() {
return new Extends($1, $3);

View File

@@ -166,30 +166,25 @@
};
Lexer.prototype.stringToken = function() {
var match, octalEsc, string;
switch (this.chunk.charAt(0)) {
var octalEsc, quote, string, trimmed;
switch (quote = this.chunk.charAt(0)) {
case "'":
if (!(match = SIMPLESTR.exec(this.chunk))) {
return 0;
}
string = match[0];
this.token('STRING', string.replace(MULTILINER, '\\\n'), 0, string.length);
string = SIMPLESTR.exec(this.chunk)[0];
break;
case '"':
if (!(string = this.balancedString(this.chunk, '"'))) {
return 0;
}
if (0 < string.indexOf('#{', 1)) {
this.interpolateString(string.slice(1, -1), {
strOffset: 1,
lexedLength: string.length
});
} else {
this.token('STRING', this.escapeLines(string), 0, string.length);
}
break;
default:
return 0;
string = this.balancedString(this.chunk, '"');
}
if (!string) {
return 0;
}
trimmed = this.removeNewlines(string.slice(1, -1));
if (quote === '"' && 0 < string.indexOf('#{', 1)) {
this.interpolateString(trimmed, {
strOffset: 1,
lexedLength: string.length
});
} else {
this.token('STRING', quote + this.escapeLines(trimmed) + quote, 0, string.length);
}
if (octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test(string)) {
this.error("octal escape sequences " + string + " are not allowed");
@@ -601,10 +596,6 @@
offsetInChunk = offsetInChunk || 0;
strOffset = strOffset || 0;
lexedLength = lexedLength || str.length;
if (heredoc && str.length > 0 && str[0] === '\n') {
str = str.slice(1);
strOffset++;
}
tokens = [];
pi = 0;
i = -1;
@@ -762,16 +753,31 @@
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
};
Lexer.prototype.removeNewlines = function(str) {
return str.replace(/^\s*\n\s*/, '').replace(/([^\\]|\\\\)\s*\n\s*$/, '$1');
};
Lexer.prototype.escapeLines = function(str, heredoc) {
return str.replace(MULTILINER, heredoc ? '\\n' : '');
str = str.replace(/\\[^\S\n]*(\n|\\)\s*/g, function(escaped, character) {
if (character === '\n') {
return '';
} else {
return escaped;
}
});
if (heredoc) {
return str.replace(MULTILINER, '\\n');
} else {
return str.replace(/\s*\n\s*/g, ' ');
}
};
Lexer.prototype.makeString = function(body, quote, heredoc) {
if (!body) {
return quote + quote;
}
body = body.replace(/\\([\s\S])/g, function(match, contents) {
if (contents === '\n' || contents === quote) {
body = body.replace(RegExp("\\\\(" + quote + "|\\\\)", "g"), function(match, contents) {
if (contents === quote) {
return contents;
} else {
return match;
@@ -840,19 +846,19 @@
NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;
HEREDOC = /^("""|''')((?:\\[\s\S]|[^\\])*?)(?:\n[^\n\S]*)?\1/;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?(\.|::)|\.{2,3})/;
WHITESPACE = /^[^\n\S]+/;
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/;
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/;
CODE = /^[-=]>/;
MULTI_DENT = /^(?:\n[^\n\S]*)+/;
SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/;
SIMPLESTR = /^'[^\\']*(?:\\[\s\S][^\\']*)*'/;
JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/;

View File

@@ -1821,7 +1821,7 @@
};
Code.prototype.compileNode = function(o) {
var answer, boundfunc, code, exprs, i, lit, p, param, params, ref, splats, uniqs, val, wasEmpty, wrapper, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref2, _ref3, _ref4, _ref5, _ref6;
var answer, boundfunc, code, exprs, i, lit, p, param, params, ref, splats, uniqs, val, wasEmpty, wrapper, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _m, _n, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7;
if (this.bound && ((_ref2 = o.scope.method) != null ? _ref2.bound : void 0)) {
this.context = o.scope.method.context;
}
@@ -1839,20 +1839,20 @@
delete o.isExistentialEquals;
params = [];
exprs = [];
this.eachParamName(function(name) {
if (!o.scope.check(name)) {
return o.scope.parameter(name);
}
});
_ref3 = this.params;
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
param = _ref3[_i];
o.scope.parameter(param.asReference(o));
}
_ref4 = this.params;
for (_j = 0, _len1 = _ref4.length; _j < _len1; _j++) {
param = _ref4[_j];
if (!param.splat) {
continue;
}
_ref4 = this.params;
for (_j = 0, _len1 = _ref4.length; _j < _len1; _j++) {
p = _ref4[_j].name;
_ref5 = this.params;
for (_k = 0, _len2 = _ref5.length; _k < _len2; _k++) {
p = _ref5[_k].name;
if (p["this"]) {
p = p.properties[0].name;
}
@@ -1861,20 +1861,20 @@
}
}
splats = new Assign(new Value(new Arr((function() {
var _k, _len2, _ref5, _results;
_ref5 = this.params;
var _l, _len3, _ref6, _results;
_ref6 = this.params;
_results = [];
for (_k = 0, _len2 = _ref5.length; _k < _len2; _k++) {
p = _ref5[_k];
for (_l = 0, _len3 = _ref6.length; _l < _len3; _l++) {
p = _ref6[_l];
_results.push(p.asReference(o));
}
return _results;
}).call(this))), new Value(new Literal('arguments')));
break;
}
_ref5 = this.params;
for (_k = 0, _len2 = _ref5.length; _k < _len2; _k++) {
param = _ref5[_k];
_ref6 = this.params;
for (_l = 0, _len3 = _ref6.length; _l < _len3; _l++) {
param = _ref6[_l];
if (param.isComplex()) {
val = ref = param.asReference(o);
if (param.value) {
@@ -1900,9 +1900,9 @@
exprs.unshift(splats);
}
if (exprs.length) {
(_ref6 = this.body.expressions).unshift.apply(_ref6, exprs);
(_ref7 = this.body.expressions).unshift.apply(_ref7, exprs);
}
for (i = _l = 0, _len3 = params.length; _l < _len3; i = ++_l) {
for (i = _m = 0, _len4 = params.length; _m < _len4; i = ++_m) {
p = params[i];
params[i] = p.compileToFragments(o);
o.scope.parameter(fragmentsToText(params[i]));
@@ -1926,7 +1926,7 @@
}
code += '(';
answer = [this.makeCode(code)];
for (i = _m = 0, _len4 = params.length; _m < _len4; i = ++_m) {
for (i = _n = 0, _len5 = params.length; _n < _len5; i = ++_n) {
p = params[i];
if (i) {
answer.push(this.makeCode(", "));

View File

@@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.6.3
(function() {
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref,
var BALANCED_PAIRS, CALL_CLOSERS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__slice = [].slice;
@@ -149,9 +149,9 @@
var stack;
stack = [];
return this.scanTokens(function(token, i, tokens) {
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, nextTag, offset, prevTag, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
var endAllImplicitCalls, endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, nextTag, offset, prevTag, prevToken, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
tag = token[0];
prevTag = (i > 0 ? tokens[i - 1] : [])[0];
prevTag = (prevToken = i > 0 ? tokens[i - 1] : [])[0];
nextTag = (i < tokens.length - 1 ? tokens[i + 1] : [])[0];
stackTop = function() {
return stack[stack.length - 1];
@@ -194,6 +194,14 @@
tokens.splice(i, 0, generate('CALL_END', ')'));
return i += 1;
};
endAllImplicitCalls = function() {
var _results;
_results = [];
while (inImplicitCall()) {
_results.push(endImplicitCall());
}
return _results;
};
startImplicitObject = function(j, startsLine) {
var idx;
if (startsLine == null) {
@@ -285,9 +293,15 @@
startImplicitObject(s, !!startsLine);
return forward(2);
}
if (prevTag === 'OUTDENT' && inImplicitCall() && (tag === '.' || tag === '?.' || tag === '::' || tag === '?::')) {
endImplicitCall();
return forward(1);
if (inImplicitCall() && __indexOf.call(CALL_CLOSERS, tag) >= 0) {
if (prevTag === 'OUTDENT') {
endImplicitCall();
return forward(1);
}
if (prevToken.newLine) {
endAllImplicitCalls();
return forward(1);
}
}
if (inImplicitObject() && __indexOf.call(LINEBREAKS, tag) >= 0) {
stackTop()[2].sameLine = false;
@@ -346,8 +360,8 @@
var action, condition, indent, outdent, starter;
starter = indent = outdent = null;
condition = function(token, i) {
var _ref, _ref1, _ref2;
return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'TERMINATOR' && (_ref1 = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref1) >= 0)) && !(token[0] === 'ELSE' && starter !== 'THEN') && !(((_ref2 = token[0]) === 'CATCH' || _ref2 === 'FINALLY') && (starter === '->' || starter === '=>'));
var _ref, _ref1, _ref2, _ref3;
return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'TERMINATOR' && (_ref1 = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref1) >= 0)) && !(token[0] === 'ELSE' && starter !== 'THEN') && !(((_ref2 = token[0]) === 'CATCH' || _ref2 === 'FINALLY') && (starter === '->' || starter === '=>')) || (_ref3 = token[0], __indexOf.call(CALL_CLOSERS, _ref3) >= 0) && this.tokens[i - 1].newLine;
};
action = function(token, i) {
return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent);
@@ -472,4 +486,6 @@
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT'];
CALL_CLOSERS = ['.', '?.', '::', '?::'];
}).call(this);