A rudimentary Racket implementation using RPython
Pycket is a Racket/Scheme implementation that is generated using the RPython framework. Given an interpreter written in RPython (in our case a CEK machine interpreter for Racket), RPython framework produces a fast binary for it. It can also add a tracing JIT.
There are currently two different modes that we refer as
linkletsand bootstraps the Racket using the
expanderlinklet exported by Racket (version 7+). The
OLDPycket, on the other hand, uses Racket's binary to fully expand the program and generates
jsonasts and evaluates them.
Note that both versions require an unmodified Racket installation. The
OLDPycket requires a Racket binary, and while the
NEWPycket doesn't require a Racket binary, it still requires the Racket packages and libraries to bootstrap.
See the Makefile targets section about how to build both versions.
Building Pycket means translating the interpreter (written in RPython) into a binary. You can use the make targets to translate Pycket. We recommend using the PyPy for translation, since it'll be much faster than CPython. If you don't have a
pypybinary in your environment, then
maketargets will default to CPython.
You can clone and make the
make make-pypytarget. It will clone the latest pypy repo in Pycket's directory and will start making it. Note that it will take more than 2 hours to build the pypy. It will result in a binary created at
pypy/pypy/goal/pypy, and you need to add it to your environment for Pycket's translation to use it.
Additionally, it helps to have the build dependencies of PyPy installed. On a Debian or Ubuntu system:
$ sudo apt-get build-dep pypy
To produce a Pycket executable, use one of the provided make targets to build the binary you need.
Assumes the mercurial binary
hgto be in the environment.
make clone-pypy: clones the latest pypy into Pycket's directory
make update-pypy: pulls and updates the pypy
make make-pypy: builds pypy, assumes that pypy directory exists in Pycket's directory
make pycket-c: translates
OLD Pycketwith JIT
make pycket-c-linklets: translates
NEW Pycketwith JIT
make pycket-c-nojit: translates
OLD Pycketwithout JIT (which may be a lot faster to translate but runs a lot lot slower)
make pycket-c-linklets-nojit: translates
NEW Pycketwithout JIT (which may be a lot faster to translate but runs a lot lot slower)
make setup-old-pycket: installs the
pycket-langto Racket and runs
make expander: generates the expander linklet (it assumes an unmodified Racket install and PLTHOME environment variable -- see the Environment Variables section below)
make setup-local-racket: if you don't have a Racket and don't want to deal with Racket related stuff, then run this to get a latest running Racket. Make sure to run
export PLTHOME=`pwd`/after it's done (see environment variables section).
Pycket currently defaults to the
OLDPycket. To use the
NEWversion with the linklets, run it with:
You can run with the
-hoption to see the different command line options for each versions:
$ ./pycket-c -h
$ ./pycket-c-linklets -h
You can run
$ ./pycket-c program.rkt
You can also run pycket under plain python (or
pypyif its available), like this:
$ ./pycket-slow.sh program
Running the interpreter on CPython or PyPy (i.e. running the targetpycket.py) requires a
PYTHONPATHthat includes both
RPython(that should be the
pypydirectory cloned above) and
pycket(that should be this directory).
Also there are a couple of variables need to be set for the
NEWPycket to interact with the Racket, since it bootstraps Racket by reading and evaluating Racket's main collection by loading and using the bootstrap linklets (currently only the
expander.rktl.linklet) exported by Racket. So the
NEWPycket needs to be able to locate various different parts of the Racket installation. The
OLDPycket is lucky to use the Racket's own binary.
Naturally, it varies on the way in which the Racket is installed:
Then all the
NEWPycket needs is a
PLTHOMEenvironment variable to point to the surrounding directory. For example it will assume the
collectsdirectory is at:
NEWPycket needs to know the locations of various directories. In particular, it needs:
PLTEXECFILEto point to the location of the
PLTCOLLECTSto point to the
collectsdirectory for the main collections
PLTCONFIGDIRto point to Racket's
etcdirectory that contains
PLTADDONDIRto point to a directory for user-specific Racket configuration, packages, and extensions. It defaults to
PLTUSERHOMEto point to the
homedirectory of the user. It's optional since Pycket will also look at other environment variables to figure out the home directory (e.g.
You can also use the command line options to provide these paths, e.g.
-Getc.. Run it with
-hto see all the commands and options.
$ ./pycket-c-linklets -h
Makefilereacts to some variables:
PYPYPATHfor when your
pypycheckout is not in this directory. Defaults to
PYTESTfor when you don’t want to use
pypy’s version of pytest. Defaults to
RPYTHONfor when you want to use something other than the default
rpythonscript, but you probably would not want that. Defaults to
Now that Pycket has two different modes with options, we run the unit tests on each of those settings using the following targets:
make testto run the unit tests on
make test-new-with-expanderto run the unit tests on
NEW Pycketusing the Racket's
make test-new-no-expanderto run the unit tests on
NEW Pycketwithout using the
NEWPycket, using the expander linklet means that for each test expression string we use the
evalfunctions in that linklet to read and evaluate the test. If we're not using the expander, on the other hand, then we manually create a linklet containing the expression and instantiate it directly (mostly with an empty target) to get the result.
NEWPycket is able to generate and use its own
.zofiles. For now both the generation and the use are manual.
To generate a
.zofile for a
.rktsource file, use
$ make compile-file FILE=$(PLTHOME)/racket/collects/racket/private/qq-and-or.rkt
The parameter that enables Racket expander to use compiled code is
use-compiled-file-paths, which defaults to
pycket-compiledin Pycket. Whenever a module is required, the expander will use the compiled code if it exists, otherwise it will use the source code of the module (read, expand, etc.).
pycket-repl> (#%require racket/private/qq-and-or)
pycket-compiledis a folder that
make compile-fileis going to generate by itself.
Currently we have two
maketargets for working with compiled files:
$ make compile-racket-modules
.zofiles for the predefined list of Racket modules (see
$ make clean-compiled-files
will remove all the
.zofiles under the
pycket-compileddirectories in Racket libraries.
This is a work in progress. We plan to have a make target that compiles all the Racket modules automatically by following the module dependencies (as opposed to using a predefined list of modules with the respective paths).
One of the beautiful perks of bootstrapping Racket is to be able to run on Pykcet some interesting programs implemented in Racket (as long as we have enough runtime support for it in Pycket -- i.e. having the runtime primitives implemented in RPython).
NEWPycket now features an original Racket REPL that's implemented in Racket. Try it out!
You can make it more verbose with the
--verboseflag if you're curious about what's going on in the background.
$ ./pycket-c-linklets --verbose
Also increase the verbosity level (defaults to 0).
$ ./pycket-c-linklets --verbose 1
version mismatch expected: "18.104.22.168" found: "22.214.171.124"
There are a couple of things that might produce this. * It might be the case that your
PLTCOLLECTSenvironment variable is set and pointing to a different place than you think. * If you're using the
pycket-c-linklets), then it might be the case that you have
compiledzo files in your Racket directory, and they need to be recompiled (you
pulleda more recent Racket, maybe?). * The
expander.rktl.linkletmight not be up to date with the latest Racket binary. You can use
make expandertarget to rebuild the expander linklet, or shoot a message on the slack channel to us to update it.
Or even this, when this directory is in your
$ python -mpycket program
You can edit the shell script to make it use pypy, if desired.
You can generate a coverage report with
$ pypy/pytest.py --cov .
$ make coverage
which also generates an HTML report in
pycket/test/coverage_report. You need these Python packages for that to work: *
pytest-cov(provided with the