Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debugging support #143

Closed
rstacruz opened this issue Nov 30, 2016 · 18 comments
Closed

Debugging support #143

rstacruz opened this issue Nov 30, 2016 · 18 comments

Comments

@rstacruz
Copy link

rstacruz commented Nov 30, 2016

It'd be nice to have a way to show what Parsimmon does under the hood. This should help trace bugs in complicated parsers. Maybe using debug or something similar.

For instance:

const parser = P.alt(
  P.regexp(/[0-9]+/i),
  P.regexp(/[a-z]+/i)
).many()

parser.parse('hello10')

I'd like to be able to see something like:

[0:0] "hello10"
  alt(2):
    regexp(/[0-9]+/i): fail
    regexp(/[a-z]+/i): "hello"
[0:5] "10"
  alt(2):
    regexp(/[0-9]+/i): "10"
@wavebeem
Copy link
Collaborator

I totally agree there should be some kind of debugging mode. Given this library is meant to be dependency free, I won't integrate debug (though custom debugging loggers could be possible).

I don't wanna implement just any random thing for debugging either, so I think this issue is probably a good place to discuss what useful debugging would look like in detail, including how to enable that debugging.

@wavebeem wavebeem changed the title Debugging support [discussion] Debugging support Dec 1, 2016
@wavebeem wavebeem changed the title [discussion] Debugging support Debugging support May 17, 2017
@wavebeem
Copy link
Collaborator

After playing around with this multiple times, I think the only way this output can be remotely useful is if the parser is annotated with some kind of "debug name" for every major parser. I wonder if people would actually bother writing out these names everywhere? Just mentioning alt(#) doesn't mean anything to anyone.

This is a very tricky problem to actually do well, and I'd rather not offer something with poor usability that we are then stuck with.

@wavebeem
Copy link
Collaborator

I did just have a thought though that using the upcoming createLanguage API we could automatically add a debugging name equal to the object key! On top of that we could just keep track of parse depth and print the debug name with appropriate indentation, and this might actually be useful.

@wavebeem
Copy link
Collaborator

Something like this with a customizable logging function and the ability to enable/disable debug mode might be useful, but I'll need to play with it more once createLanguage lands. Basically this would automatically get added to anything made with createLanguage.

  var debugEnabled = true;
  var debugLevel = 0;
  var debugIndent = '| ';
  _.debugName = function(name) {
    if (!debugEnabled) {
      return this;
    }
    var self = this;
    return Parsimmon(function(input, i) {
      debugLevel++;
      var indent = repeatString(debugIndent, debugLevel);
      console.log(indent + name);
      var result = self._(input, i);
      debugLevel--;
      return result;
    });
  };

@bd82
Copy link
Contributor

bd82 commented May 24, 2017

@wavebeem

Perhaps you would be interested in this Scala Parser Combinator library debugging capabilities.
http://www.lihaoyi.com/fastparse/#DebuggingParsers

@wavebeem
Copy link
Collaborator

wavebeem commented Jul 4, 2017

It might be useful just to supply a "debug" parser which simply prints out information about the current state, as well as a user supplied tag. Probably easiest as a method like .debug('here i am') which prints that message as well as a chunk of the surrounding text, and line/column information and whatnot.

@mixtur
Copy link

mixtur commented Jul 4, 2017

At some point I made these helpers for my language's parser.

It generates output that looks like this when logAnnotations is set to true.
debugExample

It helped me to find a few bugs that were very hard to find otherwise.

And having this experience, I will probably do it differently next time.
Since I was logging everything at the moment of parsing I could only tell if parser succeeded or failed AFTER it was done; so the tree structure is kinda upside down (children first, root last). So maybe it would be better not to log everything at parsing time, But collect data about parsing process, and then provide a way to log it.

There is also sinCounter / breakOnRepeat / MAX_SINS. It helped me to locate cases that looked like

alt(
  seq(a, b),
  seq(a, c)
)

@minamorl
Copy link
Contributor

+1 for P.debug.
If we need more information for parsers, how about creating an independent library for debugging? look like let P = require("parsimmon-logger")(require("parsimmon")). All function underlying P will be replaced by debugger.
This design allows more capability for injecting each parser.

@halfzebra
Copy link
Contributor

I would love to have some optional tools for debugging Parsimmon! 👍

@wavebeem
Copy link
Collaborator

wavebeem commented Apr 3, 2018

I'm open to ideas on how to best implement this. Needs to support browser and Node debugging with zero mandatory dependencies.

@halfzebra
Copy link
Contributor

halfzebra commented Apr 5, 2018

I don't have ideas on the overall debugging approach yet, but I think there's a problem with the error output.

Here's my proposal for improving the error messages:

screen shot 2018-04-05 at 09 21 28

symbol: "=" is coming from .desc in this case.

This doesn't change much, but it gives more context on what is happening in the input.

I'm using chalk, but it would be super easy to drop it(or replace with something that works for browser and node at the same time)

@wavebeem
Copy link
Collaborator

wavebeem commented Apr 5, 2018

@halfzebra I definitely approve of a nice text formatter like that!

I would just drop chalk since it's gonna cause browser problems and I don't want to add a dependency.

And then we need it to work for Buffer inputs too.

Would you be interested in opening an issue and working on a PR for that?

@halfzebra
Copy link
Contributor

Sure, I will send a PR shortly!

@ccorcos
Copy link

ccorcos commented Apr 27, 2018

This function has been pretty helpful

export const tap = (message: string) => <X>(x: X) => {
	console.log(message, x)
	return p.succeed(x)
}

Then I just .chain(tap("here")) whenever I need to. Would nice to make it a prototype method though.

@wavebeem
Copy link
Collaborator

wavebeem commented Apr 27, 2018 via email

@ccorcos
Copy link

ccorcos commented Apr 27, 2018 via email

@wavebeem
Copy link
Collaborator

wavebeem commented Apr 28, 2018

Printing a stack trace is easy, sure (console.trace()), but getting a useful stack trace is not something Parsimmon supports.

See https://runkit.com/wavebeem/5ae4ab80f8960f001295065b

Trace
    at Symbol.P.of.map.x (/app/index.js:40:25)
    at Parsimmon._ (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:720:51)
    at Parsimmon._ (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:432:40)
    at Parsimmon._ (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:716:23)
    at Parsimmon.<anonymous> (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:958:19)
    at Parsimmon._ (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:539:40)
    at Parsimmon.<anonymous> (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:958:19)
    at Parsimmon._ (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:432:40)
    at Parsimmon._ (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:716:23)
    at Parsimmon._ (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:632:34)
    at Parsimmon.<anonymous> (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:958:19)
    at Parsimmon._ (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:432:40)
    at Parsimmon._ (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:716:23)
    at Parsimmon._.parse (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:570:31)
    at Parsimmon._.tryParse (/app/available_modules/1524935550000/parsimmon/src/parsimmon.js:587:21)
    at /app/index.js:87:28

See how everything is just Parsimmon._? If you have an idea how how to put useful names there in place of those anonymous functions attached as methods to the Parsimmon class, please let me know.

I'm thinking for now a good compromise might be adding two methods:

_.tap = function(f) {
  return this.map(function(x) {
    f(x);
    return x;
  };
};

which could be used like:

seq(
  myCoolParser.tap(x => { console.log("here i am!", x) }),
  anotherParser
)

and maybe a shortcut version like .debug(message) which prints out the message and the current value (or maybe just the message)

@wavebeem
Copy link
Collaborator

wavebeem commented Oct 2, 2018

I'm closing this in favor of #193 since nothing compelling has come up in the mean time

@wavebeem wavebeem closed this as completed Oct 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants