Merge branch 'develop' into feature/lambda-list-keyword

This commit is contained in:
James King 2013-04-09 15:13:25 -04:00
commit 4d90123506
41 changed files with 2457 additions and 580 deletions

View File

@ -4,3 +4,6 @@
* Christopher Allan Webber <cwebber@dustycloud.org>
* Will Kahn-Greene <willg@bluesock.org>
* James King <james@agentultra.com>
* Julien Danjou <julien@danjou.info>
* Nicolas Dandrimont <nicolas.dandrimont@crans.org>
* Gergely Nagy <algernon@madhouse-project.org>

View File

@ -1,33 +0,0 @@
2013-04-01 Paul Tagliamonte <paultag@debian.org>
* hy/compiler.py (compile_print_expression): Add routines for not creating
an ast.Print in Python 3.x, it's since become an ast.Call.
* Cleaned up the tests a bit, and added some coverage to some uncovered
modules
* hy/compiler.py (compile_do_expression): Add progn as an alias
(compile_def_expression): Add setf / setv as an alias for simple assignment
(compile_list_comrehension): Added list comprehensions.
2013-03-29 Paul Tagliamonte <paultag@debian.org>
* New release, 0.9.3.
2013-03-27 Paul Tagliamonte <paultag@debian.org>
* hy/compiler.py (compile_tuple): Add routines for tuples.
(compile_def_expression): add Tuple checking
(compile_for_expression): same
(compile_with_as_expression): same
2013-03-25 Paul Tagliamonte <paultag@debian.org>
* bin/hy (global): Added readline support to the hy REPL. History should
now be stored in ~/.hy-history
(global): added paren-matching bits, hacked in hard. Better solution?
2013-03-24 Paul Tagliamonte <paultag@debian.org>
* The release before this was version 0.9.2.
* Initial ChangeLog entry. There've been a lot of changes, but I won't bother
to fake history here.

17
LICENSE Normal file
View File

@ -0,0 +1,17 @@
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.

View File

@ -22,6 +22,9 @@ site:
docs:
make -C docs html
upload: r
python setup.py sdist upload
full: d tox site docs
venv:

122
NEWS Normal file
View File

