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"
|
- "2.6"
|
||||||
# command to install dependencies
|
# command to install dependencies
|
||||||
install:
|
install:
|
||||||
- pip install -r requirements.txt --use-mirrors
|
- pip install -r requirements.txt
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install argparse importlib unittest2 astor --use-mirrors; fi
|
- pip install coveralls
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install astor --use-mirrors; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install argparse importlib unittest2 astor; fi
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then pip install astor --use-mirrors; 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
|
- python setup.py -q install
|
||||||
# # command to run tests
|
# # command to run tests
|
||||||
script: make travis
|
script: make travis
|
||||||
|
after_success: coveralls
|
||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
- paultag@gmail.com
|
- paultag@gmail.com
|
||||||
|
25
AUTHORS
25
AUTHORS
@ -18,3 +18,28 @@
|
|||||||
* Ralph Möritz <ralph.moeritz@outlook.com>
|
* Ralph Möritz <ralph.moeritz@outlook.com>
|
||||||
* Josh McLaughlin <josh@phear.cc>
|
* Josh McLaughlin <josh@phear.cc>
|
||||||
* Berker Peksag <berker.peksag@gmail.com>
|
* 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 " - tox"
|
||||||
@echo " - d"
|
@echo " - d"
|
||||||
@echo " - r"
|
@echo " - r"
|
||||||
|
@echo " - clean"
|
||||||
@echo ""
|
@echo ""
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
@ -53,11 +54,19 @@ diff:
|
|||||||
r: d tox diff
|
r: d tox diff
|
||||||
|
|
||||||
travis:
|
travis:
|
||||||
nosetests -s
|
nosetests -s --with-coverage --cover-package hy
|
||||||
ifeq (PyPy,$(findstring PyPy,$(shell python -V 2>&1 | tail -1)))
|
ifeq (PyPy,$(findstring PyPy,$(shell python -V 2>&1 | tail -1)))
|
||||||
@echo "skipping flake8 on pypy"
|
@echo "skipping flake8 on pypy"
|
||||||
else
|
else
|
||||||
flake8 hy bin tests
|
flake8 hy bin tests
|
||||||
endif
|
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
|
.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
|
Changes from Hy 0.9.10
|
||||||
|
|
||||||
* Many thanks to Guillermo Vayá (Willyfrog) for preparing this release's
|
* Many thanks to Guillermo Vayá (Willyfrog) for preparing this release's
|
||||||
|
15
README.md
15
README.md
@ -1,14 +1,14 @@
|
|||||||
Hy
|
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)
|
[![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)
|
[![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)
|
[![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
|
Hylarious Hacks
|
||||||
---------------
|
---------------
|
||||||
@ -19,6 +19,7 @@ Hylarious Hacks
|
|||||||
|
|
||||||
[Hy IRC bot](https://github.com/hylang/hygdrop)
|
[Hy IRC bot](https://github.com/hylang/hygdrop)
|
||||||
|
|
||||||
|
[miniKanren in Hy](https://github.com/algernon/adderall)
|
||||||
|
|
||||||
OK, so, why?
|
OK, so, why?
|
||||||
------------
|
------------
|
||||||
@ -30,7 +31,7 @@ Why?
|
|||||||
|
|
||||||
Well, I wrote Hy to help people realize one thing about Python:
|
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.
|
Oh, and lisps are neat.
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ Project
|
|||||||
-------
|
-------
|
||||||
|
|
||||||
* Code: https://github.com/hylang/hy
|
* Code: https://github.com/hylang/hy
|
||||||
* Docs: http://hy.rtfd.org/
|
* Docs: http://hylang.org/
|
||||||
* Quickstart: http://hy.rtfd.org/quickstart
|
* 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)
|
* Bug reports: We have no bugs! Your bugs are your own! (https://github.com/hylang/hy/issues)
|
||||||
* License: MIT (Expat)
|
* License: MIT (Expat)
|
||||||
|
46
bin/hy2py
46
bin/hy2py
@ -1,25 +1,39 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from __future__ import print_function
|
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 argparse
|
||||||
import_file_to_hst)
|
import sys
|
||||||
|
|
||||||
import astor.codegen
|
import astor.codegen
|
||||||
import sys
|
|
||||||
import ast
|
|
||||||
|
|
||||||
module_name = "<STDIN>"
|
module_name = "<STDIN>"
|
||||||
|
|
||||||
hst = import_file_to_hst(sys.argv[1])
|
parser = argparse.ArgumentParser(
|
||||||
print(hst)
|
prog="hy2py",
|
||||||
print("")
|
usage="%(prog)s [options] FILE",
|
||||||
print("")
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
_ast = import_file_to_ast(sys.argv[1], module_name)
|
parser.add_argument("--with-source", "-s", action="store_true",
|
||||||
print("")
|
help="Show the parsed source structure")
|
||||||
print("")
|
parser.add_argument("--with-ast", "-a", action="store_true",
|
||||||
print(ast.dump(_ast))
|
help="Show the generated AST")
|
||||||
print("")
|
parser.add_argument("--without-python", "-np", action="store_true",
|
||||||
print("")
|
help="Do not show the python code generated from the AST")
|
||||||
print(astor.codegen.to_source(_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
|
You should have received a copy of the CC0 Public Domain
|
||||||
Dedication along with this software. If not, see
|
Dedication along with this software. If not, see
|
||||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
<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
|
# All configuration values have a default; values that are commented out
|
||||||
# serve to show the default.
|
# serve to show the default.
|
||||||
|
|
||||||
import sys, os
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
sys.path.append(os.path.abspath(".."))
|
sys.path.append(os.path.abspath(".."))
|
||||||
|
|
||||||
import hy
|
import hy
|
||||||
@ -28,7 +30,7 @@ import hy
|
|||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# 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.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
@ -44,7 +46,7 @@ master_doc = 'index'
|
|||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'hy'
|
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
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |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
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# 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.
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
#default_role = None
|
#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!
|
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!
|
Document!
|
||||||
=========
|
=========
|
||||||
@ -83,3 +87,26 @@ To build the docs in HTML::
|
|||||||
$ make html
|
$ make html
|
||||||
|
|
||||||
Write docs---docs are good! Even this doc!
|
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!
|
Welcome to Hy's documentation!
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
|
.. image:: _static/hy-logo-small.png
|
||||||
|
:alt: Hy
|
||||||
|
|
||||||
.. image:: _static/hy_logo-smaller.png
|
|
||||||
:alt: Hy logo
|
|
||||||
:align: left
|
: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.
|
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
|
Documentation Index
|
||||||
===================
|
===================
|
||||||
@ -47,5 +32,6 @@ Contents:
|
|||||||
|
|
||||||
quickstart
|
quickstart
|
||||||
tutorial
|
tutorial
|
||||||
hacking
|
|
||||||
language/index
|
language/index
|
||||||
|
contrib/index
|
||||||
|
hacking
|
||||||
|
@ -28,16 +28,49 @@ languages.
|
|||||||
and `i♥u` will become `hy_iu_t0x`.
|
and `i♥u` will become `hy_iu_t0x`.
|
||||||
|
|
||||||
* Symbols that contain dashes will have them replaced with underscores. For
|
* 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
|
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
|
correct Python AST. The following are "special" forms, which may have
|
||||||
behavior that's slightly unexpected in some situations.
|
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
|
`->>` 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:
|
appends it as the last argument. The following code demonstrates this:
|
||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
@ -66,6 +99,37 @@ appends it as the last argument. The following code demonstrates this:
|
|||||||
5 10
|
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
|
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
|
;; assuming that (side-effect1) and (side-effect2) are functions and
|
||||||
;; collection is a list of numerical values
|
;; collection is a list of numerical values
|
||||||
|
|
||||||
(for (x collection) (do
|
(for [x collection]
|
||||||
(side-effect1 x)
|
(do
|
||||||
(if (% x 2)
|
(side-effect1 x)
|
||||||
(continue))
|
(if (% x 2)
|
||||||
(side-effect2 x)))
|
(continue))
|
||||||
|
(side-effect2 x)))
|
||||||
|
|
||||||
|
|
||||||
do / progn
|
do / progn
|
||||||
@ -218,7 +283,7 @@ do / progn
|
|||||||
the `do` and `progn` forms are used to evaluate each of their arguments and
|
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
|
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
|
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:
|
Some example usage:
|
||||||
|
|
||||||
@ -287,6 +352,8 @@ below:
|
|||||||
Meow
|
Meow
|
||||||
|
|
||||||
|
|
||||||
|
.. _defn:
|
||||||
|
|
||||||
defn / defun
|
defn / defun
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@ -357,13 +424,38 @@ Parameters may have following keywords in front of them:
|
|||||||
=> (zig-zag-sum 1 2 3 4 5 6)
|
=> (zig-zag-sum 1 2 3 4 5 6)
|
||||||
-3
|
-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
|
||||||
--------
|
--------
|
||||||
|
|
||||||
`defmacro` is used to define macros. The general format is
|
`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
|
code, allowing the user to write code in infix notation, where operator is in
|
||||||
between the operands.
|
between the operands.
|
||||||
|
|
||||||
@ -378,6 +470,102 @@ between the operands.
|
|||||||
=> (infix (1 + 1))
|
=> (infix (1 + 1))
|
||||||
2
|
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
|
eval
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -408,52 +596,38 @@ first / car
|
|||||||
|
|
||||||
|
|
||||||
for
|
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.
|
`for` 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
|
The results of each call are discarded and the for expression returns
|
||||||
collection and calls side-effect to each element in the collection:
|
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
|
.. code-block:: clj
|
||||||
|
|
||||||
;; assuming that (side-effect) is a function that takes a single parameter
|
;; 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
|
;; for can have an optional else block
|
||||||
(foreach [element collection] (side-effect element)
|
(for [element collection] (side-effect element)
|
||||||
(else (side-effect-2)))
|
(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.
|
normally. If the execution is halted with `break`, the `else` does not execute.
|
||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
=> (foreach [element [1 2 3]] (if (< element 3)
|
=> (for [element [1 2 3]] (if (< element 3)
|
||||||
... (print element)
|
... (print element)
|
||||||
... (break))
|
... (break))
|
||||||
... (else (print "loop finished")))
|
... (else (print "loop finished")))
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
|
|
||||||
=> (foreach [element [1 2 3]] (if (< element 4)
|
=> (for [element [1 2 3]] (if (< element 4)
|
||||||
... (print element)
|
... (print element)
|
||||||
... (break))
|
... (break))
|
||||||
... (else (print "loop finished")))
|
... (else (print "loop finished")))
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
@ -461,6 +635,28 @@ normally. If the execution is halted with `break`, the `else` does not execute.
|
|||||||
loop finished
|
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
|
get
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -481,8 +677,8 @@ Example usages:
|
|||||||
.. note:: `get` raises a KeyError if a dictionary is queried for a non-existing
|
.. note:: `get` raises a KeyError if a dictionary is queried for a non-existing
|
||||||
key.
|
key.
|
||||||
|
|
||||||
.. note:: `get` raises an IndexError if a list is queried for an index that is
|
.. note:: `get` raises an IndexError if a list or a tuple is queried for an index
|
||||||
out of bounds.
|
that is out of bounds.
|
||||||
|
|
||||||
|
|
||||||
global
|
global
|
||||||
@ -508,13 +704,16 @@ would thrown a `NameError`.
|
|||||||
(set-a 5)
|
(set-a 5)
|
||||||
(print-a)
|
(print-a)
|
||||||
|
|
||||||
if
|
if / if-not
|
||||||
--
|
-----------
|
||||||
|
|
||||||
the `if` form is used to conditionally select code to be executed. It has to
|
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
|
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
|
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:
|
Example usage:
|
||||||
|
|
||||||
@ -524,6 +723,10 @@ Example usage:
|
|||||||
(print "lets go shopping")
|
(print "lets go shopping")
|
||||||
(print "lets go and work"))
|
(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
|
Truth values of Python objects are respected. Values `None`, `False`, zero of
|
||||||
any numeric type, empty sequence and empty dictionary are considered `False`.
|
any numeric type, empty sequence and empty dictionary are considered `False`.
|
||||||
Everything else is considered `True`.
|
Everything else is considered `True`.
|
||||||
@ -559,6 +762,9 @@ of import you can use.
|
|||||||
[os.path [exists isdir isfile]]
|
[os.path [exists isdir isfile]]
|
||||||
[sys :as systest])
|
[sys :as systest])
|
||||||
|
|
||||||
|
;; Import all module functions into current namespace
|
||||||
|
(import [sys [*]])
|
||||||
|
|
||||||
|
|
||||||
kwapply
|
kwapply
|
||||||
-------
|
-------
|
||||||
@ -602,12 +808,28 @@ function is defined and passed to another function for filtering output.
|
|||||||
... {:name "Dave" :age 5}])
|
... {:name "Dave" :age 5}])
|
||||||
|
|
||||||
=> (defn display-people [people filter]
|
=> (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)))
|
=> (display-people people (fn [person] (< (:age person) 25)))
|
||||||
Alice
|
Alice
|
||||||
Dave
|
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
|
let
|
||||||
---
|
---
|
||||||
@ -736,6 +958,7 @@ using the backquote (`) symbol.
|
|||||||
|
|
||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
;; let `qux' be a variable with value (bar baz)
|
;; let `qux' be a variable with value (bar baz)
|
||||||
`(foo ~qux)
|
`(foo ~qux)
|
||||||
; equivalent to '(foo (bar baz))
|
; equivalent to '(foo (bar baz))
|
||||||
@ -751,6 +974,7 @@ be alternatively written using the (') symbol
|
|||||||
|
|
||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
=> (setv x '(print "Hello World"))
|
=> (setv x '(print "Hello World"))
|
||||||
; variable x is set to expression & not evaluated
|
; variable x is set to expression & not evaluated
|
||||||
=> x
|
=> x
|
||||||
@ -758,6 +982,7 @@ be alternatively written using the (') symbol
|
|||||||
=> (eval x)
|
=> (eval x)
|
||||||
Hello World
|
Hello World
|
||||||
|
|
||||||
|
|
||||||
require
|
require
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@ -877,6 +1102,42 @@ given conditional is False. The following shows how the macro expands into code.
|
|||||||
None
|
None
|
||||||
(do statement))
|
(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
|
when
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -913,16 +1174,18 @@ context to an argument or ignore it completely, as shown below:
|
|||||||
|
|
||||||
.. code-block:: clj
|
.. 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
|
The following example will open file `NEWS` and print its content on screen. The
|
||||||
file is automatically closed after it has been processed.
|
file is automatically closed after it has been processed.
|
||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
(with [f (open "NEWS")] (print (.read f)))
|
(with [[f (open "NEWS")]] (print (.read f)))
|
||||||
|
|
||||||
|
|
||||||
with-decorator
|
with-decorator
|
||||||
@ -947,6 +1210,35 @@ values that are incremented by 1. When decorated `addition` is called with value
|
|||||||
4
|
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
|
yield
|
||||||
-----
|
-----
|
||||||
|
|
||||||
@ -954,13 +1246,13 @@ yield
|
|||||||
The generator is iterable and therefore can be used in loops, list
|
The generator is iterable and therefore can be used in loops, list
|
||||||
comprehensions and other similar constructs.
|
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.
|
infinite series without consuming infinite amount of memory.
|
||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
=> (defn multiply [bases coefficients]
|
=> (defn multiply [bases coefficients]
|
||||||
... (foreach [(, base coefficient) (zip bases coefficients)]
|
... (for [[(, base coefficient) (zip bases coefficients)]]
|
||||||
... (yield (* base coefficient))))
|
... (yield (* base coefficient))))
|
||||||
|
|
||||||
=> (multiply (range 5) (range 5))
|
=> (multiply (range 5) (range 5))
|
||||||
@ -974,3 +1266,24 @@ infinite series without consuming infinite amount of memory.
|
|||||||
... (while True (yield (.randint random low high))))
|
... (while True (yield (.randint random low high))))
|
||||||
=> (list-comp x [x (take 15 (random-numbers 1 50))])])
|
=> (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]
|
[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
|
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-fn:
|
||||||
|
|
||||||
dec
|
dec
|
||||||
@ -29,6 +94,29 @@ Raises ``TypeError`` if ``(not (numeric? x))``.
|
|||||||
11.3
|
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:
|
.. _emtpy?-fn:
|
||||||
|
|
||||||
empty?
|
empty?
|
||||||
@ -50,6 +138,32 @@ Return True if ``coll`` is empty, i.e. ``(= 0 (len coll))``.
|
|||||||
False
|
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?-fn:
|
||||||
|
|
||||||
float?
|
float?
|
||||||
@ -91,6 +205,24 @@ Raises ``TypeError`` if ``(not (numeric? x))``.
|
|||||||
True
|
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-fn:
|
||||||
|
|
||||||
inc
|
inc
|
||||||
@ -220,6 +352,63 @@ Contrast with :ref:`iterable?-fn`.
|
|||||||
=> (iterator? (iter {:a 1 :b 2 :c 3}))
|
=> (iterator? (iter {:a 1 :b 2 :c 3}))
|
||||||
True
|
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?-fn:
|
||||||
|
|
||||||
neg?
|
neg?
|
||||||
@ -242,6 +431,36 @@ Raises ``TypeError`` if ``(not (numeric? x))``.
|
|||||||
=> (neg? 0)
|
=> (neg? 0)
|
||||||
False
|
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?-fn:
|
||||||
|
|
||||||
none?
|
none?
|
||||||
@ -377,6 +596,32 @@ Return the second member of ``coll``. Equivalent to
|
|||||||
1
|
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?-fn:
|
||||||
|
|
||||||
string?
|
string?
|
||||||
@ -397,7 +642,7 @@ Return True if x is a string.
|
|||||||
.. _zero?-fn:
|
.. _zero?-fn:
|
||||||
|
|
||||||
zero?
|
zero?
|
||||||
----
|
-----
|
||||||
|
|
||||||
Usage: ``(zero? x)``
|
Usage: ``(zero? x)``
|
||||||
|
|
||||||
@ -575,6 +820,26 @@ See also :ref:`remove-fn`.
|
|||||||
=> (list (filter even? [1 2 3 -4 5 -7]))
|
=> (list (filter even? [1 2 3 -4 5 -7]))
|
||||||
[2, -4]
|
[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:
|
.. _iterate-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]))
|
=> (list (take-while neg? [ 1 2 3 -4 5]))
|
||||||
[]
|
[]
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@ Contents:
|
|||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 3
|
:maxdepth: 3
|
||||||
|
|
||||||
|
cli
|
||||||
api
|
api
|
||||||
core
|
core
|
||||||
|
readermacros
|
||||||
internals
|
internals
|
||||||
|
@ -2,26 +2,469 @@
|
|||||||
Internal Hy Documentation
|
Internal Hy Documentation
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
.. info::
|
.. note:: These bits are mostly useful for folks who hack on Hy itself,
|
||||||
These bits are for folks who hack on Hy it's self, mostly!
|
but can also be used for those delving deeper in macro programming.
|
||||||
|
|
||||||
|
.. _models:
|
||||||
|
|
||||||
Hy Models
|
Hy Models
|
||||||
=========
|
=========
|
||||||
|
|
||||||
.. TODO::
|
Introduction to Hy models
|
||||||
Write this.
|
-------------------------
|
||||||
|
|
||||||
|
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
|
Hy Macros
|
||||||
=========
|
=========
|
||||||
|
|
||||||
.. TODO::
|
.. _using-gensym:
|
||||||
Write this.
|
|
||||||
|
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
|
Hy Compiler Builtins
|
||||||
====================
|
====================
|
||||||
|
|
||||||
.. TODO::
|
.. todo::
|
||||||
Write this.
|
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
|
Quickstart
|
||||||
==========
|
==========
|
||||||
|
|
||||||
.. image:: _static/cuddles.png
|
.. image:: _static/cuddles-transparent-small.png
|
||||||
:alt: Karen Rustard's Cuddles
|
: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>`_
|
<https://pypi.python.org/pypi/virtualenv>`_
|
||||||
2. activate your Python virtual environment
|
2. Activate your Virtual Python Environment
|
||||||
3. ``pip install hy``
|
3. Install `hy from PyPI <https://pypi.python.org/pypi/hy>`_ with ``pip install hy``
|
||||||
4. start a REPL with ``hy``
|
4. Start a REPL with ``hy``
|
||||||
5. type stuff in the REPL::
|
5. Type stuff in the REPL::
|
||||||
|
|
||||||
=> (print "Hy!")
|
=> (print "Hy!")
|
||||||
Hy!
|
Hy!
|
||||||
@ -26,20 +25,19 @@ HOW TO GET HY REAL FAST:
|
|||||||
|
|
||||||
etc
|
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.
|
OMG! That's amazing! I want to write a hy program.
|
||||||
|
|
||||||
7. open up an elite programming editor
|
7. Open up an elite programming editor and type::
|
||||||
8. 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``
|
8. Save as ``awesome.hy``
|
||||||
10. run::
|
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
|
10. Take a deep breath so as to not hyperventilate
|
||||||
12. smile villainously and sneak off to your hydeaway and do
|
11. Smile villainously and sneak off to your hydeaway and do
|
||||||
unspeakable things
|
unspeakable things
|
||||||
|
@ -276,7 +276,7 @@ You might notice above that if you have code like:
|
|||||||
(body-if-true)
|
(body-if-true)
|
||||||
(body-if-false))
|
(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?
|
body of one of these?
|
||||||
|
|
||||||
You can do the following:
|
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 "and why not, let's keep talking about how true it is!))
|
||||||
(print "this one's still simply just false"))
|
(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"
|
familiar with other lisps, this is the equivalent of "progn"
|
||||||
elsewhere.
|
elsewhere.
|
||||||
|
|
||||||
@ -311,8 +311,8 @@ The equivalent in hy would be:
|
|||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
(for (i (range 10))
|
(for [i (range 10)]
|
||||||
(print (+ "'i' is now at " (str i))))
|
(print (+ "'i' is now at " (str i))))
|
||||||
|
|
||||||
|
|
||||||
You can also import and make use of various python libraries. For
|
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
|
.. code-block:: clj
|
||||||
|
|
||||||
(with [f (file "/tmp/data.in")]
|
(with [[f (open "/tmp/data.in")]]
|
||||||
(print (.read f)))
|
(print (.read f)))
|
||||||
|
|
||||||
which is equivalent to::
|
which is equivalent to::
|
||||||
|
|
||||||
with file("/tmp/data.in") as f:
|
with open("/tmp/data.in") as f:
|
||||||
print f.read()
|
print f.read()
|
||||||
|
|
||||||
And yes, we do have lisp comprehensions! In Python you might do::
|
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
|
(list-comp
|
||||||
(, x y)
|
(, x y)
|
||||||
(x (range 9)
|
(x (range 8)
|
||||||
y "ABCDEFGH"))
|
y "ABCDEFGH"))
|
||||||
|
|
||||||
; [(0, 'A'), (0, 'B'), (0, 'C'), (0, 'D'), (0, 'E'), (0, 'F'), (0, 'G'), (0, 'H'),
|
; [(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'),
|
; (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'),
|
; (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'),
|
; (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'),
|
; (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')]
|
|
||||||
|
|
||||||
|
|
||||||
Python has support for various fancy argument and keyword arguments.
|
Python has support for various fancy argument and keyword arguments.
|
||||||
@ -406,7 +405,7 @@ The same thing in Hy::
|
|||||||
...
|
...
|
||||||
[3, 2, 1, 4]
|
[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
|
There's also a dictionary-style keyword arguments construction that
|
||||||
looks like:
|
looks like:
|
||||||
@ -436,12 +435,18 @@ The Hy equivalent:
|
|||||||
Finally, of course we need classes! In python we might have a class
|
Finally, of course we need classes! In python we might have a class
|
||||||
like::
|
like::
|
||||||
|
|
||||||
class FooBar (object):
|
class FooBar(object):
|
||||||
def __init__(self, x):
|
"""
|
||||||
self.x = x
|
Yet Another Example Class
|
||||||
|
"""
|
||||||
|
def __init__(self, x):
|
||||||
|
self.x = x
|
||||||
|
|
||||||
def get_x(self):
|
def get_x(self):
|
||||||
return self.x
|
"""
|
||||||
|
Return our copy of x
|
||||||
|
"""
|
||||||
|
return self.x
|
||||||
|
|
||||||
|
|
||||||
In Hy:
|
In Hy:
|
||||||
@ -449,6 +454,7 @@ In Hy:
|
|||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
(defclass FooBar [object]
|
(defclass FooBar [object]
|
||||||
|
"Yet Another Example Class"
|
||||||
[[--init--
|
[[--init--
|
||||||
(fn [self x]
|
(fn [self x]
|
||||||
(setv self.x x)
|
(setv self.x x)
|
||||||
@ -458,6 +464,7 @@ In Hy:
|
|||||||
|
|
||||||
[get-x
|
[get-x
|
||||||
(fn [self]
|
(fn [self]
|
||||||
|
"Return our copy of x"
|
||||||
self.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
|
(with-as (ThreadPoolExecutor 10) executor
|
||||||
(setv jobs (list-comp (.submit executor task-to-do) (x (range 0 10))))
|
(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)))
|
(.result future)))
|
||||||
|
@ -32,6 +32,7 @@ from hy.models.symbol import HySymbol # NOQA
|
|||||||
from hy.models.float import HyFloat # NOQA
|
from hy.models.float import HyFloat # NOQA
|
||||||
from hy.models.dict import HyDict # NOQA
|
from hy.models.dict import HyDict # NOQA
|
||||||
from hy.models.list import HyList # NOQA
|
from hy.models.list import HyList # NOQA
|
||||||
|
from hy.models.cons import HyCons # NOQA
|
||||||
|
|
||||||
|
|
||||||
import hy.importer # NOQA
|
import hy.importer # NOQA
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
|
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
|
||||||
# Copyright (c) 2013 Julien Danjou <julien@danjou.info>
|
# 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
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
# copy of this software and associated documentation files (the "Software"),
|
# copy of this software and associated documentation files (the "Software"),
|
||||||
@ -23,9 +24,24 @@ try:
|
|||||||
import __builtin__ as builtins
|
import __builtin__ as builtins
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import builtins # NOQA
|
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
|
import sys
|
||||||
|
|
||||||
|
PY27 = sys.version_info >= (2, 7)
|
||||||
PY3 = sys.version_info[0] >= 3
|
PY3 = sys.version_info[0] >= 3
|
||||||
|
PY33 = sys.version_info >= (3, 3)
|
||||||
|
PY34 = sys.version_info >= (3, 4)
|
||||||
|
|
||||||
if PY3:
|
if PY3:
|
||||||
str_type = str
|
str_type = str
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
# Copyright (c) 2013 Konrad Hinsen <konrad.hinsen@fastmail.net>
|
# Copyright (c) 2013 Konrad Hinsen <konrad.hinsen@fastmail.net>
|
||||||
# Copyright (c) 2013 Thom Neale <twneale@gmail.com>
|
# Copyright (c) 2013 Thom Neale <twneale@gmail.com>
|
||||||
# Copyright (c) 2013 Will Kahn-Greene <willg@bluesock.org>
|
# 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
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
# copy of this software and associated documentation files (the "Software"),
|
# copy of this software and associated documentation files (the "Software"),
|
||||||
@ -32,7 +33,7 @@ import sys
|
|||||||
import hy
|
import hy
|
||||||
|
|
||||||
from hy.lex import LexException, PrematureEndOfInput, tokenize
|
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.importer import ast_compile, import_buffer_to_module
|
||||||
from hy.completer import completion
|
from hy.completer import completion
|
||||||
|
|
||||||
@ -73,17 +74,22 @@ def print_python_code(_ast):
|
|||||||
|
|
||||||
|
|
||||||
class HyREPL(code.InteractiveConsole):
|
class HyREPL(code.InteractiveConsole):
|
||||||
def __init__(self, spy=False):
|
def __init__(self, spy=False, locals=None, filename="<input>"):
|
||||||
self.spy = spy
|
self.spy = spy
|
||||||
code.InteractiveConsole.__init__(self)
|
code.InteractiveConsole.__init__(self, locals=locals,
|
||||||
|
filename=filename)
|
||||||
|
|
||||||
def runsource(self, source, filename='<input>', symbol='single'):
|
def runsource(self, source, filename='<input>', symbol='single'):
|
||||||
|
global SIMPLE_TRACEBACKS
|
||||||
try:
|
try:
|
||||||
tokens = tokenize(source)
|
tokens = tokenize(source)
|
||||||
except PrematureEndOfInput:
|
except PrematureEndOfInput:
|
||||||
return True
|
return True
|
||||||
except LexException:
|
except LexException as e:
|
||||||
self.showsyntaxerror(filename)
|
if e.source is None:
|
||||||
|
e.source = source
|
||||||
|
e.filename = filename
|
||||||
|
sys.stderr.write(str(e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -91,6 +97,15 @@ class HyREPL(code.InteractiveConsole):
|
|||||||
if self.spy:
|
if self.spy:
|
||||||
print_python_code(_ast)
|
print_python_code(_ast)
|
||||||
code = ast_compile(_ast, filename, symbol)
|
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:
|
except Exception:
|
||||||
self.showtraceback()
|
self.showtraceback()
|
||||||
return False
|
return False
|
||||||
@ -153,22 +168,34 @@ def ideas_macro():
|
|||||||
require("hy.cmdline", "__console__")
|
require("hy.cmdline", "__console__")
|
||||||
require("hy.cmdline", "__main__")
|
require("hy.cmdline", "__main__")
|
||||||
|
|
||||||
|
SIMPLE_TRACEBACKS = True
|
||||||
|
|
||||||
|
|
||||||
def run_command(source):
|
def run_command(source):
|
||||||
try:
|
try:
|
||||||
import_buffer_to_module("__main__", source)
|
import_buffer_to_module("__main__", source)
|
||||||
except LexException as exc:
|
except (HyTypeError, LexException) as e:
|
||||||
# TODO: This would be better if we had line, col info.
|
if SIMPLE_TRACEBACKS:
|
||||||
print(source)
|
sys.stderr.write(str(e))
|
||||||
print(repr(exc))
|
return 1
|
||||||
return 1
|
raise
|
||||||
|
except Exception:
|
||||||
|
raise
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def run_file(filename):
|
def run_file(filename):
|
||||||
from hy.importer import import_file_to_module
|
from hy.importer import import_file_to_module
|
||||||
import_file_to_module("__main__", filename)
|
try:
|
||||||
return 0 # right?
|
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):
|
def run_repl(hr=None, spy=False):
|
||||||
@ -187,8 +214,8 @@ def run_repl(hr=None, spy=False):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def run_icommand(source):
|
def run_icommand(source, spy=False):
|
||||||
hr = HyREPL()
|
hr = HyREPL(spy)
|
||||||
hr.runsource(source, filename='<input>', symbol='single')
|
hr.runsource(source, filename='<input>', symbol='single')
|
||||||
return run_repl(hr)
|
return run_repl(hr)
|
||||||
|
|
||||||
@ -217,6 +244,9 @@ def cmdline_handler(scriptname, argv):
|
|||||||
|
|
||||||
parser.add_argument("-v", action="version", version=VERSION)
|
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.
|
# this will contain the script/program name and any arguments for it.
|
||||||
parser.add_argument('args', nargs=argparse.REMAINDER,
|
parser.add_argument('args', nargs=argparse.REMAINDER,
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
@ -227,11 +257,12 @@ def cmdline_handler(scriptname, argv):
|
|||||||
|
|
||||||
options = parser.parse_args(argv[1:])
|
options = parser.parse_args(argv[1:])
|
||||||
|
|
||||||
|
if options.show_tracebacks:
|
||||||
|
global SIMPLE_TRACEBACKS
|
||||||
|
SIMPLE_TRACEBACKS = False
|
||||||
|
|
||||||
# reset sys.argv like Python
|
# reset sys.argv like Python
|
||||||
if options.args and len(options.args) > 0:
|
sys.argv = options.args or [""]
|
||||||
sys.argv = options.args
|
|
||||||
else: # Python default
|
|
||||||
sys.argv = ['']
|
|
||||||
|
|
||||||
if options.command:
|
if options.command:
|
||||||
# User did "hy -c ..."
|
# User did "hy -c ..."
|
||||||
@ -239,7 +270,7 @@ def cmdline_handler(scriptname, argv):
|
|||||||
|
|
||||||
if options.icommand:
|
if options.icommand:
|
||||||
# User did "hy -i ..."
|
# User did "hy -i ..."
|
||||||
return run_icommand(options.icommand)
|
return run_icommand(options.icommand, spy=options.spy)
|
||||||
|
|
||||||
if options.args:
|
if options.args:
|
||||||
if options.args[0] == "-":
|
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 Julien Danjou <julien@danjou.info>
|
||||||
# Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
|
# Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
|
||||||
# Copyright (c) 2013 James King <james@agentultra.com>
|
# 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
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
# copy of this software and associated documentation files (the "Software"),
|
# 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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from hy.errors import HyError
|
|
||||||
|
|
||||||
from hy.models.lambdalist import HyLambdaListKeyword
|
from hy.models.lambdalist import HyLambdaListKeyword
|
||||||
from hy.models.expression import HyExpression
|
from hy.models.expression import HyExpression
|
||||||
from hy.models.keyword import HyKeyword
|
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.float import HyFloat
|
||||||
from hy.models.list import HyList
|
from hy.models.list import HyList
|
||||||
from hy.models.dict import HyDict
|
from hy.models.dict import HyDict
|
||||||
|
from hy.models.cons import HyCons
|
||||||
|
|
||||||
from hy.macros import require, macroexpand
|
from hy.errors import HyCompileError, HyTypeError
|
||||||
from hy._compat import str_type
|
|
||||||
|
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 hy.importer
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
@ -71,39 +74,11 @@ def load_stdlib():
|
|||||||
_stdlib[e] = module
|
_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 = {}
|
_compile_table = {}
|
||||||
|
|
||||||
|
|
||||||
def ast_str(foobar):
|
def ast_str(foobar):
|
||||||
if sys.version_info[0] >= 3:
|
if PY3:
|
||||||
return str(foobar)
|
return str(foobar)
|
||||||
|
|
||||||
try:
|
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:
|
if min is not None and (len(expression) - 1) < min:
|
||||||
_raise_wrong_args_number(
|
_raise_wrong_args_number(
|
||||||
expression,
|
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:
|
if max is not None and (len(expression) - 1) > max:
|
||||||
_raise_wrong_args_number(
|
_raise_wrong_args_number(
|
||||||
@ -419,7 +394,6 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
def compile(self, tree):
|
def compile(self, tree):
|
||||||
try:
|
try:
|
||||||
tree = macroexpand(tree, self.module_name)
|
|
||||||
_type = type(tree)
|
_type = type(tree)
|
||||||
ret = self.compile_atom(_type, tree)
|
ret = self.compile_atom(_type, tree)
|
||||||
if ret:
|
if ret:
|
||||||
@ -430,6 +404,8 @@ class HyASTCompiler(object):
|
|||||||
# nested; so let's re-raise this exception, let's not wrap it in
|
# nested; so let's re-raise this exception, let's not wrap it in
|
||||||
# another HyCompileError!
|
# another HyCompileError!
|
||||||
raise
|
raise
|
||||||
|
except HyTypeError as e:
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HyCompileError(e, sys.exc_info()[2])
|
raise HyCompileError(e, sys.exc_info()[2])
|
||||||
|
|
||||||
@ -534,18 +510,21 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
return ret, args, defaults, varargs, kwargs
|
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"""
|
"""Return a new `name` object with an ast.Store() context"""
|
||||||
|
if not func:
|
||||||
|
func = ast.Store
|
||||||
|
|
||||||
if isinstance(name, Result):
|
if isinstance(name, Result):
|
||||||
if not name.is_expr():
|
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
|
name = name.expr
|
||||||
|
|
||||||
if isinstance(name, (ast.Tuple, ast.List)):
|
if isinstance(name, (ast.Tuple, ast.List)):
|
||||||
typ = type(name)
|
typ = type(name)
|
||||||
new_elts = []
|
new_elts = []
|
||||||
for x in name.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)
|
new_name = typ(elts=new_elts)
|
||||||
elif isinstance(name, ast.Name):
|
elif isinstance(name, ast.Name):
|
||||||
new_name = ast.Name(id=name.id, arg=name.arg)
|
new_name = ast.Name(id=name.id, arg=name.arg)
|
||||||
@ -554,9 +533,9 @@ class HyASTCompiler(object):
|
|||||||
elif isinstance(name, ast.Attribute):
|
elif isinstance(name, ast.Attribute):
|
||||||
new_name = ast.Attribute(value=name.value, attr=name.attr)
|
new_name = ast.Attribute(value=name.value, attr=name.attr)
|
||||||
else:
|
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)
|
ast.copy_location(new_name, name)
|
||||||
return new_name
|
return new_name
|
||||||
|
|
||||||
@ -619,6 +598,24 @@ class HyASTCompiler(object):
|
|||||||
return imports, HyExpression([HySymbol(name),
|
return imports, HyExpression([HySymbol(name),
|
||||||
contents]).replace(form), False
|
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)):
|
elif isinstance(form, (HySymbol, HyLambdaListKeyword)):
|
||||||
return imports, HyExpression([HySymbol(name),
|
return imports, HyExpression([HySymbol(name),
|
||||||
HyString(form)]).replace(form), False
|
HyString(form)]).replace(form), False
|
||||||
@ -776,7 +773,7 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
ret = handler_results
|
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.
|
# Python 3.3 features a merge of TryExcept+TryFinally into Try.
|
||||||
return ret + ast.Try(
|
return ret + ast.Try(
|
||||||
lineno=expr.start_line,
|
lineno=expr.start_line,
|
||||||
@ -853,7 +850,7 @@ class HyASTCompiler(object):
|
|||||||
exceptions,
|
exceptions,
|
||||||
"Exception storage target name must be a symbol.")
|
"Exception storage target name must be a symbol.")
|
||||||
|
|
||||||
if sys.version_info[0] >= 3:
|
if PY3:
|
||||||
# Python3 features a change where the Exception handler
|
# Python3 features a change where the Exception handler
|
||||||
# moved the name from a Name() to a pure Python String type.
|
# moved the name from a Name() to a pure Python String type.
|
||||||
#
|
#
|
||||||
@ -1037,6 +1034,10 @@ class HyASTCompiler(object):
|
|||||||
while len(expr) > 0:
|
while len(expr) > 0:
|
||||||
iexpr = expr.pop(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):
|
if isinstance(iexpr, HySymbol):
|
||||||
rimports += _compile_import(expr, iexpr)
|
rimports += _compile_import(expr, iexpr)
|
||||||
continue
|
continue
|
||||||
@ -1083,18 +1084,81 @@ class HyASTCompiler(object):
|
|||||||
return rimports
|
return rimports
|
||||||
|
|
||||||
@builds("get")
|
@builds("get")
|
||||||
@checkargs(2)
|
@checkargs(min=2)
|
||||||
def compile_index_expression(self, expr):
|
def compile_index_expression(self, expr):
|
||||||
expr.pop(0) # index
|
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,
|
lineno=expr.start_line,
|
||||||
col_offset=expr.start_column,
|
col_offset=expr.start_column,
|
||||||
value=val.force_expr,
|
targets=del_targets)
|
||||||
slice=ast.Index(value=sli.force_expr),
|
|
||||||
ctx=ast.Load())
|
|
||||||
|
|
||||||
@builds("slice")
|
@builds("slice")
|
||||||
@checkargs(min=1, max=4)
|
@checkargs(min=1, max=4)
|
||||||
@ -1159,14 +1223,18 @@ class HyASTCompiler(object):
|
|||||||
fn.stmts[-1].decorator_list = decorators
|
fn.stmts[-1].decorator_list = decorators
|
||||||
return ret + fn
|
return ret + fn
|
||||||
|
|
||||||
@builds("with")
|
@builds("with*")
|
||||||
@checkargs(min=2)
|
@checkargs(min=2)
|
||||||
def compile_with_expression(self, expr):
|
def compile_with_expression(self, expr):
|
||||||
expr.pop(0) # with
|
expr.pop(0) # with*
|
||||||
|
|
||||||
args = expr.pop(0)
|
args = expr.pop(0)
|
||||||
if len(args) > 2 or len(args) < 1:
|
if not isinstance(args, HyList):
|
||||||
raise HyTypeError(expr, "with needs [arg (expr)] or [(expr)]")
|
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()
|
args.reverse()
|
||||||
ctx = self.compile(args.pop(0))
|
ctx = self.compile(args.pop(0))
|
||||||
@ -1195,7 +1263,7 @@ class HyASTCompiler(object):
|
|||||||
optional_vars=thing,
|
optional_vars=thing,
|
||||||
body=body.stmts)
|
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,
|
the_with.items = [ast.withitem(context_expr=ctx.force_expr,
|
||||||
optional_vars=thing)]
|
optional_vars=thing)]
|
||||||
|
|
||||||
@ -1220,54 +1288,192 @@ class HyASTCompiler(object):
|
|||||||
ctx=ast.Load())
|
ctx=ast.Load())
|
||||||
return ret
|
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")
|
@builds("list_comp")
|
||||||
@checkargs(min=2, max=3)
|
@checkargs(min=2, max=3)
|
||||||
def compile_list_comprehension(self, expr):
|
def compile_list_comprehension(self, expr):
|
||||||
# (list-comp expr (target iter) cond?)
|
# (list-comp expr (target iter) cond?)
|
||||||
expr.pop(0)
|
expr.pop(0)
|
||||||
expression = 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()
|
gen_res, gen = self._compile_generator_iterables(expr)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
compiled_expression = self.compile(expression)
|
compiled_expression = self.compile(expression)
|
||||||
ret = compiled_expression + generator_res + cond
|
ret = compiled_expression + gen_res
|
||||||
ret += ast.ListComp(
|
ret += ast.ListComp(
|
||||||
lineno=expr.start_line,
|
lineno=expr.start_line,
|
||||||
col_offset=expr.start_column,
|
col_offset=expr.start_column,
|
||||||
elt=compiled_expression.force_expr,
|
elt=compiled_expression.force_expr,
|
||||||
generators=generators)
|
generators=gen)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@builds("kwapply")
|
@builds("set_comp")
|
||||||
@checkargs(2)
|
@checkargs(min=2, max=3)
|
||||||
def compile_kwapply_expression(self, expr):
|
def compile_set_comprehension(self, expr):
|
||||||
expr.pop(0) # kwapply
|
if PY27:
|
||||||
call = self.compile(expr.pop(0))
|
ret = self.compile_list_comprehension(expr)
|
||||||
kwargs = self.compile(expr.pop(0))
|
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:
|
return ret
|
||||||
raise HyTypeError(expr, "kwapplying a non-call")
|
|
||||||
|
|
||||||
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("not")
|
||||||
@builds("~")
|
@builds("~")
|
||||||
@ -1343,11 +1549,9 @@ class HyASTCompiler(object):
|
|||||||
lineno=e.start_line,
|
lineno=e.start_line,
|
||||||
col_offset=e.start_column)
|
col_offset=e.start_column)
|
||||||
|
|
||||||
@builds("+")
|
|
||||||
@builds("%")
|
@builds("%")
|
||||||
@builds("/")
|
@builds("/")
|
||||||
@builds("//")
|
@builds("//")
|
||||||
@builds("*")
|
|
||||||
@builds("**")
|
@builds("**")
|
||||||
@builds("<<")
|
@builds("<<")
|
||||||
@builds(">>")
|
@builds(">>")
|
||||||
@ -1384,6 +1588,23 @@ class HyASTCompiler(object):
|
|||||||
col_offset=child.start_column)
|
col_offset=child.start_column)
|
||||||
return ret
|
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("-")
|
@builds("-")
|
||||||
@checkargs(min=1)
|
@checkargs(min=1)
|
||||||
def compile_maths_expression_sub(self, expression):
|
def compile_maths_expression_sub(self, expression):
|
||||||
@ -1447,8 +1668,15 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
@builds(HyExpression)
|
@builds(HyExpression)
|
||||||
def compile_expression(self, expression):
|
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 == []:
|
if expression == []:
|
||||||
return self.compile_list(expression)
|
return self.compile_list(expression)
|
||||||
|
|
||||||
fn = expression[0]
|
fn = expression[0]
|
||||||
func = None
|
func = None
|
||||||
if isinstance(fn, HyKeyword):
|
if isinstance(fn, HyKeyword):
|
||||||
@ -1468,6 +1696,9 @@ class HyASTCompiler(object):
|
|||||||
fn.replace(ofn)
|
fn.replace(ofn)
|
||||||
|
|
||||||
# Get the object we want to take an attribute from
|
# 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))
|
func = self.compile(expression.pop(1))
|
||||||
|
|
||||||
# And get the attribute
|
# And get the attribute
|
||||||
@ -1518,23 +1749,36 @@ class HyASTCompiler(object):
|
|||||||
result += ld_name
|
result += ld_name
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@builds("foreach")
|
@builds("for*")
|
||||||
@checkargs(min=1)
|
@checkargs(min=1)
|
||||||
def compile_for_expression(self, expression):
|
def compile_for_expression(self, expression):
|
||||||
expression.pop(0) # for
|
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))
|
target = self._storeize(self.compile(target_name))
|
||||||
|
|
||||||
ret = Result()
|
ret = Result()
|
||||||
|
|
||||||
orel = Result()
|
orel = Result()
|
||||||
# (foreach [] body (else …))
|
# (for* [] body (else …))
|
||||||
if expression and expression[-1][0] == HySymbol("else"):
|
if expression and expression[-1][0] == HySymbol("else"):
|
||||||
else_expr = expression.pop()
|
else_expr = expression.pop()
|
||||||
if len(else_expr) > 2:
|
if len(else_expr) > 2:
|
||||||
raise HyTypeError(
|
raise HyTypeError(
|
||||||
else_expr,
|
else_expr,
|
||||||
"`else' statement in `foreach' is too long")
|
"`else' statement in `for' is too long")
|
||||||
elif len(else_expr) == 2:
|
elif len(else_expr) == 2:
|
||||||
orel += self.compile(else_expr[1])
|
orel += self.compile(else_expr[1])
|
||||||
orel += orel.expr_as_stmt()
|
orel += orel.expr_as_stmt()
|
||||||
@ -1592,12 +1836,32 @@ class HyASTCompiler(object):
|
|||||||
arglist = expression.pop(0)
|
arglist = expression.pop(0)
|
||||||
ret, args, defaults, stararg, kwargs = self._parse_lambda_list(arglist)
|
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.arguments(
|
||||||
args=[ast.Name(arg=ast_str(x), id=ast_str(x),
|
args=args,
|
||||||
ctx=ast.Param(),
|
|
||||||
lineno=x.start_line,
|
|
||||||
col_offset=x.start_column)
|
|
||||||
for x in args],
|
|
||||||
vararg=stararg,
|
vararg=stararg,
|
||||||
kwarg=kwargs,
|
kwarg=kwargs,
|
||||||
kwonlyargs=[],
|
kwonlyargs=[],
|
||||||
@ -1706,6 +1970,19 @@ class HyASTCompiler(object):
|
|||||||
bases=bases_expr,
|
bases=bases_expr,
|
||||||
body=body.stmts)
|
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")
|
@builds("defmacro")
|
||||||
@checkargs(min=1)
|
@checkargs(min=1)
|
||||||
def compile_macro(self, expression):
|
def compile_macro(self, expression):
|
||||||
@ -1721,19 +1998,50 @@ class HyASTCompiler(object):
|
|||||||
HyExpression([HySymbol("fn")] + expression),
|
HyExpression([HySymbol("fn")] + expression),
|
||||||
]).replace(expression)
|
]).replace(expression)
|
||||||
|
|
||||||
# Compile-time hack: we want to get our new macro now
|
ret = self._compile_time_hack(new_expression)
|
||||||
# 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])
|
|
||||||
|
|
||||||
return ret
|
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")
|
@builds("eval_and_compile")
|
||||||
def compile_eval_and_compile(self, expression):
|
def compile_eval_and_compile(self, expression):
|
||||||
expression[0] = HySymbol("progn")
|
expression[0] = HySymbol("progn")
|
||||||
@ -1751,9 +2059,13 @@ class HyASTCompiler(object):
|
|||||||
self.module_name)
|
self.module_name)
|
||||||
return Result()
|
return Result()
|
||||||
|
|
||||||
|
@builds(HyCons)
|
||||||
|
def compile_cons(self, cons):
|
||||||
|
raise HyTypeError(cons, "Can't compile a top-level cons cell")
|
||||||
|
|
||||||
@builds(HyInteger)
|
@builds(HyInteger)
|
||||||
def compile_integer(self, number):
|
def compile_integer(self, number):
|
||||||
return ast.Num(n=int(number),
|
return ast.Num(n=long_type(number),
|
||||||
lineno=number.start_line,
|
lineno=number.start_line,
|
||||||
col_offset=number.start_column)
|
col_offset=number.start_column)
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
docomplete = True
|
docomplete = True
|
||||||
@ -40,6 +41,12 @@ except ImportError:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
docomplete = False
|
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.macros
|
||||||
import hy.compiler
|
import hy.compiler
|
||||||
|
|
||||||
@ -94,7 +101,7 @@ def completion(completer=None):
|
|||||||
except IOError:
|
except IOError:
|
||||||
open(history, 'a').close()
|
open(history, 'a').close()
|
||||||
|
|
||||||
readline.parse_and_bind("tab: complete")
|
readline.parse_and_bind(readline_bind)
|
||||||
|
|
||||||
yield
|
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
|
;;; Hy on Meth
|
||||||
;; based on paultag's meth library to access a Flask based application
|
;;; based on paultag's meth library to access a Flask based application
|
||||||
|
|
||||||
(defmacro route [name path params code]
|
(defmacro route-with-methods [name path methods params &rest 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]
|
|
||||||
"Same as route but with an extra methods array to specify HTTP methods"
|
"Same as route but with an extra methods array to specify HTTP methods"
|
||||||
`(let [[deco (kwapply (.route app ~path)
|
`(let [[deco (kwapply (.route app ~path)
|
||||||
{"methods" ~methods})]]
|
{"methods" ~methods})]]
|
||||||
(with-decorator deco
|
(with-decorator deco
|
||||||
(defn ~name ~params ~@code))))
|
(defn ~name ~params
|
||||||
|
(progn ~@code)))))
|
||||||
|
|
||||||
;; Some macro examples
|
;; 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"
|
"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"
|
"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"
|
"Delete request"
|
||||||
`(route-with-methods ~name ~path ~params ~code ["DELETE"]))
|
`(route-with-methods ~name ~path ["DELETE"] ~params ~@code))
|
||||||
|
|
||||||
|
|
||||||
;;; 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
|
|
||||||
|
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]
|
(defmacro macro-error [location reason]
|
||||||
"error out properly within a macro"
|
"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]
|
(defmacro defmacro-alias [names lambda-list &rest body]
|
||||||
"define one macro with several names"
|
"define one macro with several names"
|
||||||
(setv ret `(do))
|
(setv ret `(do))
|
||||||
(foreach [name names]
|
(for* [name names]
|
||||||
(.append ret
|
(.append ret
|
||||||
`(defmacro ~name ~lambda-list ~@body)))
|
`(defmacro ~name ~lambda-list ~@body)))
|
||||||
ret)
|
ret)
|
||||||
@ -52,7 +52,7 @@
|
|||||||
(setv macroed_variables [])
|
(setv macroed_variables [])
|
||||||
(if (not (isinstance variables HyList))
|
(if (not (isinstance variables HyList))
|
||||||
(macro-error variables "let lexical context must be a list"))
|
(macro-error variables "let lexical context must be a list"))
|
||||||
(foreach [variable variables]
|
(for* [variable variables]
|
||||||
(if (isinstance variable HyList)
|
(if (isinstance variable HyList)
|
||||||
(do
|
(do
|
||||||
(if (!= (len variable) 2)
|
(if (!= (len variable) 2)
|
||||||
|
@ -23,18 +23,35 @@
|
|||||||
;;;; to make functional programming slightly easier.
|
;;;; 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]
|
(defn _numeric-check [x]
|
||||||
(if (not (numeric? x))
|
(if (not (numeric? x))
|
||||||
(raise (TypeError (.format "{0!r} is not a number" 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]
|
(defn cycle [coll]
|
||||||
"Yield an infinite repetition of the items in coll"
|
"Yield an infinite repetition of the items in coll"
|
||||||
(setv seen [])
|
(setv seen [])
|
||||||
(foreach [x coll]
|
(for* [x coll]
|
||||||
(yield x)
|
(yield x)
|
||||||
(.append seen x))
|
(.append seen x))
|
||||||
(while seen
|
(while seen
|
||||||
(foreach [x seen]
|
(for* [x seen]
|
||||||
(yield x))))
|
(yield x))))
|
||||||
|
|
||||||
(defn dec [n]
|
(defn dec [n]
|
||||||
@ -42,20 +59,33 @@
|
|||||||
(_numeric-check n)
|
(_numeric-check n)
|
||||||
(- n 1))
|
(- 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]
|
(defn distinct [coll]
|
||||||
"Return a generator from the original collection with duplicates
|
"Return a generator from the original collection with duplicates
|
||||||
removed"
|
removed"
|
||||||
(let [[seen []] [citer (iter coll)]]
|
(let [[seen (set)] [citer (iter coll)]]
|
||||||
(foreach [val citer]
|
(for* [val citer]
|
||||||
(if (not_in val seen)
|
(if (not_in val seen)
|
||||||
(do
|
(do
|
||||||
(yield val)
|
(yield val)
|
||||||
(.append seen val))))))
|
(.add seen val))))))
|
||||||
|
|
||||||
(defn drop [count coll]
|
(defn drop [count coll]
|
||||||
"Drop `count` elements from `coll` and yield back the rest"
|
"Drop `count` elements from `coll` and yield back the rest"
|
||||||
(let [[citer (iter coll)]]
|
(let [[citer (iter coll)]]
|
||||||
(try (foreach [i (range count)]
|
(try (for* [i (range count)]
|
||||||
(next citer))
|
(next citer))
|
||||||
(catch [StopIteration]))
|
(catch [StopIteration]))
|
||||||
citer))
|
citer))
|
||||||
@ -63,10 +93,10 @@
|
|||||||
(defn drop-while [pred coll]
|
(defn drop-while [pred coll]
|
||||||
"Drop all elements of `coll` until `pred` is False"
|
"Drop all elements of `coll` until `pred` is False"
|
||||||
(let [[citer (iter coll)]]
|
(let [[citer (iter coll)]]
|
||||||
(foreach [val citer]
|
(for* [val citer]
|
||||||
(if (not (pred val))
|
(if (not (pred val))
|
||||||
(do (yield val) (break))))
|
(do (yield val) (break))))
|
||||||
(foreach [val citer]
|
(for* [val citer]
|
||||||
(yield val))))
|
(yield val))))
|
||||||
|
|
||||||
(defn empty? [coll]
|
(defn empty? [coll]
|
||||||
@ -78,17 +108,73 @@
|
|||||||
(_numeric-check n)
|
(_numeric-check n)
|
||||||
(= (% n 2) 0))
|
(= (% 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]
|
(defn filter [pred coll]
|
||||||
"Return all elements from `coll` that pass `pred`"
|
"Return all elements from `coll` that pass `pred`"
|
||||||
(let [[citer (iter coll)]]
|
(let [[citer (iter coll)]]
|
||||||
(foreach [val citer]
|
(for* [val citer]
|
||||||
(if (pred val)
|
(if (pred val)
|
||||||
(yield 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]
|
(defn float? [x]
|
||||||
"Return True if x is float"
|
"Return True if x is float"
|
||||||
(isinstance x 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]
|
(defn inc [n]
|
||||||
"Increment n by 1"
|
"Increment n by 1"
|
||||||
(_numeric-check n)
|
(_numeric-check n)
|
||||||
@ -97,11 +183,20 @@
|
|||||||
(defn instance? [klass x]
|
(defn instance? [klass x]
|
||||||
(isinstance x klass))
|
(isinstance x klass))
|
||||||
|
|
||||||
|
(defn integer [x]
|
||||||
|
"Return Hy kind of integer"
|
||||||
|
(long-type x))
|
||||||
|
|
||||||
(defn integer? [x]
|
(defn integer? [x]
|
||||||
"Return True if x in an integer"
|
"Return True if x in an integer"
|
||||||
(if-python2
|
(isinstance x (, int long-type)))
|
||||||
(isinstance x (, int long))
|
|
||||||
(isinstance x int)))
|
(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]
|
(defn iterable? [x]
|
||||||
"Return true if x is iterable"
|
"Return true if x is iterable"
|
||||||
@ -119,6 +214,26 @@
|
|||||||
(try (= x (iter x))
|
(try (= x (iter x))
|
||||||
(catch [TypeError] false)))
|
(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]
|
(defn neg? [n]
|
||||||
"Return true if n is < 0"
|
"Return true if n is < 0"
|
||||||
(_numeric-check n)
|
(_numeric-check n)
|
||||||
@ -128,6 +243,10 @@
|
|||||||
"Return true if x is None"
|
"Return true if x is None"
|
||||||
(is x None))
|
(is x None))
|
||||||
|
|
||||||
|
(defn nil? [x]
|
||||||
|
"Return true if x is nil (None)"
|
||||||
|
(is x None))
|
||||||
|
|
||||||
(defn numeric? [x]
|
(defn numeric? [x]
|
||||||
(import numbers)
|
(import numbers)
|
||||||
(instance? numbers.Number x))
|
(instance? numbers.Number x))
|
||||||
@ -155,15 +274,19 @@
|
|||||||
(defn remove [pred coll]
|
(defn remove [pred coll]
|
||||||
"Return coll with elements removed that pass `pred`"
|
"Return coll with elements removed that pass `pred`"
|
||||||
(let [[citer (iter coll)]]
|
(let [[citer (iter coll)]]
|
||||||
(foreach [val citer]
|
(for* [val citer]
|
||||||
(if (not (pred val))
|
(if (not (pred val))
|
||||||
(yield val)))))
|
(yield val)))))
|
||||||
|
|
||||||
|
(defn rest [coll]
|
||||||
|
"Get all the elements of a coll, except the first."
|
||||||
|
(slice coll 1))
|
||||||
|
|
||||||
(defn repeat [x &optional n]
|
(defn repeat [x &optional n]
|
||||||
"Yield x forever or optionally n times"
|
"Yield x forever or optionally n times"
|
||||||
(if (none? n)
|
(if (none? n)
|
||||||
(setv dispatch (fn [] (while true (yield x))))
|
(setv dispatch (fn [] (while true (yield x))))
|
||||||
(setv dispatch (fn [] (foreach [_ (range n)] (yield x)))))
|
(setv dispatch (fn [] (for* [_ (range n)] (yield x)))))
|
||||||
(dispatch))
|
(dispatch))
|
||||||
|
|
||||||
(defn repeatedly [func]
|
(defn repeatedly [func]
|
||||||
@ -175,6 +298,16 @@
|
|||||||
"Return second item from `coll`"
|
"Return second item from `coll`"
|
||||||
(get coll 1))
|
(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]
|
(defn string? [x]
|
||||||
"Return True if x is a string"
|
"Return True if x is a string"
|
||||||
(if-python2
|
(if-python2
|
||||||
@ -185,7 +318,7 @@
|
|||||||
"Take `count` elements from `coll`, or the whole set if the total
|
"Take `count` elements from `coll`, or the whole set if the total
|
||||||
number of entries in `coll` is less than `count`."
|
number of entries in `coll` is less than `count`."
|
||||||
(let [[citer (iter coll)]]
|
(let [[citer (iter coll)]]
|
||||||
(foreach [_ (range count)]
|
(for* [_ (range count)]
|
||||||
(yield (next citer)))))
|
(yield (next citer)))))
|
||||||
|
|
||||||
(defn take-nth [n coll]
|
(defn take-nth [n coll]
|
||||||
@ -193,16 +326,16 @@
|
|||||||
raises ValueError for (not (pos? n))"
|
raises ValueError for (not (pos? n))"
|
||||||
(if (pos? n)
|
(if (pos? n)
|
||||||
(let [[citer (iter coll)] [skip (dec n)]]
|
(let [[citer (iter coll)] [skip (dec n)]]
|
||||||
(foreach [val citer]
|
(for* [val citer]
|
||||||
(yield val)
|
(yield val)
|
||||||
(foreach [_ (range skip)]
|
(for* [_ (range skip)]
|
||||||
(next citer))))
|
(next citer))))
|
||||||
(raise (ValueError "n must be positive"))))
|
(raise (ValueError "n must be positive"))))
|
||||||
|
|
||||||
(defn take-while [pred coll]
|
(defn take-while [pred coll]
|
||||||
"Take all elements while `pred` is true"
|
"Take all elements while `pred` is true"
|
||||||
(let [[citer (iter coll)]]
|
(let [[citer (iter coll)]]
|
||||||
(foreach [val citer]
|
(for* [val citer]
|
||||||
(if (pred val)
|
(if (pred val)
|
||||||
(yield val)
|
(yield val)
|
||||||
(break)))))
|
(break)))))
|
||||||
@ -212,9 +345,16 @@
|
|||||||
(_numeric_check n)
|
(_numeric_check n)
|
||||||
(= n 0))
|
(= n 0))
|
||||||
|
|
||||||
(def *exports* ["cycle" "dec" "distinct" "drop" "drop_while" "empty?"
|
(defn zipwith [func &rest lists]
|
||||||
"even?" "filter" "float?" "inc"
|
"Zip the contents of several lists and map a function to the result"
|
||||||
"instance?" "integer?" "iterable?" "iterate" "iterator?" "neg?"
|
(do
|
||||||
"none?" "nth" "numeric?" "odd?" "pos?" "remove" "repeat"
|
(import functools)
|
||||||
"repeatedly" "second" "string?" "take" "take_nth" "take_while"
|
(map (functools.partial (fn [f args] (apply f args)) func) (apply zip lists))))
|
||||||
"zero?"])
|
|
||||||
|
(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
|
;;; 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]
|
(defmacro for [args &rest body]
|
||||||
"shorthand for nested foreach loops:
|
"shorthand for nested for loops:
|
||||||
(for [x foo y bar] baz) ->
|
(for [x foo
|
||||||
(foreach [x foo]
|
y bar]
|
||||||
(foreach [y bar]
|
baz) ->
|
||||||
|
(for* [x foo]
|
||||||
|
(for* [y bar]
|
||||||
baz))"
|
baz))"
|
||||||
;; TODO: that signature sucks.
|
|
||||||
;; (for [[x foo] [y bar]] baz) would be more consistent
|
(if (odd? (len args))
|
||||||
(if (% (len args) 2)
|
(macro-error args "`for' requires an even number of args."))
|
||||||
(macro-error args "for needs an even number of elements in its first argument"))
|
|
||||||
(if args
|
(if (empty? body)
|
||||||
`(foreach [~(.pop args 0) ~(.pop args 0)] (for ~args ~@body))
|
(macro-error None "`for' requires a body to evaluate"))
|
||||||
`(do ~@body)))
|
|
||||||
|
(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 the first element of a list/cons"
|
||||||
`(get ~thing 0))
|
`(get ~thing 0))
|
||||||
|
|
||||||
|
|
||||||
(defmacro-alias [cdr rest] [thing]
|
(defmacro cdr [thing]
|
||||||
"Get all the elements of a thing, except the first"
|
"Get all the elements of a thing, except the first"
|
||||||
`(slice ~thing 1))
|
`(slice ~thing 1))
|
||||||
|
|
||||||
@ -72,7 +109,7 @@
|
|||||||
(setv root (check-branch branch))
|
(setv root (check-branch branch))
|
||||||
(setv latest-branch root)
|
(setv latest-branch root)
|
||||||
|
|
||||||
(foreach [branch branches]
|
(for* [branch branches]
|
||||||
(setv cur-branch (check-branch branch))
|
(setv cur-branch (check-branch branch))
|
||||||
(.append latest-branch cur-branch)
|
(.append latest-branch cur-branch)
|
||||||
(setv latest-branch cur-branch))
|
(setv latest-branch cur-branch))
|
||||||
@ -82,7 +119,7 @@
|
|||||||
(defmacro -> [head &rest rest]
|
(defmacro -> [head &rest rest]
|
||||||
;; TODO: fix the docstring by someone who understands this
|
;; TODO: fix the docstring by someone who understands this
|
||||||
(setv ret head)
|
(setv ret head)
|
||||||
(foreach [node rest]
|
(for* [node rest]
|
||||||
(if (not (isinstance node HyExpression))
|
(if (not (isinstance node HyExpression))
|
||||||
(setv node `(~node)))
|
(setv node `(~node)))
|
||||||
(.insert node 1 ret)
|
(.insert node 1 ret)
|
||||||
@ -93,7 +130,7 @@
|
|||||||
(defmacro ->> [head &rest rest]
|
(defmacro ->> [head &rest rest]
|
||||||
;; TODO: fix the docstring by someone who understands this
|
;; TODO: fix the docstring by someone who understands this
|
||||||
(setv ret head)
|
(setv ret head)
|
||||||
(foreach [node rest]
|
(for* [node rest]
|
||||||
(if (not (isinstance node HyExpression))
|
(if (not (isinstance node HyExpression))
|
||||||
(setv node `(~node)))
|
(setv node `(~node)))
|
||||||
(.append node ret)
|
(.append node ret)
|
||||||
@ -101,6 +138,13 @@
|
|||||||
ret)
|
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]
|
(defmacro when [test &rest body]
|
||||||
"Execute `body` when `test` is true"
|
"Execute `body` when `test` is true"
|
||||||
`(if ~test (do ~@body)))
|
`(if ~test (do ~@body)))
|
||||||
@ -108,11 +152,50 @@
|
|||||||
|
|
||||||
(defmacro unless [test &rest body]
|
(defmacro unless [test &rest body]
|
||||||
"Execute `body` when `test` is false"
|
"Execute `body` when `test` is false"
|
||||||
`(if ~test None (do ~@body)))
|
`(if-not ~test (do ~@body)))
|
||||||
|
|
||||||
|
|
||||||
(defmacro yield-from [iterable]
|
(defmacro yield-from [iterable]
|
||||||
"Yield all the items from iterable"
|
"Yield all the items from iterable"
|
||||||
;; TODO: this needs some gensym love
|
(let [[x (gensym)]]
|
||||||
`(foreach [_hy_yield_from_x ~iterable]
|
`(for* [~x ~iterable]
|
||||||
(yield _hy_yield_from_x)))
|
(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 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
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
# copy of this software and associated documentation files (the "Software"),
|
# 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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
class HyError(Exception):
|
class HyError(Exception):
|
||||||
"""
|
"""
|
||||||
@ -25,3 +30,115 @@ class HyError(Exception):
|
|||||||
Exception.
|
Exception.
|
||||||
"""
|
"""
|
||||||
pass
|
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 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
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
# copy of this software and associated documentation files (the "Software"),
|
# 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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from py_compile import wr_long, MAGIC
|
from hy.compiler import hy_compile, HyTypeError
|
||||||
from hy.compiler import hy_compile
|
|
||||||
from hy.models import HyObject
|
from hy.models import HyObject
|
||||||
from hy.lex import tokenize
|
from hy.lex import tokenize, LexException
|
||||||
|
|
||||||
|
|
||||||
from io import open
|
from io import open
|
||||||
import marshal
|
import marshal
|
||||||
@ -32,7 +31,7 @@ import ast
|
|||||||
import os
|
import os
|
||||||
import __future__
|
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):
|
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 = imp.new_module(module_name)
|
||||||
mod.__file__ = fpath
|
mod.__file__ = fpath
|
||||||
eval(ast_compile(_ast, fpath, "exec"), mod.__dict__)
|
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:
|
except Exception:
|
||||||
sys.modules.pop(module_name, None)
|
sys.modules.pop(module_name, None)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
return mod
|
return mod
|
||||||
|
|
||||||
|
|
||||||
def import_buffer_to_module(module_name, buf):
|
def import_buffer_to_module(module_name, buf):
|
||||||
_ast = import_buffer_to_ast(buf, module_name)
|
try:
|
||||||
mod = imp.new_module(module_name)
|
_ast = import_buffer_to_ast(buf, module_name)
|
||||||
eval(ast_compile(_ast, "", "exec"), mod.__dict__)
|
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
|
return mod
|
||||||
|
|
||||||
|
|
||||||
@ -128,12 +138,12 @@ def write_hy_as_pyc(fname):
|
|||||||
open_ = builtins.open
|
open_ = builtins.open
|
||||||
|
|
||||||
with open_(cfile, 'wb') as fc:
|
with open_(cfile, 'wb') as fc:
|
||||||
if sys.version_info[0] >= 3:
|
if PY3:
|
||||||
fc.write(b'\0\0\0\0')
|
fc.write(b'\0\0\0\0')
|
||||||
else:
|
else:
|
||||||
fc.write('\0\0\0\0')
|
fc.write('\0\0\0\0')
|
||||||
wr_long(fc, timestamp)
|
wr_long(fc, timestamp)
|
||||||
if (sys.version_info[0] >= 3 and sys.version_info[1] >= 3):
|
if PY33:
|
||||||
wr_long(fc, st.st_size)
|
wr_long(fc, st.st_size)
|
||||||
marshal.dump(code, fc)
|
marshal.dump(code, fc)
|
||||||
fc.flush()
|
fc.flush()
|
||||||
|
@ -33,6 +33,5 @@ def tokenize(buf):
|
|||||||
return parser.parse(lexer.lex(buf))
|
return parser.parse(lexer.lex(buf))
|
||||||
except LexingError as e:
|
except LexingError as e:
|
||||||
pos = e.getsourcepos()
|
pos = e.getsourcepos()
|
||||||
raise LexException(
|
raise LexException("Could not identify the next token.",
|
||||||
"Could not identify the next token at line %s, column %s" % (
|
pos.lineno, pos.colno)
|
||||||
pos.lineno, pos.colno))
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
|
# 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
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
# copy of this software and associated documentation files (the "Software"),
|
# copy of this software and associated documentation files (the "Software"),
|
||||||
@ -23,9 +24,43 @@ from hy.errors import HyError
|
|||||||
|
|
||||||
class LexException(HyError):
|
class LexException(HyError):
|
||||||
"""Error during the Lexing of a Hython expression."""
|
"""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):
|
class PrematureEndOfInput(LexException):
|
||||||
"""We got a premature end of input"""
|
"""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('UNQUOTESPLICE', r'~@%s' % end_quote)
|
||||||
lg.add('UNQUOTE', r'~%s' % end_quote)
|
lg.add('UNQUOTE', r'~%s' % end_quote)
|
||||||
lg.add('HASHBANG', r'#!.*[^\r\n]')
|
lg.add('HASHBANG', r'#!.*[^\r\n]')
|
||||||
|
lg.add('HASHREADER', r'#.')
|
||||||
|
|
||||||
|
|
||||||
lg.add('STRING', r'''(?x)
|
lg.add('STRING', r'''(?x)
|
||||||
@ -59,7 +60,7 @@ lg.add('STRING', r'''(?x)
|
|||||||
lg.add('IDENTIFIER', r'[^()\[\]{}\'"\s;]+')
|
lg.add('IDENTIFIER', r'[^()\[\]{}\'"\s;]+')
|
||||||
|
|
||||||
|
|
||||||
lg.ignore(r';.*[\r\n]+')
|
lg.ignore(r';.*(?=\r|\n|$)')
|
||||||
lg.ignore(r'\s+')
|
lg.ignore(r'\s+')
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ from functools import wraps
|
|||||||
from rply import ParserGenerator
|
from rply import ParserGenerator
|
||||||
|
|
||||||
from hy.models.complex import HyComplex
|
from hy.models.complex import HyComplex
|
||||||
|
from hy.models.cons import HyCons
|
||||||
from hy.models.dict import HyDict
|
from hy.models.dict import HyDict
|
||||||
from hy.models.expression import HyExpression
|
from hy.models.expression import HyExpression
|
||||||
from hy.models.float import HyFloat
|
from hy.models.float import HyFloat
|
||||||
@ -95,9 +96,40 @@ def real_main_empty(p):
|
|||||||
return []
|
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")
|
@pg.production("paren : LPAREN list_contents RPAREN")
|
||||||
@set_boundaries
|
@set_boundaries
|
||||||
def paren(p):
|
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])
|
return HyExpression(p[1])
|
||||||
|
|
||||||
|
|
||||||
@ -150,6 +182,15 @@ def term_unquote_splice(p):
|
|||||||
return HyExpression([HySymbol("unquote_splice"), p[1]])
|
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")
|
@pg.production("dict : LCURLY list_contents RCURLY")
|
||||||
@set_boundaries
|
@set_boundaries
|
||||||
def t_dict(p):
|
def t_dict(p):
|
||||||
@ -220,6 +261,7 @@ def t_identifier(p):
|
|||||||
table = {
|
table = {
|
||||||
"true": "True",
|
"true": "True",
|
||||||
"false": "False",
|
"false": "False",
|
||||||
|
"nil": "None",
|
||||||
"null": "None",
|
"null": "None",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,11 +274,19 @@ def t_identifier(p):
|
|||||||
if obj.startswith("&"):
|
if obj.startswith("&"):
|
||||||
return HyLambdaListKeyword(obj)
|
return HyLambdaListKeyword(obj)
|
||||||
|
|
||||||
if obj.startswith("*") and obj.endswith("*") and obj not in ("*", "**"):
|
def mangle(p):
|
||||||
obj = obj[1:-1].upper()
|
if p.startswith("*") and p.endswith("*") and p not in ("*", "**"):
|
||||||
|
p = p[1:-1].upper()
|
||||||
|
|
||||||
if "-" in obj and obj != "-":
|
if "-" in p and p != "-":
|
||||||
obj = obj.replace("-", "_")
|
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)
|
return HySymbol(obj)
|
||||||
|
|
||||||
@ -245,12 +295,11 @@ def t_identifier(p):
|
|||||||
def error_handler(token):
|
def error_handler(token):
|
||||||
tokentype = token.gettokentype()
|
tokentype = token.gettokentype()
|
||||||
if tokentype == '$end':
|
if tokentype == '$end':
|
||||||
raise PrematureEndOfInput
|
raise PrematureEndOfInput("Premature end of input")
|
||||||
else:
|
else:
|
||||||
raise LexException(
|
raise LexException(
|
||||||
"Ran into a %s where it wasn't expected at line %s, column %s" %
|
"Ran into a %s where it wasn't expected." % tokentype,
|
||||||
(tokentype, token.source_pos.lineno, token.source_pos.colno)
|
token.source_pos.lineno, token.source_pos.colno)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
parser = pg.build()
|
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.float import HyFloat
|
||||||
from hy.models.complex import HyComplex
|
from hy.models.complex import HyComplex
|
||||||
from hy.models.dict import HyDict
|
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
|
from collections import defaultdict
|
||||||
|
import sys
|
||||||
|
|
||||||
CORE_MACROS = [
|
CORE_MACROS = [
|
||||||
"hy.core.bootstrap",
|
"hy.core.bootstrap",
|
||||||
@ -40,6 +42,7 @@ EXTRA_MACROS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
_hy_macros = defaultdict(dict)
|
_hy_macros = defaultdict(dict)
|
||||||
|
_hy_reader = defaultdict(dict)
|
||||||
|
|
||||||
|
|
||||||
def macro(name):
|
def macro(name):
|
||||||
@ -63,6 +66,28 @@ def macro(name):
|
|||||||
return _
|
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):
|
def require(source_module, target_module):
|
||||||
"""Load the macros from `source_module` in the namespace of
|
"""Load the macros from `source_module` in the namespace of
|
||||||
`target_module`.
|
`target_module`.
|
||||||
@ -75,6 +100,11 @@ def require(source_module, target_module):
|
|||||||
for name, macro in macros.items():
|
for name, macro in macros.items():
|
||||||
refs[name] = macro
|
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
|
# type -> wrapping function mapping for _wrap_value
|
||||||
_wrappers = {
|
_wrappers = {
|
||||||
@ -84,9 +114,14 @@ _wrappers = {
|
|||||||
complex: HyComplex,
|
complex: HyComplex,
|
||||||
str_type: HyString,
|
str_type: HyString,
|
||||||
dict: lambda d: HyDict(_wrap_value(x) for x in sum(d.items(), ())),
|
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):
|
def _wrap_value(x):
|
||||||
"""Wrap `x` into the corresponding Hy type.
|
"""Wrap `x` into the corresponding Hy type.
|
||||||
@ -157,9 +192,35 @@ def macroexpand_1(tree, module_name):
|
|||||||
if m is None:
|
if m is None:
|
||||||
m = _hy_macros[None].get(fn)
|
m = _hy_macros[None].get(fn)
|
||||||
if m is not None:
|
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)
|
obj.replace(tree)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
return ntree
|
return ntree
|
||||||
return tree
|
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):
|
if isinstance(other, HyObject):
|
||||||
for attr in ["start_line", "end_line",
|
for attr in ["start_line", "end_line",
|
||||||
"start_column", "end_column"]:
|
"start_column", "end_column"]:
|
||||||
if not hasattr(self, attr):
|
if not hasattr(self, attr) and hasattr(other, attr):
|
||||||
setattr(self, attr, getattr(other, attr))
|
setattr(self, attr, getattr(other, attr))
|
||||||
else:
|
else:
|
||||||
raise TypeError("Can't replace a non Hy object with a Hy object")
|
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):
|
def __repr__(self):
|
||||||
return "{%s}" % (" ".join([repr(x) for x in 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.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from hy.models import HyObject
|
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
|
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):
|
def __new__(cls, number, *args, **kwargs):
|
||||||
number = int(number)
|
number = long_type(number)
|
||||||
return super(HyInteger, cls).__new__(cls, number)
|
return super(HyInteger, cls).__new__(cls, number)
|
||||||
|
@ -29,5 +29,8 @@ class HyKeyword(HyObject, str_type):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __new__(cls, value):
|
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
|
return obj
|
||||||
|
@ -36,5 +36,16 @@ class HyList(HyObject, list):
|
|||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
return self.__class__(super(HyList, self).__add__(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):
|
def __repr__(self):
|
||||||
return "[%s]" % (" ".join([repr(x) for x in self]))
|
return "[%s]" % (" ".join([repr(x) for x in self]))
|
||||||
|
@ -20,4 +20,4 @@
|
|||||||
|
|
||||||
|
|
||||||
__appname__ = "hy"
|
__appname__ = "hy"
|
||||||
__version__ = "0.9.11"
|
__version__ = "0.9.12"
|
||||||
|
22
make.bat
22
make.bat
@ -20,8 +20,9 @@ if "%1" == "help" (
|
|||||||
echo. - tox
|
echo. - tox
|
||||||
echo. - d
|
echo. - d
|
||||||
echo. - r
|
echo. - r
|
||||||
|
echo. - clean
|
||||||
echo.
|
echo.
|
||||||
goto end
|
goto :EOF
|
||||||
)
|
)
|
||||||
|
|
||||||
if "%1" == "docs" (
|
if "%1" == "docs" (
|
||||||
@ -109,8 +110,25 @@ if "%1" == "r" (
|
|||||||
goto :EOF
|
goto :EOF
|
||||||
)
|
)
|
||||||
|
|
||||||
if "%1" == full (
|
if "%1" == "full" (
|
||||||
call :docs
|
call :docs
|
||||||
call :d
|
call :d
|
||||||
call :tox
|
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
|
-r requirements.txt
|
||||||
astor
|
|
||||||
tox
|
tox
|
||||||
nose
|
nose
|
||||||
Sphinx
|
Sphinx
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
# Check site / dev for more deps!
|
# Check site / dev for more deps!
|
||||||
flake8
|
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
|
make things work nicer, and lets Python and the Hy lisp variant play
|
||||||
nice together. """
|
nice together. """
|
||||||
|
|
||||||
install_requires = ['rply>=0.6.2']
|
install_requires = ['rply>=0.7.0', 'astor>=0.3']
|
||||||
if sys.version_info[:2] < (2, 7):
|
if sys.version_info[:2] < (2, 7):
|
||||||
install_requires.append('argparse>=1.2.1')
|
install_requires.append('argparse>=1.2.1')
|
||||||
install_requires.append('importlib>=1.0.2')
|
install_requires.append('importlib>=1.0.2')
|
||||||
@ -56,7 +56,6 @@ setup(
|
|||||||
name=PKG,
|
name=PKG,
|
||||||
version=__version__,
|
version=__version__,
|
||||||
install_requires=install_requires,
|
install_requires=install_requires,
|
||||||
dependency_links=['https://github.com/hylang/rply/zipball/master#egg=rply-0.6.2'],
|
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'hy = hy.cmdline:hy_main',
|
'hy = hy.cmdline:hy_main',
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import hy # noqa
|
import hy # noqa
|
||||||
|
from .native_tests.cons import * # noqa
|
||||||
from .native_tests.defclass import * # noqa
|
from .native_tests.defclass import * # noqa
|
||||||
from .native_tests.math import * # noqa
|
from .native_tests.math import * # noqa
|
||||||
from .native_tests.native_macros 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.when import * # noqa
|
||||||
from .native_tests.with_decorator import * # noqa
|
from .native_tests.with_decorator import * # noqa
|
||||||
from .native_tests.core 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 __future__ import unicode_literals
|
||||||
|
|
||||||
from hy import HyString
|
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
|
from hy.lex import tokenize
|
||||||
|
|
||||||
import ast
|
import ast
|
||||||
@ -42,10 +45,14 @@ def can_compile(expr):
|
|||||||
|
|
||||||
|
|
||||||
def cant_compile(expr):
|
def cant_compile(expr):
|
||||||
expr = tokenize(expr)
|
|
||||||
try:
|
try:
|
||||||
hy_compile(expr, "__main__")
|
hy_compile(tokenize(expr), "__main__")
|
||||||
assert False
|
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:
|
except HyCompileError as e:
|
||||||
# Anything that can't be compiled should raise a user friendly
|
# Anything that can't be compiled should raise a user friendly
|
||||||
# error, otherwise it's a compiler bug.
|
# error, otherwise it's a compiler bug.
|
||||||
@ -253,7 +260,6 @@ def test_ast_bad_get():
|
|||||||
"Make sure AST can't compile invalid get"
|
"Make sure AST can't compile invalid get"
|
||||||
cant_compile("(get)")
|
cant_compile("(get)")
|
||||||
cant_compile("(get 1)")
|
cant_compile("(get 1)")
|
||||||
cant_compile("(get 1 2 3)")
|
|
||||||
|
|
||||||
|
|
||||||
def test_ast_good_slice():
|
def test_ast_good_slice():
|
||||||
@ -295,9 +301,9 @@ def test_ast_bad_assoc():
|
|||||||
|
|
||||||
def test_ast_bad_with():
|
def test_ast_bad_with():
|
||||||
"Make sure AST can't compile invalid with"
|
"Make sure AST can't compile invalid with"
|
||||||
cant_compile("(with)")
|
cant_compile("(with*)")
|
||||||
cant_compile("(with [])")
|
cant_compile("(with* [])")
|
||||||
cant_compile("(with [] (pass))")
|
cant_compile("(with* [] (pass))")
|
||||||
|
|
||||||
|
|
||||||
def test_ast_valid_while():
|
def test_ast_valid_while():
|
||||||
@ -305,14 +311,14 @@ def test_ast_valid_while():
|
|||||||
can_compile("(while foo bar)")
|
can_compile("(while foo bar)")
|
||||||
|
|
||||||
|
|
||||||
def test_ast_valid_foreach():
|
def test_ast_valid_for():
|
||||||
"Make sure AST can compile valid foreach"
|
"Make sure AST can compile valid for"
|
||||||
can_compile("(foreach [a 2])")
|
can_compile("(for [a 2] (print a))")
|
||||||
|
|
||||||
|
|
||||||
def test_ast_invalid_foreach():
|
def test_ast_invalid_for():
|
||||||
"Make sure AST can't compile invalid foreach"
|
"Make sure AST can't compile invalid for"
|
||||||
cant_compile("(foreach [a 1] (else 1 2))")
|
cant_compile("(for* [a 1] (else 1 2))")
|
||||||
|
|
||||||
|
|
||||||
def test_ast_expression_basics():
|
def test_ast_expression_basics():
|
||||||
@ -423,8 +429,54 @@ def test_compile_error():
|
|||||||
"""Ensure we get compile error in tricky cases"""
|
"""Ensure we get compile error in tricky cases"""
|
||||||
try:
|
try:
|
||||||
can_compile("(fn [] (= 1))")
|
can_compile("(fn [] (= 1))")
|
||||||
except HyCompileError as e:
|
except HyTypeError as e:
|
||||||
assert(str(e)
|
assert(e.message == "`=' needs at least 2 arguments, got 1.")
|
||||||
== "`=' needs at least 2 arguments, got 1 (line 1, column 8)")
|
|
||||||
else:
|
else:
|
||||||
assert(False)
|
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.importer import import_file_to_module, import_buffer_to_ast, MetaLoader
|
||||||
|
from hy.errors import HyTypeError
|
||||||
import os
|
import os
|
||||||
import ast
|
import ast
|
||||||
|
|
||||||
@ -27,3 +28,16 @@ def test_imports():
|
|||||||
|
|
||||||
assert _import_test() == "Error"
|
assert _import_test() == "Error"
|
||||||
assert _import_test() is not None
|
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) 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
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
# copy of this software and associated documentation files (the "Software"),
|
# 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.symbol import HySymbol
|
||||||
from hy.models.string import HyString
|
from hy.models.string import HyString
|
||||||
from hy.models.dict import HyDict
|
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
|
from hy.lex import LexException, PrematureEndOfInput, tokenize
|
||||||
|
|
||||||
@ -252,3 +255,82 @@ def test_complex():
|
|||||||
assert entry == HyComplex("1.0j")
|
assert entry == HyComplex("1.0j")
|
||||||
entry = tokenize("(j)")[0][0]
|
entry = tokenize("(j)")[0][0]
|
||||||
assert entry == HySymbol("j")
|
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():
|
def test_list_add():
|
||||||
|
"""Check that adding two HyLists generates a HyList"""
|
||||||
a = HyList([1, 2, 3])
|
a = HyList([1, 2, 3])
|
||||||
b = HyList([3, 4, 5])
|
b = HyList([3, 4, 5])
|
||||||
c = a + b
|
c = a + b
|
||||||
assert c == [1, 2, 3, 3, 4, 5]
|
assert c == [1, 2, 3, 3, 4, 5]
|
||||||
assert c.__class__ == HyList
|
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]
|
(defn assert-equal [x y]
|
||||||
(assert (= 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 []
|
(defn test-cycle []
|
||||||
"NATIVE: testing cycle"
|
"NATIVE: testing cycle"
|
||||||
(assert-equal (list (cycle [])) [])
|
(assert-equal (list (cycle [])) [])
|
||||||
@ -115,6 +123,13 @@
|
|||||||
(try (even? None)
|
(try (even? None)
|
||||||
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
|
(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 []
|
(defn test-filter []
|
||||||
"NATIVE: testing the filter function"
|
"NATIVE: testing the filter function"
|
||||||
(setv res (list (filter pos? [ 1 2 3 -4 5])))
|
(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])))
|
(setv res (list (filter none? [1 2 None 3 4 None 4 6])))
|
||||||
(assert-equal res [None None]))
|
(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? []
|
(defn test-float? []
|
||||||
"NATIVE: testing the float? function"
|
"NATIVE: testing the float? function"
|
||||||
(assert-true (float? 4.2))
|
(assert-true (float? 4.2))
|
||||||
@ -141,6 +176,24 @@
|
|||||||
(assert-true (float? -3.2))
|
(assert-true (float? -3.2))
|
||||||
(assert-false (float? "foo")))
|
(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 []
|
(defn test-inc []
|
||||||
"NATIVE: testing the inc function"
|
"NATIVE: testing the inc function"
|
||||||
(assert-equal 3 (inc 2))
|
(assert-equal 3 (inc 2))
|
||||||
@ -163,7 +216,7 @@
|
|||||||
(assert-false (instance? Foo2 foo))
|
(assert-false (instance? Foo2 foo))
|
||||||
(assert-true (instance? Foo foo3))
|
(assert-true (instance? Foo foo3))
|
||||||
(assert-true (instance? float 1.0))
|
(assert-true (instance? float 1.0))
|
||||||
(assert-true (instance? int 3))
|
(assert-true (instance? int (int 3)))
|
||||||
(assert-true (instance? str (str "hello"))))
|
(assert-true (instance? str (str "hello"))))
|
||||||
|
|
||||||
(defn test-integer? []
|
(defn test-integer? []
|
||||||
@ -171,10 +224,20 @@
|
|||||||
(assert-true (integer? 0))
|
(assert-true (integer? 0))
|
||||||
(assert-true (integer? 3))
|
(assert-true (integer? 3))
|
||||||
(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? 4.2))
|
||||||
(assert-false (integer? None))
|
(assert-false (integer? None))
|
||||||
(assert-false (integer? "foo")))
|
(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 []
|
(defn test-iterable []
|
||||||
"NATIVE: testing iterable? function"
|
"NATIVE: testing iterable? function"
|
||||||
;; should work for a string
|
;; should work for a string
|
||||||
@ -259,6 +322,15 @@
|
|||||||
(assert-false (none? 0))
|
(assert-false (none? 0))
|
||||||
(assert-false (none? "")))
|
(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 []
|
(defn test-nth []
|
||||||
"NATIVE: testing the nth function"
|
"NATIVE: testing the nth function"
|
||||||
(assert-equal 2 (nth [1 2 4 7] 1))
|
(assert-equal 2 (nth [1 2 4 7] 1))
|
||||||
@ -334,6 +406,13 @@
|
|||||||
(assert-equal 2 (second [1 2]))
|
(assert-equal 2 (second [1 2]))
|
||||||
(assert-equal 3 (second [2 3 4])))
|
(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? []
|
(defn test-string? []
|
||||||
"NATIVE: testing string?"
|
"NATIVE: testing string?"
|
||||||
(assert-true (string? "foo"))
|
(assert-true (string? "foo"))
|
||||||
@ -391,3 +470,11 @@
|
|||||||
(assert-equal res [None None])
|
(assert-equal res [None None])
|
||||||
(setv res (list (take-while (fn [x] (not (none? x))) [1 2 3 4 None 5 6 None 7])))
|
(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]))
|
(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)))
|
(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 []
|
(defn test-while-loop []
|
||||||
"NATIVE: test while loops?"
|
"NATIVE: test while loops?"
|
||||||
(setv count 5)
|
(setv count 5)
|
||||||
@ -86,9 +126,10 @@
|
|||||||
|
|
||||||
(defn test-is []
|
(defn test-is []
|
||||||
"NATIVE: test is can deal with None"
|
"NATIVE: test is can deal with None"
|
||||||
(setv a null)
|
(setv a nil)
|
||||||
(assert (is a null))
|
(assert (is a nil))
|
||||||
(assert (is-not a "b")))
|
(assert (is-not a "b"))
|
||||||
|
(assert (none? a)))
|
||||||
|
|
||||||
|
|
||||||
(defn test-branching []
|
(defn test-branching []
|
||||||
@ -129,7 +170,16 @@
|
|||||||
(defn test-index []
|
(defn test-index []
|
||||||
"NATIVE: Test that dict access works"
|
"NATIVE: Test that dict access works"
|
||||||
(assert (= (get {"one" "two"} "one") "two"))
|
(assert (= (get {"one" "two"} "one") "two"))
|
||||||
(assert (= (get [1 2 3 4 5] 1) 2)))
|
(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 []
|
(defn test-lambda []
|
||||||
@ -152,7 +202,39 @@
|
|||||||
(assert (= (kwapply (kwtest) {"one" "two"}) {"one" "two"}))
|
(assert (= (kwapply (kwtest) {"one" "two"}) {"one" "two"}))
|
||||||
(setv mydict {"one" "three"})
|
(setv mydict {"one" "three"})
|
||||||
(assert (= (kwapply (kwtest) mydict) mydict))
|
(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 []
|
(defn test-dotted []
|
||||||
@ -407,43 +489,43 @@
|
|||||||
|
|
||||||
(defn test-context []
|
(defn test-context []
|
||||||
"NATIVE: test with"
|
"NATIVE: test with"
|
||||||
(with [fd (open "README.md" "r")] (assert fd))
|
(with [[fd (open "README.md" "r")]] (assert fd))
|
||||||
(with [(open "README.md" "r")] (do)))
|
(with [[(open "README.md" "r")]] (do)))
|
||||||
|
|
||||||
|
|
||||||
(defn test-with-return []
|
(defn test-with-return []
|
||||||
"NATIVE: test that with returns stuff"
|
"NATIVE: test that with returns stuff"
|
||||||
(defn read-file [filename]
|
(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")))))
|
(assert (!= 0 (len (read-file "README.md")))))
|
||||||
|
|
||||||
|
|
||||||
(defn test-for-doodle []
|
(defn test-for-doodle []
|
||||||
"NATIVE: test for-do"
|
"NATIVE: test for-do"
|
||||||
(do (do (do (do (do (do (do (do (do (setv (, x y) (, 0 0)))))))))))
|
(do (do (do (do (do (do (do (do (do (setv (, x y) (, 0 0)))))))))))
|
||||||
(foreach [- [1 2]]
|
(for [- [1 2]]
|
||||||
(do
|
(do
|
||||||
(setv x (+ x 1))
|
(setv x (+ x 1))
|
||||||
(setv y (+ y 1))))
|
(setv y (+ y 1))))
|
||||||
(assert (= y x 2)))
|
(assert (= y x 2)))
|
||||||
|
|
||||||
|
|
||||||
(defn test-foreach-else []
|
(defn test-for-else []
|
||||||
"NATIVE: test foreach else"
|
"NATIVE: test for else"
|
||||||
(let [[x 0]]
|
(let [[x 0]]
|
||||||
(foreach [a [1 2]]
|
(for* [a [1 2]]
|
||||||
(setv x (+ x a))
|
(setv x (+ x a))
|
||||||
(else (setv x (+ x 50))))
|
(else (setv x (+ x 50))))
|
||||||
(assert (= x 53)))
|
(assert (= x 53)))
|
||||||
|
|
||||||
(let [[x 0]]
|
(let [[x 0]]
|
||||||
(foreach [a [1 2]]
|
(for* [a [1 2]]
|
||||||
(setv x (+ x a))
|
(setv x (+ x a))
|
||||||
(else))
|
(else))
|
||||||
(assert (= x 3))))
|
(assert (= x 3))))
|
||||||
|
|
||||||
|
|
||||||
(defn test-comprehensions []
|
(defn test-list-comprehensions []
|
||||||
"NATIVE: test list comprehensions"
|
"NATIVE: test list comprehensions"
|
||||||
(assert (= (list-comp (* x 2) (x (range 2))) [0 2]))
|
(assert (= (list-comp (* x 2) (x (range 2))) [0 2]))
|
||||||
(assert (= (list-comp (* x 2) (x (range 4)) (% x 2)) [2 6]))
|
(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])))
|
(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 []
|
(defn test-defn-order []
|
||||||
"NATIVE: test defn evaluation order"
|
"NATIVE: test defn evaluation order"
|
||||||
(setv acc [])
|
(setv acc [])
|
||||||
@ -551,6 +668,12 @@
|
|||||||
(assert (= -_- "what?"))))
|
(assert (= -_- "what?"))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn test-symbol-question-mark []
|
||||||
|
"NATIVE: test foo? -> is_foo behavior"
|
||||||
|
(let [[foo? "nachos"]]
|
||||||
|
(assert (= is_foo "nachos"))))
|
||||||
|
|
||||||
|
|
||||||
(defn test-and []
|
(defn test-and []
|
||||||
"NATIVE: test the and function"
|
"NATIVE: test the and function"
|
||||||
(let [[and123 (and 1 2 3)]
|
(let [[and123 (and 1 2 3)]
|
||||||
@ -775,7 +898,93 @@
|
|||||||
(.append y x))
|
(.append y x))
|
||||||
(assert (= y [5])))
|
(assert (= y [5])))
|
||||||
|
|
||||||
|
|
||||||
(defn test-empty-list []
|
(defn test-empty-list []
|
||||||
"Evaluate an empty list to a []"
|
"Evaluate an empty list to a []"
|
||||||
(assert (= () [])))
|
(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 []
|
(setv test_mult (fn []
|
||||||
"NATIVE: Test multiplication."
|
"NATIVE: Test multiplication."
|
||||||
(assert (= 4 (square 2)))))
|
(assert (= 4 (square 2)))
|
||||||
|
(assert (= 8 (* 8)))
|
||||||
|
(assert (= 1 (*)))))
|
||||||
|
|
||||||
|
|
||||||
(setv test_sub (fn []
|
(setv test_sub (fn []
|
||||||
@ -19,7 +21,9 @@
|
|||||||
|
|
||||||
(setv test_add (fn []
|
(setv test_add (fn []
|
||||||
"NATIVE: Test addition"
|
"NATIVE: Test addition"
|
||||||
(assert (= 4 (+ 1 1 1 1)))))
|
(assert (= 4 (+ 1 1 1 1)))
|
||||||
|
(assert (= 8 (+ 8)))
|
||||||
|
(assert (= 0 (+)))))
|
||||||
|
|
||||||
|
|
||||||
(setv test_div (fn []
|
(setv test_div (fn []
|
||||||
@ -132,3 +136,7 @@
|
|||||||
(let [[x 1]]
|
(let [[x 1]]
|
||||||
(^= x 1)
|
(^= x 1)
|
||||||
(assert (= x 0))))
|
(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])
|
(defmacro a-list [] [1 2])
|
||||||
(assert (= (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})
|
(defmacro a-dict [] {1 2})
|
||||||
(assert (= (a-dict) {1 2}))
|
(assert (= (a-dict) {1 2}))
|
||||||
|
|
||||||
|
(defmacro a-none [])
|
||||||
|
(assert (= (a-none) None))
|
||||||
|
|
||||||
; A macro calling a previously defined function
|
; A macro calling a previously defined function
|
||||||
(eval-when-compile
|
(eval-when-compile
|
||||||
(defn foo [x y]
|
(defn foo [x y]
|
||||||
@ -53,7 +59,7 @@
|
|||||||
(defn test-midtree-yield-in-for []
|
(defn test-midtree-yield-in-for []
|
||||||
"NATIVE: test yielding in a for with a return"
|
"NATIVE: test yielding in a for with a return"
|
||||||
(defn kruft-in-for []
|
(defn kruft-in-for []
|
||||||
(for [i (range 5)]
|
(for* [i (range 5)]
|
||||||
(yield i))
|
(yield i))
|
||||||
(+ 1 2)))
|
(+ 1 2)))
|
||||||
|
|
||||||
@ -69,7 +75,7 @@
|
|||||||
(defn test-multi-yield []
|
(defn test-multi-yield []
|
||||||
"NATIVE: testing multiple yields"
|
"NATIVE: testing multiple yields"
|
||||||
(defn multi-yield []
|
(defn multi-yield []
|
||||||
(for [i (range 3)]
|
(for* [i (range 3)]
|
||||||
(yield i))
|
(yield i))
|
||||||
(yield "a")
|
(yield "a")
|
||||||
(yield "end"))
|
(yield "end"))
|
||||||
@ -91,11 +97,10 @@
|
|||||||
(assert initialized)
|
(assert initialized)
|
||||||
(assert (test-initialized))
|
(assert (test-initialized))
|
||||||
|
|
||||||
|
|
||||||
(defn test-yield-from []
|
(defn test-yield-from []
|
||||||
"NATIVE: testing yield from"
|
"NATIVE: testing yield from"
|
||||||
(defn yield-from-test []
|
(defn yield-from-test []
|
||||||
(for [i (range 3)]
|
(for* [i (range 3)]
|
||||||
(yield i))
|
(yield i))
|
||||||
(yield-from [1 2 3]))
|
(yield-from [1 2 3]))
|
||||||
(assert (= (list (yield-from-test)) [0 1 2 1 2 3])))
|
(assert (= (list (yield-from-test)) [0 1 2 1 2 3])))
|
||||||
@ -104,3 +109,92 @@
|
|||||||
(import sys)
|
(import sys)
|
||||||
(assert (= (get sys.version_info 0)
|
(assert (= (get sys.version_info 0)
|
||||||
(if-python2 2 3))))
|
(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 2) 2))
|
||||||
(assert (= (unless false 1 3) 3))
|
(assert (= (unless false 1 3) 3))
|
||||||
(assert (= (unless true 2) null))
|
(assert (= (unless true 2) null))
|
||||||
|
(assert (= (unless true 2) nil))
|
||||||
(assert (= (unless (!= 1 2) 42) null))
|
(assert (= (unless (!= 1 2) 42) null))
|
||||||
|
(assert (= (unless (!= 1 2) 42) nil))
|
||||||
(assert (= (unless (!= 2 2) 42) 42)))
|
(assert (= (unless (!= 2 2) 42) 42)))
|
||||||
|
@ -5,4 +5,6 @@
|
|||||||
(assert (= (when true 1 3) 3))
|
(assert (= (when true 1 3) 3))
|
||||||
(assert (= (when false 2) null))
|
(assert (= (when false 2) null))
|
||||||
(assert (= (when (= 1 2) 42) null))
|
(assert (= (when (= 1 2) 42) null))
|
||||||
|
(assert (= (when false 2) nil))
|
||||||
|
(assert (= (when (= 1 2) 42) nil))
|
||||||
(assert (= (when (= 2 2) 42) 42)))
|
(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.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
|
from nose.plugins.skip import SkipTest
|
||||||
|
|
||||||
|
|
||||||
def run_cmd(cmd, stdin_data=None):
|
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
|
# Read stdout and stderr otherwise if the PIPE buffer is full, we might
|
||||||
# wait for ever…
|
# wait for ever…
|
||||||
while p.poll() is None:
|
while p.poll() is None:
|
||||||
stdout += str(p.stdout.read())
|
stdout += p.stdout.read().decode('utf-8')
|
||||||
stderr += str(p.stderr.read())
|
stderr += p.stderr.read().decode('utf-8')
|
||||||
return p.returncode, stdout, stderr
|
return p.returncode, stdout, stderr
|
||||||
|
|
||||||
|
|
||||||
@ -63,7 +64,7 @@ def test_bin_hy_cmd():
|
|||||||
|
|
||||||
ret = run_cmd("hy -c \"(koan\"")
|
ret = run_cmd("hy -c \"(koan\"")
|
||||||
assert ret[0] == 1
|
assert ret[0] == 1
|
||||||
assert "PrematureEndOfInput" in ret[1]
|
assert "Premature end of input" in ret[2]
|
||||||
|
|
||||||
|
|
||||||
def test_bin_hy_icmd():
|
def test_bin_hy_icmd():
|
||||||
@ -75,10 +76,12 @@ def test_bin_hy_icmd():
|
|||||||
assert "figlet" in output
|
assert "figlet" in output
|
||||||
|
|
||||||
|
|
||||||
def test_bin_hy_file():
|
def test_bin_hy_icmd_and_spy():
|
||||||
ret = run_cmd("hy eg/nonfree/halting-problem/halting.hy")
|
ret = run_cmd("hy -i \"(+ [] [])\" --spy", "(+ 1 1)")
|
||||||
assert ret[0] == 0
|
assert ret[0] == 0
|
||||||
assert "27" in ret[1]
|
output = ret[1]
|
||||||
|
|
||||||
|
assert "([] + [])" in output
|
||||||
|
|
||||||
|
|
||||||
def test_bin_hy_missing_file():
|
def test_bin_hy_missing_file():
|
||||||
@ -122,20 +125,16 @@ def test_bin_hyc_missing_file():
|
|||||||
|
|
||||||
|
|
||||||
def test_hy2py():
|
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
|
# and running this script this way doesn't work on Windows
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
return
|
raise SkipTest("doesn't work on Windows")
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
for dirpath, dirnames, filenames in os.walk("tests/native_tests"):
|
for dirpath, dirnames, filenames in os.walk("tests/native_tests"):
|
||||||
for f in filenames:
|
for f in filenames:
|
||||||
if f.endswith(".hy"):
|
if f.endswith(".hy"):
|
||||||
i += 1
|
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 ret[0] == 0, f
|
||||||
assert len(ret[1]) > 1, f
|
assert len(ret[1]) > 1, f
|
||||||
assert len(ret[2]) == 0, f
|
assert len(ret[2]) == 0, f
|
||||||
|
15
tox.ini
15
tox.ini
@ -5,8 +5,7 @@ commands = nosetests
|
|||||||
deps =
|
deps =
|
||||||
nose
|
nose
|
||||||
setuptools
|
setuptools
|
||||||
-e
|
rply
|
||||||
git+https://github.com/hylang/rply.git#egg=rply
|
|
||||||
|
|
||||||
[testenv:pypy]
|
[testenv:pypy]
|
||||||
commands = nosetests
|
commands = nosetests
|
||||||
@ -14,8 +13,7 @@ deps =
|
|||||||
astor
|
astor
|
||||||
nose
|
nose
|
||||||
setuptools
|
setuptools
|
||||||
-e
|
rply
|
||||||
git+https://github.com/hylang/rply.git#egg=rply
|
|
||||||
|
|
||||||
[testenv:py27]
|
[testenv:py27]
|
||||||
commands = nosetests
|
commands = nosetests
|
||||||
@ -23,8 +21,7 @@ deps =
|
|||||||
astor
|
astor
|
||||||
nose
|
nose
|
||||||
setuptools
|
setuptools
|
||||||
-e
|
rply
|
||||||
git+https://github.com/hylang/rply.git#egg=rply
|
|
||||||
|
|
||||||
[testenv:py26]
|
[testenv:py26]
|
||||||
deps =
|
deps =
|
||||||
@ -33,12 +30,10 @@ deps =
|
|||||||
setuptools
|
setuptools
|
||||||
unittest2
|
unittest2
|
||||||
importlib
|
importlib
|
||||||
-e
|
rply
|
||||||
git+https://github.com/hylang/rply.git#egg=rply
|
|
||||||
|
|
||||||
[testenv:flake8]
|
[testenv:flake8]
|
||||||
deps =
|
deps =
|
||||||
flake8
|
flake8
|
||||||
-e
|
rply
|
||||||
git+https://github.com/hylang/rply.git#egg=rply
|
|
||||||
commands = flake8 hy bin tests
|
commands = flake8 hy bin tests
|
||||||
|
Loading…
x
Reference in New Issue
Block a user