Project

General

Profile

1 55 aaronmk
# XPath parsing
2 21 aaronmk
3 55 aaronmk
import copy
4 21 aaronmk
5
from Parser import Parser
6
7
class XpathElem:
8
    def __init__(self, name, value=None, attrs=None, is_attr=False,
9
        is_ptr=False):
10
        if attrs == None: attrs = []
11
        self.name = name
12
        self.value = value
13
        self.attrs = attrs
14
        self.is_attr = is_attr
15
        self.is_ptr = is_ptr
16
17
    def __repr__(self):
18
        str_ = ''
19
        if self.is_attr: str_ += '@'
20 25 aaronmk
        str_ += self.name
21
        if self.attrs != []: str_ += repr(self.attrs)
22
        if self.value != None: str_ += '='+repr(self.value)
23 21 aaronmk
        if self.is_ptr: str_ += '->'
24
        return str_
25
26
    def __eq__(self, other): return self.__dict__ == other.__dict__
27
28 24 aaronmk
def value(path): return path[-1].value
29
30 22 aaronmk
def set_value(path, value): path[-1].value = value
31
32 32 aaronmk
def backward_id(elem):
33 59 aaronmk
    if len(elem.attrs) >= 1 and value(elem.attrs[0]) == None and\
34
    len(elem.attrs[0]) == 1: return elem.attrs[0]
35 32 aaronmk
    else: return None
36
37 76 aaronmk
def parse(str_):
38
    parser = Parser(str_)
39 21 aaronmk
40 76 aaronmk
    def _path():
41 21 aaronmk
        tree = []
42 38 aaronmk
        trailing_slash = False
43 21 aaronmk
        while True:
44 38 aaronmk
            # Split path
45 76 aaronmk
            if parser.str_('{'):
46 38 aaronmk
                paths = []
47
                while True:
48 76 aaronmk
                    paths.append(tree + _path())
49
                    if not parser.str_(','): break
50
                parser.str_('}', required=True)
51 38 aaronmk
                tree = paths[0] # just use first subpath for now
52
                break # nothing allowed after split path
53 36 aaronmk
54 76 aaronmk
            elem = XpathElem(is_attr=parser.str_('@'),
55
                name=parser.re(r'[\w.*]+', required=True))
56 38 aaronmk
57 36 aaronmk
            # Attrs
58 76 aaronmk
            if parser.str_('['):
59 36 aaronmk
                elem.attrs = []
60
                while True:
61 76 aaronmk
                    path = _path()
62
                    if parser.str_('='):
63
                        set_value(path, parser.re(r'[\w.|]*'))
64 36 aaronmk
                    elem.attrs.append(path)
65 76 aaronmk
                    if not parser.str_(','): break
66
                parser.str_(']', required=True)
67 36 aaronmk
68 76 aaronmk
            elem.is_ptr = parser.str_('->')
69 21 aaronmk
            tree.append(elem)
70 36 aaronmk
71
            # Lookahead assertion
72 76 aaronmk
            if parser.str_('('):
73
                parser.str_('/', required=True) # next / is inside ()
74
                path = _path()
75
                parser.str_(')', required=True)
76 36 aaronmk
                elem.attrs.append(path)
77
                tree += path
78
79 76 aaronmk
            if not parser.str_('/'): break
80 32 aaronmk
81
        # Expand * abbrs
82 70 aaronmk
        for i in reversed(xrange(len(tree))):
83
            elem = tree[i]
84 32 aaronmk
            id_ = backward_id(elem)
85
            if id_ != None: elem = id_[0]; offset = -2
86
            elif elem.is_ptr: offset = 2
87
            else: offset = 1
88
            before, abbr, after = elem.name.partition('*')
89
            if abbr != '':
90 70 aaronmk
                try: elem.name = before+tree[i+offset].name+after
91 32 aaronmk
                except IndexError: pass # no replacement elem
92
93 21 aaronmk
        return tree
94 76 aaronmk
95
    parser.str_('/') # optional leading /
96
    path = _path()
97
    parser.end()
98
    return path
99 21 aaronmk
100 26 aaronmk
instance_level = 1
101 22 aaronmk
102 26 aaronmk
def obj(path):
103 55 aaronmk
    obj_path = copy.deepcopy(path[:instance_level+1])
104 26 aaronmk
    obj_path[-1].is_ptr = False # prevent pointer w/o target
105
    return obj_path
106
107 22 aaronmk
def set_id(path, id_, has_types=True):
108 26 aaronmk
    if has_types: id_level = instance_level
109 21 aaronmk
    else: id_level = 0
110
    path[id_level].attrs.append([XpathElem('id', id_, is_attr=True)])
111 62 aaronmk
112
def is_id(path): return path[0].is_attr and path[0].name == 'id'
113
114
def is_instance(elem): return elem.attrs != [] and is_id(elem.attrs[0])