PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/pyang/xpath.py

http://pyang.googlecode.com/
Python | 181 lines | 166 code | 6 blank | 9 comment | 0 complexity | 65587d27570b862a1a8fdc405be0e8fc MD5 | raw file
Possible License(s): 0BSD
  1. import re
  2. import sys
  3. # not 100% XPath / XML, but good enough for YANG
  4. namestr=r'[a-zA-Z_][a-zA-Z0-9_\-.]*'
  5. ncnamestr = '((' + namestr + '):)?(' + namestr + ')'
  6. prefixteststr = '((' + namestr + r'):)?\*'
  7. patterns = [
  8. ('whitespace', re.compile(r'\s+')),
  9. # Expr tokens
  10. ('(', re.compile(r'\(')),
  11. (')', re.compile(r'\)')),
  12. ('[', re.compile(r'\[')),
  13. (']', re.compile(r'\]')),
  14. ('..', re.compile(r'\.\.')),
  15. ('.', re.compile(r'\.')),
  16. ('@', re.compile(r'\@')),
  17. (',', re.compile(r',')),
  18. ('::', re.compile(r'::')),
  19. # operators
  20. ('//', re.compile(r'\/\/')),
  21. ('/', re.compile(r'\/')),
  22. ('|', re.compile(r'\|')),
  23. ('+', re.compile(r'\+')),
  24. ('-', re.compile(r'-')),
  25. ('=', re.compile(r'=')),
  26. ('!=', re.compile(r'!=')),
  27. ('<=', re.compile(r'<=')),
  28. ('>=', re.compile(r'>=')),
  29. ('>', re.compile(r'>')),
  30. ('<', re.compile(r'<')),
  31. ('*', re.compile(r'\*')),
  32. # others
  33. ('number', re.compile(r'[0-9]+(\.[0-9]+)?')),
  34. ('prefix-test', re.compile(prefixteststr)),
  35. ('name', re.compile(ncnamestr)),
  36. ('attribute', re.compile(r'\@' + ncnamestr)),
  37. ('variable', re.compile(r'\$' + ncnamestr)),
  38. ('literal', re.compile(r'(\".*?\")|(\'.*?\')')),
  39. ]
  40. operators = [ 'div', 'and', 'or', 'mod' ]
  41. node_types = [ 'comment', 'text', 'processing-instruction', 'node' ]
  42. axes = [ 'ancestor-or-self', 'ancestor', 'attribute', 'child',
  43. 'descendant-or-self', 'descendant', 'following-sibling',
  44. 'following', 'namespace', 'parent', 'preceding-sibling',
  45. 'preceding', 'self' ]
  46. re_open_para = re.compile(r'\s*\(')
  47. re_axis = re.compile(r'\s*::')
  48. def validate(s):
  49. """Validate the XPath expression in the string `s`
  50. Return True if the expression is correct, and throw
  51. SyntaxError on failure."""
  52. t = tokens(s)
  53. return True
  54. def tokens(s):
  55. """Return a list of tokens, or throw SyntaxError on failure.
  56. A token is one of the patterns or:
  57. ('wildcard', '*')
  58. ('axis', axisname)
  59. """
  60. pos = 0
  61. toks = []
  62. while pos < len(s):
  63. matched = False
  64. for (tokname, r) in patterns:
  65. m = r.match(s, pos)
  66. if m is not None:
  67. # found a matching token
  68. prec = _preceding_token(toks)
  69. if tokname == '*' and prec is not None and _is_special(prec):
  70. # XPath 1.0 spec, 3.7 special rule 1a
  71. # interpret '*' as a wildcard
  72. tok = ('wildcard', m.group(0))
  73. elif (tokname == 'name' and prec is not None and
  74. not _is_special(prec)):
  75. # XPath 1.0 spec, 3.7 special rule 1b
  76. # interpret the name as an operator
  77. if m.group(0) in operators:
  78. tok = (m.group(0), m.group(0))
  79. else:
  80. e = "%s: unknown operator %s" % (pos+1, m.group(0))
  81. raise SyntaxError, e
  82. elif tokname == 'name':
  83. # check if next token is '('
  84. if re_open_para.match(s, pos + len(m.group(0))):
  85. # XPath 1.0 spec, 3.7 special rule 2
  86. if m.group(0) in node_types:
  87. # XPath 1.0 spec, 3.7 special rule 2a
  88. tok = (m.group(0), m.group(0))
  89. else:
  90. # XPath 1.0 spec, 3.7 special rule 2b
  91. tok = ('function', m.group(0))
  92. # check if next token is '::'
  93. elif re_axis.match(s, pos + len(m.group(0))):
  94. # XPath 1.0 spec, 3.7 special rule 3
  95. if m.group(0) in axes:
  96. tok = ('axis', m.group(0))
  97. else:
  98. e = "%s: unknown axis %s" % (pos+1, m.group(0))
  99. raise SyntaxError, e
  100. else:
  101. tok = ('name', m.group(0))
  102. else:
  103. tok = (tokname, m.group(0))
  104. pos += len(m.group(0))
  105. toks.append(tok)
  106. matched = True
  107. break
  108. if matched == False:
  109. # no patterns matched
  110. raise SyntaxError, "at position %s" % str(pos+1)
  111. return toks
  112. def _preceding_token(toks):
  113. if len(toks) > 1 and toks[-1][0] == 'whitespace':
  114. return toks[-2][0]
  115. if len(toks) > 0 and toks[-1][0] != 'whitespace':
  116. return toks[-1][0]
  117. return None
  118. _special_toks = [ ',', '@', '::', '(', '[', '/', '//', '|', '+', '-',
  119. '=', '!=', '<', '<=', '>', '>=',
  120. 'and', 'or', 'mod', 'div' ]
  121. def _is_special(tok):
  122. return tok in _special_toks
  123. def add_prefix(prefix, s):
  124. "Add `prefix` to all unprefixed names in `s`"
  125. # tokenize the XPath expression
  126. toks = tokens(s)
  127. # add default prefix to unprefixed names
  128. toks2 = [_add_prefix(prefix, tok) for tok in toks]
  129. # build a string of the patched expression
  130. ls = [x for (_tokname, x) in toks2]
  131. return ''.join(ls)
  132. _re_ncname = re.compile(ncnamestr)
  133. def _add_prefix(prefix, tok):
  134. (tokname, s) = tok
  135. if tokname == 'name':
  136. m = _re_ncname.match(s)
  137. if m.group(2) == None:
  138. return (tokname, prefix + ':' + s)
  139. return tok
  140. core_functions = (
  141. 'last',
  142. 'position',
  143. 'count',
  144. 'id',
  145. 'local-name',
  146. 'namespace-uri',
  147. 'name',
  148. 'string',
  149. 'concat',
  150. 'starts-with',
  151. 'contains',
  152. 'substring-before',
  153. 'substring-after',
  154. 'substring',
  155. 'string-length',
  156. 'normalize-space',
  157. 'translate',
  158. 'boolean',
  159. 'not',
  160. 'true',
  161. 'false',
  162. 'lang',
  163. 'number',
  164. 'sum',
  165. 'floor',
  166. 'ceiling',
  167. 'round',
  168. )