"Utility functions used by the btm_matcher module"from . import pytreefrom .pgen2 import grammar, tokenfrom .pygram import pattern_symbols, python_symbolssyms = pattern_symbolspysyms = python_symbolstokens = grammar.opmaptoken_labels = tokenTYPE_ANY = -1TYPE_ALTERNATIVES = -2TYPE_GROUP = -3class MinNode(object):"""This class serves as an intermediate representation of thepattern tree during the conversion to sets of leaf-to-rootsubpatterns"""def __init__(self, type=None, name=None):self.type = typeself.name = nameself.children = []self.leaf = Falseself.parent = Noneself.alternatives = []self.group = []def __repr__(self):return str(self.type) + ' ' + str(self.name)def leaf_to_root(self):"""Internal method. Returns a characteristic path of thepattern tree. This method must be run for all leaves until thelinear subpatterns are merged into a single"""node = selfsubp = []while node:if node.type == TYPE_ALTERNATIVES:node.alternatives.append(subp)if len(node.alternatives) == len(node.children):#last alternativesubp = [tuple(node.alternatives)]node.alternatives = []node = node.parentcontinueelse:node = node.parentsubp = Nonebreakif node.type == TYPE_GROUP:node.group.append(subp)#probably should check the number of leavesif len(node.group) == len(node.children):subp = get_characteristic_subpattern(node.group)node.group = []node = node.parentcontinueelse:node = node.parentsubp = Nonebreakif node.type == token_labels.NAME and node.name:#in case of type=name, use the name insteadsubp.append(node.name)else:subp.append(node.type)node = node.parentreturn subpdef get_linear_subpattern(self):"""Drives the leaf_to_root method. The reason thatleaf_to_root must be run multiple times is because we need toreject 'group' matches; for example the alternative form(a | b c) creates a group [b c] that needs to be matched. Sincematching multiple linear patterns overcomes the automaton'scapabilities, leaf_to_root merges each group into a singlechoice based on 'characteristic'ity,i.e. (a|b c) -> (a|b) if b more characteristic than cReturns: The most 'characteristic'(as defined byget_characteristic_subpattern) path for the compiled patterntree."""for l in self.leaves():subp = l.leaf_to_root()if subp:return subpdef leaves(self):"Generator that returns the leaves of the tree"for child in self.children:yield from child.leaves()if not self.children:yield selfdef reduce_tree(node, parent=None):"""Internal function. Reduces a compiled pattern tree to anintermediate representation suitable for feeding theautomaton. This also trims off any optional pattern elements(like[a], a*)."""new_node = None#switch on the node typeif node.type == syms.Matcher:#skipnode = node.children[0]if node.type == syms.Alternatives :#2 casesif len(node.children) <= 2:#just a single 'Alternative', skip this nodenew_node = reduce_tree(node.children[0], parent)else:#real alternativesnew_node = MinNode(type=TYPE_ALTERNATIVES)#skip odd children('|' tokens)for child in node.children:if node.children.index(child)%2:continuereduced = reduce_tree(child, new_node)if reduced is not None:new_node.children.append(reduced)elif node.type == syms.Alternative:if len(node.children) > 1:new_node = MinNode(type=TYPE_GROUP)for child in node.children:reduced = reduce_tree(child, new_node)if reduced:new_node.children.append(reduced)if not new_node.children:# delete the group if all of the children were reduced to Nonenew_node = Noneelse:new_node = reduce_tree(node.children[0], parent)elif node.type == syms.Unit:if (isinstance(node.children[0], pytree.Leaf) andnode.children[0].value == '('):#skip parenthesesreturn reduce_tree(node.children[1], parent)if ((isinstance(node.children[0], pytree.Leaf) andnode.children[0].value == '[')or(len(node.children)>1 andhasattr(node.children[1], "value") andnode.children[1].value == '[')):#skip whole unit if its optionalreturn Noneleaf = Truedetails_node = Nonealternatives_node = Nonehas_repeater = Falserepeater_node = Nonehas_variable_name = Falsefor child in node.children:if child.type == syms.Details:leaf = Falsedetails_node = childelif child.type == syms.Repeater:has_repeater = Truerepeater_node = childelif child.type == syms.Alternatives:alternatives_node = childif hasattr(child, 'value') and child.value == '=': # variable namehas_variable_name = True#skip variable nameif has_variable_name:#skip variable name, '='name_leaf = node.children[2]if hasattr(name_leaf, 'value') and name_leaf.value == '(':# skip parenthesisname_leaf = node.children[3]else:name_leaf = node.children[0]#set node typeif name_leaf.type == token_labels.NAME:#(python) non-name or wildcardif name_leaf.value == 'any':new_node = MinNode(type=TYPE_ANY)else:if hasattr(token_labels, name_leaf.value):new_node = MinNode(type=getattr(token_labels, name_leaf.value))else:new_node = MinNode(type=getattr(pysyms, name_leaf.value))elif name_leaf.type == token_labels.STRING:#(python) name or character; remove the apostrophes from#the string valuename = name_leaf.value.strip("'")if name in tokens:new_node = MinNode(type=tokens[name])else:new_node = MinNode(type=token_labels.NAME, name=name)elif name_leaf.type == syms.Alternatives:new_node = reduce_tree(alternatives_node, parent)#handle repeatersif has_repeater:if repeater_node.children[0].value == '*':#reduce to Nonenew_node = Noneelif repeater_node.children[0].value == '+':#reduce to a single occurrence i.e. do nothingpasselse:#TODO: handle {min, max} repeatersraise NotImplementedErrorpass#add childrenif details_node and new_node is not None:for child in details_node.children[1:-1]:#skip '<', '>' markersreduced = reduce_tree(child, new_node)if reduced is not None:new_node.children.append(reduced)if new_node:new_node.parent = parentreturn new_nodedef get_characteristic_subpattern(subpatterns):"""Picks the most characteristic from a list of linear patternsCurrent order used is:names > common_names > common_chars"""if not isinstance(subpatterns, list):return subpatternsif len(subpatterns)==1:return subpatterns[0]# first pick out the ones containing variable namessubpatterns_with_names = []subpatterns_with_common_names = []common_names = ['in', 'for', 'if' , 'not', 'None']subpatterns_with_common_chars = []common_chars = "[]().,:"for subpattern in subpatterns:if any(rec_test(subpattern, lambda x: type(x) is str)):if any(rec_test(subpattern,lambda x: isinstance(x, str) and x in common_chars)):subpatterns_with_common_chars.append(subpattern)elif any(rec_test(subpattern,lambda x: isinstance(x, str) and x in common_names)):subpatterns_with_common_names.append(subpattern)else:subpatterns_with_names.append(subpattern)if subpatterns_with_names:subpatterns = subpatterns_with_nameselif subpatterns_with_common_names:subpatterns = subpatterns_with_common_nameselif subpatterns_with_common_chars:subpatterns = subpatterns_with_common_chars# of the remaining subpatterns pick out the longest onereturn max(subpatterns, key=len)def rec_test(sequence, test_func):"""Tests test_func on all items of sequence and items of includedsub-iterables"""for x in sequence:if isinstance(x, (list, tuple)):yield from rec_test(x, test_func)else:yield test_func(x)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。