<commit_before>-- This is meta-programming -- Lua code that writes C code.
-- Meta-programming introduces a lot of additonal complexity
-- for each line, but in this case the automated generation
-- of Lua wrappers for the libmarpa methods, alone,
-- allows the elimination of thousands of lines of code.

-- assumes that, when called, out_file to set to output file
local error_file

function c_safe_string (s)
    s = string.gsub(s, '"', '\\034')
    s = string.gsub(s, '\\', '\\092')
    return '"' .. s .. '"'
end

for k,v in ipairs(arg) do
   if not v:find("=")
   then return nil, "Bad options: ", arg end
   local id, val = v:match("^([^=]+)%=(.*)") -- no space around =
   if id == "out" then io.output(val)
   elseif id == "errors" then error_file = val
   else return nil, "Bad id in options: ", id end
end

-- initial piece
io.write[=[
/*
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
*/

#define LUA_LIB
#include "marpa.h"
#include "lua.h"
#include "lauxlib.h"

#include "compat-5.2.c"

#undef UNUSED
#if     __GNUC__ >  2 || (__GNUC__ == 2 && __GNUC_MINOR__ >  4)
#define UNUSED __attribute__((__unused__))
#else
#define UNUSED
#endif

#define EXPECTED_LIBMARPA_MAJOR 8
#define EXPECTED_LIBMARPA_MINOR 3
#define EXPECTED_LIBMARPA_MICRO 0

/* For debugging */
static void dump_stack (lua_State *L) {
      int i;
      int top = lua_gettop(L);
      for (i = 1; i <= top; i++) {  /* repeat for each level */
        int t = lua_type(L, i);
        switch (t) {
    
          case LUA_TSTRING:  /* strings */
            printf("`%s'", lua_tostring(L, i));
            break;
    
          case LUA_TBOOLEAN:  /* booleans */
            printf(lua_toboolean(L, i) ? "true" : "false");
            break;
    
          case LUA_TNUMBER:  /* numbers */
            printf("%g", lua_tonumber(L, i));
            break;
    
          default:  /* other values */
            printf("%s", lua_typename(L, t));
            break;
    
        }
        printf("  ");  /* put a separator */
      }
      printf("\n");  /* end the listing */
}

static void dump_table(lua_State *L, int raw_table_index)
{
    /* Original stack: [ ... ] */
    const int table_index = lua_absindex(L, raw_table_index);
    lua_pushnil(L);
    /* [ ..., nil ] */
    while (lua_next(L, table_index))
    {
        /* [ ..., key, value ] */
        const int value_stack_ix = lua_gettop(L);
        const int key_stack_ix = lua_gettop(L)+1;
        /* Copy the key, because lua_tostring() can be destructive */
        lua_pushvalue(L, -2);
        /* [ ..., key, value, key_copy ] */
        switch (lua_type(L, key_stack_ix)) {
    
          case LUA_TSTRING:  /* strings */
            printf("`%s'", lua_tostring(L, key_stack_ix));
            break;
    
          case LUA_TBOOLEAN:  /* booleans */
            printf(lua_toboolean(L, key_stack_ix) ? "true" : "false");
            break;
    
          case LUA_TNUMBER:  /* numbers */
            printf("%g", lua_tonumber(L, key_stack_ix));
            break;
    
          case LUA_TTABLE:  /* numbers */
            printf("table %s", lua_tostring(L, key_stack_ix));
            break;
    
          default:  /* other values */
            printf("%s", lua_typename(L, lua_type(L, key_stack_ix)));
            break;
    
        }
        printf(" -> ");  /* end the listing */
        switch (lua_type(L, value_stack_ix)) {
    
          case LUA_TSTRING:  /* strings */
            printf("`%s'", lua_tostring(L, value_stack_ix));
            break;
    
          case LUA_TBOOLEAN:  /* booleans */
            printf(lua_toboolean(L, value_stack_ix) ? "true" : "false");
            break;
    
          case LUA_TNUMBER:  /* numbers */
            printf("%g", lua_tonumber(L, value_stack_ix));
            break;
    
          case LUA_TTABLE:  /* numbers */
            printf("table %s", lua_tostring(L, value_stack_ix));
            break;
    
          default:  /* other values */
            printf("%s", lua_typename(L, lua_type(L, value_stack_ix)));
            break;
    
        }
        printf("\n");  /* end the listing */
        /* [ ..., key, value, key_copy ] */
        lua_pop(L, 2);
        /* [ ..., key ] */
    }
    /* Back to original stack: [ ... ] */
}

]=]

