summaryrefslogtreecommitdiff
path: root/tests/forms.py
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2017-11-13 01:00:33 -0500
committerOwen Jacobson <owen@grimoire.ca>2017-11-13 01:01:34 -0500
commit5cc96a0fb06fa7d86563f4cb64e5fa9d4f6a09f9 (patch)
tree40052668ffe030452b45e3a2d6be8d8fc24acdee /tests/forms.py
parent6a635660bd7b47238642d4a552782687352555ac (diff)
Big-ass coding binge presents: a Lisp.
This implements a continuation-passing interpreter, which means we get tail calls ferfree. I stopped short of implementing call/cc, because I don't think we need it, but we can get there if we have to.
Diffstat (limited to 'tests/forms.py')
-rw-r--r--tests/forms.py76
1 files changed, 76 insertions, 0 deletions
diff --git a/tests/forms.py b/tests/forms.py
new file mode 100644
index 0000000..1a49636
--- /dev/null
+++ b/tests/forms.py
@@ -0,0 +1,76 @@
+from hypothesis.strategies import integers, decimals as hypo_decimals, booleans, characters, text, tuples, lists as hypo_lists, just, one_of
+from hypothesis.strategies import deferred, recursive
+
+from actinide.symbol_table import *
+from actinide.types import *
+
+# Generators for forms. Where these generators create a symbol, they will use
+# the global symbol table defined in this module. Do not do this in your own
+# code! A global symbol table is a memory leak, and only the fact that tests
+# exit before they can do any substantial damage prevents this from being a
+# bigger problem.
+#
+# Each generator produces the parsed version of a form.
+
+symbol_table = SymbolTable()
+
+# Generates nil.
+def nils():
+ return just(None)
+
+# Generates integers.
+def ints():
+ return integers()
+
+# Generates language decimals.
+def decimals():
+ return hypo_decimals(allow_nan=False, allow_infinity=False)
+
+# Generates booleans.
+def bools():
+ return booleans()
+
+# Generates strings.
+def strings():
+ return text()
+
+# Generates any character legal in a symbol, which cannot be part of some other
+# kind of atom.
+def symbol_characters():
+ return characters(blacklist_characters='01234567890#. \t\n();"')
+
+# Generates symbols guaranteed not to conflict with other kinds of literal. This
+# is a subset of the legal symbols.
+def symbols():
+ return text(symbol_characters(), min_size=1)\
+ .map(lambda item: symbol_table[item])
+
+# Generates atoms.
+def atoms():
+ return one_of(
+ nils(),
+ ints(),
+ decimals(),
+ bools(),
+ strings(),
+ symbols(),
+ )
+
+# Generates arbitrary conses, with varying depth. This may happen to generate
+# lists by accident.
+def conses():
+ return recursive(
+ tuples(atoms(), atoms()).map(lambda elems: cons(*elems)),
+ lambda children: tuples(children | atoms(), children | atoms()).map(lambda elems: cons(*elems)),
+ )
+
+# Generates lists, with varying depth.
+def lists():
+ return recursive(
+ hypo_lists(atoms()).map(lambda elems: list(*elems)),
+ lambda children: hypo_lists(children | atoms()).map(lambda elems: list(*elems)),
+ )
+
+# Generates random forms.
+def forms():
+ return one_of(nils(), ints(), bools(), strings(), symbols(), conses(), lists())