@ -0,0 +1,122 @@
Changes from Hy 0.9.4
[ Syntax Fixes ]
* `try' now accepts `else': (JD)
(try BODY
(except [] BODY)
(else BODY))
Changes from Hy 0.9.4
[ Syntax Fixes ]
* Statements in the `fn' path early will not return anymore. (PT)
* Added "not" as the inline "not" operator. It's advised to still
use "not-in" or "is-not" rather then nesting. (JD)
* `let' macro added (PT)
* Added "~" as the "invert" operator. (JD)
* `catch' now accepts a new format: (JD)
(catch [] BODY)
(catch [Exception] BODY)
(catch [e Exception] BODY)
(catch [e [Exception1 Exception2]] BODY)
* With's syntax was fixed to match the rest of the code. It's now: (PT)
(with [name context-managed-fn] BODY)
(with [context-managed-fn] BODY)
[ Language Changes ]
* Added `and' and `or' (GN)
* Added the tail threading macro (->>) (PT)
* UTF encoded symbols are allowed, but mangled. All Hy source is now
presumed to be UTF-8. (JD + PT)
* Better builtin signature checking (JD)
* If hoisting (for things like printing the return of an if statement)
have been added. '(print (if true true true))' (PT)
[ Documentation ]
* Initial documentation added to the source tree. (PT)
Changes from Hy 0.9.3
[ Syntax Fixes ]
* Nested (do) expressions no longer break Hy (PT)
* `progn' is now a valid alias for `do' (PT)
* `defun' is now a valid alias for `defn' (PT)
* Added two new escapes for \ and " (PT)
[ Language Changes ]
* Show a traceback when a compile-error bubbles up in the Hy REPL (PT)
* `setf' / `setv' added, the behavior of `def` may change in the future.
* `print' no longer breaks in Python 3.x (PT)
* Added `list-comp' list comprehensions. (PT)
* Function hoisting (for things like inline invocation of functions,
e.g. '((fn [] (print "hi!")))' has been added. (PT)
* `while' form added. (ND)
(while [CONDITIONAL] BODY)
[ Documentation ]
* Initial docs added. (WKG + CW)
Changes from Hy 0.9.2
[ General Enhancements ]
* hy.__main__ added, `python -m hy' will now allow a hy shim into existing
Python scripts. (PT)
[ Language Changes ]
* `import-as' added to allow for importing modules. (Amrut Joshi)
* `slice' added to slice up arrays. (PT)
* `with-as' added to allow for context managed bits. (PT)
* `%' added to do Modulo. (PT)
* Tuples added with the '(, foo bar)' syntax. (PT)
* `car' / `first' added. (PT)
* `cdr' / `rest' added. (PT)
* hy --> .pyc compiler added. (PT)
* Completer added for the REPL Readline autocompletion. (PT)
* Merge the `meth' macros into hy.contrib. (PT)
* Changed __repr__ to match Hy source conventions. (PT)
* 2.6 support restored. (PT)
Changes from Hy 0.9.1
[ General Enhancements ]
* Hy REPL added. (PT)
* Doc templates added. (PT)
[ Language Changes ]
* Add `pass' (PT)
* Add `yield' (PT)
* Moved `for' to a macro, and move `foreach' to old `for'. (PT)
* Add the threading macro (`->'). (PT)
* Add "earmufs" in. (tenach)
* Add comments in (PT)
Changes from Hy 0.9.0
[ Language Changes ]
* Add `throw' (PT)
* Add `try' (PT)
* add `catch' (PT)
Changes from Hy 0.8.2
[ Notes ]
* Complete rewrite of old-hy. (PT)

View File

@ -7,6 +7,7 @@ Lisp and Python should love each other. Let's make it happen.
[![Build Status](https://travis-ci.org/paultag/hy.png?branch=master)](https://travis-ci.org/paultag/hy)
Hylarious Hacks
---------------
@ -28,3 +29,13 @@ Well, I wrote Hy to help people realize one thing about Python:
It's really goddamn awesome.
Oh, and lisps are neat.
Project
-------
* Code: https://github.com/paultag/hy
* Docs: http://hy.rtfd.org/
* Quickstart: http://hy.rtfd.org/en/latest/quickstart.html
* Bug reports: We have no bugs! Your bugs are your own! (https://github.com/paultag/hy/issues)
* License: MIT (Expat)

5
TODO
View File

@ -9,9 +9,6 @@
+ Use (def) to imply global foo
- New Builtins:
+ While
- New macros:
+ loop
@ -31,3 +28,5 @@
- core tests (odd? even? true? false?) which
build out to a lambda / function / expr
- add -d flag (debug) for hy REPL based on @jd's try/catch

8
bin/hy
View File

@ -51,8 +51,12 @@ class HyREPL(code.InteractiveConsole):
tokens = process(_machine.nodes)
_machine = Machine(Idle, 1, 0)
_ast = hy_compile(tokens, root=ast.Interactive)
code = compile(_ast, filename, symbol)
try:
_ast = hy_compile(tokens, root=ast.Interactive)
code = compile(_ast, filename, symbol)
except Exception:
self.showtraceback()
return False
self.runcode(code)
return False

121
docs/_static/CC0_1.0.txt vendored Normal file
View File

@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

15
docs/_static/hy_logo-about.txt vendored Normal file
View File

@ -0,0 +1,15 @@
Hy Logo is TeXGyreBonum font with the two parentheses, and two copies
of the lambda character all flipped around and rotated!
Aside from the font design (which was not done by any Hy authors!) the
Hy logo is authored by Christopher Allan Webber in 2013 and released
into the public domain under CC0... see CC0_1.0.txt!
To the extent possible under law, the author(s) have dedicated all
copyright and related and neighboring rights to this logo to
the public domain worldwide. This software is distributed without
any warranty.
You should have received a copy of the CC0 Public Domain
Dedication along with this software. If not, see
<http://creativecommons.org/publicdomain/zero/1.0/>.

111
docs/_static/hy_logo-paths.svg vendored Normal file
View File

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="187.22501"
height="164.2733"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="hy_logo.svg"
inkscape:export-filename="/home/cwebber/devel/hy/docs/_static/hy_logo-paths.png"
inkscape:export-xdpi="200"
inkscape:export-ydpi="200">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="124.29468"
inkscape:cy="108.77929"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1440"
inkscape:window-height="874"
inkscape:window-x="1440"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-148.88306,-183.07397)">
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum"
x="242.85715"
y="449.50504"
id="text2985"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan2987"
x="242.85715"
y="449.50504"
style="font-size:100px" /></text>
<g
transform="scale(-1,1)"
style="font-size:100px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum Italic"
id="text2993">
<path
d="m -195.57143,292.36218 8.4,-48.9 c 0.3,-1.6 0.4,-3.1 0.4,-4.6 0,-11.59999 -10.20001,-17.2 -21.8,-17.2 -10.09999,0 -19.9,4.00001 -19.9,9.3 0,1.7 1.4,2.5 4.1,2.5 2.1,0 4.3,-1.1 6.4,-3.3 3,-3.19999 6.4,-4.8 10.2,-4.8 7.79999,0 11.9,5.40001 11.9,13.4 0,1.5 -0.2,3.2 -0.5,4.9 l -1.4,8.3 c -4.59999,-6.09999 -7.80001,-8.4 -15.3,-8.4 -11.19999,0 -15.8,6.70001 -17.4,15.9 l -5.6,24 c -1.9,8.99999 -7.1,12.1 -12.2,12.1 -1.9,0 -3.8,-0.5 -5.7,-1.5 l -1.7,2.3 c 4.5,3.5 8.8,5.2 12.9,5.2 5.99999,0 12.4,-4.50001 14.8,-13.1 l 7.8,-33.9 c 1.2,-3.39999 4.5,-6.3 8.7,-6.3 6.09999,0 8.6,2.10001 12.9,7.8 l -6.2,36.3 9.2,0"
style="font-style:italic;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum Italic"
id="path3256" />
</g>
<g
transform="scale(1,-1)"
style="font-size:100px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum Italic"
id="text2993-0">
<path
d="m 288.50714,-244.1122 8.4,-48.9 c 0.3,-1.6 0.4,-3.1 0.4,-4.6 0,-11.59999 -10.20001,-17.2 -21.8,-17.2 -10.09999,0 -19.9,4.00001 -19.9,9.3 0,1.7 1.4,2.5 4.1,2.5 2.1,0 4.3,-1.1 6.4,-3.3 3,-3.19999 6.4,-4.8 10.2,-4.8 7.79999,0 11.9,5.40001 11.9,13.4 0,1.5 -0.2,3.2 -0.5,4.9 l -1.4,8.3 c -4.6,-6.09999 -7.80001,-8.4 -15.3,-8.4 -11.19999,0 -15.8,6.70001 -17.4,15.9 l -5.6,24 c -1.9,8.99999 -7.10001,12.1 -12.2,12.1 -1.9,0 -3.8,-0.5 -5.7,-1.5 l -1.7,2.3 c 4.49999,3.5 8.8,5.2 12.9,5.2 5.99999,0 12.4,-4.50001 14.8,-13.1 l 7.8,-33.9 c 1.2,-3.39999 4.5,-6.3 8.7,-6.3 6.09999,0 8.6,2.10001 12.9,7.8 l -6.2,36.3 9.2,0"
style="font-style:italic;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum Italic"
id="path3253" />
</g>
<g
transform="matrix(0.76181009,-0.61556036,0.30983361,1.0623103,0,0)"
style="font-size:140.70762634px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum"
id="text3089">
<path
d="m 119.16228,379.55276 0,-5.34689 c -11.53801,-4.08052 -15.05571,-16.32212 -15.05571,-52.48394 l 0,-6.47256 c 0,-36.16182 3.5177,-48.40342 15.05571,-52.48394 l 0,-5.34689 c -4.64334,0.70354 -6.61326,1.26637 -9.14599,2.53274 -20.121173,9.56811 -19.276948,37.70966 -19.276948,58.53437 0,34.33263 3.236287,46.85565 14.352178,55.57951 4.22122,3.23627 7.3168,4.36194 14.07076,5.4876"
style=""
id="path3250" />
</g>
<g
transform="matrix(0.76181009,-0.61556036,0.30983361,1.0623103,0,0)"
style="font-size:140.70762634px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum"
id="text3089-6">
<path
d="m 260.12941,384.13592 c 0,-34.61404 -3.23629,-46.85565 -14.35218,-55.57951 -4.22122,-3.23628 -7.3168,-4.36194 -14.07076,-5.4876 l 0,5.34689 c 11.53801,4.08052 15.05571,16.32212 15.05571,52.48394 l 0,6.47255 c 0,36.16183 -3.5177,48.40343 -15.05571,52.48395 l 0,5.34689 c 4.64334,-0.70354 6.61326,-1.26637 9.14599,-2.53274 20.40259,-9.56811 19.27695,-38.69462 19.27695,-58.53437"
style=""
id="path3247" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
docs/_static/hy_logo-smaller.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
docs/_static/hy_logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

125
docs/_static/hy_logo.svg vendored Normal file
View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="187.22501"
height="164.2733"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="hy_logo2.svg"
inkscape:export-filename="/home/cwebber/devel/hy/docs/_static/hy_logo4.png"
inkscape:export-xdpi="200"
inkscape:export-ydpi="200">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="124.29468"
inkscape:cy="108.77929"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1440"
inkscape:window-height="874"
inkscape:window-x="1440"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-148.88306,-183.07397)">
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum"
x="242.85715"
y="449.50504"
id="text2985"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan2987"
x="242.85715"
y="449.50504"
style="font-size:100px" /></text>
<text
xml:space="preserve"
style="font-size:100px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum Italic"
x="-253.87143"
y="292.36218"
id="text2993"
sodipodi:linespacing="125%"
transform="scale(-1,1)"><tspan
sodipodi:role="line"
id="tspan2995"
x="-253.87143"
y="292.36218"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum Italic">λ</tspan></text>
<text
xml:space="preserve"
style="font-size:100px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum Italic"
x="230.20714"
y="-244.1122"
id="text2993-0"
sodipodi:linespacing="125%"
transform="scale(1,-1)"><tspan
sodipodi:role="line"
id="tspan2995-6"
x="230.20714"
y="-244.1122"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum Italic">λ</tspan></text>
<text
xml:space="preserve"
style="font-size:140.70762634px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum"
x="80.045563"
y="358.30591"
id="text3089"
sodipodi:linespacing="125%"
transform="matrix(0.76181009,-0.61556036,0.30983361,1.0623103,0,0)"><tspan
sodipodi:role="line"
id="tspan3091"
x="80.045563"
y="358.30591">(</tspan></text>
<text
xml:space="preserve"
style="font-size:140.70762634px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeXGyreBonum;-inkscape-font-specification:TeXGyreBonum"
x="229.31444"
y="423.95618"
id="text3089-6"
sodipodi:linespacing="125%"
transform="matrix(0.76181009,-0.61556036,0.30983361,1.0623103,0,0)"><tspan
sodipodi:role="line"
id="tspan3091-5"
x="229.31444"
y="423.95618">)</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

68
docs/hacking.rst Normal file
View File

@ -0,0 +1,68 @@
===============
Hacking on hy
===============
Join our hyve!
==============
Please come hack on hy!
Please come hang out with us on ``#hy`` on ``irc.freenode.net``!
Please talk about it on Twitter with the ``#hy`` hashtag!
Please blog about it!
Please don't spraypaint it on your neighbor's fence (without asking nicely)!
Hack!
=====
Do this:
1. create a `Python virtual environment
<https://pypi.python.org/pypi/virtualenv>`_
2. (optional) go to https://github.com/paultag/hy and fork it
3. get the source code::
$ git clone git://github.com/paultag/hy.git
(or use your fork)
4. install for hacking::
$ python setup.py develop
5. install other develop-y requirements::
$ pip install -r requirements-dev.txt
6. do awesome things; make someone shriek in delight/disgust at what
you have wrought
Test!
=====
Tests are located in ``tests/``. We use `nose
<https://nose.readthedocs.org/en/latest/>`_.
To run the tests::
$ nosetests
Write tests---tests are good!
Document!
=========
Documentation is located in ``docs/``. We use `Sphinx
<http://sphinx-doc.org/>`_.
To build the docs in html::
$ cd docs
$ make html
Write docs---docs are good! Even this doc!

View File

@ -1,6 +1,10 @@
Welcome to Hy's documentation!
==============================
.. image:: _static/hy_logo-smaller.png
:alt: Hy logo
:align: left
Welcome to `Hy <https://github.com/paultag/hy>`_!
Hy is a wonderful dialect of Lisp that's embedded in Python.
Since Hy transforms its lisp code into the python Abstract Syntax
@ -32,5 +36,7 @@ Contents:
.. toctree::
:maxdepth: 3
quickstart
hacking
tutorial
language/index
.. library/index

101
docs/language/api.rst Normal file
View File

@ -0,0 +1,101 @@
=================
Hy (the language)
=================
.. warning::
This is incomplete; please consider contributing to the documentation
effort.
Theory of Hy
============
Hy maintains, over everything else, 100% compatibility in both directions
with Python it's self. All Hy code follows a few simple rules. Memorize
this, it's going to come in handy.
These rules help make sure code is idiomatic and interface-able in both
languages.
* Symbols in earmufs will be translated to the uppercased version of that
string. For example, `*foo*` will become `FOO`.
* UTF-8 entities will be encoded using
`punycode <http://en.wikipedia.org/wiki/Punycode>`_ and prefixed with
`__hy_`. For instance, `⚘` will become `__hy_w7h`, and `♥` will become
`__hy_g6h`.
* Symbols that contain dashes will have them replaced with underscores. For
example, `render-template` will become `render_template`.
Builtins
========
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.
do / progn
----------
the `do` or `progn` forms can be used in full code branches. What that means
is basically `(do)` and `(progn)` can only be used where a Python expression
can be used. These forms don't actually allow you to break Pythonic internals
such as `lambda` or `list-comp`, where you can only have one expression.
Some example usage
.. code-block:: clj
(if true
(do (print "Side effects rock!")
(print "Yeah, really!")))
`do` can accept any number of arguments, from 1 to n.
throw / raise
-------------
the `throw` or `raise` forms can be used to raise an Exception at runtime.
Example usage
.. code-block:: clj
(throw)
; re-rase the last exception
(throw IOError)
; Throw an IOError
(throw (IOError "foobar"))
; Throw an IOError("foobar")
`throw` can acccept a single argument (an `Exception` class or instance), or
no arguments to re-raise the last Exception.
try
---
.. TODO::
Document the else / finally syntax.
the `try` form is used to start a `try` / `catch` block. The form is used
as follows
.. code-block:: clj
(try
(error-prone-function)
(catch [e SomeException] (err "It sucks!")))
`try` must contain at least one `catch` block, and may optionally have an
`else` or `finally` block.

View File

@ -1,392 +1,11 @@
Language Spec
=============
This bit covers a bit about Hy's lovable quirks and eccentricities.
Documentation Index
===================
In a nutshell, Hy is a lisp dialect, but one that converts its
structure into Python... literally a conversion into Python's abstract
syntax tree! (Or to put it in more crude terms, Hy is lisp-stick on a
python!)
Contents:
This is pretty cool because it means Hy is several things:
.. toctree::
:maxdepth: 3
- A lisp that feels very pythonic
- For lispers, a great way to use lisp's crazy powers but in the wide
world of Python's libraries (why yes, you now can write a Django
application in lisp!)
- For pythonistas, a great way to start exploring lisp, from the
comfort of python!
- For everyone: a pleasant language that has a lot of neat ideas!
Basic intro to lisp for pythonistas
-----------------------------------
Okay, maybe you've never used lisp before, but you've used python!
A "hello world" in hy is actually super simple. Let's try it:
.. code-block:: clj
(print "hello world")
See? Easy! As you may have guessed, this is the same as the python
version of::
print "hello world"
To add up some super simple math, we could do:
.. code-block:: clj
(+ 1 3)
Which would return 4 and would be the equivalent of:
.. code-block:: clj
1 + 3
What you'll notice is that the first item in the list is the function
being called and the rest of the arguments are the arguments being
passed in. In fact, in hy (as with most lisps) we can pass in
multiple arguments to the plus operator:
.. code-block:: clj
(+ 1 3 55)
Which would return 59.
Maybe you've heard of lisp before but don't know much about it. Lisp
isn't as hard as you might think, and hy inherits from python, so hy
is a great way to start learning lisp. The main thing that's obvious
about lisp is that there's a lot of parentheses. This might seem
confusing at first, but it isn't so hard. Let's look at some simple
math that's wrapped in a bunch of parentheses that we could enter into
the hy interpreter:
.. code-block:: clj
(setv result (- (/ (+ 1 3 88) 2) 8))
This would return 37. But why? Well, we could look at the equivalent
expression in python::
result = ((1 + 3 + 88) / 2) - 8
If you were to try to figure out how the above were to work in python,
you'd of course figure out the results by solving each inner
parenthesis. That's the same basic idea in hy. Let's try this
exercise first in python::
result = ((1 + 3 + 88) / 2) - 8
# simplified to...
result = (92 / 2) - 8
# simplified to...
result = 46 - 8
# simplified to...
result = 38
Now let's try the same thing in hy:
.. code-block:: clj
(setv result (- (/ (+ 1 3 88) 2) 8))
; simplified to...
(setv result (- (/ 92 2) 8))
; simplified to...
(setv result (- 46 8))
; simplified to...
(setv result 38)
As you probably guessed, this last expression with "setv" means to
assign the variable "result" to 38.
See? Not too hard!
This is the basic premise of lisp... lisp stands for "list
processing"... this means that the structure of the program is
actually lists of lists. (If you're familiar with python lists,
imagine the entire same structure as above but with square brackets
instead, any you'll be able to see the structure above as both a
program and a datastructure.) This is easier to understand with more
examples, so let's write a simple python program and test it and then
show the equivalent hy program::
def simple_conversation():
print "Hello! I'd like to get to know you. Tell me about yourself!"
name = raw_input("What is your name? ")
age = raw_input("What is your age? ")
print "Hello " + name + "! I see you are " + age + " years old."
simple_conversation()
If we ran this program, it might go like::
Hello! I'd like to get to know you. Tell me about yourself!
What is your name? Gary
What is your age? 38
Hello Gary! I see you are 38 years old.
Now let's look at the equivalent hy program:
.. code-block:: clj
(defn simple-conversation []
(print "Hello! I'd like to get to know you. Tell me about yourself!")
(setv name (raw_input "What is your name? "))
(setv age (raw_input "What is your age? "))
(print (+ "Hello " name "! I see you are "
age " years old.")))
(simple-conversation)
If you look at the above program, as long as you remember that the
first element in each list of the program is the function (or
macro... we'll get to those later) being called and that the rest are
the arguments, it's pretty easy to figure out what this all means.
(As you probably also guessed, defn is the hy method of defining
methods.)
Still, lots of people find this confusing at first because there's so
many parentheses, but there are plenty of things that can help make
this easier: keep indentation nice and use an editor with parenthesis
matching (this will help you figure out what each parenthesis pairs up
with) and things will start to feel comfortable.
There are some advantages to having a code structure that's actually a
very simple datastructure as the core of lisp is based on. For one
thing, it means that your programs are easy to parse and that the
entire actual structure of the program is very clearly exposed to you.
(There's an extra step in hy where the structure you see is converted
to python's own representations... in more "pure" lisps such as common
lisp or emacs lisp, the data structure you see for the code and the
data structure that is executed is much more literally close.)
Another implication of this is macros: if a program's structure is a
simple data structure, that means you can write code that can write
code very easily, meaning that implementing entirely new language
features can be very fast. Previous to hy, this wasn't very possible
for python programmers... now you too can make use of macros'
incredible power (just be careful to not aim them footward)!
Hy is python flavored lisp (or vice versa?)
-------------------------------------------
Hy converts to python's own abstract syntax tree, so you'll soon start
to find that all the familiar power of python is at your fingertips.
You have full access to python's data types and standard library in
hy. Let's experiment with this in the hy interpreter::
=> [1 2 3]
[1, 2, 3]
=> {"dog" "bark"
... "cat" "meow"}
...
{'dog': 'bark', 'cat': 'meow'}
(You may notice that at present, the common lisp method of quoting
things like so:
.. code-block:: clj
'(1 2 3)
does not work. Instead, use square brackets as above.)
You also have access to all the builtin types' nice methods::
=> (.strip " fooooo ")
"fooooo"
What's this? Yes indeed, this is precisely the same as::
" fooooo ".strip()
That's right... lisp with dot notation! If we have this string
assigned as a variable, we can also do the following:
.. code-block:: clj
(setv this-string " fooooo ")
(this-string.strip)
What about conditionals?:
.. code-block:: clj
(if (try-some-thing)
(print "this is if true")
(print "this is if false"))
As you can tell above, the first argument to if is a truth test, the
second argument is a body if true, and the third argument (optional!)
is if false (ie, "else"!).
If you need to do more complex conditionals, you'll find that you
don't have elif available in hy. Instead, you should use something
called "cond". In python, you might do something like::
somevar = 33
if somevar > 50:
print "That variable is too big!"
elif somevar < 10:
print "That variable is too small!"
else:
print "That variable is jussssst right!"
In hy, you would do:
.. code-block:: clj
(cond
((> somevar 50)
(print "That variable is too big!"))
((< somevar 10)
(print "That variable is too small!"))
(true
(print "That variable is jussssst right!")))
What you'll notice is that cond switches off between a some statement
that is executed and checked conditionally for true or falseness, and
then a bit of code to execute if it turns out to be true. You'll also
notice that the "else" is implemented at the end simply by checking
for "true"... that's because true will always be true, so if we get
this far, we'll always run that one!
You might notice above that if you have code like:
.. code-block:: clj
(if some-condition
(body-if-true)
(body-if-false))
But wait! What if you want to execute more than one statment in the
body of one of these?
You can do the following:
.. code-block:: clj
(if (try-some-thing)
(do
(print "this is if true")
(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
familiar with other lisps, this is the equivalent of "progn"
elsewhere.
Comments start with semicolons:
.. code-block:: clj
(print "this will run")
; (print "but this will not")
(+ 1 2 3) ; we'll execute the addition, but not this comment!
Looping is not hard but has a kind of special structure. In python,
we might do::
for i in range(10):
print "'i' is now at " + str(i)
The equivalent in hy would be:
.. code-block:: clj
(for (i (range 10))
(print (+ "'i' is now at " (str i))))
You can also import and make use of various python libraries. For
example:
.. code-block:: clj
(import os)
(if (os.path.isdir "/tmp/somedir")
(os.mkdir "/tmp/somedir/anotherdir")
(print "Hey, that path isn't there!"))
Comments start with semicolons:
.. code-block:: clj
(print "this will run")
; (print "but this will not")
(+ 1 2 3) ; we'll execute the addition, but not this comment!
And yes, we do have lisp comprehensions! In Python you might do::
odds_squared = [
pow(num, 2)
for num in range(100)
if num % 2 == 1]
In hy, you could do these like:
.. code-block:: clj
; and a little more complex
(setv odds-squared
(list-comp
(pow num 2)
(num (range 100))
(= (% num 2) 1))
Protips!
--------
Hy also features something known as the "threading macro", a really neat
feature of Clojure's. The "threading macro" (written as "->"), is used
to avoid deep nesting of expressions.
The threading macro inserts each expression into the next expression's first
argument place.
Let's take the classic:
.. code-block:: clj
(loop (print (eval (read))))
Rather then write it like that, we can write it as follows:
.. code-block:: clj
(-> (read) (eval) (print) (loop))
Now, using `python-sh <http://amoffat.github.com/sh/>`_, we can show
how the threading macro (because of python-sh's setup) can be used like
a pipe:
.. code-block:: clj
=> (import-from sh cat grep wc)
=> (-> (cat "/usr/share/dict/words") (grep "-E" "^hy") (wc "-l"))
210
Which, of course, expands out to:
.. code-block:: clj
(wc (grep (cat "/usr/share/dict/words") "-E" "^hy") "-l")
Much more readable, no! Use the threading macro!
TODO
----
- How do I define classes?
- Blow your mind with macros!
- Where's my banana???
api
internals

View File

@ -0,0 +1,27 @@
=========================
Internal Hy Documentation
=========================
.. info::
These bits are for folks who hack on Hy it's self, mostly!
Hy Models
=========
.. TODO::
Write this.
Hy Macros
=========
.. TODO::
Write this.
Hy Compiler Builtins
====================
.. TODO::
Write this.

38
docs/quickstart.rst Normal file
View File

@ -0,0 +1,38 @@
==========
Quickstart
==========
HOW TO GET HY REAL FAST:
1. create a `Python virtual environment
<https://pypi.python.org/pypi/virtualenv>`_
2. activate your Python virtual environment
3. ``pip install hy``
4. start a REPL with ``hy``
5. type stuff in the REPL::
=> (print "Hy!")
Hy!
=> (defn salutationsnm [name] (print (+ "Hy " name "!")))
=> (salutationsnm "YourName")
Hy YourName!
etc
6. hit CTRL-D when you're done
OMG! That's amazing! I want to write a hy program.
7. open up an elite programming editor
8. type::
(print "i was going to code in python syntax, but then i got hy")
9. save as ``test_program_of_awesome.hy``
10. run::
hy test_program_of_awesome.hy
11. take a deep breath so as to not hyperventilate
12. smile villainously and sneak off to your hydeaway and do
unspeakable things

414
docs/tutorial.rst Normal file
View File

@ -0,0 +1,414 @@
========
Tutorial
========
Welcome to the Hy tutorial!
In a nutshell, Hy is a lisp dialect, but one that converts its
structure into Python... literally a conversion into Python's abstract
syntax tree! (Or to put it in more crude terms, Hy is lisp-stick on a
python!)
This is pretty cool because it means Hy is several things:
- A lisp that feels very pythonic
- For lispers, a great way to use lisp's crazy powers but in the wide
world of Python's libraries (why yes, you now can write a Django
application in lisp!)
- For pythonistas, a great way to start exploring lisp, from the
comfort of python!
- For everyone: a pleasant language that has a lot of neat ideas!
Basic intro to lisp for pythonistas
===================================
Okay, maybe you've never used lisp before, but you've used python!
A "hello world" in hy is actually super simple. Let's try it:
.. code-block:: clj
(print "hello world")
See? Easy! As you may have guessed, this is the same as the python
version of::
print "hello world"
To add up some super simple math, we could do:
.. code-block:: clj
(+ 1 3)
Which would return 4 and would be the equivalent of:
.. code-block:: clj
1 + 3
What you'll notice is that the first item in the list is the function
being called and the rest of the arguments are the arguments being
passed in. In fact, in hy (as with most lisps) we can pass in
multiple arguments to the plus operator:
.. code-block:: clj
(+ 1 3 55)
Which would return 59.
Maybe you've heard of lisp before but don't know much about it. Lisp
isn't as hard as you might think, and hy inherits from python, so hy
is a great way to start learning lisp. The main thing that's obvious
about lisp is that there's a lot of parentheses. This might seem
confusing at first, but it isn't so hard. Let's look at some simple
math that's wrapped in a bunch of parentheses that we could enter into
the hy interpreter:
.. code-block:: clj
(setv result (- (/ (+ 1 3 88) 2) 8))
This would return 37. But why? Well, we could look at the equivalent
expression in python::
result = ((1 + 3 + 88) / 2) - 8
If you were to try to figure out how the above were to work in python,
you'd of course figure out the results by solving each inner
parenthesis. That's the same basic idea in hy. Let's try this
exercise first in python::
result = ((1 + 3 + 88) / 2) - 8
# simplified to...
result = (92 / 2) - 8
# simplified to...
result = 46 - 8
# simplified to...
result = 38
Now let's try the same thing in hy:
.. code-block:: clj
(setv result (- (/ (+ 1 3 88) 2) 8))
; simplified to...
(setv result (- (/ 92 2) 8))
; simplified to...
(setv result (- 46 8))
; simplified to...
(setv result 38)
As you probably guessed, this last expression with "setv" means to
assign the variable "result" to 38.
See? Not too hard!
This is the basic premise of lisp... lisp stands for "list
processing"... this means that the structure of the program is
actually lists of lists. (If you're familiar with python lists,
imagine the entire same structure as above but with square brackets
instead, any you'll be able to see the structure above as both a
program and a datastructure.) This is easier to understand with more
examples, so let's write a simple python program and test it and then
show the equivalent hy program::
def simple_conversation():
print "Hello! I'd like to get to know you. Tell me about yourself!"
name = raw_input("What is your name? ")
age = raw_input("What is your age? ")
print "Hello " + name + "! I see you are " + age + " years old."
simple_conversation()
If we ran this program, it might go like::
Hello! I'd like to get to know you. Tell me about yourself!
What is your name? Gary
What is your age? 38
Hello Gary! I see you are 38 years old.
Now let's look at the equivalent hy program:
.. code-block:: clj
(defn simple-conversation []
(print "Hello! I'd like to get to know you. Tell me about yourself!")
(setv name (raw_input "What is your name? "))
(setv age (raw_input "What is your age? "))
(print (+ "Hello " name "! I see you are "
age " years old.")))
(simple-conversation)
If you look at the above program, as long as you remember that the
first element in each list of the program is the function (or
macro... we'll get to those later) being called and that the rest are
the arguments, it's pretty easy to figure out what this all means.
(As you probably also guessed, defn is the hy method of defining
methods.)
Still, lots of people find this confusing at first because there's so
many parentheses, but there are plenty of things that can help make
this easier: keep indentation nice and use an editor with parenthesis
matching (this will help you figure out what each parenthesis pairs up
with) and things will start to feel comfortable.
There are some advantages to having a code structure that's actually a
very simple datastructure as the core of lisp is based on. For one
thing, it means that your programs are easy to parse and that the
entire actual structure of the program is very clearly exposed to you.
(There's an extra step in hy where the structure you see is converted
to python's own representations... in more "pure" lisps such as common
lisp or emacs lisp, the data structure you see for the code and the
data structure that is executed is much more literally close.)
Another implication of this is macros: if a program's structure is a
simple data structure, that means you can write code that can write
code very easily, meaning that implementing entirely new language
features can be very fast. Previous to hy, this wasn't very possible
for python programmers... now you too can make use of macros'
incredible power (just be careful to not aim them footward)!
Hy is python flavored lisp (or vice versa?)
===========================================
Hy converts to python's own abstract syntax tree, so you'll soon start
to find that all the familiar power of python is at your fingertips.
You have full access to python's data types and standard library in
hy. Let's experiment with this in the hy interpreter::
=> [1 2 3]
[1, 2, 3]
=> {"dog" "bark"
... "cat" "meow"}
...
{'dog': 'bark', 'cat': 'meow'}
(You may notice that at present, the common lisp method of quoting
things like so:
.. code-block:: clj
'(1 2 3)
does not work. Instead, use square brackets as above.)
You also have access to all the builtin types' nice methods::
=> (.strip " fooooo ")
"fooooo"
What's this? Yes indeed, this is precisely the same as::
" fooooo ".strip()
That's right... lisp with dot notation! If we have this string
assigned as a variable, we can also do the following:
.. code-block:: clj
(setv this-string " fooooo ")
(this-string.strip)
What about conditionals?:
.. code-block:: clj
(if (try-some-thing)
(print "this is if true")
(print "this is if false"))
As you can tell above, the first argument to if is a truth test, the
second argument is a body if true, and the third argument (optional!)
is if false (ie, "else"!).
If you need to do more complex conditionals, you'll find that you
don't have elif available in hy. Instead, you should use something
called "cond". In python, you might do something like::
somevar = 33
if somevar > 50:
print "That variable is too big!"
elif somevar < 10:
print "That variable is too small!"
else:
print "That variable is jussssst right!"
In hy, you would do:
.. code-block:: clj
(cond
((> somevar 50)
(print "That variable is too big!"))
((< somevar 10)
(print "That variable is too small!"))
(true
(print "That variable is jussssst right!")))
What you'll notice is that cond switches off between a some statement
that is executed and checked conditionally for true or falseness, and
then a bit of code to execute if it turns out to be true. You'll also
notice that the "else" is implemented at the end simply by checking
for "true"... that's because true will always be true, so if we get
this far, we'll always run that one!
You might notice above that if you have code like:
.. code-block:: clj
(if some-condition
(body-if-true)
(body-if-false))
But wait! What if you want to execute more than one statment in the
body of one of these?
You can do the following:
.. code-block:: clj
(if (try-some-thing)
(do
(print "this is if true")
(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
familiar with other lisps, this is the equivalent of "progn"
elsewhere.
Comments start with semicolons:
.. code-block:: clj
(print "this will run")
; (print "but this will not")
(+ 1 2 3) ; we'll execute the addition, but not this comment!
Looping is not hard but has a kind of special structure. In python,
we might do::
for i in range(10):
print "'i' is now at " + str(i)
The equivalent in hy would be:
.. code-block:: clj
(for (i (range 10))
(print (+ "'i' is now at " (str i))))
You can also import and make use of various python libraries. For
example:
.. code-block:: clj
(import os)
(if (os.path.isdir "/tmp/somedir")
(os.mkdir "/tmp/somedir/anotherdir")
(print "Hey, that path isn't there!"))
Comments start with semicolons:
.. code-block:: clj
(print "this will run")
; (print "but this will not")
(+ 1 2 3) ; we'll execute the addition, but not this comment!
And yes, we do have lisp comprehensions! In Python you might do::
odds_squared = [
pow(num, 2)
for num in range(100)
if num % 2 == 1]
In hy, you could do these like:
.. code-block:: clj
(setv odds-squared
(list-comp
(pow num 2)
(num (range 100))
(= (% num 2) 1)))
.. code-block:: clj
; And, an example stolen shamelessly from a Clojure page:
; Let's list all the blocks of a Chessboard:
(list-comp
(, x y)
(x (range 9)
y "ABCDEFGH"))
; [(0, 'A'), (0, 'B'), (0, 'C'), (0, 'D'), (0, 'E'), (0, 'F'), (0, 'G'), (0, 'H'),
; (1, 'A'), (1, 'B'), (1, 'C'), (1, 'D'), (1, 'E'), (1, 'F'), (1, 'G'), (1, 'H'),
; (2, 'A'), (2, 'B'), (2, 'C'), (2, 'D'), (2, 'E'), (2, 'F'), (2, 'G'), (2, 'H'),
; (3, 'A'), (3, 'B'), (3, 'C'), (3, 'D'), (3, 'E'), (3, 'F'), (3, 'G'), (3, 'H'),
; (4, 'A'), (4, 'B'), (4, 'C'), (4, 'D'), (4, 'E'), (4, 'F'), (4, 'G'), (4, 'H'),
; (5, 'A'), (5, 'B'), (5, 'C'), (5, 'D'), (5, 'E'), (5, 'F'), (5, 'G'), (5, 'H'),
; (6, 'A'), (6, 'B'), (6, 'C'), (6, 'D'), (6, 'E'), (6, 'F'), (6, 'G'), (6, 'H'),
; (7, 'A'), (7, 'B'), (7, 'C'), (7, 'D'), (7, 'E'), (7, 'F'), (7, 'G'), (7, 'H'),
; (8, 'A'), (8, 'B'), (8, 'C'), (8, 'D'), (8, 'E'), (8, 'F'), (8, 'G'), (8, 'H')]
Protips!
========
Hy also features something known as the "threading macro", a really neat
feature of Clojure's. The "threading macro" (written as "->"), is used
to avoid deep nesting of expressions.
The threading macro inserts each expression into the next expression's first
argument place.
Let's take the classic:
.. code-block:: clj
(loop (print (eval (read))))
Rather then write it like that, we can write it as follows:
.. code-block:: clj
(-> (read) (eval) (print) (loop))
Now, using `python-sh <http://amoffat.github.com/sh/>`_, we can show
how the threading macro (because of python-sh's setup) can be used like
a pipe:
.. code-block:: clj
=> (import-from sh cat grep wc)
=> (-> (cat "/usr/share/dict/words") (grep "-E" "^hy") (wc "-l"))
210
Which, of course, expands out to:
.. code-block:: clj
(wc (grep (cat "/usr/share/dict/words") "-E" "^hy") "-l")
Much more readable, no! Use the threading macro!
TODO
====
- How do I define classes?
- Blow your mind with macros!
- Where's my banana???
- Mention that you can import .hy files in .py files and vice versa!

View File

@ -0,0 +1,12 @@
(import-from concurrent.futures ThreadPoolExecutor as-completed)
(import-from random randint)
(import-from sh sleep)
(defn task-to-do [] (sleep (randint 1 5)))
(with-as (ThreadPoolExecutor 10) executor
(setf jobs (list-comp (.submit executor task-to-do) (x (range 0 10))))
(for (future (as-completed jobs))
(.result future)))

View File

@ -20,7 +20,7 @@
__appname__ = "hy"
__version__ = "0.9.3"
__version__ = "0.9.5"
import hy.importer # NOQA

View File

@ -1,4 +1,7 @@
# -*- encoding: utf-8 -*-
#
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
# Copyright (c) 2013 Julien Danjou <julien@danjou.info>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@ -28,17 +31,48 @@ from hy.models.symbol import HySymbol
from hy.models.list import HyList
from hy.models.dict import HyDict
from hy.util import flatten_literal_list
import codecs
import ast
import sys
class HyCompileError(HyError):
pass
def __init__(self, exception,
start_line=0, start_column=0):
self.exception = exception
self.start_line = start_line
self.start_column = start_column
def __str__(self):
if self.start_line == 0:
return("Internal Compiler Bug\n%s: %s"
% (self.exception.__class__.__name__,
self.exception))
return ("Compilation error at line %d, column %d\n%s: %s"
% (self.start_line, self.start_column,
self.exception.__class__.__name__,
self.exception))
_compile_table = {}
def ast_str(foobar):
if sys.version_info[0] >= 3:
return str(foobar)
try:
return str(foobar)
except UnicodeEncodeError:
pass
enc = codecs.getencoder('punycode')
foobar, _ = enc(foobar)
return "__hy_%s" % (str(foobar).replace("-", "_"))
def builds(_type):
def _dec(fn):
_compile_table[_type] = fn
@ -49,6 +83,38 @@ def builds(_type):
return _dec
def _raise_wrong_args_number(expression, error):
err = TypeError(error % (expression.pop(0),
len(expression)))
err.start_line = expression.start_line
err.start_column = expression.start_column
raise err
def checkargs(exact=None, min=None, max=None):
def _dec(fn):
def checker(self, expression):
if exact is not None and (len(expression) - 1) != exact:
_raise_wrong_args_number(expression,
"`%%s' needs %d arguments, got %%d" %
exact)
if min is not None and (len(expression) - 1) < min:
_raise_wrong_args_number(
expression,
"`%%s' needs at least %d arguments, got %%d" % (min))
if max is not None and (len(expression) - 1) > max:
_raise_wrong_args_number(
expression,
"`%%s' needs at most %d arguments, got %%d" % (max))
return fn(self, expression)
return checker
return _dec
class HyASTCompiler(object):
def __init__(self):
@ -56,14 +122,30 @@ class HyASTCompiler(object):
self.anon_fn_count = 0
def compile(self, tree):
for _type in _compile_table:
if type(tree) == _type:
return _compile_table[_type](self, tree)
try:
for _type in _compile_table:
if type(tree) == _type:
return _compile_table[_type](self, tree)
except HyCompileError:
# compile calls compile, so we're going to have multiple raise
# nested; so let's re-raise this exception, let's not wrap it in
# another HyCompileError!
raise
except Exception as e:
raise HyCompileError(exception=e,
start_line=getattr(e, "start_line", 0),
start_column=getattr(e, "start_column", 0))
raise HyCompileError("Unknown type - `%s'" % (str(type(tree))))
def _mangle_branch(self, tree):
def _mangle_branch(self, tree, start_line, start_column):
# If tree is empty, just return a pass statement
if tree == []:
return [ast.Pass(lineno=start_line,
col_offset=start_column)]
ret = []
tree = list(flatten_literal_list(tree))
tree.reverse()
if self.returnable and len(tree) > 0:
@ -73,10 +155,21 @@ class HyASTCompiler(object):
ret.append(ast.Return(value=el,
lineno=el.lineno,
col_offset=el.col_offset))
ret += [ast.Expr(value=el,
lineno=el.lineno,
col_offset=el.col_offset)
if not isinstance(el, ast.stmt) else el for el in tree] # NOQA
if isinstance(el, ast.FunctionDef):
ret.append(ast.Return(
value=ast.Name(
arg=el.name, id=el.name, ctx=ast.Load(),
lineno=el.lineno, col_offset=el.col_offset),
lineno=el.lineno, col_offset=el.col_offset))
for el in tree:
if isinstance(el, ast.stmt):
ret.append(el)
continue
ret.append(ast.Expr(value=el,
lineno=el.lineno,
col_offset=el.col_offset))
ret.reverse()
return ret
@ -95,9 +188,11 @@ class HyASTCompiler(object):
return [self.compile(x) for x in expr[1:]]
@builds("throw")
@builds("raise")
@checkargs(max=1)
def compile_throw_expression(self, expr):
expr.pop(0)
exc = self.compile(expr.pop(0))
exc = self.compile(expr.pop(0)) if expr else None
return ast.Raise(
lineno=expr.start_line,
col_offset=expr.start_column,
@ -116,52 +211,152 @@ class HyASTCompiler(object):
else:
Try = ast.TryExcept
try:
body = expr.pop(0)
except IndexError:
body = []
# (try something…)
body = self._code_branch(self.compile(body),
expr.start_line,
expr.start_column)
orelse = []
if len(expr) == 0:
# (try) or (try body)
handlers = [ast.ExceptHandler(
lineno=expr.start_line,
col_offset=expr.start_column,
type=None,
name=None,
body=[ast.Pass(lineno=expr.start_line,
col_offset=expr.start_column)])]
else:
handlers = []
for e in expr:
if not len(e):
raise TypeError("Empty list not allowed in `try'")
if e[0] in (HySymbol("except"), HySymbol("catch")):
handlers.append(self.compile(e))
elif e[0] == HySymbol("else"):
if orelse:
raise TypeError(
"`try' cannot have more than one `else'")
else:
orelse = self._code_branch(self.compile(e[1:]),
e.start_line,
e.start_column)
else:
raise TypeError("Unknown expression in `try'")
if handlers == []:
raise TypeError(
"`try' must have at least `except' or `finally'")
return Try(
lineno=expr.start_line,
col_offset=expr.start_column,
body=self._code_branch(self.compile(expr.pop(0))),
handlers=[self.compile(s) for s in expr],
body=body,
handlers=handlers,
finalbody=[],
orelse=[])
orelse=orelse)
@builds("catch")
@builds("except")
def compile_catch_expression(self, expr):
expr.pop(0) # catch
_type = self.compile(expr.pop(0))
name = expr.pop(0)
catch = expr.pop(0) # catch
if sys.version_info[0] >= 3:
# Python3 features a change where the Exception handler
# moved the name from a Name() to a pure Python String type.
#
# We'll just make sure it's a pure "string", and let it work
# it's magic.
name = str(name)
try:
exceptions = expr.pop(0)
except IndexError:
exceptions = HyList()
# exceptions catch should be either:
# [[list of exceptions]]
# or
# [variable [list of exceptions]]
# or
# [variable exception]
# or
# [exception]
# or
# []
if not isinstance(exceptions, HyList):
raise TypeError("`%s' exceptions list is not a list" % catch)
if len(exceptions) > 2:
raise TypeError("`%s' exceptions list is too long" % catch)
# [variable [list of exceptions]]
# let's pop variable and use it as name
if len(exceptions) == 2:
name = exceptions.pop(0)
if sys.version_info[0] >= 3:
# Python3 features a change where the Exception handler
# moved the name from a Name() to a pure Python String type.
#
# We'll just make sure it's a pure "string", and let it work
# it's magic.
name = ast_str(name)
else:
# Python2 requires an ast.Name, set to ctx Store.
name = self._storeize(self.compile(name))
else:
# Python2 requires an ast.Name, set to ctx Store.
name = self.compile(name)
name.ctx = ast.Store()
name = None
try:
exceptions_list = exceptions.pop(0)
except IndexError:
exceptions_list = []
if isinstance(exceptions_list, list):
if len(exceptions_list):
# [FooBar BarFoo] → catch Foobar and BarFoo exceptions
_type = ast.Tuple(elts=[self.compile(x)
for x in exceptions_list],
lineno=expr.start_line,
col_offset=expr.start_column,
ctx=ast.Load())
else:
# [] → all exceptions catched
_type = None
elif isinstance(exceptions_list, HySymbol):
_type = self.compile(exceptions_list)
else:
raise TypeError("`%s' needs a valid exception list" % catch)
body = self._code_branch([self.compile(x) for x in expr],
expr.start_line,
expr.start_column)
return ast.ExceptHandler(
lineno=expr.start_line,
col_offset=expr.start_column,
type=_type,
name=name,
body=self._code_branch([self.compile(x) for x in expr]))
body=body)
def _code_branch(self, branch):
if isinstance(branch, list):
return self._mangle_branch(branch)
return self._mangle_branch([branch])
def _code_branch(self, branch, start_line, start_column):
return self._mangle_branch((branch
if isinstance(branch, list)
else [branch]),
start_line,
start_column)
@builds("if")
@checkargs(min=2, max=3)
def compile_if_expression(self, expr):
expr.pop(0)
expr.pop(0) # if
test = self.compile(expr.pop(0))
body = self._code_branch(self.compile(expr.pop(0)))
orel = []
if len(expr) > 0:
orel = self._code_branch(self.compile(expr.pop(0)))
body = self._code_branch(self.compile(expr.pop(0)),
expr.start_line,
expr.start_column)
if len(expr) == 1:
orel = self._code_branch(self.compile(expr.pop(0)),
expr.start_line,
expr.start_column)
else:
orel = []
return ast.If(test=test,
body=body,
@ -190,6 +385,7 @@ class HyASTCompiler(object):
nl=True)
@builds("assert")
@checkargs(1)
def compile_assert_expression(self, expr):
expr.pop(0) # assert
e = expr.pop(0)
@ -199,6 +395,7 @@ class HyASTCompiler(object):
col_offset=e.start_column)
@builds("lambda")
@checkargs(min=2)
def compile_lambda_expression(self, expr):
expr.pop(0)
sig = expr.pop(0)
@ -208,7 +405,7 @@ class HyASTCompiler(object):
lineno=expr.start_line,
col_offset=expr.start_column,
args=ast.arguments(args=[
ast.Name(arg=str(x), id=str(x),
ast.Name(arg=ast_str(x), id=ast_str(x),
ctx=ast.Param(),
lineno=x.start_line,
col_offset=x.start_column)
@ -221,10 +418,12 @@ class HyASTCompiler(object):
body=self.compile(body))
@builds("pass")
@checkargs(0)
def compile_pass_expression(self, expr):
return ast.Pass(lineno=expr.start_line, col_offset=expr.start_column)
@builds("yield")
@checkargs(1)
def compile_yield_expression(self, expr):
expr.pop(0)
return ast.Yield(
@ -238,7 +437,7 @@ class HyASTCompiler(object):
return ast.Import(
lineno=expr.start_line,
col_offset=expr.start_column,
names=[ast.alias(name=str(x), asname=None) for x in expr])
names=[ast.alias(name=ast_str(x), asname=None) for x in expr])
@builds("import_as")
def compile_import_as_expression(self, expr):
@ -247,21 +446,23 @@ class HyASTCompiler(object):
return ast.Import(
lineno=expr.start_line,
col_offset=expr.start_column,
module=str(expr.pop(0)),
names=[ast.alias(name=str(x[0]),
asname=str(x[1])) for x in modlist])
module=ast_str(expr.pop(0)),
names=[ast.alias(name=ast_str(x[0]),
asname=ast_str(x[1])) for x in modlist])
@builds("import_from")
@checkargs(min=1)
def compile_import_from_expression(self, expr):
expr.pop(0) # index
return ast.ImportFrom(
lineno=expr.start_line,
col_offset=expr.start_column,
module=str(expr.pop(0)),
names=[ast.alias(name=str(x), asname=None) for x in expr],
module=ast_str(expr.pop(0)),
names=[ast.alias(name=ast_str(x), asname=None) for x in expr],
level=0)
@builds("get")
@checkargs(2)
def compile_index_expression(self, expr):
expr.pop(0) # index
val = self.compile(expr.pop(0)) # target
@ -275,6 +476,7 @@ class HyASTCompiler(object):
ctx=ast.Load())
@builds("slice")
@checkargs(min=1, max=3)
def compile_slice_expression(self, expr):
expr.pop(0) # index
val = self.compile(expr.pop(0)) # target
@ -297,6 +499,7 @@ class HyASTCompiler(object):
ctx=ast.Load())
@builds("assoc")
@checkargs(3)
def compile_assoc_expression(self, expr):
expr.pop(0) # assoc
# (assoc foo bar baz) => foo[bar] = baz
@ -317,6 +520,7 @@ class HyASTCompiler(object):
value=self.compile(val))
@builds("decorate_with")
@checkargs(min=1)
def compile_decorate_expression(self, expr):
expr.pop(0) # decorate-with
fn = self.compile(expr.pop(-1))
@ -325,23 +529,30 @@ class HyASTCompiler(object):
fn.decorator_list = [self.compile(x) for x in expr]
return fn
@builds("with_as")
def compile_with_as_expression(self, expr):
expr.pop(0) # with-as
ctx = self.compile(expr.pop(0))
thing = self.compile(expr.pop(0))
if isinstance(thing, ast.Tuple):
for x in thing.elts:
x.ctx = ast.Store()
@builds("with")
@checkargs(min=2)
def compile_with_expression(self, expr):
expr.pop(0) # with
thing.ctx = ast.Store()
args = expr.pop(0)
if len(args) > 2 or len(args) < 1:
raise TypeError("with needs [arg (expr)] or [(expr)]")
args.reverse()
ctx = self.compile(args.pop(0))
thing = None
if args != []:
thing = self._storeize(self.compile(args.pop(0)))
ret = ast.With(context_expr=ctx,
lineno=expr.start_line,
col_offset=expr.start_column,
optional_vars=thing,
body=self._mangle_branch([
self.compile(x) for x in expr]))
body=self._code_branch(
[self.compile(x) for x in expr],
expr.start_line,
expr.start_column))
if sys.version_info[0] >= 3 and sys.version_info[1] >= 3:
ret.items = [ast.withitem(context_expr=ctx, optional_vars=thing)]
@ -357,32 +568,42 @@ class HyASTCompiler(object):
ctx=ast.Load())
@builds("list_comp")
@checkargs(min=2, max=3)
def compile_list_comprehension(self, expr):
# (list-comp expr (target iter))
# (list-comp expr (target iter) cond?)
expr.pop(0)
thing = self.compile(expr.pop(0))
ident, gen = expr.pop(0)
expression = expr.pop(0)
tar_it = iter(expr.pop(0))
targets = zip(tar_it, tar_it)
ident = self.compile(ident)
gen = self.compile(gen)
cond = self.compile(expr.pop(0)) if expr != [] else None
if isinstance(ident, ast.Tuple):
for x in ident.elts:
x.ctx = ast.Store()
ident.ctx = ast.Store()
return ast.ListComp(
ret = ast.ListComp(
lineno=expr.start_line,
col_offset=expr.start_column,
elt=thing,
generators=[
ast.comprehension(
target=ident,
iter=gen,
ifs=[self.compile(x) for x in expr])
])
elt=self.compile(expression),
generators=[])
for target, iterable in targets:
ret.generators.append(ast.comprehension(
target=self._storeize(self.compile(target)),
iter=self.compile(iterable),
ifs=[]))
if cond:
ret.generators[-1].ifs.append(cond)
return ret
def _storeize(self, name):
if isinstance(name, ast.Tuple):
for x in name.elts:
x.ctx = ast.Store()
name.ctx = ast.Store()
return name
@builds("kwapply")
@checkargs(2)
def compile_kwapply_expression(self, expr):
expr.pop(0) # kwapply
call = self.compile(expr.pop(0))
@ -391,11 +612,39 @@ class HyASTCompiler(object):
if type(call) != ast.Call:
raise TypeError("kwapplying a non-call")
call.keywords = [ast.keyword(arg=str(x),
call.keywords = [ast.keyword(arg=ast_str(x),
value=self.compile(kwargs[x])) for x in kwargs]
return call
@builds("not")
@builds("~")
@checkargs(1)
def compile_unary_operator(self, expression):
ops = {"not": ast.Not,
"~": ast.Invert}
operator = expression.pop(0)
operand = expression.pop(0)
return ast.UnaryOp(op=ops[operator](),
operand=self.compile(operand),
lineno=operator.start_line,
col_offset=operator.start_column)
@builds("and")
@builds("or")
@checkargs(min=2)
def compile_logical_or_and_and_operator(self, expression):
ops = {"and": ast.And,
"or": ast.Or}
operator = expression.pop(0)
values = []
for child in expression:
values.append(self.compile(child))
return ast.BoolOp(op=ops[operator](),
lineno=operator.start_line,
col_offset=operator.start_column,
values=values)
@builds("=")
@builds("!=")
@builds("<")
@ -406,6 +655,7 @@ class HyASTCompiler(object):
@builds("in")
@builds("is_not")
@builds("not_in")
@checkargs(min=2)
def compile_compare_op_expression(self, expression):
ops = {"=": ast.Eq, "!=": ast.NotEq,
"<": ast.Lt, "<=": ast.LtE,
@ -429,6 +679,7 @@ class HyASTCompiler(object):
@builds("-")
@builds("/")
@builds("*")
@checkargs(min=2)
def compile_maths_expression(self, expression):
# operator = Mod | Pow | LShift | RShift | BitOr |
# BitXor | BitAnd | FloorDiv
@ -467,7 +718,7 @@ class HyASTCompiler(object):
lineno=expr.start_line,
col_offset=expr.start_column,
value=self.compile(obj),
attr=str(fn),
attr=ast_str(fn),
ctx=ast.Load()),
args=[self.compile(x) for x in expr],
keywords=[],
@ -499,6 +750,7 @@ class HyASTCompiler(object):
@builds("def")
@builds("setf")
@builds("setv")
@checkargs(2)
def compile_def_expression(self, expression):
expression.pop(0) # "def"
name = expression.pop(0)
@ -509,15 +761,10 @@ class HyASTCompiler(object):
# We special case a FunctionDef, since we can define by setting
# FunctionDef's .name attribute, rather then foo == anon_fn. This
# helps keep things clean.
what.name = str(name)
what.name = ast_str(name)
return what
name = self.compile(name)
if isinstance(name, ast.Tuple):
for x in name.elts:
x.ctx = ast.Store()
name.ctx = ast.Store()
name = self._storeize(self.compile(name))
return ast.Assign(
lineno=expression.start_line,
@ -525,34 +772,43 @@ class HyASTCompiler(object):
targets=[name], value=what)
@builds("foreach")
@checkargs(min=1)
def compile_for_expression(self, expression):
ret_status = self.returnable
self.returnable = False
expression.pop(0) # for
name, iterable = expression.pop(0)
target = self.compile_symbol(name)
if isinstance(target, ast.Tuple):
for x in target.elts:
x.ctx = ast.Store()
target.ctx = ast.Store()
# support stuff like:
# (for [x [1 2 3 4]
# y [a b c d]] ...)
target = self._storeize(self.compile_symbol(name))
ret = ast.For(lineno=expression.start_line,
col_offset=expression.start_column,
target=target,
iter=self.compile(iterable),
body=self._mangle_branch([
self.compile(x) for x in expression]),
body=self._code_branch(
[self.compile(x) for x in expression],
expression.start_line,
expression.start_column),
orelse=[])
self.returnable = ret_status
return ret
@builds("while")
@checkargs(min=2)
def compile_while_expression(self, expr):
expr.pop(0) # "while"
test = self.compile(expr.pop(0))
return ast.While(test=test,
body=self._code_branch(
[self.compile(x) for x in expr],
expr.start_line,
expr.start_column),
orelse=[],
lineno=expr.start_line,
col_offset=expr.start_column)
@builds(HyList)
def compile_list(self, expr):
return ast.List(
@ -562,16 +818,30 @@ class HyASTCompiler(object):
col_offset=expr.start_column)
@builds("fn")
@checkargs(min=2)
def compile_fn_expression(self, expression):
expression.pop(0) # fn
ret_status = self.returnable
self.returnable = True
self.anon_fn_count += 1
name = "_hy_anon_fn_%d" % (self.anon_fn_count)
sig = expression.pop(0)
body = []
if expression != []:
self.returnable = True
tailop = self.compile(expression.pop(-1))
self.returnable = False
for el in expression:
body.append(self.compile(el))
body.append(tailop)
self.returnable = True
body = self._code_branch(body,
expression.start_line,
expression.start_column)
ret = ast.FunctionDef(
name=name,
lineno=expression.start_line,
@ -579,7 +849,7 @@ class HyASTCompiler(object):
args=ast.arguments(
args=[
ast.Name(
arg=str(x), id=str(x),
arg=ast_str(x), id=ast_str(x),
ctx=ast.Param(),
lineno=x.start_line,
col_offset=x.start_column)
@ -589,8 +859,7 @@ class HyASTCompiler(object):
kwonlyargs=[],
kw_defaults=[],
defaults=[]),
body=self._code_branch([
self.compile(x) for x in expression]),
body=body,
decorator_list=[])
self.returnable = ret_status
@ -613,19 +882,19 @@ class HyASTCompiler(object):
lineno=symbol.start_line,
col_offset=symbol.start_column,
value=self.compile_symbol(glob),
attr=str(local),
attr=ast_str(local),
ctx=ast.Load()
)
return ast.Name(id=str(symbol),
arg=str(symbol),
return ast.Name(id=ast_str(symbol),
arg=ast_str(symbol),
ctx=ast.Load(),
lineno=symbol.start_line,
col_offset=symbol.start_column)
@builds(HyString)
def compile_string(self, string):
return ast.Str(s=str(string), lineno=string.start_line,
return ast.Str(s=ast_str(string), lineno=string.start_line,
col_offset=string.start_column)
@builds(HyDict)
@ -649,5 +918,5 @@ def hy_compile(tree, root=None):
tlo = root
if root is None:
tlo = ast.Module
ret = tlo(body=compiler._mangle_branch(compiler.compile(tree)))
ret = tlo(body=compiler._mangle_branch(compiler.compile(tree), 0, 0))
return ret

View File

@ -20,15 +20,24 @@
from hy.macros import process as mprocess
import hy.mangle
MACROS = [
"hy.core.bootstrap", # defn, cond
"hy.core.bootstrap",
"hy.core.mangles",
]
def process(tree):
load_macros()
return mprocess(tree)
old = None
while old != tree:
old = tree
tree = mprocess(tree)
for m in hy.mangle.MANGLES:
m().mangle(tree)
return tree
def load_macros():

View File

@ -94,6 +94,20 @@ def threading_macro(tree):
return ret
@macro("_>>")
def threading_tail_macro(tree):
tree.pop(0)
ret = tree.pop(0)
for node in tree:
if not isinstance(node, HyExpression):
nnode = HyExpression([node])
nnode.replace(node)
node = nnode
node.append(ret)
ret = node
return ret
@macro("car")
@macro("first")
def first_macro(tree):
@ -114,3 +128,19 @@ def rest_macro(tree):
return HyExpression([HySymbol('slice'),
ret,
HyInteger(1)])
@macro("let")
def let_macro(tree):
tree.pop(0) # "let"
ret = tree.pop(0) # vars
# tree is now the body
expr = HyExpression([HySymbol("fn"), HyList([])])
for var in ret:
expr.append(HyExpression([HySymbol("setf"), var[0], var[1]]))
for stmt in tree:
expr.append(stmt)
return HyExpression([expr])

85
hy/core/mangles.py Normal file
View File

@ -0,0 +1,85 @@
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.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.expression import HyExpression
from hy.models.symbol import HySymbol
from hy.models.list import HyList
import hy.mangle
class HoistableMangle(hy.mangle.Mangle):
def should_hoist(self):
for frame in self.stack:
if frame is self.scope:
return False
if isinstance(frame, HyExpression) and frame != []:
call = frame[0]
if call in self.ignore:
continue
return True
return False
class FunctionMangle(HoistableMangle):
hoistable = ["fn"]
ignore = ["def", "decorate_with", "setf", "setv"]
def __init__(self):
self.series = 0
def unique_name(self):
self.series += 1
return "_hy_hoisted_fn_%s" % (self.series)
def visit(self, tree):
if isinstance(tree, HyExpression) and tree != []:
call = tree[0]
if call == "fn" and self.should_hoist():
new_name = HySymbol(self.unique_name())
new_name.replace(tree)
fn_def = HyExpression([HySymbol("def"),
new_name,
tree])
fn_def.replace(tree)
self.hoist(fn_def)
return new_name
class IfMangle(HoistableMangle):
ignore = []
def __init__(self):
self.series = 0
def visit(self, tree):
if isinstance(tree, HyExpression) and tree != []:
call = tree[0]
if call == "if" and self.should_hoist():
fn = HyExpression([HyExpression([HySymbol("fn"),
HyList([]),
tree])])
fn.replace(tree)
return fn
hy.mangle.MANGLES.append(IfMangle)
hy.mangle.MANGLES.append(FunctionMangle)

View File

@ -19,11 +19,12 @@
# DEALINGS IN THE SOFTWARE.
from hy.compiler import hy_compile
from hy.lex import tokenize
from hy.core import process
from py_compile import wr_long, MAGIC
from hy.core import process
from hy.lex import tokenize
from io import open
import marshal
import imp
import sys
@ -43,7 +44,7 @@ def import_buffer_to_hst(fd):
def import_file_to_hst(fpath):
return import_buffer_to_hst(open(fpath, 'r'))
return import_buffer_to_hst(open(fpath, 'r', encoding='utf-8'))
def import_file_to_ast(fpath):

View File

@ -166,6 +166,9 @@ class ListeyThing(State):
if char == self.end_char:
return Idle
if char in ")]}":
raise LexException("Unexpected closing character: `%s'" % (char))
if char in WHITESPACE:
self.commit()
return
@ -241,6 +244,9 @@ class String(State):
if char == "\\":
self.nodes.append("\\")
return
if char == "\"":
self.nodes.append("\"")
return
raise LexException("Unknown modifier: `%s'" % (char))

109
hy/mangle.py Normal file
View File

@ -0,0 +1,109 @@
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.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.expression import HyExpression
# from hy.models.list import HyList
MANGLES = []
class Mangle(object):
"""
Mangle (n.)
1. To mutilate or disfigure by battering, hacking, cutting,
or tearing. See Synonyms at batter1.
(but mostly hacking)
"""
class TreeChanged(Exception):
pass
def _mangle(self, tree):
# Things that force a scope push to go into:
#
# - Functions
# - If
scopable = ["fn", "if"]
scoped = False
self.push_stack(tree)
if isinstance(tree, HyExpression):
what = tree[0]
if what in scopable:
self.push_scope(tree)
scoped = True
if isinstance(tree, list):
for i, element in enumerate(tree):
nel = self.visit(element)
if nel:
tree[i] = nel
self.tree_changed()
self._mangle(element)
if scoped:
self.pop_scope()
self.pop_stack()
def hoist(self, what):
scope = self.scope
for point, el in enumerate(scope):
if el in self.stack:
break
self.scope.insert(point, what)
def get_scope(self):
return self.scopes[0]
def tree_changed(self):
raise self.TreeChanged()
@property
def scope(self):
return self.get_scope()
def push_scope(self, tree):
self.scopes.insert(0, tree)
def push_stack(self, tree):
self.stack.insert(0, tree)
def pop_scope(self):
return self.scopes.pop(0)
def pop_stack(self):
return self.stack.pop(0)
def mangle(self, tree):
unfinished = True
while unfinished:
self.root = tree
self.scopes = []
self.stack = []
self.push_scope(tree)
try:
self._mangle(tree)
unfinished = False
except self.TreeChanged:
pass

View File

@ -23,18 +23,15 @@ import sys
if sys.version_info[0] >= 3:
_str_type = str
str_type = str
else:
_str_type = unicode
str_type = unicode
class HyString(HyObject, _str_type):
class HyString(HyObject, str_type):
"""
Generic Hy String object. Helpful to store string literals from Hy
scripts. It's either a ``str`` or a ``unicode``, depending on the
Python version.
"""
def __new__(cls, value):
obj = _str_type.__new__(cls, value)
return obj
pass

28
hy/util.py Normal file
View File

@ -0,0 +1,28 @@
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.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.
def flatten_literal_list(entry):
for e in entry:
if type(e) == list:
for x in flatten_literal_list(e):
yield x # needs more yield-from
else:
yield e

View File

@ -28,6 +28,6 @@
(post-route hy2py "/hy2py" []
(try
(hy-to-py (get request.form "code"))
(catch LexException e (err "Incomplete Code."))
(catch HyError e (err "Generic error during processing."))
(catch Exception e (err "Erm, you broke something."))))
(catch [e LexException] (err "Incomplete Code."))
(catch [e HyError] (err "Generic error during processing."))
(catch [e Exception] (err "Erm, you broke something."))))

View File

@ -1,6 +1,6 @@
#
import hy
from .native_tests.math import *
from .native_tests.language import *
import hy # noqa
from .native_tests.math import * # noqa
from .native_tests.language import * # noqa

View File

@ -1,4 +1,5 @@
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
# Copyright (c) 2013 Julien Danjou <julien@danjou.info>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@ -34,6 +35,15 @@ def _ast_spotcheck(arg, root, secondary):
assert getattr(root, arg) == getattr(secondary, arg)
def cant_compile(expr):
expr = tokenize(expr)
try:
hy_compile(expr)
assert False
except HyCompileError:
pass
def test_ast_bad_type():
"Make sure AST breakage can happen"
try:
@ -43,21 +53,239 @@ def test_ast_bad_type():
pass
def test_ast_bad_if():
"Make sure AST can't compile invalid if"
cant_compile("(if)")
cant_compile("(if foobar)")
cant_compile("(if 1 2 3 4 5)")
def test_ast_valid_if():
"Make sure AST can't compile invalid if"
hy_compile(tokenize("(if foo bar)"))
def test_ast_valid_unary_op():
"Make sure AST can compile valid unary operator"
hy_compile(tokenize("(not 2)"))
hy_compile(tokenize("(~ 1)"))
def test_ast_invalid_unary_op():
"Make sure AST can't compile invalid unary operator"
cant_compile("(not 2 3 4)")
cant_compile("(not)")
cant_compile("(not 2 3 4)")
cant_compile("(~ 2 2 3 4)")
cant_compile("(~)")
def test_ast_bad_while():
"Make sure AST can't compile invalid while"
cant_compile("(while)")
cant_compile("(while (true))")
def test_ast_good_do():
"Make sure AST can compile valid do"
hy_compile(tokenize("(do)"))
hy_compile(tokenize("(do 1)"))
def test_ast_good_throw():
"Make sure AST can compile valid throw"
hy_compile(tokenize("(throw)"))
hy_compile(tokenize("(throw 1)"))
def test_ast_bad_throw():
"Make sure AST can't compile invalid throw"
cant_compile("(raise 1 2 3)")
def test_ast_good_raise():
"Make sure AST can compile valid raise"
hy_compile(tokenize("(raise)"))
hy_compile(tokenize("(raise 1)"))
def test_ast_bad_raise():
"Make sure AST can't compile invalid raise"
cant_compile("(raise 1 2 3)")
def test_ast_good_try():
"Make sure AST can compile valid try"
hy_compile(tokenize("(try)"))
hy_compile(tokenize("(try 1)"))
hy_compile(tokenize("(try 1 (except) (else 1))"))
hy_compile(tokenize("(try 1 (else 1) (except))"))
def test_ast_bad_try():
"Make sure AST can't compile invalid try"
cant_compile("(try 1 bla)")
cant_compile("(try 1 bla bla)")
cant_compile("(try (do) (else 1) (else 2))")
cant_compile("(try 1 (else 1))")
def test_ast_good_catch():
"Make sure AST can compile valid catch"
hy_compile(tokenize("(catch)"))
hy_compile(tokenize("(catch [])"))
hy_compile(tokenize("(catch [Foobar])"))
hy_compile(tokenize("(catch [[]])"))
hy_compile(tokenize("(catch [x FooBar])"))
hy_compile(tokenize("(catch [x [FooBar BarFoo]])"))
hy_compile(tokenize("(catch [x [FooBar BarFoo]])"))
def test_ast_bad_catch():
"Make sure AST can't compile invalid catch"
cant_compile("(catch 1)")
cant_compile("(catch \"A\")")
cant_compile("(catch [1 3])")
cant_compile("(catch [x [FooBar] BarBar])")
def test_ast_good_except():
"Make sure AST can compile valid except"
hy_compile(tokenize("(except)"))
hy_compile(tokenize("(except [])"))
hy_compile(tokenize("(except [Foobar])"))
hy_compile(tokenize("(except [[]])"))
hy_compile(tokenize("(except [x FooBar])"))
hy_compile(tokenize("(except [x [FooBar BarFoo]])"))
hy_compile(tokenize("(except [x [FooBar BarFoo]])"))
def test_ast_bad_except():
"Make sure AST can't compile invalid except"
cant_compile("(except 1)")
cant_compile("(except [1 3])")
cant_compile("(except [x [FooBar] BarBar])")
def test_ast_good_assert():
"Make sure AST can compile valid assert"
hy_compile(tokenize("(assert 1)"))
def test_ast_bad_assert():
"Make sure AST can't compile invalid assert"
cant_compile("(assert)")
cant_compile("(assert 1 2)")
def test_ast_good_lambda():
"Make sure AST can compile valid lambda"
hy_compile(tokenize("(lambda [] 1)"))
def test_ast_bad_lambda():
"Make sure AST can't compile invalid lambda"
cant_compile("(lambda)")
cant_compile("(lambda [])")
def test_ast_good_pass():
"Make sure AST can compile valid pass"
hy_compile(tokenize("(pass)"))
def test_ast_bad_pass():
"Make sure AST can't compile invalid pass"
cant_compile("(pass 1)")
cant_compile("(pass 1 2)")
def test_ast_good_yield():
"Make sure AST can compile valid yield"
hy_compile(tokenize("(yield 1)"))
def test_ast_bad_yield():
"Make sure AST can't compile invalid yield"
cant_compile("(yield)")
cant_compile("(yield 1 2)")
def test_ast_good_import_from():
"Make sure AST can compile valid import-from"
hy_compile(tokenize("(import-from x y)"))
def test_ast_bad_import_from():
"Make sure AST can't compile invalid import-from"
cant_compile("(import-from)")
def test_ast_good_get():
"Make sure AST can compile valid get"
hy_compile(tokenize("(get x y)"))
def test_ast_bad_get():
"Make sure AST can't compile invalid get"
cant_compile("(get)")
cant_compile("(get 1)")
cant_compile("(get 1 2 3)")
def test_ast_good_slice():
"Make sure AST can compile valid slice"
hy_compile(tokenize("(slice x)"))
hy_compile(tokenize("(slice x y)"))
hy_compile(tokenize("(slice x y z)"))
def test_ast_bad_slice():
"Make sure AST can't compile invalid slice"
cant_compile("(slice)")
cant_compile("(slice 1 2 3 4)")
def test_ast_good_assoc():
"Make sure AST can compile valid assoc"
hy_compile(tokenize("(assoc x y z)"))
def test_ast_bad_assoc():
"Make sure AST can't compile invalid assoc"
cant_compile("(assoc)")
cant_compile("(assoc 1)")
cant_compile("(assoc 1 2)")
cant_compile("(assoc 1 2 3 4)")
def test_ast_bad_with():
"Make sure AST can't compile invalid with"
cant_compile("(with)")
cant_compile("(with [])")
cant_compile("(with [] (pass))")
def test_ast_valid_while():
"Make sure AST can't compile invalid while"
hy_compile(tokenize("(while foo bar)"))
def test_ast_expression_basics():
""" Ensure basic AST expression conversion works. """
code = hy_compile(tokenize("(foo bar)")).body[0]
tree = ast.Expr(value=ast.Call(
func=ast.Name(
id="foo",
ctx=ast.Load(),
),
args=[
ast.Name(id="bar", ctx=ast.Load())
],
keywords=[],
starargs=None,
kwargs=None,
))
func=ast.Name(
id="foo",
ctx=ast.Load(),
),
args=[
ast.Name(id="bar", ctx=ast.Load())
],
keywords=[],
starargs=None,
kwargs=None,
))
_ast_spotcheck("value.func.id", code, tree)
@ -70,11 +298,7 @@ def test_ast_anon_fns_basics():
def test_ast_non_decoratable():
""" Ensure decorating garbage breaks """
try:
hy_compile(tokenize("(decorate-with (foo) (* x x))"))
assert True is False
except TypeError:
pass
cant_compile("(decorate-with (foo) (* x x))")
def test_ast_non_kwapplyable():
@ -84,7 +308,7 @@ def test_ast_non_kwapplyable():
try:
hy_compile(code)
assert True is False
except TypeError:
except HyCompileError:
pass

View File

@ -4,8 +4,8 @@ import ast
def test_basics():
"Make sure the basics of the importer work"
module = import_file_to_module("basic",
"tests/resources/importer/basic.hy")
import_file_to_module("basic",
"tests/resources/importer/basic.hy")
def test_stringer():

View File

@ -33,13 +33,28 @@ from hy.lex import tokenize
def test_lex_exception():
""" Ensure tokenize throws a fit on a partial input """
try:
objs = tokenize("(foo")
tokenize("(foo")
assert True is False
except LexException:
pass
try:
objs = tokenize("&foo&")
tokenize("&foo&")
assert True is False
except LexException:
pass
def test_unbalanced_exception():
"""Ensure the tokenization fails on unbalanced expressions"""
try:
tokenize("(bar))")
assert True is False
except LexException:
pass
try:
tokenize("(baz [quux]])")
assert True is False
except LexException:
pass

View File

@ -2,9 +2,7 @@
from hy.macros import macro, process
from hy.lex import tokenize
from hy.models.expression import HyExpression
from hy.models.string import HyString
from hy.models.symbol import HySymbol
from hy.models.list import HyList

View File

@ -1,9 +1,9 @@
;
(import-from tests.resources kwtest)
(import-from tests.resources kwtest function-with-a-dash)
(import-from os.path exists isdir isfile)
(import sys)
(import-as sys systest)
(import sys)
(defn test-sys-argv []
@ -32,6 +32,30 @@
(assert (= count 150)))
(defn test-while-loop []
"NATIVE: test while loops?"
(setv count 5)
(setv fact 1)
(while (> count 0)
(setv fact (* fact count))
(setv count (- count 1)))
(assert (= count 0))
(assert (= fact 120)))
(defn test-not []
"NATIVE: test not"
(assert (not (= 1 2)))
(assert (= true (not false)))
(assert (= false (not 42))) )
(defn test-inv []
"NATIVE: test inv"
(assert (= (~ 1) -2))
(assert (= (~ -2) 1)))
(defn test-in []
"NATIVE: test in"
(assert (in "a" ["a" "b" "c" "d"]))
@ -74,6 +98,17 @@
(assert (= 1 1))
(assert (= 1 1)))))
(defn test-branching-expr-count-with-do []
"NATIVE: make sure we execute the right number of expressions in the branch"
(setv counter 0)
(if false
(assert (= 2 1))
(do
(setv counter (+ counter 1))
(setv counter (+ counter 1))
(setv counter (+ counter 1))))
(assert (= counter 3)))
(defn test-cond []
"NATIVE: test if cond sorta works."
@ -84,7 +119,7 @@
(defn test-index []
"NATIVE: Test that dict access works"
(assert (get {"one" "two"} "one") "two")
(assert (= (get {"one" "two"} "one") "two"))
(assert (= (get [1 2 3 4 5] 1) 2)))
@ -125,12 +160,134 @@
(assert (= (.join " " ["one" "two"]) "one two")))
(defn test-do []
"NATIVE: test do"
(do))
(defn test-exceptions []
"NATIVE: test Exceptions"
(try)
(try (do))
(try (pass))
(try (pass) (except))
(try (pass) (except [IOError]) (except))
;; Test correct (raise)
(let [[passed false]]
(try
(try
(raise IndexError)
(except [IndexError] (raise)))
(except [IndexError]
(setv passed true)))
(assert passed))
;; Test incorrect (raise)
(let [[passed false]]
(try
(raise)
;; Python 2 raises TypeError
;; Python 3 raises RuntimeError
(except [[TypeError RuntimeError]]
(setv passed true)))
(assert passed))
(try
(raise (KeyError))
(catch [[IOError]] (assert false))
(catch [e [KeyError]] (assert e)))
(try
(throw (KeyError))
(catch IOError e (assert (= 2 1)))
(catch KeyError e (+ 1 1) (assert (= 1 1)))))
(except [[IOError]] (assert false))
(catch [e [KeyError]] (assert e)))
(try
(get [1] 3)
(catch [IndexError] (assert true))
(except [IndexError] (pass)))
(try
(print foobar42ofthebaz)
(catch [IndexError] (assert false))
(except [NameError] (pass)))
(try
(get [1] 3)
(except [e IndexError] (assert (isinstance e IndexError))))
(try
(get [1] 3)
(catch [e [IndexError NameError]] (assert (isinstance e IndexError))))
(try
(print foobar42ofthebaz)
(except [e [IndexError NameError]] (assert (isinstance e NameError))))
(try
(print foobar42)
(catch [[IndexError NameError]] (pass)))
(try
(get [1] 3)
(catch [[IndexError NameError]] (pass)))
(try
(print foobar42ofthebaz)
(catch))
(try
(print foobar42ofthebaz)
(except []))
(try
(print foobar42ofthebaz)
(except [] (pass)))
(try
(print foobar42ofthebaz)
(catch []
(setv foobar42ofthebaz 42)
(assert (= foobar42ofthebaz 42))))
(let [[passed false]]
(try
(try (pass) (except) (else (bla)))
(except [NameError] (setv passed true)))
(assert passed))
(let [[x 0]]
(try
(raise IOError)
(except [IOError]
(setv x 45))
(else (setv x 44)))
(assert (= x 45)))
(let [[x 0]]
(try
(raise KeyError)
(except []
(setv x 45))
(else (setv x 44)))
(assert (= x 45)))
(let [[x 0]]
(try
(try
(raise KeyError)
(except [IOError]
(setv x 45))
(else (setv x 44)))
(except))
(assert (= x 0))))
(defn test-earmuffs []
"NATIVE: Test earmuffs"
@ -146,6 +303,12 @@
["X" "B" "C" "D"])))
(defn test-tail-threading []
"NATIVE: test tail threading macro"
(assert (= (.join ", " (* 10 ["foo"]))
(->> ["foo"] (* 10) (.join ", ")))))
(defn test-threading-two []
"NATIVE: test threading macro"
(assert (= (-> "a b c d" .upper (.replace "A" "X") .split)
@ -198,8 +361,18 @@
(defn test-context []
"NATIVE: test with"
(with-as (open "README.md" "r") fd
(pass)))
(with [fd (open "README.md" "r")] (assert fd))
(with [(open "README.md" "r")] (pass)))
(defn test-for-doodle []
"NATIVE: test for-do"
(do (do (do (do (do (do (do (do (do (setf (, x y) (, 0 0)))))))))))
(foreach [- [1 2]]
(do
(setf x (+ x 1))
(setf y (+ y 1))))
(assert (= y x 2)))
(defn test-comprehensions []
@ -207,4 +380,146 @@
(assert (= (list-comp (* x 2) (x (range 2))) [0 2]))
(assert (= (list-comp (* x 2) (x (range 4)) (% x 2)) [2 6]))
(assert (= (sorted (list-comp (* y 2) ((, x y) (.items {"1" 1 "2" 2}))))
[2 4])))
[2 4]))
(assert (= (list-comp (, x y) (x (range 2) y (range 2)))
[(, 0 0) (, 0 1) (, 1 0) (, 1 1)])))
(defn test-defn-order []
"NATIVE: test defn evaluation order"
(setv acc [])
(defn my-fun []
(.append acc "Foo")
(.append acc "Bar")
(.append acc "Baz"))
(my-fun)
(assert (= acc ["Foo" "Bar" "Baz"])))
(defn test-defn-return []
"NATIVE: test defn return"
(defn my-fun [x]
(+ x 1))
(assert (= 43 (my-fun 42))))
(defn test-defn-do []
"NATIVE: test defn evaluation order with do"
(setv acc [])
(defn my-fun []
(do
(.append acc "Foo")
(.append acc "Bar")
(.append acc "Baz")))
(my-fun)
(assert (= acc ["Foo" "Bar" "Baz"])))
(defn test-defn-do-return []
"NATIVE: test defn return with do"
(defn my-fun [x]
(do
(+ x 42) ; noop
(+ x 1)))
(assert (= 43 (my-fun 42))))
(defn test-mangles []
"NATIVE: test mangles"
(assert (= 2 ((fn [] (+ 1 1))))))
(defn test-fn-return []
"NATIVE: test function return"
(setv fn-test ((fn [] (fn [] (+ 1 1)))))
(assert (= (fn-test) 2)))
(defn test-let []
"NATIVE: test let works rightish"
(assert (= (let [[x 1] [y 2] [z 3]] (+ x y z)) 6)))
(defn test-if-mangler []
"NATIVE: test that we return ifs"
(assert (= true (if true true true))))
(defn test-nested-mangles []
"NATIVE: test that we can use macros in mangled code"
(assert (= ((fn [] (-> 2 (+ 1 1) (* 1 2)))) 8)))
(defn test-let-scope []
"NATIVE: test let works rightish"
(setv y 123)
(assert (= (let [[x 1]
[y 2]
[z 3]]
(+ x y z))
6))
(try
(assert (= x 42)) ; This ain't true
(catch [e [NameError]] (assert e)))
(assert (= y 123)))
(defn test-symbol-utf-8 []
"NATIVE: test symbol encoded"
(let [[ "love"]
[ "flower"]]
(assert (= (+ ) "flowerlove"))))
(defn test-symbol-dash []
"NATIVE: test symbol encoded"
(let [[- "doublelove"]
[-_- "what?"]]
(assert (= - "doublelove"))
(assert (= -_- "what?"))))
(defn test-and []
"NATIVE: test the and function"
(let [[and123 (and 1 2 3)]
[and-false (and 1 False 3)]]
(assert (= and123 3))
(assert (= and-false False))))
(defn test-or []
"NATIVE: test the or function"
(let [[or-all-true (or 1 2 3 True "string")]
[or-some-true (or False "hello")]
[or-none-true (or False False)]]
(assert (= or-all-true 1))
(assert (= or-some-true "hello"))
(assert (= or-none-true False))))
(defn test-if-return-branching []
"NATIVE: test the if return branching"
; thanks, algernon
(assert (= 1 (let [[x 1]
[y 2]]
(if true
2)
1)))
(assert (= 1 (let [[x 1] [y 2]]
(pass)
(pass)
((fn [] 1))))))
; FEATURE: native hy-eval
;
; - related to bug #64
; - https://github.com/paultag/hy/issues/64
; - https://github.com/paultag/hy/pull/62
;
; (defn test-eval []
; "NATIVE: test eval"
; (assert (= 1 (eval 1)))
; (assert (= "foobar" (eval "foobar")))
; (setv x 42)
; (assert (= x (eval x))))

View File

@ -2,3 +2,7 @@
def kwtest(*args, **kwargs):
return kwargs
def function_with_a_dash():
pass

View File

@ -1,7 +1,11 @@
[tox]
envlist = py27,pypy,py32,py33,py26
envlist = py27,pypy,py32,py33,py26,flake8
[testenv]
commands = nosetests
deps =
nose
setuptools
[testenv:flake8]
deps = flake8
commands = flake8 hy bin tests