-- error codes

io.write[=[
struct s_libmarpa_error_code {
   lua_Integer code;
   const char* mnemonic;
   const char* description;
};

]=]

do
    local f = assert(io.open(error_file, "r"))
    local code_lines = {}
    local code_mnemonics = {}
    local max_code = 0
    while true do
        local line = f:read()
        if line == nil then break end
        local i, j = string.find(line, "#")
        if (i == nil) then stripped = line
        else stripped = string.sub(line, 0, i-1)
        end
        if string.find(stripped, "%S") then
            local raw_code
            local raw_mnemonic
            local description
            _, _, raw_code, raw_mnemonic, description = string.find(stripped, "^(%d+)%sMARPA_ERR_(%S+)%s(.*)$")
            local code = tonumber(raw_code)
            if description == nil then return nil, "Bad line in error code file ", line end
            if code > max_code then max_code = code end
            local mnemonic = 'LUIF_ERR_' .. raw_mnemonic
            code_mnemonics[code] = mnemonic
            code_lines[code] = string.format( '   { %d, %s, %s },',
                code,
                c_safe_string(mnemonic),
                c_safe_string(description)
                )
        end
    end

    io.write('#define LIBMARPA_MIN_ERROR_CODE 0\n')
    io.write('#define LIBMARPA_MAX_ERROR_CODE ' .. max_code .. '\n\n')

    for i = 0, max_code do
        local mnemonic = code_mnemonics[i]
        if mnemonic then
            io.write(
                   string.format('#define %s %d\n', mnemonic, i)
           )
       end
    end
    io.write('\n')
    io.write('struct s_libmarpa_error_code libmarpa_error_codes[LIBMARPA_MAX_ERROR_CODE-LIBMARPA_MIN_ERROR_CODE+1] = {\n')
    for i = 0, max_code do
        local code_line = code_lines[i]
        if code_line then
           io.write(code_line .. '\n')
        else
           io.write(
               string.format(
                   '    { %d, "LUIF_ERROR_RESERVED", "Unknown Libmarpa error %d" },\n',
                   i, i
               )
           )
        end
    end
    io.write('};\n\n');
    f:close()
end

-- for Kollos's own (that is, non-Libmarpa) error codes
do
    local code_lines = {}
    local code_mnemonics = {}
    local min_code = 200
    local max_code = 200

    -- Should add some checks on the errors, checking for
    -- 1.) duplicate mnenomics
    -- 2.) duplicate error codes

    function luif_error_add (code, mnemonic, description)
        code_mnemonics[code] = mnemonic
        code_lines[code] = string.format( '   { %d, %s, %s },',
            code,
            c_safe_string(mnemonic),
            c_safe_string(description)
            )
        if code > max_code then max_code = code end
    end

    -- LUIF_ERR_RESERVED_200 is a place-holder , not expected to be actually used
    luif_error_add( 200, "LUIF_ERR_RESERVED_200", "Unexpected Kollos error: 200")
    luif_error_add( 201, "LUIF_ERR_LUA_VERSION", "Bad Lua version")
    luif_error_add( 202, "LUIF_ERR_LIBMARPA_HEADER_VERSION_MISMATCH", "Libmarpa header does not match expected version")
    luif_error_add( 203, "LUIF_ERR_LIBMARPA_LIBRARY_VERSION_MISMATCH", "Libmarpa library does not match expected version")

    io.write('#define KOLLOS_MIN_ERROR_CODE ' .. min_code .. '\n')
    io.write('#define KOLLOS_MAX_ERROR_CODE ' .. max_code .. '\n\n')
    for i = min_code, max_code
    do
        local mnemonic = code_mnemonics[i]
        if mnemonic then
            io.write(
                   string.format('#define %s %d\n', mnemonic, i)
           )
       end
    end

    io.write('\n')
    io.write('struct s_libmarpa_error_code kollos_error_codes[(KOLLOS_MAX_ERROR_CODE-KOLLOS_MIN_ERROR_CODE)+1] = {\n')
    for i = min_code, max_code do
        local code_line = code_lines[i]
        if code_line then
           io.write(code_line .. '\n')
        else
           io.write(
               string.format(
                   '    { %d, "LUIF_ERROR_RESERVED", "Unknown Kollos error %d" },\n',
                   i, i
               )
           )
        end
    end
    io.write('};\n\n');

