57 Commits (c377eff876ee0dd3c975262978b2741e2caf4fba)

Author SHA1 Message Date
David Majda fb7de36051 Update website URL
PEG.js website was moved from http://pegjs.majda.cz/ to http://pegjs.org/.
10 years ago
David Majda 2b06476c69 Regenerate lib/parser.js after bytecode changes 11 years ago
David Majda 57f7fae684 Fix a bug in |stringEscape|
The |stringEscape| function both in lib/compiler/javascript.js and in
generated parsers didn't escape characters in the U+0100..U+107F and
U+1000..U+107F ranges.
11 years ago
David Majda 46ac1bf171 Wrap initializer code in generated parsers into |{...}|
Initializer code is usually indented and this indentation is carried
over to generated code. This resulted in a piece of indented code in the
middle of the parser.

This commit wraps initializer code in |{...}|, which makes indentation
in generated parsers look a bit more natural.
11 years ago
David Majda 39084496ca Expose the parser object in action/predicate code
The action/predicate code didn't have access to the parser object. This
was mostly a side effect actions/predicates being implemented as nested
functions, in which |this| is a reference to the global object (an ugly
JavaScript quirk). The initializer, being implemented differently, had
access to the parser object via |this|, but this was not documented.

Because having access to the parser object can be useful, this commits
introduces a new |parser| variable which holds a reference to it, is
visible in action/predicate/initializer code, and is properly
documented.

See also:

  https://groups.google.com/forum/#!topic/pegjs/Na7YWnz6Bmg
11 years ago
David Majda c7521fb868 Mark |parse| and |SyntaxError| as internal identifiers
The |parse| function and the |SyntaxError| exception were meant as
internal, so let's mark them as such.
11 years ago
David Majda 7e3b4ec4f8 PEG.js grammar: Remove reserved word detection
This is mostly done for consistency with the JavaScript example grammar,
from which the |Identifier| rule is taken from. See the previous commit
for details.
11 years ago
David Majda e78ffbba9c PEG.js grammar: Improve the |Code| rule a bit
Instead of matching segments between blocks character by character,
match them as a whole. Also align the style with other similar rules
(e.g. the comment ones).
11 years ago
David Majda 64eb5faf54 PEG.js grammar: Fix line continuation handling in character classes
Before this commit, line continuations in character classes contributed
an empty string to the list of characters and character ranges matched
by a class. While this didn't lead to a buggy behavior with the current
code generator, the AST was wrong and the problem could have caused bugs
later.

This commit fixes the problem.
11 years ago
David Majda 0678bd8a0c PEG.js grammar: Add missing semicolon
Fixes the following JSHint error:

  lib/parser.js: line 108, col 54, Missing semicolon.
11 years ago
David Majda 0459ab6b37 PEG.js grammar: Formatting & comments 11 years ago
David Majda 6f2510e49e PEG.js grammar: Make rules with operators more generic 11 years ago
David Majda 45c29a886f PEG.js grammar: Extract the |SemanticPredicateExpression| rule
Semantic predicates are kind of |PrimaryExpression|, not kind of
|PrefixedExpression|. Therefore I extracted a rule for them and
referenced it from the |PrimaryExpression|.
11 years ago
David Majda da18f6a729 PEG.js grammar: Extract the |RuleReferenceExpression| rule
This makes the |Primary| rule a bit more tidy.
11 years ago
David Majda 8e6f98e45c PEG.js grammar: Extract the |ActionExpression| rule
Having it separated from the |SequenceExpression| rule is cleaner and
more logical.
11 years ago
David Majda 5c6f4dd38b PEG.js grammar: Append |Expression| to expression rule names
Makes the rule names a bit longer but also clearer.
11 years ago
David Majda 27c2d26203 PEG.js grammar: More JavaScript-like initializer/rule separation
Initializer and rules are now separated in a similar way as JavaScript
statements -- either by a semicolon or a line terminator, possibly with
whitespace and comments mixed in.

One consequence is that the grammars like this are now illegal:

  foo = "a" bar = "b"

A semicolon needs to be inserted between the rules:

  foo = "a";bar = "b"

