Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- gab.lua - Gabriela Avila's calculator for quantified
--   expressions, Lua version (full)
-- Author:  Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2012may13
-- Licence: GPL3
--
-- The latest upstream version of this can be found at:
--   http://angg.twu.net/dednat5/gab.lua
--   http://angg.twu.net/dednat5/gab.lua.html
--                    (find-dn5 "gab.lua")
--
-- Older version:
--   http://angg.twu.net/dednat5/gabriela.lua
--   http://angg.twu.net/dednat5/gabriela.lua.html
--                    (find-dn5 "gabriela.lua")
--   http://angg.twu.net/dednat5/gabriela-app.lua
--   http://angg.twu.net/dednat5/gabriela-app.lua.html
--                    (find-dn5 "gabriela-app.lua")


-- Quick index:
-- «.eoo.lua»		(to "eoo.lua")
-- «.Expr»		(to "Expr")
-- «.expr:infix»	(to "expr:infix")
-- «.constructors»	(to "constructors")
-- «.expr:tolisp»	(to "expr:tolisp")
-- «.expr:eval»		(to "expr:eval")
-- «.eval-quantifiers»	(to "eval-quantifiers")
-- «.Rect»		(to "Rect")
-- «.expr:print»	(to "expr:print")
-- «.expr:Dbg»		(to "expr:Dbg")
-- «.Context»		(to "Context")


-- Quick index:
-- «.contexts»			(to "contexts")
-- «.contexts-test»		(to "contexts-test")
-- «.def-lambda-app»		(to "def-lambda-app")
-- «.comprehension»		(to "comprehension")
-- «.comprehension-test»	(to "comprehension-test")
-- «.def-lambda-app-tests»	(to "def-lambda-app-tests")
-- «.grids»			(to "grids")
-- «.grids-tests»		(to "grids-tests")
-- «.old-parser»		(to "old-parser")
--   «.precedence-table»	(to "precedence-table")
--   «.precedence»		(to "precedence")
--   «.recursive-descent»	(to "recursive-descent")
--   «.parser-grammar»		(to "parser-grammar")
-- «.lpeg-parser»		(to "lpeg-parser")
-- «.pe»			(to "pe")
-- «.modal»			(to "modal")



--                   
--   ___  ___   ___  
--  / _ \/ _ \ / _ \ 
-- |  __/ (_) | (_) |
--  \___|\___/ \___/ 
--                   
-- «eoo.lua»  (to ".eoo.lua")
-- dofile "eoo.lua"  -- (find-dn5 "eoo.lua")
-- Just the relevant definitions from eoo.lua, to make this self-contained.
-- The documentation is at:
--   (find-dn5 "eoo.lua" "test-eoo")
--   (find-dn5 "eoo.lua" "box-diagram")
Class = {
    type   = "Class",
    __call = function (class, o) return setmetatable(o, class) end,
  }
setmetatable(Class, Class)
otype = function (o)  -- works like type, except on my "objects"
    local  mt = getmetatable(o)
    return mt and mt.type or type(o)
  end
-- end of eoo.lua


