from tpers import * from scripting import Fail class Vars(TPrs): def InitPersistent(self): self.rgmpsynt_sobj = [{}] # [0] is for globals? def Get(self, syntVar, synt): for mpsynt_sobj in reversed(self.rgmpsynt_sobj): try: return mpsynt_sobj[syntVar] except: pass return Fail("Variable " + synt.name.St() + " not defined", synt) def Define(self, synt, sobj): self.rgmpsynt_sobj[-1][synt] = sobj def Set(self, syntVar, sobj, synt): if syntVar == None: return Fail("No variable specified to set", None, synt) for mpsynt_sobj in reversed(self.rgmpsynt_sobj): if syntVar in mpsynt_sobj: mpsynt_sobj[syntVar] = sobj return return Fail("Variable " + syntVar.name.St() + " not defined", synt) def Push(self): self.rgmpsynt_sobj.append({}) def Pop(self): self.rgmpsynt_sobj.pop() # instr: can be a synt or a callable. If instr is a synt, the VM will push the current list onto the rstack with its associated # ip (instruction pointer), compile the synt, and begin executing it (Vm.CallI). If instr is a callable, the vm will call it; if it # returns a synt, the synt is compiled and called. class Vm(TPrs): def InitPersistent(self): self.vstack = [] self.rstack = [] self.vars = Vars() self.rgfail = [] def Push(self, sobj): self.vstack.append(sobj) def Pop(self): return self.vstack.pop() def Log(self, fail): if fail != None: self.rgfail.append(fail) def CallI(self, synt, r, ip): rNew = synt.Compile() if len(rNew) == 0: # no-op optimization return (r, ip) if ip < len(r): # tail call optimization self.rstack.append((r, ip)) return (rNew, 0) def RunSynt(self, synt): r = synt.Compile() ip = 0 while True: if ip < len(r): instr = r[ip] ip += 1 if hasattr(instr, "Compile"): (r, ip) = self.CallI(instr, r, ip) elif callable(instr): synt = instr(self) if synt != None: (r, ip) = self.CallI(synt, r, ip) else: assert False, "invalid instr " + str(instr) else: if len(self.rstack) == 0: break (r, ip) = self.rstack.pop()