610 Commits (13f72bb19d80d4bd2fbafb134c8f86dfb7d17000)
 

Author SHA1 Message Date
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.)
10 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.)
10 years ago
David Majda 9fa7301aec Regenerate src/parser.js
Forgot to do it in f8b5e04bba.
10 years ago
David Majda f8b5e04bba Error handling: Use the new |error| function in PEG.js's grammar itself
Implements part of #198.
10 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.)
10 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.
10 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.
10 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.
10 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.)
10 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.
10 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.
10 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.)
10 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.)
10 years ago
David Majda 5312e124cd Fix object literal formatting in generated code 11 years ago
David Majda f3d392bd1c JavaScript example: Fix handling of elided elements in array literals
JavaScript allows one to skip (elide) elements in array literals. It
also allows a trailing comma, which doesn't imply an element elision.

For example, an array literal:

  [,,,]

contains three elided elements (one before each comma) and a trailing
comma.

Example JavaScript parser handled elided elements incorrectly and just
threw them away. This commit fixes this behvior and inserts |null| in
the AST for each elided element. This is in line with how SpiderMonkey's
JavaScript parser (the |Reflect.parse| API), Esprima and Acorn behave.

Based on a patch by @fpirsch:

  https://github.com/dmajda/pegjs/pull/177
11 years ago
David Majda d71bca46a1 Javascript example: Improve array literal rules
Makes the |ArrayLiteral| and |ElementList| rules more in line with the
ECMAScript grammar.

Based on a patch by @fpirsch:

  https://github.com/dmajda/pegjs/pull/177
11 years ago
David Majda b6ccad6695 Merge pull request #204 from andreineculau/upgrade-jasmine
Upgrade jasmine and jasmine-node
11 years ago
Andrei Neculau 7dc9a9ae76 Upgrade jasmine and jasmine-node 11 years ago
David Majda fe18c6ffd3 Fix |null| handling in the JSON parser
We couldn't return |null| in the |value| rule of the JSON example
parser because that would mean parse failure. So until now, we just
returned |"null"| (a string).

This was obviously stupid, so this commit changes the |value| rule to
return a special object instead that is converted to |null| later.

Based on patches by Patrick Logan (GH-91) and Jakub Vrána (GH-191).
11 years ago
David Majda c6efb337f1 Fix bytecode built for nested sequences inside actions
In the bytecode generator, the |context.action| property wasn't
correctly reset when generating bytecode for sequence elements. As a
result, when a sequence was wrapped in an action and it contained
another sequence as an element, the generator thought that the inner
sequence was wrapped in an action too.

For example, the following grammar:

  start = ("a" "b") "c" { return "x"; }

was compiled as if it looked like this:

  start = ("a" "b" { return "x"; }) "c" { return "x"; }

This commit fixes the problem by resetting |context.action| correctly.

Fixes GH-168.
11 years ago
David Majda 379f5c5eef Regenerate src/parser.js
It wasn't done in beb557d7d3.
11 years ago
David Majda 74636638d0 Merge pull request #196 from vrana/comma
Add whitespace to generated action calls
11 years ago
David Majda 64d0e39d83 Merge pull request #195 from tonylukasavage/patch-2
Add initializer example in README.md
11 years ago
Jakub Vrana beb557d7d3 Add whitespace to generated action calls
Avoids implicit array to string conversion.
11 years ago
Tony Lukasavage 35a4b35f94 Add initializer example in README.md 11 years ago
David Majda 791034fad9 Merge pull request #188 from vrana/comment
Fix typo in comment
11 years ago
Jakub Vrana 62d151cb5a Fix typo in comment 11 years ago
David Majda 798ed6a8a4 Regenerate src/parser.js
Forgot to do it in 0df8989f7a.

