Before this commit, generated parser were able to start parsing from any
rule. This was nice, but it made rule code inlining impossible.
Since this commit, the list of allowed start rules has to be specified
explicitly using the |allowedStartRules| option of the |PEG.buildParser|
method (or the --allowed-start-rule option on the command-line). These
rules will be excluded from inlining when it's implemented.
This commit replaces the |startRule| parameter of the |parse| method in
generated parsers with more generic |options| -- an options object. This
options object can be used to pass custom options to the parser because
it is visible as the |options| variable inside parser code.
The start rule can now be specified as the |startRule| option. This
means you have to replace all calls like:
parser.parse("input", "myStartRule");
with
parser.parse("input", { startRule: "myStartRule" });
Closes GH-37.
The purpose of this change is to avoid the need to index register
variables storing match results of sequences whose elements are labeled.
The indexing happened when match results of labeled elements were passed
to action/predicate functions.
In order to avoid indexing, the register allocator needs to ensure that
registers storing match results of any labeled sequence elements are
still "alive" after finishing parsing of the sequence. They should not
be used to store anything else at least until code of all actions and
predicates that can see the labels is executed. This requires that the
|allocateRegisters| pass has the knowledge of scoping. Because that
knowledge was already implicitly embedded in the |coputeParams| pass,
the logical step to prevent duplication was to merge it with the
|allocateRegisters| pass. This is what this commit does.
As a part of the merge the tests of both passes were largely refactored.
This is both to accomodate the merge and to make the tests in sync with
the code again (the tests became a bit out-of-sync during the last few
commits -- they tested more than was needed).
The speed/size impact is slightly positive:
Speed impact
------------
Before: 849.86 kB/s
After: 858.16 kB/s
Difference: 0.97%
Size impact
-----------
Before: 876618 b
After: 875602 b
Difference: -0.12%
(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
This commit changes the model underlying parser variables used to store
match results and parse positions. Until now they were treated as a
stack, now they are thought of as registers. The actual behavior does
not change (yet), only the terminology.
More specifically, this commit:
* Changes parser variable names from |result0|, |result1|, etc. to
|r0|, |r1|, etc.
* Changes various internal names and comments to match the new model.
* Renames the |computeVarIndices| pass to |allocateRegisters|.
One stack is conceptually simpler, requires less code and will make a
transition to a register-based machine easier.
Note that the stack variables are now named a bit incorrectly
(|result0|, |result1|, etc. even when they store also parse positions).
I didn't bother with renaming because a transition to a register-based
machine will follow soon and the names will change anyway.
The speed/size impact is insignificant.
Speed impact
------------
Before: 839.05 kB/s
After: 839.67 kB/s
Difference: 0.07%
Size impact
-----------
Before: 949783 b
After: 961578 b
Difference: 1.24%
(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
Changes all code that does something with "literal", "class" or "any"
AST nodes so that the code deals with these in the follwing order:
1. literal
2. class
3. any
Previously the code used this ordering:
1. literal
2. any
3. class
The new ordering is more logical because the nodes are handled from the
most specific to the most generic.
PEG.js grammar rules are represented by |rule| nodes in the AST. Until
now, all such nodes had a |displayName| property which was either |null|
or stored rule's human-readable name. This commit gets rid of the
|displayName| property and starts representing rules with a
human-readable name using a new |named| node (a child of the |rule|
node).
This change simplifies code generation code a bit as tests for
|displayName| can be removed (see changes in generate-code.js). It also
separates different concerns from each other nicely.
This option makes the generated parser track line and column during
parsing. Tracked line and column are made available inside actions and
predicates as |line| and |column| variables.
Note that in actions these variables denote start position of the
action's expression while in predicates they denote the current
position. The slightly different behavior is motivated by expected
usage.
This simplifies the code a bit and makes the AST more regular (each node
type has a fixed set of properties). The latter may get useful later
when generalizing visitors.
|quote| is used outside of the |parse| function so it must be defined in
more outer scope.
Fixes a problem (introduced in e9d8dc8eba)
where construction of some error messages could throw an error.
Also change |quote| in src/emitter.js so both are in sync.
Fixes the following JSHint errors:
./src/parser.js: line 3613, col 27, Mixed spaces and tabs.
./src/parser.js: line 3613, col 31, Unsafe character.
./src/parser.js: line 3613, col 38, Control character in string: [ .
./src/parser.js: line 3613, col 40, Control character in string: [
Fixes the following JSHint errors:
./src/parser.js: line 2878, col 44, Missing radix parameter.
./src/parser.js: line 2949, col 44, Missing radix parameter.
Fixes the following JSHint errors:
./src/parser.js: line 460, col 50, Expected '!==' and instead saw '!='.
./src/parser.js: line 486, col 42, Expected '!==' and instead saw '!='.
Fixes the following JSHint errors:
./src/parser.js: line 193, col 18, Missing semicolon.
./src/parser.js: line 407, col 20, Missing semicolon.
./src/parser.js: line 2493, col 18, Missing semicolon.
./src/parser.js: line 2759, col 40, Missing semicolon.
Fixes the following JSHint errors:
./src/parser.js: line 102, col 26, 'escapeChar' is already defined.
./src/parser.js: line 103, col 22, 'length' is already defined.
./src/parser.js: line 106, col 23, 'escapeChar' used out of scope.
./src/parser.js: line 106, col 86, 'length' used out of scope.
The speedup is marginal (if any) but let's have this anyway.
Speed impact
------------
Before: 212.49 kB/s
After: 213.01 kB/s
Difference: 0.24%
Size impact
-----------
Before: 1056976 b
After: 1058314 b
Difference: 0.12%
(Measured by /tools/impact with Node.js v0.4.8 on x86_64 GNU/Linux.)
Closes GH-50.
This is little faster than |String.prototype.match| in successful cases
since return value of |test| is just a boolean, not a special array as
with |match|.
Speed impact
------------
Before: 130.75 kB/s
After: 131.81 kB/s
Difference: 0.81%
Size impact
-----------
Before: 1059811 b
After: 1058371 b
Difference: -0.14%
(Measured by /tools/impact with Node.js v0.4.8 on x86_64 GNU/Linux.)
Before this commit, variables for saving match results and parse
positions in generated parsers were not used efficiently. Each rule
basically used its own variable(s) for storing the data, with names
generated sequentially during code emitting. There was no reuse of
variables and a lot of unnecessary assignments between them.
It is easy to see that both match results and parse positions can
actually be stored on a stack that grows as the parser walks deeper in
the grammar tree and shrinks as it returns. Moreover, if one creates a
new stack for each rule the parser enters, its maximum depth can be
computed statically from the grammar. This allows us to implement the
stack not as an array, but as a set of numbered variables in each
function that handles parsing of a grammar rule, avoiding potentially
slow array accesses.
This commit implements the idea from the previous paragraph, using
separate stack for match results and for parse positions. As a result,
defined variables are reused and unnecessary copying avoided.
Speed implications
------------------
This change speeds up the benchmark suite execution by 2.14%.
Detailed results (benchmark suite totals as reported by "jake benchmark"
on Node.js 0.4.8):
-----------------------------------
Test # Before After
-----------------------------------
1 129.01 kB/s 131.98 kB/s
2 129.39 kB/s 130.13 kB/s
3 128.63 kB/s 132.57 kB/s
4 127.53 kB/s 129.82 kB/s
5 127.98 kB/s 131.80 kB/s
-----------------------------------
Average 128.51 kB/s 131.26 kB/s
-----------------------------------
Size implications
-----------------
This change makes a sample of generated parsers 8.60% smaller:
Before:
$ wc -c src/parser.js examples/*.js
110867 src/parser.js
13886 examples/arithmetics.js
450125 examples/css.js
632390 examples/javascript.js
61365 examples/json.js
1268633 total
After:
$ wc -c src/parser.js examples/*.js
99597 src/parser.js
13077 examples/arithmetics.js
399893 examples/css.js
592044 examples/javascript.js
54797 examples/json.js
1159408 total
Disabling failure reporting is driven by the |reportFailures| variable.
So far it was a boolean and its value was saved before changing and
restored afterwards (requiring additional variable in few places). This
patch changes it to an integer where value 0 means "report errors" and
anything > 0 means "do not report errors". Instead of saving/restoring
we can now simple increment/decrement (avoiding the additional
variable and simplifying the code).
This change speeds up the benchmark suite execution by 0.66%.
Detailed results (benchmark suite totals as reported by "jake benchmark"
on Node.js 0.4.8):
-----------------------------------
Test # Before After
-----------------------------------
1 129.26 kB/s 128.28 kB/s
2 127.34 kB/s 127.53 kB/s
3 126.72 kB/s 129.01 kB/s
4 126.89 kB/s 128.05 kB/s
5 126.46 kB/s 127.98 kB/s
-----------------------------------
Average 127.33 kB/s 128.17 kB/s
-----------------------------------
The change does not change the benchmark suite execution speed
statistically significantly.
Detailed results (benchmark suite totals as reported by "jake benchmark"
on Node.js 0.4.8):
-----------------------------------
Test # Before After
-----------------------------------
1 128.20 kB/s 128.03 kB/s
2 130.36 kB/s 128.73 kB/s
3 126.53 kB/s 129.72 kB/s
4 127.46 kB/s 127.48 kB/s
5 127.63 kB/s 128.53 kB/s
-----------------------------------
Average 128.04 kB/s 125.50 kB/s
-----------------------------------
Closes GH-25.