summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2017-11-18 18:29:11 -0500
committerOwen Jacobson <owen@grimoire.ca>2017-11-18 18:29:11 -0500
commita6dcdcad4fd8407cbd67fb1c2d293f0d3be88914 (patch)
treebdacc6cc67b9b454405434f829bfd9992ecd7419
parent8d289bffdd2bf2062a6677530c4a4226175d87b2 (diff)
Macro-binding glue, and a let macro
-rw-r--r--README.rst8
-rw-r--r--actinide/__init__.py22
-rw-r--r--actinide/builtin.py17
-rw-r--r--actinide/expander.py2
-rw-r--r--actinide/stdlib.py15
5 files changed, 60 insertions, 4 deletions
diff --git a/README.rst b/README.rst
index 2b1bfcc..0301b24 100644
--- a/README.rst
+++ b/README.rst
@@ -174,7 +174,10 @@ 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.
+wrapped appropriately. An equivalent set of methods, ``macro_bind``,
+``macro_bind_void``, ``macro_bind_fn``, and ``macro_bind_builtin`` bind values
+to entries in the top-level macro table, instead of the top-level environment,
+and allow extension of the language's syntax.
Finally, Actinide can bind specially-crafted Python modules. If a module
contains a top-level symbol named ``An`` (for the informal chemical symbol for
@@ -201,6 +204,9 @@ The symbol must be bound to an instance of the ``Registry`` class from the
def two_values():
return 1, "Two"
+ # @An.macro_bind, @An.macro_void, @An.macro_fn, and @An.macro_builtin follow
+ # the same pattern.
+
Going the other direction, values can be extracted from bindings in the session
using the ``get`` method:
diff --git a/actinide/__init__.py b/actinide/__init__.py
index 90ccbfb..cfcb921 100644
--- a/actinide/__init__.py
+++ b/actinide/__init__.py
@@ -37,16 +37,31 @@ class BaseSession(object):
def bind_builtin(self, fn):
name = builtin.lisp_name(fn)
- if name is None:
- raise ValueError("Anonymous functions must be bound using `bind`")
symb = self.symbol(name)
self.bind(symb, fn)
return symb
+ def macro_bind(self, symb, value):
+ self.macros[self.symbol(symb)] = value
+
+ def macro_bind_void(self, fn):
+ return self.macro_bind_builtin(builtin.wrap_void(fn))
+
+ def macro_bind_fn(self, fn):
+ return self.macro_bind_builtin(builtin.wrap_fn(fn))
+
+ def macro_bind_builtin(self, fn):
+ name = builtin.lisp_name(fn)
+ symb = self.symbol(name)
+ self.macro_bind(symb, fn)
+ return symb
+
def bind_module(self, module):
registry = module.An
for name, binding in registry.bindings:
self.bind(name, binding)
+ for name, binding in registry.macros:
+ self.macro_bind(name, binding)
def get(self, symb):
symb = self.symbol(symb)
@@ -80,5 +95,8 @@ class BaseSession(object):
class Session(BaseSession):
def standard_library(self):
+ @self.macro_bind_fn
+ def let(bindings, *body):
+ return stdlib.let(self.symbols, bindings, *body)
self.bind_module(stdlib)
self.bind_module(ports)
diff --git a/actinide/builtin.py b/actinide/builtin.py
index 485fa96..e8132af 100644
--- a/actinide/builtin.py
+++ b/actinide/builtin.py
@@ -78,6 +78,7 @@ def wrap_fn(fn):
class Registry(object):
def __init__(self):
self.bindings = []
+ self.macros = []
def bind(self, name, value):
self.bindings.append((name, value))
@@ -94,3 +95,19 @@ class Registry(object):
def builtin(self, f):
self.bind(lisp_name(f), f)
return f
+
+ def macro_bind(self, name, value):
+ self.macros.append((name, value))
+ return value
+
+ def macro_void(self, f):
+ self.macro_bind(lisp_name(f), wrap_void(f))
+ return f
+
+ def macro_fn(self, f):
+ self.macro_bind(lisp_name(f), wrap_fn(f))
+ return f
+
+ def macro_builtin(self, f):
+ self.macro_bind(lisp_name(f), f)
+ return f
diff --git a/actinide/expander.py b/actinide/expander.py
index 9518854..432f698 100644
--- a/actinide/expander.py
+++ b/actinide/expander.py
@@ -36,7 +36,7 @@ def expand_subforms(list, symbols, macros):
if nil_p(list):
return nil
if not cons_p(list):
- return expand_subforms(list, symbols, macros)
+ return list
head, tail = uncons(list)
return cons(
expand(head, symbols, macros),
diff --git a/actinide/stdlib.py b/actinide/stdlib.py
index 8abf71c..6edaf78 100644
--- a/actinide/stdlib.py
+++ b/actinide/stdlib.py
@@ -66,3 +66,18 @@ def and_(a, b):
@An.fn
def or_(a, b):
return op.or_(a, b)
+
+def let(symbols, bindings, *body):
+ if nil_p(bindings):
+ return list(*body)
+
+ binding, bindings = uncons(bindings)
+ name, value = flatten(binding)
+
+ return list(
+ append(
+ list(symbols['lambda'], list(name)),
+ let(symbols, bindings, *body),
+ ),
+ value
+ )