Refactor / simplify scripting syntax classes
* Introduce Stype, a generalized object for producing synts by text matching * consistently use stype to determine what can be inserted in a given place * rewrite SyntDesc to more extensively use Stypes * Introduce SyntHole, a generalized anyblob that can be put anywhere and evals to failure * Remove all data types but flags, introduce bots * Remove math * Remove compiler / VM * Remove unused methods
This commit is contained in:
parent
6790f23474
commit
4e720f0d67
|
@ -28,6 +28,13 @@ from twisted.internet import reactor
|
|||
from tpers import TPrs, TPLS, Version
|
||||
import tpers
|
||||
|
||||
|
||||
# TPrs - persistent base class; includes migration facilities and the ability to selectively opt-out of
|
||||
# being persisted. Most long-lived objects should probably be derived from TPrs.
|
||||
# Ownable - has a parent ownable; die() will be called when its parent dies.
|
||||
# Taggable - ownable, and also has tags. Assumes an eventual Game ancestor where the tag index is stored.
|
||||
# Token - taggable, and also has a tasklet that runs for as long as it's alive.
|
||||
|
||||
class Event(TPrs):
|
||||
def InitPersistent(self, game):
|
||||
self.game = game
|
||||
|
|
|
@ -1,3 +1,28 @@
|
|||
My old scripting system is an overcomplicated unusable disaster zone!!
|
||||
Let's start very simply with something like Bitsy.
|
||||
|
||||
* one layer on a board is the Object Layer; it is where the player lives
|
||||
* there is a static finite set of named objects, no dynamic creation, no querying
|
||||
* nothing happens without the player bumping into it
|
||||
|
||||
VERBS
|
||||
* Show Message <string>
|
||||
* Show Long Message (pop-up) <textarea?>
|
||||
|
||||
* If <flag> Then / Else
|
||||
* Set Flag <true/false/flag>
|
||||
|
||||
* Set Visibility of <object | layer> to <true/false/flag>
|
||||
* Move <object> <N/E/W/S>...
|
||||
|
||||
EVENTS
|
||||
* player touches object
|
||||
|
||||
Once this exists we can expand if we want, but in the meantime this would be *awesome*.
|
||||
|
||||
======
|
||||
OLD THOUGHTS
|
||||
|
||||
script quotas:
|
||||
- infinite loops are bad.
|
||||
- we do not want "new frames" to happen constantly, so a "when this {expr} happens" event will be triggered
|
||||
|
|
457
scripting.py
457
scripting.py
|
@ -18,94 +18,112 @@ from basetoken import *
|
|||
from tpers import *
|
||||
from util import *
|
||||
from contextlib import contextmanager
|
||||
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):
|
||||
SyntLine.rgclsStmt.append(clsStmt)
|
||||
StypeStmt.rgclsSynt.append(clsStmt)
|
||||
return clsStmt
|
||||
|
||||
def RegRtype(clsRtype):
|
||||
Rtype.rgclsRtype.append(clsRtype)
|
||||
return clsRtype
|
||||
class Botdef(TPrs):
|
||||
def InitPersistent(self, stName, ach, x, y):
|
||||
self.stName = stName
|
||||
self.ast = ast
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.syntOnTouch = SyntBlock()
|
||||
|
||||
class Rtype(TPrs):
|
||||
"Runtime type"
|
||||
rgclsRtype = []
|
||||
@classmethod
|
||||
def RtypeMax(cls):
|
||||
"Return an instance of this rtype which will match anything that any instance of this rtype matches."
|
||||
return cls()
|
||||
def RglitFromSt(self, st):
|
||||
"Return literal objects that match a (partially entered) string."
|
||||
return []
|
||||
def RgsyntFromSt(self, syntBlock, st):
|
||||
class Flagdef(TPrs):
|
||||
def InitPersistent(self, stName, value = True):
|
||||
self.stName = stName
|
||||
self.value = True
|
||||
|
||||
class Rbot(TPrs):
|
||||
def InitPersistent(self, botdef):
|
||||
self.botdef = botdef
|
||||
self.x = botdef.x
|
||||
self.y = botdef.y
|
||||
|
||||
class Defs(TPrs):
|
||||
def InitPersistent(self, rgflagdef = None, rgbotdef = None):
|
||||
self.rgflagdef = rgflagdef or []
|
||||
self.rgbotdef = rgbotdef or []
|
||||
|
||||
def AddFlag(self, stName, fValue):
|
||||
self.rgflagdef.append(Flagdef(stName, fValue))
|
||||
|
||||
class Vm(TPrs):
|
||||
def InitPersistent(self, defs):
|
||||
self.rgflagdef = defs.rgflagdef
|
||||
self.rgbotdef = defs.rgbotdef
|
||||
self.mpflagdef = { flagdef: flagdef.value for flagdef in defs.rgflagdef }
|
||||
self.mpbotdef = { botdef: Rbot(botdef) for botdef in defs.rgbotdef }
|
||||
def SetFlag(self, flagdef, fValue):
|
||||
self.mpflagdef[flagdef] = fValue
|
||||
def FFlagSet(self, flagdef):
|
||||
return self.mpflagdef[flagdef]
|
||||
|
||||
def Log(self, fail):
|
||||
print(fail.stMessage, fail.synt)
|
||||
|
||||
class Stype(TPrs):
|
||||
"Syntax type"
|
||||
def RgsyntFromSt(self, defs, st):
|
||||
"Return syntax objects whose rtypes match this that match a (partially entered) string."
|
||||
for lit in self.RglitFromSt(st):
|
||||
yield SyntLit(lit, self)
|
||||
return []
|
||||
|
||||
for synt in self.RgsyntVarRefFromSt(syntBlock, st):
|
||||
yield synt
|
||||
class StypeEnum(Stype):
|
||||
@classmethod
|
||||
def RgclsSynt(cls):
|
||||
return cls.rgclsSynt
|
||||
def RgsyntFromSt(self, defs, st):
|
||||
for clsStmt in self.rgclsSynt:
|
||||
if clsStmt.StForTypein().lower().startswith(st.lower()):
|
||||
yield clsStmt()
|
||||
|
||||
def RgsyntVarRefFromSt(self, syntBlock, st):
|
||||
"Return variable references that match a (partially entered) string."
|
||||
while syntBlock != None:
|
||||
if isinstance(syntBlock, SyntBlock):
|
||||
for synt in self.RgsyntFromSyntBlock(syntBlock, st):
|
||||
yield synt
|
||||
syntBlock = syntBlock.syntParent
|
||||
|
||||
# helper for RgsyntVarRefFromSt
|
||||
def RgsyntFromSyntBlock(self, syntBlock, st):
|
||||
# yield all vars in scope
|
||||
for syntVar in syntBlock.rgsynt:
|
||||
if SyntVar.FVarOfType(syntVar, self) and syntVar.name.St().lower().startswith(st.lower()):
|
||||
yield SyntVarRef(syntVar)
|
||||
class StypeStmt(StypeEnum):
|
||||
rgclsSynt = []
|
||||
|
||||
class Rtype(Stype):
|
||||
"Runtime type"
|
||||
def StForSobj(self, sobj):
|
||||
"Return a user-visible description of a sobj."
|
||||
return str(sobj)
|
||||
|
||||
def FMember(self, sobj):
|
||||
"Returns true if sobj is a member of this type."
|
||||
return isinstance(sobj, self.pythontype)
|
||||
def FOverlap(self, rtype):
|
||||
"Returns true if there is some overlap between this type and rtype."
|
||||
return isinstance(rtype, self.__class__)
|
||||
|
||||
@RegRtype
|
||||
class RtypeBool(Rtype):
|
||||
pythontype = bool
|
||||
def RglitFromSt(self, st):
|
||||
def RgsyntFromSt(self, defs, st):
|
||||
if "true".startswith(st.lower()):
|
||||
yield True
|
||||
yield SyntLit(True, self)
|
||||
if "false".startswith(st.lower()):
|
||||
yield False
|
||||
@RegRtype
|
||||
class RtypeNum(Rtype):
|
||||
pythontype = float
|
||||
def RglitFromSt(self, st):
|
||||
try:
|
||||
yield float(st)
|
||||
except:
|
||||
pass
|
||||
@RegRtype
|
||||
class RtypeString(Rtype):
|
||||
pythontype = str
|
||||
def RglitFromSt(self, st):
|
||||
if st.startswith('"') or st.startswith("'"):
|
||||
yield st[1:]
|
||||
yield SyntLit(False, self)
|
||||
|
||||
class RtypeAny(Rtype):
|
||||
def RgsyntFromSt(self, syntBlock, st):
|
||||
for clsRtype in self.rgclsRtype:
|
||||
rtype = clsRtype.RtypeMax()
|
||||
for synt in rtype.RgsyntFromSt(syntBlock, st):
|
||||
class RtypeFlag(Rtype):
|
||||
pythontype = Flagdef
|
||||
def RgsyntFromSt(self, defs, st):
|
||||
return (SyntFlagRef(flagdef) for flagdef in defs.rgflagdef if flagdef.stName.lower().startswith(st.lower()))
|
||||
|
||||
class RtypeUnion(Rtype):
|
||||
def InitPersistent(self, *rgrtype):
|
||||
self.rgrtype = rgrtype
|
||||
|
||||
def RgsyntFromSt(self, defs, st):
|
||||
for rtype in self.rgrtype:
|
||||
for synt in rtype.RgsyntFromSt(defs, st):
|
||||
yield synt
|
||||
|
||||
def StForSobj(self, sobj):
|
||||
for rtype in self.rgrtype:
|
||||
if rtype.FMember(sobj):
|
||||
return rtype.StForSobj(sobj)
|
||||
return str(sobj)
|
||||
|
||||
def FMember(self, sobj):
|
||||
return True
|
||||
def FOverlap(self, rtype):
|
||||
return True
|
||||
return any((rtype.FMember(sobj) for rtype in self.rgrtype))
|
||||
|
||||
class Typeable(TPrs):
|
||||
def InitTransient(self):
|
||||
|
@ -129,11 +147,10 @@ class Synt(Typeable):
|
|||
def SyntDefault(cls):
|
||||
return cls()
|
||||
|
||||
@classmethod
|
||||
def RgsyntReplace(cls, syntChild, st, rtype):
|
||||
return []
|
||||
def ProjectTypein(self, pcur):
|
||||
return self.syntParent.ProjectTypeinForChild(pcur.defs, pcur.PwHoriz(self), self, self.StForTypein())
|
||||
|
||||
def ProjectTypeinForChild(self, pwParent, syntChild, st):
|
||||
def ProjectTypeinForChild(self, defs, pwParent, syntChild, st):
|
||||
def OnSelect(synt, psel):
|
||||
self.Replace(syntChild, synt)
|
||||
syntChild.SetStTypein(None)
|
||||
|
@ -141,11 +158,13 @@ class Synt(Typeable):
|
|||
pwParent = PwDropdown(pwParent)
|
||||
PwTypein(pwParent, st, syntChild)
|
||||
if syntChild.GetStTypein() != None:
|
||||
for synt in self.RgsyntForChild(syntChild, syntChild.GetStTypein()):
|
||||
stype = self.StypeForChild(syntChild)
|
||||
if stype:
|
||||
for synt in stype.RgsyntFromSt(defs, syntChild.GetStTypein()):
|
||||
PwButton(pwParent, synt.StForTypein(), synt, OnSelect)
|
||||
|
||||
def RgsyntForChild(self, syntChild, st):
|
||||
return []
|
||||
def StypeForChild(self, syntChild):
|
||||
return None
|
||||
|
||||
def Replace(self, syntOld, syntNew):
|
||||
assert syntNew.syntParent == None
|
||||
|
@ -162,12 +181,6 @@ class Synt(Typeable):
|
|||
return syntChild
|
||||
return self.rgsynt.index(syntChild)
|
||||
|
||||
def Rtype(self):
|
||||
return None
|
||||
|
||||
def RtypeTopForChild(self, syntChild):
|
||||
return None
|
||||
|
||||
def Populate(self):
|
||||
"Populate rgsynt with useful default values."
|
||||
pass
|
||||
|
@ -175,20 +188,17 @@ class Synt(Typeable):
|
|||
def Project(self, pcur):
|
||||
"Project the current synt into its corresponding pws."
|
||||
pass
|
||||
def Compile(self):
|
||||
"Return a list of instrs to be interpreted. See vm.py for details on what an instr can be."
|
||||
return [self.Eval]
|
||||
|
||||
def Eval(self, vm):
|
||||
"Execute yourself, in the context of vm."
|
||||
return Fail("Missing information", self)
|
||||
|
||||
# desc - list of desces
|
||||
# desce:
|
||||
# string: "foo" -- literal uneditable text
|
||||
# list: ["foo"] -- text to edit the current synt
|
||||
# tuple: (clsSynt, type) -- child description
|
||||
# clsSynt: class of child synt
|
||||
# type: number - same as another child expression with that number
|
||||
# None - no rtype specified / possible
|
||||
# tuple (number, rtypeTop) - number == same as another child expression with that number, rtypeTop = required type
|
||||
# synt: synt - insert an empty instance of this synt
|
||||
# stype: stype - insert a SyntHole, use stype to determine what can go there
|
||||
# tuple: (stype, synt) - insert a Synt that can replace itself, use stype to determine what can go there
|
||||
class SyntDesc(Synt):
|
||||
@classmethod
|
||||
def StForTypein(cls):
|
||||
|
@ -210,37 +220,30 @@ class SyntDesc(Synt):
|
|||
return cls.desc
|
||||
def Populate(self):
|
||||
for desce in self.Desc():
|
||||
if isinstance(desce, tuple):
|
||||
synt = desce[0].SyntDefault()
|
||||
self.Append(synt)
|
||||
# for synt in self.rgsynt:
|
||||
# synt.Populate()
|
||||
if hasattr(desce, "SyntDefault"):
|
||||
self.Append(desce.SyntDefault())
|
||||
elif isinstance(desce, tuple):
|
||||
self.Append(desce[1].SyntDefault())
|
||||
elif isinstance(desce, Stype):
|
||||
self.Append(SyntHole.SyntDefault())
|
||||
|
||||
def DesceChild(self, isyntChild):
|
||||
isyntDesce = 0;
|
||||
isyntDesce = 0
|
||||
for desce in self.Desc():
|
||||
if isinstance(desce, tuple):
|
||||
if isinstance(desce, Stype) or hasattr(desce, "SyntDefault") or isinstance(desce, tuple):
|
||||
if isyntDesce == isyntChild:
|
||||
return desce
|
||||
isyntDesce += 1
|
||||
return None
|
||||
|
||||
def RtypeTopForChild(self, syntChild):
|
||||
def StypeForChild(self, syntChild):
|
||||
isyntChild = self.Isynt(syntChild)
|
||||
desce = self.DesceChild(isyntChild)
|
||||
if desce != None and isinstance(desce[1], tuple):
|
||||
return desce[1][0]
|
||||
return None
|
||||
if isinstance(desce, Stype):
|
||||
return desce
|
||||
elif isinstance(desce, tuple):
|
||||
return desce[0]
|
||||
|
||||
def RgsyntForChild(self, syntChild, st):
|
||||
isyntChild = self.Isynt(syntChild)
|
||||
desce = self.DesceChild(isyntChild)
|
||||
if desce != None:
|
||||
rtype = None
|
||||
if isinstance(desce[1], tuple):
|
||||
rtype = desce[1][1]
|
||||
return desce[0].RgsyntReplace(syntChild, st, rtype)
|
||||
return []
|
||||
def Project(self, pcur):
|
||||
"adds more horizontal pws to a horizontal pw"
|
||||
isynt = 0
|
||||
|
@ -249,13 +252,13 @@ class SyntDesc(Synt):
|
|||
PwStatic(pcur.PwHoriz(self), desce)
|
||||
elif isinstance(desce, list):
|
||||
# selection for the synt itself
|
||||
self.syntParent.ProjectTypeinForChild(pcur.PwHoriz(self), self, desce[0])
|
||||
elif isinstance(desce, tuple):
|
||||
self.ProjectTypein(pcur)
|
||||
elif isinstance(desce, Stype) or hasattr(desce, "SyntDefault") or isinstance(desce, tuple):
|
||||
# embedded syntax
|
||||
self.rgsynt[isynt].Project(pcur)
|
||||
isynt += 1
|
||||
|
||||
class SyntName(Synt):
|
||||
class SyntText(Synt):
|
||||
def InitPersistent(self):
|
||||
Synt.InitPersistent(self)
|
||||
self.st = ""
|
||||
|
@ -263,39 +266,18 @@ class SyntName(Synt):
|
|||
Synt.InitTransient(self)
|
||||
self.SetStTypein(self.st)
|
||||
def Project(self, pcur):
|
||||
PwTypein(pcur.PwHoriz(self), self.St(), self)
|
||||
PwTypein(pcur.PwHoriz(self), self.St(), self, ansi.GREEN)
|
||||
def St(self):
|
||||
return self.GetStTypein()
|
||||
def HandleTypeinKey(self, key):
|
||||
val = Synt.HandleTypeinKey(self, key)
|
||||
self.st = self.GetStTypein()
|
||||
return val
|
||||
|
||||
class SyntExpr(Synt):
|
||||
rgclsSyntOp = []
|
||||
@classmethod
|
||||
def RgsyntReplace(cls, syntChild, st, rtype):
|
||||
for synt in RtypeAny().RgsyntFromSt(syntChild.syntParent, st):
|
||||
yield synt
|
||||
for clsSyntOp in cls.rgclsSyntOp:
|
||||
if clsSyntOp.StForTypein().lower().startswith(st.lower()):
|
||||
yield clsSyntOp.SyntDefault()
|
||||
|
||||
def Project(self, pcur):
|
||||
self.syntParent.ProjectTypeinForChild(pcur.PwHoriz(self), self, " ")
|
||||
def Eval(self, vm):
|
||||
Fail.Push(vm, "Empty expression", self)
|
||||
return self.st
|
||||
|
||||
class SyntLine(SyntDesc):
|
||||
rgclsStmt = []
|
||||
class SyntHole(SyntDesc):
|
||||
desc = [[" "]]
|
||||
@classmethod
|
||||
def RgsyntReplace(cls, syntChild, st, rtype):
|
||||
for clsStmt in cls.rgclsStmt:
|
||||
if clsStmt.StForTypein().lower().startswith(st.lower()):
|
||||
yield clsStmt()
|
||||
def Compile(self):
|
||||
return []
|
||||
|
||||
class SyntBlock(Synt):
|
||||
def Project(self, pcur):
|
||||
|
@ -305,19 +287,19 @@ class SyntBlock(Synt):
|
|||
pcur.pwVert = PwBlock(None, self, pcur.dxindent)
|
||||
def OnInsertNewLine(syntAfter, psel):
|
||||
self.InsertLineAfter(syntAfter, None)
|
||||
psel.Inc(pcur.pwVert)
|
||||
psel.Inc(pcur.pwVert) # does nothing because we need to reproject
|
||||
PwButtonHidden(pcur.pwVert, "[insert new line]", None, OnInsertNewLine, pcur.dxindent)
|
||||
for syntLine in self.rgsynt:
|
||||
pwKey = PwKeyHandler(pcur.pwVert, self.HandleKey)
|
||||
with pcur.ProjectHoriz(pwKey, syntLine):
|
||||
syntLine.Project(pcur)
|
||||
|
||||
def RgsyntForChild(self, syntChild, st):
|
||||
return SyntLine.RgsyntReplace(syntChild, st, None)
|
||||
def StypeForChild(self, syntChild):
|
||||
return StypeStmt()
|
||||
|
||||
def InsertLineAfter(self, syntAfter, syntStmt):
|
||||
if syntStmt == None:
|
||||
syntStmt = SyntLine()
|
||||
syntStmt = SyntHole()
|
||||
if syntAfter == None:
|
||||
self.rgsynt.insert(0, syntStmt)
|
||||
else:
|
||||
|
@ -331,8 +313,11 @@ class SyntBlock(Synt):
|
|||
psel.Inc(pwKey.PwParent())
|
||||
return True
|
||||
return False
|
||||
def Compile(self):
|
||||
return self.rgsynt
|
||||
|
||||
def Eval(self, vm):
|
||||
for synt in self.rgsynt:
|
||||
# todo: log failures
|
||||
synt.Eval(vm)
|
||||
|
||||
class SyntLit(Synt):
|
||||
def InitPersistent(self, value, rtype):
|
||||
|
@ -344,137 +329,80 @@ class SyntLit(Synt):
|
|||
def Rtype(self):
|
||||
return self.rtype
|
||||
def Project(self, pcur):
|
||||
self.syntParent.ProjectTypeinForChild(pcur.PwHoriz(self), self, self.StForTypein())
|
||||
self.ProjectTypein(pcur)
|
||||
def Eval(self, vm):
|
||||
vm.Push(self.value)
|
||||
return self.value
|
||||
|
||||
@RegStmt
|
||||
class SyntVar(SyntDesc):
|
||||
desc = [["Define"], " ", (SyntName, None), " to be ", (SyntExpr, 1)]
|
||||
name = SyntDesc.DefProp(0)
|
||||
expr = SyntDesc.DefProp(1)
|
||||
@classmethod
|
||||
def FVarOfType(cls, synt, rtype):
|
||||
return isinstance(synt, cls) and synt.expr != None and rtype.FOverlap(synt.expr.Rtype())
|
||||
def Compile(self):
|
||||
return [self.expr, self.Eval]
|
||||
def Eval(self, vm):
|
||||
vm.vars.Define(self, vm.Pop())
|
||||
|
||||
class SyntVarRef(Synt):
|
||||
def InitPersistent(self, syntVar = None):
|
||||
class SyntFlagRef(Synt):
|
||||
def InitPersistent(self, flagdef = None):
|
||||
Synt.InitPersistent(self)
|
||||
self.syntVar = syntVar
|
||||
@classmethod
|
||||
def RgsyntReplace(cls, syntChild, st, rtype):
|
||||
rtype = rtype or RtypeAny()
|
||||
return rtype.RgsyntVarRefFromSt(syntChild, st)
|
||||
def Rtype(self):
|
||||
return self.syntVar.Rtype()
|
||||
self.flagdef = flagdef
|
||||
|
||||
def StForTypein(self):
|
||||
if self.syntVar:
|
||||
return self.syntVar.name.St()
|
||||
return "<var>"
|
||||
if self.flagdef:
|
||||
return self.flagdef.stName
|
||||
return "<flag>"
|
||||
|
||||
def Project(self, pcur):
|
||||
self.syntParent.ProjectTypeinForChild(pcur.PwHoriz(self), self, self.StForTypein())
|
||||
def Eval(self, vm):
|
||||
vm.Push(vm.vars.Get(self.syntVar, self))
|
||||
self.ProjectTypein(pcur)
|
||||
|
||||
class SyntBinOp(SyntDesc):
|
||||
@classmethod
|
||||
def Desc(cls):
|
||||
return [(SyntExpr, None), " ", [cls.op], " ", (SyntExpr, None)]
|
||||
left = SyntDesc.DefProp(0)
|
||||
right = SyntDesc.DefProp(1)
|
||||
@classmethod
|
||||
def Create(cls, op, rtype, dgEval = None, stNameI = None):
|
||||
def Decorate(dgEval):
|
||||
stName = stNameI or dgEval.__name__
|
||||
def Eval(self, vm):
|
||||
right = vm.Pop()
|
||||
left = vm.Pop()
|
||||
try:
|
||||
vm.Push(dgEval(left, right))
|
||||
except Exception as e:
|
||||
Fail.Push(vm, "Failed to " + op + ": " + str(e), self)
|
||||
def Compile(self):
|
||||
return [self.left, self.right, self.Eval]
|
||||
clsNew = type(stName, (cls,), {"Compile": Compile, "Eval": Eval, "op": op})
|
||||
SyntExpr.rgclsSyntOp.append(clsNew)
|
||||
return clsNew
|
||||
return vm.FFlagSet(self.flagdef)
|
||||
except:
|
||||
return Fail("No such flag", self)
|
||||
|
||||
if dgEval == None: # used as a decorator
|
||||
return Decorate
|
||||
clsNew = Decorate(dgEval)
|
||||
if stNameI != None:
|
||||
globals()[stNameI] = clsNew
|
||||
return clsNew
|
||||
class SyntIsFlagSet(SyntDesc):
|
||||
desc = [["set"]]
|
||||
def Eval(self, vm, val):
|
||||
return val
|
||||
class SyntIsFlagUnset(SyntDesc):
|
||||
desc = [["not set"]]
|
||||
def Eval(self, vm, val):
|
||||
return not val
|
||||
class SyntIsFlagEqualTo(SyntDesc):
|
||||
desc = [["equal to"], " ", RtypeFlag()]
|
||||
flag = SyntDesc.DefProp(0)
|
||||
def Eval(self, vm, val):
|
||||
return val == self.flag.Eval(vm)
|
||||
class SyntIsFlagNotEqualTo(SyntDesc):
|
||||
desc = [["not equal to"], " ", RtypeFlag()]
|
||||
flag = SyntDesc.DefProp(0)
|
||||
def Eval(self, vm, val):
|
||||
return val != self.flag.Eval(vm)
|
||||
|
||||
SyntBinOp.Create("+", RtypeNum(), lambda l,r: l + r, "SyntPlus")
|
||||
SyntBinOp.Create("-", RtypeNum(), lambda l,r: l - r, "SyntMinus")
|
||||
SyntBinOp.Create("*", RtypeNum(), lambda l,r: l * r, "SyntMult")
|
||||
SyntBinOp.Create("/", RtypeNum(), lambda l,r: l / r, "SyntDiv") # use ansi?
|
||||
SyntBinOp.Create("=", RtypeBool(), lambda l,r: l == r, "SyntEq")
|
||||
SyntBinOp.Create(">", RtypeBool(), lambda l,r: l > r, "SyntGt")
|
||||
SyntBinOp.Create(">=", RtypeBool(), lambda l,r: l >= r, "SyntGte") # use ansi?
|
||||
SyntBinOp.Create("<", RtypeBool(), lambda l,r: l < r, "SyntLt")
|
||||
SyntBinOp.Create("<=", RtypeBool(), lambda l,r: l <= r, "SyntLte") # use ansi?
|
||||
SyntBinOp.Create("and", RtypeBool(), lambda l,r: l and r, "SyntAnd")
|
||||
SyntBinOp.Create("or", RtypeBool(), lambda l,r: l or r, "SyntOr")
|
||||
class StypeFlagTest(StypeEnum):
|
||||
rgclsSynt = [SyntIsFlagSet, SyntIsFlagUnset, SyntIsFlagEqualTo, SyntIsFlagNotEqualTo]
|
||||
|
||||
@RegStmt
|
||||
class SyntIf(SyntDesc):
|
||||
desc = [["If"], " ", (SyntExpr, (1, RtypeBool)), ", then:", (SyntBlock, None), "or else:", (SyntBlock, None)]
|
||||
desc = [["If"], " ", RtypeFlag(), " is ", (StypeFlagTest(), SyntIsFlagSet), ", then:", SyntBlock, "or else:", SyntBlock]
|
||||
expr = SyntDesc.DefProp(0)
|
||||
blockIfTrue = SyntDesc.DefProp(1)
|
||||
blockIfFalse = SyntDesc.DefProp(2)
|
||||
def Compile(self):
|
||||
return [self.expr, self.Compare, self.PopCtx]
|
||||
def Compare(self, vm):
|
||||
val = vm.Pop()
|
||||
vm.vars.Push() # lexical scoping, bitches
|
||||
if val:
|
||||
return self.blockIfTrue
|
||||
test = SyntDesc.DefProp(1)
|
||||
blockIfTrue = SyntDesc.DefProp(2)
|
||||
blockIfFalse = SyntDesc.DefProp(3)
|
||||
def Eval(self, vm):
|
||||
if self.test.Eval(vm, self.expr.Eval(vm)):
|
||||
return self.blockIfTrue.Eval(vm)
|
||||
else:
|
||||
return self.blockIfFalse
|
||||
def PopCtx(self, vm):
|
||||
vm.vars.Pop()
|
||||
return self.blockIfFalse.Eval(vm)
|
||||
|
||||
@RegStmt
|
||||
class SyntSet(SyntDesc):
|
||||
desc = [["Set"], " ", (SyntVarRef, 1), " to ", (SyntExpr, 1)]
|
||||
desc = [["Set flag"], " ", RtypeFlag(), " to ", RtypeUnion(RtypeBool(), RtypeFlag())]
|
||||
lvalue = SyntDesc.DefProp(0)
|
||||
expr = SyntDesc.DefProp(1)
|
||||
def Compile(self):
|
||||
return [self.expr, self.Eval]
|
||||
def Eval(self, vm):
|
||||
val = vm.Pop()
|
||||
if not isinstance(val, Fail):
|
||||
vm.Log(vm.vars.Set(self.lvalue.syntVar, vm.Pop(), self))
|
||||
# todo: fail if flagdef or expr isn't set properly?
|
||||
vm.SetFlag(self.lvalue.flagdef, self.expr.Eval(vm))
|
||||
|
||||
class SyntEvent(SyntDesc):
|
||||
rgclsSyntEvent = []
|
||||
desc = [[" "]]
|
||||
@classmethod
|
||||
def RgsyntReplace(cls, syntChild, st, rtype):
|
||||
for clsSyntEvent in cls.rgclsSyntEvent:
|
||||
if clsSyntEvent.StForTypein().lower().startswith(st.lower()):
|
||||
yield clsSyntEvent.SyntDefault()
|
||||
for synt in SyntExpr.RgsyntReplace(syntChild, st, rtype):
|
||||
yield synt
|
||||
|
||||
class SyntWhen(SyntDesc):
|
||||
desc = [["When"], " ", (SyntEvent, 1), ":", (SyntBlock, 2)]
|
||||
|
||||
#debug
|
||||
@RegStmt
|
||||
class SyntPrint(SyntDesc):
|
||||
desc = [["Print"], " ", (SyntExpr, 1)]
|
||||
desc = [["Show Message"], " \"", SyntText, "\""]
|
||||
expr = SyntDesc.DefProp(0)
|
||||
def Compile(self):
|
||||
return [self.expr, self.Eval]
|
||||
def Eval(self, vm):
|
||||
print vm.Pop()
|
||||
# todo
|
||||
print(self.expr.Eval(vm))
|
||||
|
||||
class Fail(TPrs):
|
||||
"""
|
||||
|
@ -483,27 +411,21 @@ class Fail(TPrs):
|
|||
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):
|
||||
def InitPersistent(self, vm, stFailure, synt):
|
||||
self.stFailure = stFailure
|
||||
self.synt = synt
|
||||
@staticmethod
|
||||
def Push(vm, stFailure, synt):
|
||||
fail = Fail(stFailure, synt)
|
||||
vm.Push(fail)
|
||||
vm.Log(fail)
|
||||
@staticmethod
|
||||
def Log(vm, stFailure, synt):
|
||||
vm.Log(Fail(stFailure, synt))
|
||||
vm.Log(self)
|
||||
|
||||
# projection cursor
|
||||
class Pcur(object):
|
||||
@staticmethod
|
||||
def PwProjected(synt):
|
||||
pcur = Pcur()
|
||||
def PwProjected(defs, synt):
|
||||
pcur = Pcur(defs)
|
||||
synt.Project(pcur)
|
||||
return pcur.pwVert
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, defs):
|
||||
self.defs = defs
|
||||
self.pwVert = None
|
||||
self.pwHoriz = None
|
||||
self.dxindent = 0
|
||||
|
@ -633,13 +555,14 @@ class PwStatic(Pw):
|
|||
ascr.PutSt(snippet.st, x, y, self.ColFg(), self.ColBg(ksel))
|
||||
|
||||
class PwTypein(PwStatic):
|
||||
def __init__(self, pwParent, stSobj, typeable):
|
||||
def __init__(self, pwParent, stSobj, typeable, colBgDefault = ansi.BLUE):
|
||||
if typeable.GetStTypein() == None:
|
||||
stDisplay = stSobj
|
||||
else:
|
||||
stDisplay = None
|
||||
PwStatic.__init__(self, pwParent, stDisplay)
|
||||
self.typeable = typeable
|
||||
self.colBgDefault = colBgDefault
|
||||
def Value(self):
|
||||
return self.typeable
|
||||
def StTextDisplay(self, ksel):
|
||||
|
@ -653,7 +576,7 @@ class PwTypein(PwStatic):
|
|||
return ansi.YELLOW
|
||||
elif ksel == Ksel.OTHERNAV:
|
||||
return ansi.MAGENTA
|
||||
return ansi.BLUE
|
||||
return self.colBgDefault
|
||||
def HandleKey(self, pov, psel, key):
|
||||
return self.typeable.HandleTypeinKey(key)
|
||||
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
|
||||
|
@ -899,20 +822,20 @@ class Psel(TPrs):
|
|||
|
||||
# projector overlay -- handles navigation, drawing the projection, etc
|
||||
class Pov(TokenClient):
|
||||
def InitPersistent(self, owner, client, block):
|
||||
def InitPersistent(self, owner, client, defs, block):
|
||||
TokenClient.InitPersistent(self, owner, client, "drawable", "overlay")
|
||||
self.defs = defs
|
||||
self.block = block
|
||||
self.pselstate = self.game.rgtoken("pselstate")[0]
|
||||
|
||||
def PwProjected(self):
|
||||
return Pcur.PwProjected(self.block)
|
||||
return Pcur.PwProjected(self.defs, self.block)
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
key = self.client.evKey.receive(self)
|
||||
if key == ansi.K_PGDN:
|
||||
Vm().RunSynt(self.block)
|
||||
continue
|
||||
self.block.Eval(Vm(self.defs))
|
||||
psel = self.pselstate.PselByClient(self.client)
|
||||
pwSel = psel.PwSelected(self.PwProjected())
|
||||
while pwSel != None:
|
||||
|
@ -928,8 +851,9 @@ class Pov(TokenClient):
|
|||
pw.Draw(ascr, 1, 1, ascr.W(), 0, mpksel, True)
|
||||
|
||||
class PselState(LeaveJoinToken):
|
||||
def InitPersistent(self, owner, block):
|
||||
def InitPersistent(self, owner, defs, block):
|
||||
Token.InitPersistent(self, owner, "pselstate")
|
||||
self.defs = defs
|
||||
self.block = block
|
||||
def InitTransient(self):
|
||||
Token.InitTransient(self)
|
||||
|
@ -937,8 +861,8 @@ class PselState(LeaveJoinToken):
|
|||
def OnLeave(self, client):
|
||||
del self.mpclient_psel[client]
|
||||
def OnJoin(self, client):
|
||||
print "client joined,", client
|
||||
self.mpclient_psel[client] = Psel(Pcur.PwProjected(self.block).PwFirstSelectable(), Ksel.NAV)
|
||||
print("client joined,", client)
|
||||
self.mpclient_psel[client] = Psel(Pcur.PwProjected(self.defs, self.block).PwFirstSelectable(), Ksel.NAV)
|
||||
class Mpksel(object):
|
||||
def __init__(self, pwRoot, client, mpclient_psel):
|
||||
self.mppw_ksel = {}
|
||||
|
@ -960,10 +884,13 @@ class PselState(LeaveJoinToken):
|
|||
class GameScriptTest(Game):
|
||||
def InitPersistent(self):
|
||||
Game.InitPersistent(self)
|
||||
self.defs = Defs()
|
||||
self.defs.AddFlag("flag1", True)
|
||||
self.defs.AddFlag("flag2", False)
|
||||
self.block = SyntBlock()
|
||||
|
||||
def GetRgclsTokTrans(self):
|
||||
return [[PselState, self.block], [AutoJoiner, [Pov, self.block]]]
|
||||
return [[PselState, self.defs, self.block], [AutoJoiner, [Pov, self.defs, self.block]]]
|
||||
|
||||
class RunnerScriptTest(Runner):
|
||||
def InitPersistent(self):
|
||||
|
|
Loading…
Reference in a new issue