ICurry is an intermediate format to compile Curry to different imperative languages. ICurry is intended to be generic so that different target languages can be supported with a similar effort.
The definition of ICurry is inspired by the Curry compiler Sprite which compiles Curry programs into LLVM code. The definition of ICurry implemented in this package follows the paper on ICurry.
This package contains the definition of ICurry as Curry data types
(module ICurry.Types
), a simple compiler to translate Curry
programs to ICurry, and an interpreter for ICurry programs based on the
small-step semantics of ICurry. The latter can also be used to visualize
the graph constructed during the execution of ICurry programs.
These tools are available in the icurry
binary installed
with this package.
Translated ICurry programs have the suffix icy
. The
suffix icurry
is already used by the front end for
interface files of Curry programs.
Note that the ICurry compiler is a prototype used to compile single
Curry modules into corresponding ICurry programs. If a Curry module has
several imports, one has to compile these imports into ICurry manually
(the automation of this process will be done in the future). In the
following, we describe various uses of the icurry
tool.
To compile a Curry program Prog.curry
into the
ICurry format, invoke the command
> icurry Prog
This will generate the file Prog.icy
in the directory
where the Curry system stores intermediate files, e.g.,
.curry/pakcs-3.2.0
. Hence, the suffix icy
is
used for generated ICurry programs. If the program is located in a
non-local directory, i.e., Prog
is not a module name but
prefixed by a directory DIR
, this command switches to the
directory DIR
so that the generated file is placed in the
intermediate files in directory DIR
.
The option -o
can be used to specify an explicit output
file different from the intermediate file for the ICurry program. With
the option -o -
, the ICurry program is printed in
stdout.
In order to see a human-readable presentation of the generated
program, use option -v
, i.e.,
> icurry -v Prog
One can also use a simple (i.e., not efficient) interpreter to
execute ICurry programs and visualize their behavior. In this case, one
has to provide the name of a 0-ary function mymain
and
invoke icurry
by
> icurry -m mymain Prog
This compiles Prog.curry
into ICurry (but do not store
the compiled program in a file), invokes the ICurry interpreter (see
ICurry.Interpreter
), and shows the results of evaluating
mymain
.
With option --interactive
, the ICurry interpreter stops
after each result and ask for a confirmation to proceed, which is useful
if their might be infinitely many results. For instance, the program
BoolList.curry
in the examples
directory
defines the function main
which non-deterministically
evaluates to any list of Booleans. Hence, it is reasonable to execute it
by
> icurry -m main --interactive BoolList
More details about the state of the interpreter can be shown by increasing the verbosity level, e.g.,
> icurry -m main --interactive -v3 BoolList
The ICurry interpreter can also visualize the term graph manipulated
during execution as a PDF generated by dot
. If
icurry
is invoked by
> icurry -m mymain --graph Prog
the graph after each step is shown by evince
(see
parameter --viewer
) and each step is executed in one
second.
One could also add a visualization level to the option. For instance, the following command shows the full graph (and not only the reachable nodes):
> icurry -m mymain --graph=2 Prog
It is also possible to generate a single PDF file containing pages
with the graph states by providing the output
option (this
requires the tool pdftk
):
> icurry -m mymain --graph --output=Eval.pdf Prog
More executions options are available by invoking the interpreter
manually via the operation
ICurry.Interpreter.execProg
.
--nolifting
:
As a default, the ICurry compiler lifts all nested case/let
expressions, i.e., it transforms them into auxiliary top-level
operations. This is also necessary for the simple interpreter contained
in this package (see below). If an implementation of ICurry can deal
with nested case/let expressions, one can use the option
--nolifting
to supress this lifting.
--optvardecls
:
The ICurry compiler generates declaration (IVarDecl
) for
each variable used in a block in order to support cyclic data structures
(see the paper on ICurry). For imperative target languages that do not
require explicit variables declaration but allow the introduction of
variables by assignments (see type IAssign
), one can use
the option --optvardecls
to suppress the generation of
variable declarations when they are introduced by assignments. With this
options, variable declarations are only generated for variables which
have their first occurrence in an expression (which is the case when
cyclic data structures are used).
The interpreter evaluates expressions up to head normal form. In
order to evaluate the main expression to normal form, one can wrap it
with the function normalForm
, e.g.,
mymain = normalForm (reverse [1,2,3])
The current version of the interpreter supports only the prelude
operations normalForm
, $#
, and
$!
.