| Modifying Lua | 
 | 
__settable) that will be called anytime a value of a table is changed.
__settable, if it exists, will override __newindex.
This is best illustrated below. We want to modify Lua so that the following code will run as shown.
test1.lua
function configmodified(t,k,v) print("Config Modified, call reload") rawset(t,k,v) end config = { maxthreads = 10, minthreads = 2 } setmetatable(config, {__setindex = configmodified})
And the results
D:\code\lua\mod\bin>lua -i test1.lua Lua 5.1.1 Copyright (C) 1994-2006 Lua.org, PUC-Rio table: 00940180 > =config.minthreads 2 > config.minthreads = 3 Config Modified, call reload > =config.minthreads 3 > config.sleepdelay = 100 Config Modified, call reload
The internal code calls metamethods 'tag methods' the base list is located in ltm.h
/*
* WARNING: if you change the order of this enumeration,
* grep "ORDER TM"
*/
typedef enum {
  TM_INDEX,
  TM_NEWINDEX,
  TM_GC,
  TM_MODE,
  TM_EQ,  /* last tag method with `fast' access */
  TM_ADD,
  TM_SUB,
  TM_MUL,
  TM_DIV,
  TM_MOD,
  TM_POW,
  TM_UNM,
  TM_LEN,
  TM_LT,
  TM_LE,
  TM_CONCAT,
  TM_CALL,
  TM_N		/* number of elements in the enum */
} TMS;
The comment of TM_EQ is pretty important. Since setting a value needs to be high performance, we need to look at this.
The way 'fast' access works is by storing a flag on each table and marking if the flag exists.
Tables are defined in lobject.h
typedef struct Table {
  CommonHeader;
  lu_byte flags;  /* 1<<p means tagmethod(p) is not present */ 
  lu_byte lsizenode;  /* log2 of size of `node' array */
  struct Table *metatable;
  TValue *array;  /* array part */
  Node *node;
  Node *lastfree;  /* any free position is before this position */
  GCObject *gclist;
  int sizearray;  /* size of `array' array */
} Table;
Table.flags is 1 byte, since each flag takes 1 bit, we can have up to 8 'fast access' metamethods. Base Lua comes with 5 setup as fast access, giving us room for 3 more. We will use one of them for SETINDEX. as such
ltm.h - mods
typedef enum {
  TM_INDEX,
  TM_NEWINDEX,
  TM_SETINDEX,	// AA - new metatag that is called everytime a value is set, its use disables TM_NEWINDEX'''
  TM_GC,
  TM_MODE,
  TM_EQ,  /* last tag method with `fast' access */
  ...
We also need to give this new tag a name this is found inside
ltm.c - luaT_init
  static const char *const luaT_eventname[] = {  /* ORDER TM */
    "__index", "__newindex", 
    "__gc", "__mode", "__eq",
    "__add", "__sub", "__mul", "__div", "__mod",
    "__pow", "__unm", "__len", "__lt", "__le",
    "__concat", "__call"
  };
We will change it to
  static const char *const luaT_eventname[] = {  /* ORDER TM */
    "__index", "__newindex", "__setindex",   // AA - Added name for setindex
    "__gc", "__mode", "__eq",
    ...
Finally we need to make this code work. Most of the heavy work occurs in lvm.c Standard calls for setting values (not rawset) go through luaV_settable. Lets look
void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
  int loop;
  for (loop = 0; loop < MAXTAGLOOP; loop++) {
    const TValue *tm;
    if (ttistable(t)) {  /* `t' is a table? */
      Table *h = hvalue(t);
      TValue *oldval = luaH_set(L, h, key); /* do a primitive set */
      if (!ttisnil(oldval) ||  /* result is no nil? */
          (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
        setobj2t(L, oldval, val);
        luaC_barriert(L, h, val);
        return;
      }
      /* else will try the tag method */
    }
    else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
      luaG_typeerror(L, t, "index");
    if (ttisfunction(tm)) {
      callTM(L, tm, t, key, val);
      return;
    }
    t = tm;  /* else repeat with `tm' */ 
  }
  luaG_runerror(L, "loop in settable");
}
We will modify this like so.
void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
  int loop;
  for (loop = 0; loop < MAXTAGLOOP; loop++) {
    const TValue *tm;
    if (ttistable(t)) {  /* `t' is a table? */
      Table *h = hvalue(t);
      TValue *oldval;	// AA -- Have to declare this here
	  // AA - Our new code here
	  tm = fasttm(L, h->metatable, TM_SETINDEX);
	  if(tm != NULL)  {
		if (ttisfunction(tm)) {
		  callTM(L, tm, t, key, val);
		  return;
		}
	  }
      oldval = luaH_set(L, h, key); /* do a primitive set */
      if (!ttisnil(oldval) ||  /* result is no nil? */
          (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
        setobj2t(L, oldval, val);
        luaC_barriert(L, h, val);
        return;
      }
      ...
Now we compile and test. The code at top should now work as advertised.
Do not alter the numbers in LUA_VERSION or LUA_RELEASE! Append a string such as '(Kirk3)' . --lhf
One of the problems with this is that every set takes an additional test. This can be mitigated somewhat with the following design considerations.
---
There are papers describing the implementation of Lua[1]. See also A No-Frills Introduction to Lua 5.1 VM Instructions[2]. Yueliang[3] (an implementation of Lua in Lua) can also be helpful.
Alternatives to modifying Lua yourself include MetaLua[4] and LuaTokenParsing. You may not need to even modify Lua given Lua's powerful metaprogramming capabilities (see in particular CodeGeneration).