Flesh out API for failure
This commit is contained in:
parent
b9dd53eea1
commit
e80826de9e
52
scripting.py
52
scripting.py
|
@ -5,6 +5,8 @@ from util import *
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from vm import *
|
from vm import *
|
||||||
|
|
||||||
|
# sobj -- generic hungarian for any object that is usable in scripts
|
||||||
|
# stmt -- statement; a synt that can be used as a line in a SyntBlock
|
||||||
def RegStmt(clsStmt):
|
def RegStmt(clsStmt):
|
||||||
SyntLine.rgclsStmt.append(clsStmt)
|
SyntLine.rgclsStmt.append(clsStmt)
|
||||||
return clsStmt
|
return clsStmt
|
||||||
|
@ -166,10 +168,9 @@ class Synt(Typeable):
|
||||||
"Project the current synt into its corresponding pws."
|
"Project the current synt into its corresponding pws."
|
||||||
pass
|
pass
|
||||||
def Compile(self):
|
def Compile(self):
|
||||||
"Return a list of {synt|callable}s to be interpreted."
|
"Return a list of instrs to be interpreted. See vm.py for details on what an instr can be."
|
||||||
if hasattr(self, "Eval"):
|
return [self.Eval]
|
||||||
return [self.Eval]
|
|
||||||
return []
|
|
||||||
|
|
||||||
# desc - list of desces
|
# desc - list of desces
|
||||||
# desce:
|
# desce:
|
||||||
|
@ -267,7 +268,9 @@ class SyntExpr(Synt):
|
||||||
|
|
||||||
def Project(self, pcur):
|
def Project(self, pcur):
|
||||||
self.syntParent.ProjectTypeinForChild(pcur.PwHoriz(self), self, " ")
|
self.syntParent.ProjectTypeinForChild(pcur.PwHoriz(self), self, " ")
|
||||||
|
def Eval(self, vm):
|
||||||
|
Fail.Push(vm, "Empty expression", self)
|
||||||
|
|
||||||
class SyntLine(SyntDesc):
|
class SyntLine(SyntDesc):
|
||||||
rgclsStmt = []
|
rgclsStmt = []
|
||||||
desc = [[" "]]
|
desc = [[" "]]
|
||||||
|
@ -276,6 +279,8 @@ class SyntLine(SyntDesc):
|
||||||
for clsStmt in cls.rgclsStmt:
|
for clsStmt in cls.rgclsStmt:
|
||||||
if clsStmt.StForTypein().lower().startswith(st.lower()):
|
if clsStmt.StForTypein().lower().startswith(st.lower()):
|
||||||
yield clsStmt()
|
yield clsStmt()
|
||||||
|
def Compile(self):
|
||||||
|
return []
|
||||||
|
|
||||||
class SyntBlock(Synt):
|
class SyntBlock(Synt):
|
||||||
def Project(self, pcur):
|
def Project(self, pcur):
|
||||||
|
@ -358,7 +363,7 @@ class SyntVarRef(Synt):
|
||||||
def Project(self, pcur):
|
def Project(self, pcur):
|
||||||
self.syntParent.ProjectTypeinForChild(pcur.PwHoriz(self), self, self.StForTypein())
|
self.syntParent.ProjectTypeinForChild(pcur.PwHoriz(self), self, self.StForTypein())
|
||||||
def Eval(self, vm):
|
def Eval(self, vm):
|
||||||
vm.Push(vm.vars.Get(self.syntVar))
|
vm.Push(vm.vars.Get(self.syntVar, self))
|
||||||
|
|
||||||
class SyntBinOp(SyntDesc):
|
class SyntBinOp(SyntDesc):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -370,16 +375,16 @@ class SyntBinOp(SyntDesc):
|
||||||
def Create(cls, op, rtype, dgEval = None, stNameI = None):
|
def Create(cls, op, rtype, dgEval = None, stNameI = None):
|
||||||
def Decorate(dgEval):
|
def Decorate(dgEval):
|
||||||
stName = stNameI or dgEval.__name__
|
stName = stNameI or dgEval.__name__
|
||||||
def Eval(vm):
|
def Eval(self, vm):
|
||||||
right = vm.Pop()
|
right = vm.Pop()
|
||||||
left = vm.Pop()
|
left = vm.Pop()
|
||||||
try:
|
try:
|
||||||
vm.Push(dgEval(left, right))
|
vm.Push(dgEval(left, right))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
vm.Push(Fail("Failed to " + op + ": " + e))
|
Fail.Push(vm, "Failed to " + op + ": " + e, self)
|
||||||
def Compile(self):
|
def Compile(self):
|
||||||
return [self.left, self.right, Eval]
|
return [self.left, self.right, self.Eval]
|
||||||
clsNew = type(stName, (cls,), {"Compile": Compile, "op": op})
|
clsNew = type(stName, (cls,), {"Compile": Compile, "Eval": Eval, "op": op})
|
||||||
SyntExpr.rgclsSyntOp.append(clsNew)
|
SyntExpr.rgclsSyntOp.append(clsNew)
|
||||||
return clsNew
|
return clsNew
|
||||||
|
|
||||||
|
@ -428,13 +433,28 @@ class SyntSet(SyntDesc):
|
||||||
def Compile(self):
|
def Compile(self):
|
||||||
return [self.expr, self.Eval]
|
return [self.expr, self.Eval]
|
||||||
def Eval(self, vm):
|
def Eval(self, vm):
|
||||||
vm.vars.Set(self.lvalue.syntVar, vm.Pop())
|
val = vm.Pop()
|
||||||
# sobj -- generic hungarian for any object that is usable in scripts
|
if not isinstance(val, Fail):
|
||||||
# fail -- a FAILURE OBJECT; if it is used as a sarg when evaluating an scmd, a failure is logged and the scmd returns fail.
|
vm.Log(vm.vars.Set(self.lvalue.syntVar, vm.Pop(), self))
|
||||||
|
|
||||||
class Fail(TPrs):
|
class Fail(TPrs):
|
||||||
def InitPersistent(self, stFailure):
|
"""
|
||||||
self.stFailure == stFailure
|
Failure object. Our scripting language has no exceptions; instead, if a failure object is involved in a calculation,
|
||||||
|
that calculation fails as well. Inspired by Icon. I would like this model to have the property that the designer can
|
||||||
|
edit a script while the game is running and see the effect of their changes immediately; this suggests that failures
|
||||||
|
should be logged and the script should continue to run.
|
||||||
|
"""
|
||||||
|
def InitPersistent(self, stFailure, synt):
|
||||||
|
self.stFailure = stFailure
|
||||||
|
self.synt = synt
|
||||||
|
@staticmethod
|
||||||
|
def Push(cls, vm, stFailure, synt):
|
||||||
|
fail = Fail(stFailure, synt)
|
||||||
|
vm.Push(fail)
|
||||||
|
vm.Log(fail)
|
||||||
|
@staticmethod
|
||||||
|
def Log(cls, vm, stFailure, synt):
|
||||||
|
vm.Log(Fail(stFailure, synt))
|
||||||
|
|
||||||
# projection cursor
|
# projection cursor
|
||||||
class Pcur(object):
|
class Pcur(object):
|
||||||
|
|
34
vm.py
34
vm.py
|
@ -5,45 +5,58 @@ class Vars(TPrs):
|
||||||
def InitPersistent(self):
|
def InitPersistent(self):
|
||||||
self.rgmpsynt_sobj = [{}] # [0] is for globals?
|
self.rgmpsynt_sobj = [{}] # [0] is for globals?
|
||||||
|
|
||||||
def Get(self, synt):
|
def Get(self, syntVar, synt):
|
||||||
for mpsynt_sobj in reversed(self.rgmpsynt_sobj):
|
for mpsynt_sobj in reversed(self.rgmpsynt_sobj):
|
||||||
try:
|
try:
|
||||||
return mpsynt_sobj[synt]
|
return mpsynt_sobj[syntVar]
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return Fail("Variable " + synt.name.St() + " not defined")
|
return Fail("Variable " + synt.name.St() + " not defined", synt)
|
||||||
|
|
||||||
def Define(self, synt, sobj):
|
def Define(self, synt, sobj):
|
||||||
self.rgmpsynt_sobj[-1][synt] = sobj
|
self.rgmpsynt_sobj[-1][synt] = sobj
|
||||||
|
|
||||||
def Set(self, 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):
|
for mpsynt_sobj in reversed(self.rgmpsynt_sobj):
|
||||||
if synt in mpsynt_sobj:
|
if syntVar in mpsynt_sobj:
|
||||||
mpsynt_sobj[synt] = sobj
|
mpsynt_sobj[syntVar] = sobj
|
||||||
return
|
return
|
||||||
return Fail("Variable " + synt.name.St() + " not defined")
|
return Fail("Variable " + syntVar.name.St() + " not defined", synt)
|
||||||
|
|
||||||
def Push(self):
|
def Push(self):
|
||||||
self.rgmpsynt_sobj.append({})
|
self.rgmpsynt_sobj.append({})
|
||||||
|
|
||||||
def Pop(self):
|
def Pop(self):
|
||||||
self.rgmpsynt_sobj.pop()
|
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):
|
class Vm(TPrs):
|
||||||
def InitPersistent(self):
|
def InitPersistent(self):
|
||||||
self.vstack = []
|
self.vstack = []
|
||||||
self.rstack = []
|
self.rstack = []
|
||||||
self.vars = Vars()
|
self.vars = Vars()
|
||||||
|
self.rgfail = []
|
||||||
|
|
||||||
def Push(self, sobj):
|
def Push(self, sobj):
|
||||||
self.vstack.append(sobj)
|
self.vstack.append(sobj)
|
||||||
def Pop(self):
|
def Pop(self):
|
||||||
return self.vstack.pop()
|
return self.vstack.pop()
|
||||||
|
|
||||||
|
def Log(self, fail):
|
||||||
|
if fail != None:
|
||||||
|
self.rgfail.append(fail)
|
||||||
|
|
||||||
def CallI(self, synt, r, ip):
|
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
|
if ip < len(r): # tail call optimization
|
||||||
self.rstack.append(r, ip)
|
self.rstack.append(r, ip)
|
||||||
return (synt.Compile(), 0)
|
return (rNew, 0)
|
||||||
|
|
||||||
def RunSynt(self, synt):
|
def RunSynt(self, synt):
|
||||||
r = synt.Compile()
|
r = synt.Compile()
|
||||||
|
@ -64,6 +77,3 @@ class Vm(TPrs):
|
||||||
if len(rstack) == 0:
|
if len(rstack) == 0:
|
||||||
break
|
break
|
||||||
(r, ip) = self.rstack.pop()
|
(r, ip) = self.rstack.pop()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue