104 lines
4.5 KiB
JavaScript
104 lines
4.5 KiB
JavaScript
(function(){
|
|
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_START, EXPRESSION_TAIL, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, SINGLE_CLOSERS, SINGLE_LINERS, __a, __b, __c, __d, __e, __f, __g, __h, pair, re;
|
|
// In order to keep the grammar simple, the stream of tokens that the Lexer
|
|
// emits is rewritten by the Rewriter, smoothing out ambiguities, mis-nested
|
|
// indentation, and single-line flavors of expressions.
|
|
exports.Rewriter = (re = function re() { });
|
|
// Tokens that must be balanced.
|
|
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END']];
|
|
// Tokens that signal the start of a balanced pair.
|
|
EXPRESSION_START = (function() {
|
|
__a = []; __b = BALANCED_PAIRS;
|
|
for (__c = 0; __c < __b.length; __c++) {
|
|
pair = __b[__c];
|
|
__a.push(pair[0]);
|
|
}
|
|
return __a;
|
|
}).call(this);
|
|
// Tokens that signal the end of a balanced pair.
|
|
EXPRESSION_TAIL = (function() {
|
|
__d = []; __e = BALANCED_PAIRS;
|
|
for (__f = 0; __f < __e.length; __f++) {
|
|
pair = __e[__f];
|
|
__d.push(pair[1]);
|
|
}
|
|
return __d;
|
|
}).call(this);
|
|
// Tokens that indicate the close of a clause of an expression.
|
|
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_TAIL);
|
|
// Tokens pairs that, in immediate succession, indicate an implicit call.
|
|
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END'];
|
|
IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', "\n", 'OUTDENT'];
|
|
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'ARGUMENTS', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT', '->', '=>', '[', '(', '{'];
|
|
// The inverse mappings of token pairs we're trying to fix up.
|
|
INVERSES = {
|
|
};
|
|
__g = BALANCED_PAIRS;
|
|
for (__h = 0; __h < __g.length; __h++) {
|
|
pair = __g[__h];
|
|
INVERSES[pair[0]] = pair[1];
|
|
INVERSES[pair[1]] = pair[0];
|
|
}
|
|
// 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 = ["\n", 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN', 'PARAM_START'];
|
|
// Rewrite the token stream in multiple passes, one logical filter at
|
|
// 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 like this.
|
|
re.prototype.rewrite = function rewrite(tokens) {
|
|
this.tokens = tokens;
|
|
this.adjust_comments();
|
|
// this.remove_leading_newlines()
|
|
// this.remove_mid_expression_newlines()
|
|
// this.move_commas_outside_outdents()
|
|
// this.close_open_calls_and_indexes()
|
|
// this.add_implicit_parentheses()
|
|
// this.add_implicit_indentation()
|
|
// this.ensure_balance(BALANCED_PAIRS)
|
|
// this.rewrite_closing_parens()
|
|
return this.tokens;
|
|
};
|
|
// Rewrite the token stream, looking one token ahead and behind.
|
|
// Allow the return value of the block to tell us how many tokens to move
|
|
// forwards (or backwards) in the stream, to make sure we don't miss anything
|
|
// as the stream changes length under our feet.
|
|
re.prototype.scan_tokens = function scan_tokens(yield) {
|
|
var i, move;
|
|
i = 0;
|
|
while (true) {
|
|
if (!(this.tokens[i])) {
|
|
break;
|
|
}
|
|
move = yield(this.tokens[i - 1], this.tokens[i], this.tokens[i + 1], i);
|
|
i += move;
|
|
}
|
|
return true;
|
|
};
|
|
// Massage newlines and indentations so that comments don't have to be
|
|
// correctly indented, or appear on their own line.
|
|
re.prototype.adjust_comments = function adjust_comments() {
|
|
return this.scan_tokens(function(prev, token, post, i) {
|
|
var after, before;
|
|
if (!(token[0] === 'COMMENT')) {
|
|
return 1;
|
|
}
|
|
before = this.tokens[i - 2];
|
|
after = this.tokens[i + 2];
|
|
if (before && after && ((before[0] === 'INDENT' && after[0] === 'OUTDENT') || (before[0] === 'OUTDENT' && after[0] === 'INDENT')) && before[1] === after[1]) {
|
|
this.tokens.splice(i + 2, 1);
|
|
this.tokens.splice(i - 2, 1);
|
|
return 0;
|
|
} else if (prev[0] === "\n" && after[0] === 'INDENT') {
|
|
this.tokens.splice(i + 2, 1);
|
|
this.tokens[i - 1] = after;
|
|
return 1;
|
|
} else if (prev[0] !== "\n" && prev[0] !== 'INDENT' && prev[0] !== 'OUTDENT') {
|
|
this.tokens.splice(i, 0, ["\n", "\n"]);
|
|
return 2;
|
|
} else {
|
|
return 1;
|
|
}
|
|
});
|
|
};
|
|
})(); |