X-Git-Url: https://git.yukkurigames.com/?p=python-bulletml.git;a=blobdiff_plain;f=bulletml%2Fparser.py;h=10b789ae83a801bc0dc538afc183f8194240f00c;hp=a07cc2dab61f7cb74ba3bbeec0f91d83df08f744;hb=70d25195d561d8b5d3126c6bbac0ecad2a502f06;hpb=16023be19df43f8b8acc4671e0fabdeffb0e720a diff --git a/bulletml/parser.py b/bulletml/parser.py index a07cc2d..10b789a 100644 --- a/bulletml/parser.py +++ b/bulletml/parser.py @@ -14,13 +14,17 @@ import math from xml.etree.ElementTree import ElementTree try: - from cStringIO import StringIO + from io import StringIO except ImportError: - from StringIO import StringIO + try: + from cStringIO import StringIO + except ImportError: + from StringIO import StringIO from bulletml.errors import Error from bulletml.expr import NumberDef, INumberDef + __all_ = ["ParseError", "BulletML"] class ParseError(Error): @@ -63,6 +67,12 @@ class Direction(object): self.type = type self.value = value + def __getstate__(self): + return dict(type=self.type, value=self.value.expr) + + def __setstate__(self, state): + self.__init__(state["type"], NumberDef(state["value"])) + @classmethod def FromElement(cls, doc, element, default="absolute"): """Construct using an ElementTree-style element.""" @@ -82,6 +92,15 @@ class ChangeDirection(object): self.term = term self.direction = direction + def __getstate__(self): + return dict(frames=self.term.expr, + type=self.direction.type, + value=self.direction.value.expr) + + def __setstate__(self, state): + self.__init__(INumberDef(state["frames"]), + Direction(state["type"], NumberDef(state["value"]))) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -114,6 +133,12 @@ class Speed(object): self.type = type self.value = value + def __getstate__(self): + return dict(type=self.type, value=self.value.expr) + + def __setstate__(self, state): + self.__init__(state["type"], NumberDef(state["value"])) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -132,6 +157,15 @@ class ChangeSpeed(object): self.term = term self.speed = speed + def __getstate__(self): + return dict(frames=self.term.expr, + type=self.speed.type, + value=self.speed.value.expr) + + def __setstate__(self, state): + self.__init__(INumberDef(state["frames"]), + Speed(state["type"], NumberDef(state["value"]))) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -159,6 +193,12 @@ class Wait(object): def __init__(self, frames): self.frames = frames + def __getstate__(self): + return dict(frames=self.frames.expr) + + def __setstate__(self, state): + self.__init__(INumberDef(state["frames"])) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -170,6 +210,40 @@ class Wait(object): def __repr__(self): return "%s(%r)" % (type(self).__name__, self.frames) +class Tag(object): + """Set a bullet tag.""" + + def __init__(self, tag): + self.tag = tag + + def __getstate__(self): + return dict(tag=self.tag) + + def __setstate__(self, state): + self.__init__(state["tag"]) + + @classmethod + def FromElement(cls, doc, element): + """Construct using an ElementTree-style element.""" + return cls(element.text) + +class Untag(object): + """Unset a bullet tag.""" + + def __init__(self, tag): + self.tag = tag + + def __getstate__(self): + return dict(tag=self.tag) + + def __setstate__(self, state): + self.__init__(state["tag"]) + + @classmethod + def FromElement(cls, doc, element): + """Construct using an ElementTree-style element.""" + return cls(element.text) + class Vanish(object): """Make the owner disappear.""" @@ -190,6 +264,12 @@ class Repeat(object): def __init__(self, times, action): self.times = times self.action = action + + def __getstate__(self): + return dict(times=self.times.expr, action=self.action) + + def __setstate__(self, state): + self.__init__(INumberDef(state["times"]), state["action"]) @classmethod def FromElement(cls, doc, element): @@ -224,6 +304,18 @@ class Accel(object): self.horizontal = horizontal self.vertical = vertical + def __getstate__(self): + state = dict(frames=self.term.expr) + if self.horizontal: + state["horizontal"] = self.horizontal + if self.vertical: + state["vertical"] = self.vertical + return state + + def __setstate__(self, state): + self.__init__(INumberDef(state["frames"]), state.get("horizontal"), + state.get("vertical")) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -265,6 +357,19 @@ class BulletDef(object): self.speed = speed self.actions = list(actions) + def __getstate__(self): + state = dict() + if self.direction: + state["direction"] = self.direction + if self.speed: + state["speed"] = self.speed + if self.actions: + state["actions"] = self.actions + return state + + def __setstate__(self, state): + self.__init__(**state) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -301,7 +406,18 @@ class BulletRef(object): def __init__(self, bullet, params=None): self.bullet = bullet - self.params = params or ParamList() + self.params = ParamList() if params is None else params + + def __getstate__(self): + state = dict(bullet=self.bullet) + if self.params.params: + state["params"] = [param.expr for param in self.params.params] + return state + + def __setstate__(self, state): + bullet = state["bullet"] + params = [NumberDef(param) for param in state.get("params", [])] + self.__init__(bullet, ParamList(params)) @classmethod def FromElement(cls, doc, element): @@ -332,6 +448,12 @@ class ActionDef(object): def __init__(self, actions): self.actions = list(actions) + def __getstate__(self): + return dict(actions=self.actions) + + def __setstate__(self, state): + self.__init__(state["actions"]) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -361,6 +483,17 @@ class ActionRef(object): self.action = action self.params = params or ParamList() + def __getstate__(self): + state = dict(action=self.action) + if self.params.params: + state["params"] = [param.expr for param in self.params.params] + return state + + def __setstate__(self, state): + action = state["action"] + params = [NumberDef(param) for param in state.get("params", [])] + self.__init__(action, ParamList(params)) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -387,6 +520,17 @@ class Offset(object): self.x = x self.y = y + def __getstate__(self): + state = dict(type=self.type) + if self.x: + state["x"] = self.x.expr + if self.y: + state["y"] = self.y.expr + return state + + def __setstate__(self, state): + self.__init__(state["type"], state.get("x"), state.get("y")) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -414,6 +558,27 @@ class FireDef(object): self.speed = speed self.offset = offset + def __getstate__(self): + try: + params = self.bullet.params + except AttributeError: + state = dict(bullet=self.bullet) + else: + if params.params: + state = dict(bullet=self.bullet) + else: + state = dict(bullet=self.bullet.bullet) + if self.direction: + state["direction"] = self.direction + if self.speed: + state["speed"] = self.speed + if self.offset: + state["offset"] = self.offset + return state + + def __setstate__(self, state): + self.__init__(**state) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -460,6 +625,17 @@ class FireRef(object): self.fire = fire self.params = params or ParamList() + def __getstate__(self): + state = dict(fire=self.fire) + if self.params.params: + state["params"] = [param.expr for param in self.params.params] + return state + + def __setstate__(self, state): + fire = state["fire"] + params = [NumberDef(param) for param in state.get("params", [])] + self.__init__(fire, ParamList(params)) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -499,10 +675,16 @@ class BulletML(object): self.actions = {} if actions is None else actions self.fires = {} if fires is None else fires + def __getstate__(self): + return dict(type=self.type, actions=self.actions) + + def __setstate__(self, state): + self.__init__(state["type"], actions=state.get("actions")) + @classmethod - def FromDocument(cls, source): - """Return a BulletML instance based on a string or file-like.""" - if isinstance(source, (str, unicode)): + def FromXML(cls, source): + """Return a BulletML instance based on XML.""" + if not hasattr(source, 'read'): source = StringIO(source) tree = ElementTree() @@ -539,10 +721,43 @@ class BulletML(object): return self + @classmethod + def FromYAML(cls, source): + """Create a BulletML instance based on YAML.""" + + # Late import to avoid a circular dependency. + try: + import bulletml.bulletyaml + import yaml + except ImportError: + raise ParseError("PyYAML is not available") + else: + try: + return yaml.load(source) + except Exception, exc: + raise ParseError(str(exc)) + + @classmethod + def FromDocument(cls, source): + """Create a BulletML instance based on a seekable file or string. + + This attempts to autodetect if the stream is XML or YAML. + """ + if not hasattr(source, 'read'): + source = StringIO(source) + start = source.read(1) + source.seek(0) + if start == "<": + return cls.FromXML(source) + elif start == "!": + return cls.FromYAML(source) + else: + raise ParseError("unknown initial character %r" % start) + @property def top(self): """Get a list of all top-level actions.""" - return [dfn for name, dfn in self.actions.iteritems() + return [dfn for name, dfn in self.actions.items() if name and name.startswith("top")] def __repr__(self): @@ -551,13 +766,15 @@ class BulletML(object): self.fires) ActionDef.CONSTRUCTORS = dict( - repeat=Repeat, - fire=FireDef, - fireRef=FireRef, - changeSpeed=ChangeSpeed, - changeDirection=ChangeDirection, - accel=Accel, - wait=Wait, - vanish=Vanish, - action=ActionDef, - actionRef=ActionRef) + repeat=Repeat, + fire=FireDef, + fireRef=FireRef, + changeSpeed=ChangeSpeed, + changeDirection=ChangeDirection, + accel=Accel, + wait=Wait, + vanish=Vanish, + tag=Tag, + untag=Untag, + action=ActionDef, + actionRef=ActionRef)