Change the semantic analysis pass to only index the rules applicable to each start...
[pilex.git] / element.py
1 import xml.etree.ElementTree
2
3 class Element(xml.etree.ElementTree._Element_Py):
4   def __init__(self, tag = 'Element', attrib = {}, text = '', children = []):
5     xml.etree.ElementTree._Element_Py.__init__(self, tag, attrib)
6     self.ref = -1
7     self.seen = False
8     set_text(self, 0, text)
9     self[:] = children
10   def serialize(self, ref_list):
11     for i in self:
12       # parented, enforce that child can only be parented at most once
13       # (although there can be unlimited numbers of numeric refs to it)
14       assert not i.seen
15       i.seen = True
16       if i.ref == -1:
17         i.serialize(ref_list)
18   def deserialize(self, ref_list):
19     for i in self:
20       i.deserialize(ref_list)
21   def copy(self, factory = None):
22     result = (Element if factory is None else factory)(self.tag, self.attrib)
23     result.text = self.text
24     result.tail = self.tail
25     result[:] = [i.copy() for i in self]
26     return result
27   def repr_serialize(self, params):
28     if len(self):
29       params.append(
30         'children = [{0:s}]'.format(
31           ', '.join([repr(i) for i in self])
32         )
33       )
34   def __repr__(self):
35     params = []
36     self.repr_serialize(params)
37     return 'element.Element({0:s})'.format(', '.join(params))
38
39 bool_to_str = ['false', 'true']
40 def serialize_bool(value):
41   return bool_to_str[int(value)]
42
43 str_to_bool = {'false': False, 'true': True}
44 def deserialize_bool(text):
45   return str_to_bool[text]
46
47 def serialize_int(value):
48   return str(value)
49
50 def deserialize_int(text):
51   return int(text)
52
53 def serialize_ref(value, ref_list):
54   if value is None:
55     ref = -1
56   else:
57     ref = value.ref
58     if ref == -1:
59       ref = len(ref_list)
60       ref_list.append(value)
61       value.ref = ref
62       value.set('ref', str(ref))
63       # this doesn't set the seen flag, so it will be parented by the
64       # root, unless it is already parented or gets parented later on
65       if not value.seen:
66         value.serialize(ref_list)
67   return str(ref)
68
69 def deserialize_ref(text, ref_list):
70   ref = int(text)
71   return None if ref < 0 else ref_list[ref]
72
73 def serialize_str(value):
74   return value
75
76 def deserialize_str(text):
77   return text
78
79 def serialize(value, fout, encoding = 'unicode'):
80   ref_list = []
81   serialize_ref(value, ref_list)
82   parents = [i for i in ref_list if not i.seen]
83   root = Element('root', children = parents)
84   for i in range(len(root)):
85     set_text(root, i, '\n  ')
86   set_text(root, len(root), '\n')
87   root.tail = '\n'
88   xml.etree.ElementTree.ElementTree(root).write(fout, encoding)
89   for i in root:
90     i.tail = None
91   for i in ref_list:
92     i.ref = -1
93     del i.attrib['ref']
94   i = 0
95   while i < len(parents):
96     for j in parents[i]:
97       j.seen = False
98       parents.append(j)
99     i += 1
100
101 def deserialize(fin, factory = Element, encoding = 'unicode'):
102   root = xml.etree.ElementTree.parse(
103     fin,
104     xml.etree.ElementTree.XMLParser(
105       target = xml.etree.ElementTree.TreeBuilder(factory),
106       encoding = encoding
107     )
108   ).getroot()
109   assert root.tag == 'root'
110   for i in root:
111     i.tail = None
112   i = 0
113   parents = root[:]
114   ref_list = []
115   while i < len(parents):
116     j = parents[i]
117     if 'ref' in j.attrib:
118       ref = int(j.attrib['ref'])
119       del j.attrib['ref']
120       if len(ref_list) < ref + 1:
121         ref_list.extend([None] * (ref + 1 - len(ref_list)))
122       ref_list[ref] = j
123     parents.extend(j[:])
124     i += 1
125   for i in root:
126     i.deserialize(ref_list)
127   return ref_list[0]
128
129 # compatibility scheme to access arbitrary xml.etree.ElementTree.Element-like
130 # objects (not just Element defined above) using a more consistent interface:
131 def get_text(root, i):
132   if i < 0:
133     i += len(root) + 1
134   text = root.text if i == 0 else root[i - 1].tail
135   return '' if text is None else text
136
137 def set_text(root, i, text):
138   if i < 0:
139     i += len(root) + 1
140   if len(text) == 0:
141     text = None
142   if i == 0:
143     root.text = text
144   else:
145     root[i - 1].tail = text
146
147 def to_end_relative(root, pos, off):
148   assert pos >= 0 and off >= 0
149   off -= len(get_text(root, pos))
150   pos -= len(root) + 1
151   return pos, off
152
153 def to_start_relative(root, pos, off):
154   assert pos < 0 and off <= 0
155   pos += len(root) + 1
156   off += len(get_text(root, pos))
157   return pos, off