R5RS scheme interpreter, supporting hygenic macros and full call/cc
Project description
schemepy
========
Implementation of scheme in python supporting call/cc and hygienic macros
Version 1.2 has added support for pre-expanding syntax in the syntax_expand
module. It modifies SimpleProcedure objects in-place. Version 1.2 also
adds a JIT compiler which converts SimpleProcedure objects to native python
functions. It is available in the jit module. Setting scheme.jit.enabled
will give a basic, safe level of JIT activity. Setting scheme.jit.lambdas_enabled
and scheme.jit.unstable_enabled increase the JIT activity, but can interfere
with dynamic programs. The JIT can be manually triggered for individual
functions by calling scheme.jit.makeFunction(proc), which expands proc's
syntax (in-place), then returns a new python function for proc (or the
original procedure if anything went wrong). By default the JIT is chatty,
but can be silenced in scheme.debug.debug_settings. Note that JITed
functions break TCO.
The unstable level sometimes emits bytecode that chokes PYPY, but CPython
has no issues with it.
Using schemepy
---------
There are 3 basic ways to use schemepy. As a stand-alone scheme interpreter:
$ /usr/bin/schemepy <script.scm>
As a stand-alone REPL:
$ /usr/bin/schemepy
schemepy>
Or from inside a python script
import scheme
scheme.repl.repl()
schemepy>
Or to run predefind strings from python
import scheme
scheme.eval.Eval(myString)
#or
scheme.eval.Eval(myFile)
Eval will only execute the first statement in its input, so if you want compound inputs, wrap them with
(begin )
The default environment setup is controlled in builtins.py, with
additional definitions in builtins.scm (scheme/builtins.scm in the
source, /usr/share/schemepy/stdlib/builtins.scm once installed).
Scheme is sandboxed away from python, so only functions provided into
the global Environment (scheme.Globals.Globals) or some other scheme
environment can be accessed. Note that by default, the interpreter is
given access to the file system and other sensitive functions. If you
want to use it as a sandbox for user code, you need to strip out
anything you don't want called. Also, getattr and getitem are
undefined in the default environment. If you are running trusted
code, you can simply add the standard getattr to the global
Environment. If you are running user code, and want to provide
getattr, write one that only allows access to approved data types:
def safegetattr(obj, attr):
if isinstance(obj, some_class):
return getattr(obj, attr)
raise TypeError("getattr only supports objects of type %r" % some_class)
or similar.
Differences from r5rs scheme
---------
Macro expansion is mixed with code execution. Normally macro
expansion would be done at compile time, but
for simplicity each statement is expanded right before execution. By
itself, the only effect this has is on performance.
Macros are first-class objects. Normally, macros and normal
procedures are essentially the same, except that macros' names are
listed in a MacroTable, while procedures are listed in the normal
variable table. I don't maintain a separate list of macros, so an
object being a macro is recorded on the object itself. Normally, this
isn't noticable, as any code which wouldn't generate errors in racket
should produce the same output (This is done by making
define-syntax take either a macro or a procedure and wrap it in a
macro, which calls the wrapped object with the syntax and expects the
return type to be syntax), but it does open the door to some things
which scheme users won't expect.
(define some-macro #f)
(define (somefun)
(define-syntax junk (lambda (x) #'(+ 1 2)))
(set! some-macro junk)
)
(somefun) (some-macro)
;3
Tail recursion and general Tail-Call-Optimisation
---------
Tail recursion is not handled differently from other tail calls; but,
TCO is partially supported. Some calls recursively call process(),
which breaks TCO, but most calls are properly TC optimised.
Booleans
---------
Truth values follow python's convention rather than scheme's (0,
False, None, (), and '' are false, or anything which provides a
__bool__ method which returns False). If you need scheme's behaviour,
simply rewrite eq? and what not to check for identity against False.
========
Implementation of scheme in python supporting call/cc and hygienic macros
Version 1.2 has added support for pre-expanding syntax in the syntax_expand
module. It modifies SimpleProcedure objects in-place. Version 1.2 also
adds a JIT compiler which converts SimpleProcedure objects to native python
functions. It is available in the jit module. Setting scheme.jit.enabled
will give a basic, safe level of JIT activity. Setting scheme.jit.lambdas_enabled
and scheme.jit.unstable_enabled increase the JIT activity, but can interfere
with dynamic programs. The JIT can be manually triggered for individual
functions by calling scheme.jit.makeFunction(proc), which expands proc's
syntax (in-place), then returns a new python function for proc (or the
original procedure if anything went wrong). By default the JIT is chatty,
but can be silenced in scheme.debug.debug_settings. Note that JITed
functions break TCO.
The unstable level sometimes emits bytecode that chokes PYPY, but CPython
has no issues with it.
Using schemepy
---------
There are 3 basic ways to use schemepy. As a stand-alone scheme interpreter:
$ /usr/bin/schemepy <script.scm>
As a stand-alone REPL:
$ /usr/bin/schemepy
schemepy>
Or from inside a python script
import scheme
scheme.repl.repl()
schemepy>
Or to run predefind strings from python
import scheme
scheme.eval.Eval(myString)
#or
scheme.eval.Eval(myFile)
Eval will only execute the first statement in its input, so if you want compound inputs, wrap them with
(begin )
The default environment setup is controlled in builtins.py, with
additional definitions in builtins.scm (scheme/builtins.scm in the
source, /usr/share/schemepy/stdlib/builtins.scm once installed).
Scheme is sandboxed away from python, so only functions provided into
the global Environment (scheme.Globals.Globals) or some other scheme
environment can be accessed. Note that by default, the interpreter is
given access to the file system and other sensitive functions. If you
want to use it as a sandbox for user code, you need to strip out
anything you don't want called. Also, getattr and getitem are
undefined in the default environment. If you are running trusted
code, you can simply add the standard getattr to the global
Environment. If you are running user code, and want to provide
getattr, write one that only allows access to approved data types:
def safegetattr(obj, attr):
if isinstance(obj, some_class):
return getattr(obj, attr)
raise TypeError("getattr only supports objects of type %r" % some_class)
or similar.
Differences from r5rs scheme
---------
Macro expansion is mixed with code execution. Normally macro
expansion would be done at compile time, but
for simplicity each statement is expanded right before execution. By
itself, the only effect this has is on performance.
Macros are first-class objects. Normally, macros and normal
procedures are essentially the same, except that macros' names are
listed in a MacroTable, while procedures are listed in the normal
variable table. I don't maintain a separate list of macros, so an
object being a macro is recorded on the object itself. Normally, this
isn't noticable, as any code which wouldn't generate errors in racket
should produce the same output (This is done by making
define-syntax take either a macro or a procedure and wrap it in a
macro, which calls the wrapped object with the syntax and expects the
return type to be syntax), but it does open the door to some things
which scheme users won't expect.
(define some-macro #f)
(define (somefun)
(define-syntax junk (lambda (x) #'(+ 1 2)))
(set! some-macro junk)
)
(somefun) (some-macro)
;3
Tail recursion and general Tail-Call-Optimisation
---------
Tail recursion is not handled differently from other tail calls; but,
TCO is partially supported. Some calls recursively call process(),
which breaks TCO, but most calls are properly TC optimised.
Booleans
---------
Truth values follow python's convention rather than scheme's (0,
False, None, (), and '' are false, or anything which provides a
__bool__ method which returns False). If you need scheme's behaviour,
simply rewrite eq? and what not to check for identity against False.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
SchemePy-1.2.0.tar.gz
(200.5 kB
view details)
Built Distributions
SchemePy-1.2.0.linux-x86_64.tar.gz
(211.2 kB
view details)
SchemePy-1.2.0-py2-none-any.whl
(40.8 kB
view details)
File details
Details for the file SchemePy-1.2.0.tar.gz
.
File metadata
- Download URL: SchemePy-1.2.0.tar.gz
- Upload date:
- Size: 200.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7465a78d5b910e0326cc310de4e517a980eaa27533ad99945640b0cca20b4d65 |
|
MD5 | c504e8cb100497e8f2d4c60008289d0f |
|
BLAKE2b-256 | ff85f9fd7ac0aaaae9cd3d38fd75e8782621199da2b04acf36d7ee3e9a8d8f84 |
File details
Details for the file SchemePy-1.2.0.linux-x86_64.tar.gz
.
File metadata
- Download URL: SchemePy-1.2.0.linux-x86_64.tar.gz
- Upload date:
- Size: 211.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0768f337590f8f9d46af16acb540d0c005e15c72033f5b5e8ebc1ba62f73aa0a |
|
MD5 | f4f12ee4b2a35fb9472cff553a8d2250 |
|
BLAKE2b-256 | 7462748e387e067f841eb47511a9cd01e81bc486893dfb03228f24e05156c9f1 |
File details
Details for the file SchemePy-1.2.0-py2-none-any.whl
.
File metadata
- Download URL: SchemePy-1.2.0-py2-none-any.whl
- Upload date:
- Size: 40.8 kB
- Tags: Python 2
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | eea2705b4075d27ce12166ad6c6b69d21962356c8f34a73b88e872be26f7c271 |
|
MD5 | ca2122aa5231a41323c98ef1d901d936 |
|
BLAKE2b-256 | 3ffb7b0b2db5668623c0f7f7749b250c26e67292a929ced83ae4a2b89749441b |