def deep_find_match_Expr(self, ins_node, std_node, check_meta=True): """ An Expression node (not to be confused with expressions denoted by the instructor nodes in Name ast nodes) checks whether it should be generic, or not Args: ins_node: Instructor ast to find in the student ast std_node: Student AST to search for the instructor ast in check_meta: flag to check whether the fields of the instructor node and the student node should match Returns: AstMap: a mapping between the instructor and student asts, or False if such a mapping doesn't exist """ # if check_meta and ins_node.field != std_node.field: if not self.metas_match(ins_node, std_node, check_meta): return [] mapping = AstMap() value = ins_node.value ast_type = type(value.astNode).__name__ if ast_type == "Name": name_id = value.astNode.id exp_match = re.compile('^__.*__$') # /regex wild_card = re.compile('^___$') # /regex matched = False meta_matched = self.metas_match(ins_node, std_node, check_meta) if exp_match.match(name_id): # and meta_matched: # if expression # terminate recursion, the whole subtree should match since expression nodes match to anything mapping.add_exp_to_sym_table(value, std_node) matched = True elif wild_card.match(name_id) and meta_matched: # if wild card, don't care # terminate the recursion, the whole subtree should match since wild cards match to anything matched = True if matched: mapping.add_node_pairing(ins_node, std_node) return [mapping] return self.deep_find_match_generic(ins_node, std_node, check_meta)
def shallow_symbol_handler(self, ins_node, std_node, id_val, check_meta=True): """ TODO: Make this handle the func field to handle functions Matches ins_node to std_node for different cases of encountering a name node in ins_node case 1: _var_ matches if std_node is a name node and automatically returns a mapping and symbol table case 2: __exp__ matches to any subtree and automatically returns a mapping and symbol table case 3: ___ matches to any subtree and automatically returns a mapping case 4: matches only if the exact names are the same (falls through to shallow_match_generic) Args: ins_node: std_node: id_val: check_meta: Returns: list of AstMap: a mapping of ins_node to std_node and possibly a symbol_table, or False if it doesn't match """ name_id = ins_node.astNode.__getattribute__(id_val) match = _name_regex(name_id) mapping = AstMap() matched = False # TODO: add functionality to add function references to func_table? meta_matched = self.metas_match(ins_node, std_node, check_meta) if match[_VAR] and meta_matched: # variable if type(std_node.astNode).__name__ == "Name" or id_val == "attr": if id_val == "attr": std_node.astNode.id = std_node.astNode.attr if std_node.field == "func" and ins_node.field != "none": # TODO: This 'ins_node.field != "none"' code is for an obscure edge case where the instructor code # is only _var_ mapping.add_func_to_sym_table(ins_node, std_node) else: mapping.add_var_to_sym_table( ins_node, std_node) # TODO: Capture result? matched = True # could else return False, but shallow_match_generic should do this as well elif match[ _EXP] and meta_matched: # expression TODO: In theory this won't run? mapping.add_exp_to_sym_table(ins_node, std_node) matched = True elif match[ _WILD] and meta_matched: # don't care TODO: In theory this won't run? matched = True if matched: mapping.add_node_pairing(ins_node, std_node) return [mapping] # else return self.shallow_match_generic(ins_node, std_node, check_meta)
def shallow_match_Module(self, ins_node, std_node, check_meta=True): """ Flexibly matches a module node to a module or a body Args: ins_node: std_node: check_meta: Returns: a mapping of ins_node to std_node, or False if doesn't match """ if type(std_node.astNode).__name__ == "Module" or std_node.field == "body": mapping = AstMap() mapping.add_node_pairing(ins_node, std_node) return [mapping] return []
def shallow_match_Module(self, ins_node, std_node, check_meta=True): """ Flexibly matches a module node to a module or a body Args: ins_node: std_node: check_meta: Returns: a mapping of ins_node to std_node, or False if doesn't match """ if type(std_node.astNode).__name__ == "Module" or std_node.field == "body": mapping = AstMap() mapping.add_node_pairing(ins_node, std_node) return [mapping] return []
def shallow_match_Pass(self, ins_node, std_node, check_meta=True): """ An empty body should match to anything Args: ins_node: Instructor ast to find in the student ast std_node: Student AST to search for the instructor ast in check_meta: flag to check whether the fields of the instructor node and the student node should match Returns: list of AstMap: a mapping between the isntructor and student asts, or False if such a mapping doesn't exist """ # if check_meta and ins_node.field != std_node.field: if not self.metas_match(ins_node, std_node, check_meta): return [] mapping = AstMap() mapping.add_node_pairing(ins_node, std_node) return [mapping]
def shallow_match_Pass(self, ins_node, std_node, check_meta=True): """ An empty body should match to anything Args: ins_node: Instructor ast to find in the student ast std_node: Student AST to search for the instructor ast in check_meta: flag to check whether the fields of the instructor node and the student node should match Returns: list of AstMap: a mapping between the isntructor and student asts, or False if such a mapping doesn't exist """ # if check_meta and ins_node.field != std_node.field: if not self.metas_match(ins_node, std_node, check_meta): return [] mapping = AstMap() mapping.add_node_pairing(ins_node, std_node) return [mapping]
def shallow_match_Expr(self, ins_node, std_node, check_meta=True): """ An Expression node (not to be confused with expressions denoted by the instructor nodes in Name ast nodes) should match to anything Args: ins_node: Instructor ast to find in the student ast std_node: Instructor ast to find in the student ast check_meta: flag to check whether the fields of the instructor node and the student node should match Returns: a mapping between the instructor and student asts, or False if such a mapping doesn't exist """ # if check_meta and ins_node.field != std_node.field: if not self.metas_match(ins_node, std_node, check_meta): return [] mapping = AstMap() mapping.add_node_pairing(ins_node, std_node) return [mapping]
def shallow_match_Expr(self, ins_node, std_node, check_meta=True): """ An Expression node (not to be confused with expressions denoted by the instructor nodes in Name ast nodes) should match to anything Args: ins_node: Instructor ast to find in the student ast std_node: Instructor ast to find in the student ast check_meta: flag to check whether the fields of the instructor node and the student node should match Returns: a mapping between the instructor and student asts, or False if such a mapping doesn't exist """ # if check_meta and ins_node.field != std_node.field: if not self.metas_match(ins_node, std_node, check_meta): return [] mapping = AstMap() mapping.add_node_pairing(ins_node, std_node) return [mapping]
def shallow_symbol_handler(self, ins_node, std_node, id_val, check_meta=True): """ TODO: Make this handle the func field to handle functions Matches ins_node to std_node for different cases of encountering a name node in ins_node case 1: _var_ matches if std_node is a name node and automatically returns a mapping and symbol table case 2: __exp__ matches to any subtree and automatically returns a mapping and symbol table case 3: ___ matches to any subtree and automatically returns a mapping case 4: matches only if the exact names are the same (falls through to shallow_match_generic) Args: ins_node: std_node: id_val: check_meta: Returns: list of AstMap: a mapping of ins_node to std_node and possibly a symbol_table, or False if it doesn't match """ name_id = ins_node.astNode.__getattribute__(id_val) match = _name_regex(name_id) mapping = AstMap() matched = False # TODO: add functionality to add function references to func_table? meta_matched = self.metas_match(ins_node, std_node, check_meta) if match[_VAR] and meta_matched: # variable if type(std_node.astNode).__name__ == "Name" or id_val in ["attr", "arg"]: if id_val in ["attr", "arg"]: std_node.astNode._id = std_node.astNode.__getattribute__(id_val) if std_node.field == "func" and ins_node.field != _NONE_FIELD: # TODO: This 'ins_node.field != _NONE_FIELD' code is for an obscure edge case where the # instructor code is only _var_ std_node.astNode._id = std_node.astNode.__getattribute__(id_val) mapping.add_func_to_sym_table(ins_node, std_node) else: std_node.astNode._id = std_node.astNode.__getattribute__(id_val) mapping.add_var_to_sym_table(ins_node, std_node) # TODO: Capture result? matched = True # could else return False, but shallow_match_generic should do this as well elif match[_EXP] and meta_matched: mapping.add_exp_to_sym_table(ins_node, std_node) matched = True elif match[_WILD] and meta_matched: matched = True if matched: mapping.add_node_pairing(ins_node, std_node) return [mapping] # else return self.shallow_match_main(ins_node, std_node, check_meta=check_meta, ignores=["ctx"])
def deep_find_match_Name(self, ins_node, std_node, check_meta=True): name_id = ins_node.astNode.id match = _name_regex(name_id) mapping = AstMap() matched = False meta_matched = self.metas_match(ins_node, std_node, check_meta) if match[_VAR] and meta_matched: # if variable # This if body is probably unnecessary. if type(std_node.astNode).__name__ == "Name": return self.deep_find_match_generic(ins_node, std_node, check_meta) # could else return False, but shallow_match_generic should do this as well elif match[_EXP]: # and meta_matched: # if expression # terminate recursion, the whole subtree should match since expression nodes match to anything mapping.add_exp_to_sym_table(ins_node, std_node) matched = True elif match[_WILD] and meta_matched: # if wild card, don't care # terminate the recursion, the whole subtree should match since wild cards match to anything matched = True if matched: mapping.add_node_pairing(ins_node, std_node) return [mapping] # else return self.deep_find_match_generic(ins_node, std_node, check_meta)
def deep_find_match_Name(self, ins_node, std_node, check_meta=True, use_previous=None): """ Args: ins_node: std_node: check_meta: use_previous: Returns: """ name_id = ins_node.astNode.id match = _name_regex(name_id) mapping = AstMap() matched = False meta_matched = self.metas_match(ins_node, std_node, check_meta) if match[_VAR] and meta_matched: # if variable if type(std_node.astNode).__name__ == "Name": return self.deep_find_match_generic(ins_node, std_node, check_meta=check_meta, ignores=["ctx"], use_previous=use_previous) # could else return False, but shallow_match_generic should do this as well elif match[_EXP] and meta_matched: # and meta_matched: # if expression # terminate recursion, the whole subtree should match since expression nodes match to anything mapping.merge_map_with(use_previous) mapping.add_exp_to_sym_table(ins_node, std_node) matched = True elif match[_WILD] and meta_matched: # if wild card, don't care # terminate the recursion, the whole subtree should match since wild cards match to anything matched = True if matched: mapping.add_node_pairing(ins_node, std_node) return [mapping] # else return self.deep_find_match_generic(ins_node, std_node, check_meta=check_meta, ignores=["ctx"], use_previous=use_previous)
def deep_find_match_Name(self, ins_node, std_node, check_meta=True): name_id = ins_node.astNode.id match = _name_regex(name_id) mapping = AstMap() matched = False meta_matched = self.metas_match(ins_node, std_node, check_meta) if match[_VAR] and meta_matched: # if variable # This if body is probably unnecessary. if type(std_node.astNode).__name__ == "Name": return self.deep_find_match_generic(ins_node, std_node, check_meta=check_meta, ignores=["ctx"]) # could else return False, but shallow_match_generic should do this as well elif match[_EXP]: # and meta_matched: # if expression # terminate recursion, the whole subtree should match since expression nodes match to anything mapping.add_exp_to_sym_table(ins_node, std_node) matched = True elif match[_WILD] and meta_matched: # if wild card, don't care # terminate the recursion, the whole subtree should match since wild cards match to anything matched = True if matched: mapping.add_node_pairing(ins_node, std_node) return [mapping] # else return self.deep_find_match_generic(ins_node, std_node, check_meta=check_meta, ignores=["ctx"])
def deep_find_match_Expr(self, ins_node, std_node, check_meta=True, use_previous=None): """ An Expression node (not to be confused with expressions denoted by the instructor nodes in Name ast nodes) checks whether it should be generic, or not Args: ins_node: Instructor ast to find in the student ast std_node: Student AST to search for the instructor ast in check_meta: flag to check whether the fields of the instructor node and the student node should match use_previous: An AstMap from a previous matching run Returns: AstMap: a mapping between the instructor and student asts, or False if such a mapping doesn't exist """ # if check_meta and ins_node.field != std_node.field: if not self.metas_match(ins_node, std_node, check_meta): return [] mapping = AstMap() if use_previous is None else use_previous value = ins_node.value ast_type = type(value.astNode).__name__ if ast_type == "Name": name_id = value.astNode.id exp_match = re.compile('^__.*__$') # /regex wild_card = re.compile('^___$') # /regex matched = False meta_matched = self.metas_match(ins_node, std_node, check_meta) if exp_match.match( name_id ) and meta_matched: # and meta_matched: # if expression # terminate recursion, the whole subtree should match since expression nodes match to anything mapping.add_exp_to_sym_table(value, std_node) matched = True elif wild_card.match( name_id) and meta_matched: # if wild card, don't care # terminate the recursion, the whole subtree should match since wild cards match to anything matched = True if matched: mapping.add_node_pairing(ins_node, std_node) return [mapping] return self.deep_find_match_generic(ins_node, std_node, check_meta)
def shallow_match_main(self, ins_node, std_node, check_meta=True, ignores=None): """ Checks that all non astNode attributes are equal between ins_node and std_node Args: ins_node: Instructor ast root node std_node: Student AST root node check_meta: flag to check whether the fields of the instructor node and the student node should match ignores: a mapping between the instructor and student root nodes, or False if such a mapping doesn't exist Returns: """ if ignores is None: ignores = [] ignores.append("_id") # special exception for symbols in lookup tables ins = ins_node.astNode std = std_node.astNode ins_field_list = list(ast.iter_fields(ins)) std_field_list = list(ast.iter_fields(std)) meta_matched = self.metas_match(ins_node, std_node, check_meta) is_match = len(ins_field_list) == len(std_field_list) and type(ins).__name__ == type( std).__name__ and meta_matched for insTup, stdTup in zip(ins_field_list, std_field_list): if not is_match: break ins_field = insTup[0] ins_value = insTup[1] std_field = stdTup[0] std_value = stdTup[1] if ins_value is None: continue ignore_field = ins_field in ignores is_match = (ins_field == std_field) or ignore_field if not isinstance(ins_value, list): ins_value = [ins_value] if not isinstance(std_value, list): std_value = [std_value] # is_match = len(ins_value) == len(std_value)# for stretchy matching this isn't True # Reference ast_node_visitor.js for the original behavior and keep note of it for the purposes of handling # the children noting the special case when the nodes of the array are actually parameters of the node # (e.g. a load function) instead of a child node if not ignore_field: for inssub_value, stdsub_value in zip(ins_value, std_value): if not is_match: break # TODO: make this a smarter comparison, maybe handle dictionaries, f-strings, tuples, etc. if is_primitive(inssub_value): is_match = inssub_value == stdsub_value if is_match: mapping = AstMap() # return MAPPING mapping.add_node_pairing(ins_node, std_node) return [mapping] else: return []
def shallow_match_main(self, ins_node, std_node, check_meta=True, ignores=None): """ Checks that all non astNode attributes are equal between ins_node and std_node Args: ins_node: Instructor ast root node std_node: Student AST root node check_meta: flag to check whether the fields of the instructor node and the student node should match ignores: a mapping between the instructor and student root nodes, or False if such a mapping doesn't exist Returns: """ if ignores is None: ignores = [] ignores.append("_id") # special exception for symbols in lookup tables ins = ins_node.astNode std = std_node.astNode ins_field_list = list(ast.iter_fields(ins)) std_field_list = list(ast.iter_fields(std)) meta_matched = self.metas_match(ins_node, std_node, check_meta) is_match = len(ins_field_list) == len(std_field_list) and type(ins).__name__ == type( std).__name__ and meta_matched for insTup, stdTup in zip(ins_field_list, std_field_list): if not is_match: break ins_field = insTup[0] ins_value = insTup[1] std_field = stdTup[0] std_value = stdTup[1] if ins_value is None: continue ignore_field = ins_field in ignores is_match = (ins_field == std_field) or ignore_field if not isinstance(ins_value, list): ins_value = [ins_value] if not isinstance(std_value, list): std_value = [std_value] # is_match = len(ins_value) == len(std_value)# for stretchy matching this isn't True # Reference ast_node_visitor.js for the original behavior and keep note of it for the purposes of handling # the children noting the special case when the nodes of the array are actually parameters of the node # (e.g. a load function) instead of a child node if not ignore_field: for inssub_value, stdsub_value in zip(ins_value, std_value): if not is_match: break # TODO: make this a smarter comparison, maybe handle dictionaries, f-strings, tuples, etc. if is_primitive(inssub_value): is_match = inssub_value == stdsub_value if is_match: mapping = AstMap() # return MAPPING mapping.add_node_pairing(ins_node, std_node) return [mapping] else: return []