Source code for curry.toolchain.flat2icurry

'''
The translation from FlatCurry to ICurry.

This is a port of the Curry package ``icurry`` 3.1.0 by Michael Hanus.  It
reads the FlatCurry of a module and the FlatCurry interfaces of its imports,
and it produces the ICurry term that ``icurry`` writes to a .icy file.  The
passes run in this order:

  0. The rewrite step of the routes (:func:`rewrite_prog`), when the
     caller asks for it: the binding optimization (:mod:`bindingopt`; the
     Boolean equalities whose value is required to be True become
     equational constraints, as the preprocessing of PAKCS makes them) and
     the set0 lift (:mod:`setlift`; ``set0 e`` over the captured variables
     of ``e`` becomes ``setk L xs``).  The routes from Curry to ICurry do
     not ask for it here: they apply it to the FlatCurry file itself
     before the translation (:func:`rewrite_file`), as PAKCS applies its
     preprocessing, so the file on disk is the rewritten program and the
     ``icurry`` program sees it too.  The flag serves a caller that must
     not touch the file, such as the option ``--rewrite`` of the command
     line.  The oracle tests do not ask for it.  The command line of
     :mod:`rewrite` applies the step to files by hand.
  1. Newtype elimination (:mod:`elimnewtype`).
  2. Case completion against the data declarations of the module and its
     imports (:mod:`casecompletion`).
  3. Lifting of nested cases, lets, and free declarations into new
     functions (:mod:`caselifting`).
  4. The name-to-index maps and the ICurry generation (:mod:`compiler`).

:func:`showterm` prints the result as the PAKCS ``showTerm`` does, so the
text is byte-identical to the output of ``icurry`` under PAKCS.
:func:`write_flatcurry` writes a FlatCurry program as the front end does.

One defect of ``icurry`` 3.1.0 is kept behind the flag ``icurry_compat``,
which is on by default: a type annotation at the root of a rule hides a
case, a let, or a free declaration from the block builder.  See
:func:`compiler.to_iblock`.  The oracle tests keep the flag on, so the
output stays byte-identical to the files ``icurry`` wrote.  The build route
(:mod:`curry.toolchain._frontend`) turns it off, so the programs it compiles
run.
'''

from . import bindingopt, setlift
from .bindingopt import optimize_bindings, optimize_file
from .casecompletion import complete_prog
from .caselifting import lift_prog
from .compiler import NameMaps, flat2icurry
from .elimnewtype import elim_newtype
from .errors import Flat2ICurryError
from .flatcurry import (
    data_decls_of, load as load_flatcurry, read as read_flatcurry
  , show as show_flatcurry, write as write_flatcurry
  )
from .interfaces import InterfaceFinder, load_interface, module_root, product_path
from .setlift import optimize_set0
from .terms import showhaskell, showterm

__all__ = [
    'Flat2ICurryError', 'InterfaceFinder', 'load_flatcurry', 'load_interface'
  , 'module_root', 'optimize_bindings', 'optimize_file', 'optimize_set0'
  , 'product_path', 'read_flatcurry', 'rewrite_file', 'rewrite_prog'
  , 'show_flatcurry', 'showhaskell', 'showterm', 'translate'
  , 'translate_file', 'write_flatcurry', 'write_icurry'
  ]

[docs] def rewrite_prog(prog, equivalence=True): ''' Applies the rewrite step of the routes to a FlatCurry program in memory: the binding optimization, then the set0 lift. Returns the program and the pair of counts (equalities replaced, set0 calls lifted); the program is ``prog`` itself when both counts are zero. The result of the binding optimization counts only when it replaced an equality, as :func:`bindingopt.optimize_bindings` has it (a rule it rebuilt without a replacement, its ``$`` reduced, is not handed on), so the lift runs over the program the former route translated. Without ``equivalence`` the binding optimization replaces ``===`` and ``/==`` alone (see :func:`bindingopt.transform_prog`). ''' optimized, n = bindingopt.transform_prog(prog, equivalence) if not n: optimized = prog lifted, m = setlift.transform_prog(optimized) return lifted, (n, m)
[docs] def rewrite_file(fcyfile, equivalence=True): ''' Applies the rewrite step to the FlatCurry file ``fcyfile`` in place, as the routes do after the front end wrote it. The file is written again, in the format of the front end, when a pass changed the program; else it is left as it is, with its time. Returns the pair of counts of :func:`rewrite_prog`. ''' prog = load_flatcurry(fcyfile) newprog, counts = rewrite_prog(prog, equivalence) if any(counts): write_flatcurry(newprog, fcyfile) return counts
[docs] def translate(prog, interfaces, icurry_compat=True, rewrite=False): ''' Translates a FlatCurry program to ICurry. Args: prog: The FlatCurry program. interfaces: The FlatCurry interfaces of the imports of ``prog``, in import order. icurry_compat: Whether to keep the output of ``icurry`` 3.1.0 for a type annotation at the root of a rule. See :func:`compiler.to_iblock`. rewrite: Whether to apply the rewrite step of the routes first, the binding optimization and the set0 lift. See :func:`rewrite_prog`. Returns: The ICurry program, an ``IProg`` term. ''' interfaces = list(interfaces) if rewrite: prog, _ = rewrite_prog(prog) prog = elim_newtype(interfaces, prog) datadecls = [] for p in [prog] + interfaces: datadecls.extend(data_decls_of(p)) ccprog = complete_prog(datadecls, prog) clprog = lift_prog(ccprog) maps = NameMaps.build(prog, interfaces, clprog) return flat2icurry(maps, clprog, icurry_compat)
[docs] def translate_file(fcyfile, finder, icurry_compat=True, rewrite=False): ''' Translates the FlatCurry file ``fcyfile``. ``finder`` is an :class:`InterfaceFinder` that supplies the interfaces of the imports. ''' prog = load_interface(fcyfile) interfaces = [finder.find(modname) for modname in prog.imports] return translate(prog, interfaces, icurry_compat, rewrite)
[docs] def write_icurry(iprog, filename): '''Writes an ICurry program to a .icy file.''' with open(filename, 'w', encoding='utf-8', newline='') as ostream: ostream.write(showterm(iprog))