zevv / npeg

PEGs for Nim, another take
MIT License
330 stars 22 forks source link

Print return stack on overflow #38

Closed Varriount closed 1 year ago

Varriount commented 3 years ago

When NPeg throws a depth error due to an overflow in the return stack, it would be nice to have the exception contain a printout of the stack.

Something like

Error: unhandled exception: return stack overflow, depth>1024
Return stack:
sample.nim(10)             rule_name_one
keywords.nim(100)       rule_name_two
...

Rather than just

Error: unhandled exception: return stack overflow, depth>1024
zevv commented 1 year ago

I poked around to see if I can make a proper implementation of this, but it's not trivial: the symbol table does not from survive compilation to run time, so there is no info available to generate a friendly stack trace at the time of the overflow.

I can make this work, but that will require keeping information around for longer then I'd like. Not sure of that is worth the effort at this time.

Any ideas on this?

zevv commented 1 year ago

Hi @Varriount, I got a reasonable implementation for collecting and dumping a backtraces happening at NPeg parse time; its a available in the backtrace branch. It doesn't only handle retstack overflows, but any exception that is raised from NPeg, so both NPegException and any other exceptions as well.

The current implementation runs the parse fucntion in a try block; if an exception happens it will print the NPeg part of the stack and re-raise the original exception.

Ideally I would like to augment the original Nim stacktrace with the NPeg stackframes instead of just printing the trace, but I'm not sure if the current Nim infrastructure allows for this; NPeg would have to prepend its own backtrace to the Exceptions trace: seq[StackTraceEntry] fields, but these are not exported and I can't seem to find any other way to modify these. Maybe I should whip up a Nim PR for this.

Example:

import npeg                                                                                             
import std/strutils                                                           
import npeg                                                                

const parser = peg("nodes", data: seq[string]):                                   
  nodes <- *(node * (' ' | !1))                  
  node <- extraWord | word                                                  
  extraWord <- word * extra * dash                                                  
  ident <- +Alpha                                     
  word <- ident:                                                        
    data.add($0)                                                                
  dash <- '-':                                                                   
    data.add($0)                                                                      
  extra <- number | dot              
  number <- +Digit:                                                                   
    raise newException(IOError, "IO failed")                                        
    data.add($0 & "(int)")                                               
  dot <- '.':                                                               
    data.add($0 & "(dot)")                                                         

var data: seq[string]                                                           
assert parser.match("a b1- c.", data).ok                                          
echo data 

Output:

/home/ico/sandbox/prjs/npeg/main.nim(30) main
/home/ico/sandbox/prjs/npeg/main.nim(27) flap
/home/ico/sandbox/prjs/npeg/src/npeg.nim(135) match
/home/ico/sandbox/prjs/npeg/src/npeg/patt.nim(90) fn`gensym46
/home/ico/sandbox/prjs/npeg/src/npeg/patt.nim(90) fn
/home/ico/sandbox/prjs/npeg/main.nim(11) nodes <- *(node * (' ' | !1))
/home/ico/sandbox/prjs/npeg/main.nim(12) node <- extraWord | word
/home/ico/sandbox/prjs/npeg/main.nim(13) extraWord <- word * extra * dash
/home/ico/sandbox/prjs/npeg/main.nim(19) extra <- number | dot
/home/ico/sandbox/prjs/npeg/main.nim(20) number <- +Digit

The last 5 lines show the NPeg part of the stacktrace including the regular lineInfo, with the NPeg rule appended for more clarity.

zevv commented 1 year ago

let's see of https://github.com/nim-lang/Nim/pull/20772 gets through, that will allow NPeg stack frames to go on the native Nim exception stack trace.

zevv commented 1 year ago

For now, enable printing the stack trace with -d:npegStacktrace