def test_Lark_DecayModelParamValueReplacement_Visitor_list(): t = Tree('decay', [ Tree('particle', [Token('LABEL', 'B0sig')]), Tree('decayline', [ Tree('value', [Token('SIGNED_NUMBER', '1.000')]), Tree('particle', [Token('LABEL', 'MyFirstD*-')]), Tree('particle', [Token('LABEL', 'MySecondD*+')]), Tree('model', [ Token('MODEL_NAME', 'SVV_HELAMP'), Tree('model_options', [ Tree('value', [Token('SIGNED_NUMBER', '0.0')]), Tree('value', [Token('SIGNED_NUMBER', '0.0')]), Tree('value', [Token('SIGNED_NUMBER', '0.0')]), Tree('value', [Token('SIGNED_NUMBER', '0.0')]), Tree('value', [Token('SIGNED_NUMBER', '1.0')]), Tree('value', [Token('SIGNED_NUMBER', '0.0')]) ]) ]) ]) ]) DecayModelParamValueReplacement().visit(t) # The visitor should do nothing in this case tree_decayline = list(t.find_data('decayline'))[0] assert get_model_name(tree_decayline) == 'SVV_HELAMP' assert get_model_parameters(tree_decayline) == [ 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 ]
def test_Lark_DecayModelParamValueReplacement_Visitor_single_value(): t = Tree('decay', [ Tree('particle', [Token('LABEL', 'Upsilon(4S)')]), Tree('decayline', [ Tree('value', [Token('SIGNED_NUMBER', '1.0')]), Tree('particle', [Token('LABEL', 'B0')]), Tree('particle', [Token('LABEL', 'anti-B0')]), Tree('model', [ Token('MODEL_NAME', 'VSS_BMIX'), Tree('model_options', [Token('LABEL', 'dm')]) ]) ]) ]) DecayModelParamValueReplacement().visit(t) # Nothing done since model parameter name has no corresponding # 'Define' statement from which the actual value can be inferred tree_decayline = list(t.find_data('decayline'))[0] assert get_model_name(tree_decayline) == 'VSS_BMIX' assert get_model_parameters(tree_decayline) == ['dm'] dict_define_defs = {'dm': 0.507e12} DecayModelParamValueReplacement(define_defs=dict_define_defs).visit(t) # The model parameter 'dm' should now be replaced by its value assert get_model_name(tree_decayline) == 'VSS_BMIX' assert get_model_parameters(tree_decayline) == [507000000000.0]
def _unused_argument_check(parse_tree: Tree) -> List[Problem]: problems = [] for func_def in parse_tree.find_data("func_def"): if (isinstance(func_def.children[1], Tree) and func_def.children[1].data == "func_args"): argument_definitions = {} # type: Dict[str, int] argument_tokens = {} func_args = func_def.children[1] for func_arg in func_args.children: arg_name_token = find_name_token_among_children(func_arg) arg_name = arg_name_token.value argument_definitions[arg_name] = ( argument_definitions.get(arg_name, 0) + 1) argument_tokens[arg_name] = arg_name_token name_occurances = {} # type: Dict[str, int] for xnode in func_def.iter_subtrees(): for node in xnode.children: if isinstance(node, Token) and node.type == "NAME": name = node.value name_occurances[name] = name_occurances.get(name, 0) + 1 for argument in argument_definitions: if argument_definitions[argument] == name_occurances[ argument] and not argument.startswith("_"): problems.append( Problem( name="unused-argument", description="unused function argument '{}'".format( argument), line=argument_tokens[argument].line, column=argument_tokens[argument].column, )) return problems
def _class_definitions_order_check(order, parse_tree: Tree) -> List[Problem]: problems = _class_definitions_order_check_for_class( "global scope", parse_tree.children, order) for class_def in parse_tree.find_data("class_def"): class_name = class_def.children[0].value problems += _class_definitions_order_check_for_class( "class {}".format(class_name), class_def.children, order) return problems
def _load_elements(enum_def: Tree) -> List[EnumElement]: elements = [] for enum_element in enum_def.find_data("enum_element"): name = enum_element.children[0].value value = (standalone_expression_to_str(enum_element.children[1]) if len(enum_element.children) > 1 else None) elements.append( EnumElement(name=name, value=value, lark_node=enum_element)) return elements
def _comparison_with_itself_check(parse_tree: Tree) -> List[Problem]: problems = [] for comparison in parse_tree.find_data("comparison"): assert len(comparison.children) == 3 if comparison.children[0] == comparison.children[2]: problems.append( Problem( name="comparison-with-itself", description="Redundant comparison", line=comparison.line, column=comparison.column, )) return problems
def gen_enum(tree: Tree) -> type: assert tree.data == "enum" name_node = tree.children[2] class_name = name_node.value assert tree.children[0].type == "ENUM_NAME" assert name_node.type == "CUSTOM_TYPE_NAME" attrs = {} for rule in tree.find_data('enum_line'): attrs[rule.children[0].value] = rule.children[2].value cls = Enum(class_name, attrs, module=__name__) return cls
def test_Lark_DecayModelParamValueReplacement_Visitor_list(): t = Tree( "decay", [ Tree("particle", [Token("LABEL", "B0sig")]), Tree( "decayline", [ Tree("value", [Token("SIGNED_NUMBER", "1.000")]), Tree("particle", [Token("LABEL", "MyFirstD*-")]), Tree("particle", [Token("LABEL", "MySecondD*+")]), Tree( "model", [ Token("MODEL_NAME", "SVV_HELAMP"), Tree( "model_options", [ Tree("value", [Token("SIGNED_NUMBER", "0.0")]), Tree("value", [Token("SIGNED_NUMBER", "0.0")]), Tree("value", [Token("SIGNED_NUMBER", "0.0")]), Tree("value", [Token("SIGNED_NUMBER", "0.0")]), Tree("value", [Token("SIGNED_NUMBER", "1.0")]), Tree("value", [Token("SIGNED_NUMBER", "0.0")]), ], ), ], ), ], ), ], ) DecayModelParamValueReplacement().visit(t) # The visitor should do nothing in this case tree_decayline = list(t.find_data("decayline"))[0] assert get_model_name(tree_decayline) == "SVV_HELAMP" assert get_model_parameters(tree_decayline) == [ 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 ]
def test_Lark_DecayModelParamValueReplacement_Visitor_no_params(): t = Tree('decay', [ Tree('particle', [Token('LABEL', 'D0')]), Tree('decayline', [ Tree('value', [Token('SIGNED_NUMBER', '1.0')]), Tree('particle', [Token('LABEL', 'K-')]), Tree('particle', [Token('LABEL', 'pi+')]), Tree('model', [Token('MODEL_NAME', 'PHSP')]) ]) ]) DecayModelParamValueReplacement().visit(t) # The visitor should do nothing in this case tree_decayline = list(t.find_data('decayline'))[0] assert get_model_name(tree_decayline) == 'PHSP' assert get_model_parameters(tree_decayline) == ''
def _function_args_num_check(threshold, parse_tree: Tree) -> List[Problem]: problems = [] for func_def in parse_tree.find_data("func_def"): func_name_token = func_def.children[0] assert func_name_token.type == "NAME" func_name = func_name_token.value if (isinstance(func_def.children[1], Tree) and func_def.children[1].data == "func_args"): args_num = len(func_def.children[1].children) if args_num > threshold: problems.append( Problem( name="function-arguments-number", description='Function "{}" has more than {} arguments'. format(func_name, threshold), line=func_name_token.line, column=func_name_token.column, )) return problems
def _expression_not_assigned_check(parse_tree: Tree) -> List[Problem]: problems = [] for expr_stmt in parse_tree.find_data("expr_stmt"): expr = expr_stmt.children[0] child = expr.children[0] if not isinstance(child, Tree) or child.data not in [ "assnmnt_expr", "standalone_call", "getattr_call", "string", ]: problems.append( Problem( name="expression-not-assigned", description= "expression is not asigned, and hence it can be removed", line=child.line, column=child.column, )) return problems
def _private_method_call_check(parse_tree: Tree) -> List[Problem]: problems = [] for getattr_call in parse_tree.find_data("getattr_call"): _getattr = getattr_call.children[0] callee_name_token = _getattr.children[-1] callee_name = callee_name_token.value called = _getattr.children[-2] if (isinstance(called, Token) and called.type == "NAME" and called.value == "self"): continue if not _is_method_private(callee_name): continue problems.append( Problem( name="private-method-call", description='Private method "{}" has been called'.format( callee_name), line=callee_name_token.line, column=callee_name_token.column, )) return problems
def gen_struct(tree: Tree) -> type: assert tree.data == "struct" name_node = tree.children[2] class_name = name_node.value assert tree.children[0].type == "STRUCT_NAME" assert name_node.type == "CUSTOM_TYPE_NAME" fields = [] for rule in tree.find_data('struct_line'): typ = get_type(rule.children[0]) field_name = rule.children[2].value fields.append(( field_name, typ, )) if len(fields) != len(set(f[0] for f in fields)): raise DuplicateFieldError("Duplicate field in struct") cls = make_dataclass(class_name, fields) return cls
def gen_message(tree: Tree) -> type: # TODO: Handle the integer mapping assert tree.data == "message" name_node = tree.children[2] class_name = name_node.value assert tree.children[0].type == "MESSAGE_NAME" assert name_node.type == "CUSTOM_TYPE_NAME" fields = [] for rule in tree.find_data('message_line'): typ = get_type(rule.children[2]) field_name = rule.children[4].value fields.append(( field_name, typ, )) if len(fields) != len(set(f[0] for f in fields)): raise DuplicateFieldError("Duplicate field in message") cls = make_dataclass(class_name, fields) return cls
def test_Lark_DecayModelParamValueReplacement_Visitor_single_value(): t = Tree( "decay", [ Tree("particle", [Token("LABEL", "Upsilon(4S)")]), Tree( "decayline", [ Tree("value", [Token("SIGNED_NUMBER", "1.0")]), Tree("particle", [Token("LABEL", "B0")]), Tree("particle", [Token("LABEL", "anti-B0")]), Tree( "model", [ Token("MODEL_NAME", "VSS_BMIX"), Tree("model_options", [Token("LABEL", "dm")]), ], ), ], ), ], ) DecayModelParamValueReplacement().visit(t) # Nothing done since model parameter name has no corresponding # 'Define' statement from which the actual value can be inferred tree_decayline = list(t.find_data("decayline"))[0] assert get_model_name(tree_decayline) == "VSS_BMIX" assert get_model_parameters(tree_decayline) == ["dm"] dict_define_defs = {"dm": 0.507e12} DecayModelParamValueReplacement(define_defs=dict_define_defs).visit(t) # The model parameter 'dm' should now be replaced by its value assert get_model_name(tree_decayline) == "VSS_BMIX" assert get_model_parameters(tree_decayline) == [507000000000.0]
def _duplicated_load_check(parse_tree: Tree) -> List[Problem]: problems = [] loaded_strings = set() # type: Set[str] for call in parse_tree.find_data("standalone_call"): name_token = call.children[0] callee_name = name_token.value if (callee_name in ["load", "preload"] and len(call.children) > 1 and isinstance(call.children[2], Tree) and call.children[2].data == "string"): string_rule = call.children[2] loaded_string = string_rule.children[0].value if loaded_string in loaded_strings: problems.append( Problem( name="duplicated-load", description="duplicated loading of {}".format( loaded_string), line=string_rule.line, column=string_rule.column, )) else: loaded_strings.add(loaded_string) return problems
def test_Lark_DecayModelParamValueReplacement_Visitor_no_params(): t = Tree( "decay", [ Tree("particle", [Token("LABEL", "D0")]), Tree( "decayline", [ Tree("value", [Token("SIGNED_NUMBER", "1.0")]), Tree("particle", [Token("LABEL", "K-")]), Tree("particle", [Token("LABEL", "pi+")]), Tree("model", [Token("MODEL_NAME", "PHSP")]), ], ), ], ) DecayModelParamValueReplacement().visit(t) # The visitor should do nothing in this case tree_decayline = list(t.find_data("decayline"))[0] assert get_model_name(tree_decayline) == "PHSP" assert get_model_parameters(tree_decayline) == ""