diff --git a/docs/language/api.rst b/docs/language/api.rst index 09545e4..e1c0fe7 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -38,6 +38,34 @@ 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. +-> +-- + +`->` 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 --- @@ -135,6 +163,36 @@ the user enters `k`. (print "Try again"))) +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 -------- @@ -229,9 +287,30 @@ below: Meow +defn / defun +------------ + + defmacro -------- +`defmacro` is used to define macros. The general format is +`(defmacro [parameters] expr)`. + +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. + +.. code-block:: clj + + => (defmacro infix [code] + ... (quasiquote ( + ... (unquote (get code 1)) + ... (unquote (get code 0)) + ... (unquote (get code 2))))) + + => (infix (1 + 1)) + 2 eval ---- @@ -245,6 +324,33 @@ 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 ------- @@ -310,6 +416,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 -- @@ -412,6 +537,10 @@ function is defined and passed to another function for filtering output. Dave +let +--- + + list-comp --------- @@ -503,6 +632,28 @@ the `print` form is used to output on screen. Example usage: 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 `module-1` and `module-2`: + +.. code-block:: clj + + (require module-1 module-2) + + +rest / cdr +---------- + +`rest` and `cdr` return the collection passed as an argument without the first +element: + +.. code-block:: clj + + => (rest (range 10)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + slice ----- @@ -583,6 +734,33 @@ 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 +------ + +`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 ----- 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)))))))))))