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.
Before this commit, expectations were sorted and de-duplicated before
they were passed to "buildMessage" and exposed in the "expected"
property of syntax errors. This commit moves this processing into
"buildMessage" and rewrites it to process only expectation descriptions.
This means expectations exposed in the "expected" property are "raw"
(not sorted and de-duplicated).
This change will allow us to get rid of the "description" property of
expectations and compute descriptions dynamically from structured
information in the expectations. This will make descriptions a
presentation-only concept. It will also make generated parsers smaller.
Note that to keep expectations in the "expected" property sorted even
without the "description" property, some sorting scheme based on
structured information in the expectations would have to be devised,
which would complicate things with only a little benefit. Therefore I
chose to keep the expectations there "raw".
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).
In most places, we talk about "generating a parser", not "building a
parser", which the function name should reflect. Also, mentioning a
parser in the name is not necessary as in case of a parser generator
it's pretty clear what is generated.
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.
Semantic predicate and action specs which verified label scope didn't
exercise labels in subexpressions. This commit adds cases exercising
them, including few commented-out cases which reveal #396 (these will be
uncommented when the bug gets fixed).
Note that added specs exercise all relevant expression types. This is
needed because code that makes subexpression labels invisible to the
outside is separate for each expression type so one generic test
wouldn't generate enough coverage.
Part of a fix of #396.
So far, semantic predicate and action specs which verified scope of
labels from containing or outer sequences exercised only cases where
label variables were defined. This commit adds also some negative cases.
The idea comes from @Mingun.
Semantic predicate and action specs which verified scope of labels from
outer sequences exercised all relevant expression types. This is not
needed as the behavior is common for all expression types and no extra
code needs to be written to make it work for each of them.
This commit changes the specs to verify scope of labels from outer
sequences using only one expression type.
Semantic predicate specs which verified scope of labels from containing
sequences used 3 elements where 1 is enough.
This commit removes the redundant elements.
Semantic predicate and action specs which verified label scope used
repetitive "it" blocks. Rewrite them to use just one "it" block and a
list of testcases. This makes them more concise.
Semantic predicate specs which verified label scope also checked parser
results. This is not necessary because for the purpose of these specs it
is enough to verify that label variables have correct values, which is
done in predicate code already.
This commit removes parser result checks from these specs.
Before this commit, generated parsers considered the following character
sequences as newlines:
Sequence Description
------------------------------
"\n" Unix
"\r" Old Mac
"\r\n" Windows
"\u2028" line separator
"\u2029" paragraph separator
This commit limits the sequences only to "\n" and "\r\n". The reason is
that nobody uses Unicode newlines or "\r" in practice.
A positive side effect of the change is that newline-handling code
became simpler (and likely faster).
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.
The |found| property wasn't very useful as it mostly contained just one
character or |null| (the exception being syntax errors triggered by
|error| or |expected|). Similarly, the "but XXX found" part of the error
message (based on the |found| property) wasn't much useful and was
redundant in presence of location info.
For these reasons, this commit removes the |found| property and
corresponding part of the error message from syntax errors. It also
modifies error location info slightly to cover a range of 0 characters,
not 1 character (except when the error is triggered by |error| or
|expected|). This corresponds more precisely to the actual situation.
Fixes#372.
Replace |line|, |column|, and |offset| properties of |SyntaxError| with
the |location| property. It contains an object similar to the one
returned by the |location| function available in action code:
{
start: { offset: 23, line: 5, column: 6 },
end: { offset: 25, line: 5, column: 8 }
}
For syntax errors produced in the middle of the input, |start| refers to
the first unparsed character and |end| refers to the character behind it
(meaning the span is 1 character). This corresponds to the portion of
the input in the |found| property.
For syntax errors produced the end of the input, both |start| and |end|
refer to a character past the end of the input (meaning the span is 0
characters).
For syntax errors produced by calling |expected| or |error| functions in
action code the location info is the same as the |location| function
would return.
Replace |line|, |column|, and |offset| functions with the |location|
function. It returns an object like this:
{
start: { offset: 23, line: 5, column: 6 },
end: { offset: 25, line: 5, column: 8 }
}
In actions, |start| refers to the position at the beginning of action's
expression and |end| refers to the position after the end of action's
expression. This allows one to easily add location info e.g. to AST
nodes created in actions.
In predicates, both |start| and |end| refer to the current position.
Fixes#246.
Action and predicate code can now see variables defined in expressions
"above" them.
Based on a pull request by Bryon Vandiver (@asterick):
https://github.com/pegjs/pegjs/pull/180Fixes#316.