def from_json(json_object): prefix = "<class 'nlglib.microplanning.struct." if '__class__' in json_object: cls = json_object['__class__'] if cls == ("%sElement'>" % prefix): rv = Element.from_dict(json_object['__value__']) elif cls == ("%sElementList'>" % prefix): rv = ElementList.from_dict(json_object['__value__']) elif cls == ("%sString'>" % prefix): rv = String.from_dict(json_object['__value__']) elif cls == ("%sWord'>" % prefix): rv = Word.from_dict(json_object['__value__']) elif cls == ("%sVar'>" % prefix): rv = Var.from_dict(json_object['__value__']) elif cls == ("%sPhrase'>" % prefix): rv = Phrase.from_dict(json_object['__value__']) elif cls == ("%sNounPhrase'>" % prefix): rv = NounPhrase.from_dict(json_object['__value__']) elif cls == ("%sVerbPhrase'>" % prefix): rv = VerbPhrase.from_dict(json_object['__value__']) elif cls == ("%sPrepositionPhrase'>" % prefix): rv = PrepositionPhrase.from_dict(json_object['__value__']) elif cls == ("%sAdjectivePhrase'>" % prefix): rv = AdjectivePhrase.from_dict(json_object['__value__']) elif cls == ("%sAdverbPhrase'>" % prefix): rv = AdverbPhrase.from_dict(json_object['__value__']) elif cls == ("%sCoordination'>" % prefix): rv = Coordination.from_dict(json_object['__value__']) elif cls == ("%sClause'>" % prefix): rv = Clause.from_dict(json_object['__value__']) elif cls == "<class 'nlglib.features.feature.FeatureSet'>": rv = FeatureSet() rv.update(json_object['__value__']) else: raise TypeError('Unknown class "{}"'.format(cls)) if hasattr(rv, 'update_parents'): rv.update_parents() return rv return json_object
class Element(object): """A base class representing an NLG element. Aside for providing a base class for other kinds of NLG elements, the class also implements basic functionality for elements. """ category = category.ELEMENT def __init__(self, features=None, parent=None, id=None): self.features = FeatureSet() self.features.update(features) self.parent = parent self.id = id self.hash = -1 def __copy__(self): rv = self.__class__(features=self.features, parent=self.parent, id=self.id) return rv # noinspection PyArgumentList def __deepcopy__(self, memo): rv = self.__class__(features=None, parent=None, id=self.id) memo[id(self)] = rv rv.features = deepcopy(self.features, memo=memo) rv.parent = memo.get(id(self.parent), None) return rv def __bool__(self): """Because Element is abstract, it will evaluate to false. """ return False def __eq__(self, other): return (isinstance(other, Element) and self.id == other.id and self.category == other.category and comparable_features( self.features) == comparable_features(other.features)) def __hash__(self): if self.hash == -1: self.hash = hash(str(self)) return self.hash def __repr__(self): from . import visitors v = visitors.ReprVisitor() self.accept(v) return str(v) def __str__(self): from . import visitors v = visitors.SimpleStrVisitor() self.accept(v) return str(v) def __contains__(self, feature_name): """Check if the argument feature name is contained in the element.""" return feature_name in self.features def __setitem__(self, feature_name, feature_value): """Set the feature name/value in the element feature set.""" self.features[feature_name] = feature_value def __getitem__(self, feature_name): """Return the value associated with the feature name, from the element feature set. If the feature name is not found in the feature dict, return None. """ return self.features.get(feature_name) def __delitem__(self, feature_name): """Remove the argument feature name and its associated value from the element feature set. """ self.features.discard(feature_name) def __add__(self, other): """Add two elements resulting in a coordination if both elements are not "False" else return the "True" element. """ if not self: return other if not other: return self return Coordination(self, other) @classmethod def from_dict(cls, dct): o = cls(None, None, None) o.__dict__.update(dct) return o @classmethod def from_json(cls, s): return json.loads(s, cls=ElementDecoder) def to_json(self): return json.dumps(self, cls=ElementEncoder) def to_xml(self, depth=0, headers=False): from . import visitors visitor = visitors.XmlVisitor(depth=depth) self.accept(visitor) if headers: return str(visitor) else: return str(visitor.xml) def accept(self, visitor, **kwargs): """Implementation of the Visitor pattern.""" visitor_method_name = self.category.lower() # get the appropriate method of the visitor instance m = getattr(visitor, visitor_method_name) # ensure that the method is callable if not hasattr(m, '__call__'): msg = 'Error: cannot call undefined method: %s on visitor' raise ValueError(msg % visitor_method_name) # and finally call the callback return m(self, **kwargs) def elements(self, recursive=False, itself=None): """Return a generator yielding elements contained in the element :param bool recursive: also include sub-elements of the contained elements :param str itself: yield `self` as one of the elements; values in (None, 'first', 'last') """ if itself or recursive and self.category != category.ELEMENT: yield self def arguments(self): """Return any arguments (vars) from the element as a list. """ return [ x for x in self.elements(recursive=True) if x.category == category.VAR ] def replace(self, one, another, key=lambda x: x): """Replace the first occurrence of `one` by `another`. :param one: a constituent to replace; will be raised to element :param another: a replacement element; will be raised to element :param key: a key function for comparison; default is identity :returns: True if replacement occurred; False otherwise """ return False # basic implementation does nothing def replace_argument(self, id, replacement): """Replace an argument with given `id` by `replacement` if such argument exists. """ for a in self.arguments(): if a.id == id: return self.replace(a, replacement) return False def replace_arguments(self, **kwargs): """Replace arguments with ids in the kwargs by the corresponding values. """ for k, v in kwargs.items(): self.replace_argument(k, v) @property def string(self): """Return the string inside the value. """ return '' def update_parents(self, parent=_sentinel): """Re-set the `parent` attribute of nested elements.""" if parent is not _sentinel: self.parent = parent
class ElementList(collections.UserList): category = category.ELEMENT_LIST def __init__(self, lst=None, parent=None, features=FeatureSet()): super().__init__() self.parent = parent self.features = FeatureSet() self.features.update(features) for o in lst or []: self.append(o) def append(self, item): item = raise_to_element(item) item.parent = self.parent item.features.update(self.features) super().append(item) def insert(self, i, item): item = raise_to_element(item) item.parent = self.parent item.features.update(self.features) super().insert(i, item) def remove(self, item): raised_item = raise_to_element(item) super().remove(raised_item) def __contains__(self, item): raised_item = raise_to_element(item) return super().__contains__(raised_item) def __iadd__(self, other): if isinstance(other, (ElementList, list, tuple)): for x in other: self.append(x) else: self.append(other) return self def __add__(self, other): rv = ElementList(self, parent=self.parent, features=self.features) rv += other return rv def __setitem__(self, i, value): value = raise_to_element(value) value.parent = self.parent value.features.update(self.features) super().__setitem__(i, value) # noinspection PyArgumentList def __deepcopy__(self, memo): rv = self.__class__() memo[id(self)] = rv rv.parent = memo.get(id(self.parent), None) rv.features = deepcopy(self.features, memo) for o in self: rv.append(deepcopy(o, memo)) return rv @classmethod def from_dict(cls, dct): o = cls() o.__dict__.update(dct) return o @classmethod def from_json(cls, s): return json.loads(s, cls=ElementDecoder) def to_json(self): return json.dumps(self, cls=ElementEncoder) def elements(self, recursive=False, itself=None): """Return a generator yielding elements contained in the element Note that ElementList is a pseudo-element so it doesn't return itself even if the param is specified. :param bool recursive: also include sub-elements of the contained elements :param str itself: yield `self` as one of the elements; values in (None, 'first', 'last') """ if recursive: for e in self: yield from e.elements(recursive, itself) else: for e in self: yield e def update_parents(self, parent=_sentinel): if parent is not _sentinel: self.parent = parent for x in self: x.update_parents(parent=parent)