From 90d164445a373a7215864c94d493504b3b73fe2f Mon Sep 17 00:00:00 2001 From: David Majda Date: Mon, 16 Apr 2012 14:33:32 +0200 Subject: [PATCH] Update embedded Codie to version 1.1.0 --- src/emitter.js | 430 +++++++++++++++++++++++++------------------------ 1 file changed, 222 insertions(+), 208 deletions(-) diff --git a/src/emitter.js b/src/emitter.js index b807178..0841d16 100644 --- a/src/emitter.js +++ b/src/emitter.js @@ -3,244 +3,258 @@ PEG.compiler.emitter = function(ast, options) { options = options || {}; options.trackLineAndColumn = options.trackLineAndColumn || false; + /* + * Codie 1.1.0 + * + * https://github.com/dmajda/codie + * + * Copyright (c) 2011-2012 David Majda + * Licensend under the MIT license. + */ var Codie = (function(undefined) { - function stringEscape(s) { - function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } - - /* - * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a - * string literal except for the closing quote character, backslash, - * carriage return, line separator, paragraph separator, and line feed. - * Any character may appear in the form of an escape sequence. - * - * For portability, we also escape escape all control and non-ASCII - * characters. Note that "\0" and "\v" escape sequences are not used - * because JSHint does not like the first and IE the second. - */ - return s - .replace(/\\/g, '\\\\') // backslash - .replace(/"/g, '\\"') // closing double quote - .replace(/\x08/g, '\\b') // backspace - .replace(/\t/g, '\\t') // horizontal tab - .replace(/\n/g, '\\n') // line feed - .replace(/\f/g, '\\f') // form feed - .replace(/\r/g, '\\r') // carriage return - .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) - .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); }) - .replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); }) - .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); - } - function push(s) { return '__p.push(' + s + ');'; } - - function pushRaw(template, length, state) { - function unindent(code, level, unindentFirst) { - return code.replace( - new RegExp('^.{' + level +'}', "gm"), - function(str, offset) { - if (offset === 0) { - return unindentFirst ? '' : str; - } else { - return ""; - } - } - ); - } + function stringEscape(s) { + function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } - var escaped = stringEscape(unindent( - template.substring(0, length), - state.indentLevel(), - state.atBOL - )); + /* + * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a + * string literal except for the closing quote character, backslash, + * carriage return, line separator, paragraph separator, and line feed. + * Any character may appear in the form of an escape sequence. + * + * For portability, we also escape escape all control and non-ASCII + * characters. Note that "\0" and "\v" escape sequences are not used + * because JSHint does not like the first and IE the second. + */ + return s + .replace(/\\/g, '\\\\') // backslash + .replace(/"/g, '\\"') // closing double quote + .replace(/\x08/g, '\\b') // backspace + .replace(/\t/g, '\\t') // horizontal tab + .replace(/\n/g, '\\n') // line feed + .replace(/\f/g, '\\f') // form feed + .replace(/\r/g, '\\r') // carriage return + .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) + .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); }) + .replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); }) + .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); + } - return escaped.length > 0 ? push('"' + escaped + '"') : ''; + function push(s) { return '__p.push(' + s + ');'; } + + function pushRaw(template, length, state) { + function unindent(code, level, unindentFirst) { + return code.replace( + new RegExp('^.{' + level +'}', "gm"), + function(str, offset) { + if (offset === 0) { + return unindentFirst ? '' : str; + } else { + return ""; + } + } + ); } - var Codie = { - /* - * Specifies by how many characters do #if/#else and #for unindent their - * content in the generated code. - */ - indentStep: 2, - - /* Description of #-commands. Extend to define your own commands. */ - commands: { - "if": { - params: /^(.*)$/, - compile: function(state, prefix, params) { - return ['if(' + params[0] + '){', []]; - }, - stackOp: "push" + var escaped = stringEscape(unindent( + template.substring(0, length), + state.indentLevel(), + state.atBOL + )); + + return escaped.length > 0 ? push('"' + escaped + '"') : ''; + } + + + var Codie = { + /* Codie version (uses semantic versioning). */ + VERSION: "1.1.0", + + /* + * Specifies by how many characters do #if/#else and #for unindent their + * content in the generated code. + */ + indentStep: 2, + + /* Description of #-commands. Extend to define your own commands. */ + commands: { + "if": { + params: /^(.*)$/, + compile: function(state, prefix, params) { + return ['if(' + params[0] + '){', []]; }, - "else": { - params: /^$/, - compile: function(state) { - var stack = state.commandStack, - insideElse = stack[stack.length - 1] === "else", - insideIf = stack[stack.length - 1] === "if"; + stackOp: "push" + }, + "else": { + params: /^$/, + compile: function(state) { + var stack = state.commandStack, + insideElse = stack[stack.length - 1] === "else", + insideIf = stack[stack.length - 1] === "if"; - if (insideElse) { throw new Error("Multiple #elses."); } - if (!insideIf) { throw new Error("Using #else outside of #if."); } + if (insideElse) { throw new Error("Multiple #elses."); } + if (!insideIf) { throw new Error("Using #else outside of #if."); } - return ['}else{', []]; - }, - stackOp: "replace" + return ['}else{', []]; }, - "for": { - params: /^([a-zA-Z_][a-zA-Z0-9_]*)[ \t]+in[ \t]+(.*)$/, - init: function(state) { - state.forCurrLevel = 0; // current level of #for loop nesting - state.forMaxLevel = 0; // maximum level of #for loop nesting - }, - compile: function(state, prefix, params) { - var c = '__c' + state.forCurrLevel, // __c for "collection" - l = '__l' + state.forCurrLevel, // __l for "length" - i = '__i' + state.forCurrLevel; // __i for "index" - - state.forCurrLevel++; - if (state.forMaxLevel < state.forCurrLevel) { - state.forMaxLevel = state.forCurrLevel; - } + stackOp: "replace" + }, + "for": { + params: /^([a-zA-Z_][a-zA-Z0-9_]*)[ \t]+in[ \t]+(.*)$/, + init: function(state) { + state.forCurrLevel = 0; // current level of #for loop nesting + state.forMaxLevel = 0; // maximum level of #for loop nesting + }, + compile: function(state, prefix, params) { + var c = '__c' + state.forCurrLevel, // __c for "collection" + l = '__l' + state.forCurrLevel, // __l for "length" + i = '__i' + state.forCurrLevel; // __i for "index" + + state.forCurrLevel++; + if (state.forMaxLevel < state.forCurrLevel) { + state.forMaxLevel = state.forCurrLevel; + } - return [ - c + '=' + params[1] + ';' - + l + '=' + c + '.length;' - + 'for(' + i + '=0;' + i + '<' + l + ';' + i + '++){' - + params[0] + '=' + c + '[' + i + '];', - [params[0], c, l, i] - ]; - }, - exit: function(state) { state.forCurrLevel--; }, - stackOp: "push" + return [ + c + '=' + params[1] + ';' + + l + '=' + c + '.length;' + + 'for(' + i + '=0;' + i + '<' + l + ';' + i + '++){' + + params[0] + '=' + c + '[' + i + '];', + [params[0], c, l, i] + ]; }, - "end": { - params: /^$/, - compile: function(state) { - var stack = state.commandStack, exit; + exit: function(state) { state.forCurrLevel--; }, + stackOp: "push" + }, + "end": { + params: /^$/, + compile: function(state) { + var stack = state.commandStack, exit; - if (stack.length === 0) { throw new Error("Too many #ends."); } + if (stack.length === 0) { throw new Error("Too many #ends."); } - exit = Codie.commands[stack[stack.length - 1]].exit; - if (exit) { exit(state); } + exit = Codie.commands[stack[stack.length - 1]].exit; + if (exit) { exit(state); } - return ['}', []]; - }, - stackOp: "pop" + return ['}', []]; }, - "block": { - params: /^(.*)$/, - compile: function(state, prefix, params) { - var x = '__x', // __x for "prefix", - n = '__n', // __n for "lines" - l = '__l', // __l for "length" - i = '__i'; // __i for "index" - - /* - * Originally, the generated code used |String.prototype.replace|, but - * it is buggy in certain versions of V8 so it was rewritten. See the - * tests for details. - */ - return [ - x + '="' + stringEscape(prefix.substring(state.indentLevel())) + '";' - + n + '=(' + params[0] + ').toString().split("\\n");' - + l + '=' + n + '.length;' - + 'for(' + i + '=0;' + i + '<' + l + ';' + i + '++){' - + n + '[' + i +']=' + x + '+' + n + '[' + i + ']+"\\n";' - + '}' - + push(n + '.join("")'), - [x, n, l, i] - ]; - }, - stackOp: "nop" - } + stackOp: "pop" }, + "block": { + params: /^(.*)$/, + compile: function(state, prefix, params) { + var x = '__x', // __x for "prefix", + n = '__n', // __n for "lines" + l = '__l', // __l for "length" + i = '__i'; // __i for "index" + + /* + * Originally, the generated code used |String.prototype.replace|, but + * it is buggy in certain versions of V8 so it was rewritten. See the + * tests for details. + */ + return [ + x + '="' + stringEscape(prefix.substring(state.indentLevel())) + '";' + + n + '=(' + params[0] + ').toString().split("\\n");' + + l + '=' + n + '.length;' + + 'for(' + i + '=0;' + i + '<' + l + ';' + i + '++){' + + n + '[' + i +']=' + x + '+' + n + '[' + i + ']+"\\n";' + + '}' + + push(n + '.join("")'), + [x, n, l, i] + ]; + }, + stackOp: "nop" + } + }, - /* - * Compiles a template into a function. When called, this function will - * execute the template in the context of an object passed in a parameter and - * return the result. - */ - template: function(template) { - var stackOps = { - push: function(stack, name) { stack.push(name); }, - replace: function(stack, name) { stack[stack.length - 1] = name; }, - pop: function(stack) { stack.pop(); }, - nop: function() { } - }; - - function compileExpr(state, expr) { - state.atBOL = false; - return [push(expr), []]; - } + /* + * Compiles a template into a function. When called, this function will + * execute the template in the context of an object passed in a parameter and + * return the result. + */ + template: function(template) { + var stackOps = { + push: function(stack, name) { stack.push(name); }, + replace: function(stack, name) { stack[stack.length - 1] = name; }, + pop: function(stack) { stack.pop(); }, + nop: function() { } + }; - function compileCommand(state, prefix, name, params) { - var command, match, result; + function compileExpr(state, expr) { + state.atBOL = false; + return [push(expr), []]; + } - command = Codie.commands[name]; - if (!command) { throw new Error("Unknown command: #" + name + "."); } + function compileCommand(state, prefix, name, params) { + var command, match, result; - match = command.params.exec(params); - if (match === null) { - throw new Error( - "Invalid params for command #" + name + ": " + params + "." - ); - } + command = Codie.commands[name]; + if (!command) { throw new Error("Unknown command: #" + name + "."); } - result = command.compile(state, prefix, match.slice(1)); - stackOps[command.stackOp](state.commandStack, name); - state.atBOL = true; - return result; + match = command.params.exec(params); + if (match === null) { + throw new Error( + "Invalid params for command #" + name + ": " + params + "." + ); } - var state = { // compilation state - commandStack: [], // stack of commands as they were nested - atBOL: true, // is the next character to process at BOL? - indentLevel: function() { - return Codie.indentStep * this.commandStack.length; - } - }, - code = '', // generated template function code - vars = ['__p=[]'], // variables used by generated code - name, match, result, i; - - /* Initialize state. */ - for (name in Codie.commands) { - if (Codie.commands[name].init) { Codie.commands[name].init(state); } - } + result = command.compile(state, prefix, match.slice(1)); + stackOps[command.stackOp](state.commandStack, name); + state.atBOL = true; + return result; + } - /* Compile the template. */ - while ((match = /^([ \t]*)#([a-zA-Z_][a-zA-Z0-9_]*)(?:[ \t]+([^ \t\n][^\n]*))?[ \t]*(?:\n|$)|#\{([^}]*)\}/m.exec(template)) !== null) { - code += pushRaw(template, match.index, state); - result = match[2] !== undefined && match[2] !== "" - ? compileCommand(state, match[1], match[2], match[3] || "") // #-command - : compileExpr(state, match[4]); // #{...} - code += result[0]; - vars = vars.concat(result[1]); - template = template.substring(match.index + match[0].length); - } - code += pushRaw(template, template.length, state); + var state = { // compilation state + commandStack: [], // stack of commands as they were nested + atBOL: true, // is the next character to process at BOL? + indentLevel: function() { + return Codie.indentStep * this.commandStack.length; + } + }, + code = '', // generated template function code + vars = ['__p=[]'], // variables used by generated code + name, match, result, i; + + /* Initialize state. */ + for (name in Codie.commands) { + if (Codie.commands[name].init) { Codie.commands[name].init(state); } + } - /* Check the final state. */ - if (state.commandStack.length > 0) { throw new Error("Missing #end."); } + /* Compile the template. */ + while ((match = /^([ \t]*)#([a-zA-Z_][a-zA-Z0-9_]*)(?:[ \t]+([^ \t\n][^\n]*))?[ \t]*(?:\n|$)|#\{([^}]*)\}/m.exec(template)) !== null) { + code += pushRaw(template, match.index, state); + result = match[2] !== undefined && match[2] !== "" + ? compileCommand(state, match[1], match[2], match[3] || "") // #-command + : compileExpr(state, match[4]); // #{...} + code += result[0]; + vars = vars.concat(result[1]); + template = template.substring(match.index + match[0].length); + } + code += pushRaw(template, template.length, state); - /* Sanitize the list of variables used by commands. */ - vars.sort(); - for (i = 0; i < vars.length; i++) { - if (vars[i] === vars[i - 1]) { vars.splice(i--, 1); } - } + /* Check the final state. */ + if (state.commandStack.length > 0) { throw new Error("Missing #end."); } - /* Create the resulting function. */ - return new Function("__v", [ - '__v=__v||{};', - 'var ' + vars.join(',') + ';', - 'with(__v){', - code, - 'return __p.join("").replace(/^\\n+|\\n+$/g,"");};' - ].join('')); + /* Sanitize the list of variables used by commands. */ + vars.sort(); + for (i = 0; i < vars.length; i++) { + if (vars[i] === vars[i - 1]) { vars.splice(i--, 1); } } - }; - return Codie; + /* Create the resulting function. */ + return new Function("__v", [ + '__v=__v||{};', + 'var ' + vars.join(',') + ';', + 'with(__v){', + code, + 'return __p.join("").replace(/^\\n+|\\n+$/g,"");};' + ].join('')); + } + }; + + return Codie; + })(); var templates = (function() {