Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
#!/usr/bin/env lua5.1
-- This file:
--   http://angg.twu.net/luatree/luatree.lua.html
--   http://angg.twu.net/luatree/luatree.lua
--           (find-angg "luatree/luatree.lua")
-- See also:
--   https://github.com/edrx/luatree
--   https://github.com/edrx/luatree/#introduction
--   http://angg.twu.net/eev-maxima.html#luatree
--   http://angg.twu.net/e/maxima.e.html#luatree
--   http://angg.twu.net/e/maxima.e.html#luatree-explanation
--               (find-es "maxima" "luatree")
--               (find-es "maxima" "luatree-explanation")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- License: Public Domain.
--
-- (defun m  () (interactive) (find-angg "luatree/luatree.mac"))
-- (defun li () (interactive) (find-angg "luatree/luatree.lisp"))
-- (defun lu () (interactive) (find-angg "luatree/luatree.lua"))


-- Assorted small functions from my init file.
-- From: http://angg.twu.net/LUA/lua50init.lua.html
--       (find-angg "LUA/lua50init.lua" "eval-and-L")
--       (find-angg "LUA/lua50init.lua" "min-and-max")
--       (find-angg "LUA/lua50init.lua" "splitlines")
--       (find-angg "LUA/lua50init.lua" "splitlines-5.3")
--       (find-angg "LUA/lua50init.lua" "map")
--       (find-angg "LUA/lua50init.lua" "fold")
--
readfile = function (fname)
    local f = assert(io.open(fname, "r"))
    local bigstr = f:read("*a")
    f:close()
    return bigstr
  end

eval = function (str) return assert(loadstring(str))() end
expr = function (str) return eval("return "..str) end

min = function (a, b)
    if a < b then return a else return b end
  end
max = function (a, b)
    if a < b then return b else return a end
  end

split = function (str, pat)
    local arr = {}
    string.gsub(str, pat or "([^%s]+)", function (word)
        table.insert(arr, word)
      end)
    return arr
  end
splitlines = function (bigstr)
    local arr = split(bigstr, "([^\n]*)\n?")
    if _VERSION:sub(5) < "5.3" then
      table.remove(arr)
    end
    return arr
  end

