From: Joe Wreschnig Date: Tue, 16 Mar 2010 02:32:35 +0000 (-0700) Subject: Parser, lacking most implementation. X-Git-Url: https://git.yukkurigames.com/?p=python-bulletml.git;a=commitdiff_plain;h=8b5f536fd3c64a9cfd21f82f3460b64f02bff73b Parser, lacking most implementation. --- diff --git a/bulletml/parser.py b/bulletml/parser.py new file mode 100644 index 0000000..10459cc --- /dev/null +++ b/bulletml/parser.py @@ -0,0 +1,398 @@ +"""BulletML parser. + +http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html +""" + +from __future__ import division + +from xml.etree.ElementTree import ElementTree + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +from bulletml.errors import Error +from bulletml.expr import NumberDef, INumberDef + +class ParseError(Error): + """Raised when an error occurs parsing the XML structure.""" + pass + +def realtag(element): + """Strip namespace poop off the front of a tag.""" + try: + return element.tag.rsplit('}', 1)[1] + except ValueError: + return element.tag + +class ParamList(object): + """List of parameter definitions.""" + def __init__(self, element): + self.params = [] + if element: + for subelem in element: + if realtag(subelem) == "param": + self.params.append(NumberDef(subelem.text)) + + def __call__(self, params, rank): + new_params = [param(params, rank) for param in self.params] + while len(new_params) < len(params): + new_params.append(params[len(new_params)]) + return new_params + + def __repr__(self): + return "%s(%r)" % (type(self).__name__, self.params) + +class Direction(object): + """Raw direction value.""" + + def __init__(self, doc, element, type="absolute"): + self.type = element.get("type", type) + self.value = NumberDef(element.text) + + def __call__(self, params, rank): + return self.value(params, rank) + + def __repr__(self): + return "%s(%r, type=%r)" % ( + type(self).__name__, self.value, self.type) + +class ChangeDirection(object): + """Direction change over time.""" + + def __init__(self, doc, element): + for subelem in element.getchildren(): + tag = realtag(subelem) + if tag == "direction": + self.direction = Direction(doc, subelem) + elif tag == "term": + self.term = INumberDef(subelem.text) + try: + self.term, self.direction + except AttributeError: + raise ParseError + + def __call__(self, params, rank): + return self.term(params, rank), self.direction(params, rank) + + def __repr__(self): + return "%s(term=%r, direction=%r)" % ( + type(self).__name__, self.term, self.direction) + +class Speed(object): + """Raw speed value.""" + + def __init__(self, doc, element, type="absolute"): + self.type = element.get("type", type) + self.value = NumberDef(element.text) + + def __call__(self, params, rank): + return self.value(params, rank) + + def __repr__(self): + return "%s(%r, type=%r)" % ( + type(self).__name__, self.value, self.type) + +class ChangeSpeed(object): + """Speed change over time.""" + + def __init__(self, doc, element): + for subelem in element.getchildren(): + tag = realtag(subelem) + if tag == "speed": + self.speed = Speed(doc, subelem) + elif tag == "term": + self.term = INumberDef(subelem.text) + try: + self.term, self.speed + except AttributeError: + raise ParseError + + def __call__(self, params, rank): + return self.term(params, rank), self.speed(params, rank) + + def __repr__(self): + return "%s(term=%r, speed=%r)" % ( + type(self).__name__, self.term, self.speed) + +class Wait(object): + """Wait for some frames.""" + def __init__(self, doc, element): + self.frames = INumberDef(element.text) + + def __call__(self, params, rank): + return self.frames(params, rank) + + def __repr__(self): + return "%s(%r)" % (type(self).__name__, self.frames) + +class Vanish(object): + """Make the owner disappear.""" + def __init__(self, doc, element): + pass + + def __repr__(self): + return "%s()" % (type(self).__name__) + +class Repeat(object): + """Repeat an action definition.""" + + def __init__(self, doc, element): + for subelem in element.getchildren(): + tag = realtag(subelem) + if tag == "times": + self.times = INumberDef(subelem.text) + elif tag == "action": + self.action = ActionDef(doc, subelem) + elif tag == "actionRef": + self.action = ActionRef(doc, subelem) + + try: + self.times, self.action + except AttributeError: + raise ParseError + + def __call__(self, params, rank): + return self.times(params, rank), self.action(params, rank) + + def __repr__(self): + return "%s(%r, %r)" % (type(self).__name__, self.times, self.action) + +class Accel(object): + """Accelerate over some time.""" + + horizontal = None + vertical = None + + def __init__(self, doc, element): + for subelem in element.getchildren(): + tag = realtag(subelem) + if tag == "term": + self.term = INumberDef(subelem.text) + elif tag == "horizontal": + self.horizontal = Speed(doc, subelem) + elif tag == "vertical": + self.vertical = Speed(doc, subelem) + + try: + self.term + except AttributeError: + raise ParseError + + def __call__(self, params, rank): + frames = self.term(params, rank) + horizontal = self.horizontal and self.horizontal(params, rank) + vertical = self.vertical and self.vertical(params, rank) + return frames, horizontal, vertical + + def __repr__(self): + return "%s(%r, horizontal=%r, vertical=%r)" % ( + type(self).__name__, self.term, self.horizontal, self.vertical) + +class BulletDef(object): + """Bullet definition.""" + + direction = None + speed = None + + def __init__(self, doc, element): + self.actions = [] + doc.bullets[element.get("label")] = self + for subelem in element.getchildren(): + tag = realtag(subelem) + if tag == "direction": + self.direction = Direction(doc, subelem) + elif tag == "speed": + self.speed = Speed(doc, subelem) + elif tag == "action": + self.actions.append(ActionDef(doc, element)) + elif tag == "actionRef": + self.actions.append(ActionRef(doc, element)) + + def __call__(self, params, rank): + actions = [] + for action in self.actions: + try: + actions.append((action.params(params), action.action)) + except AttributeError: + actions.append((params, action)) + return (self.direction and self.direction(params, rank), + self.speed and self.speed(params, rank), + actions) + + def __repr__(self): + return "%s(direction=%r, speed=%r, actions=%r)" % ( + type(self).__name__, self.direction, self.speed, self.actions) + +class BulletRef(object): + """Create a bullet by name with parameters.""" + + def __init__(self, doc, element): + self.bullet = element.get("label") + self.params = ParamList(element) + doc._bullet_refs.append(self) + + def __call__(self, params, rank): + return self.bullet(self.params(params, rank), rank) + + def __repr__(self): + return "%s(params=%r, bullet=%r)" % ( + type(self).__name__, self.params, self.bullet) + +class ActionDef(object): + """Action definition.""" + + def __init__(self, doc, element): + doc.actions[element.get("label")] = self + self.actions = [] + for subelem in element.getchildren(): + tag = realtag(subelem) + try: + ctr = dict( + repeat=Repeat, + fire=FireDef, + fireRef=FireRef, + changeSpeed=ChangeSpeed, + changeDirection=ChangeDirection, + accel=Accel, + wait=Wait, + vanish=Vanish, + action=ActionDef, + actionRef=ActionRef)[tag] + except KeyError: + continue + else: + self.actions.append(ctr(doc, subelem)) + + def __call__(self, params, rank): + return self.actions, params + + def __repr__(self): + return "%s(%r)" % (type(self).__name__, self.actions) + +class ActionRef(object): + """Run an action by name with parameters.""" + + def __init__(self, doc, element): + self.action = element.get("label") + self.params = ParamList(element) + doc._action_refs.append(self) + + def __call__(self, params, rank): + return self.action(self.params(params, rank), rank) + + def __repr__(self): + return "%s(params=%r, action=%r)" % ( + type(self).__name__, self.params, self.action) + +class FireDef(object): + """Fire definition (creates a bullet).""" + + direction = None + speed = None + + def __init__(self, doc, element): + doc.fires[element.get("label")] = self + for subelem in element.getchildren(): + tag = realtag(subelem) + if tag == "direction": + self.direction = Direction(doc, subelem) + elif tag == "speed": + self.speed = Speed(doc, subelem) + elif tag == "bullet": + self.bullet = BulletDef(doc, subelem) + elif tag == "bulletRef": + self.bullet = BulletRef(doc, subelem) + try: + self.bullet + except AttributeError: + raise ParseError + + def __call__(self, params, rank): + direction, speed, actions = self.bullet(params, rank) + if self.direction: + direction = self.direction(params, rank) + if self.speed: + speed = self.speed(params, rank) + return direction, speed, actions + + def __repr__(self): + return "%s(direction=%r, speed=%r, bullet=%r)" % ( + type(self).__name__, self.direction, self.speed, self.bullet) + +class FireRef(object): + """Fire a bullet by name with parameters.""" + + def __init__(self, doc, element): + self.fire = element.get("label") + self.params = ParamList(element) + doc._fire_refs.append(self) + + def __call__(self, params, rank): + """Generate a Bullet from the FireDef and params.""" + return self.fire(self.params(params, rank), rank) + + def __repr__(self): + return "%s(params=%r, fire=%r)" % ( + type(self).__name__, self.params, self.fire) + +class BulletML(object): + """BulletML document. + + A BulletML document is a collection of bullets, actions, and + firings, as well as a base game type. + """ + + CONSTRUCTORS = dict( + bullet=BulletDef, + action=ActionDef, + fire=FireDef, + ) + + def __init__(self, source): + self.bullets = {} + self.actions = {} + self.fires = {} + + self._bullet_refs = [] + self._action_refs = [] + self._fire_refs = [] + + if isinstance(source, (str, unicode)): + source = StringIO(source) + + tree = ElementTree() + root = tree.parse(source) + + self.type = root.get("type", "none") + + for element in root.getchildren(): + tag = realtag(element) + if tag in self.CONSTRUCTORS: + self.CONSTRUCTORS[tag](self, element) + + try: + for ref in self._bullet_refs: + ref.bullet = self.bullets[ref.bullet] + for ref in self._fire_refs: + ref.fire = self.fires[ref.fire] + for ref in self._action_refs: + ref.action = self.actions[ref.action] + except KeyError as exc: + raise ParseError("unknown reference %s" % exc) + + del(self._bullet_refs) + del(self._action_refs) + del(self._fire_refs) + + @property + def top(self): + """Get a list of all top-level actions.""" + return [dfn for name, dfn in self.actions.iteritems() + if name and name.startswith("top")] + + def __repr__(self): + return "%s(type=%r, bullets=%r, actions=%r, fires=%r)" % ( + type(self).__name__, self.type, self.bullets, self.actions, + self.fires)