Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- An explanation for the LPeg tricks used in gabcalc.
-- I sent this code to lua-l in 2012may14. See:
--   http://lua-users.org/lists/lua-l/2012-05/msg00328.html
--   http://article.gmane.org/gmane.comp.lang.lua.general/91233
--
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
loadlpeg()
--]==]

require "lpeg"

-- A nicer syntax for local variables in LPeg patterns.
set = function (name, o)
    if getmetatable(o) == getmetatable(lpeg.P"")  -- if o is a pattern
    then return o:Cg(name)               -- then name := eval(o)
    else return lpeg.Cc(o):Cg(name)      -- else name := o   (constant)
    end
  end
get    = function (name) return lpeg.Cb(name) end
group  = function (cmds) return lpeg.Cg(cmds) end

-- A trivial example:
patt = set("x", "2") *
       get("x") *
       group(set("x", "3") *
             get("x")
            ) *
       get("x")
print(patt:match"")     --> 2  3  2


-- A bigger example:
-- binary operators, with precedence.
--
-- Suppose that E is an expression in tree form - for example, this:
--
--             +
--           /   \
--    E =   *     <  
--         / \   / \ 
--        2   3 4   5
--
-- Then to find out how to print E in infix notation we need to
-- determine, for each of its "wires", whether we need or not to add
-- parentheses around the subexpression under it. The best way to do
-- that is to associate to each operator three numbers, that we will
-- call "top", "left" and "right" (op.t, op.l, or.r). We get his:
--                     ___                   
--                    /19 \          
--                   /  +  \         
--                  |_18_20_|        
--                /           \()      
--             ___             ___   
--            /21 \           /17 \  
--           /  *  \         /  <  \ 
--          |_20_22_|       |_18_18_|
--        /           \   /           \
--      2             3   4            5
--
-- The wires that need parentheses are the ones in which the number
-- above is higher than the number below, and we get:
--
--             2 * 3 + (4 < 5)
--
-- This idea can be easily adapted to parsing. After reading the "*"
-- we need to parse its second argument - the longest "expression
-- under the `*'" possible. Let's look at a bigger example:
--
--                                   &______________.    
--                                   |              |    
--                                   <=_.           >__. 
--                                   |  |           |  | 
--                                   2  +__.        7  8 
--    /------------------------\        |  |             
--    /----------------\                3  *_____.       
--         /-----------\                   |     |       
--             /-------\                   *__.  6       
--             /---\       /---\           |  |          
--    2 <= 3 + 4 * 5 * 6 & 7 > 8           4  5          
--
-- The second argument for the `+' in that expression is the longest
-- "expression under the `+'" possible, starting at the "4". In the
-- grammar below, this will be an "expression without complements" -
-- just the "4" - followed by two "complements", "* 5" and "* 6",
-- which we will be added to the "4" using lpeg.Cf:
--
--   http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html#cap-f
--
-- Note: I took the idea of op.t, op.l and op.r from Andrej Bauer's
-- PLZoo (but he uses that only for printing, not for parsing)...
-- See:
--
--   http://andrej.com/plzoo/
--   http://andrej.com/plzoo/html/calc.html
--   http://www.gnu.org/software/bison/manual/html_node/Infix-Calc.html

ops = {}
newbinop = function (l, t, r, str)
    for op in str:gmatch("[^ ]+") do ops[op] = {l=l, t=t, r=r} end
  end
newbinop(10, 11, 12, "->")
newbinop(12, 13, 14, "or")
newbinop(14, 15, 16, "&")
newbinop(18, 17, 18, "== <= >= < > !=    <-")
newbinop(18, 19, 20, "+ -")
newbinop(20, 21, 22, "* /")

pack_bin  = function (e1, op, e2) return "("..op.." "..e1.." "..e2..")" end
apply_all = function (e, T) return T[#T](e, unpack(T, 1, #T-1)) end

above     = function (lop, rop) return lop == "" or ops[lop].r < ops[rop].t end
is_binop  = function (subj, pos, c) if ops[c] then return true, c end end
is_under  = function (subj, pos, lop, op)
    if above(lop, op) then return true, op end
  end

P  = lpeg.P
V  = lpeg.V
Cc = lpeg.Cc
sp = lpeg.S" \t\n"^0
sP = function (str) return sp * P(str) end

Expr_grammar = {
  "Expr",     -- initial rule name
  Num         = sp * (lpeg.R"09"^1):C(),
  Parenexpr   = sP"(" * V"Expr" * sP")",
  --
  Binop_any   = sp * ((lpeg.P(2):Cmt(is_binop) +
                       lpeg.P(1):Cmt(is_binop))),
  Binop_under = (get"lop" * V"Binop_any"):Cmt(is_under),
  Binop       = set("lop", V"Binop_under") * get("lop"),
  --
  Complement  = group(V"Binop" * V"Expr_under" * Cc(pack_bin)):Ct(),
  --
  Expr_woc    = V"Num" + V"Parenexpr",
  Expr_under  = (V"Expr_woc" * V"Complement"^0):Cf(apply_all),
  Expr        = group(set("lop", "") * V"Expr_under"),
}
Expr_pattern  = lpeg.P(Expr_grammar)

print(Expr_pattern:match"2 <= 3 + 4 * 5 * 6 & 7 > 8")
  --> (& (<= 2 (+ 3 (* (* 4 5) 6))) (> 7 8))



-- Local Variables:
-- coding:             raw-text-unix
-- ee-anchor-format:   "«%s»"
-- End: