if not modules then modules = { } end modules ['luatex-preprocessor'] = {
    version   = 1.001,
    comment   = "companion to luatex-preprocessor.tex",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

-- This is a stripped down version of the preprocessor. In ConTeXt we have a bit
-- more, use a different logger, and use a few optimizations. A few examples are
-- shown at the end.

local rep, sub, gmatch = string.rep, string.sub, string.gmatch
local insert, remove = table.insert, table.remove
local setmetatable = setmetatable

local stack, top, n, hashes = { }, nil, 0, { }

local function set(s)
    if top then
        n = n + 1
        if n > 9 then
            texio.write_nl("number of arguments > 9, ignoring: " .. s)
        else
            local ns = #stack
            local h = hashes[ns]
            if not h then
                h = rep("#",ns)
                hashes[ns] = h
            end
            m = h .. n
            top[s] = m
            return m
        end
    end
end

local function get(s)
    local m = top and top[s] or s
    return m
end

local function push()
    top = { }
    n = 0
    local s = stack[#stack]
    if s then
        setmetatable(top,{ __index = s })
    end
    insert(stack,top)
end

local function pop()
    top = remove(stack)
end

local leftbrace   = lpeg.P("{")
local rightbrace  = lpeg.P("}")
local escape      = lpeg.P("\\")

local space       = lpeg.P(" ")
local spaces      = space^1
local newline     = lpeg.S("\r\n")
local nobrace     = 1 - leftbrace - rightbrace

local name        = lpeg.R("AZ","az")^1
local longname    = (leftbrace/"") * (nobrace^1) * (rightbrace/"")
local variable    = lpeg.P("#") * lpeg.Cs(name + longname)
local escapedname = escape * name
local definer     = escape * (lpeg.P("def") + lpeg.P("egdx") * lpeg.P("def"))
local anything    = lpeg.P(1)
local always      = lpeg.P(true)

local pushlocal   = always   / push
local poplocal    = always   / pop
local declaration = variable / set
local identifier  = variable / get

local function matcherror(str,pos)
    texio.write_nl("runaway definition at: " .. sub(str,pos-30,pos))
end

local parser = lpeg.Cs { "converter",
    definition  = pushlocal
                * definer
                * escapedname
                * (declaration + (1-leftbrace))^0
                * lpeg.V("braced")
                * poplocal,
    braced      = leftbrace
                * (   lpeg.V("definition")
                    + identifier
                    + lpeg.V("braced")
                    + nobrace
                  )^0
                * (rightbrace + lpeg.Cmt(always,matcherror)),
    converter   = (lpeg.V("definition") + anything)^1,
}

-- local texkpse

local function find_file(...)
 -- texkpse = texkpse or kpse.new("luatex","tex")
 -- return texkpse:find_file(...) or ""
    return kpse.find_file(...) or ""
end

commands = commands or { }

function commands.preprocessed(str)
    return lpeg.match(parser,str)
end

function commands.inputpreprocessed(name)
    local name = find_file(name) or ""
    if name ~= "" then
     -- we could use io.loaddata as it's loaded in luatex-plain
        local f = io.open(name,'rb')
        if f then
            texio.write("("..name)
            local d = commands.preprocessed(f:read("*a"))
            if d and d ~= "" then
                texio.write("processed: " .. name)
                for s in gmatch(d,"[^\n\r]+") do
                    tex.print(s) -- we do a dumb feedback
                end
            end
            f:close()
            texio.write(")")
        else
            tex.error("preprocessor error, invalid file: " .. name)
        end
    else
        tex.error("preprocessor error, unknown file: " .. name)
    end
end

function commands.preprocessfile(oldfile,newfile) -- no checking
    if oldfile and oldfile ~= newfile then
        local f = io.open(oldfile,'rb')
        if f then
            local g = io.open(newfile,'wb')
            if g then
                g:write(lpeg.match(parser,f:read("*a") or ""))
                g:close()
            end
            f:close()
        end
    end
end

--~ print(preprocessed([[\def\test#oeps{test:#oeps}]]))
--~ print(preprocessed([[\def\test#oeps{test:#{oeps}}]]))
--~ print(preprocessed([[\def\test#{oeps:1}{test:#{oeps:1}}]]))
--~ print(preprocessed([[\def\test#{oeps}{test:#oeps}]]))
--~ preprocessed([[\def\test#{oeps}{test:#oeps \halign{##\cr #oeps\cr}]])
--~ print(preprocessed([[\def\test#{oeps}{test:#oeps \halign{##\cr #oeps\cr}}]]))
