From 9c31e34a264e4db02b7d3e563a4ac9105fd654ee Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Sat, 6 Jul 2013 23:43:47 +0300 Subject: [PATCH 01/21] documentation for builtins relates #18 --- docs/language/api.rst | 158 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/docs/language/api.rst b/docs/language/api.rst index 0e58402..03623da 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -75,6 +75,7 @@ case the first false value will be returned. Examples of usage: I can has False False + assert ------ @@ -88,6 +89,7 @@ condition is not met, an `AssertionError` is raised. The example usage: Assert takes a single parameter, a conditional that evaluates to either `True` or `False`. + assoc ----- @@ -111,6 +113,7 @@ Examples of usage: .. note:: `assoc` modifies the datastructure in place and returns `None`. + break ----- @@ -144,6 +147,7 @@ however is called only for every other value in the list. (continue)) (side-effect2 x))) + do / progn ---------- @@ -176,10 +180,48 @@ Some example usage: def / setf / setv ----------------- +`def`, `setv` and `setf` are used to bind value, object or a function to a +symbol. For example: + +.. code-block:: clj + + => (def names ["Alice" "Bob" "Charlie"] + => (print names) + [u'Alice', u'Bob', u'Charlie'] + + => (setf counter (fn [collection item] (.count collection item))) + => (counter [1 2 3 4 5 2 3] 2) + 2 + defclass -------- +new classes are declared with `defclass`. It can takes two optional parameters: +a vector defining a possible super class and another vector containing +attributes of the new class as two item vectors. + +.. code-block:: clj + + (defclass class-name [super-class] + [[attribute value]]) + +Both values and functions can be bound on the new class as shown by the example +below: + +.. code-block:: clj + + => (defclass Cat [] + ... [[age None] + ... [colour "white"] + ... [speak (fn [self] (print "Meow"))]]) + + => (def spot (Cat)) + => (setv spot.colour "Black") + 'Black' + => (.speak spot) + Meow + defmacro -------- @@ -200,6 +242,19 @@ eval-when-compile foreach ------- +`foreach` 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 +collection and calls side-effect to each element in the collection: + +.. code-block:: clj + + ;; assuming that (side-effect) is a function that takes a single parameter + (foreach [element collection] (side-effect element)) + + ;; foreach can have an optional else block + (foreach [element collection] (side-effect element) + (else (side-effect2 element))) + get --- @@ -224,6 +279,7 @@ Example usages: .. note:: `get` raises an IndexError if a list is queried for an index that is out of bounds. + global ------ @@ -283,10 +339,51 @@ of import you can use. kwapply ------- +`kwapply` can be used to supply keyword arguments to a function. + +For example: + +.. code-block:: clj + + => (defn rent-car [&kwargs kwargs] + ... (cond ((in :brand kwargs) (print "brand:" (:brand kwargs))) + ... ((in :model kwargs) (print "model:" (:model kwargs))))) + + => (kwapply (rent-car) {:model "T-Model"}) + model: T-Model + + => (defn total-purchase [price amount &optional [fees 1.05] [vat 1.1]] + ... (* price amount fees vat)) + + => (total-purchase 10 15) + 173.25 + + => (kwapply (total-purchase 10 15) {"vat" 1.05}) + 165.375 + lambda / fn ----------- +`lambda` and `fn` can be used to define an anonymous function. The parameters are +similar to `defn`: first parameter is vector of parameters and the rest is the +body of the function. lambda returns a new function. In the example an anonymous +function is defined and passed to another function for filtering output. + +.. code-block:: clj + + => (def people [{:name "Alice" :age 20} + ... {:name "Bob" :age 25} + ... {:name "Charlie" :age 50} + ... {:name "Dave" :age 5}]) + + => (defn display-people [people filter] + ... (foreach [person people] (if (filter person) (print (:name person))))) + + => (display-people people (fn [person] (< (:age person) 25))) + Alice + Dave + list-comp --------- @@ -309,6 +406,7 @@ conditional expression. Some examples: => (list-comp (* x 2) [x collection] (< x 5)) [0, 2, 4, 6, 8] + not --- @@ -374,6 +472,7 @@ the `print` form is used to output on screen. Example usage: .. note:: `print` always returns None + require ------- @@ -469,16 +568,75 @@ The following example will output "hello world!" on screen indefinetely: (while True (print "hello world!")) + with ---- +`with` is used to wrap execution of a block with a context manager. The context +manager can then set up the local system and tear it down in a controlled +manner. Typical example of using `with` is processing files. `with` can bind +context to an argument or ignore it completely, as shown below: + +.. code-block:: clj + + (with [arg (expr)] block) + + (with [(expr)] block) + +The following example will open file `NEWS` and print its content on screen. The +file is automatically closed after it has been processed. + +.. code-block:: clj + + (with [f (open "NEWS")] (print (.read f))) + with-decorator -------------- +`with-decorator` is used to wrap a function with another. The function performing +decoration should accept a single value, the function being decorated and return +a new function. `with-decorator` takes two parameters, the function performing +decoration and the function being decorated. + +In the following example, `inc-decorator` is used to decorate function `addition` +with a function that takes two parameters and calls the decorated function with +values that are incremented by 1. When decorated `addition` is called with values +1 and 1, the end result will be 4 (1+1 + 1+1). + +.. code-block:: clj + + => (defn inc-decorator [func] + ... (fn [value-1 value-2] (func (+ value-1 1) (+ value-2 1)))) + => (with-decorator inc-decorator (defn addition [a b] (+ a b))) + => (addition 1 1) + 4 + yield ----- +`yield` is used to create a generator object, that returns 1 or more values. +The generator is iterable and therefore can be used in loops, list +comprehensions and other similar constructs. +Especially the second example shows how generators can be used to generate +infinite series without consuming infinite amount of memory. +.. code-block:: clj + + => (defn multiply [bases coefficients] + ... (foreach [(, base coefficient) (zip bases coefficients)] + ... (yield (* base coefficient)))) + + => (multiply (range 5) (range 5)) + + + => (list-comp value [value (multiply (range 10) (range 10))]) + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + + => (import random) + => (defn random-numbers [low high] + ... (while True (yield (.randint random low high)))) + => (list-comp x [x (take 15 (random-numbers 1 50))])]) + [7, 41, 6, 22, 32, 17, 5, 38, 18, 38, 17, 14, 23, 23, 19] From 08734602ca8ee5a762bd83724e05fb8d06a2a8cb Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Wed, 10 Jul 2013 07:11:11 +0300 Subject: [PATCH 02/21] defclass clarification --- docs/language/api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 03623da..06cc647 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -198,12 +198,12 @@ defclass -------- new classes are declared with `defclass`. It can takes two optional parameters: -a vector defining a possible super class and another vector containing +a vector defining a possible super classes and another vector containing attributes of the new class as two item vectors. .. code-block:: clj - (defclass class-name [super-class] + (defclass class-name [super-class-1 super-class-2] [[attribute value]]) Both values and functions can be bound on the new class as shown by the example From e09a89b7cfd1966e2e65f9410bdbacf4c60e1415 Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Wed, 10 Jul 2013 08:24:58 +0300 Subject: [PATCH 03/21] foreach - else clarification --- docs/language/api.rst | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 06cc647..903a8f7 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -252,8 +252,29 @@ collection and calls side-effect to each element in the collection: (foreach [element collection] (side-effect element)) ;; foreach can have an optional else block - (foreach [element collection] (side-effect element) - (else (side-effect2 element))) + (foreach [element collection] (side-effect-2 element) + (else (side-effect-2))) + +The optional `else` block is executed only if the `foreach` loop terminates +normally. If the execution is halted with `break`, the `else` does not execute. + +.. code-block:: clj + + => (foreach [element [1 2 3]] (if (< element 3) + ... (print element) + ... (break)) + ... (else (print "loop finished"))) + 1 + 2 + + => (foreach [element [1 2 3]] (if (< element 4) + ... (print element) + ... (break)) + ... (else (print "loop finished"))) + 1 + 2 + 3 + loop finished get From 6dc424007fa364ef2a4d21b1049b3d437f222bba Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Wed, 10 Jul 2013 11:39:27 +0300 Subject: [PATCH 04/21] removed setf --- docs/language/api.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 903a8f7..2492dd9 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -180,8 +180,8 @@ Some example usage: def / setf / setv ----------------- -`def`, `setv` and `setf` are used to bind value, object or a function to a -symbol. For example: +`def` and `setv` are used to bind value, object or a function to a symbol. For +example: .. code-block:: clj @@ -189,7 +189,7 @@ symbol. For example: => (print names) [u'Alice', u'Bob', u'Charlie'] - => (setf counter (fn [collection item] (.count collection item))) + => (setv counter (fn [collection item] (.count collection item))) => (counter [1 2 3 4 5 2 3] 2) 2 From 7c91913122232f9a6e02788fb54fdd7e326bfe98 Mon Sep 17 00:00:00 2001 From: Paul Tagliamonte Date: Sun, 14 Jul 2013 13:03:08 -0400 Subject: [PATCH 05/21] Fix yielding to not suck (#151) This adds a class to avoid returning when we have a Yieldable expression contained in the body of the function. This breaks Python 2.x, and ought to break Python 3.x, but doesn't. We need this fo' context managers, etc. This commit also has work from @rwtolbert adding new testcases and fixes for yielded entries behind a while / for. --- hy/compiler.py | 32 ++++++++++++++++++++++------- tests/native_tests/native_macros.hy | 30 +++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index ad67c28..01d7e17 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -154,7 +154,8 @@ class Result(object): The Result object is interoperable with python AST objects: when an AST object gets added to a Result object, it gets converted on-the-fly. """ - __slots__ = ("imports", "stmts", "temp_variables", "_expr", "__used_expr") + __slots__ = ("imports", "stmts", "temp_variables", + "_expr", "__used_expr", "contains_yield") def __init__(self, *args, **kwargs): if args: @@ -165,12 +166,14 @@ class Result(object): self.stmts = [] self.temp_variables = [] self._expr = None + self.contains_yield = False self.__used_expr = False # XXX: Make sure we only have AST where we should. for kwarg in kwargs: - if kwarg not in ["imports", "stmts", "expr", "temp_variables"]: + if kwarg not in ["imports", "contains_yield", "stmts", "expr", + "temp_variables"]: raise TypeError( "%s() got an unexpected keyword argument '%s'" % ( self.__class__.__name__, kwarg)) @@ -282,13 +285,21 @@ class Result(object): result.stmts = self.stmts + other.stmts result.expr = other.expr result.temp_variables = other.temp_variables + result.contains_yield = False + if self.contains_yield or other.contains_yield: + result.contains_yield = True + return result def __str__(self): - return "Result(imports=[%s], stmts=[%s], expr=%s)" % ( + return ( + "Result(imports=[%s], stmts=[%s], " + "expr=%s, contains_yield=%s)" + ) % ( ", ".join(ast.dump(x) for x in self.imports), ", ".join(ast.dump(x) for x in self.stmts), ast.dump(self.expr) if self.expr else None, + self.contains_yield ) @@ -1011,7 +1022,7 @@ class HyASTCompiler(object): @checkargs(max=1) def compile_yield_expression(self, expr): expr.pop(0) - ret = Result() + ret = Result(contains_yield=True) value = None if expr != []: @@ -1541,6 +1552,8 @@ class HyASTCompiler(object): body=body.stmts, orelse=orel.stmts) + ret.contains_yield = body.contains_yield + return ret @builds("while") @@ -1558,6 +1571,8 @@ class HyASTCompiler(object): lineno=expr.start_line, col_offset=expr.start_column) + ret.contains_yield = body.contains_yield + return ret @builds(HyList) @@ -1601,9 +1616,12 @@ class HyASTCompiler(object): return ret if body.expr: - body += ast.Return(value=body.expr, - lineno=body.expr.lineno, - col_offset=body.expr.col_offset) + if body.contains_yield: + body += body.expr_as_stmt() + else: + body += ast.Return(value=body.expr, + lineno=body.expr.lineno, + col_offset=body.expr.col_offset) if not body.stmts: body += ast.Pass(lineno=expression.start_line, diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy index 2fb23d3..439ee03 100644 --- a/tests/native_tests/native_macros.hy +++ b/tests/native_tests/native_macros.hy @@ -46,6 +46,36 @@ "NATIVE: test macro calling a plain function" (assert (= 3 (bar 1 2)))) +(defn test-midtree-yield [] + "NATIVE: test yielding with a returnable" + (defn kruft [] (yield) (+ 1 1))) + +(defn test-midtree-yield-in-for [] + "NATIVE: test yielding in a for with a return" + (defn kruft-in-for [] + (for [i (range 5)] + (yield i)) + (+ 1 2))) + +(defn test-midtree-yield-in-while [] + "NATIVE: test yielding in a while with a return" + (defn kruft-in-while [] + (setv i 0) + (while (< i 5) + (yield i) + (setv i (+ i 1))) + (+ 2 3))) + +(defn test-multi-yield [] + "NATIVE: testing multiple yields" + (defn multi-yield [] + (for [i (range 3)] + (yield i)) + (yield "a") + (yield "end")) + (assert (= (list (multi-yield)) [0 1 2 "a" "end"]))) + + ; Macro that checks a variable defined at compile or load time (setv phase "load") (eval-when-compile From a85ba71a209ba750e6996181febad1842831cbba Mon Sep 17 00:00:00 2001 From: "Joe H. Rahme" Date: Mon, 15 Jul 2013 10:45:42 +0200 Subject: [PATCH 06/21] Removes link to interactive demo from docs --- docs/index.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 5c14f4e..47f1f11 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -32,8 +32,7 @@ Meet our mascot, "Cuddles": .. \|\||/ -Read more about Hy in these docs! Or, if you'd like, try the -`interactive hy->python demo `_! +Read more about Hy in these docs! We're also on IRC! Join `#hy on irc.freenode.net `_! From c42492ad849f290a0215108265b9e07b30e53867 Mon Sep 17 00:00:00 2001 From: Bob Tolbert Date: Sun, 14 Jul 2013 17:25:57 -0600 Subject: [PATCH 07/21] Fix missing docstrings from defclass issue #248 Added ability to parse doc strings set in defclass declarations, likei: (defclass Foo [object] "this is the doc string" [[x 1]]) --- hy/compiler.py | 11 +++++++++++ tests/native_tests/defclass.hy | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/hy/compiler.py b/hy/compiler.py index 2e4e2b7..f4692fb 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1646,6 +1646,17 @@ class HyASTCompiler(object): body = Result() + # grab the doc string, if there is one + if expression and isinstance(expression[0], HyString): + docstring = expression.pop(0) + symb = HySymbol("__doc__") + symb.start_line = docstring.start_line + symb.start_column = docstring.start_column + body += self._compile_assign(symb, docstring, + docstring.start_line, + docstring.start_column) + body += body.expr_as_stmt() + if expression: try: body_expression = iter(expression.pop(0)) diff --git a/tests/native_tests/defclass.hy b/tests/native_tests/defclass.hy index 4860d39..c1ebe5b 100644 --- a/tests/native_tests/defclass.hy +++ b/tests/native_tests/defclass.hy @@ -60,3 +60,26 @@ (x) (assert false)) (except [NameError]))) + +(defn test-defclass-docstring [] + "NATIVE: test defclass docstring" + (defclass A [] + [[--doc-- "doc string"] + [x 1]]) + (setv a (A)) + (assert (= a.__doc__ "doc string")) + (defclass B [] + "doc string" + [[x 1]]) + (setv b (B)) + (assert (= b.x 1)) + (assert (= b.__doc__ "doc string")) + (defclass MultiLine [] + "begin a very long multi-line string to make + sure that it comes out the way we hope + and can span 3 lines end." + [[x 1]]) + (setv mL (MultiLine)) + (assert (= mL.x 1)) + (assert (in "begin" mL.__doc__)) + (assert (in "end" mL.__doc__))) From dda291cfb5c39cbd337592aae46dc485363d4449 Mon Sep 17 00:00:00 2001 From: Guillermo Vaya Date: Tue, 16 Jul 2013 14:35:57 +0200 Subject: [PATCH 08/21] make assoc accept multiple values, also added a even/odd check for checkargs --- hy/compiler.py | 41 +++++++++++++++++++++------------- tests/native_tests/language.hy | 5 +++++ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 2e4e2b7..5b23272 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -318,7 +318,7 @@ def _raise_wrong_args_number(expression, error): len(expression))) -def checkargs(exact=None, min=None, max=None): +def checkargs(exact=None, min=None, max=None, even=None): def _dec(fn): def checker(self, expression): if exact is not None and (len(expression) - 1) != exact: @@ -334,6 +334,11 @@ def checkargs(exact=None, min=None, max=None): _raise_wrong_args_number( expression, "`%%s' needs at most %d arguments, got %%d" % (max)) + + is_even = not((len(expression) - 1) % 2) + if even is not None and is_even!=even: + even_str = "even" if even else "odd" + _raise_wrong_args_number(expression, "`%%s' needs an %s number of arguments, got %%d" % (even_str)) return fn(self, expression) @@ -1130,25 +1135,31 @@ class HyASTCompiler(object): ctx=ast.Load()) @builds("assoc") - @checkargs(3) + @checkargs(min=3, even=False) def compile_assoc_expression(self, expr): expr.pop(0) # assoc # (assoc foo bar baz) => foo[bar] = baz target = self.compile(expr.pop(0)) - key = self.compile(expr.pop(0)) - val = self.compile(expr.pop(0)) + ret = target + while expr != []: + key = self.compile(expr.pop(0)) + try: + val = self.compile(expr.pop(0)) + except IndexError: + raise HyCompileError("Key {key} has no value to associate".format(key)) - return target + key + val + ast.Assign( - lineno=expr.start_line, - col_offset=expr.start_column, - targets=[ - ast.Subscript( - lineno=expr.start_line, - col_offset=expr.start_column, - value=target.force_expr, - slice=ast.Index(value=key.force_expr), - ctx=ast.Store())], - value=val.force_expr) + ret += key + val + ast.Assign( + lineno=expr.start_line, + col_offset=expr.start_column, + targets=[ + ast.Subscript( + lineno=expr.start_line, + col_offset=expr.start_column, + value=target.force_expr, + slice=ast.Index(value=key.force_expr), + ctx=ast.Store())], + value=val.force_expr) + return ret @builds("with_decorator") @checkargs(min=1) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index fa55e27..be2393c 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -348,6 +348,11 @@ (assoc vals "two" "three") (assert (= (get vals "two") "three"))) +(defn test-multiassoc [] + "NATIVE: test assoc multiple values" + (setv vals {"one" "two"}) + (assoc vals "two" "three" "four" "five") + (assert (and (= (get vals "two") "three") (= (get vals "four") "five")))) (defn test-pass [] "NATIVE: Test pass worksish" From 624dfdc8c9d344b0c50d4fe063e4da18b375fd27 Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Tue, 16 Jul 2013 20:23:38 +0300 Subject: [PATCH 09/21] clarified foreach - else --- docs/language/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 2492dd9..3a41249 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -252,7 +252,7 @@ collection and calls side-effect to each element in the collection: (foreach [element collection] (side-effect element)) ;; foreach can have an optional else block - (foreach [element collection] (side-effect-2 element) + (foreach [element collection] (side-effect element) (else (side-effect-2))) The optional `else` block is executed only if the `foreach` loop terminates From 6778e9b2e1d9a4264538012e885d252c892447e5 Mon Sep 17 00:00:00 2001 From: Guillermo Vaya Date: Tue, 16 Jul 2013 16:12:43 +0200 Subject: [PATCH 10/21] added @tuturto sugesstions and flake8 errors --- hy/compiler.py | 14 +++++++++----- tests/native_tests/language.hy | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 5b23272..c613b70 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -334,11 +334,14 @@ def checkargs(exact=None, min=None, max=None, even=None): _raise_wrong_args_number( expression, "`%%s' needs at most %d arguments, got %%d" % (max)) - + is_even = not((len(expression) - 1) % 2) - if even is not None and is_even!=even: + if even is not None and is_even != even: even_str = "even" if even else "odd" - _raise_wrong_args_number(expression, "`%%s' needs an %s number of arguments, got %%d" % (even_str)) + _raise_wrong_args_number( + expression, + "`%%s' needs an %s number of arguments, got %%d" + % (even_str)) return fn(self, expression) @@ -1141,12 +1144,13 @@ class HyASTCompiler(object): # (assoc foo bar baz) => foo[bar] = baz target = self.compile(expr.pop(0)) ret = target - while expr != []: + while expr: key = self.compile(expr.pop(0)) try: val = self.compile(expr.pop(0)) except IndexError: - raise HyCompileError("Key {key} has no value to associate".format(key)) + raise HyCompileError( + "Key {key} has no value to associate".format(key)) ret += key + val + ast.Assign( lineno=expr.start_line, diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index be2393c..00815b1 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -352,7 +352,7 @@ "NATIVE: test assoc multiple values" (setv vals {"one" "two"}) (assoc vals "two" "three" "four" "five") - (assert (and (= (get vals "two") "three") (= (get vals "four") "five")))) + (assert (and (= (get vals "two") "three") (= (get vals "four") "five") (= (get vals "one") "two")))) (defn test-pass [] "NATIVE: Test pass worksish" From 099ad28ad1dbe05822b3b917b8bd4259e4961865 Mon Sep 17 00:00:00 2001 From: Guillermo Vaya Date: Wed, 17 Jul 2013 15:40:38 +0200 Subject: [PATCH 11/21] added @paultag suggestions --- hy/compiler.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index c613b70..9b3be30 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1144,13 +1144,9 @@ class HyASTCompiler(object): # (assoc foo bar baz) => foo[bar] = baz target = self.compile(expr.pop(0)) ret = target - while expr: - key = self.compile(expr.pop(0)) - try: - val = self.compile(expr.pop(0)) - except IndexError: - raise HyCompileError( - "Key {key} has no value to associate".format(key)) + i = iter(expr) + for (key, val) in ((self.compile(x), self.compile(y)) + for (x, y) in zip(i, i)): ret += key + val + ast.Assign( lineno=expr.start_line, From d580351b41a17fe008d1b8f8b73d6931fb604962 Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Thu, 18 Jul 2013 15:00:24 +0300 Subject: [PATCH 12/21] initial macrodef documentation --- docs/language/api.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/language/api.rst b/docs/language/api.rst index c1235a7..f3adaf1 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -226,6 +226,21 @@ below: defmacro -------- +`defmacro` is used to define macros. + +The general format is `(defmacro [parameters] expr)`. + +Following example defines a macro that can be used to multiply all but the first +parameter given to it. + +.. codeblock:: clj + + => (defmacro multiply-some [&rest params] (quasiquote (* (unquote-splice (list (slice params 1)))))) + + => (multiply-some 0 2 3) + 6 + => (multiply-some 2 0 3) + 0 eval ---- From a17dcdbffbe7c7eb270ea669790b16faec3ce504 Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Fri, 19 Jul 2013 00:43:06 +0200 Subject: [PATCH 13/21] Make `with` return the last expression from its branch Thanks @rwtolbert for the bug report --- hy/compiler.py | 26 +++++++++++++++++++++----- tests/native_tests/language.hy | 7 +++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 70f329c..ba35791 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1200,11 +1200,18 @@ class HyASTCompiler(object): thing = self._storeize(self.compile(args.pop(0))) body = self._compile_branch(expr) - body += body.expr_as_stmt() - if not body.stmts: - body += ast.Pass(lineno=expr.start_line, - col_offset=expr.start_column) + var = self.get_anon_var() + name = ast.Name(id=ast_str(var), arg=ast_str(var), + ctx=ast.Store(), + lineno=expr.start_line, + col_offset=expr.start_column) + + # Store the result of the body in a tempvar + body += ast.Assign(targets=[name], + value=body.force_expr, + lineno=expr.start_line, + col_offset=expr.start_column) the_with = ast.With(context_expr=ctx.force_expr, lineno=expr.start_line, @@ -1216,7 +1223,16 @@ class HyASTCompiler(object): the_with.items = [ast.withitem(context_expr=ctx.force_expr, optional_vars=thing)] - return ctx + the_with + ret = ctx + the_with + # And make our expression context our temp variable + expr_name = ast.Name(id=ast_str(var), arg=ast_str(var), + ctx=ast.Load(), + lineno=expr.start_line, + col_offset=expr.start_column) + + ret += Result(expr=expr_name, temp_variables=[expr_name, name]) + + return ret @builds(",") def compile_tuple(self, expr): diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 00815b1..6dd2986 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -411,6 +411,13 @@ (with [(open "README.md" "r")] (do))) +(defn test-with-return [] + "NATIVE: test that with returns stuff" + (defn read-file [filename] + (with [fd (open filename "r")] (.read fd))) + (assert (!= 0 (len (read-file "README.md"))))) + + (defn test-for-doodle [] "NATIVE: test for-do" (do (do (do (do (do (do (do (do (do (setv (, x y) (, 0 0))))))))))) From 22d8a783300c2504c7bfa8d1eb011af0fdb07d63 Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Fri, 19 Jul 2013 12:06:23 +0300 Subject: [PATCH 14/21] better macro example --- docs/language/api.rst | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index f3adaf1..cf3cd1c 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -226,21 +226,23 @@ below: defmacro -------- -`defmacro` is used to define macros. +`defmacro` is used to define macros. The general format is +`(defmacro [parameters] expr)`. -The general format is `(defmacro [parameters] expr)`. - -Following example defines a macro that can be used to multiply all but the first -parameter given to it. +Following example defines a macro that can be used to swap order of elements in +code, allowing the user to write code in infix notation, where operator is in +between the operands. .. codeblock:: clj - => (defmacro multiply-some [&rest params] (quasiquote (* (unquote-splice (list (slice params 1)))))) + => (defmacro infix [code] + ... (quasiquote ( + ... (unquote (get code 1)) + ... (unquote (get code 0)) + ... (unquote (get code 2))))) - => (multiply-some 0 2 3) - 6 - => (multiply-some 2 0 3) - 0 + => (infix (1 + 1)) + 2 eval ---- From a786577a8b7a5e7bb2ef0d545ba7ad486c1b749d Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Fri, 19 Jul 2013 15:57:25 +0300 Subject: [PATCH 15/21] documentation for global --- docs/language/api.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/language/api.rst b/docs/language/api.rst index cf3cd1c..7443ed0 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -321,6 +321,25 @@ Example usages: global ------ +`global` can be used to mark a symbol as global. This allows the programmer to +assign a value to a global symbol. Reading a global symbol does not require the +`global` keyword, just the assigning does. + +Following example shows how global `a` is assigned a value in a function and later +on printed on another function. Without the `global` keyword, the second function +would thrown a `NameError`. + +.. code-block:: clj + + (defn set-a [value] + (global a) + (setv a value)) + + (defn print-a [] + (print a)) + + (set-a 5) + (print-a) if -- From b761c7dc83ef9954af2e95ebef44696f372e9eef Mon Sep 17 00:00:00 2001 From: "Joe H. Rahme" Date: Sun, 21 Jul 2013 02:59:35 +0200 Subject: [PATCH 16/21] Fixes a mistake (most probably a typo) in the tutorial. --- docs/tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 0c3838b..5ce99f0 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -392,7 +392,7 @@ In python we might see:: The same thing in Hy:: - => (defn optional_arg [pos1 pos2 &optional keyword1 [keyword2 88]] + => (defn optional_arg [pos1 pos2 &optional keyword1 [keyword2 42]] ... [pos1 pos2 keyword1 keyword2]) => (optional_arg 1 2) [1 2 None 42] From 82e8598cd6986e3d2d282ce29fa6c5557a64b0de Mon Sep 17 00:00:00 2001 From: Guillermo Vaya Date: Wed, 17 Jul 2013 16:25:22 +0200 Subject: [PATCH 17/21] fix to assoc docs + new multiassoc definition --- docs/language/api.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index c1235a7..09545e4 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -94,18 +94,24 @@ assoc ----- `assoc` form is used to associate a key with a value in a dictionary or to set -an index of a list to a value. It takes three parameters: `datastructure` to be -modified, `key` or `index` and `value`. +an index of a list to a value. It takes at least three parameters: `datastructure` +to be modified, `key` or `index` and `value`. If more than three parameters are +used it will associate in pairs. Examples of usage: .. code-block:: clj - =>(let [[collection ({})]] + =>(let [[collection {}]] ... (assoc collection "Dog" "Bark") ... (print collection)) {u'Dog': u'Bark'} + =>(let [[collection {}]] + ... (assoc collection "Dog" "Bark" "Cat" "Meow") + ... (print collection)) + {u'Cat': u'Meow', u'Dog': u'Bark'} + =>(let [[collection [1 2 3 4]]] ... (assoc collection 2 None) ... (print collection)) From ec76c6bee069f468d2c82589b40939967ca519b9 Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Mon, 22 Jul 2013 23:36:59 +0300 Subject: [PATCH 18/21] documentation for require working on documenting macros --- docs/language/api.rst | 52 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 7443ed0..420a05d 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -38,6 +38,14 @@ Hy features a number special forms that are used to help generate correct Python AST. The following are "special" forms, which may have behavior that's slightly unexpected in some situations. +-> +-- + + +->> +--- + + and --- @@ -129,6 +137,10 @@ the user enters `k`. (print "Try again"))) +cond +---- + + continue -------- @@ -223,6 +235,10 @@ below: Meow +defn / defun +------------ + + defmacro -------- @@ -233,7 +249,7 @@ Following example defines a macro that can be used to swap order of elements in code, allowing the user to write code in infix notation, where operator is in between the operands. -.. codeblock:: clj +.. code-block:: clj => (defmacro infix [code] ... (quasiquote ( @@ -256,6 +272,14 @@ eval-when-compile ----------------- +first / car +----------- + + +for +--- + + foreach ------- @@ -442,6 +466,10 @@ function is defined and passed to another function for filtering output. Dave +let +--- + + list-comp --------- @@ -533,6 +561,20 @@ the `print` form is used to output on screen. Example usage: require ------- +`require` is used to import macros from a given namespace. It takes at least one +parameter specifying the namespace which macros should be imported. Multiple +namesspaces can be imported with a single `require`. + +The following example will import macros from `namespace-1` and `namespace-2`: + +.. code-block:: clj + + (require namespace-1 namespace-2) + + +rest / cdr +---------- + slice ----- @@ -613,6 +655,14 @@ be executed. If no errors are raised the `else` block is executed. Regardless if an error was raised or not, the `finally` block is executed as last. +unless +------ + + +when +---- + + while ----- From c6410b70505b7233dd6b1a1857fa62b1cc70247e Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Mon, 22 Jul 2013 23:59:21 +0300 Subject: [PATCH 19/21] cond documentation --- docs/language/api.rst | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/language/api.rst b/docs/language/api.rst index 420a05d..9acaeb1 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -140,6 +140,32 @@ the user enters `k`. cond ---- +`cond` macro can be used to build nested if-statements. + +The following example shows the relationship between the macro and the expanded +code: + +.. code-block:: clj + + (cond (condition-1 result-1) + (condition-2 result-2)) + + (if condition-1 result-1 + (if condition-2 result-2)) + +As shown below only the first matching result block is executed. + +.. code-block:: clj + + => (defn check-value [value] + ... (cond ((< value 5) (print "value is smaller than 5")) + ... ((= value 5) (print "value is equal to 5")) + ... ((> value 5) (print "value is greater than 5")) + ... (True (print "value is something that it should not be")))) + + => (check-value 6) + value is greater than 5 + continue -------- From 151ac69ce8329c650eb981459426b4825a975039 Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Tue, 23 Jul 2013 00:36:34 +0300 Subject: [PATCH 20/21] working on documenting builtins --- docs/language/api.rst | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/docs/language/api.rst b/docs/language/api.rst index 9acaeb1..0e85e21 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -41,10 +41,30 @@ behavior that's slightly unexpected in some situations. -> -- +`->` or `threading macro` is used to avoid nesting of expressions. The threading +macro inserts each expression into the next expression’s first argument place. +The following code demonstrates this: + +.. code-block:: clj + + => (defn output [a b] (print a b)) + => (-> (+ 5 5) (output 5)) + 10 5 + ->> --- +`->>` or `threading tail macro` is similar to `threading macro` but instead of +inserting each expression into the next expression’s first argument place it +appends it as the last argument. The following code demonstrates this: + +.. code-block:: clj + + => (defn output [a b] (print a b)) + => (->> (+ 5 5) (output 5)) + 5 10 + and --- @@ -301,10 +321,29 @@ eval-when-compile first / car ----------- +`first` and `car` are macros for accessing the first element of a collection: + +.. code-block:: clj + + => (first (range 10)) + 0 + 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 ------- @@ -601,6 +640,14 @@ The following example will import macros from `namespace-1` and `namespace-2`: rest / cdr ---------- +`rest` and `cdr` are used to access every element in collection, except the +first one: + +.. code-block:: clj + + => (rest (range 10)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + slice ----- @@ -684,10 +731,29 @@ if an error was raised or not, the `finally` block is executed as last. unless ------ +`unless` macro is a shorthand for writing a if-statement that checks if the +given conditional is False. The following shows how the macro expands into code. + +.. code-block:: clj + + (unless conditional statement) + + (if conditional + None + (do statement)) when ---- +`when` is similar to `unless`, except it tests when the given conditional is +True. It is not possible to have an `else` block in `when` macro. The following +shows how the macro is expanded into code. + +.. code-block:: clj + + (when conditional statement) + + (if conditional (do statement)) while ----- From 6665a2b4902417b790b9ce095091a437d5889155 Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Tue, 23 Jul 2013 06:39:29 +0300 Subject: [PATCH 21/21] clarified rest / cdr, cleaned up require --- docs/language/api.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 0e85e21..b279ab3 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -626,22 +626,22 @@ the `print` form is used to output on screen. Example usage: require ------- -`require` is used to import macros from a given namespace. It takes at least one -parameter specifying the namespace which macros should be imported. Multiple -namesspaces can be imported with a single `require`. +`require` is used to import macros from a given module. It takes at least one +parameter specifying the module which macros should be imported. Multiple +modules can be imported with a single `require`. -The following example will import macros from `namespace-1` and `namespace-2`: +The following example will import macros from `module-1` and `module-2`: .. code-block:: clj - (require namespace-1 namespace-2) + (require module-1 module-2) rest / cdr ---------- -`rest` and `cdr` are used to access every element in collection, except the -first one: +`rest` and `cdr` return the collection passed as an argument without the first +element: .. code-block:: clj