Source code for curry.interpreter.conversions

'''
Functions for working with Curry expresions.  Handles conversions between Curry
and Python.
'''

from .. import backends, exceptions, inspect
from ..toolchain.flat2icurry import flatcurry as fc
import numbers

__all__ = ['currytype', 'getconverter', 'topython', 'unbox']

LIST = ('Prelude', '[]')
CHAR = ('Prelude', 'Char')
STRING_NODE = '_biString'

[docs] def currytype(interp, ty): ''' Gets the Curry type corresponding to a Python type. For instance, for the Python ``bool`` type, this returns the type object for ``Prelude.Bool``. ''' if issubclass(ty, bool): return interp.type('Prelude.Bool') elif issubclass(ty, str): return interp.type('Prelude.Char') elif issubclass(ty, numbers.Integral): return interp.type('Prelude.Int') elif issubclass(ty, numbers.Real): return interp.type('Prelude.Float') elif issubclass(ty, list): return interp.type('Prelude.[]')
# raise TypeError('cannot convert %r to a Curry type' % ty.__name__)
[docs] def unbox(arg): '''Unbox a built-in primitive or IO type.''' assert isinstance(arg, backends.Node) assert inspect.isa_primitive(arg) or inspect.isa_io(arg) return arg.successor(0)
def _element_type(typeexpr): '''The element type of a list type, or None for an unknown type.''' if isinstance(typeexpr, fc.TCons) and typeexpr.name == LIST and typeexpr.args: return typeexpr.args[0] return None def _component_types(typeexpr, n): '''The component types of a tuple type, or ``n`` unknown types.''' if isinstance(typeexpr, fc.TCons) and len(typeexpr.args) == n: return typeexpr.args return [None] * n def _is_char(typeexpr): return isinstance(typeexpr, fc.TCons) and typeexpr.name == CHAR def _is_known(typeexpr): '''Whether a type is a constructor type; a variable decides nothing.''' return isinstance(typeexpr, (fc.TCons, fc.FuncType)) class ToPython(object): def __init__(self, convert_freevars=True): self.convert_freevars = convert_freevars def __call__(self, value, convert_strings=True, typeexpr=None): '''Convert one value.''' return self.__convert(value, convert_strings, typeexpr) def __convert(self, value, convert_strings=True, typeexpr=None): if inspect.isa_boxed_primitive(value) or inspect.isa_io(value): return unbox(value) elif inspect.isa_bool(value): return inspect.isa_true(value) elif inspect.isa_list(value): elemtype = _element_type(typeexpr) elems = list(_listiter(value)) l = [self.__convert(elem, convert_strings, elemtype) for elem in elems] if convert_strings: # The static type decides when it is known: [Char] is a string, # also when it is empty. Without a type, a list of characters is a # string, as it was before the types; a list of strings stays a # list. if _is_char(elemtype): return ''.join(l) if not _is_known(elemtype) and l \ and all(inspect.isa_boxed_char(elem) for elem in elems): return ''.join(l) return l elif inspect.isa_tuple(value): comps = _component_types(typeexpr, len(value.successors)) return tuple( self.__convert(x, convert_strings, t) for x, t in zip(value.successors, comps) ) elif _is_string_node(value): # The string node of the Python backend, not yet unfolded. from ..backends.py.currylib.prelude.string import text return text(value.successor(0)) else: return value def _is_string_node(value): info = inspect.info_of(value) return info is not None and info.name == STRING_NODE \ and isinstance(value.successor(0), memoryview) _topython_converter_ = ToPython(convert_freevars=False)
[docs] def topython(interp, value, convert_strings=True, exprtype=None): ''' Converts a Curry value to Python by substituting built-in types. This functions converts (recursively) the types ``int``, ``float``, ``str``, ``bool``, ``list``, and ``tuple``. Other types are passed through untouched. Args: value: The Curry value to convert. convert_strings: If True, then lists of characters are converted to Python strings. exprtype: The static type of the value, in Curry syntax or as a FlatCurry type, or None. The type is threaded through the lists and the tuples: an empty list under ``[Char]`` converts to ``''``, while a list under a type variable or without a type converts to a string when it holds characters. ``curry.eval`` passes the type of the goal it built. Raises: NotConstructorError: Non-ground data was encountered along a list spine. Returns: The value converted to Python. ''' if isinstance(exprtype, str): from ..typecheck import exprtype as exprtype_mod exprtype, _ = exprtype_mod.parse_type(interp, exprtype) return _topython(value, convert_strings, exprtype)
def _topython(value, convert_strings=True, typeexpr=None): '''Internal version of ``topython`` that takes no interpreter.''' # This conversion probably ought to depend on the interpreter. The flags # could control how this conversion is performed. For forward compatibility, # it is probably best to require an interpreter even if currently unused. value = getattr(value, 'target', value) # handle Variable return _topython_converter_(value, convert_strings, typeexpr) def _listiter(arg): '''Iterate through a Curry list.''' while inspect.isa_cons(arg): yield arg.successor(0) arg = arg.successor(1) if not inspect.isa_nil(arg): raise exceptions.NotConstructorError(arg)
[docs] def getconverter(converter): ''' Get the converter corresponding to the argument: ``None`` and a callable are returned as they are, ``'topython'`` names :func:`topython`, and any other name means no conversion. A free variable in a value passes through the converter as a node; :func:`curry.show_value` prints it as ``_a``, ``_b`` and so on. ''' if converter is None or callable(converter): return converter elif converter == 'topython': return topython