scripting overhaul
This commit is contained in:
parent
60ca1e19f1
commit
6213a0f917
47
script-thoughts.txt
Normal file
47
script-thoughts.txt
Normal file
|
@ -0,0 +1,47 @@
|
|||
How to refer to things
|
||||
|
||||
====
|
||||
MarMOTS: set model?
|
||||
when sending a message: send to all in set (useful? or explicit foreach better?)
|
||||
when asking a question: ask a random member? if != 1 I think this is almost never what you really want.
|
||||
====
|
||||
1) construct model:
|
||||
all values (except for constants) come from some class of object, of which there are always only small number available to you
|
||||
the instance of the class that you query is chosen by narrowing the set by "picking" in the event conditions
|
||||
|
||||
pros of picking:
|
||||
= lets you say when(anything) and have it make sense
|
||||
= if there is only one of something you always get it right
|
||||
cons:
|
||||
= not always an easy way to express what you want
|
||||
|
||||
2) game maker:
|
||||
self, other, pick
|
||||
-- gml has the set model!
|
||||
other == other involved in collision
|
||||
|
||||
=============
|
||||
namespaces:
|
||||
|
||||
---------------------------------------------|--------------
|
||||
| * System *
|
||||
| Thing
|
||||
| Whatsit
|
||||
| Enemy
|
||||
| ...
|
||||
| variable?
|
||||
|
||||
some core stuff should be global (if, expr, true, false)
|
||||
I keep forgetting that this kind of interface suggests type inference
|
||||
|
||||
what is a type? it's
|
||||
- number
|
||||
- string (ansi)
|
||||
- boolean (yes/no)
|
||||
- object<|x,y,z|>
|
||||
- with mixins x, y, z
|
||||
object<|x|> is a subtype of object<|x,y|>
|
||||
- list<t>
|
||||
- void
|
||||
- t
|
||||
|
624
scripting.py
624
scripting.py
|
@ -2,6 +2,308 @@ from engine import *
|
|||
from basetoken import *
|
||||
from tpers import *
|
||||
from util import *
|
||||
from contextlib import contextmanager
|
||||
|
||||
def RegStmt(clsStmt):
|
||||
SyntLine.rgclsStmt.append(clsStmt)
|
||||
return clsStmt
|
||||
|
||||
def RegRtype(clsRtype):
|
||||
Rtype.rgclsRtype.append(clsRtype)
|
||||
return clsRtype
|
||||
|
||||
# rtype -- runtime type
|
||||
# method: RglitFromSt(st) -- parse the string and return a list of potentially matching literals
|
||||
# method: RgsyntFromSt(scope, st) -- parse the string and return a list of synts whose rtypes match this
|
||||
# method: StForSobj(sobj) -- a user-visible description of the sobj
|
||||
# method: FMember(sobj) -- returns true if sobj is a member of this type
|
||||
# method: FOverlap(rtype) -- returns true if there is some overlap between this type and rtype
|
||||
class Rtype(TPrs):
|
||||
rgclsRtype = []
|
||||
@classmethod
|
||||
def RtypeMax(cls):
|
||||
return cls()
|
||||
def RglitFromSt(self, st):
|
||||
return []
|
||||
def RgsyntFromSt(self, syntBlock, st):
|
||||
for lit in self.RglitFromSt(st):
|
||||
yield SyntLit(None, lit, self)
|
||||
|
||||
while syntBlock != None:
|
||||
if isinstance(syntBlock, SyntBlock):
|
||||
for synt in self.RgsyntFromSyntBlock(syntBlock, st):
|
||||
yield synt
|
||||
syntBlock = syntBlock.syntParent
|
||||
|
||||
# helper for RgsyntFromSt
|
||||
def RgsyntFromSyntBlock(self, syntBlock, st):
|
||||
# yield all vars in scope
|
||||
for syntLine in syntBlock.rgsynt:
|
||||
if SyntVar.FVarOfType(syntLine.rgsynt[0], self):
|
||||
yield SyntVarRef(None, syntLine.rgsynt[0])
|
||||
|
||||
def StForSobj(self, sobj):
|
||||
return str(sobj)
|
||||
def FMember(self, sobj):
|
||||
return isinstance(sobj, self.pythontype)
|
||||
def FOverlap(self, rtype):
|
||||
return isinstance(rtype, self.__class__)
|
||||
|
||||
@RegRtype
|
||||
class RtypeBool(Rtype):
|
||||
pythontype = bool
|
||||
def RglitFromSt(self, st):
|
||||
if "true".startswith(st.lower()):
|
||||
yield True
|
||||
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:]
|
||||
|
||||
class RtypeAny(Rtype):
|
||||
def RgsyntFromSt(self, syntBlock, st):
|
||||
for clsRtype in self.rgclsRtype:
|
||||
rtype = clsRtype.RtypeMax()
|
||||
for synt in rtype.RgsyntFromSt(syntBlock, st):
|
||||
yield synt
|
||||
def FMember(self, sobj):
|
||||
return True
|
||||
def FOverlap(self, rtype):
|
||||
return True
|
||||
|
||||
class Typeable(TPrs):
|
||||
def InitTransient(self):
|
||||
self.steditTypein = Stedit()
|
||||
def GetStTypein(self):
|
||||
st = self.steditTypein.GetValue()
|
||||
return None if st == "" else st
|
||||
def SetStTypein(self, stTypein):
|
||||
self.steditTypein.SetValue(stTypein or "")
|
||||
def HandleTypeinKey(self, key):
|
||||
return self.steditTypein.HandleKey(key)
|
||||
|
||||
# synt -- a piece of syntax
|
||||
# method: Project() -- projects it
|
||||
#
|
||||
class Synt(Typeable):
|
||||
desc = None
|
||||
def InitPersistent(self, syntParent):
|
||||
self.syntParent = syntParent
|
||||
self.rgsynt = []
|
||||
self.Populate()
|
||||
|
||||
@classmethod
|
||||
def SyntDefault(cls, syntParent):
|
||||
return cls(syntParent)
|
||||
|
||||
def ProjectTypeinForChild(self, pwParent, syntChild, st):
|
||||
def OnSelect(synt, psel):
|
||||
self.Replace(syntChild, synt)
|
||||
syntChild.SetStTypein(None)
|
||||
if syntChild.GetStTypein() != None:
|
||||
pwParent = PwDropdown(pwParent)
|
||||
PwTypein(pwParent, st, syntChild)
|
||||
if syntChild.GetStTypein() != None:
|
||||
for synt in self.RgsyntForChild(syntChild, syntChild.GetStTypein()):
|
||||
PwButton(pwParent, synt.StForTypein(), synt, OnSelect)
|
||||
|
||||
def RgsyntForChild(self, syntChild, st):
|
||||
return []
|
||||
|
||||
def Replace(self, syntOld, syntNew):
|
||||
self.rgsynt[self.Isynt(syntOld)] = syntNew
|
||||
syntNew.syntParent = self
|
||||
|
||||
def Isynt(self, syntChild):
|
||||
if isinstance(syntChild, int):
|
||||
return syntChild
|
||||
return self.rgsynt.index(syntChild)
|
||||
|
||||
def Rtype(self):
|
||||
return None
|
||||
|
||||
def RtypeTopForChild(self, syntChild):
|
||||
return None
|
||||
|
||||
def Eval(self, ectx):
|
||||
pass
|
||||
|
||||
def Populate(self):
|
||||
pass
|
||||
|
||||
def Project(self, pcur):
|
||||
pass
|
||||
|
||||
# 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
|
||||
class SyntDesc(Synt):
|
||||
@classmethod
|
||||
def StForTypein(cls):
|
||||
for desce in cls.desc:
|
||||
if isinstance(desce, list) and len(desce) == 1:
|
||||
return desce[0]
|
||||
|
||||
@staticmethod
|
||||
def DefProp(isynt):
|
||||
def get(self):
|
||||
return self.rgsynt[isynt]
|
||||
def set(self, syntNew):
|
||||
self.Replace(isynt, syntNew)
|
||||
def delete(self):
|
||||
self.Replace(isynt, None)
|
||||
return property(get, set, delete)
|
||||
|
||||
def Populate(self):
|
||||
for desce in self.desc:
|
||||
if isinstance(desce, tuple):
|
||||
synt = desce[0].SyntDefault(self)
|
||||
self.rgsynt.append(synt)
|
||||
# for synt in self.rgsynt:
|
||||
# synt.Populate()
|
||||
|
||||
def SyntProperty(self, isynt):
|
||||
def get():
|
||||
return self.rgsynt[isynt]
|
||||
def set(syntNew):
|
||||
self.Replace(isynt, syntNew)
|
||||
def delete():
|
||||
self.Replace(isynt, None)
|
||||
return property(get, set, delete)
|
||||
|
||||
def RtypeTopForChild(self, syntChild):
|
||||
isyntChild = self.Isynt(syntChild)
|
||||
isyntDesce = 0
|
||||
for desce in self.desc:
|
||||
if isinstance(desce, tuple):
|
||||
if isyntDesce == isyntChild:
|
||||
if isinstance(desce[1], tuple):
|
||||
return desce[1][0]
|
||||
return None
|
||||
isyntDesce += 1
|
||||
return None
|
||||
|
||||
def Project(self, pcur):
|
||||
"adds more horizontal pws to a horizontal pw"
|
||||
isynt = 0
|
||||
for desce in self.desc:
|
||||
if isinstance(desce, str):
|
||||
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):
|
||||
# embedded syntax
|
||||
self.rgsynt[isynt].Project(pcur)
|
||||
isynt += 1
|
||||
|
||||
class SyntName(Synt):
|
||||
def Project(self, pcur):
|
||||
PwTypein(pcur.PwHoriz(self), self.GetStTypein(), self)
|
||||
|
||||
class SyntExpr(Synt):
|
||||
def Project(self, pcur):
|
||||
self.syntParent.ProjectTypeinForChild(pcur.PwHoriz(self), self, " ")
|
||||
|
||||
class SyntLine(Synt):
|
||||
rgclsStmt = []
|
||||
def Populate(self):
|
||||
self.rgsynt.append(SyntBlank(self))
|
||||
def SetStmt(self, syntStmt):
|
||||
self.Replace(self.rgsynt[0], syntStmt)
|
||||
def Project(self, pcur):
|
||||
pwKey = PwKeyHandler(pcur.pwVert, self.HandleKey)
|
||||
with pcur.ProjectHoriz(pwKey, self):
|
||||
self.rgsynt[0].Project(pcur)
|
||||
|
||||
def RgsyntForChild(self, syntChild, st):
|
||||
for clsStmt in self.rgclsStmt:
|
||||
if clsStmt.StForTypein().lower().startswith(st.lower()):
|
||||
yield clsStmt(None)
|
||||
|
||||
def HandleKey(self, pwKey, pov, psel, key):
|
||||
if ansi.FEnter(key):
|
||||
self.syntParent.InsertLineAfter(self, None)
|
||||
psel.Inc(pwKey.PwParent())
|
||||
return True
|
||||
return False
|
||||
|
||||
class SyntBlock(Synt):
|
||||
def Project(self, pcur):
|
||||
with pcur.Indent(2 if pcur.pwHoriz != None else 0):
|
||||
pcur.EndLine()
|
||||
if pcur.pwVert == None:
|
||||
pcur.pwVert = PwBlock(None, self, pcur.dxindent)
|
||||
def OnInsertNewLine(syntAfter, psel):
|
||||
self.InsertLineAfter(syntAfter, None)
|
||||
psel.Inc(pcur.pwVert)
|
||||
PwButtonHidden(pcur.pwVert, "[insert new line]", None, OnInsertNewLine, pcur.dxindent)
|
||||
for syntLine in self.rgsynt:
|
||||
syntLine.Project(pcur)
|
||||
|
||||
def InsertLineAfter(self, syntAfter, syntStmt):
|
||||
syntLine = SyntLine(self)
|
||||
if syntStmt != None:
|
||||
syntLine.SetStmt(syntStmt)
|
||||
if syntAfter == None:
|
||||
self.rgsynt.insert(0, syntLine)
|
||||
else:
|
||||
self.rgsynt.insert(self.rgsynt.index(syntAfter) + 1, syntLine)
|
||||
return syntLine
|
||||
|
||||
class SyntLit(Synt):
|
||||
def InitPersistent(self, syntParent, value, rtype):
|
||||
Synt.InitPersistent(self, syntParent)
|
||||
self.value = value
|
||||
self.rtype = rtype
|
||||
|
||||
def Rtype(self):
|
||||
return rtype
|
||||
|
||||
@RegStmt
|
||||
class SyntBlank(SyntDesc):
|
||||
desc = [[" "]]
|
||||
|
||||
@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(expr.Rtype())
|
||||
|
||||
class SyntVarRef(Synt):
|
||||
def __init__(self, syntParent, syntVar = None):
|
||||
Synt.__init__(self, syntParent)
|
||||
self.syntVar = syntVar
|
||||
def Rtype(self):
|
||||
return self.syntVar.Rtype()
|
||||
|
||||
@RegStmt
|
||||
class SyntIf(SyntDesc):
|
||||
desc = [["If"], " ", (SyntExpr, (1, RtypeBool)), ", then:", (SyntBlock, None), "or else:", (SyntBlock, None)]
|
||||
expr = SyntDesc.DefProp(0)
|
||||
blockIfTrue = SyntDesc.DefProp(1)
|
||||
blockIfFalse = SyntDesc.DefProp(2)
|
||||
|
||||
# scope -- a block? a scril? (scril lets you specifically avoid vars that are defined after a certain point) + game?
|
||||
# method: rgvar(stype) -- returns a list of variables in scope that match the current stype
|
||||
|
@ -24,266 +326,46 @@ from util import *
|
|||
# iscril (selection?)
|
||||
# ectxReturn
|
||||
# methods: call(block)
|
||||
|
||||
class Block(TPrs):
|
||||
def InitPersistent(self, scrilParent):
|
||||
self.rgscril = []
|
||||
self.scrilParent = scrilParent
|
||||
def InsertScrilAfter(self, scrilAfter, scmd = None):
|
||||
scrilNew = Scril(self, scmd)
|
||||
if scrilAfter == None:
|
||||
self.rgscril.insert(0, scrilNew)
|
||||
else:
|
||||
self.rgscril.insert(self.rgscril.index(scrilAfter) + 1, scrilNew)
|
||||
return scrilNew
|
||||
def Project(self, pwBlock, dxIndent = 0):
|
||||
if pwBlock == None:
|
||||
pwBlock = PwBlock(None, self, 0)
|
||||
PwButtonHidden(pwBlock, "[insert new line]", None, self.InsertScrilAfter, dxIndent)
|
||||
for scril in self.rgscril:
|
||||
scril.Project(pwBlock, dxIndent)
|
||||
return pwBlock
|
||||
|
||||
class Typeable(TPrs):
|
||||
def InitTransient(self):
|
||||
self.steditTypein = Stedit()
|
||||
def GetStTypein(self):
|
||||
st = self.steditTypein.GetValue()
|
||||
return None if st == "" else st
|
||||
def SetStTypein(self, stTypein):
|
||||
self.steditTypein.SetValue(stTypein or "")
|
||||
def HandleTypeinKey(self, key):
|
||||
return self.steditTypein.HandleKey(key)
|
||||
|
||||
# Sarg -- pointer to scripting object / argument to a command
|
||||
# this object type appears to exist solely so that MiSarg can edit something without digging through a list
|
||||
class Sarg(Typeable):
|
||||
def InitPersistent(self, stype, scril):
|
||||
self.stype = stype
|
||||
self.sobj = stype.SobjDefault(scril)
|
||||
|
||||
def FExpr(self):
|
||||
"Returns true if the sobj must be evaluated to get a result."
|
||||
return self.sobj.__class__ == Scril
|
||||
|
||||
def Eval(self, ectx):
|
||||
if self.FExpr():
|
||||
return self.sobj.Exec(ectx)
|
||||
else:
|
||||
return self.sobj
|
||||
|
||||
class Fail(TPrs):
|
||||
def InitPersistent(self, stFailure):
|
||||
self.stFailure == stFailure
|
||||
|
||||
# stype -- a description of a sobj (scripting object)
|
||||
# method: FMember(sobj) -- returns true if an object is in the set of things that this stype represents
|
||||
# method: FOverlaps(stype) -- returns true if there is some overlap between the two stypes
|
||||
# -- there is no subtype/supertype relation because stypes are used to help the user,
|
||||
# and I suspect being specific in this manner is too much of a burden to place on them.
|
||||
# methods: rgsarg(scope) -- returns a list of all legal objects that exist in the current scope
|
||||
# this includes functions which return things of this stype, and variables
|
||||
# literals are not included
|
||||
# project(cls, game, sarg) -- returns an MiSarg which displays and edits (default sproj for a sarg?)
|
||||
class Stype(object):
|
||||
"""A definition of a scripting object type. All methods are class methods; stypes are never instantiated."""
|
||||
def __init__(self):
|
||||
raise "do not instantiate stypes"
|
||||
@classmethod
|
||||
def FMember(cls, sobj):
|
||||
return isinstance(sobj, cls.pythontype)
|
||||
@classmethod
|
||||
def FOverlaps(cls, stype):
|
||||
return stype == self
|
||||
@classmethod
|
||||
def Rgsobj(cls, scope):
|
||||
return []
|
||||
@classmethod
|
||||
def Rgsobj_st(cls, scope):
|
||||
return [(sobj, str(sobj)) for sobj in cls.Rgsobj(scope)]
|
||||
@classmethod
|
||||
def OnSelect(cls, sarg, sobj):
|
||||
sarg.sobj = sobj
|
||||
@classmethod
|
||||
def Sproj(cls, sarg, pwBlock, pwScril):
|
||||
ProjectTypein(pwScril, sarg, str(sarg.sobj), cls.Rgsobj_st, cls.OnSelect)
|
||||
return False
|
||||
|
||||
def ProjectTypein(pwParent, typeable, st, dgRgsobj_st, dgOnSelect):
|
||||
def OnSelect(sobj):
|
||||
dgOnSelect(typeable, sobj)
|
||||
typeable.SetStTypein(None)
|
||||
if typeable.GetStTypein() != None:
|
||||
pwParent = PwDropdown(pwParent)
|
||||
PwTypein(pwParent, st, typeable)
|
||||
if typeable.GetStTypein() != None:
|
||||
for sobj, st in dgRgsobj_st(typeable):
|
||||
PwButton(pwParent, st, sobj, OnSelect)
|
||||
# projection cursor
|
||||
class Pcur(object):
|
||||
@staticmethod
|
||||
def PwProjected(synt):
|
||||
pcur = Pcur()
|
||||
synt.Project(pcur)
|
||||
return pcur.pwVert
|
||||
|
||||
class StypeBool(Stype):
|
||||
pythontype = bool
|
||||
@classmethod
|
||||
def SobjDefault(cls, scril):
|
||||
return False
|
||||
@classmethod
|
||||
def Rgsobj(cls, scope):
|
||||
return [True, False]
|
||||
def __init__(self):
|
||||
self.pwVert = None
|
||||
self.pwHoriz = None
|
||||
self.dxindent = 0
|
||||
|
||||
class StypeBlock(Stype):
|
||||
pythontype = Block
|
||||
@classmethod
|
||||
def SobjDefault(cls, scril):
|
||||
return Block(scril)
|
||||
@classmethod
|
||||
def Sproj(cls, sarg, pwBlock, pwScril):
|
||||
sarg.sobj.Project(pwBlock, pwScril.DxIndent() + 2)
|
||||
return True
|
||||
@contextmanager
|
||||
def Indent(self, dxindent = 2):
|
||||
self.dxindent += dxindent
|
||||
yield
|
||||
self.dxindent -= dxindent
|
||||
|
||||
@contextmanager
|
||||
def ProjectHoriz(self, pwParent, synt):
|
||||
self.ProjectHorizI(pwParent, synt)
|
||||
yield
|
||||
self.EndLine()
|
||||
|
||||
def PwHoriz(self, synt):
|
||||
if self.pwHoriz == None:
|
||||
self.ProjectHorizI(self.pwVert, synt)
|
||||
return self.pwHoriz
|
||||
|
||||
# scmd -- a definition of a scripting command
|
||||
class Scmd(object):
|
||||
"""A definition of a scripting command. All methods are class methods; scmds are never instantiated."""
|
||||
def __init__(self):
|
||||
raise "do not instantiate scmds"
|
||||
|
||||
# used by RegScmd to build a list of all valid scmds for editing
|
||||
rgscmd = []
|
||||
|
||||
@classmethod
|
||||
def Rgscmd_st(cls, scril):
|
||||
return [(scmd, scmd.StName()) for scmd in cls.rgscmd]
|
||||
@classmethod
|
||||
def Desc(cls, *rgsarg):
|
||||
"""
|
||||
Returns a list of desces. A desce can be a constant string, representing nonselectable helper text,
|
||||
an sproj, a list containing an sproj and a reference to a passed-in sarg, which explicitly binds that
|
||||
sproj to that sarg, or a list containing a string, which stands for the selectable text which represents
|
||||
the scril itself.
|
||||
ie, [["Move"], " ", [SprojMovable, rgsarg[1]], " to ", [SprojPosition, rgsarg[0]]]
|
||||
If a bare sproj is given, bind it to the first unbound sarg (after processing all explicitly bound sargs).
|
||||
|
||||
This method must accept invalid sargs; it is the sproj's job to relay the invalidness to the user.
|
||||
"""
|
||||
return cls.desc
|
||||
|
||||
stName = None
|
||||
|
||||
@classmethod
|
||||
def StName(cls):
|
||||
"Returns the name that the user types in to select this scmd."
|
||||
if cls.stName != None:
|
||||
return cls.stName
|
||||
for desce in cls.Desc(Scril(None, cls).rgsarg):
|
||||
if type(desce) == list and type(desce[0]) == str:
|
||||
return desce[0]
|
||||
assert False, "no selectable self in desc"
|
||||
|
||||
@classmethod
|
||||
def Csarg(cls):
|
||||
"Returns the number of arguments that this command takes."
|
||||
return len(cls.rgstypeArg)
|
||||
@classmethod
|
||||
def StypeResult(cls):
|
||||
return cls.stypeResult
|
||||
@classmethod
|
||||
def RgstypeArg(cls):
|
||||
return cls.rgstypeArg
|
||||
|
||||
def RegScmd(stypeResult, *rgstypeArg):
|
||||
def RegI(cls):
|
||||
cls.stypeResult = stypeResult
|
||||
cls.rgstypeArg = rgstypeArg
|
||||
Scmd.rgscmd.append(cls)
|
||||
return cls
|
||||
return RegI
|
||||
|
||||
@RegScmd(None)
|
||||
class ScmdBlank(Scmd):
|
||||
stName = ""
|
||||
desc = [[" "]]
|
||||
@classmethod
|
||||
def Exec(cls, ectx):
|
||||
pass
|
||||
|
||||
@RegScmd(None)
|
||||
class ScmdHelloWorld(Scmd):
|
||||
"Hello, world!"
|
||||
desc = [["Hello World"], "!"]
|
||||
@classmethod
|
||||
def Exec(cls, ectx):
|
||||
print "Hello, world!"
|
||||
|
||||
@RegScmd(None, StypeBool, StypeBlock, StypeBlock)
|
||||
class ScmdIf(Scmd):
|
||||
desc = [["If"], " ", StypeBool.Sproj, ", then:", StypeBlock.Sproj, "or else:", StypeBlock.Sproj]
|
||||
@classmethod
|
||||
def Exec(cls, ectx, sargExpr, sargIfTrue, sargIfFalse):
|
||||
if sargExpr.Eval(ectx):
|
||||
print "true!"
|
||||
|
||||
# needs work
|
||||
#===========================================================================
|
||||
# @RegScmd(None, StypeSt, StypeObj)
|
||||
# class ScmdVar(Scmd):
|
||||
# desc = ["Define a new ", ["variable"], " called \"", StypeSt, "\", set to ", StypeObj, "."]
|
||||
# @classmethod
|
||||
# def Exec(cls, ectx, sargStName, sargInit):
|
||||
# ectx.mpvar_sobj[ectx.ScrilCurr()] = sargInit.Eval()
|
||||
#===========================================================================
|
||||
|
||||
class Scril(Typeable):
|
||||
"A line in a script."
|
||||
def InitPersistent(self, blockParent, scmd = None):
|
||||
self.scmd = scmd or ScmdBlank
|
||||
self.rgsarg = [Sarg(stypeArg, self) for stypeArg in self.scmd.RgstypeArg()]
|
||||
self.blockParent = blockParent
|
||||
|
||||
def SetScmd(self, scmd):
|
||||
if (scmd.Csarg() > self.scmd.Csarg()):
|
||||
self.rgsarg.extend([Sarg(stypeArg, self) for stypeArg in scmd.RgstypeArg()[self.scmd.Csarg():]])
|
||||
elif (scmd.Csarg() < self.scmd.Csarg()):
|
||||
self.rgsarg = self.rgsarg[:scmd.Csarg()]
|
||||
self.scmd = scmd
|
||||
|
||||
def Exec(self, ectx):
|
||||
return self.scmd.Exec(ectx, *self.rgsarg)
|
||||
|
||||
def HandleKey(self, pwKey, pov, psel, key):
|
||||
if ansi.FEnter(key):
|
||||
self.blockParent.InsertScrilAfter(self, ScmdBlank)
|
||||
return True
|
||||
return False
|
||||
def Project(self, pwBlock, dxindent):
|
||||
"adds horizontal pws to a vertical pw"
|
||||
pwScril = None
|
||||
def EndLine(self):
|
||||
self.pwHoriz = None
|
||||
|
||||
desc = self.scmd.Desc(*self.rgsarg)
|
||||
rgsargUnbound = list(self.rgsarg)
|
||||
#filter bound sargs
|
||||
for desce in desc:
|
||||
if isinstance(desce, list) and len(desce) > 1:
|
||||
rgsargUnbound.remove(desce[1])
|
||||
|
||||
fNewScril = True
|
||||
for desce in desc:
|
||||
if fNewScril:
|
||||
pwKey = PwKeyHandler(pwBlock, self.HandleKey)
|
||||
pwScril = PwList(pwKey, self, dxindent)
|
||||
fNewScril = False
|
||||
if isinstance(desce, str):
|
||||
PwStatic(pwScril, desce)
|
||||
elif isinstance(desce, list):
|
||||
if isinstance(desce[0], str):
|
||||
# selection for the scmd / scril
|
||||
ProjectTypein(pwScril, self, desce[0], Scmd.Rgscmd_st, Scril.SetScmd)
|
||||
else:
|
||||
# mapping to sarg
|
||||
fNewScril = self.ProjectSarg(pwBlock, pwScril, desce[0], desce[1])
|
||||
else:
|
||||
# sproj
|
||||
fNewScril = self.ProjectSarg(pwBlock, pwScril, desce, rgsargUnbound.pop(0))
|
||||
|
||||
def ProjectSarg(self, pwBlock, pwScril, sproj, sarg):
|
||||
return sproj(sarg, pwBlock, pwScril)
|
||||
def ProjectHorizI(self, pwParent, synt):
|
||||
self.pwHoriz = PwList(pwParent, synt, self.dxindent)
|
||||
|
||||
# pw - projected widget
|
||||
# Nothing ever depends on the object identity or permanence of a pw!
|
||||
|
@ -295,6 +377,12 @@ class Pw(object):
|
|||
if pwParent != None:
|
||||
pwParent.AddChild(self)
|
||||
self.pwParent = pwParent
|
||||
def PrintTree(self, dxindent = 0):
|
||||
print (" " * dxindent) + self.StDebug()
|
||||
for pwChild in self.RgpwChild():
|
||||
pwChild.PrintTree(dxindent + 2)
|
||||
def StDebug(self):
|
||||
return self.__class__.__name__
|
||||
def PwParent(self):
|
||||
return self.pwParent
|
||||
def Value(self):
|
||||
|
@ -326,11 +414,32 @@ class PwContainer(Pw):
|
|||
return self.rgpwContents
|
||||
def FContainer(self):
|
||||
return True
|
||||
|
||||
class PwContainOne(Pw):
|
||||
def AddChild(self, pw):
|
||||
self.pwChild = pw
|
||||
def RgpwChild(self):
|
||||
return [self.pwChild]
|
||||
def FContainer(self):
|
||||
return True
|
||||
|
||||
class PwExpand(PwContainOne):
|
||||
def DxDyNew(self, w, dxStart, mpksel):
|
||||
dxdy = self.pwChild.DxDyNew(w, dxStart, mpksel)
|
||||
if dxdy[0] == 0 and dxdy[1] > 0:
|
||||
return dxdy
|
||||
return (0, dxdy[0] + 1)
|
||||
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
|
||||
if not fOverlay:
|
||||
ascr.PutSt(" " * w, x, y, self.pwChild.ColFg(), self.pwChild.ColBg(mpksel.Get(self.pwChild)))
|
||||
self.pwChild.Draw(ascr, x, y, w, dxStart, mpksel, fOverlay)
|
||||
|
||||
class PwStatic(Pw):
|
||||
def __init__(self, pwParent, stText):
|
||||
Pw.__init__(self, pwParent)
|
||||
self.stText = stText
|
||||
def StDebug(self):
|
||||
return Pw.StDebug(self) + " '" + self.StTextDisplay(Ksel.NAV) + "'"
|
||||
def DxDyNew(self, w, dxStart, mpksel):
|
||||
rgsnippet = self.Rgsnippet(w, dxStart, mpksel)
|
||||
if len(rgsnippet) == 1:
|
||||
|
@ -414,7 +523,7 @@ class PwButton(PwStatic):
|
|||
return ansi.BLUE | ansi.FBRIGHT
|
||||
def HandleKey(self, pov, psel, key):
|
||||
if key == ansi.K_RETURN:
|
||||
self.dgEnter(self.Value())
|
||||
self.dgEnter(self.Value(), psel)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -632,7 +741,7 @@ class Pov(TokenClient):
|
|||
self.pselstate = self.game.rgtoken("pselstate")[0]
|
||||
|
||||
def PwProjected(self):
|
||||
return self.block.Project(None)
|
||||
return Pcur.PwProjected(self.block)
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
|
@ -662,7 +771,7 @@ class PselState(Token):
|
|||
del self.mpclient_psel[client]
|
||||
def OnJoin(self, client):
|
||||
print "client joined,", client
|
||||
self.mpclient_psel[client] = Psel(self.block.Project(None).PwFirstSelectable(), Ksel.NAV)
|
||||
self.mpclient_psel[client] = Psel(Pcur.PwProjected(self.block).PwFirstSelectable(), Ksel.NAV)
|
||||
class Mpksel(object):
|
||||
def __init__(self, pwRoot, client, mpclient_psel):
|
||||
self.mppw_ksel = {}
|
||||
|
@ -689,12 +798,7 @@ class PselState(Token):
|
|||
class GameScriptTest(Game):
|
||||
def InitPersistent(self):
|
||||
Game.InitPersistent(self)
|
||||
self.block = Block(None)
|
||||
self.block.InsertScrilAfter(None, ScmdHelloWorld)
|
||||
self.block.InsertScrilAfter(None, ScmdBlank)
|
||||
self.block.InsertScrilAfter(None, ScmdHelloWorld)
|
||||
self.block.InsertScrilAfter(None, ScmdIf)
|
||||
self.block.InsertScrilAfter(None, ScmdHelloWorld)
|
||||
self.block = SyntBlock(None)
|
||||
|
||||
def GetRgclsTokTrans(self):
|
||||
return [[PselState, self.block], [AutoJoiner, [Pov, self.block]]]
|
||||
|
|
Loading…
Reference in a new issue