版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/guzhou_diaoke/article/details/85314508
1)metatable and metamethod
class LuaTable:
def __init__(self, narr, nrec):
self.arr = None
self.map = None
self.metatable = None
if narr > 0:
self.arr = []
if nrec > 0:
self.map = {}
# ...
def has_metafield(self, name):
return self.metatable is not None and self.metatable.get(name) is not None
def __str__(self):
return str(self.arr) if self.arr else str(self.map)
class LuaState:
# ...
def set_metatable(self, idx):
v = self.stack.get(idx)
mt = self.stack.pop()
if mt is None:
self.set_metatable_kv(v, None)
elif isinstance(mt, LuaTable):
self.set_metatable_kv(v, mt)
else:
raise Exception('table expected!')
def set_metatable_kv(self, val, mt):
if isinstance(val, LuaTable):
val.metatable = mt
else:
key = '_MT' + LuaValue.type_of(val)
self.registry.put(key, mt)
def get_metatable(self, idx):
v = self.stack.get(idx)
mt = self.get_metatable_k(v)
if mt:
self.stack.push(mt)
return True
else:
return False
def get_metatable_k(self, val):
if isinstance(val, LuaTable):
return val.metatable
else:
key = '_MT' + str(LuaValue.type_of(val))
return self.registry.get(key)
def call_metamethod(self, a, mm, b):
self.stack.push(mm)
self.stack.push(a)
self.stack.push(b)
self.call(2, 1)
return self.stack.pop()
def get_metafield(self, val, name):
mt = self.get_metatable_k(val)
if mt is not None:
return mt.get(name)
return None
def get_metamethod(self, a, b, name):
metamethod = self.get_metafield(a, name)
if not metamethod:
metamethod = self.get_metafield(b, name)
return metamethod
2)arithmetic
class Arithmetic:
operator = namedtuple('operator', ['metamethod', 'integer_func', 'float_func'])
operators = {
ArithOp.ADD: operator('__add', add, add),
ArithOp.SUB: operator('__sub', sub, sub),
ArithOp.MUL: operator('__mul', mul, mul),
ArithOp.MOD: operator('__mod', mod, mod),
ArithOp.POW: operator('__pow', None, pow),
ArithOp.DIV: operator('__div', None, div),
ArithOp.IDIV: operator('__fdiv', fdiv, fdiv),
ArithOp.BAND: operator('__band', band, None),
ArithOp.BOR: operator('__bor', bor, None),
ArithOp.BXOR: operator('__bxor', bxor, None),
ArithOp.SHL: operator('__shl', shl, None),
ArithOp.SHR: operator('__shr', shr, None),
ArithOp.UNM: operator('__unm', unm, unm),
ArithOp.BNOT: operator('__bnot', bnot, None)
}
def arith(self, op):
b = self.stack.pop()
a = self.stack.pop() if (op != ArithOp.UNM and op != ArithOp.BNOT) else b
result = Arithmetic.arith(a, op, b)
if result is None:
name = Arithmetic.operators[op].metamethod
metamethod = self.get_metamethod(a, b, name)
result = self.call_metamethod(a, metamethod, b) if metamethod else None
assert(result is not None)
self.stack.push(result)
3) len,concat
def len(self, idx):
val = self.stack.get(idx)
assert(val is not None)
if isinstance(val, str):
self.stack.push(len(val))
return
metamethod = self.get_metamethod(val, val, '__len')
if metamethod is not None:
self.stack.push(self.call_metamethod(val, metamethod, val))
return
if isinstance(val, LuaTable):
self.stack.push(len(val))
return
raise Exception('length error')
def concat(self, n):
if n == 0:
self.stack.push('')
elif n >= 2:
for i in range(1, n):
if self.is_string(-1) and self.is_string(-2):
s2 = self.to_string(-1)
s1 = self.to_string(-2)
self.stack.pop()
self.stack.pop()
self.stack.push(s1+s2)
continue
b = self.stack.pop()
a = self.stack.pop()
mm = self.get_metamethod(a, b, '__concat')
if mm:
self.stack.push(self.call_metamethod(a, mm, b))
continue
raise Exception('concatenation error!')
4)compare
class Compare:
@staticmethod
def eq(a, b, ls):
if a is None:
return b is None
if isinstance(a, bool) or isinstance(a, str):
return a == b
if isinstance(a, int):
if isinstance(b, int):
return a == b
elif isinstance(b, float):
return float(a) == b
else:
return False
if isinstance(a, float):
if isinstance(b, float):
return a == b
elif isinstance(b, int):
return a == float(b)
else:
return False
if isinstance(a, LuaTable):
if isinstance(b, LuaTable) and a != b and ls:
mm = ls.get_metamethod(a, b, '__eq')
if mm:
return LuaValue.to_boolean(ls.call_metamethod(a, mm, b))
return a == b
@staticmethod
def lt(a, b, ls):
if isinstance(a, str) and isinstance(b, str):
return a < b
if isinstance(a, int):
if isinstance(b, int):
return a < b
elif isinstance(b, float):
return float(a) < b
if isinstance(a, float):
if isinstance(b, float):
return a < b
elif isinstance(b, int):
return a < float(b)
mm = ls.get_metamethod(a, b, '__lt')
if mm:
return LuaValue.to_boolean(ls.call_metamethod(a, mm, b))
raise Exception('Comparison Error')
@staticmethod
def le(a, b, ls):
if isinstance(a, str) and isinstance(b, str):
return a <= b
if isinstance(a, int):
if isinstance(b, int):
return a <= b
elif isinstance(b, float):
return float(a) <= b
if isinstance(a, float):
if isinstance(b, float):
return a <= b
elif isinstance(b, int):
return a <= float(b)
mm = ls.get_metamethod(a, b, '__le')
if mm:
return LuaValue.to_boolean(ls.call_metamethod(a, mm, b))
mm = ls.get_metamethod(b, a, '__lt')
if mm:
return LuaValue.to_boolean(ls.call_metamethod(a, mm, b))
raise Exception('Comparison Error')
def compare(self, idx1, op, idx2):
if not self.stack.is_valid(idx1) or not self.stack.is_valid(idx2):
return False
a = self.stack.get(idx1)
b = self.stack.get(idx2)
if op == CmpOp.EQ:
return Compare.eq(a, b, self)
elif op == CmpOp.LT:
return Compare.lt(a, b, self)
elif op == CmpOp.LE:
return Compare.le(a, b, self)
5)__index, __newindex
def get_table_val(self, t, k, raw):
if isinstance(t, LuaTable):
v = t.get(k)
if raw or (v is not None) or (not t.has_metafield('__index')):
return v
if not raw:
mf = self.get_metafield(t, '__index')
if mf:
if isinstance(mf, LuaTable):
return self.get_table_val(mf, k, False)
elif isinstance(mf, Closure):
v = self.call_metamethod(t, mf, k)
return v
raise Exception('not a table')
def get_table(self, idx):
t = self.stack.get(idx)
k = self.stack.pop()
v = self.get_table_val(t, k, False)
self.stack.push(v)
return LuaValue.type_of(v)
def get_i(self, idx, i):
t = self.stack.get(idx)
v = self.get_table_val(t, i, False)
self.stack.push(v)
return LuaValue.type_of(v)
def set_table(self, idx):
t = self.stack.get(idx)
v = self.stack.pop()
k = self.stack.pop()
self.set_table_kv(t, k, v, False)
def set_table_kv(self, t, k, v, raw):
if isinstance(t, LuaTable):
if raw or t.get(k) or not t.has_metafield('__newindex'):
t.put(k, v)
return
if not raw:
mf = self.get_metafield(t, '__newindex')
if mf:
if isinstance(mf, LuaTable):
self.set_table_kv(mf, k, v, False)
return
if isinstance(mf, Closure):
self.stack.push(mf)
self.stack.push(t)
self.stack.push(k)
self.stack.push(v)
self.call(3, 0)
return
raise Exception('not a table')
def set_field(self, idx, k):
t = self.stack.get(idx)
v = self.stack.pop()
self.set_table_kv(t, k, v, False)
def set_i(self, idx, i):
t = self.stack.get(idx)
v = self.stack.pop()
self.set_table_kv(t, i, v, False)
6)__call
def call(self, nargs, nresults):
val = self.stack.get(-(nargs+1))
f = val if isinstance(val, Closure) else None
if f is None:
metamethod = self.get_metafield(val, '__call')
if metamethod and isinstance(metamethod, Closure):
self.stack.push(val)
self.insert(-(nargs+2))
nargs += 1
f = metamethod
if f:
if f.proto:
self.call_lua_closure(nargs, nresults, f)
else:
self.call_py_closure(nargs, nresults, f)
else:
raise Exception(f, 'is not a function')
7)test
from lua_state import LuaState
def py_print(ls):
nargs = ls.get_top()
for i in range(1, nargs+1):
if ls.is_boolean(i):
print('true' if ls.to_boolean(i) else 'false', end='')
elif ls.is_string(i):
print(ls.to_string(i), end='')
else:
print(ls.type_name(ls.type(i)), end='')
if i < nargs:
print('\t', end='')
print()
return 0
def get_metatable(ls):
if not ls.get_metatable:
ls.push_nil()
return 1
def set_metatable(ls):
ls.set_metatable(1)
return 1
def main():
with open('./test/vector.luac', 'rb') as f:
data = f.read()
ls = LuaState()
ls.register('print', py_print)
ls.register('getmetatable', get_metatable)
ls.register('setmetatable', set_metatable)
ls.load(data)
ls.call(0, 0)
if __name__ == '__main__':
main()
8)result
[1, 2]
[3, 4]
[2, 4]
[3, 6]
5.0
false
true
[3, 6]