Conflicts:
	hy/cmdline.py
This commit is contained in:
Christian Weilbach 2014-02-24 02:00:52 +01:00
commit d388ee2fd5
92 changed files with 4793 additions and 510 deletions

11
.coveragerc Normal file
View File

@ -0,0 +1,11 @@
[run]
omit =
*/python?.?/*
*/lib-python/?.?/*.py
*/lib_pypy/_*.py
*/site-packages/nose/*
*/pypy/*
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover

View File

@ -7,13 +7,15 @@ python:
- "2.6"
# command to install dependencies
install:
- pip install -r requirements.txt --use-mirrors
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install argparse importlib unittest2 astor --use-mirrors; fi
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install astor --use-mirrors; fi
- if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then pip install astor --use-mirrors; fi
- pip install -r requirements.txt
- pip install coveralls
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install argparse importlib unittest2 astor; fi
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install astor; fi
- if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then pip install astor; fi
- python setup.py -q install
# # command to run tests
script: make travis
after_success: coveralls
notifications:
email:
- paultag@gmail.com

25
AUTHORS
View File

@ -18,3 +18,28 @@
* Ralph Möritz <ralph.moeritz@outlook.com>
* Josh McLaughlin <josh@phear.cc>
* Berker Peksag <berker.peksag@gmail.com>
* Henrique Carvalho Alves <hcarvalhoalves@gmail.com>
* Joe Hakim Rahme <joehakimrahme@gmail.com>
* Kenan Bölükbaşı <kenanbolukbasi@gmail.com>
* Abhishek L <abhishek.lekshmanan@gmail.com>
* Christopher Browne <cbbrowne@ca.afilias.info>
* Clinton N. Dreisbach <crnixon@gmail.com>
* D. Joe <deejoe+castanea@etrumeus.com>
* Duncan McGreggor <duncan.mcgreggor@rackspace.com>
* E. Anders Lannerback <anders@lannerback.net>
* Jack <jackjrabbit+github@gmail.com>
* Johan Euphrosine <proppy@google.com>
* Kevin Zita <kzita@kent.edu>
* Matt Fenwick <mfenwick100@gmail.com>
* Sean B. Palmer <sean@miscoranda.com>
* Thom Neale <twneale@gmail.com>
* Tuukka Turto <tuukka.turto@oktaeder.net>
* Vasudev Kamath <kamathvasudev@gmail.com>
* Yuval Langer <yuval.langer@gmail.com>
* Fatih Kadir Akın <fka@fatihak.in>
* Jack Hooper <contact.jhooper@gmail.com>
* Brian McKenna <brian@brianmckenna.org>
* Halit Alptekin <info@halitalptekin.com>
* Richard Parsons <richard.lee.parsons@gmail.com>
* han semaj <sangho.nah@gmail.com>
* kirbyfan64 <kirbyfan64@users.noreply.github.com>

View File

@ -13,6 +13,7 @@ all:
@echo " - tox"
@echo " - d"
@echo " - r"
@echo " - clean"
@echo ""
docs:
@ -53,11 +54,19 @@ diff:
r: d tox diff
travis:
nosetests -s
nosetests -s --with-coverage --cover-package hy
ifeq (PyPy,$(findstring PyPy,$(shell python -V 2>&1 | tail -1)))
@echo "skipping flake8 on pypy"
else
flake8 hy bin tests
endif
clean:
@find . -name "*.pyc" -exec rm {} \;
@find -name __pycache__ -delete
@${RM} -r -f .tox
@${RM} -r -f dist
@${RM} -r -f *.egg-info
@${RM} -r -f docs/_build
.PHONY: docs

73
NEWS
View File

@ -1,3 +1,76 @@
Changes from Hy 0.9.11
tl;dr:
0.9.12 comes with some massive changes,
We finally took the time to implement gensym, as well as a few
other bits that help macro writing. Check the changelog for
what exactly was added.
The biggest feature, Reader Macros, landed later
in the cycle, but were big enough to warrent a release on it's
own. A huge thanks goes to Foxboron for implementing them
and a massive hug goes out to olasd for providing ongoing
reviews during the development.
Welcome to the new Hy contributors, Henrique Carvalho Alves,
Kevin Zita and Kenan Bölükbaşı. Thanks for your work so far,
folks!
Hope y'all enjoy the finest that 2013 has to offer,
- Hy Society
* Special thanks goes to Willyfrog, Foxboron and theanalyst for writing
0.9.12's NEWS. Thanks, y'all! (PT)
[ Language Changes ]
* Translate foo? -> is_foo, for better Python interop. (PT)
* Reader Macros!
* Operators + and * now can work without arguments
* Define kwapply as a macro
* Added apply as a function
* Instant symbol generation with gensym
* Allow macros to return None
* Add a method for casting into byte string or unicode depending on python version
* flatten function added to language
* Add a method for casting into byte string or unicode depending on python version
* Added type coercing to the right integer for the platform
[ Misc. Fixes ]
* Added information about core team members
* Documentation fixed and extended
* Add astor to install_requires to fix hy --spy failing on hy 0.9.11.
* Convert stdout and stderr to UTF-8 properly in the run_cmd helper.
* Update requirements.txt and setup.py to use rply upstream.
* tryhy link added in documentation and README
* Command line options documented
* Adding support for coverage tests at coveralls.io
* Added info about tox, so people can use it prior to a PR
* Added the start of hacking rules
* Halting Problem removed from example as it was nonfree
* Fixed PyPI is now behind a CDN. The --use-mirrors option is deprecated.
* Badges for pypi version and downloads.
[ Syntax Fixes ]
* get allows multiple arguments
[ Bug Fixes ]
* OSX: Fixes for readline Repl problem which caused HyREPL not allowing 'b'
* Fix REPL completions on OSX
* Make HyObject.replace more resilient to prevent compiler breakage.
[ Contrib changes ]
* Anaphoric macros added to contrib
* Modified eg/twisted to follow the newer hy syntax
* Added (experimental) profile module
Changes from Hy 0.9.10
* Many thanks to Guillermo Vayá (Willyfrog) for preparing this release's

View File

@ -1,14 +1,14 @@
Hy
==
![](https://raw.github.com/hylang/shyte/18f6925e08684b0e1f52b2cc2c803989cd62cd91/imgs/xkcd.png)
Lisp and Python should love each other. Let's make it happen.
[![Build Status](https://travis-ci.org/hylang/hy.png?branch=master)](https://travis-ci.org/hylang/hy)
[![Downloads](https://pypip.in/d/hy/badge.png)](https://crate.io/packages/hy)
[![version](https://pypip.in/v/hy/badge.png)](https://crate.io/packages/hy)
[![Coverage Status](https://coveralls.io/repos/hylang/hy/badge.png)](https://coveralls.io/r/hylang/hy)
![](https://raw.github.com/hylang/shyte/18f6925e08684b0e1f52b2cc2c803989cd62cd91/imgs/xkcd.png)
Lisp and Python should love each other. Let's make it happen. [Try it](http://try-hy.appspot.com/).
Hylarious Hacks
---------------
@ -19,6 +19,7 @@ Hylarious Hacks
[Hy IRC bot](https://github.com/hylang/hygdrop)
[miniKanren in Hy](https://github.com/algernon/adderall)
OK, so, why?
------------
@ -30,7 +31,7 @@ Why?
Well, I wrote Hy to help people realize one thing about Python:
It's really goddamn awesome.
It's really awesome.
Oh, and lisps are neat.
@ -42,7 +43,7 @@ Project
-------
* Code: https://github.com/hylang/hy
* Docs: http://hy.rtfd.org/
* Quickstart: http://hy.rtfd.org/quickstart
* Docs: http://hylang.org/
* Quickstart: http://hylang.org/en/latest/quickstart.html
* Bug reports: We have no bugs! Your bugs are your own! (https://github.com/hylang/hy/issues)
* License: MIT (Expat)

View File

@ -1,25 +1,39 @@
#!/usr/bin/env python
from __future__ import print_function
from hy.importer import (import_file_to_ast, import_file_to_hst)
from hy.importer import (import_file_to_ast, import_file_to_module,
import_file_to_hst)
import argparse
import sys
import astor.codegen
import sys
import ast
module_name = "<STDIN>"
hst = import_file_to_hst(sys.argv[1])
print(hst)
print("")
print("")
_ast = import_file_to_ast(sys.argv[1], module_name)
print("")
print("")
print(ast.dump(_ast))
print("")
print("")
print(astor.codegen.to_source(_ast))
parser = argparse.ArgumentParser(
prog="hy2py",
usage="%(prog)s [options] FILE",
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--with-source", "-s", action="store_true",
help="Show the parsed source structure")
parser.add_argument("--with-ast", "-a", action="store_true",
help="Show the generated AST")
parser.add_argument("--without-python", "-np", action="store_true",
help="Do not show the python code generated from the AST")
parser.add_argument('args', nargs=argparse.REMAINDER, help=argparse.SUPPRESS)
import_file_to_module(module_name, sys.argv[1])
options = parser.parse_args(sys.argv[1:])
if options.with_source:
hst = import_file_to_hst(options.args[0])
print(str(hst).encode("utf-8"))
print()
print()
_ast = import_file_to_ast(options.args[0], module_name)
if options.with_ast:
print(astor.dump(_ast).encode("utf-8"))
print()
print()
if not options.without_python:
print(astor.codegen.to_source(_ast).encode("utf-8"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
docs/_static/cuddles-transparent.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

127
docs/_static/hy-logo.svg vendored Normal file
View File

@ -0,0 +1,127 @@
<?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="800"
height="800"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="hy-logo.svg"
inkscape:export-filename="/home/kenanb/doc/downloads/hy/hylogo-final.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="0.98994948"
inkscape:cx="548.63417"
inkscape:cy="397.15812"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="false"
inkscape:window-width="1918"
inkscape:window-height="1078"
inkscape:window-x="1"
inkscape:window-y="1"
inkscape:window-maximized="0"
inkscape:snap-bbox="false"
inkscape:snap-global="false"
fit-margin-top="16"
fit-margin-left="16"
fit-margin-right="16"
fit-margin-bottom="16">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="16"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
originx="-115.20959px"
originy="-32px" />
</sodipodi:namedview>
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-115.20959,-220.36216)"
style="opacity:0.27007303;display:none"
sodipodi:insensitive="true">
<path
inkscape:connector-curvature="0"
style="font-size:150px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono Bold"
d="m 1127.125,972.86218 c -5.7617,3e-5 -10.6211,1.77932 -14.625,5.34375 -4.0527,3.51564 -7.5253,9.16017 -10.4062,16.875 l -34.0625,90.87497 22.5625,0 17.9375,-53.25 16.5312,47 5.2188,0 c 3.5888,0 6.4069,-0.5191 8.4375,-1.5312 0.7042,-1.3842 1.4141,-2.9775 2.125,-4.8125 l 1.625,-4.375 c 0.123,-0.3235 0.2521,-0.6453 0.375,-0.9688 l -24.1875,-61.8125 1.5937,-4.4062 c 2.0996,-5.41993 4.168,-8.84766 6.2188,-10.31252 2.0508,-1.51366 5.2929,-2.28123 9.6875,-2.28125 l 8.7187,0 0,-16.34375 -17.75,0 z"
id="path3105" />
<path
id="path3113"
d="m 1143.865,1103.1285 c 5.7617,0 10.6211,-1.7793 14.625,-5.3437 4.0527,-3.5157 7.5254,-9.1602 10.4063,-16.875 l 34.0625,-90.87503 -22.5625,0 -17.9375,53.25003 -16.5313,-47.00003 -5.2187,0 c -3.5889,2e-5 -6.407,0.5191 -8.4375,1.53125 -0.7043,1.38419 -1.4141,2.97748 -2.125,4.81248 l -1.625,4.375 c -0.1231,0.3235 -0.2521,0.6453 -0.375,0.9688 l 24.1875,61.8125 -1.5938,4.4062 c -2.0996,5.4199 -4.168,8.8477 -6.2187,10.3125 -2.0508,1.5137 -5.293,2.2813 -9.6875,2.2813 l -8.7188,0 0,16.3437 17.75,0 z"
style="font-size:150px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono Bold"
inkscape:connector-curvature="0" />
<g
id="g3159"
transform="matrix(3.7056938,0,0,3.7056938,-549.15167,-2089.7704)">
<path
id="path3105-1"
d="m 259.67532,701.4406 c -5.7617,3e-5 -10.6211,1.77932 -14.625,5.34375 -4.0527,3.51564 -7.5253,9.16017 -10.4062,16.875 l -34.0625,90.87497 22.5625,0 17.9375,-53.25 16.5312,47 5.2188,0 c 3.5888,0 6.4069,-0.5191 8.4375,-1.5312 0.7042,-1.3842 1.4141,-2.9775 2.125,-4.8125 l 1.625,-4.375 c 0.123,-0.3235 0.2521,-0.6453 0.375,-0.9688 l -24.1875,-61.8125 1.5937,-4.4062 c 2.0996,-5.41993 4.168,-8.84766 6.2188,-10.31252 2.0508,-1.51366 5.2929,-2.28123 9.6875,-2.28125 l 8.7187,0 0,-16.34375 -17.75,0 z"
style="font-size:150px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono Bold"
inkscape:connector-curvature="0" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer"
transform="translate(-115.20959,-167.99998)">
<g
id="g4380"
transform="translate(70.923045,4.6573104)">
<path
inkscape:connector-curvature="0"
d="m 424,376 c -30.70325,0 -71.00099,1.93453 -108.15625,30.9375 -37.15526,29.00297 -68.93905,79.572 -101.90625,170.125 l -0.0312,0.0312 0,0.0625 -36,100 -0.0312,0.0312 0,0.0625 c -14.1947,39.7503 -38.24888,98.17928 -44.90625,152.6875 -3.32868,27.25411 -3.11264,55.04503 11.25,80.25 14.36264,25.20497 44.85158,41.14246 79.15625,41.8125 l 0.3125,0 5.36326,0 198.18708,10e-5 -22.18718,-64 -180.42566,-10e-5 c -61.50246,0 -16.28018,-106.09428 13.46875,-189.15625 C 310.49291,491.32309 324.80155,444.72669 424,440 l 41.375,0 -23.03125,-64 z m 0,80 c -38.92481,0 -73.78086,19.97667 -87.21875,47.40625 -56.59199,115.51626 -79.59795,197.97859 -118.2187,305.24995 -7.99944,29.85431 -15.11264,63.84878 19.66548,63.84878 l 25.52207,-0.50508 c 25.60612,-71.1243 51.22325,-142.24463 76.8125,-213.375 l 48,133.375 68.03125,0 -81.4375,-226.28125 C 383.78297,545.58564 400.43363,520.58241 424,520 l 70.21875,0 -23.03125,-64 z m 40.57309,495.9999 c 30.70325,0 71.00099,-1.93453 108.15625,-30.9375 37.15526,-29.00297 68.93905,-79.572 101.90625,-170.125 l 0.0312,-0.0312 0,-0.0625 36,-100 0.0312,-0.0312 0,-0.0625 c 14.1947,-39.7503 38.24888,-98.17928 44.90625,-152.6875 3.32868,-27.25411 3.11264,-55.04503 -11.25,-80.25 C 729.9916,392.60753 699.50266,376.67004 665.19799,376 l -0.3125,0 -5.36326,0 -198.18708,-10e-5 22.18718,64 180.42566,10e-5 c 61.50246,0 16.28018,106.09428 -13.46875,189.15625 -72.39906,207.52056 -86.7077,254.11696 -185.90615,258.84365 l -41.375,0 23.03125,64 z m 0,-80 c 38.92481,0 73.78086,-19.97667 87.21875,-47.40625 56.59199,-115.51626 79.59795,-197.97859 118.2187,-305.24995 7.99944,-29.85431 15.11264,-63.84878 -19.66548,-63.84878 L 624.82299,456 c -25.60612,71.1243 -51.22325,142.24463 -76.8125,213.375 l -48,-133.375 -68.03125,0 81.4375,226.28125 c -8.62662,20.13301 -25.27728,45.13624 -48.84365,45.71865 l -70.21875,0 23.03125,64 z"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#324b64;fill-opacity:1;stroke:none;stroke-width:64;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
id="path3027" />
<g
transform="matrix(0.55171121,0,0,0.55171121,152.06076,168.15948)"
id="g4767">
<path
transform="matrix(1.8125425,0,0,1.8125425,-275.61659,-304.7962)"
id="path4823"
d="m 372.5,174.6875 c -15.07942,-0.12167 -30.20115,4.89485 -42.65625,13.59375 -9.29705,10.77953 -28.57797,12.36756 -32.46875,27.03125 10.22244,12.48089 1.55348,27.56118 -10.84375,35.125 -16.93831,9.02537 1.72241,21.14226 1.75,32.46875 -13.44978,12.51108 -7.05171,31.12498 7.5,39.8125 1.17743,10.78132 8.23416,18.98744 16.8125,24.59375 2.31044,2.25555 5.43462,3.94695 9.3125,4.90625 0.78161,1.67417 0.56512,2.4779 -0.1875,3.75 -0.91788,1.55142 -3.25057,3.56885 -6.03125,5.78125 -2.78068,2.2124 -5.9808,4.68709 -8.40625,8.09375 -2.42545,3.40666 -3.88422,7.9987 -2.8125,13.1875 2.24294,10.85942 8.83105,19.38602 14.03125,27.5625 5.14367,8.0876 8.88274,15.48898 7.59375,24.5 -0.93857,3.28337 -4.50553,5.76222 -9.125,6.65625 -4.66444,0.90273 -9.74759,-0.21802 -11.75,-2.09375 -1.91466,-1.79351 -2.69211,-6.225 -2.4375,-10.5 0.25461,-4.275 1.25,-7.90625 1.25,-7.90625 a 3.5273523,3.5273523 0 0 0 0.15625,-0.96875 c 0,0 -3.78582,-3.76038 -5.3125,-3.625 -1.52668,0.13538 -3.53794,0.80778 -5.28125,2.4375 -3.48662,3.25944 -5.78125,9.42944 -5.78125,21.6875 0,10.11507 8.0593,17.33344 17.25,21.53125 9.1907,4.19781 19.95962,5.88577 28.375,4.625 13.11602,-1.96502 22.4736,-11.58718 27.46875,-23.1875 4.99515,-11.60032 5.98416,-25.35896 2.40625,-37.0625 -2.21905,-7.25864 -6.10451,-12.11902 -7.34375,-14.625 -0.46472,-0.93974 -0.46633,-1.17594 -0.46875,-1.1875 -0.002,0.0324 0.29253,-0.21854 0.90625,-0.625 7.087,8.15485 11.45399,24.03427 16.0625,38.65625 2.47696,7.85895 5.11869,15.31152 8.96875,21.15625 3.85006,5.84473 9.4406,10.21875 16.71875,10.21875 7.56711,0 13.42004,-3.61165 16.59375,-9 3.17371,-5.38835 4.09954,-12.19554 3.90625,-19.375 -0.11244,-4.17654 -0.64961,-8.48538 -1.4375,-12.78125 0.4323,1.2058 0.87548,2.40443 1.4375,3.59375 0.0365,0.096 0.0877,0.18591 0.125,0.28125 4.20345,10.73195 13.38467,17.22695 23.53125,20.03125 10.23672,2.82921 21.58755,2.1747 31.125,-1.15625 10.51202,-3.66023 16.4508,-11.85529 20.125,-20.6875 3.6742,-8.83221 5.44566,-18.52323 7.90625,-26.375 a 3.5273523,3.5273523 0 0 0 0.15625,-0.71875 c 0.47873,-4.96136 2.2287,-7.53263 4.4375,-9.40625 2.2088,-1.87362 5.0364,-2.91579 7.625,-3.78125 1.2943,-0.43273 2.51085,-0.78237 3.75,-1.40625 0.61958,-0.31194 1.26788,-0.71201 1.9375,-1.4375 0.66962,-0.72549 1.29058,-1.97884 1.28125,-3.21875 -0.0187,-2.47983 -1.3628,-3.69514 -2.75,-4.96875 -1.3872,-1.27361 -3.27983,-2.63478 -5.90625,-4.375 a 3.5273523,3.5273523 0 0 0 -0.84375,-0.40625 c -10.10542,-3.2779 -19.89399,0.63285 -27.03125,7.09375 -7.13726,6.4609 -12.10807,15.44902 -13.53125,23.96875 -0.97301,5.82488 -4.36936,12.9369 -8,16.15625 -1.81532,1.60967 -3.42385,2.21033 -4.9375,2.0625 -1.51365,-0.14783 -3.50244,-1.08316 -5.96875,-4.28125 -4.82498,-6.25664 -4.49185,-16.06752 -4.71875,-26.4375 -0.11345,-5.18499 -0.42411,-10.46564 -2.09375,-15.4375 -1.66964,-4.97186 -4.89156,-9.6313 -10.28125,-12.78125 -2.24859,-1.31415 -3.45734,-2.46925 -3.96875,-3.21875 -0.51141,-0.7495 -0.49137,-0.96664 -0.40625,-1.46875 0.17023,-1.00422 1.86932,-3.37212 4.4375,-5.9375 1.43992,-1.43835 3.07275,-2.9525 4.5625,-4.65625 0.28014,-0.15814 0.56522,-0.30899 0.84375,-0.46875 11.08515,-6.35836 14.87492,-19.92026 23.03125,-30.625 8.08505,-14.21649 -15.80582,-26.57672 0.28125,-38.4375 11.38748,-18.35764 -18.71563,-23.24149 -21.0625,-38.53125 5.52108,-7.07349 9.30975,-19.30272 -1.03125,-25.90625 -15.39103,-8.41439 -28.84075,-20.62641 -45.5,-26.875 -7.48324,-3.09177 -15.3825,-4.56127 -23.28125,-4.625 z"
style="fill:#324b64;fill-opacity:1;fill-rule:nonzero"
inkscape:connector-curvature="0" />
<path
id="path4786"
d="m 368.5,182.6875 c -5.91119,0.19466 -11.7873,1.6496 -17.09375,4.4375 -40.87154,24.22655 -59.29982,78.16008 -45.25,123.03125 6.80872,13.08565 -2.75882,35.85544 17.8125,39.40625 8.22469,13.69954 -19.92642,16.05056 -16.40625,33.09375 4.06676,19.68957 24.87466,32.83878 21.65625,53.625 -2.73147,10.16848 -20.16251,12.45197 -26.75,6.28125 -6.58749,-6.17067 -2.1875,-21.90625 -2.1875,-21.90625 0,-6e-5 -9.28125,-3.1211 -9.28125,20.46875 0,16.41059 26.66615,24.92392 41.59375,22.6875 23.39653,-3.50524 33.58401,-34.21336 27,-55.75 -4.01576,-13.13578 -14.08568,-17.18002 -2.75,-21.84375 18.89192,17.41576 17.13135,70.90625 40.96875,70.90625 26.27425,0 16.76722,-44.62977 6.625,-64.03125 13.53331,2.88396 11.89764,17.77826 17.1875,28.71875 7.40827,19.46051 32.64996,23.26238 50.3125,17.09375 18.78257,-6.53998 20.69742,-28.45898 25.8125,-44.78125 2.21904,-22.99703 32.39525,-11.7782 11.96875,-25.3125 -17.41791,-5.64985 -33.47565,13.16949 -36,28.28125 -2.19653,13.14943 -13.8095,30.25397 -25.1875,15.5 -11.75299,-15.2403 2.57624,-42.82554 -16.0625,-53.71875 -20.22844,-11.82223 16.87733,-20.07202 4.96875,-33.8125 2.88512,-15.06216 7.83547,-29.92822 7.84375,-45.40625 -1.96404,-32.43765 -23.19668,-59.87568 -44.96875,-82.40625 -6.97258,-9.31648 -18.02993,-14.23594 -29.28125,-14.5625 -0.84384,-0.0245 -1.68679,-0.0278 -2.53125,0 z m 56.3125,134.4375 c 3.36242,-0.23238 6.45152,2.22293 7.78125,9.0625 3.3773,8.33288 -4.14309,20.68819 -13.875,16.40625 -12.86867,-6.80895 -2.4991,-24.87488 6.09375,-25.46875 z m -97.28125,1 c 3.42434,0.11089 6.25689,2.84997 6.78125,9.71875 2.26488,8.63726 -7.08202,19.69944 -16.0625,15.03125 -12.0702,-8.23726 0.53016,-25.03339 9.28125,-24.75 z M 504.375,373.5 c 5.17264,0.12312 -7.41937,4.44292 -8.6875,9.46875 -7.16855,14.54272 -3.43939,22.15519 -13.84375,39.6875 3.34932,-26.9807 5.73096,-46.43554 19.15625,-48.9375 1.54849,-0.16455 2.63605,-0.23634 3.375,-0.21875 z m -138.53125,14.21875 c 17.59821,7.9282 12.57268,17.3815 16.625,38.78125 -7.19252,-17.18032 -7.75412,-25.68218 -16.625,-38.78125 z m -9.375,41.65625 c -3.70755,11.69208 -11.78441,27.90384 -25.875,29.125 -11.39488,0.49014 -24.61736,-2.53776 -31.96875,-11.84375 10.59997,9.08712 36.82121,7.38873 49.84375,-6.5625 l 8,-10.71875 z"
style="fill:#e1af64;fill-opacity:1;fill-rule:nonzero"
inkscape:connector-curvature="0"
transform="matrix(1.8125425,0,0,1.8125425,-275.61659,-304.7962)" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -13,3 +13,6 @@ into the public domain under CC0... see CC0_1.0.txt!
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/>.
The new Hy logo is Copyright CC-BY-SA 4.0 Kenan Bölükbaşı, thanks, Kenan!
http://www.kenanb.com/posts/Hy-Programming-Language-Logo.html

View File

@ -11,7 +11,9 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
import os
import sys
import time
sys.path.append(os.path.abspath(".."))
import hy
@ -28,7 +30,7 @@ import hy
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = []
extensions = ['sphinx.ext.todo']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -44,7 +46,7 @@ master_doc = 'index'
# General information about the project.
project = u'hy'
copyright = u'2013, Paul Tagliamonte'
copyright = u'2013-%s, Paul Tagliamonte' % time.strftime('%Y')
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -67,7 +69,7 @@ release = hy.__version__
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ['_build', 'coreteam.rst']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

197
docs/contrib/anaphoric.rst Normal file
View File

@ -0,0 +1,197 @@
================
Anaphoric Macros
================
.. versionadded:: 0.9.12
The anaphoric macros module makes functional programming in Hy very
concise and easy to read.
An anaphoric macro is a type of programming macro that
deliberately captures some form supplied to the macro which may be
referred to by an anaphor (an expression referring to another).
-- Wikipedia (http://en.wikipedia.org/wiki/Anaphoric_macro)
Macros
======
.. _ap-if:
ap-if
-------
Usage: ``(ap-if (foo) (print it))``
Evaluate the first form for trutheyness, and bind it to ``it`` in both the
true and false branch.
.. _ap-each:
ap-each
-------
Usage: ``(ap-each [1 2 3 4 5] (print it))``
Evaluate the form for each element in the list for side-effects.
.. _ap-each-while:
ap-each-while
=============
Usage: ``(ap-each-while list pred body)``
Evaluate the form for each element where the predicate form returns
True.
.. code-block:: clojure
=> (ap-each-while [1 2 3 4 5 6] (< it 4) (print it))
1
2
3
.. _ap-map:
ap-map
======
Usage: ``(ap-map form list)``
The anaphoric form of map works just like regular map except that
instead of a function object it takes a Hy form. The special name,
``it`` is bound to the current object from the list in the iteration.
.. code-block:: clojure
=> (list (ap-map (* it 2) [1 2 3]))
[2, 4, 6]
.. _ap-map-when:
ap-map-when
===========
Usage: ``(ap-map-when predfn rep list)``
Evaluate a mapping over the list using a predicate function to
determin when to apply the form.
.. code-block:: clojure
=> (list (ap-map-when odd? (* it 2) [1 2 3 4]))
[2, 2, 6, 4]
=> (list (ap-map-when even? (* it 2) [1 2 3 4]))
[1, 4, 3, 8]
.. _ap-filter:
ap-filter
=========
Usage: ``(ap-filter form list)``
As with ``ap-map`` we take a special form instead of a function to
filter the elements of the list. The special name ``it`` is bound to
the current element in the iteration.
.. code-block:: clojure
=> (list (ap-filter (> (* it 2) 6) [1 2 3 4 5]))
[4, 5]
.. _ap-reject:
ap-reject
=========
Usage: ``(ap-reject form list)``
This function does the opposite of ``ap-filter``, it rejects the
elements passing the predicate . The special name ``it`` is bound to
the current element in the iteration.
.. code-block:: clojure
=> (list (ap-reject (> (* it 2) 6) [1 2 3 4 5]))
[1, 2, 3]
.. _ap-dotimes:
ap-dotimes
==========
Usage ``(ap-dotimes n body)``
This function evaluates the body *n* times, with the special
variable ``it`` bound from *0* to *1-n*. It is useful for side-effects.
.. code-block:: clojure
=> (setv n [])
=> (ap-dotimes 3 (.append n it))
=> n
[0, 1, 2]
.. _ap-first:
ap-first
========
Usage ``(ap-first predfn list)``
This function returns the first element that passes the predicate or
``None``, with the special variable ``it`` bound to the current element in
iteration.
.. code-block:: clojure
=>(ap-first (> it 5) (range 10))
6
.. _ap-last:
ap-last
========
Usage ``(ap-last predfn list)``
This function returns the last element that passes the predicate or
``None``, with the special variable ``it`` bound to the current element in
iteration.
.. code-block:: clojure
=>(ap-last (> it 5) (range 10))
9
.. _ap-reduce:
ap-reduce
=========
Usage ``(ap-reduce form list &optional initial-value)``
This function returns the result of applying form to the first 2
elements in the body and applying the result and the 3rd element
etc. until the list is exhausted. Optionally an initial value can be
supplied so the function will be applied to initial value and the
first element instead. This exposes the element being iterated as
``it`` and the current accumulated value as ``acc``.
.. code-block:: clojure
=>(ap-reduce (+ it acc) (range 10))
45

12
docs/contrib/index.rst Normal file
View File

@ -0,0 +1,12 @@
Contrib Modules Index
=====================
Contents:
.. toctree::
:maxdepth: 3
anaphoric
loop
multi

56
docs/contrib/loop.rst Normal file
View File

@ -0,0 +1,56 @@
==========
loop/recur
==========
.. versionadded:: 0.9.13
The loop/recur macro gives programmers a simple way to use tail-call
optimization (TCO) in their Hy code.
A tail call is a subroutine call that happens inside another
procedure as its final action; it may produce a return value which
is then immediately returned by the calling procedure. If any call
that a subroutine performs, such that it might eventually lead to
this same subroutine being called again down the call chain, is in
tail position, such a subroutine is said to be tail-recursive,
which is a special case of recursion. Tail calls are significant
because they can be implemented without adding a new stack frame
to the call stack. Most of the frame of the current procedure is
not needed any more, and it can be replaced by the frame of the
tail call. The program can then jump to the called
subroutine. Producing such code instead of a standard call
sequence is called tail call elimination, or tail call
optimization. Tail call elimination allows procedure calls in tail
position to be implemented as efficiently as goto statements, thus
allowing efficient structured programming.
-- Wikipedia (http://en.wikipedia.org/wiki/Tail_call)
Macros
======
.. _loop:
loop
-----
``loop`` establishes a recursion point. With ``loop``, ``recur``
rebinds the variables set in the recursion point and sends code
execution back to that recursion point. If ``recur`` is used in a
non-tail position, an exception is thrown.
Usage: `(loop bindings &rest body)`
Example:
.. code-block:: clojure
(require hy.contrib.loop)
(defn factorial [n]
(loop [[i n] [acc 1]]
(if (zero? i)
acc
(recur (dec i) (* acc i)))))
(factorial 1000)

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

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

15
docs/coreteam.rst Normal file
View File

@ -0,0 +1,15 @@
* `Julien Danjou <https://github.com/jd>`_
* `Morten Linderud <https://github.com/Foxboron>`_
* `J Kenneth King <https://github.com/agentultra>`_
* `Gergely Nagy <https://github.com/algernon>`_
* `Tuukka Turto <https://github.com/tuturto>`_
* `Karen Rustad <https://github.com/aldeka>`_
* `Abhishek L <https://github.com/theanalyst>`_
* `Christopher Allan Webber <https://github.com/cwebber>`_
* `Konrad Hinsen <https://github.com/khinsen>`_
* `Will Kahn-Greene <https://github.com/willkg>`_
* `Paul Tagliamonte <https://github.com/paultag>`_
* `Nicolas Dandrimont <https://github.com/olasd>`_
* `Bob Tolbert <https://github.com/rwtolbert>`_
* `Berker Peksag <https://github.com/berkerpeksag>`_
* `Clinton N. Dreisbach <https://github.com/cndreisbach>`_

View File

@ -70,6 +70,10 @@ To run the tests::
Write tests---tests are good!
Also, it is good to run the tests for all the platforms supported and for pep8 compliant code.
You can do so by running tox::
$ tox
Document!
=========
@ -83,3 +87,26 @@ To build the docs in HTML::
$ make html
Write docs---docs are good! Even this doc!
Core Development Rules
======================
All incoming changes need to be acked by 2 different members of Hylang's
core team. Additional review is clearly welcome, but we need a minimum of
2 signoffs for any change.
If a core member is sending in a PR, please find 2 core members that don't
include the PR submitter. The idea here is that one can work with the PR
author, and a second acks the entire change set.
If the change is adding documentation, feel free to just merge after one
ACK. We've got low coverage, so it'd be great to keep that barrier low.
Core Team
=========
Core development team of hy consists of following developers.
.. include:: coreteam.rst

View File

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

View File

@ -28,16 +28,49 @@ languages.
and `i♥u` will become `hy_iu_t0x`.
* Symbols that contain dashes will have them replaced with underscores. For
example, `render-template` will become `render_template`.
example, `render-template` will become `render_template`. This means that
symbols with dashes will shadow their underscore equivalents, and vice
versa.
Builtins
========
Hy features a number special forms that are used to help generate
Hy features a number of 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.
.
-
.. versionadded:: 0.9.13
`.` is used to perform attribute access on objects. It uses a small DSL
to allow quick access to attributes and items in a nested datastructure.
For instance,
.. code-block:: clj
(. foo bar baz [(+ 1 2)] frob)
Compiles down to
.. code-block:: python
foo.bar.baz[1 + 2].frob
`.` compiles its first argument (in the example, `foo`) as the object on
which to do the attribute dereference. It uses bare symbols as
attributes to access (in the example, `bar`, `baz`, `frob`), and
compiles the contents of lists (in the example, ``[(+ 1 2)]``) for
indexation. Other arguments throw a compilation error.
Access to unknown attributes throws an :exc:`AttributeError`. Access to
unknown keys throws an :exc:`IndexError` (on lists and tuples) or a
:exc:`KeyError` (on dicts).
->
--
@ -56,7 +89,7 @@ The following code demonstrates this:
---
`->>` or `threading tail macro` is similar to `threading macro` but instead of
inserting each expression into the next expressions first argument place it
inserting each expression into the next expressions first argument place, it
appends it as the last argument. The following code demonstrates this:
.. code-block:: clj
@ -66,6 +99,37 @@ appends it as the last argument. The following code demonstrates this:
5 10
apply
-----
`apply` is used to apply an optional list of arguments and an optional
dictionary of kwargs to a function.
Usage: `(apply fn-name [args] [kwargs])`
Examples:
.. code-block:: clj
(defn thunk []
"hy there")
(apply thunk)
;=> "hy there"
(defn total-purchase [price amount &optional [fees 1.05] [vat 1.1]]
(* price amount fees vat))
(apply total-purchase [10 15])
;=> 173.25
(apply total-purchase [10 15] {"vat" 1.05})
;=> 165.375
(apply total-purchase [] {"price" 10 "amount" 15 "vat" 1.05})
;=> 165.375
and
---
@ -205,11 +269,12 @@ however is called only for every other value in the list.
;; assuming that (side-effect1) and (side-effect2) are functions and
;; collection is a list of numerical values
(for (x collection) (do
(side-effect1 x)
(if (% x 2)
(continue))
(side-effect2 x)))
(for [x collection]
(do
(side-effect1 x)
(if (% x 2)
(continue))
(side-effect2 x)))
do / progn
@ -218,7 +283,7 @@ do / progn
the `do` and `progn` forms are used to evaluate each of their arguments and
return the last one. Return values from every other than the last argument are
discarded. It can be used in `lambda` or `list-comp` to perform more complex
logic as show by one of the examples.
logic as shown by one of the examples.
Some example usage:
@ -287,6 +352,8 @@ below:
Meow
.. _defn:
defn / defun
------------
@ -357,13 +424,38 @@ Parameters may have following keywords in front of them:
=> (zig-zag-sum 1 2 3 4 5 6)
-3
.. _defn-alias / defun-alias:
defn-alias / defun-alias
------------------------
.. versionadded:: 0.9.13
The `defn-alias` and `defun-alias` macros are much like `defn`_ above,
with the difference that instead of defining a function with a single
name, these can also define aliases. Other than taking a list of
symbols for function names as the first parameter, `defn-alias` and
`defun-alias` have no other differences compared to `defn` and
`defun`.
.. code-block:: clj
=> (defn-alias [main-name alias] []
... (print "Hello!"))
=> (main-name)
"Hello!"
=> (alias)
"Hello!"
.. _defmacro:
defmacro
--------
`defmacro` is used to define macros. The general format is
`(defmacro [parameters] expr)`.
`(defmacro name [parameters] expr)`.
Following example defines a macro that can be used to swap order of elements in
The following example defines a macro that can be used to swap order of elements in
code, allowing the user to write code in infix notation, where operator is in
between the operands.
@ -378,6 +470,102 @@ between the operands.
=> (infix (1 + 1))
2
.. _defmacro-alias:
defmacro-alias
--------------
`defmacro-alias` is used to define macros with multiple names
(aliases). The general format is `(defmacro-alias [names] [parameters]
expr)`. It creates multiple macros with the same parameter list and
body, under the specified list of names.
The following example defines two macros, both of which allow the user
to write code in infix notation.
.. code-block:: clj
=> (defmacro-alias [infix infi] [code]
... (quasiquote (
... (unquote (get code 1))
... (unquote (get code 0))
... (unquote (get code 2)))))
=> (infix (1 + 1))
2
=> (infi (1 + 1))
2
.. _defmacro/g!:
defmacro/g!
------------
.. versionadded:: 0.9.12
`defmacro/g!` is a special version of `defmacro` that is used to
automatically generate :ref:`gensym` for any symbol that
starts with ``g!``.
So ``g!a`` would become ``(gensym "a")``.
.. seealso::
Section :ref:`using-gensym`
defreader
---------
.. versionadded:: 0.9.12
`defreader` defines a reader macro, enabling you to restructure or
modify syntax.
.. code-block:: clj
=> (defreader ^ [expr] (print expr))
=> #^(1 2 3 4)
(1 2 3 4)
=> #^"Hello"
"Hello"
.. seealso::
Section :ref:`Reader Macros <reader-macros>`
del
---
.. versionadded:: 0.9.12
`del` removes an object from the current namespace.
.. code-block:: clj
=> (setv foo 42)
=> (del foo)
=> foo
Traceback (most recent call last):
File "<console>", line 1, in <module>
NameError: name 'foo' is not defined
`del` can also remove objects from a mapping, a list, ...
.. code-block:: clj
=> (setv test (list (range 10)))
=> test
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
=> (del (slice test 2 4)) ;; remove items from 2 to 4 excluded
=> test
[0, 1, 4, 5, 6, 7, 8, 9]
=> (setv dic {"foo" "bar"})
=> dic
{"foo": "bar"}
=> (del (get dic "foo"))
=> dic
{}
eval
----
@ -408,52 +596,38 @@ first / car
for
---
`for` macro is used to build nested `foreach` loops. The macro takes two
parameters, first being a vector specifying collections to iterate over and
variables to bind. The second parameter is a statement which is executed during
each loop:
.. code-block:: clj
(for [x iter y iter] stmt)
(foreach [x iter]
(foreach [y iter] stmt))
foreach
-------
`foreach` is used to call a function for each element in a list or vector.
Results are discarded and None is returned instead. Example code iterates over
collection and calls side-effect to each element in the collection:
`for` is used to call a function for each element in a list or vector.
The results of each call are discarded and the for expression returns
None instead. The example code iterates over `collection` and
for each `element` in `collection` calls the `side-effect`
function with `element` as its argument:
.. code-block:: clj
;; assuming that (side-effect) is a function that takes a single parameter
(foreach [element collection] (side-effect element))
(for [element collection] (side-effect element))
;; foreach can have an optional else block
(foreach [element collection] (side-effect element)
(else (side-effect-2)))
;; for can have an optional else block
(for [element collection] (side-effect element)
(else (side-effect-2)))
The optional `else` block is executed only if the `foreach` loop terminates
The optional `else` block is executed only if the `for` loop terminates
normally. If the execution is halted with `break`, the `else` does not execute.
.. code-block:: clj
=> (foreach [element [1 2 3]] (if (< element 3)
... (print element)
... (break))
=> (for [element [1 2 3]] (if (< element 3)
... (print element)
... (break))
... (else (print "loop finished")))
1
2
=> (foreach [element [1 2 3]] (if (< element 4)
... (print element)
... (break))
=> (for [element [1 2 3]] (if (< element 4)
... (print element)
... (break))
... (else (print "loop finished")))
1
2
@ -461,6 +635,28 @@ normally. If the execution is halted with `break`, the `else` does not execute.
loop finished
.. _gensym:
gensym
------
.. versionadded:: 0.9.12
`gensym` form is used to generate a unique symbol to allow writing macros
without accidental variable name clashes.
.. code-block:: clj
=> (gensym)
u':G_1235'
=> (gensym "x")
u':x_1236'
.. seealso::
Section :ref:`using-gensym`
get
---
@ -481,8 +677,8 @@ Example usages:
.. note:: `get` raises a KeyError if a dictionary is queried for a non-existing
key.
.. note:: `get` raises an IndexError if a list is queried for an index that is
out of bounds.
.. note:: `get` raises an IndexError if a list or a tuple is queried for an index
that is out of bounds.
global
@ -508,13 +704,16 @@ would thrown a `NameError`.
(set-a 5)
(print-a)
if
--
if / if-not
-----------
the `if` form is used to conditionally select code to be executed. It has to
contain the condition block and the block to be executed if the condition
evaluates `True`. Optionally it may contain a block that is executed in case
the evaluation of the condition is `False`.
the evaluation of the condition is `False`. The `if-not` form (*new in
0.9.13*) is similar, but the first block after the test will be
executed when the test fails, while the other, conditional one, when
the test succeeds - opposite of the order of the `if` form.
Example usage:
@ -524,6 +723,10 @@ Example usage:
(print "lets go shopping")
(print "lets go and work"))
(if-not (money-left? account)
(print "lets go and work")
(print "lets go shopping"))
Truth values of Python objects are respected. Values `None`, `False`, zero of
any numeric type, empty sequence and empty dictionary are considered `False`.
Everything else is considered `True`.
@ -559,6 +762,9 @@ of import you can use.
[os.path [exists isdir isfile]]
[sys :as systest])
;; Import all module functions into current namespace
(import [sys [*]])
kwapply
-------
@ -602,12 +808,28 @@ function is defined and passed to another function for filtering output.
... {:name "Dave" :age 5}])
=> (defn display-people [people filter]
... (foreach [person people] (if (filter person) (print (:name person)))))
... (for [person people] (if (filter person) (print (:name person)))))
=> (display-people people (fn [person] (< (:age person) 25)))
Alice
Dave
Just as in normal function definitions, if the first element of the
body is a string, it serves as docstring. This is useful for giving
class methods docstrings.
=> (setv times-three
... (fn [x]
... "Multiplies input by three and returns the result."
... (* x 3)))
=> (help times-three)
Help on function times_three:
times_three(x)
Multiplies input by three and returns result
(END)
let
---
@ -736,6 +958,7 @@ using the backquote (`) symbol.
.. code-block:: clj
;; let `qux' be a variable with value (bar baz)
`(foo ~qux)
; equivalent to '(foo (bar baz))
@ -751,6 +974,7 @@ be alternatively written using the (') symbol
.. code-block:: clj
=> (setv x '(print "Hello World"))
; variable x is set to expression & not evaluated
=> x
@ -758,6 +982,7 @@ be alternatively written using the (') symbol
=> (eval x)
Hello World
require
-------
@ -877,6 +1102,42 @@ given conditional is False. The following shows how the macro expands into code.
None
(do statement))
unquote
-------
Within a quasiquoted form, `unquote` forces evaluation of a symbol. `unquote`
is aliased to the `~` symbol.
.. code-block:: clj
(def name "Cuddles")
(quasiquote (= name (unquote name)))
;=> (u'=' u'name' u'Cuddles')
`(= name ~name)
;=> (u'=' u'name' u'Cuddles')
unquote-splice
--------------
`unquote-splice` forces the evaluation of a symbol within a quasiquoted form,
much like `unquote`. `unquote-splice` can only be used when the symbol being
unquoted contains an iterable value, as it "splices" that iterable into the
quasiquoted form. `unquote-splice` is aliased to the `~@` symbol.
.. code-block:: clj
(def nums [1 2 3 4])
(quasiquote (+ (unquote-splice nums)))
;=> (u'+' 1L 2L 3L 4L)
`(+ ~@nums)
;=> (u'+' 1L 2L 3L 4L)
when
----
@ -913,16 +1174,18 @@ context to an argument or ignore it completely, as shown below:
.. code-block:: clj
(with [arg (expr)] block)
(with [[arg (expr)]] block)
(with [(expr)] block)
(with [[(expr)]] block)
(with [[arg (expr)] [(expr)]] block)
The following example will open file `NEWS` and print its content on screen. The
file is automatically closed after it has been processed.
.. code-block:: clj
(with [f (open "NEWS")] (print (.read f)))
(with [[f (open "NEWS")]] (print (.read f)))
with-decorator
@ -947,6 +1210,35 @@ values that are incremented by 1. When decorated `addition` is called with value
4
.. _with-gensyms:
with-gensyms
-------------
.. versionadded:: 0.9.12
`with-gensym` form is used to generate a set of :ref:`gensym` for use
in a macro.
.. code-block:: clojure
(with-gensyms [a b c]
...)
expands to:
.. code-block:: clojure
(let [[a (gensym)
[b (gensym)
[c (gensym)]]
...)
.. seealso::
Section :ref:`using-gensym`
yield
-----
@ -954,13 +1246,13 @@ yield
The generator is iterable and therefore can be used in loops, list
comprehensions and other similar constructs.
Especially the second example shows how generators can be used to generate
The function random-numbers shows how generators can be used to generate
infinite series without consuming infinite amount of memory.
.. code-block:: clj
=> (defn multiply [bases coefficients]
... (foreach [(, base coefficient) (zip bases coefficients)]
... (for [[(, base coefficient) (zip bases coefficients)]]
... (yield (* base coefficient))))
=> (multiply (range 5) (range 5))
@ -974,3 +1266,24 @@ infinite series without consuming infinite amount of memory.
... (while True (yield (.randint random low high))))
=> (list-comp x [x (take 15 (random-numbers 1 50))])])
[7, 41, 6, 22, 32, 17, 5, 38, 18, 38, 17, 14, 23, 23, 19]
.. _zipwith:
zipwith
-------
.. versionadded:: 0.9.13
`zipwith` zips multiple lists and maps the given function over the result. It is
equilavent to calling ``zip``, followed by calling ``map`` on the result.
In the following example, `zipwith` is used to add the contents of two lists
together. The equilavent ``map`` and ``zip`` calls follow.
.. code-block:: clj
=> (import operator.add)
=> (zipwith operator.add [1 2 3] [4 5 6]) ; using zipwith
[5, 7, 9]
=> (map operator.add (zip [1 2 3] [4 5 6])) ; using map+zip
[5, 7, 9]

73
docs/language/cli.rst Normal file
View File

@ -0,0 +1,73 @@
======================
Command Line Interface
======================
hy
--
Command line options
^^^^^^^^^^^^^^^^^^^^
.. cmdoption:: -c <command>
Execute the Hy code in *command*.
.. code-block:: bash
$ hy -c "(print (+ 2 2))"
4
.. cmdoption:: -i <command>
Execute the Hy code in *command*, then stay in REPL.
.. cmdoption:: --spy
Print equivalent Python code before executing. For example::
=> (defn salutationsnm [name] (print (+ "Hy " name "!")))
def salutationsnm(name):
return print(((u'Hy ' + name) + u'!'))
=> (salutationsnm "YourName")
salutationsnm(u'YourName')
Hy YourName!
=>
.. versionadded:: 0.9.11
.. cmdoption:: --show-tracebacks
Print extended tracebacks for Hy exceptions.
.. versionadded:: 0.9.12
.. cmdoption:: -v
Print the Hy version number and exit.
hyc
---
Command line options
^^^^^^^^^^^^^^^^^^^^
.. cmdoption:: file[, fileN]
Compile Hy code to Python bytecode. For example, save the
following code as ``hyname.hy``:
.. code-block:: clojure
(defn hy-hy [name]
(print (+ "Hy " name "!")))
(hy-hy "Afroman")
Then run:
.. code-block:: bash
$ hyc hyname.hy
$ python hyname.pyc
Hy Afroman!

View File

@ -6,6 +6,71 @@ Hy Core
Core Functions
===============
.. _is-coll-fn:
coll?
-----
.. versionadded:: 0.9.13
Usage: ``(coll? x)``
Returns true if argument is iterable and not a string.
.. code-block:: clojure
=> (coll? [1 2 3 4])
True
=> (coll? {"a" 1 "b" 2})
True
=> (coll? "abc")
False
cons
----
.. versionadded:: 0.9.13
Usage: ``(cons a b)``
Returns a fresh :ref:`cons cell <hycons>` with car `a` and cdr `b`.
.. code-block:: clojure
=> (setv a (cons 'hd 'tl))
=> (= 'hd (car a))
True
=> (= 'tl (cdr a))
True
cons?
-----
.. versionadded:: 0.9.13
Usage: ``(cons? foo)``
Checks whether ``foo`` is a :ref:`cons cell <hycons>`.
.. code-block:: clojure
=> (setv a (cons 'hd 'tl))
=> (cons? a)
True
=> (cons? nil)
False
=> (cons? [1 2 3])
False
.. _dec-fn:
dec
@ -29,6 +94,29 @@ Raises ``TypeError`` if ``(not (numeric? x))``.
11.3
.. _disassemble-fn:
disassemble
-----------
.. versionadded:: 0.9.13
Usage: ``(disassemble tree &optional [codegen false])``
Dump the Python AST for given Hy ``tree`` to standard output. If *codegen*
is ``true`` function prints Python code instead.
.. code-block:: clojure
=> (disassemble '(print "Hello World!"))
Module(
body=[
Expr(value=Call(func=Name(id='print'), args=[Str(s='Hello World!')], keywords=[], starargs=None, kwargs=None))])
=> (disassemble '(print "Hello World!") true)
print('Hello World!')
.. _emtpy?-fn:
empty?
@ -50,6 +138,32 @@ Return True if ``coll`` is empty, i.e. ``(= 0 (len coll))``.
False
.. _every?-fn:
every?
------
.. versionadded:: 0.9.13
Usage: ``(every? pred coll)``
Return True if ``(pred x)`` is logical true for every ``x`` in ``coll``, otherwise False. Return True if ``coll`` is empty.
.. code-block:: clojure
=> (every? even? [2 4 6])
True
=> (every? even? [1 3 5])
False
=> (every? even? [2 4 5])
False
=> (every? even? [])
True
.. _float?-fn:
float?
@ -91,6 +205,24 @@ Raises ``TypeError`` if ``(not (numeric? x))``.
True
.. _identity-fn:
identity
--------
Usage: ``(identity x)``
Returns argument supplied to the function
.. code-block:: clojure
=> (identity 4)
4
=> (list (map identity [1 2 3 4]))
[1 2 3 4]
.. _inc-fn:
inc
@ -198,7 +330,7 @@ iterator?
Usage: ``(iterator? x)``
Return True if x is an iterator. Iterators are objects that return
Return True if x is an iterator. Iterators are objects that return
themselves as an iterator when ``(iter x)`` is called.
Contrast with :ref:`iterable?-fn`.
@ -220,6 +352,63 @@ Contrast with :ref:`iterable?-fn`.
=> (iterator? (iter {:a 1 :b 2 :c 3}))
True
list*
-----
Usage: ``(list* head &rest tail)``
Generate a chain of nested cons cells (a dotted list) containing the
arguments. If the argument list only has one element, return it.
.. code-block:: clojure
=> (list* 1 2 3 4)
(1 2 3 . 4)
=> (list* 1 2 3 [4])
[1, 2, 3, 4]
=> (list* 1)
1
=> (cons? (list* 1 2 3 4))
True
.. _macroexpand-fn:
macroexpand
-----------
.. versionadded:: 0.9.13
Usage: ``(macroexpand form)``
Returns the full macro expansion of form.
.. code-block:: clojure
=> (macroexpand '(-> (a b) (x y)))
(u'x' (u'a' u'b') u'y')
=> (macroexpand '(-> (a b) (-> (c d) (e f))))
(u'e' (u'c' (u'a' u'b') u'd') u'f')
.. _macroexpand-1-fn:
macroexpand-1
-------------
.. versionadded:: 0.9.13
Usage: ``(macroexpand-1 form)``
Returns the single step macro expansion of form.
.. code-block:: clojure
=> (macroexpand-1 '(-> (a b) (-> (c d) (e f))))
(u'_>' (u'a' u'b') (u'c' u'd') (u'e' u'f'))
.. _neg?-fn:
neg?
@ -242,6 +431,36 @@ Raises ``TypeError`` if ``(not (numeric? x))``.
=> (neg? 0)
False
.. _nil?-fn:
nil?
-----
Usage: ``(nil? x)``
Return True if x is nil/None.
.. code-block:: clojure
=> (nil? nil)
True
=> (nil? None)
True
=> (nil? 0)
False
=> (setf x nil)
=> (nil? x)
True
=> ;; list.append always returns None
=> (nil? (.append [1 2 3] 4))
True
.. _none?-fn:
none?
@ -292,7 +511,7 @@ if the `n` is outside the range of `coll`.
=> (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2))
5
.. _numeric?-fn:
numeric?
@ -377,6 +596,32 @@ Return the second member of ``coll``. Equivalent to
1
.. _some-fn:
some
----
.. versionadded:: 0.9.13
Usage: ``(some pred coll)``
Return True if ``(pred x)`` is logical true for any ``x`` in ``coll``, otherwise False. Return False if ``coll`` is empty.
.. code-block:: clojure
=> (some even? [2 4 6])
True
=> (some even? [1 3 5])
False
=> (some even? [1 3 6])
True
=> (some even? [])
False
.. _string?-fn:
string?
@ -397,7 +642,7 @@ Return True if x is a string.
.. _zero?-fn:
zero?
----
-----
Usage: ``(zero? x)``
@ -469,7 +714,7 @@ To get the Fibonacci number at index 9, (starting from 0):
.. code-block:: clojure
=> (nth (fib) 9)
34
34
.. _cycle-fn:
@ -487,7 +732,7 @@ Return an infinite iterator of the members of coll.
[1, 2, 3, 1, 2, 3, 1]
=> (list (take 2 (cycle [1 2 3])))
[1, 2]
[1, 2]
.. _distinct-fn:
@ -575,6 +820,26 @@ See also :ref:`remove-fn`.
=> (list (filter even? [1 2 3 -4 5 -7]))
[2, -4]
.. _flatten-fn:
flatten
-------
.. versionadded:: 0.9.12
Usage: ``(flatten coll)``
Return a single list of all the items in ``coll``, by flattening all
contained lists and/or tuples.
.. code-block:: clojure
=> (flatten [1 2 [3 4] 5])
[1, 2, 3, 4, 5]
=> (flatten ["foo" (, 1 2) [1 [2 3] 4] "bar"])
['foo', 1, 2, 1, 2, 3, 4, 'bar']
.. _iterate-fn:
@ -667,7 +932,7 @@ Return an iterator containing the first ``n`` members of ``coll``.
=> (list (take 4 (repeat "s")))
[u's', u's', u's', u's']
=> (list (take 0 (repeat "s")))
[]
@ -693,7 +958,7 @@ Return an iterator containing every ``nth`` member of ``coll``.
=> (list (take-nth 10 [1 2 3 4 5 6 7]))
[1]
.. _take-while-fn:
@ -714,5 +979,3 @@ Return an iterator from ``coll`` as long as predicate, ``pred`` returns True.
=> (list (take-while neg? [ 1 2 3 -4 5]))
[]

View File

@ -7,6 +7,8 @@ Contents:
.. toctree::
:maxdepth: 3
cli
api
core
readermacros
internals

View File

@ -2,26 +2,469 @@
Internal Hy Documentation
=========================
.. info::
These bits are for folks who hack on Hy it's self, mostly!
.. note:: These bits are mostly useful for folks who hack on Hy itself,
but can also be used for those delving deeper in macro programming.
.. _models:
Hy Models
=========
.. TODO::
Write this.
Introduction to Hy models
-------------------------
Hy models are a very thin layer on top of regular Python objects,
representing Hy source code as data. Models only add source position
information, and a handful of methods to support clean manipulation of
Hy source code, for instance in macros. To achieve that goal, Hy models
are mixins of a base Python class and :ref:`HyObject`.
.. _hyobject:
HyObject
~~~~~~~~
``hy.models.HyObject`` is the base class of Hy models. It only
implements one method, ``replace``, which replaces the source position
of the current object with the one passed as argument. This allows us to
keep track of the original position of expressions that get modified by
macros, be that in the compiler or in pure hy macros.
``HyObject`` is not intended to be used directly to instantiate Hy
models, but only as a mixin for other classes.
Compound models
---------------
Parenthesized and bracketed lists are parsed as compound models by the
Hy parser.
.. _hylist:
HyList
~~~~~~
``hy.models.list.HyList`` is the base class of "iterable" Hy models. Its
basic use is to represent bracketed ``[]`` lists, which, when used as a
top-level expression, translate to Python list literals in the
compilation phase.
Adding a HyList to another iterable object reuses the class of the
left-hand-side object, a useful behavior when you want to concatenate Hy
objects in a macro, for instance.
.. _hyexpression:
HyExpression
~~~~~~~~~~~~
``hy.models.expression.HyExpression`` inherits :ref:`HyList` for
parenthesized ``()`` expressions. The compilation result of those
expressions depends on the first element of the list: the compiler
dispatches expressions between compiler special-forms, user-defined
macros, and regular Python function calls.
.. _hydict:
HyDict
~~~~~~
``hy.models.dict.HyDict`` inherits :ref:`HyList` for curly-bracketed ``{}``
expressions, which compile down to a Python dictionary literal.
The decision of using a list instead of a dict as the base class for
``HyDict`` allows easier manipulation of dicts in macros, with the added
benefit of allowing compound expressions as dict keys (as, for instance,
the :ref:`HyExpression` Python class isn't hashable).
Atomic models
-------------
In the input stream, double-quoted strings, respecting the Python
notation for strings, are parsed as a single token, which is directly
parsed as a :ref:`HyString`.
An ininterrupted string of characters, excluding spaces, brackets,
quotes, double-quotes and comments, is parsed as an identifier.
Identifiers are resolved to atomic models during the parsing phase in
the following order:
- :ref:`HyInteger <hy_numeric_models>`
- :ref:`HyFloat <hy_numeric_models>`
- :ref:`HyComplex <hy_numeric_models>` (if the atom isn't a bare ``j``)
- :ref:`HyKeyword` (if the atom starts with ``:``)
- :ref:`HyLambdaListKeyword` (if the atom starts with ``&``)
- :ref:`HySymbol`
.. _hystring:
HyString
~~~~~~~~
``hy.models.string.HyString`` is the base class of string-equivalent Hy
models. It also represents double-quoted string literals, ``""``, which
compile down to unicode string literals in Python. ``HyStrings`` inherit
unicode objects in Python 2, and string objects in Python 3 (and are
therefore not encoding-dependent).
``HyString`` based models are immutable.
Hy literal strings can span multiple lines, and are considered by the
parser as a single unit, respecting the Python escapes for unicode
strings.
.. _hy_numeric_models:
Numeric models
~~~~~~~~~~~~~~
``hy.models.integer.HyInteger`` represents integer literals (using the
``long`` type on Python 2, and ``int`` on Python 3).
``hy.models.float.HyFloat`` represents floating-point literals.
``hy.models.complex.HyComplex`` represents complex literals.
Numeric models are parsed using the corresponding Python routine, and
valid numeric python literals will be turned into their Hy counterpart.
.. _hysymbol:
HySymbol
~~~~~~~~
``hy.models.symbol.HySymbol`` is the model used to represent symbols
in the Hy language. It inherits :ref:`HyString`.
``HySymbol`` objects are mangled in the parsing phase, to help Python
interoperability:
- Symbols surrounded by asterisks (``*``) are turned into uppercase;
- Dashes (``-``) are turned into underscores (``_``);
- One trailing question mark (``?``) is turned into a leading ``is_``.
Caveat: as the mangling is done during the parsing phase, it is possible
to programmatically generate HySymbols that can't be generated with Hy
source code. Such a mechanism is used by :ref:`gensym` to generate
"uninterned" symbols.
.. _hykeyword:
HyKeyword
~~~~~~~~~
``hy.models.keyword.HyKeyword`` represents keywords in Hy. Keywords are
symbols starting with a ``:``. The class inherits :ref:`HyString`.
To distinguish :ref:`HyKeywords <HyKeyword>` from :ref:`HySymbols
<HySymbol>`, without the possibility of (involuntary) clashes, the
private-use unicode character ``"\uFDD0"`` is prepended to the keyword
literal before storage.
.. _hylambdalistkeyword:
HyLambdaListKeyword
~~~~~~~~~~~~~~~~~~~
``hy.models.lambdalist.HyLambdaListKeyword`` represents lambda-list
keywords, that is keywords used by the language definition inside
function signatures. Lambda-list keywords are symbols starting with a
``&``. The class inherits :ref:`HyString`
.. _hycons:
Cons Cells
==========
``hy.models.cons.HyCons`` is a representation of Python-friendly `cons
cells`_. Cons cells are especially useful to mimic features of "usual"
LISP variants such as Scheme or Common Lisp.
.. _cons cells: http://en.wikipedia.org/wiki/Cons
A cons cell is a 2-item object, containing a ``car`` (head) and a
``cdr`` (tail). In some Lisp variants, the cons cell is the fundamental
building block, and S-expressions are actually represented as linked
lists of cons cells. This is not the case in Hy, as the usual
expressions are made of Python lists wrapped in a
``HyExpression``. However, the ``HyCons`` mimicks the behavior of
"usual" Lisp variants thusly:
- ``(cons something nil)`` is ``(HyExpression [something])``
- ``(cons something some-list)`` is ``((type some-list) (+ [something]
some-list))`` (if ``some-list`` inherits from ``list``).
- ``(get (cons a b) 0)`` is ``a``
- ``(slice (cons a b) 1)`` is ``b``
Hy supports a dotted-list syntax, where ``'(a . b)`` means ``(cons 'a
'b)`` and ``'(a b . c)`` means ``(cons 'a (cons 'b 'c))``. If the
compiler encounters a cons cell at the top level, it raises a
compilation error.
``HyCons`` wraps the passed arguments (car and cdr) in Hy types, to ease
the manipulation of cons cells in a macro context.
Hy Internal Theory
==================
.. _overview:
Overview
--------
The Hy internals work by acting as a front-end to Python bytecode, so
that Hy itself compiles down to Python Bytecode, allowing an unmodified
Python runtime to run Hy code, without even noticing it.
The way we do this is by translating Hy into an internal Python AST
datastructure, and building that AST down into Python bytecode using
modules from the Python standard library, so that we don't have to
duplicate all the work of the Python internals for every single Python
release.
Hy works in four stages. The following sections will cover each step of Hy
from source to runtime.
.. _lexing:
Steps 1 and 2: Tokenizing and parsing
-------------------------------------
The first stage of compiling hy is to lex the source into tokens that we can
deal with. We use a project called rply, which is a really nice (and fast)
parser, written in a subset of Python called rpython.
The lexing code is all defined in ``hy.lex.lexer``. This code is mostly just
defining the Hy grammer, and all the actual hard parts are taken care of by
rply -- we just define "callbacks" for rply in ``hy.lex.parser``, which take
the tokens generated, and return the Hy models.
You can think of the Hy models as the "AST" for Hy, it's what Macros operate
on (directly), and it's what the compiler uses when it compiles Hy down.
.. seealso::
Section :ref:`models` for more information on Hy models and what they mean.
.. _compiling:
Step 3: Hy compilation to Python AST
------------------------------------
This is where most of the magic in Hy happens. This is where we take Hy AST
(the models), and compile them into Python AST. A couple of funky things happen
here to work past a few problems in AST, and working in the compiler is some
of the most important work we do have.
The compiler is a bit complex, so don't feel bad if you don't grok it on the
first shot, it may take a bit of time to get right.
The main entry-point to the Compiler is ``HyASTCompiler.compile``. This method
is invoked, and the only real "public" method on the class (that is to say,
we don't really promise the API beyond that method).
In fact, even internally, we don't recurse directly hardly ever, we almost
always force the Hy tree through ``compile``, and will often do this with
sub-elements of an expression that we have. It's up to the Type-based dispatcher
to properly dispatch sub-elements.
All methods that preform a compilation are marked with the ``@builds()``
decorator. You can either pass the class of the Hy model that it compiles,
or you can use a string for expressions. I'll clear this up in a second.
First stage type-dispatch
~~~~~~~~~~~~~~~~~~~~~~~~~
Let's start in the ``compile`` method. The first thing we do is check the
Type of the thing we're building. We look up to see if we have a method that
can build the ``type()`` that we have, and dispatch to the method that can
handle it. If we don't have any methods that can build that type, we raise
an internal ``Exception``.
For instance, if we have a ``HyString``, we have an almost 1-to-1 mapping of
Hy AST to Python AST. The ``compile_string`` method takes the ``HyString``, and
returns an ``ast.Str()`` that's populated with the correct line-numbers and
content.
Macro-expand
~~~~~~~~~~~~
If we get a ``HyExpression``, we'll attempt to see if this is a known
Macro, and push to have it expanded by invoking ``hy.macros.macroexpand``, then
push the result back into ``HyASTCompiler.compile``.
Second stage expression-dispatch
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The only special case is the ``HyExpression``, since we need to create different
AST depending on the special form in question. For instance, when we hit an
``(if true true false)``, we need to generate a ``ast.If``, and properly
compile the sub-nodes. This is where the ``@builds()`` with a String as an
argument comes in.
For the ``compile_expression`` (which is defined with an
``@builds(HyExpression)``) will dispatch based on the string of the first
argument. If, for some reason, the first argument is not a string, it will
properly handle that case as well (most likely by raising an ``Exception``).
If the String isn't known to Hy, it will default to create an ``ast.Call``,
which will try to do a runtime call (in Python, something like ``foo()``).
Issues hit with Python AST
~~~~~~~~~~~~~~~~~~~~~~~~~~
Python AST is great; it's what's enabled us to write such a powerful project
on top of Python without having to fight Python too hard. Like anything, we've
had our fair share of issues, and here's a short list of the common ones you
might run into.
*Python differentiates between Statements and Expressions*.
This might not sound like a big deal -- in fact, to most Python programmers,
this will shortly become a "Well, yeah" moment.
In Python, doing something like:
``print for x in range(10): pass``, because ``print`` prints expressions, and
``for`` isn't an expression, it's a control flow statement. Things like
``1 + 1`` are Expressions, as is ``lambda x: 1 + x``, but other language
features, such as ``if``, ``for``, or ``while`` are statements.
Since they have no "value" to Python, this makes working in Hy hard, since
doing something like ``(print (if true true false))`` is not just common, it's
expected.
As a result, we auto-mangle things using a ``Result`` object, where we offer
up any ``ast.stmt`` that need to get run, and a single ``ast.expr`` that can
be used to get the value of whatever was just run. Hy does this by forcing
assignment to things while running.
As example, the Hy::
(print (if true true false))
Will turn into::
if True:
_mangled_name_here = True
else:
_mangled_name_here = False
print _mangled_name_here
OK, that was a bit of a lie, since we actually turn that statement
into::
print True if True else False
By forcing things into an ``ast.expr`` if we can, but the general idea holds.
Step 4: Python bytecode output and runtime
------------------------------------------
After we have a Python AST tree that's complete, we can try and compile it to
Python bytecode by pushing it through ``eval``. From here on out, we're no
longer in control, and Python is taking care of everything. This is why things
like Python tracebacks, pdb and django apps work.
Hy Macros
=========
.. TODO::
Write this.
.. _using-gensym:
Using gensym for safer macros
------------------------------
When writing macros, one must be careful to avoid capturing external variables
or using variable names that might conflict with user code.
We will use an example macro ``nif`` (see http://letoverlambda.com/index.cl/guest/chap3.html#sec_5
for a more complete description.) ``nif`` is an example, something like a numeric ``if``,
where based on the expression, one of the 3 forms is called depending on if the
expression is positive, zero or negative.
A first pass might be someting like:
.. code-block:: clojure
(defmacro nif [expr pos-form zero-form neg-form]
`(let [[obscure-name ~expr]]
(cond [(pos? obscure-name) ~pos-form]
[(zero? obscure-name) ~zero-form]
[(neg? obscure-name) ~neg-form])))
where ``obsure-name`` is an attempt to pick some variable name as not to
conflict with other code. But of course, while well-intentioned,
this is no guarantee.
The method :ref:`gensym` is designed to generate a new, unique symbol for just
such an occasion. A much better version of ``nif`` would be:
.. code-block:: clojure
(defmacro nif [expr pos-form zero-form neg-form]
(let [[g (gensym)]]
`(let [[~g ~expr]]
(cond [(pos? ~g) ~pos-form]
[(zero? ~g) ~zero-form]
[(neg? ~g) ~neg-form]))))
This is an easy case, since there is only one symbol. But if there is
a need for several gensym's there is a second macro :ref:`with-gensyms` that
basically expands to a series of ``let`` statements:
.. code-block:: clojure
(with-gensyms [a b c]
...)
expands to:
.. code-block:: clojure
(let [[a (gensym)
[b (gensym)
[c (gensym)]]
...)
so our re-written ``nif`` would look like:
.. code-block:: clojure
(defmacro nif [expr pos-form zero-form neg-form]
(with-gensyms [g]
`(let [[~g ~expr]]
(cond [(pos? ~g) ~pos-form]
[(zero? ~g) ~zero-form]
[(neg? ~g) ~neg-form]))))
Finally, though we can make a new macro that does all this for us. :ref:`defmacro/g!`
will take all symbols that begin with ``g!`` and automatically call ``gensym`` with the
remainder of the symbol. So ``g!a`` would become ``(gensym "a")``.
Our final version of ``nif``, built with ``defmacro/g!`` becomes:
.. code-block:: clojure
(defmacro/g! nif [expr pos-form zero-form neg-form]
`(let [[~g!res ~expr]]
(cond [(pos? ~g!res) ~pos-form]
[(zero? ~g!res) ~zero-form]
[(neg? ~g!res) ~neg-form]))))
Checking macro arguments and raising exceptions
-----------------------------------------------
Hy Compiler Builtins
====================
.. TODO::
.. todo::
Write this.

View File

@ -0,0 +1,73 @@
.. _reader-macros:
.. highlight:: clj
=============
Reader Macros
=============
Reader macros gives LISP the power to modify and alter syntax on the fly.
You don't want polish notation? A reader macro can easily do just that. Want
Clojure's way of having a regex? Reader macros can also do this easily.
Syntax
======
::
=> (defreader ^ [expr] (print expr))
=> #^(1 2 3 4)
(1 2 3 4)
=> #^"Hello"
"Hello"
=> #^1+2+3+4+3+2
1+2+3+4+3+2
Hy has no literal for tuples. Lets say you dislike `(, ...)` and want something
else. This is a problem reader macros are able to solve in a neat way.
::
=> (defreader t [expr] `(, ~@expr))
=> #t(1 2 3)
(1, 2, 3)
You could even do like clojure, and have a literal for regular expressions!
::
=> (import re)
=> (defreader r [expr] `(re.compile ~expr))
=> #r".*"
<_sre.SRE_Pattern object at 0xcv7713ph15#>
Implementation
==============
``defreader`` takes a single character as symbol name for the reader macro,
anything longer will return an error. Implementation wise, ``defreader``
expands into a lambda covered with a decorator, this decorater saves the
lambda in a dict with its module name and symbol.
::
=> (defreader ^ [expr] (print expr))
;=> (with_decorator (hy.macros.reader ^) (fn [expr] (print expr)))
``#`` expands into ``(dispatch_reader_macro ...)`` where the symbol
and expression is passed to the correct function.
::
=> #^()
;=> (dispatch_reader_macro ^ ())
=> #^"Hello"
"Hello"
.. warning::
Because of a limitation in Hy's lexer and parser, reader macros can't
redefine defined syntax such as ``()[]{}``. This will most likely be
adressed in the future.

View File

@ -2,21 +2,20 @@
Quickstart
==========
.. image:: _static/cuddles.png
.. image:: _static/cuddles-transparent-small.png
:alt: Karen Rustard's Cuddles
:align: left
(thanks to Karen Rustad for Cuddles!)
(Thanks to Karen Rustad for Cuddles!)
HOW TO GET HY REAL FAST:
**HOW TO GET HY REAL FAST**:
1. create a `Python virtual environment
1. Create a `Virtual Python 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::
2. Activate your Virtual Python Environment
3. Install `hy from PyPI <https://pypi.python.org/pypi/hy>`_ with ``pip install hy``
4. Start a REPL with ``hy``
5. Type stuff in the REPL::
=> (print "Hy!")
Hy!
@ -26,20 +25,19 @@ HOW TO GET HY REAL FAST:
etc
6. hit CTRL-D when you're done
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::
7. Open up an elite programming editor and type::
(print "i was going to code in python syntax, but then i got hy")
(print "I was going to code in python syntax, but then I got hy.")
9. save as ``test_program_of_awesome.hy``
10. run::
8. Save as ``awesome.hy``
9. And run your first Hy program::
hy test_program_of_awesome.hy
hy awesome.hy
11. take a deep breath so as to not hyperventilate
12. smile villainously and sneak off to your hydeaway and do
10. Take a deep breath so as to not hyperventilate
11. Smile villainously and sneak off to your hydeaway and do
unspeakable things

View File

@ -276,7 +276,7 @@ You might notice above that if you have code like:
(body-if-true)
(body-if-false))
But wait! What if you want to execute more than one statment in the
But wait! What if you want to execute more than one statement in the
body of one of these?
You can do the following:
@ -289,7 +289,7 @@ You can do the following:
(print "and why not, let's keep talking about how true it is!))
(print "this one's still simply just false"))
You can see that we used "do" to wrap multiple statments. If you're
You can see that we used "do" to wrap multiple statements. If you're
familiar with other lisps, this is the equivalent of "progn"
elsewhere.
@ -311,8 +311,8 @@ The equivalent in hy would be:
.. code-block:: clj
(for (i (range 10))
(print (+ "'i' is now at " (str i))))
(for [i (range 10)]
(print (+ "'i' is now at " (str i))))
You can also import and make use of various python libraries. For
@ -330,13 +330,13 @@ Python's context managers ('with' statements) are used like this:
.. code-block:: clj
(with [f (file "/tmp/data.in")]
(print (.read f)))
(with [[f (open "/tmp/data.in")]]
(print (.read f)))
which is equivalent to::
with file("/tmp/data.in") as f:
print f.read()
with open("/tmp/data.in") as f:
print f.read()
And yes, we do have lisp comprehensions! In Python you might do::
@ -363,7 +363,7 @@ In hy, you could do these like:
(list-comp
(, x y)
(x (range 9)
(x (range 8)
y "ABCDEFGH"))
; [(0, 'A'), (0, 'B'), (0, 'C'), (0, 'D'), (0, 'E'), (0, 'F'), (0, 'G'), (0, 'H'),
@ -373,8 +373,7 @@ In hy, you could do these like:
; (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')]
; (7, 'A'), (7, 'B'), (7, 'C'), (7, 'D'), (7, 'E'), (7, 'F'), (7, 'G'), (7, 'H')]
Python has support for various fancy argument and keyword arguments.
@ -406,7 +405,7 @@ The same thing in Hy::
...
[3, 2, 1, 4]
See how we use kwapply to handle the fancy pssing? :)
See how we use kwapply to handle the fancy passing? :)
There's also a dictionary-style keyword arguments construction that
looks like:
@ -436,12 +435,18 @@ The Hy equivalent:
Finally, of course we need classes! In python we might have a class
like::
class FooBar (object):
def __init__(self, x):
self.x = x
class FooBar(object):
"""
Yet Another Example Class
"""
def __init__(self, x):
self.x = x
def get_x(self):
return self.x
def get_x(self):
"""
Return our copy of x
"""
return self.x
In Hy:
@ -449,6 +454,7 @@ In Hy:
.. code-block:: clj
(defclass FooBar [object]
"Yet Another Example Class"
[[--init--
(fn [self x]
(setv self.x x)
@ -458,6 +464,7 @@ In Hy:
[get-x
(fn [self]
"Return our copy of x"
self.x)]])

27
eg/flask/meth_example.hy Normal file
View File

@ -0,0 +1,27 @@
;;; Simple Flask application
;;;
;;; Requires to have Flask installed
;;;
;;; You can test it via:
;;;
;;; $ curl 127.0.0.1:5151
;;; $ curl -X POST 127.0.0.1:5151/post
;;; $ curl -X POST 127.0.0.1:5151/both
;;; $ curl 127.0.0.1:5151/both
(import [flask [Flask]])
(require hy.contrib.meth)
(setv app (Flask "__main__"))
(route get-index "/" []
(str "Hy world!"))
(post-route post-index "/post" []
(str "Hy post world!"))
(route-with-methods both-index "/both" ["GET" "POST"] []
(str "Hy to both worlds!"))
(apply app.run [] {"port" 5151})

View File

@ -1,24 +0,0 @@
#!/usr/bin/env hy
;; Very much a knockoff (straight port) of Dan Gulotta's 2013 MIT Mystery Hunt
;; puzzle "The Halting Problem". His Copyright terms are unclear, so presume
;; that this is distributable, but not free.
(defn evaluate [f] ((f (lambda [x] (+ x 1))) 0))
(defn successor [n] (lambda [f] (lambda [x] (f ((n f) x)))))
(defn plus [m n] ((n successor) m))
(defn exponent [m n] (n m))
(defn zero [f] (lambda [x] x))
(defn one [f] (lambda [x] (f x)))
(defn predecessor [n] (lambda [f] (lambda [x]
(((n (lambda [g] (lambda [h] (h (g f))))) (lambda [y] x)) (lambda [z] z)))))
(defn subtract [m n] ((m predecessor) n))
(setv two (plus one one))
(setv three (plus two one))
(print (evaluate (exponent three three)))

View File

@ -7,5 +7,5 @@
(with-as (ThreadPoolExecutor 10) executor
(setv jobs (list-comp (.submit executor task-to-do) (x (range 0 10))))
(for (future (as-completed jobs))
(for [future (as-completed jobs)]
(.result future)))

View File

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

View File

@ -1,5 +1,6 @@
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
# Copyright (c) 2013 Julien Danjou <julien@danjou.info>
# Copyright (c) 2013 Berker Peksag <berker.peksag@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@ -23,9 +24,24 @@ try:
import __builtin__ as builtins
except ImportError:
import builtins # NOQA
try:
from py_compile import MAGIC, wr_long
except ImportError:
# py_compile.MAGIC removed and imp.get_magic() deprecated in Python 3.4
from importlib.util import MAGIC_NUMBER as MAGIC # NOQA
def wr_long(f, x):
"""Internal; write a 32-bit int to a file in little-endian order."""
f.write(bytes([x & 0xff,
(x >> 8) & 0xff,
(x >> 16) & 0xff,
(x >> 24) & 0xff]))
import sys
PY27 = sys.version_info >= (2, 7)
PY3 = sys.version_info[0] >= 3
PY33 = sys.version_info >= (3, 3)
PY34 = sys.version_info >= (3, 4)
if PY3:
str_type = str

View File

@ -5,6 +5,7 @@
# Copyright (c) 2013 Konrad Hinsen <konrad.hinsen@fastmail.net>
# Copyright (c) 2013 Thom Neale <twneale@gmail.com>
# Copyright (c) 2013 Will Kahn-Greene <willg@bluesock.org>
# Copyright (c) 2013 Bob Tolbert <bob@tolbert.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@ -32,7 +33,7 @@ import sys
import hy
from hy.lex import LexException, PrematureEndOfInput, tokenize
from hy.compiler import hy_compile
from hy.compiler import hy_compile, HyTypeError
from hy.importer import ast_compile, import_buffer_to_module
from hy.completer import completion
@ -73,17 +74,22 @@ def print_python_code(_ast):
class HyREPL(code.InteractiveConsole):
def __init__(self, spy=False):
def __init__(self, spy=False, locals=None, filename="<input>"):
self.spy = spy
code.InteractiveConsole.__init__(self)
code.InteractiveConsole.__init__(self, locals=locals,
filename=filename)
def runsource(self, source, filename='<input>', symbol='single'):
global SIMPLE_TRACEBACKS
try:
tokens = tokenize(source)
except PrematureEndOfInput:
return True
except LexException:
self.showsyntaxerror(filename)
except LexException as e:
if e.source is None:
e.source = source
e.filename = filename
sys.stderr.write(str(e))
return False
try:
@ -91,6 +97,15 @@ class HyREPL(code.InteractiveConsole):
if self.spy:
print_python_code(_ast)
code = ast_compile(_ast, filename, symbol)
except HyTypeError as e:
if e.source is None:
e.source = source
e.filename = filename
if SIMPLE_TRACEBACKS:
sys.stderr.write(str(e))
else:
self.showtraceback()
return False
except Exception:
self.showtraceback()
return False
@ -153,22 +168,34 @@ def ideas_macro():
require("hy.cmdline", "__console__")
require("hy.cmdline", "__main__")
SIMPLE_TRACEBACKS = True
def run_command(source):
try:
import_buffer_to_module("__main__", source)
except LexException as exc:
# TODO: This would be better if we had line, col info.
print(source)
print(repr(exc))
return 1
except (HyTypeError, LexException) as e:
if SIMPLE_TRACEBACKS:
sys.stderr.write(str(e))
return 1
raise
except Exception:
raise
return 0
def run_file(filename):
from hy.importer import import_file_to_module
import_file_to_module("__main__", filename)
return 0 # right?
try:
import_file_to_module("__main__", filename)
except (HyTypeError, LexException) as e:
if SIMPLE_TRACEBACKS:
sys.stderr.write(str(e))
return 1
raise
except Exception:
raise
return 0
def run_repl(hr=None, spy=False):
@ -187,8 +214,8 @@ def run_repl(hr=None, spy=False):
return 0
def run_icommand(source):
hr = HyREPL()
def run_icommand(source, spy=False):
hr = HyREPL(spy)
hr.runsource(source, filename='<input>', symbol='single')
return run_repl(hr)
@ -217,6 +244,9 @@ def cmdline_handler(scriptname, argv):
parser.add_argument("-v", action="version", version=VERSION)
parser.add_argument("--show-tracebacks", action="store_true",
help="show complete tracebacks for Hy exceptions")
# this will contain the script/program name and any arguments for it.
parser.add_argument('args', nargs=argparse.REMAINDER,
help=argparse.SUPPRESS)
@ -227,11 +257,12 @@ def cmdline_handler(scriptname, argv):
options = parser.parse_args(argv[1:])
if options.show_tracebacks:
global SIMPLE_TRACEBACKS
SIMPLE_TRACEBACKS = False
# reset sys.argv like Python
if options.args and len(options.args) > 0:
sys.argv = options.args
else: # Python default
sys.argv = ['']
sys.argv = options.args or [""]
if options.command:
# User did "hy -c ..."
@ -239,7 +270,7 @@ def cmdline_handler(scriptname, argv):
if options.icommand:
# User did "hy -i ..."
return run_icommand(options.icommand)
return run_icommand(options.icommand, spy=options.spy)
if options.args:
if options.args[0] == "-":

View File

@ -4,6 +4,7 @@
# Copyright (c) 2013 Julien Danjou <julien@danjou.info>
# Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
# Copyright (c) 2013 James King <james@agentultra.com>
# Copyright (c) 2013 Bob Tolbert <bob@tolbert.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@ -23,8 +24,6 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
from hy.errors import HyError
from hy.models.lambdalist import HyLambdaListKeyword
from hy.models.expression import HyExpression
from hy.models.keyword import HyKeyword
@ -35,9 +34,13 @@ from hy.models.symbol import HySymbol
from hy.models.float import HyFloat
from hy.models.list import HyList
from hy.models.dict import HyDict
from hy.models.cons import HyCons
from hy.macros import require, macroexpand
from hy._compat import str_type
from hy.errors import HyCompileError, HyTypeError
import hy.macros
from hy._compat import str_type, long_type, PY27, PY33, PY3, PY34
from hy.macros import require, macroexpand, reader_macroexpand
import hy.importer
import traceback
@ -71,39 +74,11 @@ def load_stdlib():
_stdlib[e] = module
class HyCompileError(HyError):
def __init__(self, exception, traceback=None):
self.exception = exception
self.traceback = traceback
def __str__(self):
if isinstance(self.exception, HyTypeError):
return str(self.exception)
if self.traceback:
tb = "".join(traceback.format_tb(self.traceback)).strip()
else:
tb = "No traceback available. 😟"
return("Internal Compiler Bug 😱\n%s: %s\nCompilation traceback:\n%s"
% (self.exception.__class__.__name__,
self.exception, tb))
class HyTypeError(TypeError):
def __init__(self, expression, message):
super(HyTypeError, self).__init__(message)
self.expression = expression
def __str__(self):
return (super(HyTypeError, self).__str__() + " (line %s, column %d)"
% (self.expression.start_line,
self.expression.start_column))
_compile_table = {}
def ast_str(foobar):
if sys.version_info[0] >= 3:
if PY3:
return str(foobar)
try:
@ -340,7 +315,7 @@ def checkargs(exact=None, min=None, max=None, even=None):
if min is not None and (len(expression) - 1) < min:
_raise_wrong_args_number(
expression,
"`%%s' needs at least %d arguments, got %%d" % (min))
"`%%s' needs at least %d arguments, got %%d." % (min))
if max is not None and (len(expression) - 1) > max:
_raise_wrong_args_number(
@ -419,7 +394,6 @@ class HyASTCompiler(object):
def compile(self, tree):
try:
tree = macroexpand(tree, self.module_name)
_type = type(tree)
ret = self.compile_atom(_type, tree)
if ret:
@ -430,6 +404,8 @@ class HyASTCompiler(object):
# nested; so let's re-raise this exception, let's not wrap it in
# another HyCompileError!
raise
except HyTypeError as e:
raise
except Exception as e:
raise HyCompileError(e, sys.exc_info()[2])
@ -534,18 +510,21 @@ class HyASTCompiler(object):
return ret, args, defaults, varargs, kwargs
def _storeize(self, name):
def _storeize(self, name, func=None):
"""Return a new `name` object with an ast.Store() context"""
if not func:
func = ast.Store
if isinstance(name, Result):
if not name.is_expr():
raise TypeError("Can't assign to a non-expr")
raise TypeError("Can't assign / delete a non-expression")
name = name.expr
if isinstance(name, (ast.Tuple, ast.List)):
typ = type(name)
new_elts = []
for x in name.elts:
new_elts.append(self._storeize(x))
new_elts.append(self._storeize(x, func))
new_name = typ(elts=new_elts)
elif isinstance(name, ast.Name):
new_name = ast.Name(id=name.id, arg=name.arg)
@ -554,9 +533,9 @@ class HyASTCompiler(object):
elif isinstance(name, ast.Attribute):
new_name = ast.Attribute(value=name.value, attr=name.attr)
else:
raise TypeError("Can't assign to a %s object" % type(name))
raise TypeError("Can't assign / delete a %s object" % type(name))
new_name.ctx = ast.Store()
new_name.ctx = func()
ast.copy_location(new_name, name)
return new_name
@ -619,6 +598,24 @@ class HyASTCompiler(object):
return imports, HyExpression([HySymbol(name),
contents]).replace(form), False
elif isinstance(form, HyCons):
ret = HyExpression([HySymbol(name)])
nimport, contents, splice = self._render_quoted_form(form.car,
level)
if splice:
raise HyTypeError(form, "Can't splice dotted lists yet")
imports.update(nimport)
ret.append(contents)
nimport, contents, splice = self._render_quoted_form(form.cdr,
level)
if splice:
raise HyTypeError(form, "Can't splice the cdr of a cons")
imports.update(nimport)
ret.append(contents)
return imports, ret.replace(form), False
elif isinstance(form, (HySymbol, HyLambdaListKeyword)):
return imports, HyExpression([HySymbol(name),
HyString(form)]).replace(form), False
@ -776,7 +773,7 @@ class HyASTCompiler(object):
ret = handler_results
if sys.version_info[0] >= 3 and sys.version_info[1] >= 3:
if PY33:
# Python 3.3 features a merge of TryExcept+TryFinally into Try.
return ret + ast.Try(
lineno=expr.start_line,
@ -853,7 +850,7 @@ class HyASTCompiler(object):
exceptions,
"Exception storage target name must be a symbol.")
if sys.version_info[0] >= 3:
if PY3:
# Python3 features a change where the Exception handler
# moved the name from a Name() to a pure Python String type.
#
@ -1037,6 +1034,10 @@ class HyASTCompiler(object):
while len(expr) > 0:
iexpr = expr.pop(0)
if not isinstance(iexpr, (HySymbol, HyList)):
raise HyTypeError(iexpr, "(import) requires a Symbol "
"or a List.")
if isinstance(iexpr, HySymbol):
rimports += _compile_import(expr, iexpr)
continue
@ -1083,18 +1084,81 @@ class HyASTCompiler(object):
return rimports
@builds("get")
@checkargs(2)
@checkargs(min=2)
def compile_index_expression(self, expr):
expr.pop(0) # index
val = self.compile(expr.pop(0)) # target
sli = self.compile(expr.pop(0)) # slice
return val + sli + ast.Subscript(
val = self.compile(expr.pop(0))
slices, ret = self._compile_collect(expr)
if val.stmts:
ret += val
for sli in slices:
val = Result() + ast.Subscript(
lineno=expr.start_line,
col_offset=expr.start_column,
value=val.force_expr,
slice=ast.Index(value=sli),
ctx=ast.Load())
return ret + val
@builds(".")
@checkargs(min=1)
def compile_attribute_access(self, expr):
expr.pop(0) # dot
ret = self.compile(expr.pop(0))
for attr in expr:
if isinstance(attr, HySymbol):
ret += ast.Attribute(lineno=attr.start_line,
col_offset=attr.start_column,
value=ret.force_expr,
attr=ast_str(attr),
ctx=ast.Load())
elif type(attr) == HyList:
if len(attr) != 1:
raise HyTypeError(
attr,
"The attribute access DSL only accepts HySymbols "
"and one-item lists, got {0}-item list instead".format(
len(attr),
),
)
compiled_attr = self.compile(attr.pop(0))
ret = compiled_attr + ret + ast.Subscript(
lineno=attr.start_line,
col_offset=attr.start_column,
value=ret.force_expr,
slice=ast.Index(value=compiled_attr.force_expr),
ctx=ast.Load())
else:
raise HyTypeError(
attr,
"The attribute access DSL only accepts HySymbols "
"and one-item lists, got {0} instead".format(
type(attr).__name__,
),
)
return ret
@builds("del")
@checkargs(min=1)
def compile_del_expression(self, expr):
expr.pop(0)
ld_targets, ret = self._compile_collect(expr)
del_targets = []
for target in ld_targets:
del_targets.append(self._storeize(target, ast.Del))
return ret + ast.Delete(
lineno=expr.start_line,
col_offset=expr.start_column,
value=val.force_expr,
slice=ast.Index(value=sli.force_expr),
ctx=ast.Load())
targets=del_targets)
@builds("slice")
@checkargs(min=1, max=4)
@ -1159,14 +1223,18 @@ class HyASTCompiler(object):
fn.stmts[-1].decorator_list = decorators
return ret + fn
@builds("with")
@builds("with*")
@checkargs(min=2)
def compile_with_expression(self, expr):
expr.pop(0) # with
expr.pop(0) # with*
args = expr.pop(0)
if len(args) > 2 or len(args) < 1:
raise HyTypeError(expr, "with needs [arg (expr)] or [(expr)]")
if not isinstance(args, HyList):
raise HyTypeError(expr,
"with expects a list, received `{0}'".format(
type(args).__name__))
if len(args) < 1:
raise HyTypeError(expr, "with needs [[arg (expr)]] or [[(expr)]]]")
args.reverse()
ctx = self.compile(args.pop(0))
@ -1195,7 +1263,7 @@ class HyASTCompiler(object):
optional_vars=thing,
body=body.stmts)
if sys.version_info[0] >= 3 and sys.version_info[1] >= 3:
if PY33:
the_with.items = [ast.withitem(context_expr=ctx.force_expr,
optional_vars=thing)]
@ -1220,54 +1288,192 @@ class HyASTCompiler(object):
ctx=ast.Load())
return ret
def _compile_generator_iterables(self, trailers):
"""Helper to compile the "trailing" parts of comprehensions:
generators and conditions"""
generators = trailers.pop(0)
cond = self.compile(trailers.pop(0)) if trailers != [] else Result()
gen_it = iter(generators)
paired_gens = zip(gen_it, gen_it)
gen_res = Result()
gen = []
for target, iterable in paired_gens:
comp_target = self.compile(target)
target = self._storeize(comp_target)
gen_res += self.compile(iterable)
gen.append(ast.comprehension(
target=target,
iter=gen_res.force_expr,
ifs=[]))
if cond.expr:
gen[-1].ifs.append(cond.expr)
return gen_res + cond, gen
@builds("list_comp")
@checkargs(min=2, max=3)
def compile_list_comprehension(self, expr):
# (list-comp expr (target iter) cond?)
expr.pop(0)
expression = expr.pop(0)
tar_it = iter(expr.pop(0))
targets = zip(tar_it, tar_it)
cond = self.compile(expr.pop(0)) if expr != [] else Result()
generator_res = Result()
generators = []
for target, iterable in targets:
comp_target = self.compile(target)
target = self._storeize(comp_target)
generator_res += self.compile(iterable)
generators.append(ast.comprehension(
target=target,
iter=generator_res.force_expr,
ifs=[]))
if cond.expr:
generators[-1].ifs.append(cond.expr)
gen_res, gen = self._compile_generator_iterables(expr)
compiled_expression = self.compile(expression)
ret = compiled_expression + generator_res + cond
ret = compiled_expression + gen_res
ret += ast.ListComp(
lineno=expr.start_line,
col_offset=expr.start_column,
elt=compiled_expression.force_expr,
generators=generators)
generators=gen)
return ret
@builds("kwapply")
@checkargs(2)
def compile_kwapply_expression(self, expr):
expr.pop(0) # kwapply
call = self.compile(expr.pop(0))
kwargs = self.compile(expr.pop(0))
@builds("set_comp")
@checkargs(min=2, max=3)
def compile_set_comprehension(self, expr):
if PY27:
ret = self.compile_list_comprehension(expr)
expr = ret.expr
ret.expr = ast.SetComp(
lineno=expr.lineno,
col_offset=expr.col_offset,
elt=expr.elt,
generators=expr.generators)
if type(call.expr) != ast.Call:
raise HyTypeError(expr, "kwapplying a non-call")
return ret
call.expr.kwargs = kwargs.force_expr
expr[0] = HySymbol("list_comp").replace(expr[0])
expr = HyExpression([HySymbol("set"), expr]).replace(expr)
return self.compile(expr)
return kwargs + call
@builds("dict_comp")
@checkargs(min=3, max=4)
def compile_dict_comprehension(self, expr):
if PY27:
expr.pop(0) # dict-comp
key = expr.pop(0)
value = expr.pop(0)
gen_res, gen = self._compile_generator_iterables(expr)
compiled_key = self.compile(key)
compiled_value = self.compile(value)
ret = compiled_key + compiled_value + gen_res
ret += ast.DictComp(
lineno=expr.start_line,
col_offset=expr.start_column,
key=compiled_key.force_expr,
value=compiled_value.force_expr,
generators=gen)
return ret
# In Python 2.6, turn (dict-comp key value [foo]) into
# (dict (list-comp (, key value) [foo]))
expr[0] = HySymbol("list_comp").replace(expr[0])
expr[1:3] = [HyExpression(
[HySymbol(",")] +
expr[1:3]
).replace(expr[1])]
expr = HyExpression([HySymbol("dict"), expr]).replace(expr)
return self.compile(expr)
@builds("genexpr")
def compile_genexpr(self, expr):
ret = self.compile_list_comprehension(expr)
expr = ret.expr
ret.expr = ast.GeneratorExp(
lineno=expr.lineno,
col_offset=expr.col_offset,
elt=expr.elt,
generators=expr.generators)
return ret
@builds("apply")
@checkargs(min=1, max=3)
def compile_apply_expression(self, expr):
expr.pop(0) # apply
ret = Result()
fun = expr.pop(0)
# We actually defer the compilation of the function call to
# @builds(HyExpression), allowing us to work on method calls
call = HyExpression([fun]).replace(fun)
if isinstance(fun, HySymbol) and fun.startswith("."):
# (apply .foo lst) needs to work as lst[0].foo(*lst[1:])
if not expr:
raise HyTypeError(
expr, "apply of a method needs to have an argument"
)
# We need to grab the arguments, and split them.
# Assign them to a variable if they're not one already
if type(expr[0]) == HyList:
if len(expr[0]) == 0:
raise HyTypeError(
expr, "apply of a method needs to have an argument"
)
call.append(expr[0].pop(0))
else:
if isinstance(expr[0], HySymbol):
tempvar = expr[0]
else:
tempvar = HySymbol(self.get_anon_var()).replace(expr[0])
assignment = HyExpression(
[HySymbol("setv"), tempvar, expr[0]]
).replace(expr[0])
# and add the assignment to our result
ret += self.compile(assignment)
# The first argument is the object on which to call the method
# So we translate (apply .foo args) to (.foo (get args 0))
call.append(HyExpression(
[HySymbol("get"), tempvar, HyInteger(0)]
).replace(tempvar))
# We then pass the other arguments to the function
expr[0] = HyExpression(
[HySymbol("slice"), tempvar, HyInteger(1)]
).replace(expr[0])
ret += self.compile(call)
if not isinstance(ret.expr, ast.Call):
raise HyTypeError(
fun, "compiling the application of `{}' didn't return a "
"function call, but `{}'".format(fun, type(ret.expr).__name__)
)
if ret.expr.starargs or ret.expr.kwargs:
raise HyTypeError(
expr, "compiling the function application returned a function "
"call with arguments"
)
if expr:
stargs = expr.pop(0)
if stargs is not None:
stargs = self.compile(stargs)
ret.expr.starargs = stargs.force_expr
ret = stargs + ret
if expr:
kwargs = self.compile(expr.pop(0))
ret.expr.kwargs = kwargs.force_expr
ret = kwargs + ret
return ret
@builds("not")
@builds("~")
@ -1343,11 +1549,9 @@ class HyASTCompiler(object):
lineno=e.start_line,
col_offset=e.start_column)
@builds("+")
@builds("%")
@builds("/")
@builds("//")
@builds("*")
@builds("**")
@builds("<<")
@builds(">>")
@ -1384,6 +1588,23 @@ class HyASTCompiler(object):
col_offset=child.start_column)
return ret
@builds("+")
@builds("*")
def compile_maths_expression_mul(self, expression):
if len(expression) > 2:
return self.compile_maths_expression(expression)
else:
id_op = {"+": HyInteger(0), "*": HyInteger(1)}
op = expression.pop(0)
arg = expression.pop(0) if expression else id_op[op]
expr = HyExpression([
HySymbol(op),
id_op[op],
arg
]).replace(expression)
return self.compile_maths_expression(expr)
@builds("-")
@checkargs(min=1)
def compile_maths_expression_sub(self, expression):
@ -1447,8 +1668,15 @@ class HyASTCompiler(object):
@builds(HyExpression)
def compile_expression(self, expression):
# Perform macro expansions
expression = macroexpand(expression, self.module_name)
if not isinstance(expression, HyExpression):
# Go through compile again if the type changed.
return self.compile(expression)
if expression == []:
return self.compile_list(expression)
fn = expression[0]
func = None
if isinstance(fn, HyKeyword):
@ -1468,6 +1696,9 @@ class HyASTCompiler(object):
fn.replace(ofn)
# Get the object we want to take an attribute from
if len(expression) < 2:
raise HyTypeError(expression,
"attribute access requires object")
func = self.compile(expression.pop(1))
# And get the attribute
@ -1518,23 +1749,36 @@ class HyASTCompiler(object):
result += ld_name
return result
@builds("foreach")
@builds("for*")
@checkargs(min=1)
def compile_for_expression(self, expression):
expression.pop(0) # for
target_name, iterable = expression.pop(0)
args = expression.pop(0)
if not isinstance(args, HyList):
raise HyTypeError(expression,
"for expects a list, received `{0}'".format(
type(args).__name__))
try:
target_name, iterable = args
except ValueError:
raise HyTypeError(expression,
"for requires two forms in the list")
target = self._storeize(self.compile(target_name))
ret = Result()
orel = Result()
# (foreach [] body (else …))
# (for* [] body (else …))
if expression and expression[-1][0] == HySymbol("else"):
else_expr = expression.pop()
if len(else_expr) > 2:
raise HyTypeError(
else_expr,
"`else' statement in `foreach' is too long")
"`else' statement in `for' is too long")
elif len(else_expr) == 2:
orel += self.compile(else_expr[1])
orel += orel.expr_as_stmt()
@ -1592,12 +1836,32 @@ class HyASTCompiler(object):
arglist = expression.pop(0)
ret, args, defaults, stararg, kwargs = self._parse_lambda_list(arglist)
if PY34:
# Python 3.4+ requres that args are an ast.arg object, rather
# than an ast.Name or bare string.
args = [ast.arg(arg=ast_str(x),
annotation=None, # Fix me!
lineno=x.start_line,
col_offset=x.start_column) for x in args]
# XXX: Beware. Beware. This wasn't put into the parse lambda
# list because it's really just an internal parsing thing.
if kwargs:
kwargs = ast.arg(arg=kwargs, annotation=None)
if stararg:
stararg = ast.arg(arg=stararg, annotation=None)
# Let's find a better home for these guys.
else:
args = [ast.Name(arg=ast_str(x), id=ast_str(x),
ctx=ast.Param(),
lineno=x.start_line,
col_offset=x.start_column) for x in args]
args = ast.arguments(
args=[ast.Name(arg=ast_str(x), id=ast_str(x),
ctx=ast.Param(),
lineno=x.start_line,
col_offset=x.start_column)
for x in args],
args=args,
vararg=stararg,
kwarg=kwargs,
kwonlyargs=[],
@ -1706,6 +1970,19 @@ class HyASTCompiler(object):
bases=bases_expr,
body=body.stmts)
def _compile_time_hack(self, expression):
"""Compile-time hack: we want to get our new macro now
We must provide __name__ in the namespace to make the Python
compiler set the __module__ attribute of the macro function."""
hy.importer.hy_eval(expression,
compile_time_ns(self.module_name),
self.module_name)
# We really want to have a `hy` import to get hy.macro in
ret = self.compile(expression)
ret.add_imports('hy', [None])
return ret
@builds("defmacro")
@checkargs(min=1)
def compile_macro(self, expression):
@ -1721,19 +1998,50 @@ class HyASTCompiler(object):
HyExpression([HySymbol("fn")] + expression),
]).replace(expression)
# Compile-time hack: we want to get our new macro now
# We must provide __name__ in the namespace to make the Python
# compiler set the __module__ attribute of the macro function.
hy.importer.hy_eval(new_expression,
compile_time_ns(self.module_name),
self.module_name)
# We really want to have a `hy` import to get hy.macro in
ret = self.compile(new_expression)
ret.add_imports('hy', [None])
ret = self._compile_time_hack(new_expression)
return ret
@builds("defreader")
@checkargs(min=2)
def compile_reader(self, expression):
expression.pop(0)
name = expression.pop(0)
NOT_READERS = [":", "&"]
if name in NOT_READERS or len(name) > 1:
raise NameError("%s can't be used as a macro reader symbol" % name)
if not isinstance(name, HySymbol):
raise HyTypeError(name,
("received a `%s' instead of a symbol "
"for reader macro name" % type(name).__name__))
name = HyString(name).replace(name)
new_expression = HyExpression([
HySymbol("with_decorator"),
HyExpression([HySymbol("hy.macros.reader"), name]),
HyExpression([HySymbol("fn")] + expression),
]).replace(expression)
ret = self._compile_time_hack(new_expression)
return ret
@builds("dispatch_reader_macro")
@checkargs(exact=2)
def compile_dispatch_reader_macro(self, expression):
expression.pop(0) # dispatch-reader-macro
str_char = expression.pop(0)
if not type(str_char) == HyString:
raise HyTypeError(
str_char,
"Trying to expand a reader macro using `{0}' instead "
"of string".format(type(str_char).__name__),
)
module = self.module_name
expr = reader_macroexpand(str_char, expression.pop(0), module)
return self.compile(expr)
@builds("eval_and_compile")
def compile_eval_and_compile(self, expression):
expression[0] = HySymbol("progn")
@ -1751,9 +2059,13 @@ class HyASTCompiler(object):
self.module_name)
return Result()
@builds(HyCons)
def compile_cons(self, cons):
raise HyTypeError(cons, "Can't compile a top-level cons cell")
@builds(HyInteger)
def compile_integer(self, number):
return ast.Num(n=int(number),
return ast.Num(n=long_type(number),
lineno=number.start_line,
col_offset=number.start_column)

View File

@ -26,6 +26,7 @@
# DEALINGS IN THE SOFTWARE.
import os
import sys
from contextlib import contextmanager
docomplete = True
@ -40,6 +41,12 @@ except ImportError:
except ImportError:
docomplete = False
if sys.platform == 'darwin' and 'libedit' in readline.__doc__:
readline_bind = "bind ^I rl_complete"
else:
readline_bind = "tab: complete"
import hy.macros
import hy.compiler
@ -94,7 +101,7 @@ def completion(completer=None):
except IOError:
open(history, 'a').close()
readline.parse_and_bind("tab: complete")
readline.parse_and_bind(readline_bind)
yield

108
hy/contrib/anaphoric.hy Normal file
View File

@ -0,0 +1,108 @@
;;; Hy anaphoric macros
;;
;; Copyright (c) 2013 James King <james@agentultra.com>
;; 2013 Paul R. Tagliamonte <tag@pault.ag>
;; 2013 Abhishek L <abhishek.lekshmanan@gmail.com>
;;
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.
;;
;;; These macros make writing functional programs more concise
(defmacro ap-if (test-form &rest args)
`(let [[it ~test-form]] (if it ~@args)))
(defmacro ap-each [lst &rest body]
"Evaluate the body form for each element in the list."
`(for [it ~lst] ~@body))
(defmacro ap-each-while [lst form &rest body]
"Evalutate the body form for each element in the list while the
predicate form evaluates to True."
`(let [[p (lambda [it] ~form)]]
(for [it ~lst]
(if (p it)
~@body
(break)))))
(defmacro ap-map [form lst]
"Yield elements evaluated in the form for each element in the list."
`(let [[f (lambda [it] ~form)]]
(for [v ~lst]
(yield (f v)))))
(defmacro ap-map-when [predfn rep lst]
"Yield elements evaluated for each element in the list when the
predicate function returns True."
`(let [[f (lambda [it] ~rep)]]
(for [it ~lst]
(if (~predfn it)
(yield (f it))
(yield it)))))
(defmacro ap-filter [form lst]
"Yield elements returned when the predicate form evaluates to True."
`(let [[pred (lambda [it] ~form)]]
(for [val ~lst]
(if (pred val)
(yield val)))))
(defmacro ap-reject [form lst]
"Yield elements returned when the predicate form evaluates to False"
`(ap-filter (not ~form) ~lst))
(defmacro ap-dotimes [n &rest body]
"Execute body for side effects `n' times, with it bound from 0 to n-1"
(unless (numeric? n)
(raise (TypeError (.format "{0!r} is not a number" n))))
`(ap-each (range ~n) ~@body))
(defmacro ap-first [predfn lst]
"Yield the first element that passes `predfn`"
`(let [[n (gensym)]]
(ap-each ~lst (when ~predfn (setv n it) (break)))
n))
(defmacro ap-last [predfn lst]
"Yield the last element that passes `predfn`"
`(let [[n (gensym)]]
(ap-each ~lst (none? n)
(when ~predfn
(setv n it)))
n))
(defmacro ap-reduce [form lst &optional [initial-value None]]
"Anaphoric form of reduce, `acc' and `it' can be used for a form"
(if (none? initial-value)
`(let [[acc (car ~lst)]]
(ap-each (cdr ~lst) (setv acc ~form))
acc)
`(let [[acc ~initial-value]]
(ap-each ~lst (setv acc ~form))
acc)))

View File

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

91
hy/contrib/loop.hy Normal file
View File

@ -0,0 +1,91 @@
;;; Hy tail-call optimization
;;
;; Copyright (c) 2014 Clinton Dreisbach <clinton@dreisbach.us>
;; Copyright (c) 2014 Paul R. Tagliamonte <tag@pault.ag>
;;
;; 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.
;;
;;; The loop/recur macro allows you to construct functions that use tail-call
;;; optimization to allow arbitrary levels of recursion.
(defn --trampoline-- [f]
"Wrap f function and make it tail-call optimized."
;; Takes the function "f" and returns a wrapper that may be used for tail-
;; recursive algorithms. Note that the returned function is not side-effect
;; free and should not be called from anywhere else during tail recursion.
(setv result None)
;; We have to put this in a list because of Python's
;; weirdness around local variables.
;; Assigning directly to it later would cause it to
;; shadow in a new scope.
(setv active [False])
(setv accumulated [])
(fn [&rest args]
(.append accumulated args)
(when (not (first active))
(assoc active 0 True)
(while (> (len accumulated) 0)
(setv result (apply f (.pop accumulated))))
(assoc active 0 False)
result)))
(defn recursive-replace [old-term new-term body]
"Recurses through lists of lists looking for old-term and replacing it with new-term."
((type body)
(list-comp (cond
[(= term old-term) new-term]
[(instance? hy.HyList term)
(recursive-replace old-term new-term term)]
[True term]) [term body])))
(defmacro/g! fnr [signature &rest body]
(let [[new-body (recursive-replace 'recur g!recur-fn body)]]
`(do
(import [hy.contrib.loop [--trampoline--]])
(with-decorator
--trampoline--
(def ~g!recur-fn (fn [~@signature] ~@new-body)))
~g!recur-fn)))
(defmacro defnr [name lambda-list &rest body]
(if (not (= (type name) HySymbol))
(macro-error name "defnr takes a name as first argument"))
`(setv ~name (fnr ~lambda-list ~@body)))
(defmacro/g! loop [bindings &rest body]
;; Use inside functions like so:
;; (defun factorial [n]
;; (loop [[i n]
;; [acc 1]]
;; (if (= i 0)
;; acc
;; (recur (dec i) (* acc i)))))
;;
;; If recur is used in a non-tail-call position, None is returned, which
;; causes chaos. Fixing this to detect if recur is in a tail-call position
;; and erroring if not is a giant TODO.
(let [[fnargs (map (fn [x] (first x)) bindings)]
[initargs (map second bindings)]]
`(do (defnr ~g!recur-fn [~@fnargs] ~@body)
(~g!recur-fn ~@initargs))))

View File

@ -1,54 +1,27 @@
;;; Meth
;; based on paultag's meth library to access a Flask based application
;;; Hy on Meth
;;; based on paultag's meth library to access a Flask based application
(defmacro route [name path params code]
"Default get request"
`(let [[deco (.route app ~path)]]
(with-decorator deco
(defn ~name ~params ~@code))))
(defmacro route-with-methods [name path params code methods]
(defmacro route-with-methods [name path methods params &rest code]
"Same as route but with an extra methods array to specify HTTP methods"
`(let [[deco (kwapply (.route app ~path)
{"methods" ~methods})]]
(with-decorator deco
(defn ~name ~params ~@code))))
(defn ~name ~params
(progn ~@code)))))
;; Some macro examples
(defmacro post-route [name path params code]
(defmacro route [name path params &rest code]
"Get request"
`(route-with-methods ~name ~path ["GET"] ~params ~@code))
(defmacro post-route [name path params &rest code]
"Post request"
`(route-with-methods ~name ~path ~params ~code ["POST"]))
`(route-with-methods ~name ~path ["POST"] ~params ~@code))
(defmacro put-route [name path params code]
(defmacro put-route [name path params &rest code]
"Put request"
`(route-with-methods ~name ~path ~params ~code ["PUT"]))
`(route-with-methods ~name ~path ["PUT"] ~params ~@code))
(defmacro delete-route [name path params code]
(defmacro delete-route [name path params &rest code]
"Delete request"
`(route-with-methods ~name ~path ~params ~code ["DELETE"]))
;;; Simple example application
;;; Requires to have Flask installed
;; (import [flask [Flask]])
;; (setv app (Flask "__main__"))
;; (require methy)
;; (print "setup / with GET")
;; (route get-index "/" [] (str "Hy world!"))
;; (print "setup /post with POST")
;; (post-route post-index "/post" [] (str "Hy post world!"))
;; (route-with-methods both-index "/both" []
;; (str "Hy to both worlds!") ["GET" "POST"])
;; (.run app)
;;; Now you can do:
;;; curl 127.0.0.1:5000
;;; curl -X POST 127.0.0.1:5000/post
;;; curl -X POST 127.0.0.1:5000/both
;;; curl 127.0.0.1:5000/both
`(route-with-methods ~name ~path ["DELETE"] ~params ~@code))

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

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

51
hy/contrib/profile.hy Normal file
View File

@ -0,0 +1,51 @@
;;; Hy profiling macros
;;
;; Copyright (c) 2013 Paul R. Tagliamonte <tag@pault.ag>
;;
;; 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.
;;
;;; These macros make debugging where bottlenecks exist easier.
(defmacro profile/calls [&rest body]
`(do
(import [pycallgraph [PyCallGraph]]
[pycallgraph.output [GraphvizOutput]])
(with* [(apply PyCallGraph [] {"output" (GraphvizOutput)})]
~@body)))
(defmacro/g! profile/cpu [&rest body]
" Profile a bit of code "
`(do
(import cProfile pstats)
(if-python2
(import [StringIO [StringIO]])
(import [io [StringIO]]))
(setv ~g!hy-pr (.Profile cProfile))
(.enable ~g!hy-pr)
(do ~@body)
(.disable ~g!hy-pr)
(setv ~g!hy-s (StringIO))
(setv ~g!hy-ps
(.sort-stats (kwapply (.Stats pstats ~g!hy-pr) {"stream" ~g!hy-s})))
(.print-stats ~g!hy-ps)
(print (.getvalue ~g!hy-s))))

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

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

View File

@ -28,13 +28,13 @@
(defmacro macro-error [location reason]
"error out properly within a macro"
`(raise (hy.compiler.HyTypeError ~location ~reason)))
`(raise (hy.errors.HyMacroExpansionError ~location ~reason)))
(defmacro defmacro-alias [names lambda-list &rest body]
"define one macro with several names"
(setv ret `(do))
(foreach [name names]
(for* [name names]
(.append ret
`(defmacro ~name ~lambda-list ~@body)))
ret)
@ -52,7 +52,7 @@
(setv macroed_variables [])
(if (not (isinstance variables HyList))
(macro-error variables "let lexical context must be a list"))
(foreach [variable variables]
(for* [variable variables]
(if (isinstance variable HyList)
(do
(if (!= (len variable) 2)

View File

@ -23,18 +23,35 @@
;;;; to make functional programming slightly easier.
;;;;
(import [hy._compat [long-type]]) ; long for python2, int for python3
(import [hy.models.cons [HyCons]])
(defn _numeric-check [x]
(if (not (numeric? x))
(raise (TypeError (.format "{0!r} is not a number" x)))))
(defn coll? [coll]
"Checks whether item is a collection"
(and (iterable? coll) (not (string? coll))))
(defn cons [a b]
"Return a fresh cons cell with car = a and cdr = b"
(HyCons a b))
(defn cons? [c]
"Check whether c can be used as a cons object"
(instance? HyCons c))
(defn cycle [coll]
"Yield an infinite repetition of the items in coll"
(setv seen [])
(foreach [x coll]
(for* [x coll]
(yield x)
(.append seen x))
(while seen
(foreach [x seen]
(for* [x seen]
(yield x))))
(defn dec [n]
@ -42,20 +59,33 @@
(_numeric-check n)
(- n 1))
(defn disassemble [tree &optional [codegen false]]
"Dump the python AST for a given Hy tree to standard output
If the second argument is true, generate python code instead."
(import astor)
(import hy.compiler)
(fake-source-positions tree)
(setv compiled (hy.compiler.hy_compile tree (calling-module-name)))
(print ((if codegen
astor.codegen.to_source
astor.dump)
compiled)))
(defn distinct [coll]
"Return a generator from the original collection with duplicates
removed"
(let [[seen []] [citer (iter coll)]]
(foreach [val citer]
(let [[seen (set)] [citer (iter coll)]]
(for* [val citer]
(if (not_in val seen)
(do
(yield val)
(.append seen val))))))
(.add seen val))))))
(defn drop [count coll]
"Drop `count` elements from `coll` and yield back the rest"
(let [[citer (iter coll)]]
(try (foreach [i (range count)]
(try (for* [i (range count)]
(next citer))
(catch [StopIteration]))
citer))
@ -63,10 +93,10 @@
(defn drop-while [pred coll]
"Drop all elements of `coll` until `pred` is False"
(let [[citer (iter coll)]]
(foreach [val citer]
(for* [val citer]
(if (not (pred val))
(do (yield val) (break))))
(foreach [val citer]
(for* [val citer]
(yield val))))
(defn empty? [coll]
@ -78,17 +108,73 @@
(_numeric-check n)
(= (% n 2) 0))
(defn every? [pred coll]
"Return true if (pred x) is logical true for every x in coll, else false"
(all (map pred coll)))
(defn fake-source-positions [tree]
"Fake the source positions for a given tree"
(if (and (iterable? tree) (not (string? tree)))
(for* [subtree tree]
(fake-source-positions subtree)))
(for* [attr '[start-line end-line start-column end-column]]
(if (not (hasattr tree attr))
(setattr tree attr 1))))
(defn filter [pred coll]
"Return all elements from `coll` that pass `pred`"
(let [[citer (iter coll)]]
(foreach [val citer]
(for* [val citer]
(if (pred val)
(yield val)))))
(defn flatten [coll]
"Return a single flat list expanding all members of coll"
(if (coll? coll)
(_flatten coll [])
(raise (TypeError (.format "{0!r} is not a collection" coll)))))
(defn _flatten [coll result]
(if (and (iterable? coll) (not (string? coll)))
(do (for* [b coll]
(_flatten b result)))
(.append result coll))
result)
(defn float? [x]
"Return True if x is float"
(isinstance x float))
(import [threading [Lock]])
(setv _gensym_counter 1234)
(setv _gensym_lock (Lock))
(defn gensym [&optional [g "G"]]
(let [[new_symbol None]]
(global _gensym_counter)
(global _gensym_lock)
(.acquire _gensym_lock)
(try (do (setv _gensym_counter (inc _gensym_counter))
(setv new_symbol (HySymbol (.format ":{0}_{1}" g _gensym_counter))))
(finally (.release _gensym_lock)))
new_symbol))
(defn calling-module-name [&optional [n 1]]
"Get the name of the module calling `n` levels up the stack from the
`calling-module-name` function call (by default, one level up)"
(import inspect)
(setv f (get (.stack inspect) (+ n 1) 0))
(get f.f_globals "__name__"))
(defn first [coll]
"Return first item from `coll`"
(get coll 0))
(defn identity [x]
"Returns the argument unchanged"
x)
(defn inc [n]
"Increment n by 1"
(_numeric-check n)
@ -97,11 +183,20 @@
(defn instance? [klass x]
(isinstance x klass))
(defn integer [x]
"Return Hy kind of integer"
(long-type x))
(defn integer? [x]
"Return True if x in an integer"
(if-python2
(isinstance x (, int long))
(isinstance x int)))
(isinstance x (, int long-type)))
(defn integer-char? [x]
"Return True if char `x` parses as an integer"
(try
(integer? (int x))
(catch [e ValueError] False)
(catch [e TypeError] False)))
(defn iterable? [x]
"Return true if x is iterable"
@ -119,6 +214,26 @@
(try (= x (iter x))
(catch [TypeError] false)))
(defn list* [hd &rest tl]
"Return a dotted list construed from the elements of the argument"
(if (not tl)
hd
(cons hd (apply list* tl))))
(defn macroexpand [form]
"Return the full macro expansion of form"
(import hy.macros)
(setv name (calling-module-name))
(hy.macros.macroexpand form name))
(defn macroexpand-1 [form]
"Return the single step macro expansion of form"
(import hy.macros)
(setv name (calling-module-name))
(hy.macros.macroexpand-1 form name))
(defn neg? [n]
"Return true if n is < 0"
(_numeric-check n)
@ -128,6 +243,10 @@
"Return true if x is None"
(is x None))
(defn nil? [x]
"Return true if x is nil (None)"
(is x None))
(defn numeric? [x]
(import numbers)
(instance? numbers.Number x))
@ -155,15 +274,19 @@
(defn remove [pred coll]
"Return coll with elements removed that pass `pred`"
(let [[citer (iter coll)]]
(foreach [val citer]
(for* [val citer]
(if (not (pred val))
(yield val)))))
(defn rest [coll]
"Get all the elements of a coll, except the first."
(slice coll 1))
(defn repeat [x &optional n]
"Yield x forever or optionally n times"
(if (none? n)
(setv dispatch (fn [] (while true (yield x))))
(setv dispatch (fn [] (foreach [_ (range n)] (yield x)))))
(setv dispatch (fn [] (for* [_ (range n)] (yield x)))))
(dispatch))
(defn repeatedly [func]
@ -175,6 +298,16 @@
"Return second item from `coll`"
(get coll 1))
(defn some [pred coll]
"Return true if (pred x) is logical true for any x in coll, else false"
(any (map pred coll)))
(defn string [x]
"Cast x as current string implementation"
(if-python2
(unicode x)
(str x)))
(defn string? [x]
"Return True if x is a string"
(if-python2
@ -185,7 +318,7 @@
"Take `count` elements from `coll`, or the whole set if the total
number of entries in `coll` is less than `count`."
(let [[citer (iter coll)]]
(foreach [_ (range count)]
(for* [_ (range count)]
(yield (next citer)))))
(defn take-nth [n coll]
@ -193,16 +326,16 @@
raises ValueError for (not (pos? n))"
(if (pos? n)
(let [[citer (iter coll)] [skip (dec n)]]
(foreach [val citer]
(for* [val citer]
(yield val)
(foreach [_ (range skip)]
(for* [_ (range skip)]
(next citer))))
(raise (ValueError "n must be positive"))))
(defn take-while [pred coll]
"Take all elements while `pred` is true"
(let [[citer (iter coll)]]
(foreach [val citer]
(for* [val citer]
(if (pred val)
(yield val)
(break)))))
@ -212,9 +345,16 @@
(_numeric_check n)
(= n 0))
(def *exports* ["cycle" "dec" "distinct" "drop" "drop_while" "empty?"
"even?" "filter" "float?" "inc"
"instance?" "integer?" "iterable?" "iterate" "iterator?" "neg?"
"none?" "nth" "numeric?" "odd?" "pos?" "remove" "repeat"
"repeatedly" "second" "string?" "take" "take_nth" "take_while"
"zero?"])
(defn zipwith [func &rest lists]
"Zip the contents of several lists and map a function to the result"
(do
(import functools)
(map (functools.partial (fn [f args] (apply f args)) func) (apply zip lists))))
(def *exports* '[calling-module-name coll? cons cons? cycle dec distinct
disassemble drop drop-while empty? even? every? first filter
flatten float? gensym identity inc instance? integer
integer? integer-char? iterable? iterate iterator?
list* macroexpand macroexpand-1 neg? nil? none? nth
numeric? odd? pos? remove repeat repeatedly rest second
some string string? take take-nth take-while zero? zipwith])

View File

@ -26,27 +26,64 @@
;;; They are automatically required in every module, except inside hy.core
(import [hy.models.list [HyList]]
[hy.models.symbol [HySymbol]])
(defmacro for [args &rest body]
"shorthand for nested foreach loops:
(for [x foo y bar] baz) ->
(foreach [x foo]
(foreach [y bar]
"shorthand for nested for loops:
(for [x foo
y bar]
baz) ->
(for* [x foo]
(for* [y bar]
baz))"
;; TODO: that signature sucks.
;; (for [[x foo] [y bar]] baz) would be more consistent
(if (% (len args) 2)
(macro-error args "for needs an even number of elements in its first argument"))
(if args
`(foreach [~(.pop args 0) ~(.pop args 0)] (for ~args ~@body))
`(do ~@body)))
(if (odd? (len args))
(macro-error args "`for' requires an even number of args."))
(if (empty? body)
(macro-error None "`for' requires a body to evaluate"))
(if (empty? args)
`(do ~@body)
(if (= (len args) 2)
; basecase, let's just slip right in.
`(for* [~@args] ~@body)
; otherwise, let's do some legit handling.
(let [[alist (slice args 0 nil 2)]
[ilist (slice args 1 nil 2)]]
`(do
(import itertools)
(for* [(, ~@alist) (itertools.product ~@ilist)] ~@body))))))
(defmacro-alias [car first] [thing]
(defmacro with [args &rest body]
"shorthand for nested for* loops:
(with [[x foo] [y bar]] baz) ->
(with* [x foo]
(with* [y bar]
baz))"
(if (not (empty? args))
(let [[primary (.pop args 0)]]
(if (isinstance primary HyList)
;;; OK. if we have a list, we can go ahead and unpack that
;;; as the argument to with.
`(with* [~@primary] (with ~args ~@body))
;;; OK, let's just give it away. This may not be something we
;;; can do, but that's really the programmer's problem.
`(with* [~primary] (with ~args ~@body))))
`(do ~@body)))
(defmacro car [thing]
"Get the first element of a list/cons"
`(get ~thing 0))
(defmacro-alias [cdr rest] [thing]
(defmacro cdr [thing]
"Get all the elements of a thing, except the first"
`(slice ~thing 1))
@ -72,7 +109,7 @@
(setv root (check-branch branch))
(setv latest-branch root)
(foreach [branch branches]
(for* [branch branches]
(setv cur-branch (check-branch branch))
(.append latest-branch cur-branch)
(setv latest-branch cur-branch))
@ -82,7 +119,7 @@
(defmacro -> [head &rest rest]
;; TODO: fix the docstring by someone who understands this
(setv ret head)
(foreach [node rest]
(for* [node rest]
(if (not (isinstance node HyExpression))
(setv node `(~node)))
(.insert node 1 ret)
@ -93,7 +130,7 @@
(defmacro ->> [head &rest rest]
;; TODO: fix the docstring by someone who understands this
(setv ret head)
(foreach [node rest]
(for* [node rest]
(if (not (isinstance node HyExpression))
(setv node `(~node)))
(.append node ret)
@ -101,6 +138,13 @@
ret)
(defmacro if-not [test not-branch &optional [yes-branch nil]]
"Like `if`, but execute the first branch when the test fails"
(if (nil? yes-branch)
`(if (not ~test) ~not-branch)
`(if (not ~test) ~not-branch ~yes-branch)))
(defmacro when [test &rest body]
"Execute `body` when `test` is true"
`(if ~test (do ~@body)))
@ -108,11 +152,50 @@
(defmacro unless [test &rest body]
"Execute `body` when `test` is false"
`(if ~test None (do ~@body)))
`(if-not ~test (do ~@body)))
(defmacro yield-from [iterable]
"Yield all the items from iterable"
;; TODO: this needs some gensym love
`(foreach [_hy_yield_from_x ~iterable]
(yield _hy_yield_from_x)))
(let [[x (gensym)]]
`(for* [~x ~iterable]
(yield ~x))))
(defmacro with-gensyms [args &rest body]
`(let ~(HyList (map (fn [x] `[~x (gensym '~x)]) args))
~@body))
(defmacro defmacro/g! [name args &rest body]
(let [[syms (list (distinct (filter (fn [x] (.startswith x "g!")) (flatten body))))]]
`(defmacro ~name [~@args]
(let ~(HyList (map (fn [x] `[~x (gensym (slice '~x 2))]) syms))
~@body))))
(defmacro kwapply [call kwargs]
"Use a dictionary as keyword arguments"
(let [[-fun (car call)]
[-args (cdr call)]
[-okwargs `[(list (.items ~kwargs))]]]
(while (= -fun "kwapply") ;; join any further kw
(if (not (= (len -args) 2))
(macro-error
call
(.format "Trying to call nested kwapply with {0} args instead of 2"
(len -args))))
(.insert -okwargs 0 `(list (.items ~(car (cdr -args)))))
(setv -fun (car (car -args)))
(setv -args (cdr (car -args))))
`(apply ~-fun [~@-args] (dict (sum ~-okwargs [])))))
(defmacro-alias [defn-alias defun-alias] [names lambda-list &rest body]
"define one function with several names"
(let [[main (first names)]
[aliases (rest names)]]
(setv ret `(do (defn ~main ~lambda-list ~@body)))
(for* [name aliases]
(.append ret
`(setv ~name ~main)))
ret))

View File

@ -1,4 +1,7 @@
# -*- encoding: utf-8 -*-
#
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
# Copyright (c) 2013 Bob Tolbert <bob@tolbert.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@ -18,6 +21,8 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
import traceback
class HyError(Exception):
"""
@ -25,3 +30,115 @@ class HyError(Exception):
Exception.
"""
pass
try:
from clint.textui import colored
except Exception:
class colored:
@staticmethod
def black(foo):
return foo
@staticmethod
def red(foo):
return foo
@staticmethod
def green(foo):
return foo
@staticmethod
def yellow(foo):
return foo
@staticmethod
def blue(foo):
return foo
@staticmethod
def magenta(foo):
return foo
@staticmethod
def cyan(foo):
return foo
@staticmethod
def white(foo):
return foo
class HyCompileError(HyError):
def __init__(self, exception, traceback=None):
self.exception = exception
self.traceback = traceback
def __str__(self):
if isinstance(self.exception, HyTypeError):
return str(self.exception)
if self.traceback:
tb = "".join(traceback.format_tb(self.traceback)).strip()
else:
tb = "No traceback available. 😟"
return("Internal Compiler Bug 😱\n%s: %s\nCompilation traceback:\n%s"
% (self.exception.__class__.__name__,
self.exception, tb))
class HyTypeError(TypeError):
def __init__(self, expression, message):
super(HyTypeError, self).__init__(message)
self.expression = expression
self.message = message
self.source = None
self.filename = None
def __str__(self):
line = self.expression.start_line
start = self.expression.start_column
end = self.expression.end_column
source = []
if self.source is not None:
source = self.source.split("\n")[line-1:self.expression.end_line]
if line == self.expression.end_line:
length = end - start
else:
length = len(source[0]) - start
result = ""
result += ' File "%s", line %d, column %d\n\n' % (self.filename,
line,
start)
if len(source) == 1:
result += ' %s\n' % colored.red(source[0])
result += ' %s%s\n' % (' '*(start-1),
colored.green('^' + '-'*(length-1) + '^'))
if len(source) > 1:
result += ' %s\n' % colored.red(source[0])
result += ' %s%s\n' % (' '*(start-1),
colored.green('^' + '-'*length))
if len(source) > 2: # write the middle lines
for line in source[1:-1]:
result += ' %s\n' % colored.red("".join(line))
result += ' %s\n' % colored.green("-"*len(line))
# write the last line
result += ' %s\n' % colored.red("".join(source[-1]))
result += ' %s\n' % colored.green('-'*(end-1) + '^')
result += colored.yellow("%s: %s\n\n" %
(self.__class__.__name__,
self.message))
return result.encode('utf-8')
class HyMacroExpansionError(HyTypeError):
pass

View File

@ -1,4 +1,5 @@
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
# Copyright (c) 2013 Bob Tolbert <bob@tolbert.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@ -18,11 +19,9 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
from py_compile import wr_long, MAGIC
from hy.compiler import hy_compile
from hy.compiler import hy_compile, HyTypeError
from hy.models import HyObject
from hy.lex import tokenize
from hy.lex import tokenize, LexException
from io import open
import marshal
@ -32,7 +31,7 @@ import ast
import os
import __future__
from hy._compat import builtins, long_type
from hy._compat import PY3, PY33, MAGIC, builtins, long_type, wr_long
def ast_compile(ast, filename, mode):
@ -73,17 +72,28 @@ def import_file_to_module(module_name, fpath):
mod = imp.new_module(module_name)
mod.__file__ = fpath
eval(ast_compile(_ast, fpath, "exec"), mod.__dict__)
except (HyTypeError, LexException) as e:
if e.source is None:
with open(fpath, 'rt') as fp:
e.source = fp.read()
e.filename = fpath
raise
except Exception:
sys.modules.pop(module_name, None)
raise
return mod
def import_buffer_to_module(module_name, buf):
_ast = import_buffer_to_ast(buf, module_name)
mod = imp.new_module(module_name)
eval(ast_compile(_ast, "", "exec"), mod.__dict__)
try:
_ast = import_buffer_to_ast(buf, module_name)
mod = imp.new_module(module_name)
eval(ast_compile(_ast, "", "exec"), mod.__dict__)
except (HyTypeError, LexException) as e:
if e.source is None:
e.source = buf
e.filename = '<stdin>'
raise
return mod
@ -128,12 +138,12 @@ def write_hy_as_pyc(fname):
open_ = builtins.open
with open_(cfile, 'wb') as fc:
if sys.version_info[0] >= 3:
if PY3:
fc.write(b'\0\0\0\0')
else:
fc.write('\0\0\0\0')
wr_long(fc, timestamp)
if (sys.version_info[0] >= 3 and sys.version_info[1] >= 3):
if PY33:
wr_long(fc, st.st_size)
marshal.dump(code, fc)
fc.flush()

View File

@ -33,6 +33,5 @@ def tokenize(buf):
return parser.parse(lexer.lex(buf))
except LexingError as e:
pos = e.getsourcepos()
raise LexException(
"Could not identify the next token at line %s, column %s" % (
pos.lineno, pos.colno))
raise LexException("Could not identify the next token.",
pos.lineno, pos.colno)

View File

@ -1,4 +1,5 @@
# Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
# Copyright (c) 2013 Bob Tolbert <bob@tolbert.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@ -23,9 +24,43 @@ from hy.errors import HyError
class LexException(HyError):
"""Error during the Lexing of a Hython expression."""
pass
def __init__(self, message, lineno, colno):
super(LexException, self).__init__(message)
self.message = message
self.lineno = lineno
self.colno = colno
self.source = None
self.filename = '<stdin>'
def __str__(self):
from hy.errors import colored
line = self.lineno
start = self.colno
result = ""
source = self.source.split("\n")
if line > 0 and start > 0:
result += ' File "%s", line %d, column %d\n\n' % (self.filename,
line,
start)
if len(self.source) > 0:
source_line = source[line-1]
else:
source_line = ""
result += ' %s\n' % colored.red(source_line)
result += ' %s%s\n' % (' '*(start-1), colored.green('^'))
result += colored.yellow("LexException: %s\n\n" % self.message)
return result
class PrematureEndOfInput(LexException):
"""We got a premature end of input"""
pass
def __init__(self, message):
super(PrematureEndOfInput, self).__init__(message, -1, -1)

View File

@ -40,6 +40,7 @@ lg.add('QUASIQUOTE', r'`%s' % end_quote)
lg.add('UNQUOTESPLICE', r'~@%s' % end_quote)
lg.add('UNQUOTE', r'~%s' % end_quote)
lg.add('HASHBANG', r'#!.*[^\r\n]')
lg.add('HASHREADER', r'#.')
lg.add('STRING', r'''(?x)
@ -59,7 +60,7 @@ lg.add('STRING', r'''(?x)
lg.add('IDENTIFIER', r'[^()\[\]{}\'"\s;]+')
lg.ignore(r';.*[\r\n]+')
lg.ignore(r';.*(?=\r|\n|$)')
lg.ignore(r'\s+')

View File

@ -24,6 +24,7 @@ from functools import wraps
from rply import ParserGenerator
from hy.models.complex import HyComplex
from hy.models.cons import HyCons
from hy.models.dict import HyDict
from hy.models.expression import HyExpression
from hy.models.float import HyFloat
@ -95,9 +96,40 @@ def real_main_empty(p):
return []
def reject_spurious_dots(*items):
"Reject the spurious dots from items"
for list in items:
for tok in list:
if tok == "." and type(tok) == HySymbol:
raise LexException("Malformed dotted list",
tok.start_line, tok.start_column)
@pg.production("paren : LPAREN list_contents RPAREN")
@set_boundaries
def paren(p):
cont = p[1]
# Dotted lists are expressions of the form
# (a b c . d)
# that evaluate to nested cons cells of the form
# (a . (b . (c . d)))
if len(cont) >= 3 and isinstance(cont[-2], HySymbol) and cont[-2] == ".":
reject_spurious_dots(cont[:-2], cont[-1:])
if len(cont) == 3:
# Two-item dotted list: return the cons cell directly
return HyCons(cont[0], cont[2])
else:
# Return a nested cons cell
return HyCons(cont[0], paren([p[0], cont[1:], p[2]]))
# Warn preemptively on a malformed dotted list.
# Only check for dots after the first item to allow for a potential
# attribute accessor shorthand
reject_spurious_dots(cont[1:])
return HyExpression(p[1])
@ -150,6 +182,15 @@ def term_unquote_splice(p):
return HyExpression([HySymbol("unquote_splice"), p[1]])
@pg.production("term : HASHREADER term")
@set_quote_boundaries
def hash_reader(p):
st = p[0].getstr()[1]
str_object = HyString(st)
expr = p[1]
return HyExpression([HySymbol("dispatch_reader_macro"), str_object, expr])
@pg.production("dict : LCURLY list_contents RCURLY")
@set_boundaries
def t_dict(p):
@ -220,6 +261,7 @@ def t_identifier(p):
table = {
"true": "True",
"false": "False",
"nil": "None",
"null": "None",
}
@ -232,11 +274,19 @@ def t_identifier(p):
if obj.startswith("&"):
return HyLambdaListKeyword(obj)
if obj.startswith("*") and obj.endswith("*") and obj not in ("*", "**"):
obj = obj[1:-1].upper()
def mangle(p):
if p.startswith("*") and p.endswith("*") and p not in ("*", "**"):
p = p[1:-1].upper()
if "-" in obj and obj != "-":
obj = obj.replace("-", "_")
if "-" in p and p != "-":
p = p.replace("-", "_")
if p.endswith("?") and p != "?":
p = "is_%s" % (p[:-1])
return p
obj = ".".join([mangle(part) for part in obj.split(".")])
return HySymbol(obj)
@ -245,12 +295,11 @@ def t_identifier(p):
def error_handler(token):
tokentype = token.gettokentype()
if tokentype == '$end':
raise PrematureEndOfInput
raise PrematureEndOfInput("Premature end of input")
else:
raise LexException(
"Ran into a %s where it wasn't expected at line %s, column %s" %
(tokentype, token.source_pos.lineno, token.source_pos.colno)
)
"Ran into a %s where it wasn't expected." % tokentype,
token.source_pos.lineno, token.source_pos.colno)
parser = pg.build()

View File

@ -26,10 +26,12 @@ from hy.models.integer import HyInteger
from hy.models.float import HyFloat
from hy.models.complex import HyComplex
from hy.models.dict import HyDict
from hy._compat import str_type
from hy._compat import str_type, long_type
from hy.errors import HyTypeError, HyMacroExpansionError
from collections import defaultdict
import sys
CORE_MACROS = [
"hy.core.bootstrap",
@ -40,6 +42,7 @@ EXTRA_MACROS = [
]
_hy_macros = defaultdict(dict)
_hy_reader = defaultdict(dict)
def macro(name):
@ -63,6 +66,28 @@ def macro(name):
return _
def reader(name):
"""Decorator to define a macro called `name`.
This stores the macro `name` in the namespace for the module where it is
defined.
If the module where it is defined is in `hy.core`, then the macro is stored
in the default `None` namespace.
This function is called from the `defmacro` special form in the compiler.
"""
def _(fn):
module_name = fn.__module__
if module_name.startswith("hy.core"):
module_name = None
_hy_reader[module_name][name] = fn
return fn
return _
def require(source_module, target_module):
"""Load the macros from `source_module` in the namespace of
`target_module`.
@ -75,6 +100,11 @@ def require(source_module, target_module):
for name, macro in macros.items():
refs[name] = macro
readers = _hy_reader[source_module]
reader_refs = _hy_reader[target_module]
for name, reader in readers.items():
reader_refs[name] = reader
# type -> wrapping function mapping for _wrap_value
_wrappers = {
@ -84,9 +114,14 @@ _wrappers = {
complex: HyComplex,
str_type: HyString,
dict: lambda d: HyDict(_wrap_value(x) for x in sum(d.items(), ())),
list: lambda l: HyList(_wrap_value(x) for x in l)
list: lambda l: HyList(_wrap_value(x) for x in l),
tuple: lambda t: HyList(_wrap_value(x) for x in t),
type(None): lambda foo: HySymbol("None"),
}
if sys.version_info[0] < 3: # do not add long on python3
_wrappers[long_type] = HyInteger
def _wrap_value(x):
"""Wrap `x` into the corresponding Hy type.
@ -157,9 +192,35 @@ def macroexpand_1(tree, module_name):
if m is None:
m = _hy_macros[None].get(fn)
if m is not None:
obj = _wrap_value(m(*ntree[1:]))
try:
obj = _wrap_value(m(*ntree[1:]))
except HyTypeError as e:
if e.expression is None:
e.expression = tree
raise
except Exception as e:
msg = "`" + str(tree[0]) + "' " + \
" ".join(str(e).split()[1:])
raise HyMacroExpansionError(tree, msg)
obj.replace(tree)
return obj
return ntree
return tree
def reader_macroexpand(char, tree, module_name):
"""Expand the reader macro "char" with argument `tree`."""
load_macros(module_name)
if not char in _hy_reader[module_name]:
raise HyTypeError(
char,
"`{0}' is not a reader macro in module '{1}'".format(
char,
module_name,
),
)
expr = _hy_reader[module_name][char](tree)
return _wrap_value(expr).replace(tree)

View File

@ -29,7 +29,7 @@ class HyObject(object):
if isinstance(other, HyObject):
for attr in ["start_line", "end_line",
"start_column", "end_column"]:
if not hasattr(self, attr):
if not hasattr(self, attr) and hasattr(other, attr):
setattr(self, attr, getattr(other, attr))
else:
raise TypeError("Can't replace a non Hy object with a Hy object")

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

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

View File

@ -28,3 +28,12 @@ class HyDict(HyList):
def __repr__(self):
return "{%s}" % (" ".join([repr(x) for x in self]))
def keys(self):
return self[0::2]
def values(self):
return self[1::2]
def items(self):
return list(zip(self.keys(), self.values()))

View File

@ -19,14 +19,16 @@
# DEALINGS IN THE SOFTWARE.
from hy.models import HyObject
from hy._compat import long_type
class HyInteger(HyObject, int):
class HyInteger(HyObject, long_type):
"""
Internal represntation of a Hy Integer. May raise a ValueError as if
int(foo) was caled, given HyInteger(foo).
int(foo) was called, given HyInteger(foo). On python 2.x long will
be used instead
"""
def __new__(cls, number, *args, **kwargs):
number = int(number)
number = long_type(number)
return super(HyInteger, cls).__new__(cls, number)

View File

@ -29,5 +29,8 @@ class HyKeyword(HyObject, str_type):
"""
def __new__(cls, value):
obj = str_type.__new__(cls, "\uFDD0" + value)
if not value.startswith("\uFDD0"):
value = "\uFDD0" + value
obj = str_type.__new__(cls, value)
return obj

View File

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

View File

@ -20,4 +20,4 @@
__appname__ = "hy"
__version__ = "0.9.11"
__version__ = "0.9.12"

View File

@ -20,8 +20,9 @@ if "%1" == "help" (
echo. - tox
echo. - d
echo. - r
echo. - clean
echo.
goto end
goto :EOF
)
if "%1" == "docs" (
@ -109,8 +110,25 @@ if "%1" == "r" (
goto :EOF
)
if "%1" == full (
if "%1" == "full" (
call :docs
call :d
call :tox
)
goto :EOF
)
if "%1" == "clean" (
:clean
if EXIST hy\*.pyc cmd /C del /S /Q hy\*.pyc
if EXIST tests\*pyc cmd /C del /S /Q tests\*pyc
for /r %%R in (__pycache__) do if EXIST %%R (rmdir /S /Q %%R)
if EXIST .tox\NUL cmd /C rmdir /S /Q .tox
if EXIST dist\NUL cmd /C rmdir /S /Q dist
if EXIST hy.egg-info\NUL cmd /C rmdir /S /Q hy.egg-info
if EXIST docs\_build\NUL cmd /C rmdir /S /Q docs\_build
goto :EOF
)
echo.Error: '%1' - unknown target
echo.
goto :help

View File

@ -1,5 +1,4 @@
-r requirements.txt
astor
tox
nose
Sphinx

View File

@ -1,3 +1,3 @@
# Check site / dev for more deps!
flake8
-e git+https://github.com/hylang/rply.git#egg=rply
rply>=0.7.0

View File

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

View File

@ -45,7 +45,7 @@ long_description = """Hy is a Python <--> Lisp layer. It helps
make things work nicer, and lets Python and the Hy lisp variant play
nice together. """
install_requires = ['rply>=0.6.2']
install_requires = ['rply>=0.7.0', 'astor>=0.3']
if sys.version_info[:2] < (2, 7):
install_requires.append('argparse>=1.2.1')
install_requires.append('importlib>=1.0.2')
@ -56,7 +56,6 @@ setup(
name=PKG,
version=__version__,
install_requires=install_requires,
dependency_links=['https://github.com/hylang/rply/zipball/master#egg=rply-0.6.2'],
entry_points={
'console_scripts': [
'hy = hy.cmdline:hy_main',

View File

@ -2,6 +2,7 @@
import hy # noqa
from .native_tests.cons import * # noqa
from .native_tests.defclass import * # noqa
from .native_tests.math import * # noqa
from .native_tests.native_macros import * # noqa
@ -11,3 +12,10 @@ from .native_tests.unless import * # noqa
from .native_tests.when import * # noqa
from .native_tests.with_decorator import * # noqa
from .native_tests.core import * # noqa
from .native_tests.reader_macros import * # noqa
from .native_tests.with_test import * # noqa
from .native_tests.contrib.anaphoric import * # noqa
from .native_tests.contrib.loop import * # noqa
from .native_tests.contrib.meth import * # noqa
from .native_tests.contrib.walk import * # noqa
from .native_tests.contrib.multi import * # noqa

View File

@ -22,7 +22,10 @@
from __future__ import unicode_literals
from hy import HyString
from hy.compiler import hy_compile, HyCompileError, HyTypeError
from hy.models import HyObject
from hy.compiler import hy_compile
from hy.errors import HyCompileError, HyTypeError
from hy.lex.exceptions import LexException
from hy.lex import tokenize
import ast
@ -42,10 +45,14 @@ def can_compile(expr):
def cant_compile(expr):
expr = tokenize(expr)
try:
hy_compile(expr, "__main__")
hy_compile(tokenize(expr), "__main__")
assert False
except HyTypeError as e:
# Anything that can't be compiled should raise a user friendly
# error, otherwise it's a compiler bug.
assert isinstance(e.expression, HyObject)
assert e.message
except HyCompileError as e:
# Anything that can't be compiled should raise a user friendly
# error, otherwise it's a compiler bug.
@ -253,7 +260,6 @@ 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():
@ -295,9 +301,9 @@ def test_ast_bad_assoc():
def test_ast_bad_with():
"Make sure AST can't compile invalid with"
cant_compile("(with)")
cant_compile("(with [])")
cant_compile("(with [] (pass))")
cant_compile("(with*)")
cant_compile("(with* [])")
cant_compile("(with* [] (pass))")
def test_ast_valid_while():
@ -305,14 +311,14 @@ def test_ast_valid_while():
can_compile("(while foo bar)")
def test_ast_valid_foreach():
"Make sure AST can compile valid foreach"
can_compile("(foreach [a 2])")
def test_ast_valid_for():
"Make sure AST can compile valid for"
can_compile("(for [a 2] (print a))")
def test_ast_invalid_foreach():
"Make sure AST can't compile invalid foreach"
cant_compile("(foreach [a 1] (else 1 2))")
def test_ast_invalid_for():
"Make sure AST can't compile invalid for"
cant_compile("(for* [a 1] (else 1 2))")
def test_ast_expression_basics():
@ -423,8 +429,54 @@ def test_compile_error():
"""Ensure we get compile error in tricky cases"""
try:
can_compile("(fn [] (= 1))")
except HyCompileError as e:
assert(str(e)
== "`=' needs at least 2 arguments, got 1 (line 1, column 8)")
except HyTypeError as e:
assert(e.message == "`=' needs at least 2 arguments, got 1.")
else:
assert(False)
def test_for_compile_error():
"""Ensure we get compile error in tricky 'for' cases"""
try:
can_compile("(fn [] (for)")
except LexException as e:
assert(e.message == "Premature end of input")
else:
assert(False)
try:
can_compile("(fn [] (for)))")
except LexException as e:
assert(e.message == "Ran into a RPAREN where it wasn't expected.")
else:
assert(False)
try:
can_compile("(fn [] (for [x]))")
except HyTypeError as e:
assert(e.message == "`for' requires an even number of args.")
else:
assert(False)
try:
can_compile("(fn [] (for [x xx]))")
except HyTypeError as e:
assert(e.message == "`for' requires a body to evaluate")
else:
assert(False)
def test_attribute_access():
"""Ensure attribute access compiles correctly"""
can_compile("(. foo bar baz)")
can_compile("(. foo [bar] baz)")
can_compile("(. foo bar [baz] [0] quux [frob])")
can_compile("(. foo bar [(+ 1 2 3 4)] quux [frob])")
cant_compile("(. foo bar :baz [0] quux [frob])")
cant_compile("(. foo bar baz (0) quux [frob])")
cant_compile("(. foo bar baz [0] quux {frob})")
def test_cons_correct():
"""Ensure cons gets compiled correctly"""
can_compile("(cons a b)")

View File

@ -0,0 +1,27 @@
# Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
from .test_ast import can_compile, cant_compile
def test_macro_nested_kwapply():
"Make sure nested kwapply compile correctly"
can_compile("(kwapply (kwapply (foo) bar) baz)")
cant_compile("(kwapply (kwapply (foo)) bar)")

View File

@ -1,4 +1,5 @@
from hy.importer import import_file_to_module, import_buffer_to_ast, MetaLoader
from hy.errors import HyTypeError
import os
import ast
@ -27,3 +28,16 @@ def test_imports():
assert _import_test() == "Error"
assert _import_test() is not None
def test_import_error_reporting():
"Make sure that (import) reports errors correctly."
def _import_error_test():
try:
import_buffer_to_ast("(import \"sys\")", '')
except HyTypeError:
return "Error reported"
assert _import_error_test() == "Error reported"
assert _import_error_test() is not None

View File

@ -1,4 +1,5 @@
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
# Copyright (c) 2014 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@ -26,6 +27,8 @@ from hy.models.complex import HyComplex
from hy.models.symbol import HySymbol
from hy.models.string import HyString
from hy.models.dict import HyDict
from hy.models.list import HyList
from hy.models.cons import HyCons
from hy.lex import LexException, PrematureEndOfInput, tokenize
@ -252,3 +255,82 @@ def test_complex():
assert entry == HyComplex("1.0j")
entry = tokenize("(j)")[0][0]
assert entry == HySymbol("j")
def test_reader_macro():
"""Ensure reader macros are handles properly"""
entry = tokenize("#^()")
assert entry[0][0] == HySymbol("dispatch_reader_macro")
assert entry[0][1] == HyString("^")
assert len(entry[0]) == 3
def test_lex_comment_382():
"""Ensure that we can tokenize sources with a comment at the end"""
entry = tokenize("foo ;bar\n;baz")
assert entry == [HySymbol("foo")]
def test_lex_mangling_star():
"""Ensure that mangling starred identifiers works according to plan"""
entry = tokenize("*foo*")
assert entry == [HySymbol("FOO")]
entry = tokenize("*")
assert entry == [HySymbol("*")]
entry = tokenize("*foo")
assert entry == [HySymbol("*foo")]
def test_lex_mangling_hyphen():
"""Ensure that hyphens get translated to underscores during mangling"""
entry = tokenize("foo-bar")
assert entry == [HySymbol("foo_bar")]
entry = tokenize("-")
assert entry == [HySymbol("-")]
def test_lex_mangling_qmark():
"""Ensure that identifiers ending with a question mark get mangled ok"""
entry = tokenize("foo?")
assert entry == [HySymbol("is_foo")]
entry = tokenize("?")
assert entry == [HySymbol("?")]
entry = tokenize("im?foo")
assert entry == [HySymbol("im?foo")]
entry = tokenize(".foo?")
assert entry == [HySymbol(".is_foo")]
entry = tokenize("foo.bar?")
assert entry == [HySymbol("foo.is_bar")]
entry = tokenize("foo?.bar")
assert entry == [HySymbol("is_foo.bar")]
entry = tokenize(".foo?.bar.baz?")
assert entry == [HySymbol(".is_foo.bar.is_baz")]
def test_simple_cons():
"""Check that cons gets tokenized correctly"""
entry = tokenize("(a . b)")[0]
assert entry == HyCons(HySymbol("a"), HySymbol("b"))
def test_dotted_list():
"""Check that dotted lists get tokenized correctly"""
entry = tokenize("(a b c . (d . e))")[0]
assert entry == HyCons(HySymbol("a"),
HyCons(HySymbol("b"),
HyCons(HySymbol("c"),
HyCons(HySymbol("d"),
HySymbol("e")))))
def test_cons_list():
"""Check that cons of something and a list gets tokenized as a list"""
entry = tokenize("(a . [])")[0]
assert entry == HyList([HySymbol("a")])
assert type(entry) == HyList
entry = tokenize("(a . ())")[0]
assert entry == HyExpression([HySymbol("a")])
assert type(entry) == HyExpression
entry = tokenize("(a b . {})")[0]
assert entry == HyDict([HySymbol("a"), HySymbol("b")])
assert type(entry) == HyDict

View File

@ -0,0 +1,11 @@
from hy.macros import macroexpand
from hy.compiler import HyTypeError
from hy.lex import tokenize
def test_reader_macro_error():
"""Check if we get correct error with wrong disptach character"""
try:
macroexpand(tokenize("(dispatch_reader_macro '- '())")[0], __name__)
except HyTypeError as e:
assert "with the character `-`" in str(e)

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

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

16
tests/models/test_dict.py Normal file
View File

@ -0,0 +1,16 @@
from hy.models.dict import HyDict
hydict = HyDict(["a", 1, "b", 2, "c", 3])
def test_dict_items():
assert hydict.items() == [("a", 1), ("b", 2), ("c", 3)]
def test_dict_keys():
assert hydict.keys() == ["a", "b", "c"]
def test_dict_values():
assert hydict.values() == [1, 2, 3]

View File

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

View File

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

View File

View File

@ -0,0 +1,101 @@
;; Copyright (c) 2013 James King <james@agentultra.com>
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.
;;;; some simple helpers
(require hy.contrib.anaphoric)
(defn assert-true [x]
(assert (= True x)))
(defn assert-false [x]
(assert (= False x)))
(defn assert-equal [x y]
(assert (= x y)))
(defn test-ap-if []
"NATIVE: testing anaphoric if"
(ap-if true (assert-true it))
(ap-if false true (assert-false it)))
(defn test-ap-each []
"NATIVE: testing anaphoric each"
(setv res [])
(ap-each [1 2 3 4] (.append res it))
(assert-equal res [1 2 3 4]))
(defn test-ap-each-while []
"NATIVE: testing anaphoric each-while"
(setv res [])
(ap-each-while [2 2 4 3 4 5 6] (even? it) (.append res it))
(assert-equal res [2 2 4]))
(defn test-ap-map []
"NATIVE: testing anaphoric map"
(assert-equal (list (ap-map (* it 3) [1 2 3]))
[3 6 9])
(assert-equal (list (ap-map (* it 3) []))
[]))
(defn test-ap-map-when []
"NATIVE: testing anaphoric map-when"
(assert-equal (list (ap-map-when even? (* it 2) [1 2 3 4]))
[1 4 3 8]))
(defn test-ap-filter []
"NATIVE: testing anaphoric filter"
(assert-equal (list (ap-filter (> it 2) [1 2 3 4]))
[3 4])
(assert-equal (list (ap-filter (even? it) [1 2 3 4]))
[2 4]))
(defn test-ap-reject []
"NATIVE: testing anaphoric filter"
(assert-equal (list (ap-reject (> it 2) [1 2 3 4]))
[1 2])
(assert-equal (list (ap-reject (even? it) [1 2 3 4]))
[1 3]))
(defn test-ap-dotimes []
"NATIVE: testing anaphoric dotimes"
(assert-equal (let [[n []]] (ap-dotimes 3 (.append n 3)) n)
[3 3 3])
(assert-equal (let [[n []]] (ap-dotimes 3 (.append n it)) n)
[0 1 2]))
(defn test-ap-first []
"NATIVE: testing anaphoric first"
(assert-equal (ap-first (> it 5) (range 10)) 6)
(assert-equal (ap-first (even? it) [1 2 3 4]) 2))
(defn test-ap-last []
"NATIVE: testing anaphoric last"
(assert-equal (ap-last (> it 5) (range 10)) 9)
(assert-equal (ap-last (even? it) [1 2 3 4]) 4))
(defn test-ap-reduce []
"NATIVE: testing anaphoric reduce"
(assert-equal (ap-reduce (* acc it) [1 2 3]) 6)
(assert-equal (ap-reduce (* acc it) [1 2 3] 6) 36)
(assert-equal (ap-reduce (+ acc " on " it) ["Hy" "meth"])
"Hy on meth")
(assert-equal (ap-reduce (+ acc it) [] 1) 1))

View File

@ -0,0 +1,46 @@
(require hy.contrib.loop)
(import sys)
(defn tco-sum [x y]
(loop [[x x] [y y]]
(cond
[(> y 0) (recur (inc x) (dec y))]
[(< y 0) (recur (dec x) (inc y))]
[True x])))
(defn non-tco-sum [x y]
(cond
[(> y 0) (inc (non-tco-sum x (dec y)))]
[(< y 0) (dec (non-tco-sum x (inc y)))]
[True x]))
(defn test-loop []
;; non-tco-sum should fail
(try
(setv n (non-tco-sum 100 10000))
(catch [e RuntimeError]
(assert true))
(else
(assert false)))
;; tco-sum should not fail
(try
(setv n (tco-sum 100 10000))
(catch [e RuntimeError]
(assert false))
(else
(assert (= n 10100)))))
(defn test-recur-in-wrong-loc []
(defn bad-recur [n]
(loop [[i n]]
(if (= i 0)
0
(inc (recur (dec i))))))
(try
(bad-recur 3)
(catch [e TypeError]
(assert true))
(else
(assert false))))

View File

@ -0,0 +1,54 @@
(require hy.contrib.meth)
(defclass FakeMeth []
"Mocking decorator class"
[[rules {}]
[route (fn [self rule &kwargs options]
(fn [f]
(assoc self.rules rule (, f options))
f))]])
(defn test_route []
(let [[app (FakeMeth)]]
(route get-index "/" [] (str "Hy world!"))
(setv app-rules (getattr app "rules"))
(assert (in "/" app-rules))
(let [[(, rule-fun rule-opt) (get app-rules "/")]]
(assert (not (empty? rule-opt)))
(assert (in "GET" (get rule-opt "methods")))
(assert (= (getattr rule-fun "__name__") "get_index"))
(assert (= "Hy world!" (rule-fun))))))
(defn test_post_route []
(let [[app (FakeMeth)]]
(post-route get-index "/" [] (str "Hy world!"))
(setv app-rules (getattr app "rules"))
(assert (in "/" app-rules))
(let [[(, rule-fun rule-opt) (get app-rules "/")]]
(assert (not (empty? rule-opt)))
(assert (in "POST" (get rule-opt "methods")))
(assert (= (getattr rule-fun "__name__") "get_index"))
(assert (= "Hy world!" (rule-fun))))))
(defn test_put_route []
(let [[app (FakeMeth)]]
(put-route get-index "/" [] (str "Hy world!"))
(setv app-rules (getattr app "rules"))
(assert (in "/" app-rules))
(let [[(, rule-fun rule-opt) (get app-rules "/")]]
(assert (not (empty? rule-opt)))
(assert (in "PUT" (get rule-opt "methods")))
(assert (= (getattr rule-fun "__name__") "get_index"))
(assert (= "Hy world!" (rule-fun))))))
(defn test_delete_route []
(let [[app (FakeMeth)]]
(delete-route get-index "/" [] (str "Hy world!"))
(setv app-rules (getattr app "rules"))
(assert (in "/" app-rules))
(let [[(, rule-fun rule-opt) (get app-rules "/")]]
(assert (not (empty? rule-opt)))
(assert (in "DELETE" (get rule-opt "methods")))
(assert (= (getattr rule-fun "__name__") "get_index"))
(assert (= "Hy world!" (rule-fun))))))

View File

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

View File

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

View File

@ -30,6 +30,14 @@
(defn assert-equal [x y]
(assert (= x y)))
(defn test-coll? []
"NATIVE: testing coll?"
(assert-true (coll? [1 2 3]))
(assert-true (coll? {"a" 1 "b" 2}))
(assert-true (coll? (range 10)))
(assert-false (coll? "abc"))
(assert-false (coll? 1)))
(defn test-cycle []
"NATIVE: testing cycle"
(assert-equal (list (cycle [])) [])
@ -115,6 +123,13 @@
(try (even? None)
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-every? []
"NATIVE: testing the every? function"
(assert-true (every? even? [2 4 6]))
(assert-false (every? even? [1 3 5]))
(assert-false (every? even? [2 4 5]))
(assert-true (every? even? [])))
(defn test-filter []
"NATIVE: testing the filter function"
(setv res (list (filter pos? [ 1 2 3 -4 5])))
@ -133,6 +148,26 @@
(setv res (list (filter none? [1 2 None 3 4 None 4 6])))
(assert-equal res [None None]))
(defn test-flatten []
"NATIVE: testing the flatten function"
(setv res (flatten [1 2 [3 4] 5]))
(assert-equal res [1 2 3 4 5])
(setv res (flatten ["foo" (, 1 2) [1 [2 3] 4] "bar"]))
(assert-equal res ["foo" 1 2 1 2 3 4 "bar"])
(setv res (flatten [1]))
(assert-equal res [1])
(setv res (flatten []))
(assert-equal res [])
(setv res (flatten (, 1)))
(assert-equal res [1])
;; test with None
(setv res (flatten (, 1 (, None 3))))
(assert-equal res [1 None 3])
(try (flatten "foo")
(catch [e [TypeError]] (assert (in "not a collection" (str e)))))
(try (flatten 12.34)
(catch [e [TypeError]] (assert (in "not a collection" (str e))))))
(defn test-float? []
"NATIVE: testing the float? function"
(assert-true (float? 4.2))
@ -141,6 +176,24 @@
(assert-true (float? -3.2))
(assert-false (float? "foo")))
(defn test-gensym []
"NATIVE: testing the gensym function"
(import [hy.models.symbol [HySymbol]])
(setv s1 (gensym))
(assert (isinstance s1 HySymbol))
(assert (= 0 (.find s1 ":G_")))
(setv s2 (gensym "xx"))
(setv s3 (gensym "xx"))
(assert (= 0 (.find s2 ":xx_")))
(assert (not (= s2 s3)))
(assert (not (= (str s2) (str s3)))))
(defn test-identity []
"NATIVE: testing the identity function"
(assert (= 4 (identity 4)))
(assert (= "hy" (identity "hy")))
(assert (= [1 2] (identity [1 2]))))
(defn test-inc []
"NATIVE: testing the inc function"
(assert-equal 3 (inc 2))
@ -163,7 +216,7 @@
(assert-false (instance? Foo2 foo))
(assert-true (instance? Foo foo3))
(assert-true (instance? float 1.0))
(assert-true (instance? int 3))
(assert-true (instance? int (int 3)))
(assert-true (instance? str (str "hello"))))
(defn test-integer? []
@ -171,10 +224,20 @@
(assert-true (integer? 0))
(assert-true (integer? 3))
(assert-true (integer? -3))
(assert-true (integer? (integer "-3")))
(assert-true (integer? (integer 3)))
(assert-false (integer? 4.2))
(assert-false (integer? None))
(assert-false (integer? "foo")))
(defn test-integer-char? []
"NATIVE: testing the integer-char? function"
(assert-true (integer-char? "1"))
(assert-true (integer-char? "-1"))
(assert-true (integer-char? (str (integer 300))))
(assert-false (integer-char? "foo"))
(assert-false (integer-char? None)))
(defn test-iterable []
"NATIVE: testing iterable? function"
;; should work for a string
@ -259,6 +322,15 @@
(assert-false (none? 0))
(assert-false (none? "")))
(defn test-nil? []
"NATIVE: testing for `is nil`"
(assert-true (nil? nil))
(assert-true (nil? None))
(setv f nil)
(assert-true (nil? f))
(assert-false (nil? 0))
(assert-false (nil? "")))
(defn test-nth []
"NATIVE: testing the nth function"
(assert-equal 2 (nth [1 2 4 7] 1))
@ -334,6 +406,13 @@
(assert-equal 2 (second [1 2]))
(assert-equal 3 (second [2 3 4])))
(defn test-some []
"NATIVE: testing the some function"
(assert-true (some even? [2 4 6]))
(assert-false (some even? [1 3 5]))
(assert-true (some even? [1 3 6]))
(assert-false (some even? [])))
(defn test-string? []
"NATIVE: testing string?"
(assert-true (string? "foo"))
@ -391,3 +470,11 @@
(assert-equal res [None None])
(setv res (list (take-while (fn [x] (not (none? x))) [1 2 3 4 None 5 6 None 7])))
(assert-equal res [1 2 3 4]))
(defn test-zipwith []
"NATIVE: testing the zipwith function"
(import operator)
(setv res (zipwith operator.add [1 2 3] [3 2 1]))
(assert-equal (list res) [4 4 4])
(setv res (zipwith operator.sub [3 7 9] [1 2 4]))
(assert-equal (list res) [2 5 5]))

View File

@ -41,6 +41,46 @@
(assert (= count 150)))
(defn test-nasty-for-nesting []
"NATIVE: test nesting for loops harder"
;; This test and feature is dedicated to @nedbat.
;; let's ensure empty iterating is an implicit do
(setv t 0)
(for [] (setv t 1))
(assert (= t 1))
;; OK. This first test will ensure that the else is hooked up to the
;; for when we break out of it.
(for [x (range 2)
y (range 2)]
(break)
(else (throw Exception)))
;; OK. This next test will ensure that the else is hooked up to the
;; "inner" iteration
(for [x (range 2)
y (range 2)]
(if (= y 1) (break))
(else (throw Exception)))
;; OK. This next test will ensure that the else is hooked up to the
;; "outer" iteration
(for [x (range 2)
y (range 2)]
(if (= x 1) (break))
(else (throw Exception)))
;; OK. This next test will ensure that we call the else branch exactly
;; once.
(setv flag 0)
(for [x (range 2)
y (range 2)]
(+ 1 1)
(else (setv flag (+ flag 2))))
(assert (= flag 2)))
(defn test-while-loop []
"NATIVE: test while loops?"
(setv count 5)
@ -86,9 +126,10 @@
(defn test-is []
"NATIVE: test is can deal with None"
(setv a null)
(assert (is a null))
(assert (is-not a "b")))
(setv a nil)
(assert (is a nil))
(assert (is-not a "b"))
(assert (none? a)))
(defn test-branching []
@ -129,7 +170,16 @@
(defn test-index []
"NATIVE: Test that dict access works"
(assert (= (get {"one" "two"} "one") "two"))
(assert (= (get [1 2 3 4 5] 1) 2)))
(assert (= (get [1 2 3 4 5] 1) 2))
(assert (= (get {"first" {"second" {"third" "level"}}}
"first" "second" "third")
"level"))
(assert (= (get ((fn [] {"first" {"second" {"third" "level"}}}))
"first" "second" "third")
"level"))
(assert (= (get {"first" {"second" {"third" "level"}}}
((fn [] "first")) "second" "third")
"level")))
(defn test-lambda []
@ -152,7 +202,39 @@
(assert (= (kwapply (kwtest) {"one" "two"}) {"one" "two"}))
(setv mydict {"one" "three"})
(assert (= (kwapply (kwtest) mydict) mydict))
(assert (= (kwapply (kwtest) ((fn [] {"one" "two"}))) {"one" "two"})))
(assert (= (kwapply (kwtest) ((fn [] {"one" "two"}))) {"one" "two"}))
(assert (= (kwapply
(kwapply
(kwapply
(kwapply
(kwapply (kwtest) {"x" 4})
mydict)
{"x" 8})
{"x" (- 3 2) "y" 2})
{"y" 5 "z" 3})
{"x" 1 "y" 5 "z" 3 "one" "three"})))
(defn test-apply []
"NATIVE: test working with args and functions"
(defn sumit [a b c] (+ a b c))
(assert (= (apply sumit [1] {"b" 2 "c" 3}) 6))
(assert (= (apply sumit [1 2 2]) 5))
(assert (= (apply sumit [] {"a" 1 "b" 1 "c" 2}) 4))
(assert (= (apply sumit ((fn [] [1 1])) {"c" 1}) 3))
(defn noargs [] [1 2 3])
(assert (= (apply noargs) [1 2 3])))
(defn test-apply-with-methods []
"NATIVE: test apply to call a method"
(setv str "foo {bar}")
(assert (= (apply .format [str] {"bar" "baz"})
(apply .format ["foo {0}" "baz"])
"foo baz"))
(setv lst ["a {0} {1} {foo} {bar}" "b" "c"])
(assert (= (apply .format lst {"foo" "d" "bar" "e"})
"a b c d e")))
(defn test-dotted []
@ -407,43 +489,43 @@
(defn test-context []
"NATIVE: test with"
(with [fd (open "README.md" "r")] (assert fd))
(with [(open "README.md" "r")] (do)))
(with [[fd (open "README.md" "r")]] (assert fd))
(with [[(open "README.md" "r")]] (do)))
(defn test-with-return []
"NATIVE: test that with returns stuff"
(defn read-file [filename]
(with [fd (open filename "r")] (.read fd)))
(with [[fd (open filename "r")]] (.read fd)))
(assert (!= 0 (len (read-file "README.md")))))
(defn test-for-doodle []
"NATIVE: test for-do"
(do (do (do (do (do (do (do (do (do (setv (, x y) (, 0 0)))))))))))
(foreach [- [1 2]]
(for [- [1 2]]
(do
(setv x (+ x 1))
(setv y (+ y 1))))
(assert (= y x 2)))
(defn test-foreach-else []
"NATIVE: test foreach else"
(defn test-for-else []
"NATIVE: test for else"
(let [[x 0]]
(foreach [a [1 2]]
(for* [a [1 2]]
(setv x (+ x a))
(else (setv x (+ x 50))))
(assert (= x 53)))
(let [[x 0]]
(foreach [a [1 2]]
(for* [a [1 2]]
(setv x (+ x a))
(else))
(assert (= x 3))))
(defn test-comprehensions []
(defn test-list-comprehensions []
"NATIVE: test list comprehensions"
(assert (= (list-comp (* x 2) (x (range 2))) [0 2]))
(assert (= (list-comp (* x 2) (x (range 4)) (% x 2)) [2 6]))
@ -454,6 +536,41 @@
(assert (= (list-comp j (j [1 2])) [1 2])))
(defn test-set-comprehensions []
"NATIVE: test set comprehensions"
(assert (instance? set (set-comp x [x (range 2)])))
(assert (= (set-comp (* x 2) (x (range 2))) (set [0 2])))
(assert (= (set-comp (* x 2) (x (range 4)) (% x 2)) (set [2 6])))
(assert (= (set-comp (* y 2) ((, x y) (.items {"1" 1 "2" 2})))
(set [2 4])))
(assert (= (set-comp (, x y) (x (range 2) y (range 2)))
(set [(, 0 0) (, 0 1) (, 1 0) (, 1 1)])))
(assert (= (set-comp j (j [1 2])) (set [1 2]))))
(defn test-dict-comprehensions []
"NATIVE: test dict comprehensions"
(assert (instance? dict (dict-comp x x [x (range 2)])))
(assert (= (dict-comp x (* x 2) (x (range 2))) {1 2 0 0}))
(assert (= (dict-comp x (* x 2) (x (range 4)) (% x 2)) {3 6 1 2}))
(assert (= (dict-comp x (* y 2) ((, x y) (.items {"1" 1 "2" 2})))
{"2" 4 "1" 2}))
(assert (= (dict-comp (, x y) (+ x y) (x (range 2) y (range 2)))
{(, 0 0) 0 (, 1 0) 1 (, 0 1) 1 (, 1 1) 2})))
(defn test-generator-expressions []
"NATIVE: test generator expressions"
(assert (not (instance? list (genexpr x [x (range 2)]))))
(assert (= (list (genexpr (* x 2) (x (range 2)))) [0 2]))
(assert (= (list (genexpr (* x 2) (x (range 4)) (% x 2))) [2 6]))
(assert (= (list (sorted (genexpr (* y 2) ((, x y) (.items {"1" 1 "2" 2})))))
[2 4]))
(assert (= (list (genexpr (, x y) (x (range 2) y (range 2))))
[(, 0 0) (, 0 1) (, 1 0) (, 1 1)]))
(assert (= (list (genexpr j (j [1 2]))) [1 2])))
(defn test-defn-order []
"NATIVE: test defn evaluation order"
(setv acc [])
@ -551,6 +668,12 @@
(assert (= -_- "what?"))))
(defn test-symbol-question-mark []
"NATIVE: test foo? -> is_foo behavior"
(let [[foo? "nachos"]]
(assert (= is_foo "nachos"))))
(defn test-and []
"NATIVE: test the and function"
(let [[and123 (and 1 2 3)]
@ -769,13 +892,99 @@
(defn test-continue-continuation []
"NATIVE: test checking if continue actually continues"
(setv y [])
(for [x (range 10)]
(if (!= x 5)
(continue))
(for [x (range 10)]
(if (!= x 5)
(continue))
(.append y x))
(assert (= y [5])))
(defn test-empty-list []
"Evaluate an empty list to a []"
(assert (= () [])))
(defn test-string []
(assert (string? (string "a")))
(assert (string? (string 1)))
(assert (= u"unicode" (string "unicode"))))
(defn test-del []
"NATIVE: Test the behavior of del"
(setv foo 42)
(assert (= foo 42))
(del foo)
(assert (= 'good
(try
(do foo 'bad)
(except [NameError] 'good))))
(setv test (list (range 5)))
(del (get test 4))
(assert (= test [0 1 2 3]))
(del (get test 2))
(assert (= test [0 1 3])))
(defn test-macroexpand []
"Test macroexpand on ->"
(assert (= (macroexpand '(-> (a b) (x y)))
'(x (a b) y)))
(assert (= (macroexpand '(-> (a b) (-> (c d) (e f))))
'(e (c (a b) d) f))))
(defn test-macroexpand-1 []
"Test macroexpand-1 on ->"
(assert (= (macroexpand-1 '(-> (a b) (-> (c d) (e f))))
'(-> (a b) (c d) (e f)))))
(defn test-calling-module-name []
"NATIVE: Test the calling-module-name function"
(assert (= (calling-module-name -1) "hy.core.language"))
(assert (= (calling-module-name 0) "tests.native_tests.language")))
(defn test-disassemble []
"NATIVE: Test the disassemble function"
(import sys)
(if-python2
(import [io [BytesIO :as StringIO]])
(import [io [StringIO]]))
(setv prev-stdout sys.stdout)
(setv sys.stdout (StringIO))
(disassemble '(do (leaky) (leaky) (macros)))
(setv stdout (.getvalue sys.stdout))
(setv sys.stdout prev-stdout)
(assert (in "leaky" stdout))
(assert (in "macros" stdout))
(setv sys.stdout (StringIO))
(disassemble '(do (leaky) (leaky) (macros)) true)
(setv stdout (.getvalue sys.stdout))
(setv sys.stdout prev-stdout)
(assert (= stdout "leaky()\nleaky()\nmacros()\n")))
(defn test-attribute-access []
"NATIVE: Test the attribute access DSL"
(defclass mycls [object])
(setv foo [(mycls) (mycls) (mycls)])
(assert (is (. foo) foo))
(assert (is (. foo [0]) (get foo 0)))
(assert (is (. foo [0] --class--) mycls))
(assert (is (. foo [1] --class--) mycls))
(assert (is (. foo [(+ 1 1)] --class--) mycls))
(assert (= (. foo [(+ 1 1)] --class-- --name-- [0]) "m"))
(assert (= (. foo [(+ 1 1)] --class-- --name-- [1]) "y"))
(setv bar (mycls))
(setv (. foo [1]) bar)
(assert (is bar (get foo 1)))
(setv (. foo [1] test) "hello")
(assert (= (getattr (. foo [1]) "test") "hello")))
(defn test-keyword-quoting []
"NATIVE: test keyword quoting magic"
(assert (= :foo "\ufdd0:foo"))
(assert (= `:foo "\ufdd0:foo")))

View File

@ -8,7 +8,9 @@
(setv test_mult (fn []
"NATIVE: Test multiplication."
(assert (= 4 (square 2)))))
(assert (= 4 (square 2)))
(assert (= 8 (* 8)))
(assert (= 1 (*)))))
(setv test_sub (fn []
@ -19,7 +21,9 @@
(setv test_add (fn []
"NATIVE: Test addition"
(assert (= 4 (+ 1 1 1 1)))))
(assert (= 4 (+ 1 1 1 1)))
(assert (= 8 (+ 8)))
(assert (= 0 (+)))))
(setv test_div (fn []
@ -132,3 +136,7 @@
(let [[x 1]]
(^= x 1)
(assert (= x 0))))
(defn overflow-int-to-long []
"NATIVE: test if int does not raise an overflow exception"
(assert (integer? (+ 1 1000000000000000000000000))))

View File

@ -31,9 +31,15 @@
(defmacro a-list [] [1 2])
(assert (= (a-list) [1 2]))
(defmacro a-tuple [&rest b] b)
(assert (= (a-tuple 1 2) [1 2]))
(defmacro a-dict [] {1 2})
(assert (= (a-dict) {1 2}))
(defmacro a-none [])
(assert (= (a-none) None))
; A macro calling a previously defined function
(eval-when-compile
(defn foo [x y]
@ -53,7 +59,7 @@
(defn test-midtree-yield-in-for []
"NATIVE: test yielding in a for with a return"
(defn kruft-in-for []
(for [i (range 5)]
(for* [i (range 5)]
(yield i))
(+ 1 2)))
@ -69,7 +75,7 @@
(defn test-multi-yield []
"NATIVE: testing multiple yields"
(defn multi-yield []
(for [i (range 3)]
(for* [i (range 3)]
(yield i))
(yield "a")
(yield "end"))
@ -91,11 +97,10 @@
(assert initialized)
(assert (test-initialized))
(defn test-yield-from []
"NATIVE: testing yield from"
(defn yield-from-test []
(for [i (range 3)]
(for* [i (range 3)]
(yield i))
(yield-from [1 2 3]))
(assert (= (list (yield-from-test)) [0 1 2 1 2 3])))
@ -104,3 +109,92 @@
(import sys)
(assert (= (get sys.version_info 0)
(if-python2 2 3))))
(defn test-gensym-in-macros []
(import ast)
(import [astor.codegen [to_source]])
(import [hy.importer [import_buffer_to_ast]])
(setv macro1 "(defmacro nif [expr pos zero neg]
(let [[g (gensym)]]
`(let [[~g ~expr]]
(cond [(pos? ~g) ~pos]
[(zero? ~g) ~zero]
[(neg? ~g) ~neg]))))
(print (nif (inc -1) 1 0 -1))
")
;; expand the macro twice, should use a different
;; gensym each time
(setv _ast1 (import_buffer_to_ast macro1 "foo"))
(setv _ast2 (import_buffer_to_ast macro1 "foo"))
(setv s1 (to_source _ast1))
(setv s2 (to_source _ast2))
;; and make sure there is something new that starts with :G_
(assert (in ":G_" s1))
(assert (in ":G_" s2))
;; but make sure the two don't match each other
(assert (not (= s1 s2))))
(defn test-with-gensym []
(import ast)
(import [astor.codegen [to_source]])
(import [hy.importer [import_buffer_to_ast]])
(setv macro1 "(defmacro nif [expr pos zero neg]
(with-gensyms [a]
`(let [[~a ~expr]]
(cond [(pos? ~a) ~pos]
[(zero? ~a) ~zero]
[(neg? ~a) ~neg]))))
(print (nif (inc -1) 1 0 -1))
")
;; expand the macro twice, should use a different
;; gensym each time
(setv _ast1 (import_buffer_to_ast macro1 "foo"))
(setv _ast2 (import_buffer_to_ast macro1 "foo"))
(setv s1 (to_source _ast1))
(setv s2 (to_source _ast2))
(assert (in ":a_" s1))
(assert (in ":a_" s2))
(assert (not (= s1 s2))))
(defn test-defmacro-g! []
(import ast)
(import [astor.codegen [to_source]])
(import [hy.importer [import_buffer_to_ast]])
(setv macro1 "(defmacro/g! nif [expr pos zero neg]
`(let [[~g!res ~expr]]
(cond [(pos? ~g!res) ~pos]
[(zero? ~g!res) ~zero]
[(neg? ~g!res) ~neg])))
(print (nif (inc -1) 1 0 -1))
")
;; expand the macro twice, should use a different
;; gensym each time
(setv _ast1 (import_buffer_to_ast macro1 "foo"))
(setv _ast2 (import_buffer_to_ast macro1 "foo"))
(setv s1 (to_source _ast1))
(setv s2 (to_source _ast2))
(assert (in ":res_" s1))
(assert (in ":res_" s2))
(assert (not (= s1 s2))))
(defn test-if-not []
(assert (= (if-not True :yes :no)
:no))
(assert (= (if-not False :yes :no)
:yes))
(assert (nil? (if-not True :yes)))
(assert (= (if-not False :yes)
:yes)))
(defn test-defn-alias []
(defn-alias [tda-main tda-a1 tda-a2] [] :bazinga)
(defun-alias [tda-main tda-a1 tda-a2] [] :bazinga)
(assert (= (tda-main) :bazinga))
(assert (= (tda-a1) :bazinga))
(assert (= (tda-a2) :bazinga))
(assert (= tda-main tda-a1 tda-a2)))

View File

@ -0,0 +1,36 @@
(defn test-reader-macro []
"Test a basic redaer macro"
(defreader ^ [expr]
expr)
(assert (= #^"works" "works")))
(defn test-reader-macro-expr []
"Test basic exprs like lists and arrays"
(defreader n [expr]
(get expr 1))
(assert (= #n[1 2] 2))
(assert (= #n(1 2) 2)))
(defn test-reader-macro-override []
"Test if we can override function symbols"
(defreader + [n]
(+ n 1))
(assert (= #+2 3)))
(defn test-reader-macros-macros []
"Test if defreader is actually a macro"
(defreader t [expr]
`(, ~@expr))
(def a #t[1 2 3])
(assert (= (type a) tuple))
(assert (= (, 1 2 3) a)))

View File

@ -4,5 +4,7 @@
(assert (= (unless false 1 2) 2))
(assert (= (unless false 1 3) 3))
(assert (= (unless true 2) null))
(assert (= (unless true 2) nil))
(assert (= (unless (!= 1 2) 42) null))
(assert (= (unless (!= 1 2) 42) nil))
(assert (= (unless (!= 2 2) 42) 42)))

View File

@ -5,4 +5,6 @@
(assert (= (when true 1 3) 3))
(assert (= (when false 2) null))
(assert (= (when (= 1 2) 42) null))
(assert (= (when false 2) nil))
(assert (= (when (= 1 2) 42) nil))
(assert (= (when (= 2 2) 42) 42)))

View File

@ -0,0 +1,44 @@
(defclass WithTest [object]
[(--init--
(fn [self val]
(setv self.val val)
None))
(--enter--
(fn [self]
self.val))
(--exit--
(fn [self type value traceback]
(setv self.val None)))])
(defn test-single-with []
"NATIVE: test a single with"
(with [[t (WithTest 1)]]
(assert (= t 1))))
(defn test-two-with []
"NATIVE: test two withs"
(with [[t1 (WithTest 1)]
[t2 (WithTest 2)]]
(assert (= t1 1))
(assert (= t2 2))))
(defn test-thrice-with []
"NATIVE: test three withs"
(with [[t1 (WithTest 1)]
[t2 (WithTest 2)]
[t3 (WithTest 3)]]
(assert (= t1 1))
(assert (= t2 2))
(assert (= t3 3))))
(defn test-quince-with []
"NATIVE: test four withs, one with no args"
(with [[t1 (WithTest 1)]
[t2 (WithTest 2)]
[t3 (WithTest 3)]
[(WithTest 4)]]
(assert (= t1 1))
(assert (= t2 2))
(assert (= t3 3))))

View File

@ -22,7 +22,8 @@
# DEALINGS IN THE SOFTWARE.
import os
import subprocess
import sys
from nose.plugins.skip import SkipTest
def run_cmd(cmd, stdin_data=None):
@ -40,8 +41,8 @@ def run_cmd(cmd, stdin_data=None):
# Read stdout and stderr otherwise if the PIPE buffer is full, we might
# wait for ever…
while p.poll() is None:
stdout += str(p.stdout.read())
stderr += str(p.stderr.read())
stdout += p.stdout.read().decode('utf-8')
stderr += p.stderr.read().decode('utf-8')
return p.returncode, stdout, stderr
@ -63,7 +64,7 @@ def test_bin_hy_cmd():
ret = run_cmd("hy -c \"(koan\"")
assert ret[0] == 1
assert "PrematureEndOfInput" in ret[1]
assert "Premature end of input" in ret[2]
def test_bin_hy_icmd():
@ -75,10 +76,12 @@ def test_bin_hy_icmd():
assert "figlet" in output
def test_bin_hy_file():
ret = run_cmd("hy eg/nonfree/halting-problem/halting.hy")
def test_bin_hy_icmd_and_spy():
ret = run_cmd("hy -i \"(+ [] [])\" --spy", "(+ 1 1)")
assert ret[0] == 0
assert "27" in ret[1]
output = ret[1]
assert "([] + [])" in output
def test_bin_hy_missing_file():
@ -122,20 +125,16 @@ def test_bin_hyc_missing_file():
def test_hy2py():
# XXX Astor doesn't seem to support Python3 :(
if sys.version_info[0] == 3:
return
# and running this script this way doesn't work on Windows
if os.name == "nt":
return
raise SkipTest("doesn't work on Windows")
i = 0
for dirpath, dirnames, filenames in os.walk("tests/native_tests"):
for f in filenames:
if f.endswith(".hy"):
i += 1
ret = run_cmd("bin/hy2py " + os.path.join(dirpath, f))
ret = run_cmd("bin/hy2py -s -a " + os.path.join(dirpath, f))
assert ret[0] == 0, f
assert len(ret[1]) > 1, f
assert len(ret[2]) == 0, f

15
tox.ini
View File

@ -5,8 +5,7 @@ commands = nosetests
deps =
nose
setuptools
-e
git+https://github.com/hylang/rply.git#egg=rply
rply
[testenv:pypy]
commands = nosetests
@ -14,8 +13,7 @@ deps =
astor
nose
setuptools
-e
git+https://github.com/hylang/rply.git#egg=rply
rply
[testenv:py27]
commands = nosetests
@ -23,8 +21,7 @@ deps =
astor
nose
setuptools
-e
git+https://github.com/hylang/rply.git#egg=rply
rply
[testenv:py26]
deps =
@ -33,12 +30,10 @@ deps =
setuptools
unittest2
importlib
-e
git+https://github.com/hylang/rply.git#egg=rply
rply
[testenv:flake8]
deps =
flake8
-e
git+https://github.com/hylang/rply.git#egg=rply
rply
commands = flake8 hy bin tests