Merge branch 'master' into pr/484

Conflicts:
	AUTHORS
This commit is contained in:
Paul Tagliamonte 2014-02-15 11:57:43 -05:00
commit 2a8a7f3f79
31 changed files with 876 additions and 106 deletions

View File

@ -40,3 +40,5 @@
* Jack Hooper <contact.jhooper@gmail.com>
* Brian McKenna <brian@brianmckenna.org>
* Halit Alptekin <info@halitalptekin.com>
* Richard Parsons <richard.lee.parsons@gmail.com>
* han semaj <sangho.nah@gmail.com>

View File

@ -1,15 +1,15 @@
Hy
==
![](https://raw.github.com/hylang/shyte/18f6925e08684b0e1f52b2cc2c803989cd62cd91/imgs/xkcd.png)
Lisp and Python should love each other. Let's make it happen. [Try it](http://try-hy.appspot.com/).
[![Build Status](https://travis-ci.org/hylang/hy.png?branch=master)](https://travis-ci.org/hylang/hy)
[![Downloads](https://pypip.in/d/hy/badge.png)](https://crate.io/packages/hy)
[![version](https://pypip.in/v/hy/badge.png)](https://crate.io/packages/hy)
[![Coverage Status](https://coveralls.io/repos/hylang/hy/badge.png)](https://coveralls.io/r/hylang/hy)
![](https://raw.github.com/hylang/shyte/18f6925e08684b0e1f52b2cc2c803989cd62cd91/imgs/xkcd.png)
Lisp and Python should love each other. Let's make it happen. [Try it](http://try-hy.appspot.com/).
Hylarious Hacks
---------------
@ -19,6 +19,7 @@ Hylarious Hacks
[Hy IRC bot](https://github.com/hylang/hygdrop)
[miniKanren in Hy](https://github.com/algernon/adderall)
OK, so, why?
------------
@ -30,7 +31,7 @@ Why?
Well, I wrote Hy to help people realize one thing about Python:
It's really goddamn awesome.
It's really awesome.
Oh, and lisps are neat.

BIN
docs/_static/hy-logo-small.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -9,3 +9,4 @@ Contents:
anaphoric
loop
multi

23
docs/contrib/multi.rst Normal file
View File

@ -0,0 +1,23 @@
========
defmulti
========
.. versionadded:: 0.9.13
`defmulti` lets you arity-overload a function by the given number of
args and/or kwargs. Inspired by clojures take on `defn`.
.. code-block:: clj
=> (require hy.contrib.multi)
=> (defmulti fun
... ([a] a)
... ([a b] "a b")
... ([a b c] "a b c"))
=> (fun 1 2 3)
'a b c'
=> (fun a b)
"a b"
=> (fun 1)
1

View File

@ -1,43 +1,26 @@
Welcome to Hy's documentation!
==============================
.. image:: _static/hy-logo-full.png
:alt: Hy logo
.. image:: _static/hy-logo-small.png
:alt: Hy
:align: left
Welcome to `Hy <https://github.com/hylang/hy>`_!
:Try Hy: https://try-hy.appspot.com
:PyPI: https://pypi.python.org/pypi/hy
:Source: https://github.com/hylang/hy
:List: `hylang-discuss <https://groups.google.com/forum/#!forum/hylang-discuss>`_
:IRC: ``#hy`` on Freenode
:Build status:
.. image:: https://secure.travis-ci.org/hylang/hy.png
:alt: Travis CI
:target: http://travis-ci.org/hylang/hy
Hy is a wonderful dialect of Lisp that's embedded in Python.
Since Hy transforms its lisp code into the python Abstract Syntax
Tree, you have the whole beautiful world of python at your fingertips,
in lisp form!
Meet our mascot, "Cuddles":
Since Hy transforms its Lisp code into the Python Abstract Syntax
Tree, you have the whole beautiful world of Python at your fingertips,
in Lisp form!
.. image:: http://fc07.deviantart.net/fs70/i/2013/138/f/0/cuddles_the_hacker_by_doctormo-d65l7lq.png
:alt: Paul riding cuddles into the distance
..
Our old ascii art mascot version
Retained as an easter egg for those who read the docs via .rst!
LET'S CUDDLEFISH
______
_.----'#' # '
,' #' ,# ;
(' (w) _,-'_/
/// / /'.____.'
\|\||/
You can try Hy `in your browser <https://try-hy.appspot.com>`_.
Read more about Hy in these docs!
We're also on IRC! Join
`#hy on irc.freenode.net <http://webchat.freenode.net/?channels=hy>`_!
Documentation Index
===================
@ -49,6 +32,6 @@ Contents:
quickstart
tutorial
hacking
language/index
contrib/index
hacking

View File

@ -762,6 +762,9 @@ of import you can use.
[os.path [exists isdir isfile]]
[sys :as systest])
;; Import all module functions into current namespace
(import [sys [*]])
kwapply
-------

View File

@ -29,6 +29,48 @@ Returns true if argument is iterable and not a string.
False
cons
----
.. versionadded:: 0.9.13
Usage: ``(cons a b)``
Returns a fresh :ref:`cons cell <hycons>` with car `a` and cdr `b`.
.. code-block:: clojure
=> (setv a (cons 'hd 'tl))
=> (= 'hd (car a))
True
=> (= 'tl (cdr a))
True
cons?
-----
.. versionadded:: 0.9.13
Usage: ``(cons? foo)``
Checks whether ``foo`` is a :ref:`cons cell <hycons>`.
.. code-block:: clojure
=> (setv a (cons 'hd 'tl))
=> (cons? a)
True
=> (cons? nil)
False
=> (cons? [1 2 3])
False
.. _dec-fn:
dec
@ -96,6 +138,32 @@ Return True if ``coll`` is empty, i.e. ``(= 0 (len coll))``.
False
.. _every?-fn:
every?
------
.. versionadded:: 0.9.13
Usage: ``(every? pred coll)``
Return True if ``(pred x)`` is logical true for every ``x`` in ``coll``, otherwise False. Return True if ``coll`` is empty.
.. code-block:: clojure
=> (every? even? [2 4 6])
True
=> (every? even? [1 3 5])
False
=> (every? even? [2 4 5])
False
=> (every? even? [])
True
.. _float?-fn:
float?
@ -284,6 +352,28 @@ Contrast with :ref:`iterable?-fn`.
=> (iterator? (iter {:a 1 :b 2 :c 3}))
True
list*
-----
Usage: ``(list* head &rest tail)``
Generate a chain of nested cons cells (a dotted list) containing the
arguments. If the argument list only has one element, return it.
.. code-block:: clojure
=> (list* 1 2 3 4)
(1 2 3 . 4)
=> (list* 1 2 3 [4])
[1, 2, 3, 4]
=> (list* 1)
1
=> (cons? (list* 1 2 3 4))
True
.. _macroexpand-fn:
macroexpand
@ -506,6 +596,32 @@ Return the second member of ``coll``. Equivalent to
1
.. _some-fn:
some
----
.. versionadded:: 0.9.13
Usage: ``(some pred coll)``
Return True if ``(pred x)`` is logical true for any ``x`` in ``coll``, otherwise False. Return False if ``coll`` is empty.
.. code-block:: clojure
=> (some even? [2 4 6])
True
=> (some even? [1 3 5])
False
=> (some even? [1 3 6])
True
=> (some even? [])
False
.. _string?-fn:
string?
@ -863,5 +979,3 @@ Return an iterator from ``coll`` as long as predicate, ``pred`` returns True.
=> (list (take-while neg? [ 1 2 3 -4 5]))
[]

View File

@ -10,5 +10,5 @@ Contents:
cli
api
core
internals
readermacros
internals

View File

@ -172,6 +172,38 @@ keywords, that is keywords used by the language definition inside
function signatures. Lambda-list keywords are symbols starting with a
``&``. The class inherits :ref:`HyString`
.. _hycons:
Cons Cells
==========
``hy.models.cons.HyCons`` is a representation of Python-friendly `cons
cells`_. Cons cells are especially useful to mimic features of "usual"
LISP variants such as Scheme or Common Lisp.
.. _cons cells: http://en.wikipedia.org/wiki/Cons
A cons cell is a 2-item object, containing a ``car`` (head) and a
``cdr`` (tail). In some Lisp variants, the cons cell is the fundamental
building block, and S-expressions are actually represented as linked
lists of cons cells. This is not the case in Hy, as the usual
expressions are made of Python lists wrapped in a
``HyExpression``. However, the ``HyCons`` mimicks the behavior of
"usual" Lisp variants thusly:
- ``(cons something nil)`` is ``(HyExpression [something])``
- ``(cons something some-list)`` is ``((type some-list) (+ [something]
some-list))`` (if ``some-list`` inherits from ``list``).
- ``(get (cons a b) 0)`` is ``a``
- ``(slice (cons a b) 1)`` is ``b``
Hy supports a dotted-list syntax, where ``'(a . b)`` means ``(cons 'a
'b)`` and ``'(a b . c)`` means ``(cons 'a (cons 'b 'c))``. If the
compiler encounters a cons cell at the top level, it raises a
compilation error.
``HyCons`` wraps the passed arguments (car and cdr) in Hy types, to ease
the manipulation of cons cells in a macro context.
Hy Internal Theory
==================

View File

@ -276,7 +276,7 @@ You might notice above that if you have code like:
(body-if-true)
(body-if-false))
But wait! What if you want to execute more than one statment in the
But wait! What if you want to execute more than one statement in the
body of one of these?
You can do the following:
@ -289,7 +289,7 @@ You can do the following:
(print "and why not, let's keep talking about how true it is!))
(print "this one's still simply just false"))
You can see that we used "do" to wrap multiple statments. If you're
You can see that we used "do" to wrap multiple statements. If you're
familiar with other lisps, this is the equivalent of "progn"
elsewhere.
@ -311,7 +311,7 @@ The equivalent in hy would be:
.. code-block:: clj
(for (i (range 10))
(for [i (range 10)]
(print (+ "'i' is now at " (str i))))
@ -330,12 +330,12 @@ Python's context managers ('with' statements) are used like this:
.. code-block:: clj
(with [f (file "/tmp/data.in")]
(with [[f (open "/tmp/data.in")]]
(print (.read f)))
which is equivalent to::
with file("/tmp/data.in") as f:
with open("/tmp/data.in") as f:
print f.read()
And yes, we do have lisp comprehensions! In Python you might do::

View File

@ -32,6 +32,7 @@ from hy.models.symbol import HySymbol # NOQA
from hy.models.float import HyFloat # NOQA
from hy.models.dict import HyDict # NOQA
from hy.models.list import HyList # NOQA
from hy.models.cons import HyCons # NOQA
import hy.importer # NOQA

View File

@ -34,6 +34,7 @@ from hy.models.symbol import HySymbol
from hy.models.float import HyFloat
from hy.models.list import HyList
from hy.models.dict import HyDict
from hy.models.cons import HyCons
from hy.errors import HyCompileError, HyTypeError
@ -597,6 +598,24 @@ class HyASTCompiler(object):
return imports, HyExpression([HySymbol(name),
contents]).replace(form), False
elif isinstance(form, HyCons):
ret = HyExpression([HySymbol(name)])
nimport, contents, splice = self._render_quoted_form(form.car,
level)
if splice:
raise HyTypeError(form, "Can't splice dotted lists yet")
imports.update(nimport)
ret.append(contents)
nimport, contents, splice = self._render_quoted_form(form.cdr,
level)
if splice:
raise HyTypeError(form, "Can't splice the cdr of a cons")
imports.update(nimport)
ret.append(contents)
return imports, ret.replace(form), False
elif isinstance(form, (HySymbol, HyLambdaListKeyword)):
return imports, HyExpression([HySymbol(name),
HyString(form)]).replace(form), False
@ -1645,15 +1664,15 @@ class HyASTCompiler(object):
@builds(HyExpression)
def compile_expression(self, expression):
if expression == []:
return self.compile_list(expression)
# Perform macro expansions
expression = macroexpand(expression, self.module_name)
if not isinstance(expression, HyExpression):
# Go through compile again if the type changed.
return self.compile(expression)
if expression == []:
return self.compile_list(expression)
fn = expression[0]
func = None
if isinstance(fn, HyKeyword):
@ -2036,6 +2055,10 @@ class HyASTCompiler(object):
self.module_name)
return Result()
@builds(HyCons)
def compile_cons(self, cons):
raise HyTypeError(cons, "Can't compile a top-level cons cell")
@builds(HyInteger)
def compile_integer(self, number):
return ast.Num(n=long_type(number),

View File

@ -0,0 +1,50 @@
# -*- encoding: utf-8 -*-
#
# Decorator for defmulti
#
# Copyright (c) 2014 Morten Linderud <mcfoxax@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
from collections import defaultdict
class MultiDispatch(object):
_fns = defaultdict(dict)
def __init__(self, fn):
self.fn = fn
self.__doc__ = fn.__doc__
if fn.__name__ not in self._fns[fn.__module__].keys():
self._fns[fn.__module__][fn.__name__] = {}
values = fn.__code__.co_varnames
self._fns[fn.__module__][fn.__name__][values] = fn
def is_fn(self, v, args, kwargs):
"""Compare the given (checked fn) too the called fn"""
com = list(args) + list(kwargs.keys())
if len(com) == len(v):
return all([kw in com for kw in kwargs.keys()])
return False
def __call__(self, *args, **kwargs):
for i, fn in self._fns[self.fn.__module__][self.fn.__name__].items():
if self.is_fn(i, args, kwargs):
return fn(*args, **kwargs)
raise TypeError("No matching functions with this signature!")

41
hy/contrib/multi.hy Normal file
View File

@ -0,0 +1,41 @@
;; Hy Arity-overloading
;; Copyright (c) 2014 Morten Linderud <mcfoxax@gmail.com>
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.
(import [collections [defaultdict]])
(import [hy.models.string [HyString]])
(defmacro defmulti [name &rest bodies]
(def comment (HyString))
(if (= (type (first bodies)) HyString)
(do (def comment (car bodies))
(def bodies (cdr bodies))))
(def ret `(do))
(.append ret '(import [hy.contrib.dispatch [MultiDispatch]]))
(for [body bodies]
(def let-binds (car body))
(def body (cdr body))
(.append ret
`(with-decorator MultiDispatch (defn ~name ~let-binds ~comment ~@body))))
ret)

58
hy/contrib/walk.hy Normal file
View File

@ -0,0 +1,58 @@
;;; Hy AST walker
;;
;; Copyright (c) 2014 Gergely Nagy <algernon@madhouse-project.org>
;;
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.
(import [hy [HyExpression HyDict]]
[functools [partial]])
(defn walk [inner outer form]
"Traverses form, an arbitrary data structure. Applies inner to each
element of form, building up a data structure of the same type.
Applies outer to the result."
(cond
[(instance? HyExpression form)
(outer (HyExpression (map inner form)))]
[(instance? HyDict form)
(HyDict (outer (HyExpression (map inner form))))]
[(cons? form)
(outer (cons (inner (first form))
(inner (rest form))))]
[(instance? list form)
((type form) (outer (HyExpression (map inner form))))]
[true (outer form)]))
(defn postwalk [f form]
"Performs depth-first, post-order traversal of form. Calls f on each
sub-form, uses f's return value in place of the original."
(walk (partial postwalk f) f form))
(defn prewalk [f form]
"Performs depth-first, pre-order traversal of form. Calls f on each
sub-form, uses f's return value in place of the original."
(walk (partial prewalk f) identity (f form)))
(defn macroexpand-all [form]
"Recursively performs all possible macroexpansions in form."
(prewalk (fn [x]
(if (instance? HyExpression x)
(macroexpand x)
x))
form))

View File

@ -25,6 +25,8 @@
(import [hy._compat [long-type]]) ; long for python2, int for python3
(import [hy.models.cons [HyCons]])
(defn _numeric-check [x]
(if (not (numeric? x))
@ -34,6 +36,14 @@
"Checks whether item is a collection"
(and (iterable? coll) (not (string? coll))))
(defn cons [a b]
"Return a fresh cons cell with car = a and cdr = b"
(HyCons a b))
(defn cons? [c]
"Check whether c can be used as a cons object"
(instance? HyCons c))
(defn cycle [coll]
"Yield an infinite repetition of the items in coll"
(setv seen [])
@ -65,12 +75,12 @@
(defn distinct [coll]
"Return a generator from the original collection with duplicates
removed"
(let [[seen []] [citer (iter coll)]]
(let [[seen (set)] [citer (iter coll)]]
(for* [val citer]
(if (not_in val seen)
(do
(yield val)
(.append seen val))))))
(.add seen val))))))
(defn drop [count coll]
"Drop `count` elements from `coll` and yield back the rest"
@ -98,6 +108,10 @@
(_numeric-check n)
(= (% n 2) 0))
(defn every? [pred coll]
"Return true if (pred x) is logical true for every x in coll, else false"
(all (map pred coll)))
(defn fake-source-positions [tree]
"Fake the source positions for a given tree"
(if (and (iterable? tree) (not (string? tree)))
@ -200,6 +214,12 @@
(try (= x (iter x))
(catch [TypeError] false)))
(defn list* [hd &rest tl]
"Return a dotted list construed from the elements of the argument"
(if (not tl)
hd
(cons hd (apply list* tl))))
(defn macroexpand [form]
"Return the full macro expansion of form"
(import hy.macros)
@ -278,6 +298,10 @@
"Return second item from `coll`"
(get coll 1))
(defn some [pred coll]
"Return true if (pred x) is logical true for any x in coll, else false"
(any (map pred coll)))
(defn string [x]
"Cast x as current string implementation"
(if-python2
@ -321,11 +345,10 @@
(_numeric_check n)
(= n 0))
(def *exports* '[calling-module-name coll? cycle dec distinct
disassemble drop drop-while empty? even? first filter
(def *exports* '[calling-module-name coll? cons cons? cycle dec distinct
disassemble drop drop-while empty? even? every? first filter
flatten float? gensym identity inc instance? integer
integer? integer-char? iterable? iterate iterator?
macroexpand macroexpand-1 neg? nil? none? nth
numeric? odd? pos? remove repeat repeatedly rest
second string string? take take-nth take-while
zero?])
list* macroexpand macroexpand-1 neg? nil? none? nth
numeric? odd? pos? remove repeat repeatedly rest second
some string string? take take-nth take-while zero?])

View File

@ -24,6 +24,7 @@ from functools import wraps
from rply import ParserGenerator
from hy.models.complex import HyComplex
from hy.models.cons import HyCons
from hy.models.dict import HyDict
from hy.models.expression import HyExpression
from hy.models.float import HyFloat
@ -95,9 +96,40 @@ def real_main_empty(p):
return []
def reject_spurious_dots(*items):
"Reject the spurious dots from items"
for list in items:
for tok in list:
if tok == "." and type(tok) == HySymbol:
raise LexException("Malformed dotted list",
tok.start_line, tok.start_column)
@pg.production("paren : LPAREN list_contents RPAREN")
@set_boundaries
def paren(p):
cont = p[1]
# Dotted lists are expressions of the form
# (a b c . d)
# that evaluate to nested cons cells of the form
# (a . (b . (c . d)))
if len(cont) >= 3 and isinstance(cont[-2], HySymbol) and cont[-2] == ".":
reject_spurious_dots(cont[:-2], cont[-1:])
if len(cont) == 3:
# Two-item dotted list: return the cons cell directly
return HyCons(cont[0], cont[2])
else:
# Return a nested cons cell
return HyCons(cont[0], paren([p[0], cont[1:], p[2]]))
# Warn preemptively on a malformed dotted list.
# Only check for dots after the first item to allow for a potential
# attribute accessor shorthand
reject_spurious_dots(cont[1:])
return HyExpression(p[1])

108
hy/models/cons.py Normal file
View File

@ -0,0 +1,108 @@
# Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
from hy.macros import _wrap_value
from hy.models import HyObject
from hy.models.expression import HyExpression
from hy.models.symbol import HySymbol
class HyCons(HyObject):
"""
HyCons: a cons object.
Building a HyCons of something and a HyList really builds a HyList
"""
__slots__ = ["car", "cdr"]
def __new__(cls, car, cdr):
if isinstance(cdr, list):
# Keep unquotes in the cdr of conses
if type(cdr) == HyExpression:
if len(cdr) > 0 and type(cdr[0]) == HySymbol:
if cdr[0] in ("unquote", "unquote_splice"):
return super(HyCons, cls).__new__(cls)
return cdr.__class__([_wrap_value(car)] + cdr)
elif cdr is None:
return HyExpression([_wrap_value(car)])
else:
return super(HyCons, cls).__new__(cls)
def __init__(self, car, cdr):
self.car = _wrap_value(car)
self.cdr = _wrap_value(cdr)
def __getitem__(self, n):
if n == 0:
return self.car
if n == slice(1, None):
return self.cdr
raise IndexError(
"Can only get the car ([0]) or the cdr ([1:]) of a HyCons")
def __setitem__(self, n, new):
if n == 0:
self.car = new
return
if n == slice(1, None):
self.cdr = new
return
raise IndexError(
"Can only set the car ([0]) or the cdr ([1:]) of a HyCons")
def __iter__(self):
yield self.car
try:
iterator = (i for i in self.cdr)
except TypeError:
if self.cdr is not None:
yield self.cdr
raise TypeError("Iteration on malformed cons")
else:
for i in iterator:
yield i
def replace(self, other):
if self.car is not None:
self.car.replace(other)
if self.cdr is not None:
self.cdr.replace(other)
HyObject.replace(self, other)
def __repr__(self):
if isinstance(self.cdr, self.__class__):
return "(%s %s)" % (repr(self.car), repr(self.cdr)[1:-1])
else:
return "(%s . %s)" % (repr(self.car), repr(self.cdr))
def __eq__(self, other):
return (
isinstance(other, self.__class__) and
self.car == other.car and
self.cdr == other.cdr
)

View File

@ -36,5 +36,16 @@ class HyList(HyObject, list):
def __add__(self, other):
return self.__class__(super(HyList, self).__add__(other))
def __getslice__(self, start, end):
return self.__class__(super(HyList, self).__getslice__(start, end))
def __getitem__(self, item):
ret = super(HyList, self).__getitem__(item)
if isinstance(item, slice):
return self.__class__(ret)
return ret
def __repr__(self):
return "[%s]" % (" ".join([repr(x) for x in self]))

View File

@ -0,0 +1,37 @@
;; You need to install the requests package first
(import os.path)
(import requests)
(setv *api-url* "https://api.github.com/{}")
(setv *rst-format* "* `{} <{}>`_")
(setv *missing-names* {"khinsen" "Konrad Hinsen"})
;; We have three concealed members on the hylang organization
;; and GitHub only shows public members if the requester is not
;; an owner of the organization.
(setv *concealed-members* [(, "aldeka" "Karen Rustad")
(, "tuturto" "Tuukka Turto")
(, "cndreisbach" "Clinton N. Dreisbach")])
(defn get-dev-name [login]
(setv name (get (.json (requests.get (.format *api-url* (+ "users/" login)))) "name"))
(if-not name
(.get *missing-names* login)
name))
(setv coredevs (requests.get (.format *api-url* "orgs/hylang/members")))
(setv result (set))
(for [dev (.json coredevs)]
(result.add (.format *rst-format* (get-dev-name (get dev "login"))
(get dev "html_url"))))
(for [(, login name) *concealed-members*]
(result.add (.format *rst-format* name (+ "https://github.com/" login))))
(setv filename (os.path.abspath (os.path.join os.path.pardir
"docs" "coreteam.rst")))
(with [[fobj (open filename "w+")]]
(fobj.write (+ (.join "\n" result) "\n")))

View File

@ -1,44 +0,0 @@
"""
You need to install the requests package first::
$ pip install requests
"""
import os.path
import requests
API_URL = 'https://api.github.com/%s'
RST_FORMAT = '* `%s <%s>`_'
MISSING_NAMES = {
'khinsen': 'Konrad Hinsen',
}
# We have three concealed members on the hylang organization
# and GitHub only shows public members if the requester is not
# an owner of the organization.
CONCEALED_MEMBERS = [
('aldeka', 'Karen Rustad'),
('tuturto', 'Tuukka Turto'),
]
def get_dev_name(login):
name = requests.get(API_URL % 'users/' + login).json()['name']
if not name:
return MISSING_NAMES.get(login)
return name
coredevs = requests.get(API_URL % 'orgs/hylang/members')
result = set()
for dev in coredevs.json():
result.add(RST_FORMAT % (get_dev_name(dev['login']), dev['html_url']))
for login, name in CONCEALED_MEMBERS:
result.add(RST_FORMAT % (name, 'https://github.com/' + login))
filename = os.path.abspath(os.path.join(os.path.pardir,
'docs', 'coreteam.rst'))
with open(filename, 'w+') as fobj:
fobj.write('\n'.join(result) + '\n')

View File

@ -2,6 +2,7 @@
import hy # noqa
from .native_tests.cons import * # noqa
from .native_tests.defclass import * # noqa
from .native_tests.math import * # noqa
from .native_tests.native_macros import * # noqa
@ -16,3 +17,5 @@ from .native_tests.with_test import * # noqa
from .native_tests.contrib.anaphoric import * # noqa
from .native_tests.contrib.loop import * # noqa
from .native_tests.contrib.meth import * # noqa
from .native_tests.contrib.walk import * # noqa
from .native_tests.contrib.multi import * # noqa

View File

@ -475,3 +475,8 @@ def test_attribute_access():
cant_compile("(. foo bar :baz [0] quux [frob])")
cant_compile("(. foo bar baz (0) quux [frob])")
cant_compile("(. foo bar baz [0] quux {frob})")
def test_cons_correct():
"""Ensure cons gets compiled correctly"""
can_compile("(cons a b)")

View File

@ -1,4 +1,5 @@
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
# Copyright (c) 2014 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@ -26,6 +27,8 @@ from hy.models.complex import HyComplex
from hy.models.symbol import HySymbol
from hy.models.string import HyString
from hy.models.dict import HyDict
from hy.models.list import HyList
from hy.models.cons import HyCons
from hy.lex import LexException, PrematureEndOfInput, tokenize
@ -302,3 +305,32 @@ def test_lex_mangling_qmark():
assert entry == [HySymbol("is_foo.bar")]
entry = tokenize(".foo?.bar.baz?")
assert entry == [HySymbol(".is_foo.bar.is_baz")]
def test_simple_cons():
"""Check that cons gets tokenized correctly"""
entry = tokenize("(a . b)")[0]
assert entry == HyCons(HySymbol("a"), HySymbol("b"))
def test_dotted_list():
"""Check that dotted lists get tokenized correctly"""
entry = tokenize("(a b c . (d . e))")[0]
assert entry == HyCons(HySymbol("a"),
HyCons(HySymbol("b"),
HyCons(HySymbol("c"),
HyCons(HySymbol("d"),
HySymbol("e")))))
def test_cons_list():
"""Check that cons of something and a list gets tokenized as a list"""
entry = tokenize("(a . [])")[0]
assert entry == HyList([HySymbol("a")])
assert type(entry) == HyList
entry = tokenize("(a . ())")[0]
assert entry == HyExpression([HySymbol("a")])
assert type(entry) == HyExpression
entry = tokenize("(a b . {})")[0]
assert entry == HyDict([HySymbol("a"), HySymbol("b")])
assert type(entry) == HyDict

56
tests/models/test_cons.py Normal file
View File

@ -0,0 +1,56 @@
# Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
from hy.models.cons import HyCons
def test_cons_slicing():
"""Check that cons slicing works as expected"""
cons = HyCons("car", "cdr")
assert cons[0] == "car"
assert cons[1:] == "cdr"
try:
cons[:]
assert True is False
except IndexError:
pass
try:
cons[1]
assert True is False
except IndexError:
pass
def test_cons_replacing():
"""Check that assigning to a cons works as expected"""
cons = HyCons("foo", "bar")
cons[0] = "car"
assert cons == HyCons("car", "bar")
cons[1:] = "cdr"
assert cons == HyCons("car", "cdr")
try:
cons[:] = "foo"
assert True is False
except IndexError:
pass

View File

@ -2,8 +2,21 @@ from hy.models.list import HyList
def test_list_add():
"""Check that adding two HyLists generates a HyList"""
a = HyList([1, 2, 3])
b = HyList([3, 4, 5])
c = a + b
assert c == [1, 2, 3, 3, 4, 5]
assert c.__class__ == HyList
def test_list_slice():
"""Check that slicing a HyList produces a HyList"""
a = HyList([1, 2, 3, 4])
sl1 = a[1:]
sl5 = a[5:]
assert type(sl1) == HyList
assert sl1 == HyList([2, 3, 4])
assert type(sl5) == HyList
assert sl5 == HyList([])

View File

@ -0,0 +1,63 @@
(defn test-cons-mutability []
"Test the mutability of conses"
(setv tree (cons (cons 1 2) (cons 2 3)))
(setv (car tree) "foo")
(assert (= tree (cons "foo" (cons 2 3))))
(setv (cdr tree) "bar")
(assert (= tree (cons "foo" "bar"))))
(defn test-cons-quoting []
"Test quoting of conses"
(assert (= (cons 1 2) (quote (1 . 2))))
(assert (= (quote foo) (car (quote (foo . bar)))))
(assert (= (quote bar) (cdr (quote (foo . bar))))))
(defn test-cons-behavior []
"NATIVE: test the behavior of cons is consistent"
(defn t= [a b]
(and (= a b) (= (type a) (type b))))
(assert (t= (cons 1 2) '(1 . 2)))
(assert (t= (cons 1 nil) '(1)))
(assert (t= (cons nil 2) '(nil . 2)))
(assert (t= (cons 1 []) [1]))
(setv tree (cons (cons 1 2) (cons 2 3)))
(assert (t= (car tree) (cons 1 2)))
(assert (t= (cdr tree) (cons 2 3))))
(defn test-cons-iteration []
"NATIVE: test the iteration behavior of cons"
(setv x '(0 1 2 3 4 . 5))
(setv it (iter x))
(for* [i (range 6)]
(assert (= i (next it))))
(assert
(= 'success
(try
(do
(next it)
'failurenext)
(except [e TypeError] (if (= e.args (, "Iteration on malformed cons"))
'success
'failureexc))
(except [e Exception] 'failureexc2)))))
(defn test-cons? []
"NATIVE: test behavior of cons?"
(assert (cons? (cons 1 2)))
(assert (cons? '(1 . 2)))
(assert (cons? '(1 2 3 . 4)))
(assert (cons? (list* 1 2 3)))
(assert (not (cons? (cons 1 [2]))))
(assert (not (cons? (list* 1 nil)))))
(defn test-list* []
"NATIVE: test behavior of list*"
(assert (= 1 (list* 1)))
(assert (= (cons 1 2) (list* 1 2)))
(assert (= (cons 1 (cons 2 3)) (list* 1 2 3)))
(assert (= '(1 2 3 4 . 5) (list* 1 2 3 4 5))))

View File

@ -0,0 +1,57 @@
;; Copyright (c) 2014 Morten Linderud <mcfoxax@gmail.com>
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.
(require hy.contrib.multi)
(defn test-basic-multi []
"NATIVE: Test a basic defmulti"
(defmulti fun
([] "Hello!")
([a] a)
([a b] "a b")
([a b c] "a b c"))
(assert (= (fun) "Hello!"))
(assert (= (fun "a") "a"))
(assert (= (fun "a" "b") "a b"))
(assert (= (fun "a" "b" "c") "a b c")))
(defn test-kw-args []
"NATIVE: Test if kwargs are handled correctly"
(defmulti fun
([a] a)
([&optional [a "nop"] [b "p"]] (+ a b)))
(assert (= (fun 1) 1))
(assert (= (apply fun [] {"a" "t"}) "t"))
(assert (= (apply fun ["hello "] {"b" "world"}) "hello world"))
(assert (= (apply fun [] {"a" "hello " "b" "world"}) "hello world")))
(defn test-docs []
"NATIVE: Test if docs are properly handled"
(defmulti fun
"docs"
([a] (print a))
([a b] (print b)))
(assert (= fun.--doc-- "docs")))

View File

@ -0,0 +1,29 @@
(import [hy.contrib.walk [*]])
(def walk-form '(print {"foo" "bar"
"array" [1 2 3 [4]]
"something" (+ 1 2 3 4)
"cons!" (cons 1 2)
"quoted?" '(foo)}))
(defn collector [acc x]
(.append acc x)
nil)
(defn test-walk-identity []
(assert (= (walk identity identity walk-form)
walk-form)))
(defn test-walk []
(let [[acc '()]]
(assert (= (walk (partial collector acc) identity walk-form)
[nil nil]))
(assert (= acc walk-form)))
(let [[acc []]]
(assert (= (walk identity (partial collector acc) walk-form)
nil))
(assert (= acc [walk-form]))))
(defn test-macroexpand-all []
(assert (= (macroexpand-all '(with [a b c] (for [d c] foo)))
'(with* [a] (with* [b] (with* [c] (do (for* [d c] foo))))))))

View File

@ -123,6 +123,13 @@
(try (even? None)
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-every? []
"NATIVE: testing the every? function"
(assert-true (every? even? [2 4 6]))
(assert-false (every? even? [1 3 5]))
(assert-false (every? even? [2 4 5]))
(assert-true (every? even? [])))
(defn test-filter []
"NATIVE: testing the filter function"
(setv res (list (filter pos? [ 1 2 3 -4 5])))
@ -399,6 +406,13 @@
(assert-equal 2 (second [1 2]))
(assert-equal 3 (second [2 3 4])))
(defn test-some []
"NATIVE: testing the some function"
(assert-true (some even? [2 4 6]))
(assert-false (some even? [1 3 5]))
(assert-true (some even? [1 3 6]))
(assert-false (some even? [])))
(defn test-string? []
"NATIVE: testing string?"
(assert-true (string? "foo"))
@ -456,4 +470,3 @@
(assert-equal res [None None])
(setv res (list (take-while (fn [x] (not (none? x))) [1 2 3 4 None 5 6 None 7])))
(assert-equal res [1 2 3 4]))