The for-in statement in JavaScript iterates also over inherited
properties. This is typically not desired and requires adding a
check using Object.prototype.hasOwnProperty inside the loop.
This commit replaces all for-in statements and related checks inside
them with iteration over Object.keys(...). The iteration is performed
using either Array.prototype.forEach of a plain for loop.
Because arrow functions work rather differently than normal functions (a
bad design mistake if you ask me), I decided to be conservative with the
conversion.
I converted:
* event handlers
* callbacks
* arguments to Array.prototype.map & co.
* small standalone lambda functions
I didn't convert:
* functions assigned to object literal properties (the new shorthand
syntax would be better here)
* functions passed to "describe", "it", etc. in specs (because Jasmine
relies on dynamic "this")
See #442.
This is purely a mechanical change, not taking advantage of block scope
of "let" and "const". Minimizing variable scope will come in the next
commit.
In general, "var" is converted into "let" and "const" is used only for
immutable variables of permanent character (generally spelled in
ALL_CAPS). Using it for any immutable variable regardless on its
permanence would feel confusing.
Any code which is not transpiled and needs to run in ES6 environment
(examples, code in grammars embedded in specs, ...) is kept unchanged.
This is also true for code generated by PEG.js.
See #442.
This will allow to use ES2015 constructs in spec code.
The change required introducing a small server, which serves both PEG.js
and spec code passed through Babel and bundled together. This allowed to
convert the specs to regular modules and get rid of the hackery that was
previously needed to make them run both in Node.js and in the browser.
Note the specs no longer exercise the browser version. This will allow
to spec PEG.js internals in the future.
See #442.
Rename compiler passes as follows:
reportLeftRecursion -> reportInfiniteRecursion
reportInfiniteLoops -> reportInfiniteRepetition
This reflects the fact that both passes detect different ways of causing
the same problem (possible infinite loop when parsing).
The new terminology is more precise and in line with commonly used
programming languages.
The change involves mainly renaming related compiler pass and files
associated with it.
Until now, expectations were constructed using object literals. This
commit changes the construction to use factory functions.
This change makes generated parsers slightly smaller because property
names don't have to be repeated many times and factory function calls
are more amenable to minifying.
Some numbers based on the aggregate size of parsers generated from
examples/*.pegjs:
Optimization Minified? Size before Size after Saving
------------------------------------------------------------
speed no 719066 716063 0.42%
speed yes 188998 180202 4.65%
size no 194810 197813 1.52%
size yes 108782 99947 8.12%
(Minification was done using "uglify --mangle --compress" with
uglify-js 2.4.24.)
Instead of pre-generating expectation descriptions when generating
parsers, generate them dynamically from structured information contained
in the expectations.
This change makes descriptions a presentation-only concept. It also
makes generated parsers smaller.
Changes:
* Remove the "value" property (it is replaced by other properties).
* Add the "parts", "inverted", and "ignoreCase" properties (which
allow more structured access to expectation data).
Changes:
* Rename the "value" property to "text" (because it doesn't contain
the whole value, which also includes the case sensitivity flag).
* Add the "ignoreCase" property (which was missing).
If the described class is case-sensitive, nothing changes.
If the described class is case-insensitive, its description doesn't
indicate that anymore. The indication was awkward and it was meaningful
only for parser users familiar with PEG.js grammar syntax (typically a
minority). For cases where case insensitivity indication is vital, named
rules can be used to customize the reporting.
Note that literal descriptions already ignore the case-sensitivity flag;
this commit only makes things consistent.
I no longer think that using raw literal texts in error messages is the
right thing to do. The main reason is that it couples error messages
with details of the grammar such as use of single or double quotes in
literals. A better solution is coming in the next commit.
This reverts commit 69a0f769fc.
So far, PEG.js was exported in a "PEG" global variable when no module
loader was detected. The same variable name was also conventionally used
when requiring it in Node.js or otherwise referring to it. This was
reflected in various places in the code, documentation, examples, etc.
This commit changes the variable name to "peg" and fixes all relevant
occurrences. The main reason for the change is that in Node.js, modules
are generally referred to by lower-case variable names, so "PEG" was
sticking out when used in Node.js projects.
Instead of testing arguments.length to see whether an optional parameter
was passed to a function, compare its value to "undefined". This
approach has two advantages:
* It is in line with handling of default parameters in ES6.
* Optional parameters are actually spelled out in the parameter
list.
There is also one important disadvantage, namely that it's impossible to
pass "undefined" as an optional parameter value. This required a small
change in two tests.
Additional notes:
* Default parameter values are set in assignments immediately
after the function header. This reflects the fact that these
assignments really belong to the parameter list (which is where they
are in ES6).
* Parameter values are checked against "void 0" in places where
"undefined" can potentially be redefiend.
The "toParse" matcher in generated-parser-behavior.spec.js effectively
had these signatures:
toParse(input)
toParse(input, expected)
toParse(input, options, expected)
This commit regularizes them to:
toParse(input)
toParse(input, expected)
toParse(input, expected, options)
Similarly, the "toFailToParse" matcher in
generated-parser-behavior.spec.js effectively had these signatures:
toFailToParse(input)
toFailToParse(input, details)
toFailToParse(input, options, details)
This commit regularizes them to:
toFailToParse(input)
toFailToParse(input, details)
toFailToParse(input, details, options)
Finally, the "toChangeAST" matcher in helpers.js effectively had these
signatures:
toChangeAST(grammar, details)
toChangeAST(grammar, options, details)
This commit regularizes them to:
toChangeAST(grammar, details)
toChangeAST(grammar, details, options)
The overall purpose of these changes is to avoid different parameters
appearing at the same position, which is hard to manage without using
"arguments".
Labels in expressions like "(a:"a")" or "(a:"a" b:"b" c:"c")" were
visible to the outside despite being wrapped in parens. This commit
makes them invisible, as they should be.
Note this required introduction of a new "group" AST node, whose purpose
is purely to provide label scope isolation. This was necessary because
"label" and "sequence" nodes don't (and can't!) provide this isolation
themselves.
Part of a fix of #396.
Instead of setting ESLint environment to "node" globally, set it on
per-directory basis using separate .eslintrc.json files:
Directory Environment
-----------------------
bin node
lib commonjs
spec jasmine
It was impossible to use this approach for the "benchmark" directory
which contains a mix of files used in various environments. For
benchmark/run, the environment is set inline. For the other files, as
well as spec/helpers.js, the globals are declared manually (it is
impossible to express how these files are used just by a list of
environments).
Fixes#408.
Implement the swap and change various directives in the source code. The
"make hint" target becomes "make lint".
The change leads to quite some errors being reported by ESLint. These
will be fixed in subsequent commits.
Note the configuration enables just the recommended rules. Later I plan
to enable more rules to enforce the coding standard. The configuration
also sets the environment to "node", which is far from ideal as the
codebase contains a mix of CommonJS, Node.js and browser code. I hope to
clean this up at some point.
Report left recursion also in cases where the recursive rule invocation
is not a direct element of a sequence, but is wrapped inside an
expression.
Fixes#359.
Add missing |named| case to the visitor in lib/compiler/asts.js, which
makes the infinite loop and left recursion detectors work correctly with
named rules.
The missing case caused |make parser| to fail with:
140:34: Infinite loop detected.
make: *** [parser] Error 1
Preform the following renames:
* |reportedPos| -> |savedPos| (abstract machine variable)
* |peg$reportedPos| -> |peg$savedPos| (variable in generated code)
* |REPORT_SAVED_POS| -> |LOAD_SAVED_POS| (instruction)
* |REPORT_CURR_POS| -> |UPDATE_SAVED_POS| (instruction)
The idea is that the name |reportedPos| is no longer accurate after the
|location| change (seea the previous commit) because now both
|reportedPos| and |currPos| are reported to user code. Renaming to
|savedPos| resolves this inaccuracy.
There is probably some better name for the concept than quite generic
|savedPos|, but it doesn't come to me.