if not modules then modules = { } end modules ['util-lua'] = {
    version   = 1.001,
    comment   = "companion to luat-lib.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    comment   = "the strip code is written by Peter Cawley",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

local rep, sub, byte, dump, format = string.rep, string.sub, string.byte, string.dump, string.format
local loadstring, loadfile, type = loadstring, loadfile, type

utilities          = utilities or {}
utilities.lua      = utilities.lua or { }
local luautilities = utilities.lua

utilities.report   = logs and logs.reporter("system") or print -- can be overloaded later

local tracestripping           = false
local forcestupidcompile       = true  -- use internal bytecode compiler
luautilities.stripcode         = true  -- support stripping when asked for
luautilities.alwaysstripcode   = false -- saves 1 meg on 7 meg compressed format file (2012.08.12)
luautilities.nofstrippedchunks = 0
luautilities.nofstrippedbytes  = 0

-- The next function was posted by Peter Cawley on the lua list and strips line
-- number information etc. from the bytecode data blob. We only apply this trick
-- when we store data tables. Stripping makes the compressed format file about
-- 1MB smaller (and uncompressed we save at least 6MB).
--
-- You can consider this feature an experiment, so it might disappear. There is
-- no noticeable gain in runtime although the memory footprint should be somewhat
-- smaller (and the file system has a bit less to deal with).
--
-- Begin of borrowed code ... works for Lua 5.1 which LuaTeX currently uses ...

local function strip_code_pc(dump,name)
    local before = #dump
    local version, format, endian, int, size, ins, num = byte(dump,5,11)
    local subint
    if endian == 1 then
        subint = function(dump, i, l)
            local val = 0
            for n = l, 1, -1 do
                val = val * 256 + byte(dump,i + n - 1)
            end
            return val, i + l
        end
    else
        subint = function(dump, i, l)
            local val = 0
            for n = 1, l, 1 do
                val = val * 256 + byte(dump,i + n - 1)
            end
            return val, i + l
        end
    end
    local strip_function
    strip_function = function(dump)
        local count, offset = subint(dump, 1, size)
        local stripped, dirty = rep("\0", size), offset + count
        offset = offset + count + int * 2 + 4
        offset = offset + int + subint(dump, offset, int) * ins
        count, offset = subint(dump, offset, int)
        for n = 1, count do
            local t
            t, offset = subint(dump, offset, 1)
            if t == 1 then
                offset = offset + 1
            elseif t == 4 then
                offset = offset + size + subint(dump, offset, size)
            elseif t == 3 then
                offset = offset + num
            end
        end
        count, offset = subint(dump, offset, int)
        stripped = stripped .. sub(dump,dirty, offset - 1)
        for n = 1, count do
            local proto, off = strip_function(sub(dump,offset, -1))
            stripped, offset = stripped .. proto, offset + off - 1
        end
        offset = offset + subint(dump, offset, int) * int + int
        count, offset = subint(dump, offset, int)
        for n = 1, count do
            offset = offset + subint(dump, offset, size) + size + int * 2
        end
        count, offset = subint(dump, offset, int)
        for n = 1, count do
            offset = offset + subint(dump, offset, size) + size
        end
        stripped = stripped .. rep("\0", int * 3)
        return stripped, offset
    end
    dump = sub(dump,1,12) .. strip_function(sub(dump,13,-1))
    local after = #dump
    local delta = before-after
    if tracestripping then
        utilities.report("stripped bytecode: %s, before %s, after %s, delta %s",name or "unknown",before,after,delta)
    end
    luautilities.nofstrippedchunks = luautilities.nofstrippedchunks + 1
    luautilities.nofstrippedbytes  = luautilities.nofstrippedbytes  + delta
    return dump, delta
end

-- ... end of borrowed code.

local function strippedbytecode(code,forcestrip,name)
    if (forcestrip and luautilities.stripcode) or luautilities.alwaysstripcode then
        return strip_code_pc(code,name)
    else
        return code, 0
    end
end

luautilities.stripbytecode    = strip_code_pc
luautilities.strippedbytecode = strippedbytecode

local function fatalerror(name)
    utilities.report(format("fatal error in %q",name or "unknown"))
end

-- quite subtle ... doing this wrong incidentally can give more bytes


function luautilities.loadedluacode(fullname,forcestrip,name)
    -- quite subtle ... doing this wrong incidentally can give more bytes
    name = name or fullname
    local code = loadfile(fullname)
    if code then
        code()
    end
    if forcestrip and luautilities.stripcode then
        if type(forcestrip) == "function" then
            forcestrip = forcestrip(fullname)
        end
        if forcestrip then
            local code, n = strip_code_pc(dump(code,name))
            return loadstring(code), n
        elseif luautilities.alwaysstripcode then
            return loadstring(strip_code_pc(dump(code),name))
        else
            return code, 0
        end
    elseif luautilities.alwaysstripcode then
        return loadstring(strip_code_pc(dump(code),name))
    else
        return code, 0
    end
end

function luautilities.strippedloadstring(code,forcestrip,name) -- not executed
    local n = 0
    if (forcestrip and luautilities.stripcode) or luautilities.alwaysstripcode then
        code = loadstring(code)
        if not code then
            fatalerror(name)
        end
        code, n = strip_code_pc(dump(code),name)
    end
    return loadstring(code), n
end

local function stupidcompile(luafile,lucfile,strip)
    local code = io.loaddata(luafile)
    local n = 0
    if code and code ~= "" then
        code = loadstring(code)
        if not code then
            fatalerror()
        end
        code = dump(code)
        if strip then
            code, n = strippedbytecode(code,true,luafile) -- last one is reported
        end
        if code and code ~= "" then
            io.savedata(lucfile,code)
        end
    end
    return n
end

local luac_normal = "texluac -o %q %q"
local luac_strip  = "texluac -s -o %q %q"

function luautilities.compile(luafile,lucfile,cleanup,strip,fallback) -- defaults: cleanup=false strip=true
    utilities.report("lua: compiling %s into %s",luafile,lucfile)
    os.remove(lucfile)
    local done = false
    if strip ~= false then
        strip = true
    end
    if forcestupidcompile then
        fallback = true
    elseif strip then
        done = os.spawn(format(luac_strip, lucfile,luafile)) == 0
    else
        done = os.spawn(format(luac_normal,lucfile,luafile)) == 0
    end
    if not done and fallback then
        local n = stupidcompile(luafile,lucfile,strip)
        if n > 0 then
            utilities.report("lua: %s dumped into %s (%i bytes stripped)",luafile,lucfile,n)
        else
            utilities.report("lua: %s dumped into %s (unstripped)",luafile,lucfile)
        end
        cleanup = false -- better see how bad it is
    end
    if done and cleanup == true and lfs.isfile(lucfile) and lfs.isfile(luafile) then
        utilities.report("lua: removing %s",luafile)
        os.remove(luafile)
    end
    return done
end
--~ local getmetatable, type = getmetatable, type

--~ local types = { }

--~ function luautilities.registerdatatype(d,name)
--~     types[getmetatable(d)] = name
--~ end

--~ function luautilities.datatype(d)
--~     local t = type(d)
--~     if t == "userdata" then
--~         local m = getmetatable(d)
--~         return m and types[m] or "userdata"
--~     else
--~         return t
--~     end
--~ end

--~ luautilities.registerdatatype(lpeg.P("!"),"lpeg")

--~ print(luautilities.datatype(lpeg.P("oeps")))