I consider this a good change as the now-illegal syntax was somewhat
confusing.
11 years ago
David Majda 4ce7593f5f PEG.js grammar: Extract the |AnyMatcher| rule
This makes the |Primary| rule a bit more tidy. Also, matching the |.|
character really belongs to the lexical part of the grammar, next to
literals and character classes.
11 years ago
David Majda c0df01b092 PEG.js grammar: Improve code block handling
* Rename the |Action| rule to |CodeBlock| (it better describes what
    the rule matches).

  * Implement the rule in a simpler way and move it after more basic
    lexical elements.
11 years ago
David Majda 13f72bb19d PEG.js grammar: More JavaScript-like rules for identifiers
This change has two side effects:

  * Label names can no longer be JavaScript reserved words.

  * |$| is allowed again in label names. However, because of the
    preference rules, names starting with it will be usually parsed as a
    text operator followed by another identifier (denoting a rule
    reference or label name).
11 years ago
David Majda 0d6b91cb20 PEG.js grammar: More JavaScript-like rules for strings/literals/classes 11 years ago
David Majda bcb5271649 PEG.js grammar: More JavaScript-like rules for skipped elements 11 years ago
David Majda a5a0609505 PEG.js grammar: Inline trivial character rules 11 years ago
David Majda ae89f5e469 PEG.js grammar: Change whitespace handling
Before this commit, whitespace was handled at the lexical level by
making tokens consume any whitespace coming after them. This was
accomplished by appending |__| to every token rule.

This commit changes whitespace handling to be more explicit. Tokens no
longer consume whitespace coming after them and syntactic rules have to
cope with it. While this slightly complicates the syntactic grammar, I
think it's a cleaner way. Moreover, it is what JavaScript example
grammar does.

