Project

General

Profile

1
# XPath parsing
2

    
3
import copy
4

    
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
        str_ += self.name
21
        if self.attrs != []: str_ += repr(self.attrs)
22
        if self.value != None: str_ += '='+repr(self.value)
23
        if self.is_ptr: str_ += '->'
24
        return str_
25
    
26
    def __eq__(self, other): return self.__dict__ == other.__dict__
27

    
28
def value(path): return path[-1].value
29

    
30
def set_value(path, value): path[-1].value = value
31

    
32
def backward_id(elem):
33
    if len(elem.attrs) >= 1 and value(elem.attrs[0]) == None and\
34
    len(elem.attrs[0]) == 1: return elem.attrs[0]
35
    else: return None
36

    
37
def parse(str_):
38
    parser = Parser(str_)
39
    
40
    def _path():
41
        tree = []
42
        trailing_slash = False
43
        while True:
44
            # Split path
45
            if parser.str_('{'):
46
                paths = []
47
                while True:
48
                    paths.append(tree + _path())
49
                    if not parser.str_(','): break
50
                parser.str_('}', required=True)
51
                tree = paths[0] # just use first subpath for now
52
                break # nothing allowed after split path
53
            
54
            elem = XpathElem(is_attr=parser.str_('@'),
55
                name=parser.re(r'[\w.*]+', required=True))
56
            
57
            # Attrs
58
            if parser.str_('['):
59
                elem.attrs = []
60
                while True:
61
                    path = _path()
62
                    if parser.str_('='):
63
                        set_value(path, parser.re(r'[\w.|]*'))
64
                    elem.attrs.append(path)
65
                    if not parser.str_(','): break
66
                parser.str_(']', required=True)
67
            
68
            elem.is_ptr = parser.str_('->')
69
            tree.append(elem)
70
            
71
            # Lookahead assertion
72
            if parser.str_('('):
73
                parser.str_('/', required=True) # next / is inside ()
74
                path = _path()
75
                parser.str_(')', required=True)
76
                elem.attrs.append(path)
77
                tree += path
78
            
79
            if not parser.str_('/'): break
80
        
81
        # Expand * abbrs
82
        for i in reversed(xrange(len(tree))):
83
            elem = tree[i]
84
            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
                try: elem.name = before+tree[i+offset].name+after
91
                except IndexError: pass # no replacement elem
92
        
93
        return tree
94
    
95
    parser.str_('/') # optional leading /
96
    path = _path()
97
    parser.end()
98
    return path
99

    
100
instance_level = 1
101

    
102
def obj(path):
103
    obj_path = copy.deepcopy(path[:instance_level+1])
104
    obj_path[-1].is_ptr = False # prevent pointer w/o target
105
    return obj_path
106

    
107
def set_id(path, id_, has_types=True):
108
    if has_types: id_level = instance_level
109
    else: id_level = 0
110
    path[id_level].attrs.append([XpathElem('id', id_, is_attr=True)])
111

    
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]) 
(10-10/10)