Home
Reading
Searching
Subscribe
Sponsors
Statistics
Posting
Contact
Spam
Lists
Links
About
Hosting
Filtering
Features Download
Marketing
Archives
FAQ
Blog
 
Gmane
From: Philippe Lhoste <PhiLho <at> GMX.net>
Subject: Re: Trace debugger for lpeg
Newsgroups: gmane.comp.lang.lua.general
Date: Tuesday 29th May 2012 10:22:48 UTC (over 5 years ago)
On 21/10/2009 13:00, Patrick Donnelly wrote:
> Hi List,
>
> I thought I would share a debugger I created for tracing the path lpeg
> takes in matching a pattern using a large/complex grammar. I've gone
> through a lot of headache debugging a parser I made for Lua so I
> thought I would share this bit of code which made it much easier.
>
> The code changes the grammar like so:
>
> (1) Changes each rule in the grammar to have a prefix pattern that is
> always true and run time captures to print "ENTER"
> (2) Changes each rule in the grammar to have a suffix pattern that is
> composed of a pattern always true that run time captures to print
> "LEAVE". The end of the suffix pattern is always false so it
> does not change the actual pattern when it fails.
> (3) If the rule in the grammar matches, (2) does not happen and
> instead the current position and subject up to the position is
> printed.
>
> For my Lua parser, it looks something like this:
>
> ENTER   1
> ENTER   space
> ENTER   comment
> LEAVE   comment
> ---     space   ---
> 2
>
> ENTER   chunk
> ENTER   space
> ENTER   comment
> LEAVE   comment
> ---     space   ---
> 2
>
> ENTER   stat
> ENTER   varlist
> ENTER   var
> ENTER   prefix
> ENTER   Name
>
> The actual code to make this happen is quite simple:
>
> for k, p in pairs(grammar) do
>    local enter = lpeg.Cmt(lpeg.P(true), function(s, p, ...)
> print("ENTER", k) return p end);
>    local leave = lpeg.Cmt(lpeg.P(true), function(s, p, ...)
> print("LEAVE", k) return p end) * (lpeg.P("k") - lpeg.P "k");
>    grammar[k] = lpeg.Cmt(enter * p + leave, function(s, p, ...)
> print("---", k, "---") print(p, s:sub(1, p-1)) return p end)
> end
>
> (Notice that there is a trick in leave, we can't use lpeg.P(false)
> because LPeg optimizes it out.)
>
> Hope someone out there finds it useful!

I do!
The idea is smart and once I understood how it worked, I found the
implementation brilliant.

For some reasons, I had trouble making it to work with the latest version
of LPeg 
(0.10.2), but perhaps it was just my failure. Anyway, I slightly adapted
the code to use 
the latest features of LPeg, and to fit my taste in formatting the result.

Here is my version:

   for k, r in pairs(grammar) do
     if type(k) == "number" then
       if type(r) ~= "pattern" and type(r) ~= "userdata" then
         print("Initial rule: " .. r)
       end
     else
       local enter = lpeg.P(function (s, p, ...) print("ENTER", k) return
true end)
       local leave = lpeg.P(function (s, p, ...) print("LEAVE", k) return
false end)
       grammar[k] = lpeg.Cmt(enter * r + leave,
         function (s, p, ...)
           print("MATCH", k, p, "'" .. s:sub(1, p-1) .. "'")
           return true
         end)
     end
   end

It choked on the initial rule, so I made a (clumsy) detection to skip it.

Thanks for sharing.

-- 
Philippe Lhoste
--  (near) Paris -- France
--  http://Phi.Lho.free.fr
--  --  --  --  --  --  --  --  --  --  --  --  --  --
 
CD: 2ms