m2m模型翻译
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

174 lines
5.2 KiB

6 months ago
  1. #
  2. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  3. # not use this file except in compliance with the License. You may obtain
  4. # a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  10. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  11. # License for the specific language governing permissions and limitations
  12. # under the License.
  13. from .. import lexer
  14. from .. import parser
  15. from .. import Fields, This, Child
  16. from . import arithmetic as _arithmetic
  17. from . import filter as _filter
  18. from . import iterable as _iterable
  19. from . import string as _string
  20. class ExtendedJsonPathLexer(lexer.JsonPathLexer):
  21. """Custom LALR-lexer for JsonPath"""
  22. literals = lexer.JsonPathLexer.literals + ['?', '@', '+', '*', '/', '-']
  23. tokens = (['BOOL'] +
  24. parser.JsonPathLexer.tokens +
  25. ['FILTER_OP', 'SORT_DIRECTION', 'FLOAT'])
  26. t_FILTER_OP = r'=~|==?|<=|>=|!=|<|>'
  27. def t_BOOL(self, t):
  28. r'true|false'
  29. t.value = True if t.value == 'true' else False
  30. return t
  31. def t_SORT_DIRECTION(self, t):
  32. r',?\s*(/|\\)'
  33. t.value = t.value[-1]
  34. return t
  35. def t_ID(self, t):
  36. r'@?[a-zA-Z_][a-zA-Z0-9_@\-]*'
  37. # NOTE(sileht): This fixes the ID expression to be
  38. # able to use @ for `This` like any json query
  39. t.type = self.reserved_words.get(t.value, 'ID')
  40. return t
  41. def t_FLOAT(self, t):
  42. r'-?\d+\.\d+'
  43. t.value = float(t.value)
  44. return t
  45. class ExtentedJsonPathParser(parser.JsonPathParser):
  46. """Custom LALR-parser for JsonPath"""
  47. tokens = ExtendedJsonPathLexer.tokens
  48. def __init__(self, debug=False, lexer_class=None):
  49. lexer_class = lexer_class or ExtendedJsonPathLexer
  50. super(ExtentedJsonPathParser, self).__init__(debug, lexer_class)
  51. def p_jsonpath_operator_jsonpath(self, p):
  52. """jsonpath : NUMBER operator NUMBER
  53. | FLOAT operator FLOAT
  54. | ID operator ID
  55. | NUMBER operator jsonpath
  56. | FLOAT operator jsonpath
  57. | jsonpath operator NUMBER
  58. | jsonpath operator FLOAT
  59. | jsonpath operator jsonpath
  60. """
  61. # NOTE(sileht): If we have choice between a field or a string we
  62. # always choice string, because field can be full qualified
  63. # like $.foo == foo and where string can't.
  64. for i in [1, 3]:
  65. if (isinstance(p[i], Fields) and len(p[i].fields) == 1): # noqa
  66. p[i] = p[i].fields[0]
  67. p[0] = _arithmetic.Operation(p[1], p[2], p[3])
  68. def p_operator(self, p):
  69. """operator : '+'
  70. | '-'
  71. | '*'
  72. | '/'
  73. """
  74. p[0] = p[1]
  75. def p_jsonpath_named_operator(self, p):
  76. "jsonpath : NAMED_OPERATOR"
  77. if p[1] == 'len':
  78. p[0] = _iterable.Len()
  79. elif p[1] == 'keys':
  80. p[0] = _iterable.Keys()
  81. elif p[1] == 'sorted':
  82. p[0] = _iterable.SortedThis()
  83. elif p[1].startswith("split("):
  84. p[0] = _string.Split(p[1])
  85. elif p[1].startswith("sub("):
  86. p[0] = _string.Sub(p[1])
  87. elif p[1].startswith("str("):
  88. p[0] = _string.Str(p[1])
  89. else:
  90. super(ExtentedJsonPathParser, self).p_jsonpath_named_operator(p)
  91. def p_expression(self, p):
  92. """expression : jsonpath
  93. | jsonpath FILTER_OP ID
  94. | jsonpath FILTER_OP FLOAT
  95. | jsonpath FILTER_OP NUMBER
  96. | jsonpath FILTER_OP BOOL
  97. """
  98. if len(p) == 2:
  99. left, op, right = p[1], None, None
  100. else:
  101. __, left, op, right = p
  102. p[0] = _filter.Expression(left, op, right)
  103. def p_expressions_expression(self, p):
  104. "expressions : expression"
  105. p[0] = [p[1]]
  106. def p_expressions_and(self, p):
  107. "expressions : expressions '&' expressions"
  108. # TODO(sileht): implements '|'
  109. p[0] = p[1] + p[3]
  110. def p_expressions_parens(self, p):
  111. "expressions : '(' expressions ')'"
  112. p[0] = p[2]
  113. def p_filter(self, p):
  114. "filter : '?' expressions "
  115. p[0] = _filter.Filter(p[2])
  116. def p_jsonpath_filter(self, p):
  117. "jsonpath : jsonpath '[' filter ']'"
  118. p[0] = Child(p[1], p[3])
  119. def p_sort(self, p):
  120. "sort : SORT_DIRECTION jsonpath"
  121. p[0] = (p[2], p[1] != "/")
  122. def p_sorts_sort(self, p):
  123. "sorts : sort"
  124. p[0] = [p[1]]
  125. def p_sorts_comma(self, p):
  126. "sorts : sorts sorts"
  127. p[0] = p[1] + p[2]
  128. def p_jsonpath_sort(self, p):
  129. "jsonpath : jsonpath '[' sorts ']'"
  130. sort = _iterable.SortedThis(p[3])
  131. p[0] = Child(p[1], sort)
  132. def p_jsonpath_this(self, p):
  133. "jsonpath : '@'"
  134. p[0] = This()
  135. precedence = [
  136. ('left', '+', '-'),
  137. ('left', '*', '/'),
  138. ] + parser.JsonPathParser.precedence + [
  139. ('nonassoc', 'ID'),
  140. ]
  141. def parse(path, debug=False):
  142. return ExtentedJsonPathParser(debug=debug).parse(path)