One small side-effect of thich change is that the grammar is now
stand-alone (it doesn't require utils.js anymore).
11 years ago
David Majda 4725632641 PEG.js grammar: Capitalize rule names
When rule names are capitalized, it's easier to visually distinguish
them from non-capitalized label names. Moreover, I use capitalized rule
names in all my grammars these days.
11 years ago
David Majda fb72c430e6 PEG.js grammar: Fix line continuation handling
Before this commit, a line continuation (backslash followed by a line
terminator character) contributed a character to a string or a character
class it was used in. In JavaScript and many other languages, line
continuation doesn't contribute anything.

This commit aligns PEG.js line continuation behavior with JavaScript.
11 years ago
David Majda 3dbec0b30d PEG.js grammar: Fix how |rawText| is created
Before this commit, the value of the |rawText| property of "class" AST
nodes was created in a hackish way from processed input and it didn't
always exactly represent the actual input text.

This commit changes the code so that the value of the |rawText| property
is created using the |text| function. This is a clean way which also
resolves the exact representation problem.
11 years ago
David Majda df154daafb PEG.js grammar: Disallow empty sequences
Empty sequences are useless and they only confused users. Let's disallow
them.
11 years ago
David Majda 2263a30034 Update version to 0.8.0 11 years ago
David Majda a56d3ac94f Fix error messages in certain cases with trailing input
In case the generated parser parsed successfully part of input and left
some input unparsed (trailing input), the error message produced was
sometimes wrong. The code worked correctly only if there were no match
failures in the successfully parsed part (highly unlikely).

This commit fixes things by explicitly triggering a match failure with the
following expectation at the end of the successfully parsed part of the
input:

  peg$fail({ type: "end", description: "end of input" });

This change also made it possible to simplify the |buildMessage|
function, which can now ignore the case of no expectations.

Fixes #119.
11 years ago
David Majda e15c57066c Remove an error check after calling action code
The error check was useful when actions could have returned |null| to
trigger a match failure. This is no longer supported so the check isn't
needed anymore.

Speed impact
------------
Before:     1022.70 kB/s
After:      1035.45 kB/s
Difference: 1.24%

Size impact
-----------
Before:     975434 b
After:      931540 b
Difference: -4.50%

(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
11 years ago
David Majda 2f2152204a Refine error handling further
Before this commit, the |expected| and |error| functions didn't halt the
parsing immediately, but triggered a regular match failure. After they
were called, the parser could backtrack, try another branches, and only
if no other branch succeeded, it triggered an exception with information
possibly based on parameters passed to the |expected| or |error|
function (this depended on positions where failures in other branches
have occurred).

While nice in theory, this solution didn't work well in practice. There
were at least two problems:

  1. Action expression could have easily triggered a match failure later
     in the input than the action itself. This resulted in the
     action-triggered failure to be shadowed by the expression-triggered
     one.

     Consider the following example:

       integer = digits:[0-9]+ {
         var result = parseInt(digits.join(""), 10);

         if (result % 2 === 0) {
           error("The number must be an odd integer.");
           return;
         }

         return result;
       }

     Given input "2", the |[0-9]+| expression would record a match
     failure at position 1 (an unsuccessful attempt to parse yet another
     digit after "2"). However, a failure triggered by the |error| call
     would occur at position 0.

     This problem could have been solved by silencing match failures in
     action expressions, but that would lead to severe performance
     problems (yes, I tried and measured). Other possible solutions are
     hacks which I didn't want to introduce into PEG.js.

  2. Triggering a match failure in action code could have lead to
     unexpected backtracking.

     Consider the following example:

       class = "[" (charRange / char)* "]"

       charRange = begin:char "-" end:char {
         if (begin.data.charCodeAt(0) > end.data.charCodeAt(0)) {
           error("Invalid character range: " + begin + "-" + end + ".");
         }

         // ...
       }

       char = [a-zA-Z0-9_\-]

     Given input "[b-a]", the |charRange| rule would fail, but the
     parser would try the |char| rule and succeed repeatedly, resulting
     in "b-a" being parsed as a sequence of three |char|'s, which it is
     not.

     This problem could have been solved by using negative predicates,
     but that would complicate the grammar and still wouldn't get rid of
     unintuitive behavior.

Given these problems I decided to change the semantics of the |expected|
and |error| functions. They don't interact with regular match failure
mechanism anymore, but they cause and immediate parse failure by
throwing an exception. I think this is more intuitive behavior with less
harmful side effects.

The disadvantage of the new approach is that one can't backtrack from an
action-triggered error. I don't see this as a big deal as I think this
will be rarely needed and one can always use a semantic predicate as a
workaround.

Speed impact
------------
Before:     993.84 kB/s
After:      998.05 kB/s
Difference: 0.42%

Size impact
-----------
Before:     1019968 b
After:      975434 b
Difference: -4.37%

(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
11 years ago
David Majda 9fa7301aec Regenerate src/parser.js
Forgot to do it in f8b5e04bba.
11 years ago
David Majda 5460a881af Error handling: Implement the |error| function
The |error| function allows users to report custom match failures inside
actions.

If the |error| function is called, and the reported match failure turns
out to be the cause of a parse error, the error message reported by the
parser will be exactly the one specified in the |error| call.

Implements part of #198.

Speed impact
------------
Before:     999.83 kB/s
After:      1000.84 kB/s
Difference: 0.10%

Size impact
-----------
Before:     1017212 b
After:      1019968 b
Difference: 0.27%

(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
11 years ago
David Majda dd74ea4144 Error handling: Build error message out of |SyntaxError|'s constructor
It will be possible to create errors with user-supplied messages soon.
The |SyntaxError| class needs to be ready for that.

Implements part of #198.
11 years ago
David Majda 3fe6aba7e2 Error handling: Extract exception building into its own function
The exception-creating code will get somewhat hairy soon, so let's make
sure them mess will be contained.

Implements part of #198.
11 years ago
David Majda d96eb317fd Error handling: Rename |peg$fail| to |peg$expected|
This is in anticipation of |peg$error|. The |peg$expected| and
|peg$error| internal functions will nicely mirror the |expected| and
|error| functions available to user code in actions.

Implements part of #198.
11 years ago
David Majda af701dcf80 Error handling: Implement the |expected| function
The |expected| function allows users to report regular match failures
inside actions.

If the |expected| function is called, and the reported match failure
turns out to be the cause of a parse error, the error message reported
by the parser will be in the usual "Expected ... but found ..." format
with the description specified in the |expected| call used as part of
the message.

Implements part of #198.

Speed impact
------------
Before:     1146.82 kB/s
After:      1031.25 kB/s
Difference: -10.08%

Size impact
-----------
Before:     950817 b
After:      973269 b
Difference: 2.36%

(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
11 years ago
David Majda 1b2279e026 Error handling: Make predicates always return |undefined|
After making the |?| operator return |null| instead of an empty string
in the previous commit, empty strings were still returned from
predicates. This didn't make much sense.

Return value of a predicate is unimportant (if you have one in hand, you
already know the predicate succeeded) and one could even argue that
predicates shouldn't return any value at all. The closest thing to
"return no value" in JavaScript is returning |undefined|, so I decided
to make predicates return exactly that.

Implements part of #198.
11 years ago
David Majda 86769a6c5c Error handling: Make |?| return |null| on unsuccessful match
Before this commit, the |?| operator returned an empty string upon
unsuccessful match. This commit changes the returned value to |null|. It
also updates the PEG.js grammar and the example grammars, which used the
value returned by |?| quite often.

Returning |null| is possible because it no longer indicates a match
failure.

I expect that this change will simplify many real-world grammars, as an
empty string is almost never desirable as a return value (except some
lexer-level rules) and it is often translated into |null| or some other
value in action code.

Implements part of #198.
11 years ago
David Majda 57e806383c Error handling: Use a special value (not |null|) to indicate failure
Using a special value to indicate match failure instead of |null| allows
actions to return |null| as a regular value. This simplifies e.g. the
JSON parser.

Note the special value is internal and intentionally undocumented. This
means that there is currently no official way how to trigger a match
failure from an action. This is a temporary state which will be fixed
soon.

The negative performance impact (see below) is probably caused by
changing lot of comparisons against |null| (which likely check the value
against a fixed constant representing |null| in the interpreter) to
comparisons against the special value (which likely check the value
against another value in the interpreter).

Implements part of #198.

Speed impact
------------
Before:     1146.82 kB/s
After:      1031.25 kB/s
Difference: -10.08%

Size impact
-----------
Before:     950817 b
After:      973269 b
Difference: 2.36%

(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
11 years ago
David Majda 435bb8f2df Error handling: Structured expectations
Before this commit, the |expected| property of an exception object
thrown when a generated parser encountered an error contained
expectations as strings. These strings were in a human-readable format
suitable for displaying in the UI but not suitable for machine
processing. For example, expected string literals included quotes and a
string "any character" was used when any character was expected.

This commit makes expectations structured objects. This makes the
machine processing easier, while still allowing to generate a
human-readable representation if needed.

Implements part of #198.

Speed impact
------------
Before:     1180.41 kB/s
After:      1165.31 kB/s
Difference: -1.28%

Size impact
-----------
Before:     863523 b
After:      950817 b
Difference: 10.10%

(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
11 years ago
David Majda 5312e124cd Fix object literal formatting in generated code 11 years ago
David Majda 379f5c5eef Regenerate src/parser.js
It wasn't done in beb557d7d3.
11 years ago
David Majda 798ed6a8a4 Regenerate src/parser.js
Forgot to do it in 0df8989f7a.

Part of a fix of GH-152.
12 years ago
David Majda 8759d4899e Fix deduplication in |peg$cleanupExpected|
The deduplication skipped over an expected string right after the one
that was removed because the index variable was incorrectly incremented
in that case.

Based on a patch by @fresheneesz:

  https://github.com/dmajda/pegjs/pull/146
12 years ago
David Majda 02af83f9b4 s/subclass/peg$subclass/
The |subclass| function is not intended to be used by user code.
12 years ago
David Majda 5942988f66 Remove the |startRule| property from the AST
It's redundant.
12 years ago
David Majda fe1ca481ab 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
David Majda f0a6bc92cc Text nodes: Use text nodes in PEG.js grammar 12 years ago