Advertisement
morozow

Untitled

Apr 2nd, 2020
223
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.72 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2.  
  3. from __future__ import unicode_literals
  4. import re
  5.  
  6.  
  7. class Lexeme(object):
  8.     def __init__(self,
  9.                  lexeme_type,
  10.                  start=-1,
  11.                  end=-1):
  12.         self.lexeme_type = lexeme_type
  13.         self.start = start
  14.         self.end = end
  15.  
  16.     def debug(self):
  17.         return ('lexeme_type: {}'.format(self.lexeme_type)).encode('utf-8')
  18.  
  19.  
  20. class LexemeOperator(Lexeme):
  21.     pass
  22.  
  23.  
  24. class LexemeError(Lexeme):
  25.     pass
  26.  
  27.  
  28. class LexemeValue(Lexeme):
  29.     def __init__(self,
  30.                  lexeme_type,
  31.                  text_before_asterisk='',
  32.                  start=-1,
  33.                  end=-1,
  34.                  has_asterisk=False,
  35.                  text_after_asterisk='',
  36.                  has_error=False):
  37.         super(LexemeValue, self).__init__(lexeme_type, start, end)
  38.         self.has_asterisk = has_asterisk
  39.         self.text_before_asterisk = text_before_asterisk
  40.         self.text_after_asterisk = text_after_asterisk
  41.         self.has_error = has_error
  42.  
  43.     def debug(self):
  44.         return ('lexeme_type: {}, '
  45.                 'has_asterisk: {}, '
  46.                 'text_before_asterisk: {}, '
  47.                 'text_after_asterisk: {}, '
  48.                 'has_error: {}'.format(self.lexeme_type,
  49.                                        self.has_asterisk,
  50.                                        self.text_before_asterisk,
  51.                                        self.text_after_asterisk,
  52.                                        self.has_error)).encode('utf-8')
  53.  
  54.  
  55. # noinspection RegExpRedundantEscape
  56. value_pattern = re.compile(r"[\w'\-:\*]+", re.UNICODE)
  57.  
  58.  
  59. def match_value(text, start):
  60.     if start >= len(text):
  61.         return False
  62.     if text[start] == '"':
  63.         end = text.find('"', start + 1)
  64.         if end == -1:
  65.             return LexemeError(lexeme_type='QUOTES_PARITY_ERROR',
  66.                                start=start,
  67.                                end=len(text))
  68.         else:
  69.             return LexemeValue(lexeme_type='QVALUE',
  70.                                start=start,
  71.                                end=end + 1,  # value is without quotes, but lexeme start and end are with quotes
  72.                                text_before_asterisk=text[start + 1: end])
  73.  
  74.     value = value_pattern.match(text[start:])
  75.     if value:
  76.         return LexemeValue(lexeme_type='VALUE',
  77.                            start=start,
  78.                            end=start + len(value.group()),
  79.                            text_before_asterisk=value.group())
  80.     return False
  81.  
  82.  
  83. def core_lexer(text):
  84.     token_types = {'(': 'LPAREN',
  85.                    ')': 'RPAREN',
  86.                    '|': 'OR'}
  87.     pos = 0
  88.  
  89.     while pos < len(text):
  90.         whitespaces_beginning = re.match(r'[\s\&\,]+', text[pos:])
  91.         if whitespaces_beginning:
  92.             pos += len(whitespaces_beginning.group())
  93.  
  94.         first_symbol = text[pos]
  95.  
  96.         if first_symbol in '|()':
  97.             yield LexemeOperator(lexeme_type=token_types[first_symbol],
  98.                                  start=pos,
  99.                                  end=pos + 1)
  100.             pos += 1
  101.         elif first_symbol == '-':
  102.             if text[pos:].startswith('-('):
  103.                 yield LexemeOperator(lexeme_type='NOT',
  104.                                      start=pos,
  105.                                      end=pos + 1)
  106.                 yield LexemeOperator(lexeme_type='LPAREN',
  107.                                      start=pos + 1,
  108.                                      end=pos + 2)
  109.                 pos += 2
  110.             else:
  111.                 value = match_value(text, pos + 1)
  112.                 if value:
  113.                     yield LexemeOperator(lexeme_type='NOT',
  114.                                          start=pos,
  115.                                          end=pos + 1)
  116.                     yield value
  117.                     pos = value.end
  118.                 else:
  119.                     yield LexemeError(lexeme_type='MINUS_ERROR',
  120.                                       start=pos,
  121.                                       end=pos + 1)
  122.                     pos += 1
  123.         else:
  124.             value = match_value(text, pos)
  125.             if value:
  126.                 yield value
  127.                 pos = value.end
  128.             else:
  129.                 yield LexemeError(lexeme_type='UNEXPECTED_SYMBOL',
  130.                                   start=pos,
  131.                                   end=pos + 1)
  132.                 pos += 1
  133.  
  134.  
  135. def find_asterisk(token):
  136.     asterisks = re.findall(r'\*', token.text_before_asterisk)
  137.     if not asterisks:
  138.         return token
  139.     elif len(asterisks) > 1 or len(token.text_before_asterisk) == 1:
  140.         token.has_asterisk = True
  141.         token.has_error = True
  142.         return token
  143.     else:
  144.         value = token.text_before_asterisk
  145.         asterisk_position = re.search(r'\*', value)
  146.         token.has_asterisk = True
  147.         token.text_before_asterisk = value[:asterisk_position.start()]
  148.         token.text_after_asterisk = value[asterisk_position.start() + 1:]
  149.         return token
  150.  
  151.  
  152. def parse_value(value_lexeme):
  153.     return find_asterisk(value_lexeme)
  154.  
  155.  
  156. class Lexer(object):
  157.     def __init__(self, text):
  158.         self.core_lexer_generator = core_lexer(text)
  159.         self.dup_count = 0
  160.         self.buff = None
  161.         self.lexer_errors = list()
  162.  
  163.     def __iter__(self):
  164.         return self.core_lexer_generator
  165.  
  166.     def __next__(self):
  167.         return self.next()
  168.  
  169.     def next(self):
  170.         if self.dup_count > 0:
  171.             self.dup_count -= 1
  172.             return self.buff
  173.         else:
  174.             self.buff = next(self.core_lexer_generator)
  175.             if isinstance(self.buff, LexemeValue):
  176.                 self.buff = parse_value(self.buff)
  177.                 if self.buff.has_error:
  178.                     self.lexer_errors.append(LexemeError(lexeme_type='MULTIASTERISKS', start=-1, end=-1))
  179.                     return self.next()
  180.             elif isinstance(self.buff, LexemeError):
  181.                 self.lexer_errors.append(self.buff)
  182.                 return self.next()
  183.             return self.buff
  184.  
  185.     def dup_lexeme(self):
  186.         self.dup_count += 1
  187.  
  188.  
  189. class Parser(object):
  190.     def parse(self, lexeme_generator):
  191.         return parse_expression(lexeme_generator)
  192.  
  193.  
  194. class ParserError(object):
  195.     def __init__(self, error_type):
  196.         self.error_type = error_type
  197.  
  198.     def debug(self):
  199.         return 'lexeme_type: {}'.format(self.error_type).encode('utf-8')
  200.  
  201.  
  202. class TreeNode(object):
  203.     def __init__(self, value=None, children=None):
  204.         if children:
  205.             self.children = children
  206.         else:
  207.             self.children = list()
  208.         self.value = value
  209.  
  210.     def debug(self):
  211.         if self.children:
  212.             return '{} -> {}'.format(self.value.lexeme_type, ','.join([x.value.lexeme_type for x in self.children]))
  213.         else:
  214.             return '{}'.format(self.value.text_before_asterisk + self.value.text_after_asterisk)
  215.  
  216.  
  217. def refine_tree_node(tree_node):
  218.     if not tree_node.children:
  219.         return None
  220.     elif len(tree_node.children) == 1 and tree_node.value != 'NOT':
  221.         return tree_node.children[0]
  222.     else:
  223.         return tree_node
  224.  
  225.  
  226. def parse_expression(lexeme_generator):
  227.     current_node = TreeNode(value=LexemeOperator('OR', -1, -1))
  228.     expression_errors = list()
  229.     while True:
  230.         subtree, alternative_errors = parse_alternative(lexeme_generator)
  231.  
  232.         if subtree:
  233.             current_node.children.append(subtree)
  234.             expression_errors += alternative_errors
  235.         elif alternative_errors:
  236.             expression_errors += alternative_errors
  237.         else:
  238.             try:
  239.                 lexeme = next(lexeme_generator)
  240.             except StopIteration:
  241.                 return refine_tree_node(current_node), expression_errors
  242.             if lexeme.lexeme_type != 'OR':
  243.                 lexeme_generator.dup_lexeme()
  244.                 return refine_tree_node(current_node), expression_errors
  245.  
  246.  
  247. def parse_alternative(lexeme_generator):
  248.     current_node = TreeNode(value=LexemeOperator('AND', -1, -1))
  249.     alternative_errors = list()
  250.     while True:
  251.         subtree, condition_errors = parse_condition(lexeme_generator)
  252.  
  253.         if subtree:
  254.             current_node.children.append(subtree)
  255.             alternative_errors += condition_errors
  256.         elif condition_errors:
  257.             alternative_errors += condition_errors
  258.         else:
  259.             return refine_tree_node(current_node), alternative_errors
  260.  
  261.  
  262. def parse_condition(lexeme_generator):
  263.     try:
  264.         lexeme = next(lexeme_generator)
  265.     except StopIteration:
  266.         return None, list()
  267.     if lexeme.lexeme_type == 'NOT':
  268.         subtree, condition_errors = parse_term(lexeme_generator)
  269.         if subtree:
  270.             if subtree.value.lexeme_type == 'NOT':
  271.                 current_node = subtree.children[0]
  272.             else:
  273.                 current_node = TreeNode(value=lexeme, children=[subtree])
  274.         else:
  275.             current_node = None
  276.             condition_errors.append(ParserError(error_type='EMPTY_NEGATIVE'))
  277.         return current_node, condition_errors
  278.     else:
  279.         lexeme_generator.dup_lexeme()
  280.         subtree, condition_errors = parse_term(lexeme_generator)
  281.         if subtree:
  282.             return subtree, condition_errors
  283.         else:
  284.             return None, condition_errors
  285.  
  286.  
  287. def parse_term(lexeme_generator):
  288.     try:
  289.         lexeme = next(lexeme_generator)
  290.     except StopIteration:
  291.         return None, list()
  292.     if isinstance(lexeme, LexemeValue):
  293.         current_node = TreeNode(value=lexeme)
  294.         term_errors = []
  295.         return current_node, term_errors
  296.     elif lexeme.lexeme_type == 'LPAREN':
  297.         subtree, term_errors = parse_expression(lexeme_generator)
  298.  
  299.         try:
  300.             lexeme = next(lexeme_generator)
  301.         except StopIteration:
  302.             term_errors.append(ParserError(error_type='NO_RPAREN_AFTER_EXPRESSION'))
  303.             return subtree, term_errors
  304.  
  305.         if lexeme.lexeme_type != 'RPAREN':
  306.             lexeme_generator.dup_lexeme()
  307.         elif not subtree:
  308.             term_errors.append(ParserError(error_type='EMPTY_SUBTREE'))
  309.  
  310.         return subtree, term_errors
  311.     else:
  312.         lexeme_generator.dup_lexeme()
  313.         return None, list()
  314.  
  315.  
  316. class QueryNode(object):
  317.     pass
  318.  
  319.  
  320. class QueryMatch(QueryNode):
  321.     def __init__(self, operation, field, value, quoted=False):
  322.         self.operation = operation
  323.         self.field = field
  324.         self.value = value
  325.         self.quoted = quoted
  326.  
  327.     def debug(self):
  328.         return '[{} {} {} {}]'.format(self.operation, self.field, self.value, self.quoted)
  329.  
  330.  
  331. class QueryLogic(QueryNode):
  332.     def __init__(self, logic_operation, children):
  333.         self.logic_operation = logic_operation
  334.         self.children = children
  335.  
  336.     def debug(self):
  337.         return '[{} -> {}]'.format(self.logic_operation, [x.value if isinstance(x, QueryMatch) else x.logic_operation for x in self.children])
  338.  
  339.  
  340. def convert_node(tree_node, field_name='', tree_type=''):
  341.     if tree_node.value.lexeme_type in ['AND', 'OR', 'NOT']:
  342.         converted_children = list()
  343.         for child in tree_node.children:
  344.             converted_children.append(convert_node(child, field_name, tree_type))
  345.         return QueryLogic(tree_node.value.lexeme_type, converted_children)
  346.     elif tree_type == 'STRING':
  347.         if tree_node.value.lexeme_type == 'QVALUE':
  348.             quoted = True
  349.         else:
  350.             quoted = False
  351.         if tree_node.value.has_asterisk:
  352.             operation = 'LIKE'
  353.             value = [tree_node.value.text_before_asterisk, tree_node.value.text_after_asterisk]
  354.         else:
  355.             operation = 'EQUAL'
  356.             value = tree_node.value.text_before_asterisk
  357.         return QueryMatch(operation, field_name, value, quoted)
  358.     else:
  359.         return QueryMatch('INCLUDE', field_name, tree_node.value.text_before_asterisk)
  360.  
  361.  
  362. def build_query_tree(tree_root, field_name, tree_type):
  363.     return convert_node(tree_root, field_name, tree_type)
  364.  
  365.  
  366. QueryFieldsLG = {'lex': 'STRING', 'gramm': 'SET', 'sem': 'SET', 'flags': 'SET'}
  367.  
  368.  
  369. class UserQuery(object):
  370.     pass
  371.  
  372.  
  373. class UserQueryLG(UserQuery):
  374.     def __init__(self, word_queries_list):
  375.         self.word_queries_list = word_queries_list
  376.         self.query_inner_forest = list()
  377.         self.repo_forest = list()
  378.  
  379.     def build_inner_forest(self):
  380.         for word_query in self.word_queries_list:
  381.             word_query_forest = dict()
  382.             for field_name, field_query in word_query.items():
  383.                 if field_name not in QueryFieldsLG:
  384.                     word_query_forest[field_name] = field_query
  385.                 else:
  386.                     lexeme_generator = Lexer(field_query)
  387.                     root, errors = parse_expression(lexeme_generator)
  388.                     word_query_forest[field_name] = root
  389.             self.query_inner_forest.append(word_query_forest)
  390.  
  391.     def debug_inner(self):
  392.         inner_forest_representation = list()
  393.         for word_query_forest in self.query_inner_forest:
  394.             word_forest_representation = dict()
  395.             for field_name, field_tree in word_query_forest.items():
  396.                 if field_name not in QueryFieldsLG:
  397.                     word_forest_representation[field_name] = field_tree
  398.                 else:
  399.                     nodes = list()
  400.                     level = [field_tree]
  401.                     while level:
  402.                         next_level = list()
  403.                         for node in level:
  404.                             nodes.append(node.debug())
  405.                             if node:
  406.                                 next_level += node.children
  407.                         level = next_level
  408.                     word_forest_representation[field_name] = nodes
  409.             inner_forest_representation.append(word_forest_representation)
  410.         return inner_forest_representation
  411.  
  412.     def build_repo_forest(self):
  413.         repo_forest = list()
  414.         for word_query_forest in self.query_inner_forest:
  415.             word_repo_forest = list()
  416.             word_additional = dict()
  417.             for field_name, field_tree in word_query_forest.items():
  418.                 if field_name not in QueryFieldsLG:
  419.                     word_additional[field_name] = field_tree
  420.                 else:
  421.                     query_tree = build_query_tree(field_tree, field_name, tree_type=QueryFieldsLG[field_name])
  422.                     word_repo_forest.append(query_tree)
  423.             repo_forest.append({'query_tree': QueryLogic('AND', word_repo_forest), 'additional_params': word_additional})
  424.         self.repo_forest = repo_forest
  425.         return repo_forest
  426.  
  427.     def debug_repo(self):
  428.         repo_forest_representation = list()
  429.         for word_query_forest in self.repo_forest:
  430.             nodes = list()
  431.             level = [word_query_forest['query_tree']]
  432.             while level:
  433.                 next_level = list()
  434.                 for node in level:
  435.                     nodes.append(node.debug())
  436.                     if node and isinstance(node, QueryLogic):
  437.                         next_level += node.children
  438.                     level = next_level
  439.             repo_forest_representation.append(nodes)
  440.         return repo_forest_representation
  441.  
  442.  
  443. if __name__ == "__main__":
  444.     query = [{'lex': 'word word | word & word', 'gramm': 'NUM,gen,imper2'}, {'lex': 'word word | *word & word', 'sem': 'r:concr & t:space & top:contain'}]
  445.     # query = [{'lex': 'NUM,gen,imper2'}]
  446.  
  447.     user_query_processor = UserQueryLG(query)
  448.     user_query_processor.build_inner_forest()
  449.     print(user_query_processor.debug_inner())
  450.     user_query_processor.build_repo_forest()
  451.     for i, forest in enumerate(user_query_processor.debug_repo()):
  452.         print('word', i)
  453.         print('\n'.join(forest))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement