Merge branch 'master' into pr/254
This commit is contained in:
commit
ad86dff754
@ -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 <http://hy.pault.ag/>`_!
|
||||
Read more about Hy in these docs!
|
||||
|
||||
We're also on IRC! Join
|
||||
`#hy on irc.freenode.net <http://webchat.freenode.net/?channels=hy>`_!
|
||||
|
@ -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 / setv
|
||||
-----------------
|
||||
|
||||
`def` and `setv` 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']
|
||||
|
||||
=> (setv 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 classes and another vector containing
|
||||
attributes of the new class as two item vectors.
|
||||
|
||||
.. code-block:: clj
|
||||
|
||||
(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
|
||||
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,40 @@ 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-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
|
||||
---
|
||||
@ -224,6 +300,7 @@ Example usages:
|
||||
.. note:: `get` raises an IndexError if a list is queried for an index that is
|
||||
out of bounds.
|
||||
|
||||
|
||||
global
|
||||
------
|
||||
|
||||
@ -283,10 +360,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 +427,7 @@ conditional expression. Some examples:
|
||||
=> (list-comp (* x 2) [x collection] (< x 5))
|
||||
[0, 2, 4, 6, 8]
|
||||
|
||||
|
||||
not
|
||||
---
|
||||
|
||||
@ -374,6 +493,7 @@ the `print` form is used to output on screen. Example usage:
|
||||
|
||||
.. note:: `print` always returns None
|
||||
|
||||
|
||||
require
|
||||
-------
|
||||
|
||||
@ -469,16 +589,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))
|
||||
<generator object multiply at 0x978d8ec>
|
||||
|
||||
=> (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]
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
@ -1019,7 +1030,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 != []:
|
||||
@ -1551,6 +1562,8 @@ class HyASTCompiler(object):
|
||||
body=body.stmts,
|
||||
orelse=orel.stmts)
|
||||
|
||||
ret.contains_yield = body.contains_yield
|
||||
|
||||
return ret
|
||||
|
||||
@builds("while")
|
||||
@ -1568,6 +1581,8 @@ class HyASTCompiler(object):
|
||||
lineno=expr.start_line,
|
||||
col_offset=expr.start_column)
|
||||
|
||||
ret.contains_yield = body.contains_yield
|
||||
|
||||
return ret
|
||||
|
||||
@builds(HyList)
|
||||
@ -1611,9 +1626,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,
|
||||
@ -1657,6 +1675,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))
|
||||
|
@ -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__)))
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user