From 9c31e34a264e4db02b7d3e563a4ac9105fd654ee Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Sat, 6 Jul 2013 23:43:47 +0300 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 624dfdc8c9d344b0c50d4fe063e4da18b375fd27 Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Tue, 16 Jul 2013 20:23:38 +0300 Subject: [PATCH 8/8] 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