--                                                  _   
--  _ __ ___   __ _ _ __   ___ ___  _ __   ___ __ _| |_ 
-- | '_ ` _ \ / _` | '_ \ / __/ _ \| '_ \ / __/ _` | __|
-- | | | | | | (_| | |_) | (_| (_) | | | | (_| (_| | |_ 
-- |_| |_| |_|\__,_| .__/ \___\___/|_| |_|\___\__,_|\__|
--                 |_|                                  
--
-- And here are "mapconcat" and friends, from my init file:
-- (find-angg "LUA/lua50init.lua" "map" "mapconcat")
-- (find-lua51manualw3m "#pdf-table.concat")
map = function (f, A, i, j)
    local B = {}
    for k=(i or 1),(j or #A) do table.insert(B, (f(A[k]))) end
    return B
  end
mapconcat = function (f, A, sep, i, j)
    return table.concat(map(f, A, i, j), sep)
  end
foldl = function (f, a, B, i, j)
    for k=(i or 1),(j or #B) do a = f(a, B[k]) end
    return a
  end
id  = function (...) return ... end
sorted = function (T, lt) table.sort(T, lt); return T end
keys = function (T)
    local ks = {}
    for k,_ in pairs(T) do table.insert(ks, k) end
    return ks
  end
min = function (a, b) return a < b and a or b end
max = function (a, b) return a < b and b or a end
unpack = unpack or table.unpack
-- end of mapconcat



--  _____                 
-- | ____|_  ___ __  _ __ 
-- |  _| \ \/ / '_ \| '__|
-- | |___ >  <| |_) | |   
-- |_____/_/\_\ .__/|_|   
--            |_|         
--
-- «Expr»  (to ".Expr")
-- «expr:infix»  (to ".expr:infix")
Expr = Class {
  type    = "Expr",
  __index = {
    print  = function (e) print(e); return e end,  -- uses __tostring
  },
  __tostring = function (e) return tostring(e:infix()) end,
  __add = function (e1, e2) return Expr {[0]="+", e1, e2} end,
  __sub = function (e1, e2) return Expr {[0]="-", e1, e2} end,
  __mul = function (e1, e2) return Expr {[0]="*", e1, e2} end,
  __div = function (e1, e2) return Expr {[0]="/", e1, e2} end,
  __unm = function (e1)     return Expr {[0]="u-", e1} end,
}

-- Constructors
-- «constructors»  (to ".constructors")
Num  = function (n)  return Expr {[0]="n", n} end
Var  = function (s)  return Expr {[0]="v", s} end
Bool = function (b)  return Expr {[0]="b", b} end

Unm = function (e1) return - e1 end
Mul = function (e1, e2) return e1 * e2 end
Div = function (e1, e2) return e1 / e2 end
Add = function (e1, e2) return e1 + e2 end
Sub = function (e1, e2) return e1 - e2 end

Eq  = function (e1, e2) return Expr {[0]="==", e1, e2} end
Lt  = function (e1, e2) return Expr {[0]="<",  e1, e2} end
Le  = function (e1, e2) return Expr {[0]="<=", e1, e2} end
Ge  = function (e1, e2) return Expr {[0]=">=", e1, e2} end
Gt  = function (e1, e2) return Expr {[0]="<",  e1, e2} end
Neq = function (e1, e2) return Expr {[0]="!=", e1, e2} end

Not = function (e1)     return Expr {[0]="nt", e1} end
And = function (e1, e2) return Expr {[0]="&",  e1, e2} end
Or  = function (e1, e2) return Expr {[0]="or", e1, e2} end
Imp = function (e1, e2) return Expr {[0]="->", e1, e2} end

Tuple = function (...)  return Expr {[0]="()", ...} end
Set   = function (...)  return Expr {[0]="{}", ...} end

-- Fa  = function (e1, e2, e3) return Expr {[0]="Fa", e1, e2, e3} end
-- Ex  = function (e1, e2, e3) return Expr {[0]="Ex", e1, e2, e3} end

Fa  = function (e1, e2) return Expr {[0]="Fa", e1, e2} end
Ex  = function (e1, e2) return Expr {[0]="Ex", e1, e2} end

True  = Bool(true)
False = Bool(false)

--                         ___                         _       
-- __   ____ _ _ __ ___   ( _ )     ___ ___  _ __  ___| |_ ___ 
-- \ \ / / _` | '__/ __|  / _ \/\  / __/ _ \| '_ \/ __| __/ __|
--  \ V / (_| | |  \__ \ | (_>  < | (_| (_) | | | \__ \ |_\__ \
--   \_/ \__,_|_|  |___/  \___/\/  \___\___/|_| |_|___/\__|___/
--                                                             
-- Convenience: some expressions (variables and numeric constants)
-- that I use in tests
_0 = Num(0)
_1 = Num(1)
_2 = Num(2)
_3 = Num(3)
_4 = Num(4)
_5 = Num(5)
a = Var "a"
b = Var "b"
c = Var "c"
d = Var "d"
k = Var "k"
x = Var "x"
y = Var "y"





--  _        _ _           
-- | |_ ___ | (_)___ _ __  
-- | __/ _ \| | / __| '_ \ 
-- | || (_) | | \__ \ |_) |
--  \__\___/|_|_|___/ .__/ 
--                  |_|    
--
-- «expr:tolisp»  (to ".expr:tolisp")
-- A Lisp-ish representation.
-- Note that our Exprs use 0-based arrays, not cons cells.
--   (find-elnode "Cons Cell Type")
--   (find-elnode "Box Diagrams")
tolisp = function (e)
    if     type(e) == "number" then return e
    elseif type(e) == "string" then return "\""..e.."\""
    elseif otype(e) == "Expr"  then
      return "("..tolisp(e[0]).." "..mapconcat(tolisp, e, " ")..")"
    else   return "<"..tostring(e)..">"
    end
  end
Expr.__index.tolisp = function (e) return tolisp(e) end
Expr.__index.lprint = function (e) print(e:tolisp()); return e end



--  _        __ _      
-- (_)_ __  / _(_)_  __
-- | | '_ \| |_| \ \/ /
-- | | | | |  _| |>  < 
-- |_|_| |_|_| |_/_/\_\
--                     

Expr.__index.infix = function (e, b)
    local op, e1, e2, e3 = e[0], e[1], e[2], e[3]
    local t, str
    if     op == "v"  then t, str = 200, e[1]
    elseif op == "n"  then t, str = 200, tostring(e[1])
    elseif op == "b"  then t, str = 200, e[1] and "T" or "F"
    elseif op == "u-" then t, str = 9, "-"..e1:infix(100)
    elseif op == "*"  then t, str = 8, e1:infix(7).."*"..e2:infix(8)
    elseif op == "/"  then t, str = 8, e1:infix(7).."*"..e2:infix(8)
    elseif op == "+"  then t, str = 7, e1:infix(6).."+"..e2:infix(7)
    elseif op == "-"  then t, str = 7, e1:infix(6).."-"..e2:infix(7)
    elseif op == "==" then t, str = 6, e1:infix(6).." == "..e2:infix(6)
    elseif op == "<=" then t, str = 6, e1:infix(6).." <= "..e2:infix(6)
    elseif op == ">=" then t, str = 6, e1:infix(6).." >= "..e2:infix(6)
    elseif op == "<"  then t, str = 6, e1:infix(6).." < " ..e2:infix(6)
    elseif op == ">"  then t, str = 6, e1:infix(6).." > " ..e2:infix(6)
    elseif op == "!=" then t, str = 6, e1:infix(6).." != "..e2:infix(6)
    elseif op == "nt" then t, str = 5, "not "..e1:infix(4)
    elseif op == "&"  then t, str = 4, e1:infix(3).." & " ..e2:infix(4)
    elseif op == "or" then t, str = 3, e1:infix(2).." or "..e2:infix(3)
    elseif op == "->" then t, str = 2, e1:infix(2).." -> "..e2:infix(2)
    elseif op == "()" then t, str = 200,   "("..e:infixs()..")"
    elseif op == "{}" then t, str = 200,   "{"..e:infixs().."}"
    elseif op == "Fa" then t, str = 1, "Fa "..e1:infix()..". "..e2:infix()
    elseif op == "Ex" then t, str = 1, "Ex "..e1:infix()..". "..e2:infix()
    -- elseif op == "Fa" then t, str = 1, "Fa "..e1:infix().." in "..
    --                                           e2:infix()..". "..e3:infix()
    -- elseif op == "Ex" then t, str = 1, "Ex "..e1:infix().." in "..
    --                                           e2:infix()..". "..e3:infix()
    elseif op == "Dg" then str, t = e1:infix()
    --#ifdef HAS_LAMBDA
    elseif op == "De" then t, str = 200, e1
    elseif op == "Ap" then t, str = 200, e1:infix(3).."("..e2:infix()..")"
    elseif op == "\\" then t, str = 3,   "\\"..e1:infix().."."..e2:infix()
    --#endif HAS_LAMBDA
    --#ifdef HAS_COMPREHENSION
    -- subset, set of images, generator, filter, collect
    elseif op == "Su" then t, str = 200, "{"..e1:infix().." | "..
                                              e:infixs(", ", 2, #e-1).."}"
    elseif op == "So" then t, str = 200, "{"..e[#e]:infix().." | "..
                                              e:infixs(", ", 1, #e-1).."}"
    elseif op == "<-" then t, str = 200, e1:infix().." <- "..e2:infix()
    --#endif HAS_COMPREHENSION
    --#endif HAS_MODAL
    elseif op == "Mo" then t, str = 200, e1
    --#ifdef HAS_MODAL
    --                else str, t = e:infix_other(b)  -- all extensions
                      else error("Bad expr:"..tolisp(e))
    end
    if b and t <= b then str = "("..str..")" end
    return str, t
  end
Expr.__index.infixs = function (e, sep, i, j)
    return mapconcat(e.infix, e, (sep or ", "), i, j)
  end






--                  _ 
--   _____   ____ _| |
--  / _ \ \ / / _` | |
-- |  __/\ V / (_| | |
--  \___| \_/ \__,_|_|
--                    

-- «expr:eval»  (to ".expr:eval")
-- Evaluation.
-- To avoid making the code above too big we inject new methods into
-- Expr, using a low-level syntax for that:
--     Expr.__index.newmethod = function (e, a, b, c) ... end

etype  = function (e) return otype(e) == "Expr" and e[0] end
Expr.__index.neval = function (e)
    local ee = e:eval()
    if etype(ee) == "n" then return ee[1] end
    error("Not a number: "..tostring(ee))
  end
Expr.__index.beval = function (e)
    local ee = e:eval()
    if etype(ee) == "b" then return ee[1] end
    error("Not a boolean: "..tostring(ee))
  end
Expr.__index.seval = function (e)
    local ee = e:eval()
    if etype(ee) == "{}" then return ee end
    error("Not a set: "..tostring(ee))
  end
Expr.__index.eval_components = function (e)
    local A = map(e.eval, e)
    A[0] = e[0]
    return Expr(A)
  end

_and = function (P, Q) return P and Q end
_or  = function (P, Q) return P or  Q end
_imp = function (P, Q) return (not P) or Q end
context = {}
Expr.__index.eval = function (e)
    local op, e1, e2, e3 = e[0], e[1], e[2], e[3]
    if     op == "n" then return e
    elseif op == "b" then return e
    elseif op == "v" then return context[e1] or error("Undef: "..e1)
    elseif op == "u-" then return Num(- e1:neval())
    elseif op == "*"  then return Num(e1:neval() * e2:neval())
    elseif op == "/"  then return Num(e1:neval() / e2:neval())
    elseif op == "+"  then return Num(e1:neval() + e2:neval())
    elseif op == "-"  then return Num(e1:neval() - e2:neval())
    elseif op == "==" then return Bool(e1:neval() == e2:neval())
    elseif op == "<=" then return Bool(e1:neval() <= e2:neval())
    elseif op == ">=" then return Bool(e1:neval() >= e2:neval())
    elseif op == "<"  then return Bool(e1:neval() <  e2:neval())
    elseif op == ">"  then return Bool(e1:neval() >  e2:neval())
    elseif op == "!=" then return Bool(e1:neval() ~= e2:neval())
    elseif op == "nt" then return Bool(not e1:beval())
    elseif op == "&"  then return Bool(_and(e1:beval(), e2:beval()))
    elseif op == "or" then return Bool(_or (e1:beval(), e2:beval()))
    elseif op == "->" then return Bool(_imp(e1:beval(), e2:beval()))
    elseif op == "{}" then return e:eval_components()
    elseif op == "()" then return e:eval_components()
    elseif op == "Fa" then return Bool(e1[1]:_fold(true,_and,_beval, e1[2], e2))
    elseif op == "Ex" then return Bool(e1[1]:_fold(false,_or,_beval, e1[2], e2))
    -- elseif op == "Fa" then return Bool(e1:_fold(true, _and, _beval, e2, e3))
    -- elseif op == "Ex" then return Bool(e1:_fold(false, _or, _beval, e2, e3))
    elseif op == "Dg" then return e1:DbgEval()    -- defined elsewhere
                      else return e:eval_other()  -- all extensions
    end
  end


--                          _   _  __ _               
--   __ _ _   _  __ _ _ __ | |_(_)/ _(_) ___ _ __ ___ 
--  / _` | | | |/ _` | '_ \| __| | |_| |/ _ \ '__/ __|
-- | (_| | |_| | (_| | | | | |_| |  _| |  __/ |  \__ \
--  \__, |\__,_|\__,_|_| |_|\__|_|_| |_|\___|_|  |___/
--     |_|                                            

-- «eval-quantifiers»  (to ".eval-quantifiers")
-- The evaluation code for "Fa" and "Ex" calls "_fold" and "_beval",
-- that are defined below.
Expr.__index.varname = function (e)
    if etype(e) == "v" then return e[1] end
    error("Not a variable: "..tostring(ee))
  end
Expr.__index.as = function (var, value, expr)
    local vname    = var:varname()
    local oldvalue = context[vname]
    context[vname] = value
    local result   = expr:eval()
    context[vname] = oldvalue
    return result
  end
Expr.__index._fold = function (var, r, op, normalize, set, expr)
    for _,value in ipairs(set) do
        r = op(r, normalize(var:as(value, expr)))
      end
    return r
  end

_beval = function (e) return e:beval() end

Expr.__index.peval  = function (e)
    local result = e:eval()
    print(tostring(e).."  -->  "..tostring(result))
    return result
  end


-- __        ___                
-- \ \      / / |__   ___ _ __  
--  \ \ /\ / /| '_ \ / _ \ '_ \ 
--   \ V  V / | | | |  __/ | | |
--    \_/\_/  |_| |_|\___|_| |_|
--                              
-- Obsolete?
Expr.__index.When = function (var, value, expr)
    return "When "  ..tostring(var)..
           "="      ..tostring(value)..
           ":  "    ..tostring(expr)..
           "  -->  "..tostring(var:as(value, expr))
  end
Expr.__index.Whens = function (var, set, expr)
    for _,value in ipairs(set) do
      print(var:When(value, expr))
    end
  end



--  ____           _   
-- |  _ \ ___  ___| |_ 
-- | |_) / _ \/ __| __|
-- |  _ <  __/ (__| |_ 
-- |_| \_\___|\___|\__|
--                     
-- «Rect»  (to ".Rect")
-- Rectangles (for trees)
-- (find-luamanualw3m "#pdf-string.rep")
strrep = function (str, n) return string.rep(str, max(0, n)) end
strpad = function (str, n, char) return str..strrep(char or " ", n-#str) end
copy = function (A)
    local B = {}
    for k,v in pairs(A) do B[k] = v end
    setmetatable(B, getmetatable(A))
    return B
  end

stringtorect = function (str, height)
    local lines = splitlines(str)
    lines.w     = foldl(max, 0, map(string.len, lines))
    return Rect(lines):raise(height or 0)
  end
torect = function (o, height)
    if otype(o) == "Rect" then return o end
    if otype(o) == "Expr" then return o:torect() end
    return stringtorect(tostring(o), height)
  end

Rect = Class {
  type    = "Rect",
  __index = {
    copy = function (rect) return copy(rect) end,
    min  = function (rect)
        for i=1,-100000,-1 do if rect[i-1] == nil then return i end end
      end,
    max  = function (rect) return #rect end,
    raise = function (rect, n)
        if n == "b" then n = rect:max()-1 end
        for i=rect:min(),rect:max()+n do rect[i-n] = rect[i] end
        return rect
      end,
    lower = function (rect, n)
        if n == "t" then n = -rect:min()+1 end
        for i=rect:max(),rect:min()-n,-1 do rect[i+n] = rect[i] end
        return rect
      end,
    lowert = function (rect)
        return rect:min() == 1 and rect or rect:copy():lower("t")
      end,
    --
    get  = function (rect, y) return rect[y] or "" end,
    getp = function (rect, y, c) return strpad(rect:get(y), rect.w, c) end,
    adjw = function (rect, y) rect.w = max(rect.w, #rect:get(y)) end,
    set  = function (rect, y, str, fill)
        for i=y+1,rect:min()-1 do rect[i] = fill or "" end
        for i=#rect+1,y-1      do rect[i] = fill or "" end
	rect[y] = str
	rect:adjw(y)
	return rect
      end,
    under  = function (rect, str)
        return rect:copy():set(rect:min()-2, str, "|"):lower(2)
      end,
    under_ = function (rect, str, n)
        return rect:under(strpad(str, rect.w+(n or 2), "_"))
      end,
  },
  __tostring = function (rect)
      return mapconcat(id, rect, "\n", rect:min(), rect:max())
    end,
  __concat = function (r1, r2)
      r1 = torect(r1)
      r2 = torect(r2)
      r3 = Rect {w=0}
      for y=min(r1:min(), r2:min()),max(r1:max(), r2:max()) do 
        r3:set(y, r1:getp(y, " ")..r2:get(y))
      end
      r3.l = r1.l
      r3.r = r2.r
      return r3
    end,
}

rectconcat = function (op, rects)
    if #rects == 0 then return torect(op) end
    if #rects == 1 then return torect(rects[1]):under(op) end
    local out = torect(rects[1]):under_(op)
    for i=2,#rects-1 do out = out..torect(rects[i]):under_(".") end
    return out..torect(rects[#rects]):under(".")
  end


Expr.__index.torect = function (e)
    local op, e1 = e[0], e[1]
    if op == "n" then return torect(e1) end
    if op == "v" then return torect(e1) end
    if op == "b" then return torect(e:infix()) end
    -- local subrects = map(Expr.__index.torect, e)
    local subrects = map(torect, e)
    return rectconcat (op, subrects)
  end

-- «expr:print»  (to ".expr:print")
-- Some methods for printing
Expr.__index.rprint = function (e) print(e:torect()); return e end
Expr.__index.preval  = function (e)
    print(tostring(e))
    print(torect(e))
    print()
    local result = e:eval()
    print("  -->  "..tostring(result))
    print()
    return result
  end



--  ____  _           
-- |  _ \| |__   __ _ 
-- | | | | '_ \ / _` |
-- | |_| | |_) | (_| |
-- |____/|_.__/ \__, |
--              |___/ 
--
-- «expr:Dbg»  (to ".expr:Dbg")
-- Support for debugging (verbose subexpressions)
Expr.__index.Dbg     = function (e1) return Expr {[0]="Dg", e1} end
Expr.__index.DbgEval = function (expr)
    local result = expr:eval()
    print(contextstring()..tostring(expr).."  -->  "..tostring(result))
    return result
  end
contextstring = function ()
    local sk = sorted(keys(context))
    local f = function (name) return name.."="..tostring(context[name]) end
    return "["..mapconcat(f, sk, " ").."]  "
  end




--   ____            _            _   
--  / ___|___  _ __ | |_ _____  _| |_ 
-- | |   / _ \| '_ \| __/ _ \ \/ / __|
-- | |__| (_) | | | | ||  __/>  <| |_ 
--  \____\___/|_| |_|\__\___/_/\_\\__|
--                                    
-- «Context»  (to ".Context")
-- Contexts are used for variables and for substitutions.
Context = Class {
  type    = "Context",
  __index = {
    push  = function (ctxt, name, expr)
        local pair = {name, ctxt[name]}
	table.insert(ctxt.__stack, pair)
	ctxt[name] = expr
        return ctxt
      end,
    pop   = function (ctxt)
        local pair = table.remove(ctxt.__stack) or error("Empty __stack")
	local name, oldvalue = pair[1], pair[2]
	ctxt[name] = oldvalue
	return ctxt
      end,
    minus = function (ctxt, name)
        if ctxt[name] == nil then return ctxt end
        ctxt = copy(ctxt); ctxt[name] = nil; return ctxt
      end,
    keys     = function (ctxt) return sorted(keys(ctxt:minus("__stack"))) end,
    tostring = function (ctxt, sep)
        local ks = ctxt:keys()
        for i,name in ipairs(ks) do ks[i] = name.."="..tostring(ctxt[name]) end
        return mapconcat(id, ks, sep or ", ")
      end,
    print  = function (ctxt) print(ctxt); return ctxt end,
    vprint = function (ctxt) print(ctxt:tostring"\n"); return ctxt end,
  },
  __tostring = function (ctxt) return ctxt:tostring() end,
}

newcontext = function (T) T.__stack = {}; return Context(T) end

context = newcontext {}
defs    = newcontext {}




--  _                 _         _       
-- | | __ _ _ __ ___ | |__   __| | __ _ 
-- | |/ _` | '_ ` _ \| '_ \ / _` |/ _` |
-- | | (_| | | | | | | |_) | (_| | (_| |
-- |_|\__,_|_| |_| |_|_.__/ \__,_|\__,_|
--                                      
-- «def-lambda-app»  (to ".def-lambda-app")
-- A very simple implementation of definitions and application
Def    = function (name)      return Expr {[0]="De", name} end
App    = function (e1, e2)    return Expr {[0]="Ap", e1, e2} end
Lambda = function (var, expr) return Expr {[0]="\\", var, expr} end

Expr.__index.eval_other = function (e)
    local op, e1, e2, e3 = e[0], e[1], e[2], e[3]
    if     op == "De" then return defs[e1]:eval()
    elseif op == "\\" then return e
    elseif op == "Ap" then return _app(e1:eval(), e2:eval())
    elseif op == "Su" then return _evalcompr(e)
    elseif op == "So" then return _evalcompr(e)
                      else print(tolisp(e)); error("Bad expr")
    end
  end

_app = function (f, a)
    if otype(f) ~= "Expr" then error("f must be an expr") end
    if otype(a) ~= "Expr" then error("a must be an expr") end
    if f[1][0]  ~= "v"    then error("x must ber a var")  end
    local f0, fvarname, fexpr = f[0], f[1][1], f[2]
    if f0 ~= "\\" then error("f must be a lambda form") end
    context:push(fvarname, a)
    local result = fexpr:eval()
    context:pop()
    return result
  end



--   ____                               _                    _             
--  / ___|___  _ __ ___  _ __  _ __ ___| |__   ___ _ __  ___(_) ___  _ __  
-- | |   / _ \| '_ ` _ \| '_ \| '__/ _ \ '_ \ / _ \ '_ \/ __| |/ _ \| '_ \ 
-- | |__| (_) | | | | | | |_) | | |  __/ | | |  __/ | | \__ \ | (_) | | | |
--  \____\___/|_| |_| |_| .__/|_|  \___|_| |_|\___|_| |_|___/_|\___/|_| |_|
--                      |_|                                                

-- «comprehension»  (to ".comprehension")
-- (find-dn5 "gab-tests.lua" "comprehension-1")
-- Inspired by:
--   (find-es "haskell" "comprehension")
--   http://www.haskell.org/tutorial/goodies.html#tut-list-comps
--   http://www.haskell.org/onlinereport/exps.html#list-comprehensions
--   http://www.haskell.org/haskellwiki/List_comprehension
--   http://en.wikipedia.org/wiki/List_comprehension

Subset  = function (...)      return Expr {[0]="Su", ...} end
Setof   = function (...)      return Expr {[0]="So", ...} end
Gen     = function (var, set) return Expr {[0]="<-", var, set} end

_evalcompr_ = function (es, collect, i)
    local e, op, e1, e2 = es[i], es[i][0], es[i][1], es[i][2]
    if i == #es then collect(e:eval()); return end
    if op == "<-" then                  -- generator
      if e1[0] ~= "v" then error("Not a var") end
      local varname = e1[1]
      local set     = e2:seval()
      for _,value in ipairs(set) do
        context:push(varname, value)
        _evalcompr_(es, collect, i+1)
        context:pop()
      end
    else                                -- filter
      if not e:beval() then return end  --  on F: abort
      _evalcompr_(es, collect, i+1)     --  on T: continue
    end
  end

_evalcompr = function (e)
    local results = Set()
    local collect = function (e) table.insert(results, e) end
    _evalcompr_(e, collect, 1)
    return results
  end




--             _     _     
--   __ _ _ __(_) __| |___ 
--  / _` | '__| |/ _` / __|
-- | (_| | |  | | (_| \__ \
--  \__, |_|  |_|\__,_|___/
--  |___/                  
--
-- «grids»  (to ".grids")
-- Some functions to create tables.
-- Too many things are hardcoded at the moment.
--
withdef = function (name, def, expr)
    defs:push(name, def)
    local result = expr:eval()
    defs:pop()
    return result
  end

cell = function (x, y)
    if y == 1 then return Cols[x] end
    if x == 1 then return Ps[y-1] end
    return withdef("P", Lambda("k", Ps[y-1]), Cols[x])
  end
column = function (x)
    local C = Rect {w=0}
    for y=1,#Ps+1 do C:set(y, cell(x, y):infix()) end
    return C
  end
columns = function (x1, x2)
    x1 = x1 or 1
    x2 = x2 or #Cols
    local result = column(x1)
    for x=x1+1,x2 do result = result.."  "..column(x) end
    return result
  end

efold = function (f, a, ...)
    local result = a
    for _,b in ipairs {...} do result = f(result, b) end
    return result
  end
Ors  = function (...) return efold(Or,  ...) end
Ands = function (...) return efold(And, ...) end

P = Def "P"

P1, P2, P3, P4 = App(P, _1), App(P, _2), App(P, _3), App(P, _4)
defs.E_1 = Ors (    P2, P3    )
defs.E_2 = Ors (P1, P2, P3, P4)
defs.E_3 = Ands(    P2, P3    )
defs.E_4 = Ands(P1, P2, P3, P4)
defs["E'_1"] = Or (Ors (P1, P2, P3), Ors (P2, P3, P4))
defs["E'_2"] = And(Ors (P1, P2, P3), Ors (P2, P3, P4))
defs["E'_3"] = Or (Ands(P1, P2, P3), Ands(P2, P3, P4))
defs["E'_4"] = And(Ands(P1, P2, P3), Ands(P2, P3, P4))

P123or   = Ors (P1, P2, P3)
P123and  = Ands(P1, P2, P3)
P234or   = Ors (P2, P3, P4)
P234and  = Ands(P2, P3, P4)
P123or   = Ex(k, Set(_1, _2, _3), App(P, k))
P123and  = Fa(k, Set(_1, _2, _3), App(P, k))
P234or   = Ex(k, Set(_2, _3, _4), App(P, k))
P234and  = Fa(k, Set(_2, _3, _4), App(P, k))
defs["E'_1"] = Or (P123or,  P234or)
defs["E'_2"] = And(P123or,  P234or)
defs["E'_3"] = Or (P123and, P234and)
defs["E'_4"] = And(P123and, P234and)

Cols = {
  App(P,  k),
  P1, P2, P3, P4,
  Def("E_1"), Def("E_2"), Def("E_3"), Def("E_4"),
}
Ps = {
   Ge(    k, _2),
   Ge( _2*k, _2),
   Eq(    k, _1),
   Lt(    k, _1),
  Neq(    k, _2),
  Neq(    k, _3),
  And(Neq(k, _2), Neq(k, _3)),
  Neq(    k, _0),
}




--        _     _                                  
--   ___ | | __| |  _ __   __ _ _ __ ___  ___ _ __ 
--  / _ \| |/ _` | | '_ \ / _` | '__/ __|/ _ \ '__|
-- | (_) | | (_| | | |_) | (_| | |  \__ \  __/ |   
--  \___/|_|\__,_| | .__/ \__,_|_|  |___/\___|_|   
--                 |_|                             
-- Moved to:
-- (find-dn5 "gab-oldparser.lua")
-- «old-parser»  (to ".old-parser")
--   «precedence-table»   (to ".precedence-table")
--   «precedence»         (to ".precedence")
--   «recursive-descent»  (to ".recursive-descent")
--   «parser-grammar»     (to ".parser-grammar")




--  _                                                   
-- | |_ __   ___  __ _   _ __   __ _ _ __ ___  ___ _ __ 
-- | | '_ \ / _ \/ _` | | '_ \ / _` | '__/ __|/ _ \ '__|
-- | | |_) |  __/ (_| | | |_) | (_| | |  \__ \  __/ |   
-- |_| .__/ \___|\__, | | .__/ \__,_|_|  |___/\___|_|   
--   |_|         |___/  |_|                             
--
-- «lpeg-parser»  (to ".lpeg-parser")
-- A reimplementation in LPEG of the recursive-descent parser

if userocks
then userocks(); loadlpeg()  -- (find-angg "LUA/lua50init.lua" "loadlpeg")
else require "lpeg"          -- (find-vldifile "liblua5.1-lpeg2.list")
end

-- require "lpeg"
P  = lpeg.P
V  = lpeg.V
Cc = lpeg.Cc
Ct = lpeg.Ct
Cb = lpeg.Cb
Cg = lpeg.Cg

local ispattern = function (o)
     return getmetatable(o) == getmetatable(lpeg.P"")
  end
local set = function (name, o)
    if ispattern(o) then return o:Cg(name) else return lpeg.Cc(o):Cg(name) end
  end
local get    = function (name) return lpeg.Cb(name) end
local group  = function (cmds) return lpeg.Cg(cmds) end
local applys = function (e, T) return T[#T](e, unpack(T, 1, #T-1)) end


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, "* /")

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

pack_num    = function (str)        return Num(tonumber(str)) end
pack_var    = function (str)        return Var(str) end
pack_bin    = function (e1, op, e2) return Expr {[0]=op, e1, e2} end
pack_app    = function (e1, e2)     return App(e1, e2) end
pack_tuple  = function (L)        return #L==1 and L[1] or Tuple(unpack(L)) end
pack_qlexpr = function (ql, a, b)   -- quantifiers and lambda
    if ql == "Fa" then return Fa(a, b) end
    if ql == "Ex" then return Ex(a, b) end
    if ql == "\\" then return Lambda(a, b) end
  end

-- Sets come in four flavors:
--   {e1<-e2 | P, Q}       => Subset(e1<-e2, P, Q, e1}
--   {e      | a<-A, b<-B} => Setof(a<-A, b<-B, e}
--   {a, b, c}             => Set(a, b, c)
--   {}                    => Set()
pack_subset = function (e, L)
    table.insert(L, e[1])
    return Subset(e, unpack(L))
  end
pack_setof = function (e, L)     
    table.insert(L, e)
    return Setof(unpack(L))
  end
pack_set = function (a, b, c)
    if b == "|" and a[0] == "<-" then return pack_subset(a, c) end
    if b == "|" and a[0] ~= "<-" then return pack_setof (a, c) end
    if a == "" or a == nil then return Set() end
    return Set(a, unpack(b or {}))
  end

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

Expr_grammar = {
  "Expr",     -- initial rule name
  Alpha       = lpeg.R"AZ" + lpeg.R"az",
  Alphanum    = lpeg.R"AZ" + lpeg.R"az" + lpeg.R"09" + P"_",
  Num         = sp * (lpeg.R"09"^1):C()              / pack_num,
  Var         = sp * (V"Alpha" * V"Alphanum"^0):C()  / pack_var,
  --
  Commaexprs  = (sP"," * V"Expr")^0,
  Exprs       = V"Expr" * V"Commaexprs",
  Parenexpr   = (sP"(" * V"Exprs" * sP")"):Ct()      / pack_tuple,
  Setexpr     = sP"{" *
                ( sP"}" * Cc""
                + V"Expr" *
                  ( sP"}"
                  + V"Commaexprs":Ct() * sP"}"
                  + sp * P"|":C() * V"Exprs":Ct() * sP"}")) / pack_set,
  --
  Ql          = sp * (P"Fa" + P"Ex" + P"\\"):C(),
  Qlexpr      = V"Ql" * V"Expr" * sP"." * V"Expr"       / pack_qlexpr,
  --
  Binop       = sp * ((lpeg.P(2):Cmt(is_binop) +
                       lpeg.P(1):Cmt(is_binop))),
  Binop_under = (get"lop" * V"Binop"):Cmt(is_under),
  Binop_setlop = set("lop", V"Binop_under") * get("lop"),
  --
  Expr_wos    = V"Parenexpr"
              + V"Setexpr"
              + V"Qlexpr"
              + V"Num"
              + V"Var",
  Complement  = group(V"Binop_setlop" * V"Expr_under" * Cc(pack_bin)):Ct()
              + (V"Parenexpr" * Cc(pack_app)):Ct(),
  Expr_under  = (V"Expr_wos" * V"Complement"^0):Cf(applys),
  Expr        = group(set("lop", "") * V"Expr_under"),
}
Expr_pattern  = lpeg.P(Expr_grammar)

lpeg.prmatch = function (patt, ...) PP(patt:match(...)) end
etest = function (str)
    local e = Expr_pattern:match(str)
    print(e)
    print(e:torect())
  end
eteste = function (str)
    local e = Expr_pattern:match(str)
    print(e)
    print(e:torect())
    print(e:eval())
  end

-- «pe»  (to ".pe")
-- pe:  parse expression
-- pev: parse expression, evaluate, print all
-- pep: parse expression and print (do not evaluate)
pe = function (str) return Expr_pattern:match(str) end
pep = function (str) return pe(str):print():rprint() end
pev = function (str) return pe(str):print():rprint():eval():print() end



--  __  __           _       _ 
-- |  \/  | ___   __| | __ _| |
-- | |\/| |/ _ \ / _` |/ _` | |
-- | |  | | (_) | (_| | (_| | |
-- |_|  |_|\___/ \__,_|\__,_|_|
--                             
-- «modal»  (to ".modal")
-- (find-angg "LUA/canvas3.lua")
-- (find-angg "LUA/canvas3.lua" "ZDag-functions")

-- It should be easy to make the logic of the calculator more
-- flexible... at present it is boolean, two-valued, with the same
-- truth values as the underlying Lua.

Vee_ = function (s)
    local a, b, c = s:sub(1,1), s:sub(2,2), s:sub(3,3)
    return Rect {w=3, a.." "..b, " "..c}
  end
Vee = function (s)
    return Expr {[0]="Mo", Vee_(s)}
  end

-- (find-angg "LUA/canvas3.lua" "ZDag-functions")
-- A "shape" is a string made of digits, newlines, and spaces.
-- A "short" is a string made of "0"s and "1"s.
-- For example:
-- reh_shape = " 1\n"  ..
--             "2 3\n" ..
--             "4"
-- and "0101" is a short that represents an open set on Reh
-- in a compact form.

cton = function (c) return tonumber(c, 36) end
intoshape = function (shape, short)
    local f = function (c) return short:sub(cton(c), cton(c)) end
    return (shape:gsub("(%w)", f))
  end
shapetoarrows = function (shape)
    local lines = splitlines(shape)
    local arrows = {}
    local registerbpm = function (v1, v2)
        if v1 and v2 then table.insert(arrows, {v1, v2}) end
      end
    for y=1,#lines-1 do
      for x=1,#lines[y] do
        local c  = cton(lines[y  ]:sub(x,   x  ))
        local sw = cton(lines[y+1]:sub(x-1, x-1))
        local s  = cton(lines[y+1]:sub(x,   x  ))
        local se = cton(lines[y+1]:sub(x+1, x+1))
        registerbpm(c, sw)    -- is southwest a black pawn's move?
        registerbpm(c, s)     -- is south     a black pawn's move?
        registerbpm(c, se)    -- is southeast a black pawn's move?
      end
    end
    return arrows
  end
shapetocoords = function (shape)
    local lines = splitlines(shape)
    local coords = {}
    for y=1,#lines do
      for x=1,#lines[y] do
        local c  = cton(lines[y]:sub(x, x))
	if c then coords[c] = {x, y} end
      end
    end
    return coords
  end





-- This is just a library.
print("Loading: gab.lua")
io.stdout:setvbuf("no")    -- (find-es "lua5" "setvbuf")






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