Part of a fix of GH-152.
11 years ago
David Majda 34fe2c01ae Fix matching of case-instensitive literals
Code that calculated which part of the input to match against a literal
was wrong in case of case-insensitive literals when generating
speed-optimized parsers. As a result, matching of case-insensitive
literals worked only at the end of the input (where too big length
passed to the |substr| method didn't matter).

Fixes GH-153.
11 years ago
David Majda 0df8989f7a Fix buggy position computation
Fixes GH-152.
11 years ago
David Majda 76cc5d55b4 Use the |s| function instead of hardcoded |s0| value
Based on a patch by @fresheneesz:

  https://github.com/dmajda/pegjs/pull/148
11 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
11 years ago
David Majda 851681d663 Implement the --extra-options and --extra-options-file options
These are mainly useful to pass additional options to plugins.
11 years ago
David Majda d013016717 bin/pegjs: Fix help wrapping
All help text should be wrapped at column 80.
11 years ago
David Majda e8a68e91e0 Merge pull request #155 from fpirsch/patch-3
Fix automatic semi-colon insertion
11 years ago
David Majda 2dc39bb779 bin/pegjs: Output just the parser source if --export-var is empty
This will make embedding generated parsers into other files easier.

Based on a patch by Glen Huang:

  https://github.com/dmajda/pegjs/pull/143
11 years ago
David Majda e1af175af8 Plugin API: Implement the --plugin option
Implements part of GH-106.
11 years ago
David Majda e4b5588327 Plugin API: Split compiler passes into stages
The compiler passes are now split into three stages:

  * check -- passes that check for various error conditions

  * transform -- passes that transform the AST (e.g. to perform
    optimizations)

  * generate -- passes that are related to code generation

Splitting the passes into stages is important for plugins. For example,
if a plugin wants to add a new optimization pass, it can add it at the
end of the "transform" stage without any knowledge about other passes it
contains. Similarly, if it wants to generate something else than the
default code generator does from the AST, it can just replace all passes
in the "generate" stage by its own one(s).

More generally, the stages make it possible to write plugins that do not
depend on names and actions of specific passes (which I consider
internal and subject of change), just on the definition of stages (which
I consider a public API with to which semver rules apply).

Implements part of GH-106.
11 years ago
David Majda 76f5c88073 Plugin API: Implement the |plugins| option for |PEG.buildParser|
The |plugins| option allows users to use plugins that change how PEG.js
operates.

A plugin is any JavaScript object with a |use| method. After the user
calls |PEG.buildParser|, this method is called for each plugin with the
following two parameters:

  * PEG.js config that describes used grammar parser and compiler
    passes used to generate the parser

  * options passed by user to |PEG.buildParser|

The plugin is expected to change the config as needed, possibly based on
the options passed by user. It can e.g. change the used grammar parser,
change the compiler passes (including adding its own), etc. This way it
can extend PEG.js in a flexible way.

Implements part of GH-106.
11 years ago
fpirsch d7e853b87c Fix automatic semi-colon insertion
Fix automatic semi-colon insertion in var statements without
initialisers.
var i
i = 1;
is valid and not accepted by the parser

but
var i = 2
i = 3;
is valid and accepted by the parser, as it should be.

With this fix, both are accepted.
11 years ago
David Majda d02098eebe Plugin API: Implement the |passes| parameter for |PEG.compiler.compile|
The |passes| parameter will allow to pass the list of passes from
|PEG.buildParser|. This will be used by plugins. The old way via setting
the |appliedPassNames| property is removed.

Implements part of GH-106.
11 years ago
David Majda 3b3798fa39 Merge lib/compiler/passes.js into lib/compiler.js
It didn't make sense to have the passes in a separate file.
11 years ago
David Majda 02af83f9b4 s/subclass/peg$subclass/
The |subclass| function is not intended to be used by user code.
11 years ago
David Majda 4fe32cee8c Fix indentation 11 years ago
David Majda f985bd76ed Fix opcodes in comment in generate-bytecode.js 11 years ago
David Majda c7481d4da1 Test property presence in |utils.defaults| using |in|
This is more correct than comparing to |undefined|.
11 years ago
David Majda d3d4ace153 Move options handling from passes to |PEG.compiler.compile|
This eliminates some duplicate code.
11 years ago
David Majda 5942988f66 Remove the |startRule| property from the AST
It's redundant.
11 years ago
David Majda 3981433984 Fix too eager proxy rules removal
Fixes GH-137.
11 years ago
David Majda 5d00815b41 Improve |removeProxyRules| pass specs a bit 11 years ago