added for and with macros

Fixed up anaphoric.hy and added the tests too native_tests/__init__.py

added __init__.hy
This commit is contained in:
Foxboron 2013-11-10 19:00:01 +01:00
parent f189f0a457
commit d82636958b
11 changed files with 160 additions and 105 deletions

View File

@ -441,50 +441,34 @@ first / car
for for
---
`for` macro is used to build nested `foreach` loops. The macro takes two
parameters, first being a vector specifying collections to iterate over and
variables to bind. The second parameter is a statement which is executed during
each loop:
.. code-block:: clj
(for [x iter y iter] stmt)
(foreach [x iter]
(foreach [y iter] stmt))
foreach
------- -------
`foreach` is used to call a function for each element in a list or vector. `for` is used to call a function for each element in a list or vector.
Results are discarded and None is returned instead. Example code iterates over Results are discarded and None is returned instead. Example code iterates over
collection and calls side-effect to each element in the collection: collection and calls side-effect to each element in the collection:
.. code-block:: clj .. code-block:: clj
;; assuming that (side-effect) is a function that takes a single parameter ;; assuming that (side-effect) is a function that takes a single parameter
(foreach [element collection] (side-effect element)) (for [[element collection]] (side-effect element))
;; foreach can have an optional else block ;; for can have an optional else block
(foreach [element collection] (side-effect element) (for [[element collection]] (side-effect element)
(else (side-effect-2))) (else (side-effect-2)))
The optional `else` block is executed only if the `foreach` loop terminates The optional `else` block is executed only if the `for` loop terminates
normally. If the execution is halted with `break`, the `else` does not execute. normally. If the execution is halted with `break`, the `else` does not execute.
.. code-block:: clj .. code-block:: clj
=> (foreach [element [1 2 3]] (if (< element 3) => (for [[element [1 2 3]]] (if (< element 3)
... (print element) ... (print element)
... (break)) ... (break))
... (else (print "loop finished"))) ... (else (print "loop finished")))
1 1
2 2
=> (foreach [element [1 2 3]] (if (< element 4) => (for [[element [1 2 3]]] (if (< element 4)
... (print element) ... (print element)
... (break)) ... (break))
... (else (print "loop finished"))) ... (else (print "loop finished")))
@ -635,7 +619,7 @@ function is defined and passed to another function for filtering output.
... {:name "Dave" :age 5}]) ... {:name "Dave" :age 5}])
=> (defn display-people [people filter] => (defn display-people [people filter]
... (foreach [person people] (if (filter person) (print (:name person))))) ... (for [[person people]] (if (filter person) (print (:name person)))))
=> (display-people people (fn [person] (< (:age person) 25))) => (display-people people (fn [person] (< (:age person) 25)))
Alice Alice
@ -949,16 +933,18 @@ context to an argument or ignore it completely, as shown below:
.. code-block:: clj .. code-block:: clj
(with [arg (expr)] block) (with [[arg (expr)]] block)
(with [(expr)] block) (with [[(expr)]] block)
(with [[arg (expr)] [(expr)]] block)
The following example will open file `NEWS` and print its content on screen. The The following example will open file `NEWS` and print its content on screen. The
file is automatically closed after it has been processed. file is automatically closed after it has been processed.
.. code-block:: clj .. code-block:: clj
(with [f (open "NEWS")] (print (.read f))) (with [[f (open "NEWS")]] (print (.read f)))
with-decorator with-decorator
@ -996,7 +982,7 @@ infinite series without consuming infinite amount of memory.
.. code-block:: clj .. code-block:: clj
=> (defn multiply [bases coefficients] => (defn multiply [bases coefficients]
... (foreach [(, base coefficient) (zip bases coefficients)] ... (for [[(, base coefficient) (zip bases coefficients)]]
... (yield (* base coefficient)))) ... (yield (* base coefficient))))
=> (multiply (range 5) (range 5)) => (multiply (range 5) (range 5))

View File

