summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2017-11-13 01:45:18 -0500
committerOwen Jacobson <owen@grimoire.ca>2017-11-13 01:45:21 -0500
commit6ee8b48ce8f2189c23f8bf64bcf93e2210e67d26 (patch)
treeca5f90322a000a56d267130bdfd23f0b873ee2f4
parent5cc96a0fb06fa7d86563f4cb64e5fa9d4f6a09f9 (diff)
A basic expander.
This doesn't support macro expansion, but does support some basic syntax niceties. Macro expansion requires quote and quasiquote support.
-rw-r--r--actinide/__init__.py5
-rw-r--r--actinide/expander.py34
-rw-r--r--primer.py8
3 files changed, 41 insertions, 6 deletions
diff --git a/actinide/__init__.py b/actinide/__init__.py
index afa27d8..0278cd8 100644
--- a/actinide/__init__.py
+++ b/actinide/__init__.py
@@ -1,4 +1,4 @@
-from . import builtin, ports, symbol_table, reader, evaluator, types
+from . import builtin, ports, symbol_table, reader, expander, evaluator, types
class Session(object):
def __init__(self):
@@ -8,7 +8,8 @@ class Session(object):
def read(self, port):
if types.string_p(port):
port = ports.string_to_input_port(port)
- return reader.read(port, self.symbols)
+ form = reader.read(port, self.symbols)
+ return expander.expand(form, self.symbols)
def eval(self, form):
cps = evaluator.eval(form, self.environment, self.symbols, None)
diff --git a/actinide/expander.py b/actinide/expander.py
new file mode 100644
index 0000000..46a98ce
--- /dev/null
+++ b/actinide/expander.py
@@ -0,0 +1,34 @@
+# ## EXPANDER
+
+from .types import *
+
+# Expand and syntax-check a form.
+#
+# This replaces shorthand notations, such as ``(define (a b c) body)``, with
+# their longhand equivalents (``(define a (lambda (b c) body)))``).
+#
+# Because this deals with unevaluated programs, this algorithm can safely
+# recurse: the input depth simply isn't that large.
+def expand(form, symbols):
+ if nil_p(form) or not list_p(form):
+ return form
+ # huge cheat. Working with python lists is hugely easier for this than
+ # working with conses, and it's after midnight. Flatten the form, expand it,
+ # and reconstitute it. This incurs two bonus copies per form: suck it up.
+ symb, *args = flatten(form)
+ if symb == symbols['if'] and len(args) == 2:
+ args.append(None)
+ elif symb == symbols['define']:
+ decl, *body = args
+ if list_p(decl):
+ decl = flatten(decl)
+ name, *formals = decl
+ lambda_ = list(symbols['lambda'], list(*formals), *body)
+ args = [name, lambda_]
+ elif symb == symbols['lambda']:
+ formals, *body = args
+ if len(body) != 1:
+ body = [list(symbols['begin'], *body)]
+ args = [formals, *body]
+ form = list(symb, *[expand(subform, symbols) for subform in args])
+ return form
diff --git a/primer.py b/primer.py
index 2c6d73a..b223550 100644
--- a/primer.py
+++ b/primer.py
@@ -9,10 +9,10 @@ program = session.read("""
1
1.0
"Hello"
- (define a
- (lambda (b) (values 1 2.2 "three" a b)))
- (define pp
- (lambda () (pp)))
+ (define (a b)
+ (values 1 2.2 "three" a b))
+ (define (pp) (pp))
+
(print (a "foo"))
(print (eval (list (symbol "a") "bar")))
(print 0 (values 1 2 3) 4 5)