Merge branch 'master' into pr/236
This commit is contained in:
commit
acfc5c6aa5
@ -32,8 +32,7 @@ Meet our mascot, "Cuddles":
|
|||||||
.. \|\||/
|
.. \|\||/
|
||||||
|
|
||||||
|
|
||||||
Read more about Hy in these docs! Or, if you'd like, try the
|
Read more about Hy in these docs!
|
||||||
`interactive hy->python demo <http://hy.pault.ag/>`_!
|
|
||||||
|
|
||||||
We're also on IRC! Join
|
We're also on IRC! Join
|
||||||
`#hy on irc.freenode.net <http://webchat.freenode.net/?channels=hy>`_!
|
`#hy on irc.freenode.net <http://webchat.freenode.net/?channels=hy>`_!
|
||||||
|
@ -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
|
correct Python AST. The following are "special" forms, which may have
|
||||||
behavior that's slightly unexpected in some situations.
|
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
|
and
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -75,6 +103,7 @@ case the first false value will be returned. Examples of usage:
|
|||||||
I can has False
|
I can has False
|
||||||
False
|
False
|
||||||
|
|
||||||
|
|
||||||
assert
|
assert
|
||||||
------
|
------
|
||||||
|
|
||||||
@ -88,22 +117,29 @@ condition is not met, an `AssertionError` is raised. The example usage:
|
|||||||
Assert takes a single parameter, a conditional that evaluates to either `True`
|
Assert takes a single parameter, a conditional that evaluates to either `True`
|
||||||
or `False`.
|
or `False`.
|
||||||
|
|
||||||
|
|
||||||
assoc
|
assoc
|
||||||
-----
|
-----
|
||||||
|
|
||||||
`assoc` form is used to associate a key with a value in a dictionary or to set
|
`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
|
an index of a list to a value. It takes at least three parameters: `datastructure`
|
||||||
modified, `key` or `index` and `value`.
|
to be modified, `key` or `index` and `value`. If more than three parameters are
|
||||||
|
used it will associate in pairs.
|
||||||
|
|
||||||
Examples of usage:
|
Examples of usage:
|
||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
=>(let [[collection ({})]]
|
=>(let [[collection {}]]
|
||||||
... (assoc collection "Dog" "Bark")
|
... (assoc collection "Dog" "Bark")
|
||||||
... (print collection))
|
... (print collection))
|
||||||
{u'Dog': u'Bark'}
|
{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]]]
|
=>(let [[collection [1 2 3 4]]]
|
||||||
... (assoc collection 2 None)
|
... (assoc collection 2 None)
|
||||||
... (print collection))
|
... (print collection))
|
||||||
@ -111,6 +147,7 @@ Examples of usage:
|
|||||||
|
|
||||||
.. note:: `assoc` modifies the datastructure in place and returns `None`.
|
.. note:: `assoc` modifies the datastructure in place and returns `None`.
|
||||||
|
|
||||||
|
|
||||||
break
|
break
|
||||||
-----
|
-----
|
||||||
|
|
||||||
@ -126,6 +163,36 @@ the user enters `k`.
|
|||||||
(print "Try again")))
|
(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
|
continue
|
||||||
--------
|
--------
|
||||||
|
|
||||||
@ -144,6 +211,7 @@ however is called only for every other value in the list.
|
|||||||
(continue))
|
(continue))
|
||||||
(side-effect2 x)))
|
(side-effect2 x)))
|
||||||
|
|
||||||
|
|
||||||
do / progn
|
do / progn
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@ -176,14 +244,73 @@ Some example usage:
|
|||||||
def / setv
|
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
|
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
|
||||||
|
|
||||||
|
|
||||||
|
defn / defun
|
||||||
|
------------
|
||||||
|
|
||||||
|
|
||||||
defmacro
|
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
|
eval
|
||||||
----
|
----
|
||||||
@ -197,9 +324,70 @@ 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
|
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
|
get
|
||||||
---
|
---
|
||||||
@ -224,9 +412,29 @@ Example usages:
|
|||||||
.. note:: `get` raises an IndexError if a list is queried for an index that is
|
.. note:: `get` raises an IndexError if a list is queried for an index that is
|
||||||
out of bounds.
|
out of bounds.
|
||||||
|
|
||||||
|
|
||||||
global
|
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
|
if
|
||||||
--
|
--
|
||||||
@ -283,10 +491,55 @@ of import you can use.
|
|||||||
kwapply
|
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 / 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
|
||||||
|
|
||||||
|
|
||||||
|
let
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
list-comp
|
list-comp
|
||||||
---------
|
---------
|
||||||
@ -309,6 +562,7 @@ conditional expression. Some examples:
|
|||||||
=> (list-comp (* x 2) [x collection] (< x 5))
|
=> (list-comp (* x 2) [x collection] (< x 5))
|
||||||
[0, 2, 4, 6, 8]
|
[0, 2, 4, 6, 8]
|
||||||
|
|
||||||
|
|
||||||
not
|
not
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -374,9 +628,32 @@ the `print` form is used to output on screen. Example usage:
|
|||||||
|
|
||||||
.. note:: `print` always returns None
|
.. note:: `print` always returns None
|
||||||
|
|
||||||
|
|
||||||
require
|
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
|
slice
|
||||||
-----
|
-----
|
||||||
@ -457,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.
|
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
|
while
|
||||||
-----
|
-----
|
||||||
|
|
||||||
@ -469,16 +773,75 @@ The following example will output "hello world!" on screen indefinetely:
|
|||||||
|
|
||||||
(while True (print "hello world!"))
|
(while True (print "hello world!"))
|
||||||
|
|
||||||
|
|
||||||
with
|
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
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
`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
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
`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]
|
||||||
|
@ -392,7 +392,7 @@ In python we might see::
|
|||||||
|
|
||||||
The same thing in Hy::
|
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])
|
... [pos1 pos2 keyword1 keyword2])
|
||||||
=> (optional_arg 1 2)
|
=> (optional_arg 1 2)
|
||||||
[1 2 None 42]
|
[1 2 None 42]
|
||||||
|
110
hy/compiler.py
110
hy/compiler.py
@ -154,7 +154,8 @@ class Result(object):
|
|||||||
The Result object is interoperable with python AST objects: when an AST
|
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.
|
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):
|
def __init__(self, *args, **kwargs):
|
||||||
if args:
|
if args:
|
||||||
@ -165,12 +166,14 @@ class Result(object):
|
|||||||
self.stmts = []
|
self.stmts = []
|
||||||
self.temp_variables = []
|
self.temp_variables = []
|
||||||
self._expr = None
|
self._expr = None
|
||||||
|
self.contains_yield = False
|
||||||
|
|
||||||
self.__used_expr = False
|
self.__used_expr = False
|
||||||
|
|
||||||
# XXX: Make sure we only have AST where we should.
|
# XXX: Make sure we only have AST where we should.
|
||||||
for kwarg in kwargs:
|
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(
|
raise TypeError(
|
||||||
"%s() got an unexpected keyword argument '%s'" % (
|
"%s() got an unexpected keyword argument '%s'" % (
|
||||||
self.__class__.__name__, kwarg))
|
self.__class__.__name__, kwarg))
|
||||||
@ -282,13 +285,21 @@ class Result(object):
|
|||||||
result.stmts = self.stmts + other.stmts
|
result.stmts = self.stmts + other.stmts
|
||||||
result.expr = other.expr
|
result.expr = other.expr
|
||||||
result.temp_variables = other.temp_variables
|
result.temp_variables = other.temp_variables
|
||||||
|
result.contains_yield = False
|
||||||
|
if self.contains_yield or other.contains_yield:
|
||||||
|
result.contains_yield = True
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def __str__(self):
|
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.imports),
|
||||||
", ".join(ast.dump(x) for x in self.stmts),
|
", ".join(ast.dump(x) for x in self.stmts),
|
||||||
ast.dump(self.expr) if self.expr else None,
|
ast.dump(self.expr) if self.expr else None,
|
||||||
|
self.contains_yield
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -318,7 +329,7 @@ def _raise_wrong_args_number(expression, error):
|
|||||||
len(expression)))
|
len(expression)))
|
||||||
|
|
||||||
|
|
||||||
def checkargs(exact=None, min=None, max=None):
|
def checkargs(exact=None, min=None, max=None, even=None):
|
||||||
def _dec(fn):
|
def _dec(fn):
|
||||||
def checker(self, expression):
|
def checker(self, expression):
|
||||||
if exact is not None and (len(expression) - 1) != exact:
|
if exact is not None and (len(expression) - 1) != exact:
|
||||||
@ -335,6 +346,14 @@ def checkargs(exact=None, min=None, max=None):
|
|||||||
expression,
|
expression,
|
||||||
"`%%s' needs at most %d arguments, got %%d" % (max))
|
"`%%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)
|
return fn(self, expression)
|
||||||
|
|
||||||
return checker
|
return checker
|
||||||
@ -1011,7 +1030,7 @@ class HyASTCompiler(object):
|
|||||||
@checkargs(max=1)
|
@checkargs(max=1)
|
||||||
def compile_yield_expression(self, expr):
|
def compile_yield_expression(self, expr):
|
||||||
expr.pop(0)
|
expr.pop(0)
|
||||||
ret = Result()
|
ret = Result(contains_yield=True)
|
||||||
|
|
||||||
value = None
|
value = None
|
||||||
if expr != []:
|
if expr != []:
|
||||||
@ -1130,25 +1149,28 @@ class HyASTCompiler(object):
|
|||||||
ctx=ast.Load())
|
ctx=ast.Load())
|
||||||
|
|
||||||
@builds("assoc")
|
@builds("assoc")
|
||||||
@checkargs(3)
|
@checkargs(min=3, even=False)
|
||||||
def compile_assoc_expression(self, expr):
|
def compile_assoc_expression(self, expr):
|
||||||
expr.pop(0) # assoc
|
expr.pop(0) # assoc
|
||||||
# (assoc foo bar baz) => foo[bar] = baz
|
# (assoc foo bar baz) => foo[bar] = baz
|
||||||
target = self.compile(expr.pop(0))
|
target = self.compile(expr.pop(0))
|
||||||
key = self.compile(expr.pop(0))
|
ret = target
|
||||||
val = self.compile(expr.pop(0))
|
i = iter(expr)
|
||||||
|
for (key, val) in ((self.compile(x), self.compile(y))
|
||||||
|
for (x, y) in zip(i, i)):
|
||||||
|
|
||||||
return target + key + val + ast.Assign(
|
ret += key + val + ast.Assign(
|
||||||
lineno=expr.start_line,
|
lineno=expr.start_line,
|
||||||
col_offset=expr.start_column,
|
col_offset=expr.start_column,
|
||||||
targets=[
|
targets=[
|
||||||
ast.Subscript(
|
ast.Subscript(
|
||||||
lineno=expr.start_line,
|
lineno=expr.start_line,
|
||||||
col_offset=expr.start_column,
|
col_offset=expr.start_column,
|
||||||
value=target.force_expr,
|
value=target.force_expr,
|
||||||
slice=ast.Index(value=key.force_expr),
|
slice=ast.Index(value=key.force_expr),
|
||||||
ctx=ast.Store())],
|
ctx=ast.Store())],
|
||||||
value=val.force_expr)
|
value=val.force_expr)
|
||||||
|
return ret
|
||||||
|
|
||||||
@builds("with_decorator")
|
@builds("with_decorator")
|
||||||
@checkargs(min=1)
|
@checkargs(min=1)
|
||||||
@ -1178,11 +1200,18 @@ class HyASTCompiler(object):
|
|||||||
thing = self._storeize(self.compile(args.pop(0)))
|
thing = self._storeize(self.compile(args.pop(0)))
|
||||||
|
|
||||||
body = self._compile_branch(expr)
|
body = self._compile_branch(expr)
|
||||||
body += body.expr_as_stmt()
|
|
||||||
|
|
||||||
if not body.stmts:
|
var = self.get_anon_var()
|
||||||
body += ast.Pass(lineno=expr.start_line,
|
name = ast.Name(id=ast_str(var), arg=ast_str(var),
|
||||||
col_offset=expr.start_column)
|
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,
|
the_with = ast.With(context_expr=ctx.force_expr,
|
||||||
lineno=expr.start_line,
|
lineno=expr.start_line,
|
||||||
@ -1194,7 +1223,16 @@ class HyASTCompiler(object):
|
|||||||
the_with.items = [ast.withitem(context_expr=ctx.force_expr,
|
the_with.items = [ast.withitem(context_expr=ctx.force_expr,
|
||||||
optional_vars=thing)]
|
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(",")
|
@builds(",")
|
||||||
def compile_tuple(self, expr):
|
def compile_tuple(self, expr):
|
||||||
@ -1537,6 +1575,8 @@ class HyASTCompiler(object):
|
|||||||
body=body.stmts,
|
body=body.stmts,
|
||||||
orelse=orel.stmts)
|
orelse=orel.stmts)
|
||||||
|
|
||||||
|
ret.contains_yield = body.contains_yield
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@builds("while")
|
@builds("while")
|
||||||
@ -1554,6 +1594,8 @@ class HyASTCompiler(object):
|
|||||||
lineno=expr.start_line,
|
lineno=expr.start_line,
|
||||||
col_offset=expr.start_column)
|
col_offset=expr.start_column)
|
||||||
|
|
||||||
|
ret.contains_yield = body.contains_yield
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@builds(HyList)
|
@builds(HyList)
|
||||||
@ -1597,9 +1639,12 @@ class HyASTCompiler(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
if body.expr:
|
if body.expr:
|
||||||
body += ast.Return(value=body.expr,
|
if body.contains_yield:
|
||||||
lineno=body.expr.lineno,
|
body += body.expr_as_stmt()
|
||||||
col_offset=body.expr.col_offset)
|
else:
|
||||||
|
body += ast.Return(value=body.expr,
|
||||||
|
lineno=body.expr.lineno,
|
||||||
|
col_offset=body.expr.col_offset)
|
||||||
|
|
||||||
if not body.stmts:
|
if not body.stmts:
|
||||||
body += ast.Pass(lineno=expression.start_line,
|
body += ast.Pass(lineno=expression.start_line,
|
||||||
@ -1643,6 +1688,17 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
body = Result()
|
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:
|
if expression:
|
||||||
try:
|
try:
|
||||||
body_expression = iter(expression.pop(0))
|
body_expression = iter(expression.pop(0))
|
||||||
|
@ -60,3 +60,26 @@
|
|||||||
(x)
|
(x)
|
||||||
(assert false))
|
(assert false))
|
||||||
(except [NameError])))
|
(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__)))
|
||||||
|
@ -348,6 +348,11 @@
|
|||||||
(assoc vals "two" "three")
|
(assoc vals "two" "three")
|
||||||
(assert (= (get 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") (= (get vals "one") "two"))))
|
||||||
|
|
||||||
(defn test-pass []
|
(defn test-pass []
|
||||||
"NATIVE: Test pass worksish"
|
"NATIVE: Test pass worksish"
|
||||||
@ -406,6 +411,13 @@
|
|||||||
(with [(open "README.md" "r")] (do)))
|
(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 []
|
(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)))))))))))
|
||||||
|
@ -46,6 +46,36 @@
|
|||||||
"NATIVE: test macro calling a plain function"
|
"NATIVE: test macro calling a plain function"
|
||||||
(assert (= 3 (bar 1 2))))
|
(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
|
; Macro that checks a variable defined at compile or load time
|
||||||
(setv phase "load")
|
(setv phase "load")
|
||||||
(eval-when-compile
|
(eval-when-compile
|
||||||
|
Loading…
x
Reference in New Issue
Block a user