236 Commits (a4a66a2e5be41832bcf23127a37515d155b700bc)

Author SHA1 Message Date
David Majda a4a66a2e5b Switch from first/rest to head/tail in the PEG.js grammar
In the past year I worked on various grammars where first/rest or
head/tail were used as labels for parts of lists. I found I associate
head/tail with a list immediately, while in case of first/rest I have to
"parse" grammar rules for a while before understanding their structure.

Moreover, I tend to assume that rest is a list of the same thigs as
first, but I don't have such assumption in case of head/tail. This
assumption was in conflict with the grammar structure.

I'm not sure how much these observations are applicable to others, but I
decided to act on them and switch from first/rest to head/tail.
9 years ago
David Majda 69a0f769fc Use literal raw text in error messages
Fixes #127.
9 years ago
David Majda 25ab98027d Remove info about found string from syntax errors
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.
9 years ago
David Majda bbb4f006cd Report full rule chain in recursive rule errors
The idea came from a PR by @Mingun:

  https://github.com/pegjs/pegjs/pull/307
9 years ago
David Majda 491106c347 Report left recursion and infinite loops only as "possible"
A semantic predicate can prevent the parser to actually enter infinite
recursion or loop. This is undetectable at compile-time.
9 years ago
David Majda ebf5d969b2 s/alwaysAdvancesOnSuccess/alwaysConsumesOnSuccess/
Matches terminology change from the previous commit.
9 years ago
David Majda 48fe4d6580 Rename generate-javascript.js to generate-js.js
Short & sweet.
9 years ago
David Majda 575883586f Rename javascript.js to js.js
Short & sweet.
9 years ago
David Majda 20a4fb2e7f Update version to 0.9.0 9 years ago
David Majda 671c22e80f Avoid using |console| in default tracer and its tests when not defined
This makes default tracer and its tests work in IE 8-10.
9 years ago
David Majda 6d82422045 Remove trailing comma in an array literal
It caused an additional newline in generated parsers in IE 8.
9 years ago
David Majda 969d39e8d9 Remove trailing commas in object literals
They broke IE 8-9.
9 years ago
David Majda d5f93a8282 Fix comment typo 9 years ago
David Majda a4772376fb Renumber bytecode instructions sequentially 9 years ago
David Majda ad27a300a8 Fix left recursion detection in sequences
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.
9 years ago
David Majda cdeecf750f reportLeftRecursion: Change handling of the |visitedRules| array
Before this commit, the |reportLeftRecursion| pass was written in
functional style, passing the |visitedRules| array around as a parameter
and making a new copy each time a rule was visited. This apparently
caused performance problems in some deeply recursive grammars.

This commit makes it so that there is just one array which is shared
across all the visitor functions via a closure and modified as rules are
visited.

I don't like losing the functional style (it was elegant) but
performance is more important.

Fixes #203.
9 years ago
David Majda 4b154e177f Update character categories in grammars to Unicode 8.0.0 9 years ago
David Majda d7d7e87874 Make infinite loop and left recursion detectors work with named rules
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
9 years ago
David Majda 130cbcfaa3 Rename asts.matchesEmpty to alwaysAdvancesOnSuccess and negate it
This makes it more clear that the function isn't about the input the
expression *matched* but about the input it *consumed* when it matched.

Based on a comment by @Mingun:

  https://github.com/pegjs/pegjs/pull/307#issuecomment-89512575
9 years ago
David Majda 5e6b5da4e9 Merge pull request #347 from mbaumgartl/errorstack
Add stack trace in engines based on V8
9 years ago
Marco Baumgartl 940a66fb38 Add stack trace in engines based on V8. Fixes #331 9 years ago
Arlo Breault 12c169e7b5 Convert PEG.js code to strict mode
* Issues #323
9 years ago
Arlo Breault 45e39c3ac8 Make generated parsers use strict mode
* Issue #324

 * JSHint complains about two possible strict violations. But are valid
   uses of `this`, so we suppress the warnings.
