Source code for valasp.domain.primitive_types

# This file is part of ValAsp which is released under the Apache License, Version 2.0.
# See file README.md for full license details.

"""The enum :class:`Fun` and the primitive types are defined in this module.

The primitive types are markers to be used in annotations. They cannot be instantiated, and offer a very limited set of utility function.
"""

from dataclasses import dataclass
from enum import Enum
from typing import List, ClassVar

import clingo
import typing

from valasp.domain.names import PredicateName


[docs]class Fun(Enum): """Modalities for the :meth:`validate` decorator. * **FORWARD_IMPLICIT** means FORWARD for symbols of arity 1, and IMPLICIT otherwise. * **FORWARD** can be used with symbols of arity 1 and essentially means to leave the init argument as it is. * **IMPLICIT** must be used if the init argument is expected to be a function with the same name of the symbol. The arguments of the function are unpacked. * **TUPLE** is like IMPLICIT, but the function is expected to have emtpy name. """ FORWARD_IMPLICIT = 0 FORWARD = 1 IMPLICIT = 2 TUPLE = 3
[docs]@dataclass(frozen=True) class Type: """Base class for type annotations. This class cannot be instantiated, and its subclasses are expected to satisfy this invariant. """ __primitives = None def __init__(self): raise NotImplemented('this class must be used only as a marker')
[docs] @staticmethod def init_code(arg: str) -> List[str]: """Return code to validate the type of the given argument name. Subclasses are expected to call this method in their init_code() method. :param arg: the name of the argument :return: validation code """ return [ f'if not isinstance({arg}, clingo.Symbol):', f' raise TypeError(f"expecting clingo.Symbol, but received type({{{arg}}})")', ]
[docs] @classmethod def is_primitive(cls, typ: ClassVar) -> bool: """Return true if typ is considered a primitive type. :param typ: a type :return: true if typ is a primitive type """ return typ in cls.__primitives
[docs] @classmethod def get_primitive(cls, typ: ClassVar) -> 'Type': """Return a subclass of Type associated with typ. :param typ: a type for which Type.is_primitive(typ) == True :raise: KeyError if Type.is_primitive(typ) != True :return: a subclass of Type associated with typ """ return cls.__primitives[typ]
[docs] @classmethod def set_primitives(cls, value) -> None: """Init the Type class with primitive types. This method is called at the end of this file, and cannot be called a second time. :param value: a dictionary (see the bottom of this file) :raise: PermissionError if called a second time """ if cls.__primitives: raise PermissionError(f'attempt to call set_primitives of {cls} from outside its module') cls.__primitives = value
[docs] @classmethod def parse(cls, value: str) -> str: """Return value, or raises an exception if value is not a string. :param value: a string to be parsed :raise: TypeError if value is not str :return: value """ if not isinstance(value, str): raise TypeError(f"expecting str, but found {type(value)}") return value
[docs]@dataclass(frozen=True) class Integer(Type): """Primitive type representing integers. This class cannot be instantiated. """ def __init__(self): super().__init__()
[docs] @classmethod def init_code(cls, arg: str) -> List[str]: return super().init_code(arg) + [ f'if {arg}.type != clingo.SymbolType.Number:', f' raise TypeError(f"expecting clingo.SymbolType.Number, but received {{{arg}}}")', f'self.{arg} = {arg}.number', f'if not({cls.min()} <= self.{arg} <= {cls.max()}):', f' raise OverflowError(f"argument {arg} will overflow with value {{{arg}}}")', ]
[docs] @classmethod def max(cls) -> int: """Return the greatest integer for the ASP system. :return: the greatest integer """ return 2**31 - 1
[docs] @classmethod def min(cls) -> int: """Return the smallest integer for the ASP system. :return: the smallest integer """ return -2**31
[docs] @classmethod def parse(cls, value: str) -> int: """Return the integer represented in value. :param value: a string to be parsed :raise: TypeError if value is not an integer, or if its type is not str :raise: OverflowError if value does not fit into a 32-bit signed integer :return: the integer in value """ res = int(super().parse(value)) if not(cls.min() <= res <= cls.max()): raise OverflowError(f"{value} will overflow") return res
[docs]@dataclass(frozen=True) class String(Type): """ Primitive type representing strings. This class cannot be instantiated. """ def __init__(self): super().__init__()
[docs] @classmethod def init_code(cls, arg: str) -> List[str]: return super().init_code(arg) + [ f'if {arg}.type != clingo.SymbolType.String:', f' raise TypeError(f"expecting clingo.SymbolType.String, but received {{{arg}}}")', f'self.{arg} = {arg}.string', ]
[docs] @classmethod def parse(cls, value: str) -> str: """Return value, as any string is valid :param value: a string to be parsed :return: value """ return super().parse(value)
[docs]@dataclass(frozen=True) class Alpha(Type): """Primitive type representing named constants. This class cannot be instantiated. """ def __init__(self): super().__init__()
[docs] @classmethod def init_code(cls, arg: str) -> List[str]: return super().init_code(arg) + [ f'if {arg}.type != clingo.SymbolType.Function:', f' raise TypeError(f"expecting clingo.SymbolType.Function, but received {{{arg}}}")', f'if {arg}.arguments:', f' raise TypeError(f"expecting function of arity 0, but it is {{{arg}.arguments}}")', f'self.{arg} = {arg}.name', ]
[docs] @classmethod def parse(cls, value: str) -> str: """Return value, or raise an exception if value is not valid :param value: a string to be parsed :raise: ValueError if value is not a valid function name, or TypeError if the type of value is not str :return: value """ value = super().parse(value) return PredicateName(value).value
[docs]@dataclass(frozen=True) class Any(Type): """Primitive type representing wildcards. This class cannot be instantiated. """ def __init__(self): super().__init__()
[docs] @classmethod def init_code(cls, arg: str) -> List[str]: return super().init_code(arg) + [ f'if {arg}.type == clingo.SymbolType.Number:', f' self.{arg} = {arg}.number', f'elif {arg}.type == clingo.SymbolType.String:', f' self.{arg} = {arg}.string', f'elif {arg}.type == clingo.SymbolType.Function:', f' self.{arg} = {arg}', f'else:' f' raise ValueError("expecting Number, String or Function, received {{{arg}.type}}")' ]
Type.set_primitives({ Integer: Integer, int: Integer, clingo.Number: Integer, String: String, str: String, clingo.String: String, Alpha: Alpha, Any: Any, typing.Any: Any, })