map = function (f, arr, n)
    local brr = {}
    for i=1,(n or #arr) do table.insert(brr, (f(arr[i]))) end
    return brr
  end
copy = function (A)
    local B = {}
    for k,v in pairs(A) do B[k] = v end
    setmetatable(B, getmetatable(A))
    return B
  end
deepcopy = function (A)
    if type(A) ~= "table" then return A end
    local B = {}
    for k,v in pairs(A) do B[k] = deepcopy(v) end
    setmetatable(B, getmetatable(A))
    return B
  end
deepcopymt = function (A, mt)
    if type(A) ~= "table" then return A end
    local B = {}
    for k,v in pairs(A) do B[k] = deepcopymt(v, mt) end
    setmetatable(B, mt)  -- use mt
    return B
  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


--   ____ _               
--  / ___| | __ _ ___ ___ 
-- | |   | |/ _` / __/ __|
-- | |___| | (_| \__ \__ \
--  \____|_|\__,_|___/___/
--                        
-- From:   (find-angg "LUA/lua50init.lua" "Class")
-- http://angg.twu.net/LUA/lua50init.lua.html#Class
--
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


--  ____           _   
-- |  _ \ ___  ___| |_ 
-- | |_) / _ \/ __| __|
-- |  _ <  __/ (__| |_ 
-- |_| \_\___|\___|\__|
--                     
-- From:   (find-angg "LUA/lua50init.lua" "Rect")
-- http://angg.twu.net/LUA/lua50init.lua.html#Rect
--
torect = function (o)
    if otype(o) == "Rect" then return o end
    if type(o) == "string" then return Rect.new(o) end
    error()
  end
--
Rect = Class {
  type = "Rect",
  new  = function (str) return Rect(splitlines(str)) end,
  rep  = function (str, n) local r=Rect{}; for i=1,n do r[i]=str end; return r end,
  from = function (o)
      if type(o) == "string" then return Rect.new(o) end
      if type(o) == "number" then return Rect.new(tostring(o)) end
      return o -- missing: test otypeness
    end,
  --
  -- A hack to let us build syntax trees very quickly:
  syntree = function (op, a1, ...)
      if not a1 then return Rect.from(op) end
      local r = Rect.from(a1):syn1(op)
      for _,an in ipairs({...}) do r = r:synconcat(Rect.from(an)) end
      return r
    end,
  --
  __tostring = function (rect) return rect:tostring() end,
  __concat = function (r1, r2) return torect(r1):concat(torect(r2)) end,
  __index = {
    tostring = function (rect) return table.concat(rect, "\n") end,
    copy = function (rect) return copy(rect) end,
    width = function (rect)
        return rect.w or foldl(max, 0, map(string.len, rect))
      end,
    push1 = function (rect, str) table.insert(rect, 1, str); return rect end,
    push2 = function (rect, str1, str2) return rect:push1(str2):push1(str1) end,
    pad0  = function (rect, y, w, c, rstr)
        rect[y] = ((rect[y] or "")..(c or " "):rep(w)):sub(1, w)..(rstr or "")
        return rect
      end,
    lower = function (rect, n, str)
        for i=1,n do rect:push1(str or "") end
        return rect
      end,
    concat = function (r1, r2, w, dy)
        r1 = r1:copy()
        w = w or r1:width()
        dy = dy or 0
        for y=#r1+1,#r2+dy do r1[y] = "" end
        for y=1,#r2 do r1:pad0(y+dy, w, nil, r2[y]) end
        return r1
      end,
    prepend = function (rect, str) return Rect.rep(str, #rect)..rect end,
    --
    -- Low-level methods for building syntax trees:
    --   syn1 draws a "|" above a rect and draws the opname above it,
    --   synconcat draws two syntax trees side by side and joins them with "_"s.
    syn1 = function (r1, opname) return r1:copy():push2(opname or ".", "|") end,
    synconcat = function (r1, r2)
        return r1:copy():pad0(1, r1:width()+2, "_")..r2:copy():push2(".", "|")
      end,
    --
    -- Low-level methods for building deduction trees:
    --   dedconcat draws two deduction trees side by side,
    --   dedsetbar draws or redraws the bar above the conclusion,
    --   dedaddroot adds a bar and a conclusion below some trees drawn
    --   side by side.
    dedconcat = function (r1, r2)
        local w = r1:width() + 2
        if #r1 <  #r2 then return r1:copy():lower(#r2-#r1):concat(r2, w) end
        if #r1 == #r2 then return r1:copy():concat(r2, w) end
        if #r1  > #r2 then return r1:copy():concat(r2, w, #r1-#r2) end
      end,
    dedsetbar = function (r, barchar, barname)
        if #r == 1 then table.insert(r, 1, "") end
        local trim = function (str) return (str:match("^(.-) *$")) end
        local strover  = trim(r[#r-2] or "")  -- the hypotheses above the bar
        local strunder = trim(r[#r])          -- the conclusion below the bar
        local len = max(#strover, #strunder)
	local bar = (barchar or "-"):rep(len)
        r[#r-1] = bar..(barname or "")
        return r
      end,
    dedaddroot = function (r, rootstr, barchar, barname)
        table.insert(r, "")                  -- The bar will be here.
        table.insert(r, rootstr)             -- Draw the conclusion,
        return r:dedsetbar(barchar, barname) -- and then set the bar.
      end,
  },
}


--  ____            _____              
-- / ___| _   _ _ _|_   _| __ ___  ___ 
-- \___ \| | | | '_ \| || '__/ _ \/ _ \
--  ___) | |_| | | | | || | |  __/  __/
-- |____/ \__, |_| |_|_||_|  \___|\___|
--        |___/                        
--
-- From:   (find-angg "LUA/lua50init.lua" "SynTree")
-- http://angg.twu.net/LUA/lua50init.lua.html#SynTree
--
SynTree = Class {
  type = "SynTree",
  from = function (A) return deepcopymt(A, SynTree) end,
  torect = function (o)
      if type(o) == "number" then return Rect.new(tostring(o)) end
      if type(o) == "string" then return Rect.new(o) end
      if type(o) == "table" then
	local op = o[0]
	if type(op) == "number" then op = tostring(op) end
        if #o == 0 then return Rect.new(op or ".") end
        local r = SynTree.torect(o[1]):syn1(op)
        for i=2,#o do r = r:synconcat(SynTree.torect(o[i])) end
        return r
      end
    end,
  __tostring = function (o) return o:torect():tostring() end,
  __index = {
    torect = function (o)
        return SynTree.torect(o)
      end,
  },
}


-- Process stdin or arg[1]:
if arg and arg[1] then
  bigstr = readfile(arg[1])
else
  bigstr = io.read("*a")
end

print(SynTree.from(expr(bigstr)))


--[[
-- Make sure that this works without my init file.
-- See: (find-lua51manual "#6" "LUA_INIT")
--      (find-anggfile "LUA/lua50init.lua")
--      (find-es "maxima" "luatree-explanation")
--      (find-es "maxima" "luatree-explanation" "echo")

* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
echo  $LUA_INIT
export LUA_INIT=""
echo '{[0]="[", {[0]="/", "x", "y"}, "33"}' \
  | ./luatree.lua

echo '{[0]="[", {[0]="/", "x", "y"}, "33"}' > /tmp/o1.lua
./luatree.lua /tmp/o1.lua

--]]