@ -1177,14 +1177,18 @@ class HyASTCompiler(object):
fn.stmts[-1].decorator_list = decorators fn.stmts[-1].decorator_list = decorators
return ret + fn return ret + fn
@builds("with") @builds("with*")
@checkargs(min=2) @checkargs(min=2)
def compile_with_expression(self, expr): def compile_with_expression(self, expr):
expr.pop(0) # with expr.pop(0) # with*
args = expr.pop(0) args = expr.pop(0)
if len(args) > 2 or len(args) < 1: if not isinstance(args, HyList):
raise HyTypeError(expr, "with needs [arg (expr)] or [(expr)]") raise HyTypeError(expr,
"with expects a list, received `{0}'".format(
type(args).__name__))
if len(args) < 1:
raise HyTypeError(expr, "with needs [[arg (expr)]] or [[(expr)]]]")
args.reverse() args.reverse()
ctx = self.compile(args.pop(0)) ctx = self.compile(args.pop(0))
@ -1623,23 +1627,36 @@ class HyASTCompiler(object):
result += ld_name result += ld_name
return result return result
@builds("foreach") @builds("for*")
@checkargs(min=1) @checkargs(min=1)
def compile_for_expression(self, expression): def compile_for_expression(self, expression):
expression.pop(0) # for expression.pop(0) # for
target_name, iterable = expression.pop(0)
args = expression.pop(0)
if not isinstance(args, HyList):
raise HyTypeError(expression,
"for expects a list, received `{0}'".format(
type(args).__name__))
try:
target_name, iterable = args
except ValueError:
raise HyTypeError(expression,
"for requires two forms in the list")
target = self._storeize(self.compile(target_name)) target = self._storeize(self.compile(target_name))
ret = Result() ret = Result()
orel = Result() orel = Result()
# (foreach [] body (else …)) # (for* [] body (else …))
if expression and expression[-1][0] == HySymbol("else"): if expression and expression[-1][0] == HySymbol("else"):
else_expr = expression.pop() else_expr = expression.pop()
if len(else_expr) > 2: if len(else_expr) > 2:
raise HyTypeError( raise HyTypeError(
else_expr, else_expr,
"`else' statement in `foreach' is too long") "`else' statement in `for' is too long")
elif len(else_expr) == 2: elif len(else_expr) == 2:
orel += self.compile(else_expr[1]) orel += self.compile(else_expr[1])
orel += orel.expr_as_stmt() orel += orel.expr_as_stmt()

View File

@ -31,14 +31,14 @@
(defmacro ap-each [lst &rest body] (defmacro ap-each [lst &rest body]
"Evaluate the body form for each element in the list." "Evaluate the body form for each element in the list."
`(foreach [it ~lst] ~@body)) `(for [[it ~lst]] ~@body))
(defmacro ap-each-while [lst form &rest body] (defmacro ap-each-while [lst form &rest body]
"Evalutate the body form for each element in the list while the "Evalutate the body form for each element in the list while the
predicate form evaluates to True." predicate form evaluates to True."
`(let [[p (lambda [it] ~form)]] `(let [[p (lambda [it] ~form)]]
(foreach [it ~lst] (for [[it ~lst]]
(if (p it) (if (p it)
~@body ~@body
(break))))) (break)))))
@ -47,7 +47,7 @@
(defmacro ap-map [form lst] (defmacro ap-map [form lst]
"Yield elements evaluated in the form for each element in the list." "Yield elements evaluated in the form for each element in the list."
`(let [[f (lambda [it] ~form)]] `(let [[f (lambda [it] ~form)]]
(foreach [v ~lst] (for [[v ~lst]]
(yield (f v))))) (yield (f v)))))
@ -55,7 +55,7 @@
"Yield elements evaluated for each element in the list when the "Yield elements evaluated for each element in the list when the
predicate function returns True." predicate function returns True."
`(let [[f (lambda [it] ~rep)]] `(let [[f (lambda [it] ~rep)]]
(foreach [it ~lst] (for [[it ~lst]]
(if (~predfn it) (if (~predfn it)
(yield (f it)) (yield (f it))
(yield it))))) (yield it)))))
@ -64,7 +64,7 @@
(defmacro ap-filter [form lst] (defmacro ap-filter [form lst]
"Yield elements returned when the predicate form evaluates to True." "Yield elements returned when the predicate form evaluates to True."
`(let [[pred (lambda [it] ~form)]] `(let [[pred (lambda [it] ~form)]]
(foreach [val ~lst] (for [[val ~lst]]
(if (pred val) (if (pred val)
(yield val))))) (yield val)))))

View File

