Source code for cls.cls_json

import json

from .fcl import (
    Apply,
    Combinator,
    Failed,
    FiniteCombinatoryLogic,
    InhabitationResult,
    Tree,
)
from .subtypes import Subtypes
from .types import Arrow, Constructor, Intersection, Omega, Product


[docs]class CLSEncoder(json.JSONEncoder): """A custom JSON encoder for classes related to Finite Combinatory Logic. This class extends `json.JSONEncoder` and provides a custom implementation of the `default` method to handle objects of the classes `Tree`, `Combinator`, `Apply`, `Failed`, `Arrow`, `Intersection`, `Product`, `Omega`, `Constructor`, `Subtypes`, `InhabitationResult`, and `FiniteCombinatoryLogic`. The output of the encoding process is a dictionary with a special key `__type__` that indicates the type of the original object. The remaining keys in the dictionary store the attributes of the original object. """ def __init__(self, **kwargs): """Initialize the JSON encoder. This method calls the `__init__` method of the parent class `json.JSONEncoder` with the same `kwargs` to properly initialize the object. """ super().__init__(**kwargs)
[docs] def combinator_hook(self, o): """Handle the encoding of `Combinator` objects. This method delegates the encoding of a `Combinator` object to the `default` method of `json.JSONEncoder`. :param o: The `Combinator` object to be encoded. :type o: Combinator :return: The encoded `Combinator` object. :rtype: dict """ return json.JSONEncoder.default(self, o)
[docs] def constructor_hook(self, o): """Handle the encoding of `Constructor` objects. This method delegates the encoding of a `Constructor` object to the `default` method of `json.JSONEncoder`. :param o: The `Constructor` object to be encoded. :type o: Constructor :return: The encoded `Constructor` object. :rtype: dict """ return json.JSONEncoder.default(self, o)
[docs] @staticmethod def tpe(o): """Return a string representation of the type of an object. This method returns a string of the form `module_name.class_name` for a given object. :param o: The object whose type is to be returned. :type o: object :return: A string representation of the type of `o`. :rtype: str """ return f"{o.__class__.__module__}.{o.__class__.__qualname__}"
[docs] def default(self, o): """Handle the encoding of objects. This method provides a custom implementation of the `default` method to handle objects of various types related to Finite Combinatory Logic. If the object is not one of the supported types, the method delegates the encoding to the `default` method of `json.JSONEncoder`. :param o: The object to be encoded. :type o: object :return: The encoded object. :rtype: dict """ if isinstance(o, Tree): return { "__type__": CLSEncoder.tpe(o), "rule": self.default(o.rule), "children": [self.default(c) for c in o.children], } elif isinstance(o, Combinator): return { "__type__": CLSEncoder.tpe(o), "target": self.default(o.target), "combinator": self.combinator_hook(o.combinator), } elif isinstance(o, Apply): return { "__type__": CLSEncoder.tpe(o), "target": self.default(o.target), "function_type": self.default(o.function_type), "argument_type": self.default(o.argument_type), } elif isinstance(o, Failed): return {"__type__": CLSEncoder.tpe(o), "target": self.default(o.target)} elif isinstance(o, Arrow): return { "__type__": CLSEncoder.tpe(o), "source": self.default(o.source), "target": self.default(o.target), } elif isinstance(o, Intersection): return { "__type__": CLSEncoder.tpe(o), "left": self.default(o.left), "right": self.default(o.right), } elif isinstance(o, Product): return { "__type__": CLSEncoder.tpe(o), "left": self.default(o.left), "right": self.default(o.right), } elif isinstance(o, Omega): return {"__type__": CLSEncoder.tpe(o)} elif isinstance(o, Constructor): return { "__type__": CLSEncoder.tpe(o), "name": self.constructor_hook(o.name), "arg": self.default(o.arg), } elif isinstance(o, Subtypes): return { "__type__": CLSEncoder.tpe(o), "environment": { self.constructor_hook(k): [self.constructor_hook(c) for c in v] for k, v in o.environment.items() }, } elif isinstance(o, InhabitationResult): return { "__type__": CLSEncoder.tpe(o), "targets": [t for t in self.default(o.targets)], "rules": [self.default(r) for r in o.rules], } elif isinstance(o, FiniteCombinatoryLogic): return { "__type__": CLSEncoder.tpe(o), "repository": { self.combinator_hook(c): self.default(t) for c, t in o.repository }, "subtypes": self.default(o.subtypes), "processes": self.default(o.processes), } else: return json.JSONEncoder.default(self, o)
[docs]class CLSDecoder(json.JSONDecoder): """A custom JSON decoder for decoding objects of different types from a JSON string related to Finite Combinatory Logic. The decoder uses the `__type__` field in the JSON string to determine the type of the object being decoded. The object is then constructed using the information in the JSON string. The decoder supports several different types of objects including `Tree`, `Combinator`, `Apply`, `Failed`, `Arrow`, `Intersection`, `Product`, `Omega`, `Constructor`, `Subtypes`, `InhabitationResult`, and `FiniteCombinatoryLogic` """ def __init__(self, **kwargs): """Initializes the `CLSDecoder` class with the `object_hook` set to `self`. Any additional keyword arguments passed to the constructor will be passed to the parent `JSONDecoder` constructor. """ super().__init__(object_hook=self, **kwargs)
[docs] def combinator_hook(self, dct): """A hook for processing the `combinator` field in the JSON string. By default, the hook simply returns the `combinator` field without any processing. :param dct: The dictionary representing the JSON object being decoded. :type dct: dict :return: The `combinator` field. :rtype: dict """ return dct
[docs] def constructor_hook(self, dct): """Return the input dictionary `dct` unmodified. This method is a hook for `CLSDecoder` class to handle `Constructor` objects during JSON decoding. It simply returns the input dictionary `dct` unmodified. :param dct: A dictionary representing a JSON object. :type dct: dict :return: The input dictionary `dct` unmodified. :rtype: dict """ return dct
[docs] @staticmethod def tpe(cls): """A static method that returns a string representation of the class object. :return: A string representation of the class object, in the format `{module}.{qualname}`. :rtype: str """ return f"{cls.__module__}.{cls.__qualname__}"
def __call__(self, dct): """The `__call__` method of the `CLSDecoder` class is used to parse a dictionary, `dct`, into an object of one of several possible subclasses of the `CLS` class. The class of the returned object is determined by the `__type__` key in `dct`, which specifies the type of the object. The type is represented as a string of the form `module_name.class_name`. The method uses the `tpe` static method to convert a class object into such a string. The method uses several `if` statements to check the value of `dct["__type__"]` against the string representation of each possible `CLS` subclass, and returns an object of the corresponding class if a match is found. The constructor arguments of the returned object are extracted from `dct` and possibly processed by the `constructor_hook` and `combinator_hook` methods of the `CLSDecoder` class. If the value of `dct["__type__"]` does not match any of the expected values, the original dictionary `dct` is returned as is. :param dct: The dictionary to be parsed into an object of a `CLS` subclass. :type dct: dict :return: An object of a subclass of the `CLS` class, or the original `dct` if no match was found. :rtype: object """ if "__type__" in dct: tpe = dct["__type__"] if tpe == CLSDecoder.tpe(Tree): return Tree(rule=dct["rule"], children=tuple(dct["children"])) elif tpe == CLSDecoder.tpe(Combinator): return Combinator( target=dct["target"], combinator=self.combinator_hook(dct["combinator"]), ) elif tpe == CLSDecoder.tpe(Apply): return Apply( target=dct["target"], function_type=dct["function_type"], argument_type=dct["argument_type"], ) elif tpe == CLSDecoder.tpe(Failed): return Failed(target=dct["target"]) elif tpe == CLSDecoder.tpe(Arrow): return Arrow(source=dct["source"], target=dct["target"]) elif tpe == CLSDecoder.tpe(Intersection): return Intersection(left=dct["left"], right=dct["right"]) elif tpe == CLSDecoder.tpe(Product): return Product(left=dct["left"], right=dct["right"]) elif tpe == CLSDecoder.tpe(Omega): return Omega() elif tpe == CLSDecoder.tpe(Constructor): return Constructor( name=self.constructor_hook(dct["name"]), arg=dct["arg"] ) elif tpe == CLSDecoder.tpe(Subtypes): return Subtypes( environment={ self.constructor_hook(k): { self.constructor_hook(st) for st in v } for k, v in dct["environment"].items() } ) elif tpe == CLSDecoder.tpe(InhabitationResult): return InhabitationResult( targets=dct["targets"], rules={r for r in dct["rules"]} ) elif tpe == CLSDecoder.tpe(FiniteCombinatoryLogic): return FiniteCombinatoryLogic( repository={ self.combinator_hook(k): v for k, v in dct["repository"].items() }, subtypes=dct["subtypes"], processes=dct["processes"], ) else: return dct return dct