diff options
Diffstat (limited to 'README.rst')
| -rw-r--r-- | README.rst | 168 |
1 files changed, 163 insertions, 5 deletions
@@ -66,13 +66,11 @@ Or, if you prefer, add ``actinide`` to your application's ``Pipfile`` or Freestanding REPL ***************** -**Note: this section is presently incorrect - the ``actinide-repl`` command -doesn't exist.** - The Actinide interpreter can be started interactively using the ``actinide-repl`` command. In this mode, Actinide forms can be entered interactively. The REPL will immediately evaluate each top-level form, then -print the result of that evaluation. +print the result of that evaluation. The environment is persisted from form to +form, to allow interactive definitions. To exit the REPL, type an end-of-file (Ctrl-D on most OSes, Ctrl-Z on Windows). @@ -80,7 +78,167 @@ To exit the REPL, type an end-of-file (Ctrl-D on most OSes, Ctrl-Z on Windows). Embedding Actinide ****************** -WIP +Actinide is designed to be embedded into larger Python programs. It's possible +to call into Actinide, either by providing code to be evaluated, or by +obtaining builtin functions and procedures from Actinide and invoking them. + +The ``Session`` class is the basic building block of an Actinide integration. +Creating a session creates a number of resources associated with Actinide +evaluation: a symbol table for interning symbols, and an initial top-level +environment to evaluate code in, pre-populated with the Actinide standard +library. + +Executing Actinide programs in a session consists of two steps: reading the +program in from a string or an input port, and evaluating the resulting forms. +The following example illustrates a simple infinite loop: + +:: + + import actinide + + session = actinide.Session() + program = session.read(''' + (begin + ; define the factorial function + (define (factorial n) + (fact n 1)) + + ; define a tail-recursive factorial function + (define (fact n a) + (if (= n 1) + a + (fact (- n 1) (* n a)))) + + ; call them both + (factorial 100)) + ''') + + # Compute the factorial of 100 + result = session.eval(program) + +As a shorthand for this common sequence of operations, the Session exposes a +``run`` method: + +:: + + print(*session.run('(factorial 5)')) # prints "120" + +Callers can inject variables, including new builtin functions, into the initial +environment using the ``bind``, ``bind_void``, ``bind_fn``, and +``bind_builtin`` methods of the session. + +To bind a simple value, or to manually bind a wrapped builtin, call +``session.bind``: + +:: + + session.bind('var', 5) + print(*session.run('var')) # prints "5" + +To bind a function whose return value should be ignored, call ``bind_void``. +This will automatically determine the name to bind the function to: + +:: + + session.bind_void(print) + session.run('(print "Hello, world!")') # prints "Hello, world!" using Python's print fn + +To bind a function returning one value (most functions), call ``bind_fn``. This +will automatically determine the name to bind to: + +:: + + def example(): + return 5 + + session.bind_fn(example) + print(*session.run('(example)')) # prints "5" + +Finally, to bind a function returning a tuple of results, call +``bind_builtin``. This will automatically determine the name to bind to: + +:: + + def pair(): + return 1, 2 + + session.bind_builtin(pair) + print(*session.run('(pair)')) # prints "1 2" + +Actinide functions can return zero, one, or multiple values. As a result, the +``result`` returned by ``session.eval`` is a tuple, with one value per result. + +Actinide can bind Python functions, as well as bound and unbound methods, and +nearly any other kind of callable. Under the hood, Actinide uses a thin adapter +layer to map Python return values to Actinide value lists. The ``bind_void`` +helper ultimately calls that module's ``wrap_void`` to wrap the function, and +``bind_fn`` calls ``wrap_fn``. (Tuple-returning functions do not need to be +wrapped.) If you prefer to manually bind functions using ``bind``, they must be +wrapped appropriately. + +Finally Actinide can bind specially-crafted Python modules. If a module +contains top-level symbols named ``ACTINIDE_BINDINGS``, ``ACTINIDE_VOIDS``, +``ACTINIDE_FNS``, and/or ``ACTINIDE_BUILTINS``, it can be passed to the +session's ``bind_module`` method. The semantics of each symbol are as follows: + +* ``ACTINIDE_BINDINGS`` is a list of name, value pairs. Each binding binding + will be installed verbatim, without any function mangling, as if by + ``session.bind``. + +* ``ACTINIDE_VOIDS``, ``ACTINIDE_FNS``, and ``ACTINIDE_BUILTINS`` are lists of + function objects. Each will be bound as if by the corresponding + ``session.bind_void``, ``session.bind_fn``, or ``session.bind_builtin`` + method. + +The ``actinide.builtin`` module contains a helper function, ``make_registry``, +which can simplify construction of these fields: + +:: + + from actinide.builtin import make_registry + ACTINIDE_BINDINGS, ACTINIDE_VOIDS, ACTINIDE_FNS, ACTINIDE_BUILTINS, bind, void, fn, builtin = make_registry() + + five = bind('five', 5) + + @void + def python_print(*args): + print(*args) + + @fn + def bitwise_and(a, b): + return a & b + + @builtin + def two_values(): + return 1, "Two" + +Going the other direction, values can be extracted from bindings in the session +using the ``get`` method: + +:: + + session.run('(define x 8)') + print(session.get('x')) # prints "8" + +If the extracted value is a built-in function or an Actinide procedure, it can +be invoked like a Python function. However, much like ``eval`` and ``run``, +Actinide functions returne a tuple of results rather than a single value: + +:: + + session.run(''' + ; Set a variable + (define x 5) + + ; Define a function that reads the variable + (define (get-x) x) + ''') + + get_x = session.get('get-x') + print(*get_x()) # prints "5" + +This two-way binding mechanism makes it straightforward to define interfaces +between Actinide and the target domain. ********************************* The Actinide Programming Language |