@ -34,7 +34,7 @@
(defmacro defmacro-alias [names lambda-list &rest body] (defmacro defmacro-alias [names lambda-list &rest body]
"define one macro with several names" "define one macro with several names"
(setv ret `(do)) (setv ret `(do))
(foreach [name names] (for* [name names]
(.append ret (.append ret
`(defmacro ~name ~lambda-list ~@body))) `(defmacro ~name ~lambda-list ~@body)))
ret) ret)
@ -52,7 +52,7 @@
(setv macroed_variables []) (setv macroed_variables [])
(if (not (isinstance variables HyList)) (if (not (isinstance variables HyList))
(macro-error variables "let lexical context must be a list")) (macro-error variables "let lexical context must be a list"))
(foreach [variable variables] (for* [variable variables]
(if (isinstance variable HyList) (if (isinstance variable HyList)
(do (do
(if (!= (len variable) 2) (if (!= (len variable) 2)

View File

@ -33,11 +33,11 @@
(defn cycle [coll] (defn cycle [coll]
"Yield an infinite repetition of the items in coll" "Yield an infinite repetition of the items in coll"
(setv seen []) (setv seen [])
(foreach [x coll] (for* [x coll]
(yield x) (yield x)
(.append seen x)) (.append seen x))
(while seen (while seen
(foreach [x seen] (for* [x seen]
(yield x)))) (yield x))))
(defn dec [n] (defn dec [n]
@ -49,7 +49,7 @@
"Return a generator from the original collection with duplicates "Return a generator from the original collection with duplicates
removed" removed"
(let [[seen []] [citer (iter coll)]] (let [[seen []] [citer (iter coll)]]
(foreach [val citer] (for* [val citer]
(if (not_in val seen) (if (not_in val seen)
(do (do
(yield val) (yield val)
@ -58,7 +58,7 @@
(defn drop [count coll] (defn drop [count coll]
"Drop `count` elements from `coll` and yield back the rest" "Drop `count` elements from `coll` and yield back the rest"
(let [[citer (iter coll)]] (let [[citer (iter coll)]]
(try (foreach [i (range count)] (try (for* [i (range count)]
(next citer)) (next citer))
(catch [StopIteration])) (catch [StopIteration]))
citer)) citer))
@ -66,10 +66,10 @@
(defn drop-while [pred coll] (defn drop-while [pred coll]
"Drop all elements of `coll` until `pred` is False" "Drop all elements of `coll` until `pred` is False"
(let [[citer (iter coll)]] (let [[citer (iter coll)]]
(foreach [val citer] (for* [val citer]
(if (not (pred val)) (if (not (pred val))
(do (yield val) (break)))) (do (yield val) (break))))
(foreach [val citer] (for* [val citer]
(yield val)))) (yield val))))
(defn empty? [coll] (defn empty? [coll]
@ -84,7 +84,7 @@
(defn filter [pred coll] (defn filter [pred coll]
"Return all elements from `coll` that pass `pred`" "Return all elements from `coll` that pass `pred`"
(let [[citer (iter coll)]] (let [[citer (iter coll)]]
(foreach [val citer] (for* [val citer]
(if (pred val) (if (pred val)
(yield val))))) (yield val)))))
@ -96,7 +96,7 @@
(defn _flatten [coll result] (defn _flatten [coll result]
(if (and (iterable? coll) (not (string? coll))) (if (and (iterable? coll) (not (string? coll)))
(do (foreach [b coll] (do (for* [b coll]
(_flatten b result))) (_flatten b result)))
(.append result coll)) (.append result coll))
result) result)
@ -187,7 +187,7 @@
(defn remove [pred coll] (defn remove [pred coll]
"Return coll with elements removed that pass `pred`" "Return coll with elements removed that pass `pred`"
(let [[citer (iter coll)]] (let [[citer (iter coll)]]
(foreach [val citer] (for* [val citer]
(if (not (pred val)) (if (not (pred val))
(yield val))))) (yield val)))))
@ -195,7 +195,7 @@
"Yield x forever or optionally n times" "Yield x forever or optionally n times"
(if (none? n) (if (none? n)
(setv dispatch (fn [] (while true (yield x)))) (setv dispatch (fn [] (while true (yield x))))
(setv dispatch (fn [] (foreach [_ (range n)] (yield x))))) (setv dispatch (fn [] (for* [_ (range n)] (yield x)))))
(dispatch)) (dispatch))
(defn repeatedly [func] (defn repeatedly [func]
@ -223,7 +223,7 @@
"Take `count` elements from `coll`, or the whole set if the total "Take `count` elements from `coll`, or the whole set if the total
number of entries in `coll` is less than `count`." number of entries in `coll` is less than `count`."
(let [[citer (iter coll)]] (let [[citer (iter coll)]]
(foreach [_ (range count)] (for* [_ (range count)]
(yield (next citer))))) (yield (next citer)))))
(defn take-nth [n coll] (defn take-nth [n coll]
@ -231,16 +231,16 @@
raises ValueError for (not (pos? n))" raises ValueError for (not (pos? n))"
(if (pos? n) (if (pos? n)
(let [[citer (iter coll)] [skip (dec n)]] (let [[citer (iter coll)] [skip (dec n)]]
(foreach [val citer] (for* [val citer]
(yield val) (yield val)
(foreach [_ (range skip)] (for* [_ (range skip)]
(next citer)))) (next citer))))
(raise (ValueError "n must be positive")))) (raise (ValueError "n must be positive"))))
(defn take-while [pred coll] (defn take-while [pred coll]
"Take all elements while `pred` is true" "Take all elements while `pred` is true"
(let [[citer (iter coll)]] (let [[citer (iter coll)]]
(foreach [val citer] (for* [val citer]
(if (pred val) (if (pred val)
(yield val) (yield val)
(break))))) (break)))))

View File

@ -26,17 +26,24 @@
;;; They are automatically required in every module, except inside hy.core ;;; They are automatically required in every module, except inside hy.core
(defmacro for [args &rest body] (defmacro for [args &rest body]
"shorthand for nested foreach loops: "shorthand for nested for loops:
(for [x foo y bar] baz) -> (for [[x foo] [y bar]] baz) ->
(foreach [x foo] (for* [x foo]
(foreach [y bar] (for* [y bar]
baz))" baz))"
;; TODO: that signature sucks.
;; (for [[x foo] [y bar]] baz) would be more consistent
(if (% (len args) 2)
(macro-error args "for needs an even number of elements in its first argument"))
(if args (if args
`(foreach [~(.pop args 0) ~(.pop args 0)] (for ~args ~@body)) `(for* ~(.pop args 0) (for ~args ~@body))
`(do ~@body)))
(defmacro with [args &rest body]
"shorthand for nested for* loops:
(with [[x foo] [y bar]] baz) ->
(with* [x foo]
(with* [y bar]
baz))"
(if args
`(with* ~(.pop args 0) (with ~args ~@body))
`(do ~@body))) `(do ~@body)))
@ -71,7 +78,7 @@
(setv root (check-branch branch)) (setv root (check-branch branch))
(setv latest-branch root) (setv latest-branch root)
(foreach [branch branches] (for* [branch branches]
(setv cur-branch (check-branch branch)) (setv cur-branch (check-branch branch))
(.append latest-branch cur-branch) (.append latest-branch cur-branch)
(setv latest-branch cur-branch)) (setv latest-branch cur-branch))
@ -81,7 +88,7 @@
(defmacro -> [head &rest rest] (defmacro -> [head &rest rest]
;; TODO: fix the docstring by someone who understands this ;; TODO: fix the docstring by someone who understands this
(setv ret head) (setv ret head)
(foreach [node rest] (for* [node rest]
(if (not (isinstance node HyExpression)) (if (not (isinstance node HyExpression))
(setv node `(~node))) (setv node `(~node)))
(.insert node 1 ret) (.insert node 1 ret)
@ -92,7 +99,7 @@
(defmacro ->> [head &rest rest] (defmacro ->> [head &rest rest]
;; TODO: fix the docstring by someone who understands this ;; TODO: fix the docstring by someone who understands this
(setv ret head) (setv ret head)
(foreach [node rest] (for* [node rest]
(if (not (isinstance node HyExpression)) (if (not (isinstance node HyExpression))
(setv node `(~node))) (setv node `(~node)))
(.append node ret) (.append node ret)
@ -113,7 +120,7 @@
(defmacro yield-from [iterable] (defmacro yield-from [iterable]
"Yield all the items from iterable" "Yield all the items from iterable"
(let [[x (gensym)]] (let [[x (gensym)]]
`(foreach [~x ~iterable] `(for* [~x ~iterable]
(yield ~x)))) (yield ~x))))
(defmacro with-gensyms [args &rest body] (defmacro with-gensyms [args &rest body]

View File

@ -12,4 +12,5 @@ from .native_tests.when import * # noqa
from .native_tests.with_decorator import * # noqa from .native_tests.with_decorator import * # noqa
from .native_tests.core import * # noqa from .native_tests.core import * # noqa
from .native_tests.reader_macros import * # noqa from .native_tests.reader_macros import * # noqa
from .native_tests.with_test import * # noqa
from .native_tests.contrib.anaphoric import * # noqa from .native_tests.contrib.anaphoric import * # noqa

View File

@ -295,9 +295,9 @@ def test_ast_bad_assoc():
def test_ast_bad_with(): def test_ast_bad_with():
"Make sure AST can't compile invalid with" "Make sure AST can't compile invalid with"
cant_compile("(with)") cant_compile("(with*)")
cant_compile("(with [])") cant_compile("(with* [])")
cant_compile("(with [] (pass))") cant_compile("(with* [] (pass))")
def test_ast_valid_while(): def test_ast_valid_while():
@ -305,14 +305,14 @@ def test_ast_valid_while():
can_compile("(while foo bar)") can_compile("(while foo bar)")
def test_ast_valid_foreach(): def test_ast_valid_for():
"Make sure AST can compile valid foreach" "Make sure AST can compile valid for"
can_compile("(foreach [a 2])") can_compile("(for [[a 2]])")
def test_ast_invalid_foreach(): def test_ast_invalid_for():
"Make sure AST can't compile invalid foreach" "Make sure AST can't compile invalid for"
cant_compile("(foreach [a 1] (else 1 2))") cant_compile("(for* [a 1] (else 1 2))")
def test_ast_expression_basics(): def test_ast_expression_basics():

View File

@ -31,12 +31,12 @@
(defn test-for-loop [] (defn test-for-loop []
"NATIVE: test for loops?" "NATIVE: test for loops?"
(setv count 0) (setv count 0)
(for [x [1 2 3 4 5]] (for [[x [1 2 3 4 5]]]
(setv count (+ count x))) (setv count (+ count x)))
(assert (= count 15)) (assert (= count 15))
(setv count 0) (setv count 0)
(for [x [1 2 3 4 5] (for [[x [1 2 3 4 5]]
y [1 2 3 4 5]] [y [1 2 3 4 5]]]
(setv count (+ count x y))) (setv count (+ count x y)))
(assert (= count 150))) (assert (= count 150)))
@ -394,9 +394,9 @@
(defn test-yield [] (defn test-yield []
"NATIVE: test yielding" "NATIVE: test yielding"
(defn gen [] (for [x [1 2 3 4]] (yield x))) (defn gen [] (for [[x [1 2 3 4]]] (yield x)))
(setv ret 0) (setv ret 0)
(for [y (gen)] (setv ret (+ ret y))) (for [[y (gen)]] (setv ret (+ ret y)))
(assert (= ret 10))) (assert (= ret 10)))
@ -439,37 +439,37 @@
(defn test-context [] (defn test-context []
"NATIVE: test with" "NATIVE: test with"
(with [fd (open "README.md" "r")] (assert fd)) (with [[fd (open "README.md" "r")]] (assert fd))
(with [(open "README.md" "r")] (do))) (with [[(open "README.md" "r")]] (do)))
(defn test-with-return [] (defn test-with-return []
"NATIVE: test that with returns stuff" "NATIVE: test that with returns stuff"
(defn read-file [filename] (defn read-file [filename]
(with [fd (open filename "r")] (.read fd))) (with [[fd (open filename "r")]] (.read fd)))
(assert (!= 0 (len (read-file "README.md"))))) (assert (!= 0 (len (read-file "README.md")))))
(defn test-for-doodle [] (defn test-for-doodle []
"NATIVE: test for-do" "NATIVE: test for-do"
(do (do (do (do (do (do (do (do (do (setv (, x y) (, 0 0))))))))))) (do (do (do (do (do (do (do (do (do (setv (, x y) (, 0 0)))))))))))
(foreach [- [1 2]] (for [[- [1 2]]]
(do (do
(setv x (+ x 1)) (setv x (+ x 1))
(setv y (+ y 1)))) (setv y (+ y 1))))
(assert (= y x 2))) (assert (= y x 2)))
(defn test-foreach-else [] (defn test-for-else []
"NATIVE: test foreach else" "NATIVE: test for else"
(let [[x 0]] (let [[x 0]]
(foreach [a [1 2]] (for* [a [1 2]]
(setv x (+ x a)) (setv x (+ x a))
(else (setv x (+ x 50)))) (else (setv x (+ x 50))))
(assert (= x 53))) (assert (= x 53)))
(let [[x 0]] (let [[x 0]]
(foreach [a [1 2]] (for* [a [1 2]]
(setv x (+ x a)) (setv x (+ x a))
(else)) (else))
(assert (= x 3)))) (assert (= x 3))))
@ -636,7 +636,7 @@
(defn test-nested-if [] (defn test-nested-if []
"NATIVE: test nested if" "NATIVE: test nested if"
(for [x (range 10)] (for [[x (range 10)]]
(if (in "foo" "foobar") (if (in "foo" "foobar")
(do (do
(if true true true)) (if true true true))
@ -800,14 +800,14 @@
(defn test-break-breaking [] (defn test-break-breaking []
"NATIVE: test checking if break actually breaks" "NATIVE: test checking if break actually breaks"
(defn holy-grail [] (for [x (range 10)] (if (= x 5) (break))) x) (defn holy-grail [] (for [[x (range 10)]] (if (= x 5) (break))) x)
(assert (= (holy-grail) 5))) (assert (= (holy-grail) 5)))
(defn test-continue-continuation [] (defn test-continue-continuation []
"NATIVE: test checking if continue actually continues" "NATIVE: test checking if continue actually continues"
(setv y []) (setv y [])
(for [x (range 10)] (for [[x (range 10)]]
(if (!= x 5) (if (!= x 5)
(continue)) (continue))
(.append y x)) (.append y x))

View File

@ -56,7 +56,7 @@
(defn test-midtree-yield-in-for [] (defn test-midtree-yield-in-for []
"NATIVE: test yielding in a for with a return" "NATIVE: test yielding in a for with a return"
(defn kruft-in-for [] (defn kruft-in-for []
(for [i (range 5)] (for* [i (range 5)]
(yield i)) (yield i))
(+ 1 2))) (+ 1 2)))
@ -72,7 +72,7 @@
(defn test-multi-yield [] (defn test-multi-yield []
"NATIVE: testing multiple yields" "NATIVE: testing multiple yields"
(defn multi-yield [] (defn multi-yield []
(for [i (range 3)] (for* [i (range 3)]
(yield i)) (yield i))
(yield "a") (yield "a")
(yield "end")) (yield "end"))
@ -97,7 +97,7 @@
(defn test-yield-from [] (defn test-yield-from []
"NATIVE: testing yield from" "NATIVE: testing yield from"
(defn yield-from-test [] (defn yield-from-test []
(for [i (range 3)] (for* [i (range 3)]
(yield i)) (yield i))
(yield-from [1 2 3])) (yield-from [1 2 3]))
(assert (= (list (yield-from-test)) [0 1 2 1 2 3]))) (assert (= (list (yield-from-test)) [0 1 2 1 2 3])))

View File

@ -0,0 +1,44 @@
(defclass WithTest [object]
[(--init--
(fn [self val]
(setv self.val val)
None))
(--enter--
(fn [self]
self.val))
(--exit--
(fn [self type value traceback]
(setv self.val None)))])
(defn test-single-with []
"NATIVE: test a single with"
(with [[t (WithTest 1)]]
(assert (= t 1))))
(defn test-two-with []
"NATIVE: test two withs"
(with [[t1 (WithTest 1)]
[t2 (WithTest 2)]]
(assert (= t1 1))
(assert (= t2 2))))
(defn test-thrice-with []
"NATIVE: test three withs"
(with [[t1 (WithTest 1)]
[t2 (WithTest 2)]
[t3 (WithTest 3)]]
(assert (= t1 1))
(assert (= t2 2))
(assert (= t3 3))))
(defn test-quince-with []
"NATIVE: test four withs, one with no args"
(with [[t1 (WithTest 1)]
[t2 (WithTest 2)]
[t3 (WithTest 3)]
[(WithTest 4)]]
(assert (= t1 1))
(assert (= t2 2))
(assert (= t3 3))))