Merge branch 'develop' into feature/lambda-list-keyword
This commit is contained in:
commit
4d90123506
3
AUTHORS
3
AUTHORS
@ -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>
|
||||
|
33
ChangeLog
33
ChangeLog
@ -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
17
LICENSE
Normal 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.
|
3
Makefile
3
Makefile
@ -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
122
NEWS
Normal 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)
|
11
README.md
11
README.md
@ -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
5
TODO
@ -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
8
bin/hy
@ -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
121
docs/_static/CC0_1.0.txt
vendored
Normal 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
15
docs/_static/hy_logo-about.txt
vendored
Normal 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
111
docs/_static/hy_logo-paths.svg
vendored
Normal 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
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
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
125
docs/_static/hy_logo.svg
vendored
Normal 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
68
docs/hacking.rst
Normal 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!
|
@ -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
101
docs/language/api.rst
Normal 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.
|
@ -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
|
||||
|
27
docs/language/internals.rst
Normal file
27
docs/language/internals.rst
Normal 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
38
docs/quickstart.rst
Normal 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
414
docs/tutorial.rst
Normal 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!
|
12
eg/python3/futures/hello-world.hy
Normal file
12
eg/python3/futures/hello-world.hy
Normal 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)))
|
@ -20,7 +20,7 @@
|
||||
|
||||
|
||||
__appname__ = "hy"
|
||||
__version__ = "0.9.3"
|
||||
__version__ = "0.9.5"
|
||||
|
||||
|
||||
import hy.importer # NOQA
|
||||
|
473
hy/compiler.py
473
hy/compiler.py
@ -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
|
||||
|
@ -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():
|
||||
|
@ -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
85
hy/core/mangles.py
Normal 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)
|
@ -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):
|
||||
|
@ -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
109
hy/mangle.py
Normal 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
|
@ -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
28
hy/util.py
Normal 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
|
@ -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."))))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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():
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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))))
|
||||
|
@ -2,3 +2,7 @@
|
||||
|
||||
def kwtest(*args, **kwargs):
|
||||
return kwargs
|
||||
|
||||
|
||||
def function_with_a_dash():
|
||||
pass
|
||||
|
Loading…
Reference in New Issue
Block a user