|
|
|
var utils = {
|
|
|
|
/* Like Python's |range|, but without |step|. */
|
|
|
|
range: function(start, stop) {
|
|
|
|
if (stop === undefined) {
|
|
|
|
stop = start;
|
|
|
|
start = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
var result = new Array(Math.max(0, stop - start));
|
|
|
|
for (var i = 0, j = start; j < stop; i++, j++) {
|
|
|
|
result[i] = j;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
find: function(array, callback) {
|
|
|
|
var length = array.length;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
if (callback(array[i])) {
|
|
|
|
return array[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
Code generator rewrite
This is a complete rewrite of the PEG.js code generator. Its goals are:
1. Allow optimizing the generated parser code for code size as well as
for parsing speed.
2. Prepare ground for future optimizations and big features (like
incremental parsing).
2. Replace the old template-based code-generation system with
something more lightweight and flexible.
4. General code cleanup (structure, style, variable names, ...).
New Architecture
----------------
The new code generator consists of two steps:
* Bytecode generator -- produces bytecode for an abstract virtual
machine
* JavaScript generator -- produces JavaScript code based on the
bytecode
The abstract virtual machine is stack-based. Originally I wanted to make
it register-based, but it turned out that all the code related to it
would be more complex and the bytecode itself would be longer (because
of explicit register specifications in instructions). The only downsides
of the stack-based approach seem to be few small inefficiencies (see
e.g. the |NIP| instruction), which seem to be insignificant.
The new generator allows optimizing for parsing speed or code size (you
can choose using the |optimize| option of the |PEG.buildParser| method
or the --optimize/-o option on the command-line).
When optimizing for size, the JavaScript generator emits the bytecode
together with its constant table and a generic bytecode interpreter.
Because the interpreter is small and the bytecode and constant table
grow only slowly with size of the grammar, the resulting parser is also
small.
When optimizing for speed, the JavaScript generator just compiles the
bytecode into JavaScript. The generated code is relatively efficient, so
the resulting parser is fast.
Internal Identifiers
--------------------
As a small bonus, all internal identifiers visible to user code in the
initializer, actions and predicates are prefixed by |peg$|. This lowers
the chance that identifiers in user code will conflict with the ones
from PEG.js. It also makes using any internals in user code ugly, which
is a good thing. This solves GH-92.
Performance
-----------
The new code generator improved parsing speed and parser code size
significantly. The generated parsers are now:
* 39% faster when optimizing for speed
* 69% smaller when optimizing for size (without minification)
* 31% smaller when optimizing for size (with minification)
(Parsing speed was measured using the |benchmark/run| script. Code size
was measured by generating parsers for examples in the |examples|
directory and adding up the file sizes. Minification was done by |uglify
--ascii| in version 1.3.4.)
Final Note
----------
This is just a beginning! The new code generator lays a foundation upon
which many optimizations and improvements can (and will) be made.
Stay tuned :-)
12 years ago
|
|
|
indexOf: function(array, callback) {
|
|
|
|
var length = array.length;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
if (callback(array[i])) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
},
|
|
|
|
|
|
|
|
contains: function(array, value) {
|
|
|
|
/*
|
|
|
|
* Stupid IE does not have Array.prototype.indexOf, otherwise this function
|
|
|
|
* would be a one-liner.
|
|
|
|
*/
|
|
|
|
var length = array.length;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
if (array[i] === value) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
each: function(array, callback) {
|
|
|
|
var length = array.length;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
callback(array[i], i);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
map: function(array, callback) {
|
|
|
|
var result = [];
|
|
|
|
var length = array.length;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
result[i] = callback(array[i], i);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
pluck: function(array, key) {
|
|
|
|
return utils.map(array, function (e) { return e[key]; });
|
|
|
|
},
|
|
|
|
|
|
|
|
keys: function(object) {
|
|
|
|
var result = [];
|
|
|
|
for (var key in object) {
|
|
|
|
result.push(key);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
values: function(object) {
|
|
|
|
var result = [];
|
|
|
|
for (var key in object) {
|
|
|
|
result.push(object[key]);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: function(object) {
|
|
|
|
var result = {};
|
|
|
|
for (var key in object) {
|
|
|
|
result[key] = object[key];
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
defaults: function(object, defaults) {
|
|
|
|
for (var key in defaults) {
|
|
|
|
if (!(key in object)) {
|
|
|
|
object[key] = defaults[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The code needs to be in sync with the code template in the compilation
|
|
|
|
* function for "action" nodes.
|
|
|
|
*/
|
|
|
|
subclass: function(child, parent) {
|
|
|
|
function ctor() { this.constructor = child; }
|
|
|
|
ctor.prototype = parent.prototype;
|
|
|
|
child.prototype = new ctor();
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns a string padded on the left to a desired length with a character.
|
|
|
|
*
|
|
|
|
* The code needs to be in sync with the code template in the compilation
|
|
|
|
* function for "action" nodes.
|
|
|
|
*/
|
|
|
|
padLeft: function(input, padding, length) {
|
|
|
|
var result = input;
|
|
|
|
|
|
|
|
var padLength = length - input.length;
|
|
|
|
for (var i = 0; i < padLength; i++) {
|
|
|
|
result = padding + result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns an escape sequence for given character. Uses \x for characters <=
|
|
|
|
* 0xFF to save space, \u for the rest.
|
|
|
|
*
|
|
|
|
* The code needs to be in sync with the code template in the compilation
|
|
|
|
* function for "action" nodes.
|
|
|
|
*/
|
|
|
|
escape: function(ch) {
|
|
|
|
var charCode = ch.charCodeAt(0);
|
|
|
|
var escapeChar;
|
|
|
|
var length;
|
|
|
|
|
|
|
|
if (charCode <= 0xFF) {
|
|
|
|
escapeChar = 'x';
|
|
|
|
length = 2;
|
|
|
|
} else {
|
|
|
|
escapeChar = 'u';
|
|
|
|
length = 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
return '\\' + escapeChar + utils.padLeft(charCode.toString(16).toUpperCase(), '0', length);
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Surrounds the string with quotes and escapes characters inside so that the
|
|
|
|
* result is a valid JavaScript string.
|
|
|
|
*
|
|
|
|
* The code needs to be in sync with the code template in the compilation
|
|
|
|
* function for "action" nodes.
|
|
|
|
*/
|
|
|
|
quote: function(s) {
|
|
|
|
/*
|
|
|
|
* 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 quote character
|
|
|
|
.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-\x1F\x80-\uFFFF]/g, utils.escape)
|
|
|
|
+ '"';
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Escapes characters inside the string so that it can be used as a list of
|
|
|
|
* characters in a character class of a regular expression.
|
|
|
|
*/
|
|
|
|
quoteForRegexpClass: function(s) {
|
|
|
|
/*
|
|
|
|
* Based on ECMA-262, 5th ed., 7.8.5 & 15.10.1.
|
|
|
|
*
|
|
|
|
* For portability, we also escape escape all control and non-ASCII
|
|
|
|
* characters.
|
|
|
|
*/
|
|
|
|
return s
|
|
|
|
.replace(/\\/g, '\\\\') // backslash
|
|
|
|
.replace(/\//g, '\\/') // closing slash
|
|
|
|
.replace(/\]/g, '\\]') // closing bracket
|
|
|
|
.replace(/\^/g, '\\^') // caret
|
|
|
|
.replace(/-/g, '\\-') // dash
|
|
|
|
.replace(/\0/g, '\\0') // null
|
|
|
|
.replace(/\t/g, '\\t') // horizontal tab
|
|
|
|
.replace(/\n/g, '\\n') // line feed
|
|
|
|
.replace(/\v/g, '\\x0B') // vertical tab
|
|
|
|
.replace(/\f/g, '\\f') // form feed
|
|
|
|
.replace(/\r/g, '\\r') // carriage return
|
|
|
|
.replace(/[\x01-\x08\x0E-\x1F\x80-\uFFFF]/g, utils.escape);
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Builds a node visitor -- a function which takes a node and any number of
|
|
|
|
* other parameters, calls an appropriate function according to the node type,
|
|
|
|
* passes it all its parameters and returns its value. The functions for
|
|
|
|
* various node types are passed in a parameter to |buildNodeVisitor| as a
|
|
|
|
* hash.
|
|
|
|
*/
|
|
|
|
buildNodeVisitor: function(functions) {
|
|
|
|
return function(node) {
|
|
|
|
return functions[node.type].apply(null, arguments);
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
findRuleByName: function(ast, name) {
|
|
|
|
return utils.find(ast.rules, function(r) { return r.name === name; });
|
Code generator rewrite
This is a complete rewrite of the PEG.js code generator. Its goals are:
1. Allow optimizing the generated parser code for code size as well as
for parsing speed.
2. Prepare ground for future optimizations and big features (like
incremental parsing).
2. Replace the old template-based code-generation system with
something more lightweight and flexible.
4. General code cleanup (structure, style, variable names, ...).
New Architecture
----------------
The new code generator consists of two steps:
* Bytecode generator -- produces bytecode for an abstract virtual
machine
* JavaScript generator -- produces JavaScript code based on the
bytecode
The abstract virtual machine is stack-based. Originally I wanted to make
it register-based, but it turned out that all the code related to it
would be more complex and the bytecode itself would be longer (because
of explicit register specifications in instructions). The only downsides
of the stack-based approach seem to be few small inefficiencies (see
e.g. the |NIP| instruction), which seem to be insignificant.
The new generator allows optimizing for parsing speed or code size (you
can choose using the |optimize| option of the |PEG.buildParser| method
or the --optimize/-o option on the command-line).
When optimizing for size, the JavaScript generator emits the bytecode
together with its constant table and a generic bytecode interpreter.
Because the interpreter is small and the bytecode and constant table
grow only slowly with size of the grammar, the resulting parser is also
small.
When optimizing for speed, the JavaScript generator just compiles the
bytecode into JavaScript. The generated code is relatively efficient, so
the resulting parser is fast.
Internal Identifiers
--------------------
As a small bonus, all internal identifiers visible to user code in the
initializer, actions and predicates are prefixed by |peg$|. This lowers
the chance that identifiers in user code will conflict with the ones
from PEG.js. It also makes using any internals in user code ugly, which
is a good thing. This solves GH-92.
Performance
-----------
The new code generator improved parsing speed and parser code size
significantly. The generated parsers are now:
* 39% faster when optimizing for speed
* 69% smaller when optimizing for size (without minification)
* 31% smaller when optimizing for size (with minification)
(Parsing speed was measured using the |benchmark/run| script. Code size
was measured by generating parsers for examples in the |examples|
directory and adding up the file sizes. Minification was done by |uglify
--ascii| in version 1.3.4.)
Final Note
----------
This is just a beginning! The new code generator lays a foundation upon
which many optimizations and improvements can (and will) be made.
Stay tuned :-)
12 years ago
|
|
|
},
|
|
|
|
|
|
|
|
indexOfRuleByName: function(ast, name) {
|
|
|
|
return utils.indexOf(ast.rules, function(r) { return r.name === name; });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = utils;
|