c8bc990b105bfab652326be334a7db4b0be6a820
3 This is based on the format described at
4 http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/bulletml_ref_e.html.
6 Unless you are adding support for new actions, the only class you
7 should care about in here is BulletML.
10 from __future__
import division
12 from math
import sin
, cos
, radians
, pi
as PI
14 from xml
.etree
.ElementTree
import ElementTree
16 # Python 3 moved this for no really good reason.
18 from sys
import intern
23 from io
import StringIO
26 from cStringIO
import StringIO
28 from StringIO
import StringIO
30 from bulletml
.errors
import Error
31 from bulletml
.expr
import NumberDef
, INumberDef
34 __all__
= ["ParseError", "BulletML"]
38 class ParseError(Error
):
39 """Raised when an error occurs parsing the XML structure."""
43 """Strip namespace poop off the front of a tag."""
45 return element
.tag
.rsplit('}', 1)[1]
49 class ParamList(object):
50 """List of parameter definitions."""
52 def __init__(self
, params
=()):
53 self
.params
= list(params
)
56 def FromXML(cls
, doc
, element
):
57 """Construct using an ElementTree-style element."""
58 return cls([NumberDef(subelem
.text
) for subelem
in element
59 if realtag(subelem
) == "param"])
61 def __call__(self
, params
, rank
):
62 return [param(params
, rank
) for param
in self
.params
]
65 return "%s(%r)" % (type(self
).__name
__, self
.params
)
67 class Direction(object):
68 """Raw direction value."""
70 VALID_TYPES
= ["relative", "absolute", "aim", "sequence"]
72 def __init__(self
, type, value
):
73 if type not in self
.VALID_TYPES
:
74 raise ValueError("invalid type %r" % type)
75 self
.type = intern(type)
78 def __getstate__(self
):
79 return [('type', self
.type), ('value', self
.value
.expr
)]
81 def __setstate__(self
, state
):
83 self
.__init
__(state
["type"], NumberDef(state
["value"]))
86 def FromXML(cls
, doc
, element
, default
="absolute"):
87 """Construct using an ElementTree-style element."""
88 return cls(element
.get("type", default
), NumberDef(element
.text
))
90 def __call__(self
, params
, rank
):
91 return (radians(self
.value(params
, rank
)), self
.type)
94 return "%s(%r, type=%r)" % (
95 type(self
).__name
__, self
.value
, self
.type)
97 class ChangeDirection(object):
98 """Direction change over time."""
100 def __init__(self
, term
, direction
):
102 self
.direction
= direction
104 def __getstate__(self
):
105 return [('frames', self
.term
.expr
),
106 ('type', self
.direction
.type),
107 ('value', self
.direction
.value
.expr
)]
109 def __setstate__(self
, state
):
111 self
.__init
__(INumberDef(state
["frames"]),
112 Direction(state
["type"], NumberDef(state
["value"])))
115 def FromXML(cls
, doc
, element
):
116 """Construct using an ElementTree-style element."""
117 for subelem
in element
.getchildren():
118 tag
= realtag(subelem
)
119 if tag
== "direction":
120 direction
= Direction
.FromXML(doc
, subelem
)
122 term
= INumberDef(subelem
.text
)
124 return cls(term
, direction
)
125 except UnboundLocalError as exc
:
126 raise ParseError(str(exc
))
128 def __call__(self
, owner
, action
, params
, rank
, created
):
129 frames
= self
.term(params
, rank
)
130 direction
, type = self
.direction(params
, rank
)
131 action
.direction_frames
= frames
132 action
.aiming
= False
133 if type == "sequence":
134 action
.direction
= direction
136 if type == "absolute":
137 direction
-= owner
.direction
138 elif type != "relative": # aim or default
140 direction
+= owner
.aim
- owner
.direction
142 # Normalize to [-pi, pi).
143 direction
= (direction
+ PI
) % PI_2
- PI
145 owner
.direction
+= direction
147 action
.direction
= direction
/ frames
150 return "%s(term=%r, direction=%r)" % (
151 type(self
).__name
__, self
.term
, self
.direction
)
154 """Raw speed value."""
156 VALID_TYPES
= ["relative", "absolute", "sequence"]
158 def __init__(self
, type, value
):
159 if type not in self
.VALID_TYPES
:
160 raise ValueError("invalid type %r" % type)
161 self
.type = intern(type)
164 def __getstate__(self
):
165 return [('type', self
.type), ('value', self
.value
.expr
)]
167 def __setstate__(self
, state
):
169 self
.__init
__(state
["type"], NumberDef(state
["value"]))
172 def FromXML(cls
, doc
, element
):
173 """Construct using an ElementTree-style element."""
174 return cls(element
.get("type", "absolute"), NumberDef(element
.text
))
176 def __call__(self
, params
, rank
):
177 return (self
.value(params
, rank
), self
.type)
180 return "%s(%r, type=%r)" % (type(self
).__name
__, self
.value
, self
.type)
182 class ChangeSpeed(object):
183 """Speed change over time."""
185 def __init__(self
, term
, speed
):
189 def __getstate__(self
):
190 return [('frames', self
.term
.expr
),
191 ('type', self
.speed
.type),
192 ('value', self
.speed
.value
.expr
)]
194 def __setstate__(self
, state
):
196 self
.__init
__(INumberDef(state
["frames"]),
197 Speed(state
["type"], NumberDef(state
["value"])))
200 def FromXML(cls
, doc
, element
):
201 """Construct using an ElementTree-style element."""
202 for subelem
in element
.getchildren():
203 tag
= realtag(subelem
)
205 speed
= Speed
.FromXML(doc
, subelem
)
207 term
= INumberDef(subelem
.text
)
209 return cls(term
, speed
)
210 except UnboundLocalError as exc
:
211 raise ParseError(str(exc
))
213 def __call__(self
, owner
, action
, params
, rank
, created
):
214 frames
= self
.term(params
, rank
)
215 speed
, type = self
.speed(params
, rank
)
216 action
.speed_frames
= frames
218 if type == "absolute":
220 elif type == "relative":
222 elif type == "sequence":
224 elif type == "relative":
225 action
.speed
= speed
/ frames
227 action
.speed
= (speed
- owner
.speed
) / frames
230 return "%s(term=%r, speed=%r)" % (
231 type(self
).__name
__, self
.term
, self
.speed
)
234 """Wait for some frames."""
236 def __init__(self
, frames
):
239 def __getstate__(self
):
240 return dict(frames
=self
.frames
.expr
)
242 def __setstate__(self
, state
):
243 self
.__init
__(INumberDef(state
["frames"]))
246 def FromXML(cls
, doc
, element
):
247 """Construct using an ElementTree-style element."""
248 return cls(INumberDef(element
.text
))
250 def __call__(self
, owner
, action
, params
, rank
, created
):
251 action
.wait_frames
= self
.frames(params
, rank
)
255 return "%s(%r)" % (type(self
).__name
__, self
.frames
)
258 """Set a bullet tag."""
260 def __init__(self
, tag
):
263 def __getstate__(self
):
264 return dict(tag
=self
.tag
)
266 def __setstate__(self
, state
):
267 self
.__init
__(state
["tag"])
270 def FromXML(cls
, doc
, element
):
271 """Construct using an ElementTree-style element."""
272 return cls(element
.text
)
274 def __call__(self
, owner
, action
, params
, rank
, created
):
275 owner
.tags
.add(self
.tag
)
278 """Unset a bullet tag."""
280 def __init__(self
, tag
):
283 def __getstate__(self
):
284 return dict(tag
=self
.tag
)
286 def __setstate__(self
, state
):
287 self
.__init
__(state
["tag"])
290 def FromXML(cls
, doc
, element
):
291 """Construct using an ElementTree-style element."""
292 return cls(element
.text
)
294 def __call__(self
, owner
, action
, params
, rank
, created
):
296 owner
.tags
.remove(self
.tag
)
300 class Appearance(object):
301 """Set a bullet appearance."""
303 def __init__(self
, appearance
):
304 self
.appearance
= appearance
306 def __getstate__(self
):
307 return dict(appearance
=self
.appearance
)
309 def __setstate__(self
, state
):
310 self
.__init
__(state
["appearance"])
313 def FromXML(cls
, doc
, element
):
314 """Construct using an ElementTree-style element."""
315 return cls(element
.text
)
317 def __call__(self
, owner
, action
, params
, rank
, created
):
318 owner
.apearance
= self
.appearance
320 class Vanish(object):
321 """Make the owner disappear."""
327 def FromXML(cls
, doc
, element
):
328 """Construct using an ElementTree-style element."""
332 return "%s()" % (type(self
).__name
__)
334 def __call__(self
, owner
, action
, params
, rank
, created
):
338 class Repeat(object):
339 """Repeat an action definition."""
341 def __init__(self
, times
, action
):
345 def __getstate__(self
):
346 return [('times', self
.times
.expr
), ('action', self
.action
)]
348 def __setstate__(self
, state
):
350 self
.__init
__(INumberDef(state
["times"]), state
["action"])
353 def FromXML(cls
, doc
, element
):
354 """Construct using an ElementTree-style element."""
355 for subelem
in element
.getchildren():
356 tag
= realtag(subelem
)
358 times
= INumberDef(subelem
.text
)
359 elif tag
== "action":
360 action
= ActionDef
.FromXML(doc
, subelem
)
361 elif tag
== "actionRef":
362 action
= ActionRef
.FromXML(doc
, subelem
)
364 return cls(times
, action
)
365 except UnboundLocalError as exc
:
366 raise ParseError(str(exc
))
368 def __call__(self
, owner
, action
, params
, rank
, created
):
369 repeat
= self
.times(params
, rank
)
370 actions
, params
= self
.action(params
, rank
)
371 child
= action
.Child(actions
, params
, rank
, repeat
)
372 owner
.replace(action
, child
)
373 child
.step(owner
, created
)
377 return "%s(%r, %r)" % (type(self
).__name
__, self
.times
, self
.action
)
380 """Conditional actions."""
382 def __init__(self
, cond
, then
, else_
=None):
387 def __getstate__(self
):
389 return [('cond', self
.cond
.expr
),
391 ('else', self
.else_
)]
393 return [('cond', self
.cond
.expr
), ('then', self
.then
)]
395 def __setstate__(self
, state
):
397 state
["else_"] = state
.pop("else", None)
398 state
["cond"] = INumberDef(state
["cond"])
399 self
.__init
__(**state
)
402 def FromXML(cls
, doc
, element
):
403 """Construct using an ElementTree-style element."""
405 for subelem
in element
.getchildren():
406 tag
= realtag(subelem
)
408 cond
= INumberDef(subelem
.text
)
410 then
= ActionDef
.FromXML(doc
, subelem
)
412 else_
= ActionDef
.FromXML(doc
, subelem
)
414 return cls(cond
, then
, else_
)
415 except UnboundLocalError as exc
:
416 raise ParseError(str(exc
))
418 def __call__(self
, owner
, action
, params
, rank
, created
):
419 if self
.cond(params
, rank
):
425 actions
, params
= branch(params
, rank
)
426 child
= action
.Child(actions
, params
, rank
)
427 owner
.replace(action
, child
)
428 child
.step(owner
, created
)
432 """Accelerate over some time."""
437 def __init__(self
, term
, horizontal
=None, vertical
=None):
439 self
.horizontal
= horizontal
440 self
.vertical
= vertical
442 def __getstate__(self
):
443 state
= [('frames', self
.term
.expr
)]
445 state
.append(('horizontal', self
.horizontal
))
447 state
.append(('vertical', self
.vertical
))
450 def __setstate__(self
, state
):
452 self
.__init
__(INumberDef(state
["frames"]), state
.get("horizontal"),
453 state
.get("vertical"))
456 def FromXML(cls
, doc
, element
):
457 """Construct using an ElementTree-style element."""
461 for subelem
in element
.getchildren():
462 tag
= realtag(subelem
)
464 term
= INumberDef(subelem
.text
)
465 elif tag
== "horizontal":
466 horizontal
= Speed
.FromXML(doc
, subelem
)
467 elif tag
== "vertical":
468 vertical
= Speed
.FromXML(doc
, subelem
)
471 return cls(term
, horizontal
, vertical
)
472 except AttributeError:
475 def __call__(self
, owner
, action
, params
, rank
, created
):
476 frames
= self
.term(params
, rank
)
477 horizontal
= self
.horizontal
and self
.horizontal(params
, rank
)
478 vertical
= self
.vertical
and self
.vertical(params
, rank
)
479 action
.accel_frames
= frames
481 mx
, type = horizontal
483 if type == "absolute":
485 elif type == "relative":
487 elif type == "sequence":
489 elif type == "absolute":
490 action
.mx
= (mx
- owner
.mx
) / frames
491 elif type == "relative":
492 action
.mx
= mx
/ frames
496 if type == "absolute":
498 elif type == "relative":
500 elif type == "sequence":
502 elif type == "absolute":
503 action
.my
= (my
- owner
.my
) / frames
504 elif type == "relative":
505 action
.my
= my
/ frames
508 return "%s(%r, horizontal=%r, vertical=%r)" % (
509 type(self
).__name
__, self
.term
, self
.horizontal
, self
.vertical
)
511 class BulletDef(object):
512 """Bullet definition."""
514 def __init__(self
, actions
=(), direction
=None, speed
=None, tags
=(),
516 self
.direction
= direction
518 self
.actions
= list(actions
)
519 self
.tags
= set(tags
)
520 self
.appearance
= appearance
522 def __getstate__(self
):
525 state
.append(("direction", self
.direction
))
527 state
.append(("speed", self
.speed
))
529 state
.append(("actions", self
.actions
))
531 state
.append(("tags", list(self
.tags
)))
533 state
.append(("appearance", self
.appearance
))
536 def __setstate__(self
, state
):
538 self
.__init
__(**state
)
541 def FromXML(cls
, doc
, element
):
542 """Construct using an ElementTree-style element."""
547 for subelem
in element
.getchildren():
548 tag
= realtag(subelem
)
549 if tag
== "direction":
550 direction
= Direction
.FromXML(doc
, subelem
)
552 speed
= Speed
.FromXML(doc
, subelem
)
553 elif tag
== "action":
554 actions
.append(ActionDef
.FromXML(doc
, subelem
))
555 elif tag
== "actionRef":
556 actions
.append(ActionRef
.FromXML(doc
, subelem
))
558 tags
.add(subelem
.text
)
559 dfn
= cls(actions
, direction
, speed
, tags
)
560 doc
._bullets
[element
.get("label")] = dfn
563 def __call__(self
, params
, rank
):
564 actions
= [action(params
, rank
) for action
in self
.actions
]
566 self
.direction
and self
.direction(params
, rank
),
567 self
.speed
and self
.speed(params
, rank
),
573 return "%s(direction=%r, speed=%r, actions=%r)" % (
574 type(self
).__name
__, self
.direction
, self
.speed
, self
.actions
)
576 class BulletRef(object):
577 """Create a bullet by name with parameters."""
579 def __init__(self
, bullet
, params
=None):
581 self
.params
= ParamList() if params
is None else params
583 def __getstate__(self
):
585 if self
.params
.params
:
586 params
= [param
.expr
for param
in self
.params
.params
]
587 state
.append(("params", params
))
588 state
.append(('bullet', self
.bullet
))
591 def __setstate__(self
, state
):
593 bullet
= state
["bullet"]
594 params
= [NumberDef(param
) for param
in state
.get("params", [])]
595 self
.__init
__(bullet
, ParamList(params
))
598 def FromXML(cls
, doc
, element
):
599 """Construct using an ElementTree-style element."""
600 bullet
= cls(element
.get("label"), ParamList
.FromXML(doc
, element
))
601 doc
._bullet
_refs
.append(bullet
)
604 def __call__(self
, params
, rank
):
605 return self
.bullet(self
.params(params
, rank
), rank
)
608 return "%s(params=%r, bullet=%r)" % (
609 type(self
).__name
__, self
.params
, self
.bullet
)
611 class ActionDef(object):
612 """Action definition.
614 To support parsing new actions, add tags to
615 ActionDef.CONSTRUCTORS. It maps tag names to classes with a
616 FromXML classmethod, which take the BulletML instance and
617 ElementTree element as arguments.
620 # This is self-referential, so it's filled in later.
621 CONSTRUCTORS
= dict()
623 def __init__(self
, actions
):
624 self
.actions
= list(actions
)
626 def __getstate__(self
):
627 return dict(actions
=self
.actions
)
629 def __setstate__(self
, state
):
631 self
.__init
__(state
["actions"])
634 def FromXML(cls
, doc
, element
):
635 """Construct using an ElementTree-style element."""
637 for subelem
in element
.getchildren():
638 tag
= realtag(subelem
)
640 ctr
= cls
.CONSTRUCTORS
[tag
]
644 actions
.append(ctr
.FromXML(doc
, subelem
))
646 doc
._actions
[element
.get("label")] = dfn
649 def __call__(self
, params
, rank
):
650 return self
.actions
, params
653 return "%s(%r)" % (type(self
).__name
__, self
.actions
)
655 class ActionRef(object):
656 """Run an action by name with parameters."""
658 def __init__(self
, action
, params
=None):
660 self
.params
= params
or ParamList()
662 def __getstate__(self
):
664 if self
.params
.params
:
665 params
= [param
.expr
for param
in self
.params
.params
]
666 state
.append(("params", params
))
667 state
.append(('action', self
.action
))
670 def __setstate__(self
, state
):
672 action
= state
["action"]
673 params
= [NumberDef(param
) for param
in state
.get("params", [])]
674 self
.__init
__(action
, ParamList(params
))
677 def FromXML(cls
, doc
, element
):
678 """Construct using an ElementTree-style element."""
679 action
= cls(element
.get("label"), ParamList
.FromXML(doc
, element
))
680 doc
._action
_refs
.append(action
)
683 def __call__(self
, params
, rank
):
684 return self
.action(self
.params(params
, rank
), rank
)
687 return "%s(params=%r, action=%r)" % (
688 type(self
).__name
__, self
.params
, self
.action
)
690 class Offset(object):
691 """Provide an offset to a bullet's initial position."""
693 VALID_TYPES
= ["relative", "absolute"]
695 def __init__(self
, type, x
, y
):
696 if type not in self
.VALID_TYPES
:
697 raise ValueError("invalid type %r" % type)
698 self
.type = intern(type)
702 def __getstate__(self
):
703 state
= [('type', self
.type)]
705 state
.append(('x', self
.x
.expr
))
707 state
.append(('y', self
.y
.expr
))
710 def __setstate__(self
, state
):
712 x
= NumberDef(state
["x"]) if "x" in state
else None
713 y
= NumberDef(state
["y"]) if "y" in state
else None
714 self
.__init
__(state
["type"], x
, y
)
717 def FromXML(cls
, doc
, element
):
718 """Construct using an ElementTree-style element."""
719 type = element
.get("type", "relative")
722 for subelem
in element
:
723 tag
= realtag(subelem
)
725 x
= NumberDef(subelem
.text
)
727 y
= NumberDef(subelem
.text
)
728 return cls(type, x
, y
)
730 def __call__(self
, params
, rank
):
731 return (self
.x(params
, rank
) if self
.x
else 0,
732 self
.y(params
, rank
) if self
.y
else 0)
734 class FireDef(object):
735 """Fire definition (creates a bullet)."""
737 def __init__(self
, bullet
, direction
=None, speed
=None, offset
=None,
738 tags
=(), appearance
=None):
740 self
.direction
= direction
743 self
.tags
= set(tags
)
744 self
.appearance
= appearance
746 def __getstate__(self
):
749 state
.append(("direction", self
.direction
))
751 state
.append(("speed", self
.speed
))
753 state
.append(("offset", self
.offset
))
755 state
.append(("tags", list(self
.tags
)))
757 state
.append(("appearance", self
.appearance
))
759 params
= self
.bullet
.params
760 except AttributeError:
761 state
.append(('bullet', self
.bullet
))
764 state
.append(('bullet', self
.bullet
))
766 # Strip out empty BulletRefs.
767 state
.append(('bullet', self
.bullet
.bullet
))
770 def __setstate__(self
, state
):
772 self
.__init
__(**state
)
775 def FromXML(cls
, doc
, element
):
776 """Construct using an ElementTree-style element."""
783 for subelem
in element
.getchildren():
784 tag
= realtag(subelem
)
785 if tag
== "direction":
786 direction
= Direction
.FromXML(doc
, subelem
, "aim")
788 speed
= Speed
.FromXML(doc
, subelem
)
789 elif tag
== "bullet":
790 bullet
= BulletDef
.FromXML(doc
, subelem
)
791 elif tag
== "bulletRef":
792 bullet
= BulletRef
.FromXML(doc
, subelem
)
793 elif tag
== "offset":
794 offset
= Offset
.FromXML(doc
, subelem
)
796 tags
.add(subelem
.text
)
797 elif tag
== "appearance":
798 appearance
= subelem
.text
800 fire
= cls(bullet
, direction
, speed
, offset
, tags
, appearance
)
801 except UnboundLocalError as exc
:
802 raise ParseError(str(exc
))
804 doc
._fires
[element
.get("label")] = fire
807 def __call__(self
, owner
, action
, params
, rank
, created
):
808 direction
, speed
, tags
, appearance
, actions
= self
.bullet(params
, rank
)
810 direction
= self
.direction(params
, rank
)
812 speed
= self
.speed(params
, rank
)
813 tags
= tags
.union(self
.tags
)
815 appearance
= self
.appearance
818 direction
, type = direction
819 if type == "aim" or type is None:
820 direction
+= owner
.aim
821 elif type == "sequence":
822 direction
+= action
.previous_fire_direction
823 elif type == "relative":
824 direction
+= owner
.direction
826 direction
= owner
.aim
827 action
.previous_fire_direction
= direction
831 if type == "sequence":
832 speed
+= action
.previous_fire_speed
833 elif type == "relative":
834 # The reference Noiz implementation uses
835 # prvFireSpeed here, but the standard is
836 # pretty clear -- "In case of the type is
837 # "relative", ... the speed is relative to the
838 # speed of this bullet."
842 action
.previous_fire_speed
= speed
844 x
, y
= owner
.x
, owner
.y
846 off_x
, off_y
= self
.offset(params
, rank
)
847 if self
.offset
.type == "relative":
850 x
+= c
* off_x
+ s
* off_y
851 y
+= s
* off_x
- c
* off_y
856 if appearance
is None:
857 appearance
= owner
.appearance
858 bullet
= owner
.__class
__(
859 x
=x
, y
=y
, direction
=direction
, speed
=speed
,
860 target
=owner
.target
, actions
=actions
, rank
=rank
,
861 appearance
=appearance
, tags
=tags
, Action
=action
.__class
__)
862 created
.append(bullet
)
865 return "%s(direction=%r, speed=%r, bullet=%r)" % (
866 type(self
).__name
__, self
.direction
, self
.speed
, self
.bullet
)
868 class FireRef(object):
869 """Fire a bullet by name with parameters."""
871 def __init__(self
, fire
, params
=None):
873 self
.params
= params
or ParamList()
875 def __getstate__(self
):
877 if self
.params
.params
:
878 params
= [param
.expr
for param
in self
.params
.params
]
879 state
.append(("params", params
))
880 state
.append(('fire', self
.fire
))
883 def __setstate__(self
, state
):
886 params
= [NumberDef(param
) for param
in state
.get("params", [])]
887 self
.__init
__(fire
, ParamList(params
))
890 def FromXML(cls
, doc
, element
):
891 """Construct using an ElementTree-style element."""
892 fired
= cls(element
.get("label"), ParamList
.FromXML(doc
, element
))
893 doc
._fire
_refs
.append(fired
)
896 def __call__(self
, owner
, action
, params
, rank
, created
):
897 params
= self
.params(params
, rank
)
898 return self
.fire(owner
, action
, params
, rank
, created
)
901 return "%s(params=%r, fire=%r)" % (
902 type(self
).__name
__, self
.params
, self
.fire
)
904 class BulletML(object):
905 """BulletML document.
907 A BulletML document is a collection of top-level actions and the
910 You can add tags to the BulletML.CONSTRUCTORS dictionary to extend
911 its parsing. It maps tag names to classes with a FromXML
912 classmethod, which take the BulletML instance and ElementTree
913 element as arguments.
923 def __init__(self
, type="none", actions
=None):
924 self
.type = intern(type)
925 self
.actions
= [] if actions
is None else actions
927 def __getstate__(self
):
928 return [('type', self
.type), ('actions', self
.actions
)]
930 def __setstate__(self
, state
):
932 self
.__init
__(state
["type"], actions
=state
.get("actions"))
935 def FromXML(cls
, source
):
936 """Return a BulletML instance based on XML."""
937 if not hasattr(source
, 'read'):
938 source
= StringIO(source
)
941 root
= tree
.parse(source
)
943 doc
= cls(type=root
.get("type", "none"))
948 doc
._bullet
_refs
= []
949 doc
._action
_refs
= []
952 for element
in root
.getchildren():
953 tag
= realtag(element
)
954 if tag
in doc
.CONSTRUCTORS
:
955 doc
.CONSTRUCTORS
[tag
].FromXML(doc
, element
)
958 for ref
in doc
._bullet
_refs
:
959 ref
.bullet
= doc
._bullets
[ref
.bullet
]
960 for ref
in doc
._fire
_refs
:
961 ref
.fire
= doc
._fires
[ref
.fire
]
962 for ref
in doc
._action
_refs
:
963 ref
.action
= doc
._actions
[ref
.action
]
964 except KeyError as exc
:
965 raise ParseError("unknown reference %s" % exc
)
967 doc
.actions
= [act
for name
, act
in doc
._actions
.items()
968 if name
and name
.startswith("top")]
970 del(doc
._bullet
_refs
)
971 del(doc
._action
_refs
)
980 def FromYAML(cls
, source
):
981 """Create a BulletML instance based on YAML."""
983 # Late import to avoid a circular dependency.
985 import bulletml
.bulletyaml
988 raise ParseError("PyYAML is not available")
991 return yaml
.load(source
)
992 except Exception as exc
:
993 raise ParseError(str(exc
))
996 def FromDocument(cls
, source
):
997 """Create a BulletML instance based on a seekable file or string.
999 This attempts to autodetect if the stream is XML or YAML.
1001 if not hasattr(source
, 'read'):
1002 source
= StringIO(source
)
1003 start
= source
.read(1)
1006 return cls
.FromXML(source
)
1007 elif start
== "!" or start
== "#":
1008 return cls
.FromYAML(source
)
1010 raise ParseError("unknown initial character %r" % start
)
1013 return "%s(type=%r, actions=%r)" % (
1014 type(self
).__name
__, self
.type, self
.actions
)
1016 ActionDef
.CONSTRUCTORS
= dict(
1020 changeSpeed
=ChangeSpeed
,
1021 changeDirection
=ChangeDirection
,
1026 appearance
=Appearance
,
1029 actionRef
=ActionRef
)
1030 ActionDef
.CONSTRUCTORS
["if"] = If