end

-- error objects
--
-- There are written in C, but not because of efficiency --
-- efficiency is not needed, and in any case, when the overhead
-- from the use of the debug calls is considered, is not really
-- gained.
--
-- The reason for the use of C is that the error routines
-- must be available for use inside both C and Lua, and must
-- also be available as early as possible during set up.
-- It's possible to run Lua code both inside C and early in
-- the set up, but the added unclarity, complexity from issues
-- of error reporting for the Lua code, etc., etc. mean that
-- it actually is easier to write them in C than in Lua.

io.write[=[

static inline const char* error_description_by_code(lua_Integer error_code)
{
   if (error_code >= LIBMARPA_MIN_ERROR_CODE && error_code <= LIBMARPA_MAX_ERROR_CODE) {
       return libmarpa_error_codes[error_code-LIBMARPA_MIN_ERROR_CODE].description;
   }
   if (error_code >= KOLLOS_MIN_ERROR_CODE && error_code <= KOLLOS_MAX_ERROR_CODE) {
       return kollos_error_codes[error_code-KOLLOS_MIN_ERROR_CODE].description;
   }
   return (const char *)0;
}

static inline int l_error_description_by_code(lua_State* L)
{
   const lua_Integer error_code = luaL_checkinteger(L, 1);
   const char* description = error_description_by_code(error_code);
   if (description)
   {
       lua_pushstring(L, description);
   } else {
       lua_pushfstring(L, "Unknown error code (%d)", error_code);
   }
   return 1;
}
 
static inline const char* error_name_by_code(lua_Integer error_code)
{
   if (error_code >= LIBMARPA_MIN_ERROR_CODE && error_code <= LIBMARPA_MAX_ERROR_CODE) {
       return libmarpa_error_codes[error_code-LIBMARPA_MIN_ERROR_CODE].mnemonic;
   }
   if (error_code >= KOLLOS_MIN_ERROR_CODE && error_code <= KOLLOS_MAX_ERROR_CODE) {
       return kollos_error_codes[error_code-KOLLOS_MIN_ERROR_CODE].mnemonic;
   }
   return (const char *)0;
}

static inline int l_error_name_by_code(lua_State* L)
{
   const lua_Integer error_code = luaL_checkinteger(L, 1);
   const char* mnemonic = error_name_by_code(error_code);
   if (mnemonic)
   {
       lua_pushstring(L, mnemonic);
   } else {
       lua_pushfstring(L, "Unknown error code (%d)", error_code);
   }
   return 1;
}
 
/* userdata metatable keys
   The contents of these locations are never examined.
   These location are used as a key in the Lua registry.
   This guarantees that the key will be unique
   within the Lua state.
*/
static char kollos_error_mt_key;
static char kollos_g_ud_mt_key;
static char kollos_r_ud_mt_key;

/* Leaves the stack as before,
   except with the error object on top */
static inline void kollos_error(lua_State* L,
    Marpa_Error_Code code, const char* details)
{
   const int error_object_stack_ix = lua_gettop(L)+1;
   lua_newtable(L);
   /* [ ..., error_object ] */
   lua_rawgetp(L, LUA_REGISTRYINDEX, &kollos_error_mt_key);
   /* [ ..., error_object, error_metatable ] */
   lua_setmetatable(L, error_object_stack_ix);
   /* [ ..., error_object ] */
   lua_pushinteger(L, (lua_Integer)code);
   lua_setfield(L, error_object_stack_ix, "code" );
  printf ("%s %s %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__);
  printf ("%s code = %d\n", __PRETTY_FUNCTION__, code);
   /* [ ..., error_object ] */
   lua_pushstring(L, details);
   lua_setfield(L, error_object_stack_ix, "details" );
   /* [ ..., error_object ] */
}

/* Replace an error object, on top of the stack,
   with its string equivalent
 */
static inline void error_tostring(lua_State* L)
{
  Marpa_Error_Code error_code = -1;
  const int error_object_ix = lua_gettop (L);
  const char *temp_string;

  if (!lua_checkstack (L, 20))
    {
      /* This should never happen -- if it does it is
       * unrecoverable
       */
      lua_pushstring(L, "Cannot grow stack in error_to_string()");
      lua_error (L);
    }
  lua_getfield (L, error_object_ix, "string");

  /* [ ..., error_object, string ] */

  printf ("%s %s %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__);
  /* If present, a "string" overrides everything else */
  if (lua_isstring (L, -1))
    {
      lua_replace (L, error_object_ix);
      return;
    }

  /* [ ..., error_object, bad-string ] */
  lua_pop (L, 1);
  /* [ ..., error_object ] */

  lua_getfield (L, error_object_ix, "details");
  /* [ ..., error_object, details ] */
  if (lua_isstring (L, -1))
    {
      lua_pushstring (L, ": ");
    }
  else
    {
      lua_pop (L, 1);
    }

  /* [ ..., error_object ] */
  lua_getfield (L, error_object_ix, "code");
  /* [ ..., error_object, code ] */
  printf ("%s %s %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__);
  if (!lua_isnumber (L, -1))
    {
      lua_pop (L, 1);
      lua_pushstring (L, "[No error code] ");
    }
  else
    {
      error_code = lua_tointeger (L, -1);
      temp_string = lua_tostring (L, -1);
      lua_pushstring (L, " ");
    }

  temp_string = error_name_by_code (error_code);
  if (temp_string)
    {
      lua_pushstring (L, temp_string);
    }
  else
    {
      lua_pushfstring (L, "Unknown error code (%d)", (int) error_code);
    }
  lua_pushstring (L, " ");

  temp_string = error_description_by_code (error_code);
  lua_pushstring (L, temp_string ? temp_string : "[no description]");
  lua_pushstring (L, " ");

  printf ("%s %s %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__);
  lua_concat (L, lua_gettop (L) - error_object_ix);
  lua_replace (L, error_object_ix);
}
  
static inline int kollos_throw(lua_State* L,
    Marpa_Error_Code code, const char* details)
{
   kollos_error(L, code, details);
   error_tostring(L);
   return lua_error(L);
}

/* not safe - intended for internal use */
static inline int wrap_kollos_throw(lua_State* L)
{
   const Marpa_Error_Code code = lua_tointeger(L, 1);
   const char* details = lua_tostring(L, 2);
   return kollos_throw(L, code, details);
   /* NOTREACHED */
}

static int l_error_tostring(lua_State* L)
{
   /* [ error_object ] */
   error_tostring(L);
   /* [ error_string ] */
  return 1;
}
  
]=]
<commit_msg>Work on JSON parser<commit_after>-- This is meta-programming -- Lua code that writes C code.
-- Meta-programming introduces a lot of additonal complexity
-- for each line, but in this case the automated generation
-- of Lua wrappers for the libmarpa methods, alone,
-- allows the elimination of thousands of lines of code.

-- assumes that, when called, out_file to set to output file
local error_file

function c_safe_string (s)
    s = string.gsub(s, '"', '\\034')
    s = string.gsub(s, '\\', '\\092')
    return '"' .. s .. '"'
end

for k,v in ipairs(arg) do
   if not v:find("=")
   then return nil, "Bad options: ", arg end
   local id, val = v:match("^([^=]+)%=(.*)") -- no space around =
   if id == "out" then io.output(val)
   elseif id == "errors" then error_file = val
   else return nil, "Bad id in options: ", id end
end

-- initial piece
io.write[=[
/*
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
*/

#define LUA_LIB
#include "marpa.h"
#include "lua.h"
#include "lauxlib.h"

#include "compat-5.2.c"

#undef UNUSED
#if     __GNUC__ >  2 || (__GNUC__ == 2 && __GNUC_MINOR__ >  4)
#define UNUSED __attribute__((__unused__))
#else
#define UNUSED
#endif

#define EXPECTED_LIBMARPA_MAJOR 8
#define EXPECTED_LIBMARPA_MINOR 3
#define EXPECTED_LIBMARPA_MICRO 0

/* For debugging */
static void dump_stack (lua_State *L) {
      int i;
      int top = lua_gettop(L);
      for (i = 1; i <= top; i++) {  /* repeat for each level */
        int t = lua_type(L, i);
        switch (t) {
    
          case LUA_TSTRING:  /* strings */
            printf("`%s'", lua_tostring(L, i));
            break;
    
          case LUA_TBOOLEAN:  /* booleans */
            printf(lua_toboolean(L, i) ? "true" : "false");
            break;
    
          case LUA_TNUMBER:  /* numbers */
            printf("%g", lua_tonumber(L, i));
            break;
    
          default:  /* other values */
            printf("%s", lua_typename(L, t));
            break;
    
        }
        printf("  ");  /* put a separator */
      }
      printf("\n");  /* end the listing */
}

static void dump_table(lua_State *L, int raw_table_index)
{
    /* Original stack: [ ... ] */
    const int table_index = lua_absindex(L, raw_table_index);
    lua_pushnil(L);
    /* [ ..., nil ] */
    while (lua_next(L, table_index))
    {
        /* [ ..., key, value ] */
        const int value_stack_ix = lua_gettop(L);
        const int key_stack_ix = lua_gettop(L)+1;
        /* Copy the key, because lua_tostring() can be destructive */
        lua_pushvalue(L, -2);
        /* [ ..., key, value, key_copy ] */
        switch (lua_type(L, key_stack_ix)) {
    
          case LUA_TSTRING:  /* strings */
            printf("`%s'", lua_tostring(L, key_stack_ix));
            break;
    
          case LUA_TBOOLEAN:  /* booleans */
            printf(lua_toboolean(L, key_stack_ix) ? "true" : "false");
            break;
    
          case LUA_TNUMBER:  /* numbers */
            printf("%g", lua_tonumber(L, key_stack_ix));
            break;
    
          case LUA_TTABLE:  /* numbers */
            printf("table %s", lua_tostring(L, key_stack_ix));
            break;
    
          default:  /* other values */
            printf("%s", lua_typename(L, lua_type(L, key_stack_ix)));
            break;
    
        }
        printf(" -> ");  /* end the listing */
        switch (lua_type(L, value_stack_ix)) {
    
          case LUA_TSTRING:  /* strings */
            printf("`%s'", lua_tostring(L, value_stack_ix));
            break;
    
          case LUA_TBOOLEAN:  /* booleans */
            printf(lua_toboolean(L, value_stack_ix) ? "true" : "false");
            break;
    
          case LUA_TNUMBER:  /* numbers */
            printf("%g", lua_tonumber(L, value_stack_ix));
            break;
    
          case LUA_TTABLE:  /* numbers */
            printf("table %s", lua_tostring(L, value_stack_ix));
            break;
    
          default:  /* other values */
            printf("%s", lua_typename(L, lua_type(L, value_stack_ix)));
            break;
    
        }
        printf("\n");  /* end the listing */
        /* [ ..., key, value, key_copy ] */
        lua_pop(L, 2);
        /* [ ..., key ] */
    }
    /* Back to original stack: [ ... ] */
}

]=]

-- error codes

io.write[=[
struct s_libmarpa_error_code {
   lua_Integer code;
   const char* mnemonic;
   const char* description;
};

]=]

do
    local f = assert(io.open(error_file, "r"))
    local code_lines = {}
    local code_mnemonics = {}
    local max_code = 0
    while true do
        local line = f:read()
        if line == nil then break end
        local i, j = string.find(line, "#")
        if (i == nil) then stripped = line
        else stripped = string.sub(line, 0, i-1)
        end
        if string.find(stripped, "%S") then
            local raw_code
            local raw_mnemonic
            local description
            _, _, raw_code, raw_mnemonic, description = string.find(stripped, "^(%d+)%sMARPA_ERR_(%S+)%s(.*)$")
            local code = tonumber(raw_code)
            if description == nil then return nil, "Bad line in error code file ", line end
            if code > max_code then max_code = code end
            local mnemonic = 'LUIF_ERR_' .. raw_mnemonic
            code_mnemonics[code] = mnemonic
            code_lines[code] = string.format( '   { %d, %s, %s },',
                code,
                c_safe_string(mnemonic),
                c_safe_string(description)
                )
        end
    end

    io.write('#define LIBMARPA_MIN_ERROR_CODE 0\n')
    io.write('#define LIBMARPA_MAX_ERROR_CODE ' .. max_code .. '\n\n')

    for i = 0, max_code do
        local mnemonic = code_mnemonics[i]
        if mnemonic then
            io.write(
                   string.format('#define %s %d\n', mnemonic, i)
           )
       end
    end
    io.write('\n')
    io.write('struct s_libmarpa_error_code libmarpa_error_codes[LIBMARPA_MAX_ERROR_CODE-LIBMARPA_MIN_ERROR_CODE+1] = {\n')
    for i = 0, max_code do
        local code_line = code_lines[i]
        if code_line then
           io.write(code_line .. '\n')
        else
           io.write(
               string.format(
                   '    { %d, "LUIF_ERROR_RESERVED", "Unknown Libmarpa error %d" },\n',
                   i, i
               )
           )
        end
    end
    io.write('};\n\n');
    f:close()
end

-- for Kollos's own (that is, non-Libmarpa) error codes
do
    local code_lines = {}
    local code_mnemonics = {}
    local min_code = 200
    local max_code = 200

    -- Should add some checks on the errors, checking for
    -- 1.) duplicate mnenomics
    -- 2.) duplicate error codes

    function luif_error_add (code, mnemonic, description)
        code_mnemonics[code] = mnemonic
        code_lines[code] = string.format( '   { %d, %s, %s },',
            code,
            c_safe_string(mnemonic),
            c_safe_string(description)
            )
        if code > max_code then max_code = code end
    end

    -- LUIF_ERR_RESERVED_200 is a place-holder , not expected to be actually used
    luif_error_add( 200, "LUIF_ERR_RESERVED_200", "Unexpected Kollos error: 200")
    luif_error_add( 201, "LUIF_ERR_LUA_VERSION", "Bad Lua version")
    luif_error_add( 202, "LUIF_ERR_LIBMARPA_HEADER_VERSION_MISMATCH", "Libmarpa header does not match expected version")
    luif_error_add( 203, "LUIF_ERR_LIBMARPA_LIBRARY_VERSION_MISMATCH", "Libmarpa library does not match expected version")

    io.write('#define KOLLOS_MIN_ERROR_CODE ' .. min_code .. '\n')
    io.write('#define KOLLOS_MAX_ERROR_CODE ' .. max_code .. '\n\n')
    for i = min_code, max_code
    do
        local mnemonic = code_mnemonics[i]
        if mnemonic then
            io.write(
                   string.format('#define %s %d\n', mnemonic, i)
           )
       end
    end

    io.write('\n')
    io.write('struct s_libmarpa_error_code kollos_error_codes[(KOLLOS_MAX_ERROR_CODE-KOLLOS_MIN_ERROR_CODE)+1] = {\n')
    for i = min_code, max_code do
        local code_line = code_lines[i]
        if code_line then
           io.write(code_line .. '\n')
        else
           io.write(
               string.format(
                   '    { %d, "LUIF_ERROR_RESERVED", "Unknown Kollos error %d" },\n',
                   i, i
               )
           )
        end
    end
    io.write('};\n\n');

end

-- error objects
--
-- There are written in C, but not because of efficiency --
-- efficiency is not needed, and in any case, when the overhead
-- from the use of the debug calls is considered, is not really
-- gained.
--
-- The reason for the use of C is that the error routines
-- must be available for use inside both C and Lua, and must
-- also be available as early as possible during set up.
-- It's possible to run Lua code both inside C and early in
-- the set up, but the added unclarity, complexity from issues
-- of error reporting for the Lua code, etc., etc. mean that
-- it actually is easier to write them in C than in Lua.

io.write[=[

static inline const char* error_description_by_code(lua_Integer error_code)
{
   if (error_code >= LIBMARPA_MIN_ERROR_CODE && error_code <= LIBMARPA_MAX_ERROR_CODE) {
       return libmarpa_error_codes[error_code-LIBMARPA_MIN_ERROR_CODE].description;
   }
   if (error_code >= KOLLOS_MIN_ERROR_CODE && error_code <= KOLLOS_MAX_ERROR_CODE) {
       return kollos_error_codes[error_code-KOLLOS_MIN_ERROR_CODE].description;
   }
   return (const char *)0;
}

static inline int l_error_description_by_code(lua_State* L)
{
   const lua_Integer error_code = luaL_checkinteger(L, 1);
   const char* description = error_description_by_code(error_code);
   if (description)
   {
       lua_pushstring(L, description);
   } else {
       lua_pushfstring(L, "Unknown error code (%d)", error_code);
   }
   return 1;
}
 
static inline const char* error_name_by_code(lua_Integer error_code)
{
   if (error_code >= LIBMARPA_MIN_ERROR_CODE && error_code <= LIBMARPA_MAX_ERROR_CODE) {
       return libmarpa_error_codes[error_code-LIBMARPA_MIN_ERROR_CODE].mnemonic;
   }
   if (error_code >= KOLLOS_MIN_ERROR_CODE && error_code <= KOLLOS_MAX_ERROR_CODE) {
       return kollos_error_codes[error_code-KOLLOS_MIN_ERROR_CODE].mnemonic;
   }
   return (const char *)0;
}

static inline int l_error_name_by_code(lua_State* L)
{
   const lua_Integer error_code = luaL_checkinteger(L, 1);
   const char* mnemonic = error_name_by_code(error_code);
   if (mnemonic)
   {
       lua_pushstring(L, mnemonic);
   } else {
       lua_pushfstring(L, "Unknown error code (%d)", error_code);
   }
   return 1;
}
 
/* userdata metatable keys
   The contents of these locations are never examined.
   These location are used as a key in the Lua registry.
   This guarantees that the key will be unique
   within the Lua state.
*/
static char kollos_error_mt_key;
static char kollos_g_ud_mt_key;
static char kollos_r_ud_mt_key;

/* Leaves the stack as before,
   except with the error object on top */
static inline void kollos_error(lua_State* L,
    Marpa_Error_Code code, const char* details)
{
   const int error_object_stack_ix = lua_gettop(L)+1;
   lua_newtable(L);
   /* [ ..., error_object ] */
   lua_rawgetp(L, LUA_REGISTRYINDEX, &kollos_error_mt_key);
   /* [ ..., error_object, error_metatable ] */
   lua_setmetatable(L, error_object_stack_ix);
   /* [ ..., error_object ] */
   lua_pushinteger(L, (lua_Integer)code);
   lua_setfield(L, error_object_stack_ix, "code" );
  printf ("%s %s %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__);
  printf ("%s code = %d\n", __PRETTY_FUNCTION__, code);
   /* [ ..., error_object ] */
   lua_pushstring(L, details);
   lua_setfield(L, error_object_stack_ix, "details" );
   /* [ ..., error_object ] */
}

/* Replace an error object, on top of the stack,
   with its string equivalent
 */
static inline void error_tostring(lua_State* L)
{
  Marpa_Error_Code error_code = -1;
  const int error_object_ix = lua_gettop (L);
  const char *temp_string;

  if (!lua_checkstack (L, 20))
    {
      /* This should never happen -- if it does it is
       * unrecoverable
       */
      lua_pushstring (L, "Cannot grow stack in error_to_string()");
      lua_error (L);
    }
  lua_getfield (L, error_object_ix, "string");

  /* [ ..., error_object, string ] */

  printf ("%s %s %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__);
  /* If present, a "string" overrides everything else */
  if (lua_isstring (L, -1))
    {
      lua_replace (L, error_object_ix);
      return;
    }

  /* [ ..., error_object, bad-string ] */
  lua_pop (L, 1);
  /* [ ..., error_object ] */

  lua_getfield (L, error_object_ix, "details");
  /* [ ..., error_object, details ] */
  if (lua_isstring (L, -1))
    {
      lua_pushstring (L, ": ");
    }
  else
    {
      lua_pop (L, 1);
    }

  /* [ ..., error_object ] */
  lua_getfield (L, error_object_ix, "code");
  /* [ ..., error_object, code ] */
  printf ("%s %s %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__);
  if (!lua_isnumber (L, -1))
    {
      lua_pop (L, 1);
      lua_pushstring (L, "[No error code]");
    }
  else
    {
      error_code = lua_tointeger (L, -1);
      /* Concatenation will eventually convert a numeric
       * code on top of the stack to a string, so we do
       * nothing with it here.
       */
    }

  lua_pushstring (L, " ");	/* Add space separator */

  temp_string = error_name_by_code (error_code);
  if (temp_string)
    {
      lua_pushstring (L, temp_string);
    }
  else
    {
      lua_pushfstring (L, "Unknown error code (%d)", (int) error_code);
    }
  lua_pushstring (L, " ");	/* Add space separator */

  temp_string = error_description_by_code (error_code);
  lua_pushstring (L, temp_string ? temp_string : "[no description]");
  lua_pushstring (L, "\n");	/* Add space separator */

  printf ("%s %s %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__);
  lua_concat (L, lua_gettop (L) - error_object_ix);
  /* stack is [ ..., error_object, concatenated_result ] */
  lua_replace (L, error_object_ix);
  /* [ ..., concatenated_result ] */
}
  
static inline int kollos_throw(lua_State* L,
    Marpa_Error_Code code, const char* details)
{
   kollos_error(L, code, details);
   error_tostring(L);
   return lua_error(L);
}

/* not safe - intended for internal use */
static inline int wrap_kollos_throw(lua_State* L)
{
   const Marpa_Error_Code code = lua_tointeger(L, 1);
   const char* details = lua_tostring(L, 2);
   return kollos_throw(L, code, details);
   /* NOTREACHED */
}

static int l_error_tostring(lua_State* L)
{
   /* [ error_object ] */
   error_tostring(L);
   /* [ error_string ] */
  return 1;
}
  
]=]

{'fraction_non_alphanumeric': 0.12222684636808913, 'fraction_numerical': 0.004834010948554599, 'mean_word_length': 2.487551635592274, 'pattern_counts': {'":': 2, '<': 13, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5296683', 'n_tokens_mistral': 10839, 'n_tokens_neox': 10338, 'n_words': 2853}