9 years ago
Arlo Breault 7285ccfd4e Remove block around initialize code
* In strict mode code, functions can only be declared at top level or
   immediately within another function.  This means functions defined in
   the initializer would throw.
9 years ago
Arlo Breault 7695e5e3c5 Fix complaints from `make hint` 9 years ago
David Majda f2200e48af Optimize location info computation
Before this commit, position details (line and column) weren't computed
efficiently from the current parse position. There was a cache but it
held only one item and it was rarely hit in practice. This resulted in
frequent rescanning of the whole input when the |location| function was
used in various places in a grammar.

This commit extends the cache to remember position details for any
position they were ever computed for. In case of a cache miss, the cache
is searched for a value corresponding to the nearest lower position,
which is then used to compute position info for the desired position
(which is then cached). The whole input never needs to be rescanned.

No items are ever evicted from the cache. I think this is fine as the
max number of entries is the length of the input. If this becomes a
problem I can introduce some eviction logic later.

The performance impact of this change is significant. As the benchmark
suite doesn't contain any grammar with |location| calls I just used a
little ad-hoc benchmark script which measured time to parse the grammar
of PEG.js itself (which contains |location| calls):

  var fs     = require("fs"),
      parser = require("./lib/parser");

  var grammar = fs.readFileSync("./src/parser.pegjs", "utf-8"),
      startTime, endTime;

  startTime = (new Date()).getTime();
  parser.parse(grammar);
  endTime = (new Date()).getTime();

  console.log(endTime - startTime);

The measured time went from ~293 ms to ~54 ms on my machine.

