from collections import defaultdict from enum import Enum class SMEM_Obj(): def __init__(self): self.processed = False self.obj_type = None self.id_var = None self.class_str = None self.wme_list = [] self.description = None def set_id_var(self, var): self.id_var = var def add_wme(self, attr, val): self.wme_list.append((attr,val)) def get_class(self): if self.class_str != None: return self.class_str for (attr,val) in self.wme_list: if attr == "^class": self.class_str = val break return self.class_str def get_description(self): if self.description != None: return self.description # Search for the description for (attr,val) in self.wme_list: if attr == "^description": self.description = val break return self.description def get_referenced_features(self, dict_of_values=None): # Collect a map of feature fields to possible values, as referenced by descendants from this object if not dict_of_values: dict_of_values = defaultdict(set) children = self.get_child_objects(compact=False) for child in children: _, short_label, desc = child.to_string() # Trim the label based on the object type if child.get_class() == "prim": if desc and desc != "()": str_to_use = desc try: ind = str_to_use.index(" is ") feature, value = str_to_use.replace("(","").replace(")","").split(" is ",maxsplit=1) dict_of_values[feature].add(value) except: print("ERROR: str_to_use missing 'is'. Value was: '"+str(str_to_use)+"'") else: str_to_use = short_label try: ind = str_to_use.index(" = ") feature, value = str_to_use.replace("(","").replace(")","").split(" = ",maxsplit=1) dict_of_values[feature].add(value) except: print("ERROR: str_to_use missing '='. Value was: '"+str(str_to_use)+"'") # Merge this dict of sets with results from each branch of children child_dict = child.get_referenced_features(dict_of_values) for key, val in child_dict.items(): if key in dict_of_values: dict_of_values[key].update(val) else: dict_of_values[key] = val return dict_of_values def get_compact_printable_object(self): """ Return this object if printable in compact mode, else return the set of next printable descendants. """ if self.obj_type == None: print("Warning: No obj_type for obj '"+self.id_var+"' in get_compact_printable_object().") return None retval = set() returnSelf = False if self.obj_type == ObjType.COND_PRIM: for (attr,val) in self.wme_list: if attr == "^supplies-condition": val.obj_type = ObjType.COND_CONJ retval.update(val.get_compact_printable_object()) elif self.obj_type == ObjType.COND_CONJ: for (attr,val) in self.wme_list: if attr == "^satisfies-op": val.obj_type = ObjType.OP returnSelf = True break elif attr == "^activates-context": val.obj_type = ObjType.CONTEXT returnSelf = True break elif attr == "^supplies-condition": val.obj_type = ObjType.COND_CONJ retval.update(val.get_compact_printable_object()) elif self.obj_type == ObjType.CONTEXT: for (attr,val) in self.wme_list: if attr == "^primes-condition": val.obj_type = ObjType.COND_PRIM retval.update(val.get_compact_printable_object()) else: returnSelf = True if returnSelf: retval = set() retval.add(self) return retval def get_child_objects(self, compact=True): """ If compact=True, skip condition prims and contexts, only get conjunctions and action groups """ if self.obj_type == None: print("Warning: No obj_type for obj '"+self.id_var+"'") return None retval = set() for (attr,val) in self.wme_list: if self.obj_type == ObjType.CONTEXT: if attr == "^primes-condition": val.obj_type = ObjType.COND_PRIM if compact: retval.update(val.get_compact_printable_object()) else: retval.add(val) elif self.obj_type == ObjType.COND_PRIM: if attr == "^supplies-condition": val.obj_type = ObjType.COND_CONJ if compact: retval.update(val.get_compact_printable_object()) else: retval.add(val) elif self.obj_type == ObjType.COND_CONJ: if attr == "^satisfies-op": val.obj_type = ObjType.OP retval.add(val) elif attr == "^activates-context": val.obj_type = ObjType.CONTEXT if compact: retval.update(val.get_compact_printable_object()) else: retval.add(val) elif attr == "^supplies-condition": val.obj_type = ObjType.COND_CONJ if compact: retval.update(val.get_compact_printable_object()) else: retval.add(val) elif self.obj_type == ObjType.OP: if attr == "^activates-action": val.obj_type = ObjType.ACT_GROUP retval.add(val) elif self.obj_type == ObjType.ACT_GROUP: if attr == "^action": val.obj_type = ObjType.ACT_PRIM retval.add(val) return retval def get_prim_cond_string(self, negate=False): if len(self.wme_list) == 0: return "", "" if negate: if self.class_str == "prim-exists": op_join_str = " not provided" else: op_join_str = " is not " else: if self.class_str == "prim-exists": op_join_str = " provided" else: op_join_str = " is " if self.class_str == None: self.get_class() retval_long, retval_short, retval_desc = ["(", "(", None] if self.class_str == "prim" or self.class_str == "prim-exists": group_str, attr_str, val_str = ["","",""] for (attr,val) in self.wme_list: if attr == "^group": group_str = val elif attr == "^attribute": attr_str = val elif attr == "^value": val_str = val elif attr == "^description": self.description = val retval_desc = val retval_long += group_str+"."+attr_str+op_join_str+val_str retval_short += attr_str+op_join_str+val_str return retval_long+")", retval_short+")", retval_desc def get_cond_string(self): retval_long, retval_short, retval_desc = ["","",None] # Compile the parent prim descriptions parent_conds = [] # child_conjs = [] is_negation = (self.get_class() == "negation") for (attr,val) in self.wme_list: if attr == "^receives-condition": parent_conds.append(val) if attr == "^description": self.description = val retval_desc = val # if attr == "^supplies-condition": # child_conjs.append(val) compile_component_descs = (retval_desc == None) if compile_component_descs: retval_desc = "" for i,cond in enumerate(parent_conds): if i > 0: retval_long += " \nAND " retval_short += " AND " if compile_component_descs: retval_desc += " AND " if cond.obj_type == ObjType.COND_CONJ: long, short, desc = cond.get_cond_string() else: long, short, desc = cond.get_prim_cond_string(negate=is_negation) retval_long += long retval_short += short if compile_component_descs: if desc: retval_desc += "("+desc+")" else: retval_desc = None compile_component_descs = False return retval_long, retval_short, retval_desc def get_op_string(self): retval_long, retval_short, retval_desc = ["", "", None] numeric_pref = None for (attr,val) in self.wme_list: if attr == "^name": retval_long = val retval_short = val elif attr == "^numeric-preference": try: numeric_pref = str(float(val)*100)+"%" except Exception as e: print("ERROR in get_op_string() converting numeric_pref to string: "+str(e)) numeric_pref = None elif attr == "^description": self.description = val retval_desc = val if numeric_pref: retval_long += "\nPercentage: "+numeric_pref # print(self.id_var+": "+retval) return retval_long, retval_short, retval_desc def get_prim_act_string(self): if len(self.wme_list) == 0: return "" if self.class_str == None: self.get_class() retval_long = "(" retval_short = "(" if self.class_str == "prim": type_str, attr_str, val_str = ["","",""] for (attr,val) in self.wme_list: if attr == "^type": type_str = val elif attr == "^attribute": attr_str = val elif attr == "^value": val_str = val retval_long += type_str+"."+attr_str+" = "+val_str retval_short += attr_str+" = "+val_str elif self.class_str == "prim-message": type_str, flag_str, msg_str = ["","",""] for (attr,val) in self.wme_list: if attr == "^type": type_str = val elif attr == "^flag": flag_str = val elif attr == "^message": msg_str = val retval_long += type_str+".flag = "+flag_str+" AND "+type_str+'.message = "'+msg_str+'"' retval_short += 'message: "'+msg_str+'"' return retval_long+")", retval_short+")" def get_act_group_string(self): retval_long, retval_short, retval_desc = ["", "", None] # Compile the child prim action descriptions child_prims = [] for (attr,val) in self.wme_list: if attr == "^action": child_prims.append(val) elif attr == "^description": self.description = val retval_desc = val for i,prim in enumerate(child_prims): if i > 0: retval_long += " AND " retval_short += " AND " long, short = prim.get_prim_act_string() retval_long += long retval_short += short return retval_long, retval_short, retval_desc def to_string(self): long = self.id_var short = self.id_var description = None if self.obj_type == ObjType.CONTEXT: return "", "", self.get_description() if self.obj_type == ObjType.COND_PRIM: long, short, description = self.get_prim_cond_string() # long = "IF "+long # short = "IF "+short if self.obj_type == ObjType.COND_CONJ: long, short, description = self.get_cond_string() if self.obj_type == ObjType.OP: long, short, description = self.get_op_string() if self.obj_type == ObjType.ACT_GROUP: long, short, description = self.get_act_group_string() if self.obj_type == ObjType.ACT_PRIM: long, short = self.get_prim_act_string() return long, short, description class ObjType(Enum): CONTEXT = 0 COND_PRIM = 1 COND_CONJ = 2 OP = 3 ACT_GROUP = 4 ACT_PRIM = 5