commit
d388ee2fd5
11
.coveragerc
Normal file
11
.coveragerc
Normal 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
|
10
.travis.yml
10
.travis.yml
@ -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
25
AUTHORS
@ -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>
|
||||
|
11
Makefile
11
Makefile
@ -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
73
NEWS
@ -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
|
||||
|
15
README.md
15
README.md
@ -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)
|
||||
|
46
bin/hy2py
46
bin/hy2py
@ -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"))
|
||||
|
BIN
docs/_static/cuddles-transparent-small.png
vendored
Normal file
BIN
docs/_static/cuddles-transparent-small.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
BIN
docs/_static/cuddles-transparent.png
vendored
Normal file
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
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
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
127
docs/_static/hy-logo.svg
vendored
Normal 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 |
3
docs/_static/hy_logo-about.txt
vendored
3
docs/_static/hy_logo-about.txt
vendored
@ -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
|
||||
|
10
docs/conf.py
10
docs/conf.py
@ -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
197
docs/contrib/anaphoric.rst
Normal 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
12
docs/contrib/index.rst
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
Contrib Modules Index
|
||||
=====================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
anaphoric
|
||||
loop
|
||||
multi
|
56
docs/contrib/loop.rst
Normal file
56
docs/contrib/loop.rst
Normal 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
23
docs/contrib/multi.rst
Normal 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
15
docs/coreteam.rst
Normal 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>`_
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 expression’s first argument place it
|
||||
inserting each expression into the next expression’s first argument place, it
|
||||
appends it as the last argument. The following code demonstrates this:
|
||||
|
||||
.. code-block:: clj
|
||||
@ -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
73
docs/language/cli.rst
Normal 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!
|
@ -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]))
|
||||
[]
|
||||
|
||||
|
||||
|
@ -7,6 +7,8 @@ Contents:
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
cli
|
||||
api
|
||||
core
|
||||
readermacros
|
||||
internals
|
||||
|
@ -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.
|
||||
|
73
docs/language/readermacros.rst
Normal file
73
docs/language/readermacros.rst
Normal 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.
|
@ -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
|
||||
|
@ -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
27
eg/flask/meth_example.hy
Normal 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})
|
@ -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)))
|
@ -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)))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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] == "-":
|
||||
|
522
hy/compiler.py
522
hy/compiler.py
@ -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)
|
||||
|
||||
|
@ -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
108
hy/contrib/anaphoric.hy
Normal 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)))
|
50
hy/contrib/dispatch/__init__.py
Normal file
50
hy/contrib/dispatch/__init__.py
Normal 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
91
hy/contrib/loop.hy
Normal 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))))
|
@ -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
41
hy/contrib/multi.hy
Normal 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
51
hy/contrib/profile.hy
Normal 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
58
hy/contrib/walk.hy
Normal 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))
|
@ -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)
|
||||
|
@ -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])
|
||||
|
@ -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))
|
||||
|
117
hy/errors.py
117
hy/errors.py
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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+')
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
69
hy/macros.py
69
hy/macros.py
@ -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)
|
||||
|
@ -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
108
hy/models/cons.py
Normal 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
|
||||
)
|
@ -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()))
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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]))
|
||||
|
@ -20,4 +20,4 @@
|
||||
|
||||
|
||||
__appname__ = "hy"
|
||||
__version__ = "0.9.11"
|
||||
__version__ = "0.9.12"
|
||||
|
24
make.bat
24
make.bat
@ -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
|
||||
|
@ -1,5 +1,4 @@
|
||||
-r requirements.txt
|
||||
astor
|
||||
tox
|
||||
nose
|
||||
Sphinx
|
||||
|
@ -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
|
||||
|
37
scripts/update-coreteam.hy
Normal file
37
scripts/update-coreteam.hy
Normal 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")))
|
3
setup.py
3
setup.py
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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)")
|
||||
|
27
tests/compilers/test_error_reporting.py
Normal file
27
tests/compilers/test_error_reporting.py
Normal 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)")
|
@ -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
|
||||
|
@ -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
|
||||
|
11
tests/macros/test_reader_macros.py
Normal file
11
tests/macros/test_reader_macros.py
Normal 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
56
tests/models/test_cons.py
Normal 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
16
tests/models/test_dict.py
Normal 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]
|
@ -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([])
|
||||
|
63
tests/native_tests/cons.hy
Normal file
63
tests/native_tests/cons.hy
Normal 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))))
|
0
tests/native_tests/contrib/__init__.hy
Normal file
0
tests/native_tests/contrib/__init__.hy
Normal file
101
tests/native_tests/contrib/anaphoric.hy
Normal file
101
tests/native_tests/contrib/anaphoric.hy
Normal 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))
|
46
tests/native_tests/contrib/loop.hy
Normal file
46
tests/native_tests/contrib/loop.hy
Normal 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))))
|
54
tests/native_tests/contrib/meth.hy
Normal file
54
tests/native_tests/contrib/meth.hy
Normal 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))))))
|
57
tests/native_tests/contrib/multi.hy
Normal file
57
tests/native_tests/contrib/multi.hy
Normal 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")))
|
29
tests/native_tests/contrib/walk.hy
Normal file
29
tests/native_tests/contrib/walk.hy
Normal 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))))))))
|
@ -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]))
|
||||
|
@ -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")))
|
||||
|
@ -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))))
|
||||
|
@ -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)))
|
||||
|
36
tests/native_tests/reader_macros.hy
Normal file
36
tests/native_tests/reader_macros.hy
Normal 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)))
|
||||
|
||||
|
@ -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)))
|
||||
|
@ -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)))
|
||||
|
44
tests/native_tests/with_test.hy
Normal file
44
tests/native_tests/with_test.hy
Normal 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))))
|
@ -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
15
tox.ini
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user