Fixes #337.
9 years ago
David Majda 29bb921994 Rename |peg$cache| to |peg$resultsCache|
This change will make the results cache clearly distinguishable from the
position details cache (which I'll add in a minute).
9 years ago
David Majda eaca5f0acf Add location information to |GrammarError|
This means all errors thrown by |PEG.buildParser| now have associated
location information.
9 years ago
David Majda 89146915ce Add location information to AST nodes
This will allow to add location information to |GrammarError| exceptions
thrown in various passes.
9 years ago
David Majda d1fe86683b Improve location info in tracing events
Replace |line|, |column|, and |offset| properties of tracing events 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 the |rule.match| event, |start| refers to the position at the
beginning of the matched input and |end| refers to the position after
the end of the matched input.

For |rule.enter| and |rule.fail| events, both |start| and |end| refer to
the current position at the time the rule was entered.
9 years ago
David Majda 065f4e1b75 Improve location info in syntax errors
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.
9 years ago
David Majda b1ad2a1f61 Rename |reportedPos| to |savedPos|
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.
9 years ago
David Majda 4f7145e360 Improve location info available in action code
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.
9 years ago
David Majda d7fc0b5c3b Implement infinite loop detection
Fixes #26.
9 years ago
David Majda 95ce20ed92 Extract the |matchesEmpty| visitor from the |reportLeftRecursion| pass
Beside the recursion detector, the visitor will also be used by infinite
loop detector.

Note the newly created |asts.matchesEmpty| function re-creates the
visitor each time it is called, which makes it slower than necessary.
This could have been worked around in various ways but I chose to defer
that optimization because real-world performance impact is small.
9 years ago
David Majda 03a391e874 s/appliedRules/visitedRules/
The rules are not really *applied* by the |reportLeftRecursion| pass,
they are just *visited*.
9 years ago
David Majda 25ed2b7ee2 Improve comment describing the |reportLeftRecursion| pass 9 years ago
David Majda 6ce97457bf Fix left recursion detection
So far, left recursion detector assumed that left recursion occurs only
when the recursive rule is at the very left-hand side of rule's
expression:

  start = start

This didn't catch cases like this:

  start = "a"? start

In general, if a rule reference can be reached without consuming any
input, it can lead to left recursion. This commit fixes the detector to
consider that.

Fixes #190.
9 years ago
David Majda da57118a43 Implement basic support for tracing
Parsers can now be generated with support for tracing using the --trace
CLI option or a boolean |trace| option to |PEG.buildParser|. This makes
them trace their progress, which can be useful for debugging. Parsers
generated with tracing support are called "tracing parsers".

When a tracing parser executes, by default it traces the rules it enters
and exits by writing messages to the console. For example, a parser
built from this grammar:

  start = a / b
  a = "a"
  b = "b"

will write this to the console when parsing input "b":

  1:1 rule.enter start
  1:1 rule.enter   a
  1:1 rule.fail    a
  1:1 rule.enter   b
  1:2 rule.match   b
  1:2 rule.match start

You can customize tracing by passing a custom *tracer* to parser's
|parse| method using the |tracer| option:

  parser.parse(input, { trace: tracer });

This will replace the built-in default tracer (which writes to the
console) by the tracer you supplied.

The tracer must be an object with a |trace| method. This method is
called each time a tracing event happens. It takes one argument which is
an object describing the tracing event.

Currently, three events are supported:

  * rule.enter -- triggered when a rule is entered
  * rule.match -- triggered when a rule matches successfully
  * rule.fail  -- triggered when a rule fails to match

These events are triggered in nested pairs -- for each rule.enter event
there is a matching rule.match or rule.fail event.

The event object passed as an argument to |trace| contains these
properties:

  * type   -- event type
  * rule   -- name of the rule the event is related to
  * offset -- parse position at the time of the event
  * line   -- line at the time of the event
  * column -- column at the time of the event
  * result -- rule's match result (only for rule.match event)

The whole tracing API is somewhat experimental (which is why it isn't
documented properly yet) and I expect it will evolve over time as
experience is gained.

The default tracer is also somewhat bare-bones. I hope that PEG.js user
community will develop more sophisticated tracers over time and I'll be
able to integrate their best ideas into the default tracer.
9 years ago
David Majda 675561f085 Rename and generalize |generateCache{Header,Footer}|
Rename |generateCache{Header,Footer}| to |generateRule{Header,Footer}|
and change their responsibility to generate overall header/footer of a
rule function (when optimizing for speed) or the |peg$parseRule|
function (when optimizing for speed). This creates a natural place where
to generate tracing code (coming soon).
9 years ago
David Majda fb5f6c6ee9 Make labels behave like block-scoped variables
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/180

Fixes #316.
9 years ago
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 0977dd37a3 Reordering in visitors and their specs
Reorder visiting functions and their specs to more closely match
ordering used in the PEG.js grammar.
10 years ago
David Majda 4ec9e6ba10 Remove useless test in the |reportLeftRecursion| pass
Empty sequences are not allowed anymore so we don't have to test for
them.
10 years ago
David Majda 898a7b5a2d Simplify visitors by providing default visit functions
The |visitor.build| function now supplies default visit functions for
visitors it builds. These functions don't do anything beside traversing
the tree and passing arguments around to child visit functions.

Having the default visit functions allowed to simplify several visitors.
10 years ago
David Majda f457c41dd4 Declare the |j| variable before use in lib/utils/arrays.js
Until now it was inadvertently global.
10 years ago
David Majda 24394e3f91 Fix comment alignment in lib/compiler/passes/generate-javascript.js 10 years ago
David Majda 2b06476c69 Regenerate lib/parser.js after bytecode changes 10 years ago
David Majda dad1207c46 Improve semantics of the TEXT bytecode instruction
The TEXT instruction now replaces position at the top of the stack with
the input from that position until the current position. This is simpler
and cleaner semantics than the previous one, where TEXT also popped an
additional value from the stack and kept the position there.
10 years ago
David Majda a815a8b902 Implement additional PUSH_* bytecode instructions
Implement the following bytecode instructions:

  * PUSH_UNDEFINED
  * PUSH_NULL
  * PUSH_FAILED
  * PUSH_EMPTY_ARRAY

These instructions push simple JavaSccript values to the stack directly,
without going through constants. This makes the bytecode slightly
shorter and the bytecode generator somewhat simpler.

Also note that PUSH_EMPTY_ARRAY allows us to avoid a hack which protects
the [] constant from modification.
10 years ago