luapy (10) metatable and metamethod

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]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值