├── LICENSE ├── README.md ├── misc ├── pegdebug-0.40-1.rockspec ├── pegdebug-0.40-2.rockspec └── pegdebug-0.41-1.rockspec └── src └── pegdebug.lua /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Paul Kulchenko 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Description 2 | 3 | PegDebug is a trace debugger for LPeg rules and captures. 4 | 5 | ## Features 6 | 7 | PegDebug allows to trace the processing of LPeg rules and to visualize how 8 | patterns are explored and matched as well as when captures are made. 9 | 10 | PegDebug is based on a [prototype by Patrick Donnelly](http://lua-users.org/lists/lua-l/2009-10/msg00774.html), 11 | which has been improved to provide formatted output, handle folding captures, 12 | and report captures (with some [limitations](#limitations)). 13 | 14 | ## Usage 15 | 16 | ```lua 17 | local grammar = require('pegdebug').trace(lpegGrammar, traceOptions) 18 | lpeg.match(lpeg.P(grammar),"subject string") 19 | ``` 20 | 21 | ## Example 22 | 23 | The following example demonstrates the usage of PegDebug with default options: 24 | 25 | ```lua 26 | local lpeg = require('lpeg') 27 | local grammar = require('pegdebug').trace({ "List"; 28 | NonNumber = lpeg.R('az'); 29 | Number = lpeg.R"09"^1 / tonumber; 30 | List = lpeg.V("NonNumber") + lpeg.V("Number") * ("," * lpeg.V("Number"))^0; 31 | }) 32 | print(lpeg.match(lpeg.P(grammar),"10,30,43")) 33 | ``` 34 | 35 | This will generate the following output: 36 | 37 | ``` 38 | + List 1 "1" 39 | + NonNumber 1 "1" 40 | - NonNumber 1 41 | + Number 1 "1" 42 | = Number 1-2 "10" 43 | + Number 4 "3" 44 | = Number 4-5 "30" 45 | + Number 7 "4" 46 | = Number 7-8 "43" 47 | = List 1-8 "10,30,43" 48 | / Number 1 1 10 49 | / Number 4 1 30 50 | / Number 7 1 43 51 | / List 1 3 10, 30, 43 52 | 10 30 43 53 | ``` 54 | 55 | As you can see, the output shows all the patterns that LPeg explored ('+'), 56 | matched ('='), and failed to match ('-'). The output includes the name 57 | of the pattern, the range of positions and the content (in case of a match). 58 | The output also includes all captures when they are produced. 59 | 60 | ## Options 61 | 62 | * `out` (table) -- provide a table to store results instead of `printing` them; 63 | * `only` (table) -- provide a table to specify which rules to include in the output; 64 | * `serializer` (function) -- provide an alternative mechanism to serialize captured values; 65 | * `'/'`, `'+'`, `'-'`, `'='` (boolean) -- disable handling of specific events; 66 | for example, `['/'] = false` will disable showing captures in the output. 67 | 68 | ## Installation 69 | 70 | Make `src/pegdebug.lua` available to your script. 71 | 72 | ## Limitations 73 | 74 | PegDebug may return incorrect captures when folding captures are used; 75 | for example, the following fragment will return `10`, instead of expected `83`: 76 | 77 | ```lua 78 | local lpeg = require('lpeg') 79 | local grammar = require('pegdebug').trace({ "Sum"; 80 | NonNumber = lpeg.R('az'); 81 | Number = lpeg.R"09"^1 / tonumber; 82 | List = lpeg.V("NonNumber") + lpeg.V("Number") * ("," * lpeg.V("Number"))^0; 83 | Sum = lpeg.Cf(lpeg.V("List"), 84 | function(acc, newvalue) return newvalue and acc + newvalue end); 85 | }) 86 | print(lpeg.match(lpeg.P(grammar),"10,30,43")) 87 | ``` 88 | 89 | This is because the function capture used to report captures returns only one 90 | capture, which breaks the folding capture (which expects a list of captures). 91 | 92 | You may disable capture reporting by using `{['/'] = false}` or disable 93 | capture reporting specifically for the rules that generate captures used 94 | in folding: using `{['/'] = {List = false}}` will 'fix' this example. 95 | 96 | ## Author 97 | 98 | Paul Kulchenko (paul@kulchenko.com) 99 | 100 | ## License 101 | 102 | See LICENSE file 103 | -------------------------------------------------------------------------------- /misc/pegdebug-0.40-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "PegDebug" 2 | version = "0.40-1" 3 | 4 | source = { 5 | url = "git://github.com/pkulchenko/PegDebug.git", 6 | tag = "0.40" 7 | } 8 | 9 | description = { 10 | summary = "PegDebug is a trace debugger for LPeg rules and captures.", 11 | detailed = "PegDebug is a trace debugger for LPeg rules and captures.", 12 | homepage = "http://github.com/pkulchenko/PegDebug", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1", 18 | } 19 | 20 | build = { 21 | type = "builtin", 22 | install = { 23 | lua = { pegdebug = "src/pegdebug.lua" } 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /misc/pegdebug-0.40-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "PegDebug" 2 | version = "0.40-2" 3 | 4 | source = { 5 | url = "git://github.com/pkulchenko/PegDebug.git", 6 | tag = "0.40", 7 | } 8 | 9 | description = { 10 | summary = "PegDebug is a trace debugger for LPeg rules and captures.", 11 | detailed = "PegDebug is a trace debugger for LPeg rules and captures.", 12 | homepage = "http://github.com/pkulchenko/PegDebug", 13 | maintainer = "Paul Kulchenko ", 14 | license = "MIT/X11", 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1", 19 | "lpeg", 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["pegdebug"] = "src/pegdebug.lua", 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /misc/pegdebug-0.41-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "PegDebug" 2 | version = "0.41-1" 3 | 4 | source = { 5 | url = "git://github.com/pkulchenko/PegDebug.git", 6 | tag = "0.41", 7 | } 8 | 9 | description = { 10 | summary = "PegDebug is a trace debugger for LPeg rules and captures.", 11 | detailed = "PegDebug is a trace debugger for LPeg rules and captures.", 12 | homepage = "http://github.com/pkulchenko/PegDebug", 13 | maintainer = "Paul Kulchenko ", 14 | license = "MIT/X11", 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1", 19 | "lpeg", 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["pegdebug"] = "src/pegdebug.lua", 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /src/pegdebug.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- PegDebug -- A debugger for LPeg expressions and processing 3 | -- Copyright 2014 Paul Kulchenko 4 | -- 5 | 6 | local lpeg = require "lpeg" 7 | 8 | local Cmt = lpeg.Cmt 9 | local Cp = lpeg.Cp 10 | local P = lpeg.P 11 | 12 | local pegdebug = { 13 | _NAME = "pegdebug", 14 | _VERSION = 0.41, 15 | _COPYRIGHT = "Paul Kulchenko", 16 | _DESCRIPTION = "Debugger for LPeg expressions and processing", 17 | } 18 | 19 | function pegdebug.trace(grammar, opts) 20 | opts = opts or {} 21 | local serpent = opts.serializer 22 | or pcall(require, "serpent") and require("serpent").line 23 | or pcall(require, "mobdebug") and require("mobdebug").line 24 | or nil 25 | local function line(s) return (string.format("%q", s):gsub("\\\n", "\\n")) end 26 | local function pretty(...) 27 | if serpent then return serpent({...}, {comment = false}):sub(2,-2) end 28 | local res = {} 29 | for i = 1, select('#', ...) do 30 | local v = select(i, ...) 31 | local tv = type(v) 32 | res[i] = tv == 'number' and v or tv == 'string' and line(v) or tostring(v) 33 | end 34 | return table.concat(res, ", ") 35 | end 36 | local level = 0 37 | local start = {} 38 | local print = print 39 | if type(opts.out) == 'table' then 40 | print = function(...) table.insert(opts.out, table.concat({...}, "\t")) end 41 | end 42 | for k, p in pairs(grammar) do 43 | local enter = Cmt(P(true), function(s, p, ...) 44 | start[level] = p 45 | if opts['+'] ~= false then 46 | print((" "):rep(level).."+", k, p, line(s:sub(p,p))) 47 | end 48 | level = level + 1 49 | return true 50 | end) 51 | local leave = Cmt(P(true), function(s, p, ...) 52 | level = level - 1 53 | if opts['-'] ~= false then 54 | print((" "):rep(level).."-", k, p) 55 | end 56 | return true 57 | end) * (P(1) - P(1)) 58 | local eq = Cmt(P(true), function(s, p, ...) 59 | level = level - 1 60 | if opts['='] ~= false then 61 | print((" "):rep(level).."=", k, start[level]..'-'..(p-1), line(s:sub(start[level],p-1))) 62 | end 63 | return true 64 | end) 65 | if k ~= 1 and (not opts.only or opts.only[k]) then 66 | if opts['/'] ~= false 67 | and (type(opts['/']) ~= 'table' or opts['/'][k] ~= false) then 68 | -- Cp() is needed to only get captures (and not the whole match) 69 | p = Cp() * p / function(pos, ...) 70 | print((" "):rep(level).."/", k, pos, select('#', ...), pretty(...)) 71 | return ... 72 | end 73 | end 74 | grammar[k] = enter * p * eq + leave 75 | end 76 | end 77 | return grammar 78 | end 79 | 80 | return pegdebug 81 | --------------------------------------------------------------------------------