pax_global_header00006660000000000000000000000064146740127600014521gustar00rootroot0000000000000052 comment=5cdba24bf5d911091712a125214f864d2b576595 hy-1.0.0/000077500000000000000000000000001467401276000121375ustar00rootroot00000000000000hy-1.0.0/.dockerignore000066400000000000000000000000051467401276000146060ustar00rootroot00000000000000.git hy-1.0.0/.git-blame-ignore-revs000066400000000000000000000001221467401276000162320ustar00rootroot00000000000000ce6d23806ac4c91e678c34f24d0010829fbbfc94 1b8d0b4d9670ecbca944decc68e08643500ad3c7 hy-1.0.0/.github/000077500000000000000000000000001467401276000134775ustar00rootroot00000000000000hy-1.0.0/.github/workflows/000077500000000000000000000000001467401276000155345ustar00rootroot00000000000000hy-1.0.0/.github/workflows/tests.yml000066400000000000000000000042551467401276000174270ustar00rootroot00000000000000name: Tests on: [pull_request, push] jobs: pytest: # Run the test suite. strategy: fail-fast: false matrix: name-prefix: [''] os: [ubuntu-latest] python: [3.8, 3.9, '3.10', 3.11, 3.12, 3.13-dev, pypy-3.10, pyodide] include: # To keep the overall number of runs low, we test Windows and MacOS # only on the latest CPython. - name-prefix: 'win-' os: windows-latest python: 3.12 - name-prefix: 'mac-' os: macos-latest python: 3.12 name: ${{ format('{0}{1}', matrix.name-prefix, matrix.python) }} runs-on: ${{ matrix.os }} env: TERM: xterm-256color # This is needed to avoid a terminfo-related crash when # testing PyPy. PYTHONPATH: . steps: - run: git config --global core.autocrlf false - uses: actions/checkout@v4 - if: ${{ matrix.python != 'pyodide' }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - if: ${{ matrix.python == 'pyodide' }} uses: actions/setup-python@v5 with: python-version: 3.11 - if: ${{ matrix.python == 'pyodide' }} uses: actions/setup-node@v4 - name: Install shell: bash run: | if [[ ${{ matrix.python }} = pyodide ]] ; then npm install pyodide pip install pyodide-build pyodide venv .venv-pyodide source .venv-pyodide/bin/activate fi pip install . rm -r hy # We want to be sure we're testing the installed version, # instead of running from the source tree. pip install pytest - name: Test shell: bash run: | if [[ ${{ matrix.python }} = pyodide ]] ; then source .venv-pyodide/bin/activate fi python -m pytest tests docs: # Try building the manual, ensuring that Sphinx doesn't produce # any warnings. runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install run: | pip install . pip install -r requirements-dev.txt - run: sphinx-build -W -b html docs/ docs/_build/ hy-1.0.0/.gitignore000066400000000000000000000001521467401276000141250ustar00rootroot00000000000000*.pyc .*.sw? *hy*egg* *pyreadline*egg* .tox *pycache* dist .coverage build/ /.cache /.pytest_cache /.eggs hy-1.0.0/.mailmap000066400000000000000000000014611467401276000135620ustar00rootroot00000000000000Paul R. Tagliamonte Paul Tagliamonte Paul R. Tagliamonte Paul Tagliamonte Paul R. Tagliamonte Paul Tagliamonte Paul R. Tagliamonte Paul Tagliamonte Morten Linderud Foxboron James King agentultra James King J Kenneth King Abhishek L Bob Tolbert Bob Tolbert Guillermo Vaya Guillermo Vaya Gergely Nagy Gergely Nagy hy-1.0.0/.readthedocs.yaml000066400000000000000000000003201467401276000153610ustar00rootroot00000000000000version: 2 build: os: ubuntu-22.04 tools: python: "3.11" sphinx: builder: html configuration: docs/conf.py python: install: - method: pip path: . - requirements: requirements-dev.txt hy-1.0.0/AUTHORS000066400000000000000000000106341467401276000132130ustar00rootroot00000000000000* Paul Tagliamonte * Thomas Mashek * Amrut Joshi * Christopher Allan Webber * Will Kahn-Greene * James King * Julien Danjou * Nicolas Dandrimont * Gergely Nagy * Konrad Hinsen * Vladimir Gorbunov * John Jacobsen * Roger Erens * Thomas Ballinger * Morten Linderud * Guillermo Vayá * Bob Tolbert * Ralph Möritz * Josh McLaughlin * Berker Peksag * Henrique Carvalho Alves * Joe Hakim Rahme * Kenan Bölükbaşı * Abhishek Lekshmanan * Christopher Browne * Clinton N. Dreisbach * Duncan McGreggor * E. Anders Lannerback * Jack Laxson * Johan Euphrosine * Kevin Zita * Matt Fenwick * Sean B. Palmer * Thom Neale * Tuula Turto * Vasudev Kamath * Yuval Langer * Fatih Kadir Akın * Jack Hooper * Brian McKenna * Halit Alptekin * Richard Parsons * Sangho Na * Ryan Gonzalez * Brendan Curran-Johnson * Ivan Kozik * Allison Kaptur * Matthew Wampler-Doty * Tianon Gravi * Ian Denhardt * Ruslan Prokopiev * Alexander Artemenko * Ed Singleton * Kevin Yap * Matthías Páll Gissurarson * Nathan Woodrow * Adam Schwalm * Ilia Choly * Shrayas Rajagopal * Shenyang Zhao * Zack M. Davis * Nicolas Pénet * Adrià Garriga Alonso * Antony Woods * Matthew Egan Odendahl * Tim Martin * Johnathon Mlady * Andrew Savchyn * Lev Kravinsky * Luna Lunapiena * Jakub Wilk * Kodi Arfer * Karan Sharma * Sergey Sobko * Philip Xu * Charles de Lacombe * John Patterson * Kai Lüke * Neil Lindquist * David Schaefer * Jordan Danford * Andrew Silva * Zaheer Soebhan * Rob Day * Eric Kaschalk * Yoan Tournade * Simon Gomizelj * Yigong Wang * Oskar Kvist * Brandon T. Willard * Andrew Miller * Tristan de Cacqueray * Sören Tempel * Noah Snelson * Adam Porter * Gábor Lipták * Raymund MARTINEZ * Zepeng Zhang * Joseph Egan * Xi Jin * Alexey Yurchenko * Allie Jo Casey * Joshua Munn * Peter Andreev * Sunjay Cauligi * David Tscheppen * Dmitry Ivanov * Andrey Vlasovskikh * Joseph LaFreniere * Daniel Tan * Zhan Tang hy-1.0.0/CONTRIBUTING.rst000066400000000000000000000166151467401276000146110ustar00rootroot00000000000000Contributor guidelines ====================== Contributions are welcome and greatly appreciated. Every little bit helps in making Hy better. Potential contributions include: - Reporting and fixing bugs. - Requesting features. - Adding features. - Writing tests for outstanding bugs or untested features. - You can mark tests that Hy can't pass yet as xfail_. - Cleaning up the code. - Improving the documentation. - Answering questions on `the Github Discussions page`_ or `Stack Overflow`_. - Evangelizing for Hy in your organization, user group, conference, or bus stop. Issues ~~~~~~ In order to report bugs or request features, search the `issue tracker`_ to check for a duplicate. (If you're reporting a bug, make sure you can reproduce it with the very latest, bleeding-edge version of Hy from the ``master`` branch on GitHub. Bugs in stable versions of Hy are fixed on ``master`` before the fix makes it into a new stable release.) If there aren't any duplicates, then you can make a new issue. It's totally acceptable to create an issue when you're unsure whether something is a bug or not. We'll help you figure it out. Use the same issue tracker to report problems with the documentation. Pull requests ~~~~~~~~~~~~~ Submit proposed changes to the code or documentation as pull requests (PRs) on GitHub_. Git can be intimidating and confusing to the uninitiated. `This getting-started guide`_ may be helpful. However, if you're overwhelmed by Git, GitHub, or the rules below, don't sweat it. We want to keep the barrier to contribution low, so we're happy to help you with these finicky things or do them for you if necessary. Deciding what to do ------------------- If you're proposing a major change to the Hy language, or you're unsure of the proposed change, create an issue to discuss it before you write any code. This will allow others to give feedback on your idea, and it can avoid wasted work. Commit formatting ----------------- Many PRs are small enough that only one commit is necessary, but bigger ones should be organized into logical units as separate commits. PRs should be free of merge commits and commits that fix or revert other commits in the same PR (``git rebase`` is your friend). Avoid committing spurious whitespace changes. Don't commit comments tagged with things like "FIXME", "TODO", or "XXX". Ideas for how the code or documentation should change go in the issues list, not the code or documentation itself. The first line of a commit message should describe the overall change in 50 characters or less. If you wish to add more information, separate it from the first line with a blank line. Testing ------- Tests can be run by executing ``pytest`` in the root of this repository. New features and bug fixes should be tested. If you've caused an xfail_ test to start passing, remove the xfail mark. If you're testing a bug that has a GitHub issue, include a comment with the URL of the issue. No PR may be merged if it causes any tests to fail. The byte-compiled versions of the test files can be purged using ``git clean -dfx tests/``. If you want to run the tests while skipping the slow ones in ``test_bin.py``, use ``pytest --ignore=tests/test_bin.py``. Documentation ------------- Generally, new features deserve coverage in the manual, either by editing the manual files directly or by changing docstrings that get included in the manual. To render the manual, install its dependencies with ``pip install -r requirements-dev.txt`` and then use the command ``cd docs; sphinx-build . _build -b html``. NEWS and AUTHORS ---------------- If you're making user-visible changes to the code, add one or more items describing them to the NEWS file. Finally, add yourself to the AUTHORS file (as a separate commit): you deserve it. :) The PR itself ------------- PRs should ask to merge a new branch that you created for the PR into hylang/hy's ``master`` branch, and they should have as their origin the most recent commit possible. If the PR fulfills one or more issues, then the body text of the PR (or the commit message for any of its commits) should say "Fixes #123" or "Closes #123" for each affected issue number. Use this exact (case-insensitive) wording, because when a PR containing such text is merged, GitHub automatically closes the mentioned issues, which is handy. Conversely, avoid this exact language if you want to mention an issue without closing it (because e.g. you've partly but not entirely fixed a bug). There are two situations in which a PR is allowed to be merged: 1. When it is approved by **two** members of Hy's core team other than the PR's author. Changes to the documentation, or trivial changes to code, need only **one** approving member. 2. When the PR is at least **three days** old and **no** member of the Hy core team has expressed disapproval of the PR in its current state. Anybody on the Hy core team may perform the merge. Merging should create a merge commit (don't squash unnecessarily, because that would remove separation between logically separate commits, and don't fast-forward, because that would throw away the history of the commits as a separate branch), which should include the PR number in the commit message. The typical workflow for this is to run the following commands on your own machine, then press the merge button on GitHub. .. code-block:: console $ git checkout master $ git pull $ git checkout $PR_BRANCH $ git fetch $ get reset --hard $REMOTE/$PR_BRANCH $ git rebase master $ git push -f Contributor Code of Conduct =========================== As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. This Code of Conduct is adapted from the `Contributor Covenant`_, version 1.1.0, available at http://contributor-covenant.org/version/1/1/0/. .. _Contributor Covenant: http://contributor-covenant.org .. _issue tracker: https://github.com/hylang/hy/issues .. _GitHub: https://github.com/hylang/hy .. _This getting-started guide: http://rogerdudler.github.io/git-guide/ .. _the Github Discussions page: https://github.com/hylang/hy/discussions .. _Stack Overflow: https://stackoverflow.com/questions/tagged/hy .. _xfail: https://docs.pytest.org/en/latest/skipping.html#mark-a-test-function-as-expected-to-fail hy-1.0.0/Dockerfile000066400000000000000000000002561467401276000141340ustar00rootroot00000000000000# Base image # # VERSION 0.2 FROM python:3 MAINTAINER Paul R. Tagliamonte ADD . /opt/hylang/hy RUN pip3 install -e /opt/hylang/hy CMD ["hy"] hy-1.0.0/LICENSE000066400000000000000000000023641467401276000131510ustar00rootroot00000000000000Copyright 2024 the authors. Portions of setup.py, copyright 2016 Jason R Coombs . 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. Portions of hy/contrib/pprint.hy copyright 2020 Python Software Foundation, licensed under the Python Software Foundation License Version 2. hy-1.0.0/MANIFEST.in000066400000000000000000000001361467401276000136750ustar00rootroot00000000000000include AUTHORS include LICENSE include NEWS.rst include README.md include fastentrypoints.py hy-1.0.0/NEWS.rst000066400000000000000000001714511467401276000134560ustar00rootroot00000000000000.. default-role:: code 1.0.0 ("Afternoon Review", released 2024-09-22) ====================================================================== Supports Python 3.8 – Python 3.13 See also the announcement post for this release (to be linked). Bug Fixes ------------------------------ * Fixed a crash on Python 3.12.6. * Keyword objects can now be compared to each other with `<` etc. * The order of evaluation in multi-item `with`\s now matches that of nested one-item `with`\s. * Fixed a bug in which the REPL misinterpreted the symbol `pass`. 0.29.0 (released 2024-05-20) ============================= Removals ------------------------------ * `hy.disassemble` has been removed. * `(defn/a …)` is now `(defn :async …)`. * `(fn/a …)` is now `(fn :async …)`. * `(with/a […] …)` is now `(with [:async …] …)`. * As with `for`, `:async` must precede each name to be bound asynchronously, because you can mix synchronous and asynchronous types. * `(yield-from …)` is now `(yield :from …)`. New Features ------------------------------ * You can now set `repl-ps1` and `repl-ps2` in your `HYSTARTUP` to customize `sys.ps1` and `sys.ps2` for the Hy REPL. Bug Fixes ------------------------------ * Tracebacks now point to the correct code in more cases. * `help` should no longer crash when objects are missing docstrings. * `hy -i < script.hy` now executes `script.hy` inside the REPL environment, like Python. 0.28.0 (released 2024-01-05) ============================= Removals ------------------------------ * `doc` has been removed. Use `(help (get-macro foo))` or `(help (get-macro :reader foo))` instead. * `delmacro` has been removed. Use `(eval-when-compile (del (get _hy_macros (hy.mangle "foo"))))` instead. * `hy.reserved` has been removed. Use `(.keys (builtins._hy_macros))` or Python's built-in `keyword` module instead. * The environment variables `HY_DEBUG` and `HY_FILTER_INTERNAL_ERRORS` have been replaced with `HY_SHOW_INTERNAL_ERRORS`. Other Breaking Changes ------------------------------ * `defmacro` and `require` can now define macros locally instead of only module-wide. * When a macro is `require`\d from another module, that module is no longer implicitly included when checking for further macros in the expansion. * `hy.M` has been renamed to `hy.I`. * `hy.eval` has been overhauled to be more like Python's `eval`. It also has a new parameter `macros`. * `hy.macroexpand` and `hy.macroexpand-1` have been overhauled and generalized to include more of the features of `hy.eval`. * `hy` now only implicitly launches a REPL if standard input is a TTY. * `hy -i` has been overhauled to work as a flag like `python3 -i`. * `hy2py` now requires `-m` to specify modules, and uses the same `sys.path` rules as Python when parsing a module vs a standalone script. * New macro `deftype`. * New macro `get-macro`. * New macro `local-macros`. New Features ------------------------------ * New syntax `(hy.R.aaa/bbb.m …)` for calling the macro `m` from the module `aaa.bbb` without bringing `m` or `aaa.bbb` into scope. * `nonlocal` now also works for globally defined names. * `defn`, `defn/a`, and `defclass` now support type parameters. * `HyReader` now has an optional parameter to install existing reader macros from the calling module. * New pragma `warn-on-core-shadow`. Misc. Improvements ------------------------------ * Some syntax errors raised by core macros now have more informative messages. * Logical operators now compile to simpler Python code in some cases. Bug Fixes ------------------------------ * Implicit returns are now disabled in async generators. * Fixed parsing of infinite and NaN imaginary literals with an uppercase "J". * Double quotes inside of bracketed f-strings are now properly handled. * Fixed incomplete recognition of macro calls with a unary dotted head like `((. defn) f [])`. * `~@ #*` now produces a syntax error instead of a nonsensical result. * `nonlocal` now works for top-level `let`-bound names. * `require` now warns when you shadow a core macro, like `defmacro` already did. * Fixed `hy.eval` failing on `defreader` or `require` forms that install a new reader. * The parameter `result-ok` that was mistakenly included in the signature of `hy.macroexpand` is now gone. * `hy -i` with a filename now skips shebang lines. 0.27.0 (released 2023-07-06) ============================= Removals ------------------------------ * Python 3.7 is no longer supported. Other Breaking Changes ------------------------------ * Reader macros now always read a full identifier after the initial `#`. Thus, `#*foo` is now parsed as a call to the reader macro named `*foo`; to unpack a variable named `foo`, say `#* foo`. * The names of reader macros names are no longer mangled. * Question marks (`?`) are no longer mangled specially, so `foo?` now mangles to `hyx_fooXquestion_markX` instead of `is_foo`. * `hy2py`'s recursive mode now expects a module name as input, not any old directory. You must be in the parent directory of the module directory. New Features ------------------------------ * Python 3.12 is now supported. * New built-in object `hy.M` for easy imports in macros. * `cut` now has a function version in `hy.pyops`. * The `py` macro now implicitly parenthesizes the input code, so Python's indentation restrictions don't apply. * `try` no longer requires `except`, `except*`, or `finally`, and it allows `else` even without `except` or `except*`. * `nonlocal` and `global` can now be called with no arguments, in which case they're no-ops. * For easier reading, `hy --spy` now prints a delimiter after the Python equivalent of your code, before the result of evaluating the code. Bug Fixes ------------------------------ * Fixed an installation failure in some situations when version lookup fails. * Fixed some bugs with traceback pointing. * Fixed some bugs with escaping in bracket f-strings * The parser no longer looks for shebangs in the REPL or `hy -c`. * `require` with relative module names should now work correctly with `hy -m`, as well as `hy2py`'s recursive mode. * `hy.models.Symbol` no longer allows constructing a symbol beginning with `#`. 0.26.0 (released 2023-02-08) ============================= Removals ------------------------------ * Coloring error messages and Python representations for models is no longer supported. (Thus, Hy no longer depends on `colorama`.) Other Breaking Changes ------------------------------ * Various warts have been smoothed over in the syntax of `'`, \`, `~`, and `~@`: * Whitespace is now allowed after these syntactic elements. Thus one can apply `~` to a symbol whose name begins with "@". * \` and `~` are no longer allowed in identifiers. (This was already the case for `'`.) * The bitwise NOT operator `~` has been renamed to `bnot`. * Dotted identifiers like `foo.bar` and `.sqrt` now parse as expressions (like `(. foo bar)` and `(. None sqrt)`) instead of symbols. Some odd cases like `foo.` and `foo..bar` are now syntactically illegal. * New macro `do-mac`. * New macro `pragma` (although it doesn't do anything useful yet). * `hy.cmdline.HyREPL` is now `hy.REPL`. * Redundant scripts named `hy3`, `hyc3`, and `hy2py3` are no longer installed. Use `hy`, `hyc`, and `hy2py` instead. New Features ------------------------------ * Pyodide is now officially supported. * `.`, `..`, etc. are now usable as ordinary symbols (with the remaining special rule that `...` compiles to `Ellipsis`). * On Pythons ≥ 3.7, Hy modules can now be imported from ZIP archives in the same way as Python modules, via `zipimport`_. * `hy2py` has a new command-line option `--output`. * `hy2py` can now operate recursively on a directory. Bug Fixes ------------------------------ * `hy.REPL` now restores the global values it changes (such as `sys.ps1`) after `hy.REPL.run` terminates. * `hy.REPL` no longer mixes up Hy's and Python's Readline histories when run inside Python's REPL. * Fixed `hy.repr` of non-compilable uses of sugared macros, such as `(quote)` and `(quote 1 2)`. .. _zipimport: https://docs.python.org/3.11/library/zipimport.html 0.25.0 (released 2022-11-08) ============================== Breaking Changes ------------------------------ * `dfor` no longer requires brackets around its final arguments, so `(dfor x (range 5) [x (* 2 x)])` is now `(dfor x (range 5) x (* 2 x))`. * `except*` (PEP 654) is now recognized in `try`, and a placeholder macro for `except*` has been added. Bug Fixes ------------------------------ * `__file__` should now be set the same way as in Python. * `\N{…}` escape sequences are now recognized in f-strings. * Fixed a bug with `python -O` where assertions were still partly evaluated. * Fixed `hy.repr` of `slice` objects with non-integer arguments. New Features ------------------------------ * Python 3.11 is now supported. Misc. Improvements ------------------------------ * `hyc` now requires a command-line argument. * `hyc` prints each path it writes bytecode to, and its messages now go to standard error instead of standard output. 0.24.0 (released 2022-06-23) ============================== This release is a direct successor to 1.0a4. We've returned to 0.* version numbers to work around the inflexibility of PyPI and pip regarding the default version to install. (We skipped some version numbers because this release is several major releases since 0.20.0.) Sorry for the mess. Removals ------------------------------ * Tag macros have been removed. Use reader macros instead, rewriting `(defmacro "#foo" [arg] …)` as `(defreader foo (setv arg (.parse-one-form &reader)) …)`. * `with-decorator` and `#@` have been removed in favor of decorator lists (see below). * Fraction literals have been removed. Use `fractions.Fraction` instead. * Unrecognized backslash escapes in string and byte literals are no longer allowed. (They've been `deprecated in Python since 3.6 `_.) * A bare `#` is no longer a legal symbol. * `u` is no longer allowed as a string prefix. (It had no effect, anyway.) * `hy.read-str` has been removed. Use `hy.read`, which now accepts strings, instead. Other Breaking Changes ------------------------------ * Tuples are now indicated with `#( … )`, as in `#(1 2 3)`, instead of `(, … )`, as in `(, 1 2 3)`. * Tuples have their own model type, `hy.models.Tuple`, instead of being represented as `Expression`\s. * `if` now requires all three arguments. For the two-argument case (i.e., with no else-clause), `when` is a drop-in replacement. * `cond` has a new unbracketed syntax:: (cond [a b] [x y z]) ; Old (cond a b x (do y z)) ; New * `defmacro` once again requires the macro name as a symbol, not a string literal. * Annotations are now indicated by `#^` instead of `^`. * `annotate` (but not `#^`) now takes the target first and the type second, as in `(annotate x int)`. * The way f-strings are parsed has changed, such that unescaped double quotes are now allowed inside replacement fields. * Non-ASCII whitespace is no longer ignored during tokenization like ASCII whitespace. * The mangling rules have been refined to account for Python's treatment of distinct names as referring to the same variable if they're NFKC-equivalent. Very little real code should be affected. * `hy.cmdline.run_repl` has been replaced with `hy.cmdline.HyREPL.run`. New Features ------------------------------ * Added user-defined reader macros, defined with `defreader`. * `defn` and `defclass` now allow a decorator list as their first argument. * `...` is now understood to refer to `Ellipsis`, as in Python. * Python reserved words are allowed once more as parameter names and keyword arguments. Hy includes a workaround for a CPython bug that prevents the generation of legal Python code for these cases (`cpython#90678`_). * New macro `export`. - Or you can set the variable `_hy_export_macros` to control what macros are collected by `(require module *)`. * New macro `delmacro`. * New function `hy.read_many`. * New function `hy.model_patterns.parse_if`. * New function `hy.model_patterns.in_tuple`. * Added a command-line option `-u` (or `--unbuffered`) per CPython. * Tab-completion in the REPL now attempts to unmangle names. Bug Fixes ------------------------------ * Fixed a crash when using keyword objects in `match`. * Fixed a scoping bug in comprehensions in `let` bodies. * Literal newlines (of all three styles) are now recognized properly in string and bytes literals. * `defmacro` no longer allows further arguments after `#* args`. * `!=` with model objects is now consistent with `=`. * Tracebacks from code parsed with `hy.read` now show source positions. * Elements of `builtins` such as `help` are no longer overridden until the REPL actually starts. * Readline is now imported only when necessary, to avoid triggering a CPython bug regarding the standard module `curses` (`cpython#46927`_). * Module names supplied to `hy -m` are now mangled. * Hy now precompiles its own Hy code during installation. .. _cpython#46927: https://github.com/python/cpython/issues/46927#issuecomment-1093418916 .. _cpython#90678: https://github.com/python/cpython/issues/90678 1.0a4 (released 2022-01-09) ============================== Removals ------------------------------ * Python 3.6 is no longer supported. Other Breaking Changes ------------------------------ * `import` and `require` no longer need outer brackets. `(import [foo [bar]])` is now `(import foo [bar])` and `(import [foo :as baz])` is now `(import foo :as baz)`. To import all names from a module, use `(import foo *)`. * Lots of objects (listed below) have been spun off to a new package called `Hyrule`_, from which you can `import` or `require` them. Thus Hy now brings only the `hy` module and a limited set of core macros into scope automatically. * Functions: `butlast`, `coll?`, `constantly`, `dec`, `destructure`, `distinct`, `drop-last`, `end-sequence`, `flatten`, `inc`, `macroexpand-all`, `parse-args`, `pformat`, `postwalk`, `pp`, `pprint`, `prewalk`, `readable?`, `recursive?`, `rest`, `saferepr`, `walk` * Classes: `PrettyPrinter`, `Sequence` * Macros: `#%`, `#:`, `->`, `->>`, `ap-dotimes`, `ap-each`, `ap-each-while`, `ap-filter`, `ap-first`, `ap-if`, `ap-last`, `ap-map`, `ap-map-when`, `ap-reduce`, `ap-reject`, `as->`, `assoc`, `cfor`, `comment`, `defmacro!`, `defmacro/g!`, `defmain`, `defn+`, `defn/a+`, `defseq`, `dict=:`, `do-n`, `doto`, `fn+`, `fn/a+`, `ifp`, `let+`, `lif`, `list-n`, `loop`, `ncut`, `of`, `profile/calls`, `profile/cpu`, `seq`, `setv+`, `smacrolet`, `unless`, `with-gensyms` * Functions that provide first-class Python operators, such as `+` in constructs like `(reduce + xs)`, are no longer brought into scope automatically. Say `(import hy.pyops *)` to get them. * Hy scoping rules more closely follow Python scoping in certain edge cases. * `let` is now a core macro with somewhat different semantics. In particular, definition-like core macros (`defn`, `defclass`, `import`) now introduce new names that shadow corresponding `let`-bound names and persist outside the body of the `let`. * The constructors of `String` and `FString` now check that the input would be syntactically legal as a literal. * `hy.extra.reserved` has been renamed to `hy.reserved`. New Features ------------------------------ * `hy.repr` now supports several more standard types. * The attribute access macro `.` now allows method calls. For example, `(. x (f a))` is equivalent to `(x.f a)`. * `hy.as-model` checks for self-references in its argument. * New function `hy.model_patterns.keepsym`. Bug Fixes ------------------------------ * In comprehension forms other than `for`, assignments (other than `:setv` and loop clauses) are now always visible in the surrounding scope. * `match` now only evaluates the subject once. * `let` will no longer re-evaluate the default arguments of a function it's used in. * `hy.repr` now properly formats bracket strings. * The `repr` and `str` of string models now include `brackets` if necessary. * When standard output can't accommodate Unicode, `hy2py` now crashes instead of emitting incorrect Python code. * Fixed a bug with self-requiring files on Windows. * Improved error messages for illegal uses of `finally` and `else`. .. _Hyrule: https://github.com/hylang/hyrule 1.0a3 (released 2021-07-09) ============================== Bug Fixes ------------------------------ * Fixed a dependency-management bug that prevented installation of Hy from a wheel on Pythons < 3.9. 1.0a2 (released 2021-07-07) ============================== Removals ------------------------------ * All reimplementations of functions in the package `Toolz`_ have been removed. Import these from Toolz (or `CyToolz`_) instead. Beware that the Toolz functions are not all drop-in replacements for the old Hy functions; e.g., `partition` has a different order of parameters. * `complement`, `compose` (formerly `comp` in Hy), `drop`, `first`, `identity`, `interleave`, `interpose`, `iterate`, `juxt`, `last`, `merge-with`, `nth`, `partition`, `second`, `take-nth`, `take` * All aliases of objects in Python's standard library have been removed. Import these objects explicitly instead. * From `itertools`: `accumulate`, `chain`, `combinations-with-replacement` (formerly `multicombinations` in Hy), `combinations`, `compress`, `count`, `cycle`, `dropwhile` (formerly `drop-while`), `filterfalse` (formerly `remove`), `groupby` (formerly `group-by`), `islice`, `permutations`, `product`, `repeat`, `starmap` (formerly `*map`), `takewhile` (formerly `take-while`), `tee`, `zip-longest` * From `functools`: `reduce` * From `fractions`: `Fraction` (formerly `fraction`) * The following core predicate functions have been removed. Use `isinstance` etc. instead. * `empty?`, `even?`, `every?`, `float?`, `integer-char?`, `integer?`, `iterable?`, `iterator?`, `keyword?`, `list?`, `neg?`, `none?`, `numeric?`, `odd?`, `pos?`, `some`, `string?`, `symbol?`, `tuple?`, `zero?` * Several other core functions and macros have been removed: * `keyword`: Use `(hy.models.Keyword (hy.unmangle …))` instead. * `repeatedly`: Use `toolz.iterate` instead. * `if-not`: Use `(if (not …) …)` instead. * `lif-not`: Use `(lif (not …) …)` instead. * `macro-error`: Use `raise` instead. * `calling-module`: Now internal to Hy. * `calling-module-name`: Now internal to Hy. Other Breaking Changes ------------------------------ * `if` no longer allows more than three arguments. Use `cond` instead. * `cut` with exactly two arguments (the object to be cut and the index) now works like Python slicing syntax and the `slice` function: `(cut x n)` gets the first `n` elements instead of everything after the first `n`. * In `defn`, the return-value annotation, if any, is now placed before the function name instead of after. * Python reserved words are no longer allowed as parameter names, nor as keywords in keyword function calls. * Hy model objects are no longer equal to ordinary Python values. For example, `(!= 1 '1)`. You can promote values to models with `hy.as-model` before making such a check. * The following functions are now called as attributes of the `hy` module: * `hy.disassemble`, `hy.gensym`, `hy.macroexpand`, `hy.macroexpand-1`, `hy.repr` (formerly `hy.contrib.hy-repr.hy-repr`), `hy.repr-register` (formerly `hy.contrib.hy-repr.hy-repr-register`) * `cmp` has been renamed to `chainc`. * `defclass` no longer automatically adds `None` to the end of `__init__` method definitions. * All special forms have been replaced with macros. This won't affect most preexisting code, but it does mean that user-defined macros can now shadow names like `setv`. * `hy.repr` no longer uses the registered method of a supertype. * The constructors of `Symbol` and `Keyword` now check that the input would be syntactically legal. * Attempting to call a core macro not implemented on the current version of Python is now an error. * `hy.extra.reserved.special` has been replaced with `hy.extra.reserved.macros`. New Features ------------------------------ * `hy-repr` is now the default REPL output function. * The command `python -m hy` now works the same as `hy`. * New function `hy.as-model`. * New macro `match` (Python 3.10 only). * `annotate` is now a user-visible macro. Bug Fixes ------------------------------ * Fixed issues with newer prereleases of Python 3.10. * The REPL now properly displays `SyntaxError`\s. * Fixed a bug in `pprint` in which `width` was ignored. * Corrected `repr` and `hy.repr` for f-strings. * `--spy` and `--repl-output-fn` can now overwrite `HYSTARTUP` values. .. _Toolz: https://toolz.readthedocs.io .. _CyToolz: https://github.com/pytoolz/cytoolz 1.0a1 (released 2021-04-12) ============================== Removals ------------------------------ * The core function `name` has been removed. Use `unmangle` or the `name` attribute of keyword objects instead. * `deftag` has been removed. Instead of `(deftag foo …)`, say `(defmacro "#foo" …)`. * `#doc` has been removed. Instead of `#doc @`, say `(doc "#@")`. * `__tags__` has been removed. Tag macros are now tracked in `__macros__`. Other Breaking Changes ------------------------------ * Lambda lists (function parameter lists) have been simplified. `&optional` is gone, `&args` is `#*`, `&kwargs` is `#**`, and `&kwonly` is `*`. Thus, `[a &optional b [c 3] &rest args &kwargs kwargs]` is now `[a [b None] [c 3] #* args #** kwargs]`. * Hy models have been renamed to remove "Hy", and are no longer automatically brought into scope. Thus, `HyList` is now `hy.models.List`. * `eval` is no longer automatically brought into scope. Call it as `hy.eval` (or import it explicitly). * Calling a keyword object now does a string lookup, instead of a keyword-object lookup. Thus, `(:key obj)` is equivalent to `(get obj (mangle (. :key name)))`. * To require a tag macro `foo`, instead of `(require [module [foo]])`, you must now say `(require [module ["#foo"]])`. * Mangling no longer converts leading hyphens to underscores, and unmangling no longer converts leading underscores to hyphens. * F-strings now have their own model type, and store their code parts as models instead of strings. New Features ------------------------------ * Python 3.10 is now supported. * Lambda lists now support positional-only arguments. * F-strings now support `=` syntax per Python. * `with` now supports unnamed context managers. * `defmacro` and `require` can now take macro names as string literals. * New standard macros `do-n`, `list-n`, and `cfor`. * The location of the REPL history file can now be set with the environment variable `HY_HISTORY`. * REPL initialization scripts are now supported with the envrionment variable `HYSTARTUP`. * The module `hy.extra.reserved` has a new function `special`. * New module `hy.contrib.destructure` for Clojure-style destructuring. * New module `hy.contrib.slicing` for multi-index sequence slicing. Bug Fixes ------------------------------ * Fixed the identifier `J` being incorrectly parsed as a complex number. * Attempts to assign to constants are now more reliably detected. * Fixed a bug where AST nodes from macro expansion did not properly receive source locations. * Fixed `doc` sometimes failing to find core macros. * `doc` now works with names that need mangling. * Fixed bugs with `require` of names that need mangling. * Fixed a compiler crash from trying to use `..` as an operator. * Fixed namespace pollution caused by automatic imports of Hy builtins and macros. * `require` now works with relative imports and can name modules as members, as in `(require [hy.contrib [walk]])`. * Fixed error handling for illegal macro names. * Fixed `hyc` and `hy2py` not finding relative imports. * Fixed `hy.contrib.walk.smacrolet` requiring a module name. Misc. Improvements ------------------------------ * The library `astor` is no longer required on Pythons ≥ 3.9. 0.20.0 (released 2021-01-25) ============================== Removals ------------------------------ * Python 3.5 is no longer supported. New Features ------------------------------ * `let` macro now supports extended iterable unpacking syntax. * New contrib module `pprint`, a Hy equivalent of `python.pprint`. Bug Fixes ------------------------------ * Fixed a bug that made `hy.eval` from Python fail on `require`. * Fixed a bug that prevented pickling of keyword objects. * Fixed a compiler crash from `setv` with an odd number of arguments in `defclass`. 0.19.0 (released 2020-07-16) ============================== Breaking Changes ------------------------------ * `parse-args` is no longer implemented with `eval`; so e.g. you should now say `:type int` instead of `:type 'int`. New Features ------------------------------ * Python 3.9 is now supported. Bug Fixes ------------------------------ * Improved support for nesting anaphoric macros by only applying symbol replacement where absolutely necessary. * Quoted f-strings are no longer evaluated prematurely. * Fixed a regression in the production of error messages for empty expressions. * Fixed a scoping bug for code executed with `hy -c`. * Fixed a bug in the compilation of multiple `require`\s. * Fixed various bugs in command-line option parsing. 0.18.0 (released 2020-02-02) ============================== Removals ------------------------------ * Python 2 is no longer supported. * Support for attribute lists in `defclass` has been removed. Use `setv` and `defn` instead. * Literal keywords are no longer parsed differently in calls to functions with certain names. * `hy.contrib.multi` has been removed. Use `cond` or the PyPI package `multipledispatch` instead. Other Breaking Changes ------------------------------ * `HySequence` is now a subclass of `tuple` instead of `list`. Thus, a `HyList` will never be equal to a `list`, and you can't use `.append`, `.pop`, etc. on a `HyExpression` or `HyList`. New Features ------------------------------ * Added special forms `py` to `pys` that allow Hy programs to include inline Python code. * Added a special form `cmp` for chained comparisons. * All augmented assignment operators (except `%=` and `^=`) now allow more than two arguments. * Added support for function annotations (PEP 3107) and variable annotations (PEP 526). * Added a function `parse-args` as a wrapper for Python's `argparse`. Bug Fixes ------------------------------ * Statements in the second argument of `assert` are now executed. * Fixed a bug that caused the condition of a `while` to be compiled twice. * `in` and `not-in` now allow more than two arguments, as in Python. * `hy2py` can now handle format strings. * Fixed crashes from inaccessible history files. * Removed an accidental import from the internal Python module `test`. * Fixed a swarm of bugs in `hy.extra.anaphoric`. Misc. Improvements ------------------------------ * Replaced the dependency `clint` with `colorama`. 0.17.0 (released 2019-05-20) ============================== **Warning**: Hy 0.17.x will be the last Hy versions to support Python 2, and we expect 0.17.0 to be the only release in this line. By the time 0.18.0 is released (in 2020, after CPython 2 has ceased being developed), Hy will only support Python 3. Removals ------------------------------ * Python 3.4 is no longer supported. New Features ------------------------------ * Python 3.8 is now supported. * Format strings with embedded Hy code (e.g., `f"The sum is {(+ x y)}"`) are now supported, even on Pythons earlier than 3.6. * Added a special form `setx` to create Python 3.8 assignment expressions. * Added new core functions `list?` and `tuple`. * Gensyms now have a simpler format that's more concise when mangled (e.g., `_hyx_XsemicolonXfooXvertical_lineX1235` is now `_hyx_fooXUffffX1`). Bug Fixes ------------------------------ * Fixed a crash caused by errors creating temporary files during bytecode compilation. 0.16.0 (released 2019-02-12) ============================== Removals ------------------------------ * Empty expressions (`()`) are no longer legal at the top level. New Features ------------------------------ * `eval` / `hy_eval` and `hy_compile` now accept an optional `compiler` argument that enables the use of an existing `HyASTCompiler` instance. * Keyword objects (not just literal keywords) can be called, as shorthand for `(get obj :key)`, and they accept a default value as a second argument. * Minimal macro expansion namespacing has been implemented. As a result, external macros no longer have to `require` their own macro dependencies. * Macros and tags now reside in module-level `__macros__` and `__tags__` attributes. Bug Fixes ------------------------------ * Cleaned up syntax and compiler errors. * You can now call `defmain` with an empty lambda list. * `require` now compiles to Python AST. * Fixed circular `require`\s. * Fixed module reloading. * Fixed circular imports. * Fixed errors from `from __future__ import ...` statements and missing Hy module docstrings caused by automatic importing of Hy builtins. * Fixed `__main__` file execution. * Fixed bugs in the handling of unpacking forms in method calls and attribute access. * Fixed crashes on Windows when calling `hy-repr` on date and time objects. * Fixed a crash in `mangle` for some pathological inputs. * Fixed incorrect mangling of some characters at low code points. * Fixed a crash on certain versions of Python 2 due to changes in the standard module `tokenize`. 0.15.0 (released 2018-07-21) ============================== Removals ------------------------------ * Dotted lists, `HyCons`, `cons`, `cons?`, and `list*` have been removed. These were redundant with Python's built-in data structures and Hy's most common model types (`HyExpression`, `HyList`, etc.). * `&key` is no longer special in lambda lists. Use `&optional` instead. * Lambda lists can no longer unpack tuples. * `ap-pipe` and `ap-compose` have been removed. Use threading macros and `comp` instead. * `for/a` has been removed. Use `(for [:async ...] ...)` instead. * `(except)` is no longer allowed. Use `(except [])` instead. * `(import [foo])` is no longer allowed. Use `(import foo)` instead. Other Breaking Changes ------------------------------ * `HyExpression`, `HyDict`, and `HySet` no longer inherit from `HyList`. This means you can no longer use alternative punctuation in place of square brackets in special forms (e.g. `(fn (x) ...)` instead of the standard `(fn [x] ...)`). * Mangling rules have been overhauled; now, mangled names are always legal Python identifiers. * `_` and `-` are now equivalent, even as single-character names. * The REPL history variable `_` is now `*1`. * Non-shadow unary `=`, `is`, `<`, etc. now evaluate their argument instead of ignoring it. * `list-comp`, `set-comp`, `dict-comp`, and `genexpr` have been replaced by `lfor`, `sfor`, `dfor`, and `gfor`, respectively, which use a new syntax and have additional features. All Python comprehensions can now be written in Hy. * `&`-parameters in lambda lists must now appear in the same order that Python expects. * Literal keywords now evaluate to themselves, and `HyKeyword` no longer inherits from a Python string type * `HySymbol` no longer inherits from `HyString`. New Features ------------------------------ * Python 3.7 is now supported. * `while` and `for` are allowed to have empty bodies. * `for` supports the various new clause types offered by `lfor`. * `defclass` in Python 3 supports specifying metaclasses and other keyword arguments. * Added `mangle` and `unmangle` as core functions. * Added more REPL history variables: `*2` and `*3`. * Added a REPL variable holding the last exception: `*e`. * Added a command-line option `-E` per CPython. * Added a new module `hy.model_patterns`. Bug Fixes ------------------------------ * `hy2py` should now output legal Python code equivalent to the input Hy code in all cases. * Fixed `(return)` so it can exit a Python 2 generator. * Fixed a case where `->` and `->>` duplicated an argument. * Fixed bugs that caused `defclass` to drop statements or crash. * Fixed a REPL crash caused by illegal backslash escapes. * `NaN` can no longer create an infinite loop during macro-expansion. * Fixed a bug that caused `try` to drop expressions. * The compiler now properly recognizes `unquote-splice`. * Trying to import a dotted name is now a syntax error, as in Python. * `defmacro!` now allows optional arguments. * Fixed handling of variables that are bound multiple times in a single `let`. Misc. Improvements ---------------------------- * `hy-repr` uses registered functions instead of methods. * `hy-repr` supports more standard types. * `macroexpand-all` will now expand macros introduced by a `require` in the body of a macro. 0.14.0 (released 2018-02-14) ============================== Removals ------------------------------ * Python 3.3 is no longer supported * `def` is gone; use `setv` instead * `apply` is gone; use the new `#*` and `#**` syntax instead * `yield-from` is no longer supported under Python 2 * Periods are no longer allowed in keywords * Numeric literals can no longer begin with a comma or underscore * Literal `Inf`\s and `NaN`\s must now be capitalized like that Other Breaking Changes ------------------------------ * Single-character "sharp macros" are now "tag macros", which can have longer names * `xi` from `hy.extra.anaphoric` is now a tag macro `#%` * `eval` is now a function instead of a special form New Features ------------------------------ * The compiler now automatically promotes values to Hy model objects as necessary, so you can write ``(eval `(+ 1 ~n))`` instead of ``(eval `(+ 1 ~(HyInteger n)))`` * `return` has been implemented as a special form * Added a form of string literal called "bracket strings" delimited by `#[FOO[` and `]FOO]`, where `FOO` is customizable * Added support for PEP 492 (`async` and `await`) with `fn/a`, `defn/a`, `with/a`, and `for/a` * Added Python-style unpacking operators `#*` and `#**` (e.g., `(f #* args #** kwargs)`) * Added a macro `comment` * Added EDN `#_` syntax to discard the next term * `while` loops may now contain an `else` clause, like `for` loops * `#%` works on any expression and has a new `&kwargs` parameter `%**` * Added a macro `doc` and a tag macro `#doc` * `get` is available as a function * `~@` (`unquote-splice`) form now accepts any false value as empty Bug Fixes ------------------------------ * Relative imports (PEP 328) are now allowed * Numeric literals are no longer parsed as symbols when followed by a dot and a symbol * Hy now respects the environment variable `PYTHONDONTWRITEBYTECODE` * String literals should no longer be interpreted as special forms or macros * Tag macros (née sharp macros) whose names begin with `!` are no longer mistaken for shebang lines * Fixed a bug where REPL history wasn't saved if you quit the REPL with `(quit)` or `(exit)` * `exec` now works under Python 2 * No TypeError from multi-arity `defn` returning values evaluating to `None` * `try` forms are now possible in `defmacro` and `deftag` * Multiple expressions are now allowed in `try` * Fixed a crash when `macroexpand`\ing a macro with a named import * Fixed a crash when `with` suppresses an exception. `with` now returns `None` in this case. * Fixed a crash when `--repl-output-fn` raises an exception * Fixed a crash when `HyTypeError` was raised with objects that had no source position * `assoc` now evaluates its arguments only once each * Multiple expressions are now allowed in the `else` clause of a `for` loop * `else` clauses in `for` and `while` are recognized more reliably * Statements in the condition of a `while` loop are repeated properly * Argument destructuring no longer interferes with function docstrings * Nullary `yield-from` is now a syntax error * `break` and `continue` now raise an error when given arguments instead of silently ignoring them Misc. Improvements ------------------------------ * `read`, `read_str`, and `eval` are exposed and documented as top-level functions in the `hy` module * An experimental `let` macro has been added to `hy.contrib.walk` 0.13.1 (released 2017-11-03) ============================== Bug Fixes ------------------------------ * Changed setup.py to require astor 0.5, since 0.6 isn't backwards-compatible. 0.13.0 (released 2017-06-20) ============================== Language Changes ------------------------------ * Pythons 2.6, 3.0, 3.1, and 3.2 are no longer supported * `let` has been removed. Python's scoping rules do not make a proper implementation of it possible. Use `setv` instead. * `lambda` has been removed, but `fn` now does exactly what `lambda` did * `defreader` has been renamed to `defsharp`; what were previously called "reader macros", which were never true reader macros, are now called "sharp macros" * `try` now enforces the usual Python order for its elements (`else` must follow all `except`\s, and `finally` must come last). This is only a syntactic change; the elements were already run in Python order even when defined out of order. * `try` now requires an `except` or `finally` clause, as in Python * Importing or executing a Hy file automatically byte-compiles it, or loads a byte-compiled version if it exists and is up to date. This brings big speed boosts, even for one-liners, because Hy no longer needs to recompile its standard library for every startup. * Added bytestring literals, which create `bytes` objects under Python 3 and `str` objects under Python 2 * Commas and underscores are allowed in numeric literals * Many more operators (e.g., `**`, `//`, `not`, `in`) can be used as first-class functions * The semantics of binary operators when applied to fewer or more than two arguments have been made more logical * `(** a b c d)` is now equivalent to `(** a (** b (** c d)))`, not `(** (** (** a b) c) d)` * `setv` always returns `None` * When a `try` form executes an `else` clause, the return value for the `try` form is taken from `else` instead of the `try` body. For example, `(try 1 (except [ValueError] 2) (else 3))` returns `3`. * `xor`: If exactly one argument is true, return it * `hy.core.reserved` is now `hy.extra.reserved` * `cond` now supports single argument branches Bug Fixes ------------------------------ * All shadowed operators have the same arities as real operators * Shadowed comparison operators now use `and` instead of `&` for chained comparisons * `partition` no longer prematurely exhausts input iterators * `read` and `read-str` no longer raise an error when the input parses to a false value (e.g., the empty string) * A `yield` inside of a `with` statement will properly suppress implicit returns * `setv` no longer unnecessarily tries to get attributes * `loop` no longer replaces string literals equal to "recur" * The REPL now prints the correct value of `do` and `try` forms * Fixed a crash when tokenizing a single quote followed by whitespace Misc. Improvements ------------------------------ * New contrib module `hy-repr` * Added a command-line option `--repl-output-fn` 0.12.1 (released 2017-01-24) ============================== Bug Fixes ------------------------------ * Allow installation without Git 0.12.0 (released 2017-01-17) ============================== This release brings some quite significant changes on the language and as a result very large portions of previously written Hy programs will require changes. At the same time, documentation and error messages were improved, hopefully making the language easier to use. Language Changes ------------------------------ * New syntax for let, with and defclass * defmacro will raise an error on &kwonly, &kwargs and &key arguments * Keyword argument labels to functions are required to be strings * slice replaced with cut to stop overloading the python built-in * removed redundant throw, catch, progn, defun, lisp-if, lisp-if-not, filterfalse, true, false and nil * global now takes multiple arguments * Nonlocal keyword (Python 3 only) * Set literals (#{1 2 3}) * Keyword-only arguments (Python 3 only) * Setv can assign multiple variables at once * Empty form allowed for setv, del and cond * One-argument division, rationals and comparison operators (=, !=, <, >, <=, >=) * partition form for chunking collection to n-sized tuples * defn-alias and demacro-alias moved into hy.contrib.alias * None is returned instead of the last form in --init-- * for and cond can take a multi-expression body * Hex and octal support for integer literals * Apply now mangles strings and keywords according to Hy mangling rules * Variadic if * defreader can use strings as macro names * as-> macro added * require syntax changed and now supports same features as import * defmulti changed to work with dispatching function * old defmulti renamed to defn * Lazy sequences added to contrib * defmacro! added for once-only evaluation for parameters * comp, constantly, complement and juxt added * keyword arguments allowed in method calls before the object Bug Fixes ------------------------------ * Better error when for doesn't have body * Better error detection with list comprehensions in Python 2.7 * Setting value to callable will raise an error * defclass can have properties / methods with built-in names * Better error messages on invalid macro arguments * Better error messages with hy2py and hyc * Cmdline error to string conversion. * In python 3.3+, generator functions always return a value * &rest can be used after &optional Misc. Improvements ------------------------------ * Version information includes SHA1 of current commit * Improved Python 3.5 support * Allow specification of global table and module name for (eval ...) * General documentation improvements * Contrib.walk: Coerce non-list iterables into list form * Flow macros (case and switch) * ap-pipe and ap-compose macros * #@ reader macro for with-decorator * Type check `eval` parameters * `and` and `or` short-circuit * `and` and `or` accept zero or more arguments * read-str for tokenizing a line * botsbuildbots moved to contrib * Trailing bangs on symbols are mangled * xi forms (anonymous function literals) * if form optimizations in some cases * xor operator * Overhauled macros to allow macros to ref the Compiler * ap-if requires then branch * Parameters for numeric operations (inc, dec, odd?, even?, etc.) aren't type checked * import_file_to_globals added for use in emacs inferior lisp mode * hy.core.reserved added for querying reserved words * hy2py can use standard input instead of a file * alias, curry, flow and meth removed from contrib * contrib.anaphoric moved to hy.extra Changes from 0.10.1 ============================== Language Changes ------------------------------ * new keyword-argument call syntax * Function argument destructuring has been added. * Macro expansion inside of class definitions is now supported. * yield-from support for Python 2 * with-decorator can now be applied to classes. * assert now accepts an optional assertion message. * Comparison operators can now be used with map, filter, and reduce. * new last function * new drop-last function * new lisp-if-not/lif-not macro * new symbol? function * butlast can now handle lazy sequences. * Python 3.2 support has been dropped. * Support for the @ matrix-multiplication operator (forthcoming in Python 3.5) has been added. Bug Fixes ------------------------------ * Nested decorators now work correctly. * Importing hy modules under Python >=3.3 has been fixed. * Some bugs involving macro unquoting have been fixed. * Misleading tracebacks when Hy programs raise IOError have been corrected. Misc. Improvements ------------------------------ * attribute completion in REPL * new -m command-line flag for running a module * new -i command-line flag for running a file * improved error messaging for attempted function definitions without argument lists * Macro expansion error messages are no longer truncated. * Error messaging when trying to bind to a non-list non-symbol in a let form has been improved. Changes from 0.10.0 ============================== This release took some time (sorry, all my fault) but it's got a bunch of really nice features. We hope you enjoy hacking with Hy as much as we enjoy hacking on Hy. In other news, we're Dockerized as an official library image! $ docker run -it --rm hylang hy 0.10.0 using CPython(default) 3.4.1 on Linux => ((lambda [] (print "Hello, World!"))) Hello, World! - Hy Society Language Changes ------------------------------ * Implement raise :from, Python 3 only. * defmain macro * name & keyword functions added to core * (read) added to core * shadow added to core * New functions interleave interpose zip_longest added to core * nth returns default value when out of bounds * merge-with added * doto macro added * keyword? to find out keywords * setv no longer allows "." in names Internals ------------------------------ * Builtins reimplemented in terms of python stdlib * gensyms (defmacro/g!) handles non-string types better Tools ------------------------------ * Added hy2py to installed scripts Misc. Fixes ------------------------------ * Symbols like true, false, none can't be assigned * Set sys.argv default to [''] like Python does * REPL displays the python version and platform at startup * Dockerfile added for https://registry.hub.docker.com/_/hylang/ Contrib changes ------------------------------ * Fix ap-first and ap-last for failure conditions Changes from 0.9.12 ============================== 0.10.0 - the "oh man I'm late for PyCon" release Thanks to theanalyst (Abhi) for getting the release notes together. You're the best! - Hy Society Breaking Changes ------------------------------ We're calling this release 0.10 because we broke API. Sorry about that. We've removed kwapply in favor of using `apply`. Please be sure to upgrade all code to work with `apply`. (apply function-call args kwargs) ; is the signature Thanks ------------------------------ Major shoutout to Clinton Dreisbach for implementing loop/recur. As always, massive hugs to olasd for the constant reviews and for implementing HyCons cells. Thanks to @kenanb for redesigning the new Hy logo. Many thanks to algernon for working on adderall, which helped push Hy further this cycle. Adderall is an implementation of miniKanren in Hy. If you're interested in using Adderall, check out hydiomatic, which prettifies Hy source using Adderall rules. This release saw an increase of about 11 contributors for a point release, you guys rock! -Hy Society Language Changes ------------------------------ * `for` revamped again (Last time, we hope!), this time using a saner itertools.product when nesting * `lisp-if`/`lif` added for the lisp-like everything is true if, giving seasoned lispers a better if check (0 is a value, etc) * Reader Macros are macros now! * yield-from is now a proper yield from on Python 3. It also now breaks on Python 2.x. * Added if-not macro * We finally have a lisp like cons cells * Generator expressions, set & dict comprehensions are now supported * (.) is a mini DSL for attribute access * `macroexpand` & `macroexpand-1` added to core * `disassemble` added to core, which dumps the AST or equivalent python code * `coll?` added to core to check for a collection * `identity` function added to core Misc. Fixes ------------------------------ * Lots of doc fixes. Reorganization as well as better docs on Hy internals * Universal Wheel Support * Pygments > 1.6 supports Hy now. All codeblocks in docs have been changed from clojure to hy * Hy REPL supports invoking with --spy & -i options [reword] * `first` and `rest` are functions and not macros anymore * "clean" target added to Makefile * hy2py supports a bunch of commandline options to show AST, source etc. * Sub-object mangling: every identifier is split along the dots & mangled separately Bug Fixes ------------------------------ * Empty MacroExpansions work as expected * Python 3.4 port. Sorry this wasn't in a 3.4 release time, we forgot to do a release. Whoops. * eg/lxml/parse-tumblr.hy works with Python 3 * hy2py works on Windows * Fixed unicode encoding issue in REPL during unicode exceptions * Fixed handling of comments at end of input (#382) Contrib changes ------------------------------ * Curry module added to contrib * Loop/recur module added which provides TCO at tail position * defmulti has been added - check out more in the docs -- thanks to Foxboron for this one! * Walk module for walking the Hy AST, features a `macroexpand-all` as well 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 warrant a release on its own. A huge thanks goes to Foxboron for implementing them and a massive hug goes out to olasd for providing ongoing reviews during the development. Welcome to the new Hy contributors, Henrique Carvalho Alves, Kevin Zita and Kenan Bölükbaşı. Thanks for your work so far, folks! Hope y'all enjoy the finest that 2013 has to offer, - Hy Society * Special thanks goes to Willyfrog, Foxboron and theanalyst for writing 0.9.12's NEWS. Thanks, y'all! (PT) Language Changes ------------------------------ * Translate foo? -> is_foo, for better Python interop. (PT) * Reader Macros! * Operators + and * now can work without arguments * Define kwapply as a macro * Added apply as a function * Instant symbol generation with gensym * Allow macros to return None * Add a method for casting into byte string or unicode depending on python version * flatten function added to language * Add a method for casting into byte string or unicode depending on python version * Added type coercing to the right integer for the platform Misc. Fixes ------------------------------ * Added information about core team members * Documentation fixed and extended * Add astor to install_requires to fix hy --spy failing on hy 0.9.11. * Convert stdout and stderr to UTF-8 properly in the run_cmd helper. * Update requirements.txt and setup.py to use rply upstream. * tryhy link added in documentation and README * Command line options documented * Adding support for coverage tests at coveralls.io * Added info about tox, so people can use it prior to a PR * Added the start of hacking rules * Halting Problem removed from example as it was nonfree * Fixed PyPI is now behind a CDN. The --use-mirrors option is deprecated. * Badges for pypi version and downloads. Syntax Fixes ------------------------------ * get allows multiple arguments Bug Fixes ------------------------------ * OSX: Fixes for readline Repl problem which caused HyREPL not allowing 'b' * Fix REPL completions on OSX * Make HyObject.replace more resilient to prevent compiler breakage. Contrib changes ------------------------------ * Anaphoric macros added to contrib * Modified eg/twisted to follow the newer hy syntax * Added (experimental) profile module Changes from Hy 0.9.10 ============================== * Many thanks to Guillermo Vayá (Willyfrog) for preparing this release's release notes. Major shout-out. (PT) Misc. Fixes ------------------------------ * Many many many documentation fixes * Change virtualenv name to be `hy` * Rewrite language.hy not to require hy.core.macros * Rewrite the bootstrap macros in hy * Cleanup the hy.macros module * Add comments to the functions and reorder them * Translation of meth from Python to Hy * PY3 should really check for Python >= 3 * Add hy._compat module to unify all Python 2 and 3 compatibility codes. * Import future.print_statement in hy code * Coerce the contents of unquote-splice'd things to a list * Various setup.py enhancements. * PEP8 fixes * Use setuptools.find_packages() * Update PyPI classifiers * Update website URL * Install the argparse module in Python 2.6 and before * Delete the duplicate rply in install_requires. With the PyPI version, tests are failed. * Finally fixed access to hy.core.macros here. have to explicitly require them. Language Changes ------------------------------ * Slightly cleaner version of drop-while, could use yield-from when ready * Added many native core functions * Add zero? predicate to check if an object is zero * Macro if-python2 for compile-time choice between Python 2 and Python 3 code branches * Added new travis make target to skip flake8 on pypy but run it on all others * Add "spy mode" to REPL * Add CL handling to hyc * Add yield from via macro magic. * Add some machinery to avoid importing hy in setup.py * Add a rply-based parser and lexer * Allow quoting lambda list keywords. * Clarified rest / cdr, cleaned up require * Make with return the last expression from its branch * Fix yielding to not suck (#151) * Make assoc accept multiple values, also added an even/odd check for checkargs * Added ability to parse doc strings set in defclass declarations, * Provide bin scripts for both Windows and \*nix * Removes setf in favor of setv Changes from Hy 0.9.9 ============================== Stupid Fixes ------------------------------ * I forgot to include hy.core.language in the sdist. (PT) Changes from Hy 0.9.8 ============================== Syntax Fixes ------------------------------ * Macros are now module-specific, and must be required when used. (KH) * Added a few more string escapes to the compiler (Thomas Ballinger) * Keywords are pseudo-callable again, to get the value out of a dict. (PT) * Empty expression is now the same as an empty vector. (Guillermo Vaya) Language Changes ------------------------------ * HyDicts (quoted dicts or internal HST repr) are now lists that compiled down to dicts by the Compiler later on. (ND) * Macros can be constants as well. (KH) * Add eval-when-compile and eval-and-compile (KH) * Add break and continue to Hy (Morten Linderud) * Core language libraries added. As example, I've included `take` and `drop` in this release. More to come (PT) * Importing a broken module's behavior now matches Python's more closely. (Morten Linderud) Misc. Fixes ------------------------------ * Ensure compiler errors are always "user friendly" (JD) * Hy REPL quitter repr adjusted to match Hy syntax (Morten Linderud) * Windows will no longer break due to missing readline (Ralph Moritz) Changes from Hy 0.9.7 ============================== Syntax Fixes ------------------------------ * Quasi-quoting now exists long with quoting. Macros will also not expand things in quotes. * kwapply now works with symbols as well as raw dicts. (ND) * Try / Except will now return properly again. (PT) * Bare-names sprinkled around the AST won't show up anymore (ND) Language Changes ------------------------------ * Added a new (require) form, to import macros for that module (PT) * Native macros exist and work now! (ND) * (fn) and (lambda) have been merged (ND) * New (defclass) builtin for class definitions (JD) * Add unquote-splicing (ND) Errata ------------------------------ * Paul was an idiot and marked the j-related bug as a JD fix, it was actually ND. My bad. Changes from Hy 0.9.6 ============================== Syntax Fixes ------------------------------ * UTF-8 encoded hy symbols are now `hy_`... rather than `__hy_`..., it's silly to prefex them as such. (PT) * `j` is no longer always interpreted as a complex number; we use it much more as a symbol. (ND) * (decorate-with) has been moved to (with-decorate) (JD) * New (unless) macro (JD) * New (when) macro (JD) * New (take) macro (@eigenhombre) * New (drop) macro (@eigenhombre) * import-from and import-as finally removed. (GN) * Allow bodyless functions (JD) * Allow variable without value in `let` declaration (JD) * new (global) builtin (@eal) * new lambda-list syntax for function defs, for var-arity, kwargs. (JK) Language Changes ------------------------------ * *HUGE* rewrite of the compiler. Massive thanks go to olasd and jd for making this happen. This solves just an insane number of bugs. (ND, PT, JD) * Eval no longer sucks with statements (ND) * New magic binary flags / mis fixes with the hy interpreter (WKG + @eigenhombre) Changes from Hy 0.9.5 ============================== Syntax Fixes ------------------------------ * .pyc generation routines now work on Python 3. (Vladimir Gorbunov) * Allow empty (do) forms (JD) * The `else` form is now supported in `try` statements. (JD) * Allow `(raise)`, which, like Python, will re-raise the last Exception. (JD) * Strings, bools, symbols are now valid top-level entries. (Konrad Hinsen) * UTF-8 strings will no longer get punycode encoded. (ND) * bare (yield) is now valid. (PT) * (try) now supports the (finally) form. (JD) * Add in the missing operators and AugAssign operators. (JD) * (foreach) now supports the (else) form. (JD) WARNING: WARNING: READ ME: READ ME: ----------------------------------- From here on out, we will only support "future division" as part of hy. This is actually quite a pain for us, but it's going to be quite an amazing feature. This also normalizes behavior from Py 2 --> Py 3. Thank you so much, Konrad Hinsen. Language Changes ------------------------------ * (pass) has been removed from the language; it's a wart that comes from a need to create valid Python syntax without breaking the whitespace bits. (JD) * We've moved to a new import style, (import-from) and (import-as) will be removed before 1.0. (GN) * Prototypes for quoted forms (PT) * Prototypes for eval (PT) * Enhance tracebacks from language breakage coming from the compiler (JD) * The REPL no longer bails out if the internals break (Konrad Hinsen) * We now support float and complex numbers. (Konrad Hinsen) * Keywords (such as :foo) are now valid and loved. (GN) Changes from Hy 0.9.4 ============================== Syntax Fixes ------------------------------ * `try` now accepts `else`: (JD) `(try BODY (except [] BODY) (else BODY))` Changes from Hy 0.9.4 ============================== Syntax Fixes ------------------------------ * Statements in the `fn` path early will not return anymore. (PT) * Added "not" as the inline "not" operator. It's advised to still use "not-in" or "is-not" rather than nesting. (JD) * `let` macro added (PT) * Added "~" as the "invert" operator. (JD) * `catch` now accepts a new format: (JD) (catch [] BODY) (catch [Exception] BODY) (catch [e Exception] BODY) (catch [e [Exception1 Exception2]] BODY) * With's syntax was fixed to match the rest of the code. It's now: (PT) (with [name context-managed-fn] BODY) (with [context-managed-fn] BODY) Language Changes ------------------------------ * Added `and` and `or` (GN) * Added the tail threading macro (->>) (PT) * UTF encoded symbols are allowed, but mangled. All Hy source is now presumed to be UTF-8. (JD + PT) * Better builtin signature checking (JD) * If hoisting (for things like printing the return of an if statement) have been added. '(print (if true true true))' (PT) Documentation ------------------------------ * Initial documentation added to the source tree. (PT) Changes from Hy 0.9.3 ============================== Syntax Fixes ------------------------------ * Nested (do) expressions no longer break Hy (PT) * `progn` is now a valid alias for `do` (PT) * `defun` is now a valid alias for `defn` (PT) * Added two new escapes for \ and " (PT) Language Changes ------------------------------ * Show a traceback when a compile-error bubbles up in the Hy REPL (PT) * `setf` / `setv` added, the behavior of `def` may change in the future. * `print` no longer breaks in Python 3.x (PT) * Added `list-comp` list comprehensions. (PT) * Function hoisting (for things like inline invocation of functions, e.g. '((fn [] (print "hi!")))' has been added. (PT) * `while` form added. (ND) (while [CONDITIONAL] BODY) Documentation ------------------------------ * Initial docs added. (WKG + CW) Changes from Hy 0.9.2 ============================== General Enhancements ------------------------------ * hy.__main__ added, `python -m hy` will now allow a hy shim into existing Python scripts. (PT) Language Changes ------------------------------ * `import-as` added to allow for importing modules. (Amrut Joshi) * `slice` added to slice up arrays. (PT) * `with-as` added to allow for context managed bits. (PT) * `%` added to do Modulo. (PT) * Tuples added with the '(, foo bar)' syntax. (PT) * `car` / `first` added. (PT) * `cdr` / `rest` added. (PT) * hy --> .pyc compiler added. (PT) * Completer added for the REPL Readline autocompletion. (PT) * Merge the `meth` macros into hy.contrib. (PT) * Changed __repr__ to match Hy source conventions. (PT) * 2.6 support restored. (PT) Changes from Hy 0.9.1 ============================== General Enhancements ------------------------------ * Hy REPL added. (PT) * Doc templates added. (PT) Language Changes ------------------------------ * Add `pass` (PT) * Add `yield` (PT) * Moved `for` to a macro, and move `foreach` to old `for`. (PT) * Add the threading macro (`->`). (PT) * Add "earmufs" in. (tenach) * Add comments in (PT) Changes from Hy 0.9.0 ============================== Language Changes ------------------------------ * Add `throw` (PT) * Add `try` (PT) * add `catch` (PT) Changes from Hy 0.8.2 ============================== Notes ------------------------------ * Complete rewrite of old-hy. (PT) hy-1.0.0/README.md000066400000000000000000000034121467401276000134160ustar00rootroot00000000000000Hy == [![Version](https://img.shields.io/pypi/v/hy.svg)](https://pypi.python.org/pypi/hy) XKCD #224 Lisp and Python should love each other. Let's make it happen. Hy is a Lisp dialect that's embedded in Python. Since Hy transforms its Lisp code into Python abstract syntax tree (AST) objects, you have the whole beautiful world of Python at your fingertips, in Lisp form. To install the latest release of Hy, just use the command `pip3 install --user hy`. Then you can start an interactive read-eval-print loop (REPL) with the command `hy`, or run a Hy program with `hy myprogram.hy`. * [The Hy homepage](http://hylang.org) * [Try Hy with a web console](http://hylang.org/try-hy) Project ------- * Code: https://github.com/hylang/hy * Documentation: http://hylang.org/hy/doc * Bug reports: We have no bugs! Your bugs are your own! (https://github.com/hylang/hy/issues) * License: MIT (Expat) * Community: Join us on [Github Discussions](https://github.com/hylang/hy/discussions)! * [Stack Overflow: The [hy] tag](https://stackoverflow.com/questions/tagged/hy) Hy's current maintainer is [Kodi Arfer](https://github.com/Kodiologist). He takes responsibility for answering user questions, which should primarily be asked on Stack Overflow or GitHub Discussions, but feel free to [poke him](http://arfer.net/elsewhere) if he's missed a question or you've found a serious security issue. ![Cuddles the Hacker](https://i.imgur.com/QbPMXTN.png) (fan art from the one and only [doctormo](http://doctormo.deviantart.com/art/Cuddles-the-Hacker-372184766)) hy-1.0.0/bin/000077500000000000000000000000001467401276000127075ustar00rootroot00000000000000hy-1.0.0/bin/py2ast000077500000000000000000000001501467401276000140530ustar00rootroot00000000000000#!/usr/bin/env python import ast import sys print(ast.dump(ast.parse(open(sys.argv[1], "r").read()))) hy-1.0.0/conftest.py000066400000000000000000000006611467401276000143410ustar00rootroot00000000000000import os from pathlib import Path import hy, pytest NATIVE_TESTS = Path.cwd() / "tests/native_tests" # https://github.com/hylang/hy/issues/2029 os.environ.pop("HYSTARTUP", None) def pytest_collect_file(file_path, parent): if ( file_path.suffix == ".hy" and NATIVE_TESTS in file_path.parents and file_path.name != "__init__.hy" ): return pytest.Module.from_parent(parent, path=file_path) hy-1.0.0/docs/000077500000000000000000000000001467401276000130675ustar00rootroot00000000000000hy-1.0.0/docs/.gitignore000066400000000000000000000000071467401276000150540ustar00rootroot00000000000000_build hy-1.0.0/docs/_static/000077500000000000000000000000001467401276000145155ustar00rootroot00000000000000hy-1.0.0/docs/_static/.keepme000066400000000000000000000000001467401276000157520ustar00rootroot00000000000000hy-1.0.0/docs/_static/CC0_1.0.txt000066400000000000000000000156101467401276000162040ustar00rootroot00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. hy-1.0.0/docs/_static/cuddles-transparent-small.png000066400000000000000000001123261467401276000223200ustar00rootroot00000000000000PNG  IHDR@P tEXtSoftwareAdobe ImageReadyqe<hiTXtXML:com.adobe.xmp boIDATx}dUyr&0 i$ +`uQd +]oUD+ H&+ǗW3ӡѕջ;;ְ5fK` kX5aְZkX5a ְ5,Zha kX51}a`|_$eݖU=nyP5 |T49D:"V (^RTN]s9 GyHw]vK m"\U[6 67}/NVs2uzN|}RR!QwZі;osn C5O_ H}mvIG|I|#*G,%Cɾӹsœgwx~ץeC 0gvA`! l>N; gѡe͗ӵzmzC@ϑsϦo?s歨AO8]iR(קdu5>}) &]GABRF3G %FJI`W;G"x, TKZcLӴ< 7&}=f i OiLj;I 3 uʴt/9jGP{?lYc -A˟" LQu*d|F#[#>ۣ (8z1 Okn{avhdmUZQߋM%(tl1(@5]{7һy^sz(ڔOKV5KZ`e`$k8^{&T|9譡L)leӱSv[M@i?gh_ hX!T2XFERϣ[CPe' j;[[9+m۬'2MiYo6vll;'KA$PBj4jQ<Yj@JH}[/T*Xdk,[K5dZ@X'2i%ظ68B4,2Ѭ@zSZRRuJ\`&{ UC "j]A:d璅J>p:iXWaU~M׿.-em@[O'2IeN~iP&d~IW)9$0 NŠz漒22贵 ^+^!k 6ilvFiA4#$*KD+N93J% t;HΨsߝ;A{G?wbXXXg1#+63pߋE-N0FxF}SJ~~aTA? GӸnsY :<(,YRk. Z0th]˨UKeYhal<).I+%P?zo`%NV QS&ɓ08ÒJQnNStfZVěC-Id e"ÉRdMEwB~ iLFZx ؑbΪݻ"b0bgQ}l ~T3 [~e %f)閌ޚսM=xsʃB 5Q@쀼0O3yɁ56mFdl&m Y$ڵ.%)dF; XcUyz@4`[*cD"-^*3\>hGl+a|P'ICLKёmEvf1l?uQen1gmw%3Tu}p~5,ОIͅH)铡ԂB1dk7J/9L;Ǚ:s*7+M$%{NZ8(A ׸"a,mǎޟu8-l$()X {C.,ͦHIIՖ20p-h (-gve֖E(1ʽtuk)4 Ӥ T[[wv*VV.Q5RKWT͑Zc8.#Ѵ+\ _ d܊ &r'ljQ5GAK9WphZ:fW&0іso2@"3l@~3HMHʱhJ6jk1-Y72 I$#tEJv %b!󁳍e0iC<ʥf9ʌ G8Hd2Y;pJL$-::¹}XZA{6C ;?&ki% {p tu3<}7OfXp 32fm'LRjc}Yd.LQjc^bn\x; 8/jLHH-ME$%hQu}T9%' -COBKK7sB [!あNȒ¢ S ^ޫ)T(!Tny6)BIIM˿A*\{+zr,x ;1fzށ@R Up8l% 2F]pV_%dUUA%e훴3Dtn 5rrVP"".dvw8Y;X0%%d fbkI>M'k+XbFQ }0Oe48;.h_2?ꆱؤv+;O}KdmTYW.0$p{œKxxPR:G&i@Э oSO@=x|fRcRS@ifEENJy'")j\Ȝ;zgUUP\i`DEm'Z-#O~q'mF;r%rdPϣ'ԮS l6G u~+:1$%@GPlB3IzElϽ6і =ʺ֘MVǻEɣ%"Apm׋ꦯ#q>_*HA;#IhZVep_uC; I?wM,Q-z8 5ʎ&=K~*[qJ+4ɑ2VAٮ$86~޷,)]H^ʼn5IɄ-FRVU7.3Dp̮d mތr]M1P3jԠ <22D32t?!zqu&"waEx`؆3iX(?P!*ԏ4<9k`΢ZhF"ŒHwSIU' vEc>թ9jgd|){v0B6,a4͠;DjJ*{!1Q h~2%+ `y#-j<{K11$U鵬$шƵb)f S4㭣9 jvKi ~ƛ3}+Q6U3Zmi")*XN{DHZqƳ{mO i'P̾]쟇;s 8KN?h E쏦B)'bxR3P{-nu<7Ѿ=_%%_akvZ@K2iY&'%>\[R`->4ڛ#)B7<dr[An|?y&){%,XȚ5<oeǕԘ&61GGAW gD]OɌz3bEbҁ.LNɟH.7klٍ؉.6a H$Vmm -$rPRz,Q>O$QYR/LK%E:},r6g-j<-ٶTyDS۞rJI/ສ̇Y͜ƅfo`Y& =)9+¡8c Ұ(Wmsrl}Q LMߖQCiE|WEĀ8{cy~U1zg;,\`̹CGGK"ȷOY.@>El> Lr x$)#z!va$)5Vdi{6y_폥'2>OYLi 2LJ7lbvsV6Qm˃h=4YNB1^M=/f%=redȆf%< dR7wƖxnI=*r>r/J22ڼqz#$!6O8)})q .rC S1K TRޞ .?AЎB ?.pg:> #Grg0qaF,Vjs!c)`bI._* B XGFVMon2_j( dZ!I&dG"|sZ6^,,αl_P^|jA%T 5/jp{?vX# (\Ս-bv {YԴ,tPƗ,1m۸:hֹy]W9gK|GU`H 㟘D,R :3H}nɗAsUr(_;߄7hUM[NŸ,u7g-M;@K17hqݨU .v7nCcsݶ<_TR])GQUV7"Yb SI+WFQor9^1oZmA[x^M-'edVl&;-o_m_,ĩ5*&63Vg9+ocRd ( he(ûiAk8jے$MK(Pf$\4ʒ)9+IH]PВJk$dv-k7siCXͽP/*[3( v-OpI&)n0>vM{!'mkǣx$5+e$ Rp\s;q&M0tkIgǰ5@= _2-6mV#Vd߾~ [sM󗰦AТ"b MKQqA+n'.@v\b+:l`񶔬\('A~ &K˲Ƽ,Z5e PZYy"h:'|vEQ[Ԓ)m+ി-mEP!au 0˙h00m;Pt;L3L@cJA;lKd\Eaimη䇅YkH:*06]jCk|[JK! T.|SRMxqipkoMkFN96{)7mBSr EbՔ|Y)/`̃؄b3,{vރ֌SƍCtm8YqEhǿAHSLnͱsژgDyIgMZQ#(8%IR@< S! {Kt!hZ 0m6X]*cb-RkԺb6e>Ű ZYiHR`BMYc4ﶋQT=khYPϯ A3՟JJҊv}6 -kZ`4L ic@z|[4EӖ>37hchL L]93u( AXRn,5j< 1)hԋі}w>!dX-MRX PHaEz$%=Tݰ@;9$n4%x i&\1 ]xZPь &,j$;5eGͳʜV7SnfWAM%?I>Rח&fJsUt90laSq\o'zԢAȘLɪbK{0MBZZ~ylϢyrL4In:m T/ZӚ#zlJY+*j J|".fp[5ڼAA\IY3vd{P=HeR@;@knm 8b}iY.TAhZy^$AFF)kGa@!1@Qעvcj,ʪٽ]Q_'s{MC-/k%3zfYbU/.?ORcQMfe=r`p0by4rnsҟE)U/Md"p>P{JF7 PXas(55&K,ˡh> s?^'oH~A* HnAt签>{ 8_?oEr8) \#dnV|в8V? K1pEXt #-}h>S^%[M綖P6s`Թ~-^ e2g@Keel".Z_b]'ڵDxޢs۞5XwƅTE3rBfQz/2y.HZh_5mۊJ_6yگX7POI5!Y {LQTq|?~f[JAs֖Je,a{rdJe`_t0/,^P< pdqK=Ji"+*%wsўpvۓ^m#Λ E=(wµiY,Cs lW"ETsTPt5ƹhcTE]S (4"&%HkVylMΟE4q'6ᵲ=w,'A*?+ACP`si"aYJC:b 0&c!ȶ*x4UY̅=Q>v5xMUnmK(Ĺ;)QtExF~5Jʵg|hڳVҬz|?:57@  Š8y^T33 ,]y>lXBI5Xb~AEVDG~i [|tlnLzL{:>*hcgFV A5*|4CHY0)[C1HdX`AE~fJ)NNA CU;n5s_$tTTLNu8=WŸ~s+wXR&BI=Wo  $y'Ȓ*ԴFL%?}{1Jlrm^.2nu3(!ZOvv#ЂSi:qEQAC/}\9Ens?u6q <ɋ\QcدN|?lgPUYwqg? k׮~m_h[Hw?_|巆;ᮻ:pNihu :PÒ=VOeIHQp<249f֖NPiA$bn .-xz\BR'- ēn `DךiJFFSdcW{ض8 _߀/}Kp;_xsx<ڶҶy Cد;,m?½ sرjkkx4 c%ЂVUmVR yJF]|iX;/택{C2fN'ZjMӍc3Lh&\NL(aV@XZ8Ex7߃ Oo3^|i'YJ .e\`_|1|[:]m¾HxI!i(vC-Ƕt,`N"[x^} z~?FRO=cb9m]N o 5h*gQd xV8ƔGrvfYvAט ?]}Hw+xl! <=FЎwiDѤ6N r6y=N'0;XėwԬ)-IJvT ) O ͉ME  2 ڇ 7t#\z饣~Q3{ \k=pGvC,d%Xd Ӭgvbg<~}/.XbKDǢNԪ:l?r#Y\^&ݽA`J74=Saf=ZCD3.r#gK\MŝeD.GH"~m_qŕc~sl= 'V1/H'b&m6d=5^5qL: ͆1Pȑ?N`l$Q}яڵDN*>YBYy/N /ݎUDғMd&o/CԠ#Ҿ~ߌwIFfyj򝁡,lAn+>vtjrceIam⒉E,Ph2)Q e[:oj)gdh4.r04KAԌ#g'% ~F;i9JI 1dB~WVdy|;?sGvApy) KΤAv{1x衇F MSW`>QI*G8efTZRgo 1-0QbӃb Ǟx o;|Gnmikd@ve)f1cT<0wTwa]`ZlNYSyvB㮽'X}45m,3%b͠ %  > :wS\GOA2v:w,_m飯 _ℿctn̷Ny684nvsO聧(`4,fk&-3_9h១Q9xk99}PtP ri YdLٮE5uڋ؎@kRqyV<+b>[#~>yb::g*(4&\a wY_Wc"ɯilЙ-ОC39)2kPviEϰku];d W4#6{{ijZL.{X9l ں)%gdO8KݢEc]£EB"g mR$k 8]NGIʟ5qlA4&wNTahOkB~T)bmh%cWuMmI`uEm׾p ciMey^p!/n Y6C[x~4|rN;./r a&Ү4δ{oF#Ipۿ\鱟/ >Q(QFH{ E}ϪnѵRɞYagĢmJs" Qkt)KղH7EaA-eU^oY$(B(г  Y0G3$/5CU^mN_Ts}iFIBzZBC_`hD8 X6+xJرFMxGhP)iLF*+CxGqC۵2FzlEMMyU돛ԭ[N}[ A3[H֘'v_=έ;i-8<,2ʪiY6KՍ!^h m8I[?Ĥ_I2 n .VXbH'bNΤX o\r#@7΋MW5BӚ `+`- У!3"$}ͺ[Xϳ0avf3`# hzbͲ ^OGҺWaX֎H#AEm.Ͱxzhp R/3R>m~8x>p$L%Ow^6%W8^6d-`?{T@UIBjɎeyKe߻|/wEӬ0(eQ:fAk< /ݼǻOPvK8;SK'*L|!Yg g ?3 4 v[gBaeRI;W{8s݂L 6SL: Kՙc*D> dFl\u^aiCxزm$~EdUP8H<}P0Xm qf<3\xt:DTY#ϳTϊFTDשhZ*eQ:3/&ҳؑn!彟EMkX-b+QPyt TtB^JW-ۥev6IQ2-W.O껻ŒUNmCE)Ē?&”nL2&v[Fc8~uš`Ұbwگ ЎdӒ{W75MᏋ*録H[WZrf@&*!im#]n?؜NֳrC"m57mU(MLY+H "\iͫC,IVH-k>I& ncڲxXc*.- L{ :ZBMRT72 #MI lwE)d孀փpէU/dNokO~k9 Uq`-Ў?Mi^f~isYQVj_ *j` Ýc.[חPpٜcJvL B:co.z3:iF%/=nS'`8LҶiIa'ݫ`-ОI j?$osw*.Sm}( 5 EԒ }U^BA:t%ö{ =%KԹ(ijlAZW׉T9(J-DpF)I^T_U0µB(dMᢞ-[Ғ52t+#,qpZKU\Kdآ ^^Q-PuźO"+Bhߒvn?ۼ/zӶa7# mTFqn{{p2<.;ƹT(+"?id]@EQ ~)SSAD95c5κyYo@[ ,_\  @ƅ@@] hSv @6hܹuwbe*JZn<.o`='#];L0TW:ycgfԑ} (eTX/?ŋf1hG@*B)lGJSs_!ԸmV/٬oHIY4,EN,nYzPp?6pTQ?wi5hj=A:趬$Բ=?mׁF!4(r =hkOh ?J4*pn+Rsq_Ka pY? c`UN{VL m/SW"Es96\| k Aˁm׳8鉜Zx{>:Kq(~#Of{uwMm]f~pմ#o #{Ww}s{ߵlW4gh?3i\m>Q&ô/ _[ ˳ icq=˞|ؑ`^ݪR;mbiRoUC{og X{Nf2:4/P[/c2ΧXhSg":>}p .o;z~w5Í,Жu@I myͧ׹"xP:/7 i _ Wv@GpNg~3Bڛ?-fjoPaJGl+@[^E]/m}?m=NR2LL/j[ ɢ(Nk 4 0{OUl)(> PYUas/i4_r}aλYV6^yq)'veÆft^n=`g϶wEXz3+e:ZpɓL!{u2In&sS|Vp)cXuzmWs FѢ8aoz;NP?<~J H%leٚ"%Ӳsk6]v٣m7B|m`4C[r#JLAtt M}]v۬CmeB僟gt-.sOۑ3Ka̽L5mj4ˀkāP4zב/(է`~Nj[[]_⾶rԝuUl5!\ (/wphv`@MK|߼%3 ƬAQlwߵbQ+97 JӐ؃4fJ%Ko{u~q\LyS1TœRcm:&)ّ j37* [s>':5y3 sjZi탟c{;LY-*V.@G! %VM!YlympӢ9lKP9%.LZ1&ȉ R5"鬃80Cl/ JƵ|uCU~7"SDSZ1-..C-=Ne(ZQ5sCumH7TYj áhb_wp#Ycn "pt8s&|u& ٶIPvFd2m޲VEL}!પ iNX:;8:pz뫫]_Ŕ7hS%U?I\LkME E'R|T^^YnJZ:/ Eӓ]jEZ3ZtŊpѥa7՚c,[W-COuעٹo%m(%㍝}ዉzX-Nt sˤ]!KӲ k;SdEC, K)5& IĿ/}D߲\aԢt-xΤel]v5'Q)Ҳlv}c Š@0"Uӷ^Rw1tզ{<veC**@<EV^)hWMk%SdfmNBZ\_4dNx#뻢f_*b+ABkQF&NGBWg/th?كcMwVRWt27I\m+ҫf sdXq:i[_.4Dc Aia.[.|~5j>btd^TMz5I@H^sfȬ1Kf^vmS0z=^s߸ߒ("ю '7h>Q]\rc AK>Z"5h|c`%ϙit1}K0ZT]P{֯_ ]xlal4 Bj첬1:ȆeL#K(aCZv?v {0P|_|FMw+9|ZZ:4 ˷xUgLl<ӌOR)=C<5H?Ĝ+At׽|QP+D)`|MJR /qxAY 0iO4Γ nq?lb l` ~ٝkoڣ6}©*y?et7ow94:>-k`ɲNeƥn]KڇkF!tſ:eO"p$U ~>4׼A9[>.z}n;K~r 'ܼe͢g{cLrfdu5as׍>Oy&88ZhfI# /.tr2)s$g[AdYԴO-\!k|PhTiQ/b4X JcC l8<`a&3fߞcL"@EjouՍ .n&o7h_{yS:DQI\-29M>%PP1Iͫ?{q DIԌ۶韣@s[!E î^K4+,=l”˅n rSGoFCY;jYY~ݏk8Oejº hZPڕ~s@!BmiY2ˉ" _dk{>F m {~yKi/+IUK*Ab2~vū^9xg_xypL /RIiLy. PM,IC=&sNa,j2uK8nB2Qp,mz?:h6/j >'lZP}q}Ş֮ [D31r]#;kԅށ ~(~`$]f47=7X ҟAm؛f%Ӯbh,Z| QP!Ko\1fZ%%WЩL6vӖcLx B=& !Lh)n7Ge {ޱ(fz=хuWYPuUk/:<EYH\$65 hPPj7\'^:me)'WPB5AAkK q4(i9ڲue'DmTz3rV/zTԼf3Bk@;]eE?k|?[2X#!هW{xϷkr  վ[;Ut,"(e&?2h*H1HÊE5+x#|FV5X6Y>GSP'ksn|{PP ohK(%ʇ4AY%;~ɖ.[LvX#'mE L078PBǘ*Nӈeu~\X\Su':_@R_?Nv ݄i%mQDy$ɮCJhݨL"#@'U ?XWHo+ޥ QDb4] Cht|#mYX @m}* eBMwJԢ;T&ȕԓi4VݷC=52*>!M>Eu\N{ Fp:BK>}片(&D{9{RRΡ7^ʷCfy ;}o_0bz˲v&;Q~)Q%;P--BɌ\E?ZhCj!5m3"':A/ۀi.(4U;xIH$ɤ3(37?u)m:t\a]D4\m{OD5VbH_!cؑ6K/ۜ@M93'zD RW[*/+FOR']U'+??{,YO%wEjJKTK>yq0o^JTYrBzƅP,̀FO?z< IϿ1]؅ M˨4D!]HA-+IVi#bI7j}{9d4Z|]uʗlǴA`ӼDx^n|#t/cs&;Ow߆S 7Xp7F";f_'ZJ{*; HD’;Xpr@}aݨ[)y=`cJl:> J Z{g0F ̮b4A'K>Vs~q9fPDTGoh2\ Ț' X6Ӊ"C QW* 3 Dsm  qh BAlZ}b F/%QjD%Z5T ':(3K.^:y ܉@GBj?OfxȏQvFXgTqHCMN@9QD (ڎG;럞|/.Ytm}MS#>-4˱g0ȁ7Ƣ h=o[[.\>*[h+icOAK)h$׀ҷ}$I'/( mjbRۍUk G0 _3$S-͌Bm[e,0pVhS/?*c+^:] = Dc+XȬ|ZPyƦ9hhfZp%ug]Z]n'v8 )e-t 3i;LK@; i`h2m)ıxEmtt}ι,Kֵ-]/95tus{?L՟[.[.^;8׉Ypp໢tT}yH/+,zZz^!);bL/GF= KHP55/F z Z|x 3YąjvTkx+VYqWh?Lp?ur _e)V9N7ju5GQE,JYxxM5GV)wh9@Jauį@[LGt߿to m[!zbsI_pPC?T]" 6ACɺq ]P6q2ѥ5Bĵ#+=dm%b<7[xuN|GbXl g,>Gf&'kko>K Vv50\ =u 5YY\wM(,#,mIps5 16nI†%QG'|(iv3M_8ަkZFiR"<:7ܻZȪ=38#xJQKl;oV2uRxϩTcrv7oKŏVc0ZI>@Fxsp'ә|=${ZZ3?OG8榁rkgͦ=iY|y9OϷ6ɋJߣ/0 vdpl1>V]'Rn@85qѮkuh3ѩKNőqB:ዅ[,P_QߗX DVdGpH3 '8L$"V9E׺e|c9S諏L"1TË逿+9~Z 1E-i?CK+_({e>(to2٥#SM.˻ȯxb_2 ԂBKhk4˧/VAR]md\{,̘]IѐB<ŷX*a" ZˀFeY虪ז$I+G m$I]6ӫXjuD224p@yըDޠA8]R=>͊+$Dnm+n`hCGߥ ` ;@ XEP:w> Wm2A |nhә^4XEv5`& uY\ʹI]O4j(jX<:A{\ոiEu>DpoN.,?U#Fbh*GKɐo:(ѨzEfInnfa&{޿1;=Zb4h'__6ӕbwsWfq-1=fmHhl6qZMCyqi~{5;KBQ8Tm.Fpjub6I6b35U8t:rC5ꥱdiK%KfG:TʭCJ޽.:сRPI<[eV m)5],m->,^]oŌZ8pNщb`pL&R<d_Ie ZCX&tv;ys3Ii,Y|UTʒ;2hGli W墨x;0k|ągnhduB_-EdN:ë(e%eЂ4̐kMV,ʋx{FraÇ/玥Y6]ssx8>y(H=?ߥ39Ƚ6Q gjM1BO&М\j$Y3Qyfo$w +UiWbg|HZoB-,,MdEa!vXHnLqZR{|_8,xdbPw^N2ᜫ^p0 qlPx\ul X&qy9ehJꩵ3!lf%إ+MR˙duIhϮdh.m{dwF߽t %ݝ |@>j2lm|: oOnN] j(B124% ڢ{ྶ?s͓KxӠ(ͭy}Sz=D 6"d-;[EJ` A*i8Dv2^~,'Te@%3rbbaZ@M5DK61>E}]Pj uf<~#뼣w?NU2eSYvUqvjCB̸#|p0VSw!DX[ ?G,/_äF;txgzD,`wc.*ojP*f;c 'qVo}'AHCcV-\e Mxu6_ [ih ~ghZ\Ú+Qa$7i#єQ%Y䉆OV^B =G}o IwwҪxWFxy}vQa {q\t&?vaxb5h|NdJ:2/opu=5.636/% v6M&p7 ȗ'c RW̨BMrllˆ教N&n坲 (6-GR' "?FCиvr|>qniR&V+إPX9?Kg u{uvxJ5Q;3spVK7ᶋn?gRWp>ņ E`B7T"U=&fޱjjgk_LD'Xc8M$+ !2\s( f^el^!f 篌͐g*;)`vv4|X[pGD)<]dɤn įZZ0‰#2Y&iK8P2˜)LC=,ެ *0" j\^ul-^nV'|N yV|U>~ csw2{piїN%89 ( :!VЮUgu&7Ol0D}c-+?~]E'Uj5Oydf*r-5dTq U[yd%"FtzS㴼ﳹr|-i;ȧ-a nxZTH̥JG1h-,iW{Dmh"噟SN۠Յ{{Jv1Kzn1(d{JG]8mkt\47/N}TNV ȟ)zz[#g[ťq-1uYi. !{Tjj.LUrq o$E̴ :lfi3/o5KyLǿwՉ=M,/r=v,:[jiCid I ŻtpD~82~U[!1Ao%%RM[Uj+ 2Sgs@e7 {pCXE^ƙZmO'SpbW$勍dP8AFWEˠF.ImrK#4hڴ?G8 &1٨eXs/Ȏ#XZ.TjQ+$3#BnoM{8B,)*|f E8#DڮZG![ K0ϳK\ t` ˥+[diTL~O܀{.M}(4D=jON=J@9xd_[Rb~3]ǢѠKmmwϟDNC$ eꋥ*f26z׀nKd:ƞ[$P>il c \?qXf.U+K 8fy@P$8ŭD]Jr-HEr1³$']&[S.]y,w}6D]I&Zl. vN54uxrctSX1{£Uk e@IQ"Ż2Юm6!XgRz^gDrG 7hFn2hzr{m=Y m}N~Er6JR2OrlÓ?]TPT\MWu^7Oseoϛk} Z\RF_ZzUOj2$[5N C @ ݤ' ܐϮFK$=nxA AΎ AS4ONJjV%ҞolA1k[̆L# nh2m b4Hb !ڱ>Wj 5L'/aqvX`#hA_]Qf/R}hKIrw|J] ͞^<t7Գ%^l$b4hFb2Hf>0~k:/&D'?;1\U1,-A%TRj'/i 8LGv*6t=g<ƢZƶ|=9?GT;ܨ+%eg]{ 1`Evv~_;#Sƺ{D<0v;']\Bf.gZaG'8ð^*Yntx*3~ژI{+64Lfq9ˬ'A~qy ֺV%DieJv2Hd 54 OS[ MOwe Mِ75 znӪ.})yc/simfH7Hcώvt9Gl$jt s25+H=AZ<bرiaDOWn3]J ǠQ-%L%q{2x9Z!/baK--X=*ٵDwi{wcᬲz#zK[pXtvŃGIT0?s_\8{y9+?,Z;ڪxڿ"9PopbXZ}KēVUAY7%P h~!}cO8Jᴋ풒 'Cv sI٭UFsIĉ}z"dk>q8@!qF?GS㳡\g猟Bg#Ѫ=Ki7/둝O8r+n8,jKCxj7CWaih*Pe%(ML ad6a#a$+J~1=5Ϗr/}jptAxoJ[$90:s*g=OfBo0WYwh7;YQئSCjh"mBW˩XVdP$їOYfJnֺuEŜd۷aeqP@BՃL0<nwe<8㾵۽@Cȁ:B{z+Ei&pAѼowelA+ D̡ 71L#G7>a?,.b[ ,q嶋X]@,s$s3>,u \%%=_w% fA L\{}/OZb僄Yt\)aocVj:dYDs?|/OH(,n^h jNFtjz"IŻQ0_"s{w3a[oh'χD`aAF'L n;T~V=;ZI M:fKUrXd]FYyŕU@[Z$EGSw>cDxS (DSrkSl^iWCnt!@@ҿyYj7HO̮c6KN48jܖ_wpqK:n72t$9̬Vvd*\(i<L=a:h^&<6N-S-[]d!Fwɝlt?X}b_[թY'9V$ H`Y7:$јW aɮxF\_P+ jJm1bPZ1od1$ 4ۥ_)䡞h( Rm=e,Xbjqw<+y =FVTIb]Z8ij?ɺ^RYӷ$fYA]/XXF c ֻH6dd]q|-HxmuSV! CU]Nwaqo$2!S[z2=GV#g7zJ~#@/FKm,ϓ{~}ow%XZees, #J՚&bc$`M_\0*$XZ'1KӛƋo~2ٓl;Z*U@ f¢4n-oU;0 bwS:P ^*kU!&VѐdB1ۅyb!w-ݝ F>GY h$ `àUU(JP0a>50yCIo\((XUuRh0szmy6VqކMV/_[~I!C+*1ēd#s pla1t H4ɖJ&'Թ]sup-nGD+J,Jo~ˮB(:-l,KjN֓D_K^Z-4KE)-M +1X ։ jv[-mVl|5/AwxUòqo2cRC>s!?:Ӂ04e@Y h>&#kD.dZ%zClzqaZ֪¦gqHC1?ϟb,c\ htIM5ݪYց'fAPvR#?貛z?< rz2X (:;z:&q ,[@{AY֫  j72Bb:Ve"8S"E-ekʮr>/!>xۃc*K;$MksBq[(cCpx}f:i $0~.g.p1s)(S@{& %5n61^:7K<ls6j [[ %U`~&i  Œ*Q#Xb$%R6X.i5 ?UI,\0_~j1bUxK<7`aqHeˏAY h+c˛% GѴme <֩Py3rCɩ1+Zu\UcOg"1( yrnQC$BhJN{E\͙'Ep%pb6\0 1I?>:8Le'RD*(a5MnȊ9p@uQnE,Vk [D2#mEXZO \J( lprblO!,7>أ~Z34fL$qb. e)&ك{jTjV5A9$ُp&½1Juu5gG/@0Ngz}HlӤ-r3sQvB(4U-lowG%l>z˃ؔVUĠU}Sh h]-5w1؅\6/ZZkC,/Exb*dUm63TZni"+ƒ勣6}$ՍEAR@ҁkY,V"% zx/<|e 2jx7.,)c <IDATx pu{z_@cH AD)Z(KB&ۑ26WVt^Lj"H`츱2{CUQƤ$%Bר~uS+m$m`DT7fO0G"9.YT)OHU*rpG4 " døգDZURHT@ˢR*#joШ-u:e Ȃg,fQS8 aF$kץרlz4D,Ҭ5\dKn.[i`v g.IaMNaJS*`ײNrh %W.x]aNt EDܻv6aT@eo7ZD n${G1$@dA Z,Xa-J)Q;4! y{v82M;MX&fŤ0Ȃj`1.{܁H[2 .`u DT(|޺ @&kᅶwp=}0@dApyϖk4VǤGs~4`RɏlJ5k0hZL˨ DȐwv_4q9)H0@H|F#S$"} qUT֨HӒAKZjijRIA @N,m!WDSsfs_ZZ͍u!rj*ߤSOE-ȂXpԴb&D4dۄb~8mjj{N- lvwٚ J8|",HaCS "j3*ow78=1qWl&dƢ1r4?ge($ңժ:;^BTn4ivƭͬw\E%8bs\i~~YdÄÍ'h.dnܜMuQ"[pզ%Ot7vfE,LkkvޠB<};iV/oM؎~*{8A`,47gu1} ^]*ne(b٪9vhV^VgMaѕz9T"[\u[݁+Urb"ubB~0joo>вUk6hzW lڭpZNѫn{wn?rZH9z* [+ӂ[ύҢ)>ᒌlr=d9 -Oöù>"[-jnh W:F&QX7e .}=X>&CF^?"+cu82rL[KK5'"Ay :vwRWWy#Y2D;qʆ7ƭ'H/ lR9|WhԴgO7u1' Z ̕bgkb]-UX.&@yxط:%-O<4<+ '<'^W{^N2X["jow7uwTjqxV-Ȃ;lH4nN@\^Zӭs*]ǻwwUmj@dpQ y EbrU]EIP2ͶA[2TWk -[jl_FsWw /'q]8Yi||n]>PYp -Tut4WrFtn`ltq7f-YE΅B QqPVQ8E,D-G)9%hzz& 5L]I-  Il 1l aW /7U\d ,A`kܚe;[MM-lVQ"[p;p\.)9[Y|--t,6g+X`%xU]sJb)?,DJx}l/풣Iצ xfֶF+YF>'ek @d+3Zò lZkmѲLH)jݻIoVrO{DWlCt,6U1˾g Nvu|*Z!u:3%"+sOڎ:Ayk#Xl+lOOSMBˁQ-&è c1;'+z]`Ε(_+76eW\~U\Mգ+b~80 "+ϫuk\Nak6_ FImJϡMkv7Ȗ Vn9罖3\8F sq:Ķ^}kDD3| H}u*Ǩq+ZOE^2 6bt2H&&-tFl{ + )z4ҍb,S9,%705 v="ҊWX41/\6a9+=d1{ )v&S^_L .TZbo{pDvѕuWr>:@' {Gn7 c:n9*f?'PQ  *1C@dKswTU`E]b:J7SS F:Ҽb"["򊅊Y* ΝP՜?[I^1ksXe NKDN9Z1L"[ciAZ,uAo#KT ʹf,$\IJ0SD3P " ˆh%?,kORVMsVYR!K]el.AU)mpxk.sȖZe")T7;9auFdeٲQL+'8azzQ_Պ) bձW @d@GK5k_v 71nx~[4?\IzD/Ś`'b#lAplE/^1\Q݂}5kҫ*{8# _ *|aܷN*M in֊vb+>[ U t gU|~ 칙Ңb V,fozr-Q:.J+GvW b/fM %+Aݦ,q/f_j"#wt7ZK:>V,Du[<^71ɁFK_V~*b10pd%(2(Y>Yn<.zj6Zypo:bm.q xR(^.v*bUEGfB<';]<.)`Î^=BjogȨUc$|l87n= Fz !33Kun ::p̍&!e{Fv讖˸"fv,N$6 5ܭgjzP9gj{NC`D.( 䨨|@7y_\t"~߾Q.@dK ('x!Y p:ɚ="[\+֣]i7)4 V18x=~㼌Y"в5^,2eo,YdQ|AP yYpҋDXHtVl*G,H 8@z8|qN|J&a/{0"[.ce2 ,8nDPgR,|IlқD,BGi Wq"}Gfu( |--9m Joͦf/XVVl67;9P:cͧ@:uZa-XX87SĢ1ј=oK964W BX^x~LQ [ak[a7Q(rz)B'{O w\a͑W.X&^( |qq".2f 4;'y5!x*p9S>t m$ffjlޞzS=˻ ̢h^T:v?ޯQ٭y}l餋'wrT#@p/!@dװ ETenhX\X&W8 7bmP";2운ynF܇bcQ 'U{+18Y}sz,9",7OY/ҦcA]o֒8/k N T<.0C!4`iN&2t|)I`$vF FTȾ;c\:{ݮJ@Rs܄^ڋ {,N7ow9>Ȗrzs-* $L(UfK΢ll^w܁H?ٛ+ƒ6}$ gsClUbšmj/5Hi)Wj4*d9~jdp&UMQ+w|b(@\YkQV6U^5*Y'^sMF\X9&%SP "Ygo Y9zܢ4FÉ +O) ,'Z 9+T7`'s4.išM͛aie+%˹ UpbˍeqW'*qHxaYaȞx(Mz;bxsc=~ˆD"l6ȉ#ͤ*6OY'l^ 7Ecq#).Ql ’TfPWבVbƭ~+>^p(rjA-YyK~-zzNʝjwl0X*_ХbժM>ڳ>ȖŹ?[V!]d<T&|s=c6.YRE#֗/<:\!lIْ!Rt<3T8lV4֊N/:ڱW "[T'rF9ݘ,l*p%?yK68^<\%lѰ[exQ0q|lV"(m+RyDv+;[.nLV,z+6يseʩV NzD65h\L LD57 [gV^E,D67\Q"ECSj.|쫇@L0o%vajZq;*; eP$Z 8=g͆*侯 (Fc11Xdc1DPKpq2?\lhpu7ϨZv C>@dflbzFڭ}\ *% 3-s{ۇ6{#[4ΪUOClY`cYGs=W:QEӗfv_X(}=\VpJF'K\Fv gv6q鰱ygtbOQRٸ.UJIwANْqGw#W8yʜ&m4(*+;!!D]rq gՍ|nz ChbE&C\`^.iLl93Aɪ-r4,`CZ[QqF;Q4Gzkz r~sĘ=k6gWadEVbvJTt)Ⱥ07%D@QE6+XbxDtBX8'7[gXa5[wPs"}Zd(z4H @  -  ;/Bdke d-Z:Yܛ \BB'pSdo,́pՅGXJ\y@Rdn  a<(bOY$T*e[l^\u#1sq_glAaƱW d%hV%;W(%{0@6jp4^GBdEV*wrCde8\Q(YdUH",{0ٲx40fhոKl4nf/F"[6Ɩڱ- ˾xV*|7Qp-`k)=ْ0ajeڽ Q)l"[tҟŮJR#VSR{؋w7V)+ A'Y`/{0٢ :픠Fq:\}%/-f}8 W45g0:.a@9KۛTd/ZCXkh2,LYjvS*řl9`o:@d .zlwZ>Ppd|B^=-pK67Fw0|cD \Z>'dQ@dYʺf{p% -tAJPor ّygWgo*E>2`{pE b:*'+vUduzPh4dlʰPpceEb+p+>+ltbo{pe 9qiޟH`cPC ѨQp F8lX3JXOxL?PC ZYa{p YqcmN_ɕӒRFg3©{j,I'"J%a>~fD֨Z? #J" ڀWzd{q ((&8~1D6≭;휖EN,evF{/ (VuuQP"kDV&AO̚ Y9ziζT;\X.AOz"RᯔA FP͋r%P"Q)bg3ʊuy?Xm BV*efq8??ɱ 0[ A* (z١J>5p. @"4zbyD6{#Ҏ,2uT):ɒS{TJ  vB4v%ވfT!luJ^@͌٨ qg2D#w@ubA+7?Ad@WE25Aȗ:E;n\clɭ7 hh,ՆN/P b_ qsz$HV9QB'XW ":\u:uL~Ң,Ue :=UHd f;T zP 3 YfCeDJ2\Sw4y{^?\-lܻV$G/FO,Fzwѡ>@;Y 2}ν4ܺJaɊ]5x+͡hPN_T9\?KMM" gv_P,xw%'LHB?]:]8od,h4Ė񓟦|/zjÂ{ksg8WPVo:?|@rr>OMdUIOYLlz4 _(U(6ϵ|]<&} 榮ӛ/>K3W)PBTS}-(Jv /h,ŇSlLf/pt?F{vSfb_TS.\7lXt4ϖ|Xa0ݾX˱\Ohqn@ƻ-jhl+gn^aƯiys.f:iL&c<=*V]-1S`M-y6o([DBSJKKv6MX/O}njҎ] JjoHYGXOJL F:pO.CQpvO[}3T ^L W2[pEWiGQ`u?>zjll^H6|>_Zawܾ6%XE_~M󸧧iddN'E"y. =9ukZ\Ť|тY*JdגbZ>(u;W(EN)gX/>Jkk+}S;wV zg啅jb7~}~#֠s(>w9(m8v-*:D| 500@?詧k׮M<|Q21O455Mx)UPDrl `vMR LuÃŲlyl6]i=!鮻'O +`Ղf0;}Ξ=Kݎ食ԹcYPTX gF?G[7Yt9F^Mŵ;?H/|wZ()9MuQX  |ʕFeO[Y7OpwAx`r} _$ څtq`xoң>*וR o>z796X݀Mz3uL -7Ҽ/'G ,~19d2ё#G1ŋJ:e_yZfI\[aɲ:90^Ŭ\ ]7Do =f[[W4KϊX~˿K:~xT/Rly9m,(;>ro]^bo}[|dˈ^>9g?+6)==Ҝ-I:R Z2dԎ@׏jAaP"<~B;c%sz@>pGbgE{ҟٟѱcDztBhGo}n6|>OK'?I!ș67hTQWz~ zը B7qptNsXl/| 2M(mڔ/sy,(8-o~ƦE?f8j׮]i=߾sqGs9B ,o; [`Ќ) lͲєNYZX\]xExqF;.z?x,( ")/. _hӏCz~D,MlaOwbkP˛u\|Mݽ-Uf<9g6[صy5 wmGdrN8f6(&D7-sb939ot_g}6+ș ,ЩB>9g!k 2sfƯ֎;DS؊tRZK"_(kLsNFy/--eYdLYȚ6)qɊe/:fA>7JB$l6 /N_ Vdٽ%q\ڑ#@n^^9" V%NZd^#֙2֊l!`"w4MK7^ֹ~+4vMݸ{"B8 Rp2՚]_q/peP*SwMn6W, c.ݬcr݌1pz3ތ W@ds@Vڲ T Btf9>#傓{-ևWX ۀ )|ݿ"?cGk7Kd_Kk*C(Ι=S^@d.{Ohlo.U=@DLJ:?uc\K$l*`<3<) w&8U^/jEdm.8!x̮b- 3A"âMX"YgZ v9u'GO? (cVHƜY?(ݣo5^"#[MqEOLk˛BܸG sqT N(G;Œ!M6\^L#l~;y^^z-?c+isS,hwi:lч9PXLWNBzXE] $7Vm[hrzM~=HeI(=kDzwO$jǂHRc}vyEUv#/HnP #{fҵF*BO*hHxJnrW˛q`2yšW*?#!e|IkS*VX(U,vGo_z%(\Զ0Cƥ튇k3. Y$ن )hk9#d '`:[ + לu. Pdá@H~4`%lRbJ e C'\WF1DŽ+ф#Y#QtFeRhoPQhY5u De-l J\]k~L*I6w O])ir {;z&N.l4*ٜz*Yqɒ fV~! lR\ˤɪ)30ձO*ۅ y2i9jpm. eѕn|>J(eFD+ ?D&; A,9"]kNA9sBh&t-و&m|6jF9.uͮXN=cD@7A3F9lP-bamډY`E0dY^K-<7iPDvzAgclw Ңd*A/F;ԀYF7Co#-}sd2Bw)[a/49"7kVVZ˕w-=e?eW;y#Z;{3>أ-b&ޠd("[(nn1/(rpsA sRw 77UZzBH"~Ө(|DotZ2O(n*l9u?1iݗ6:\F:tޘb0Qh;uвp7]2[䧖e,殁%bPQLYI+o/ eMlEG+I XK߾1{vy5}:+vA\Dvb&f' ͍_p((R2&jr=)$n_W"e_2}ƒ E=e^}c ud"bk'O~x`g DϽSK$hfGhZZq. K7& n"'JCzLfj)v; ވ@HkI(P/*@qBBOTc|ɨۿՏg/]fD ,_|.9j V++~ c:cws"//M| cT(6.z%97ȃ6.\pciv]%.~_gnB$ʿoW;J!,(/|_ߘ^ePY;q9I~0 i[m7H"x57u*p@&!慶.})vcKVho?QY ӯ_{:zn64Rw~az% X,, q/9RP К"$2+y!'~ѸB]C565$%dG!/Ί7d)y,g=s# @6luYjFT*O<Ηm׾(Y /vh<|w*eǂj;zv.~ ŨcZ^nV"Y #lҏwǸDȎ9}dpjmPSjjimAڗ!= jYUvW1%g0 " dg?pl:]`k)1P@fם֊4#ͲΊhtԷ."X@dΠl[wIl. VÝ}'FD۩mG՚x7OO=:؊@d,Cw|\U糜۱s@DЭk&P ɵ(Zw掲#xܾ` oO}璘=}Q T_1 BwE\3 pqZ^I]B?u Kf|4qm/L*PƺOQkW{Z8/='7y^cd,Y uޮk QQ qΗ"HK#1q0u=.nŒ{ Q8=h89{?S6Dȃwx͞JB'5wvDz޺,kX+]\x-\xbXo$ޚ#Q[VIp \\l=ǛꍣzWBw[Wxk|PѭX>L^kſw((׶vr`(b~ķ:sµCP-`5fmƉǸi=M9VE S. y]:Om; k) b5Z:pank%~{Ƿ>uoaTDw^X]yލv5<6m]h#t3 CL^Mp0@cɾ4~'z2 ߼:9@%w tc=s7f I"%KhY׻r Yhb՝$z#~pcwlǩK} `ܜY\w #uKD$A!=FA!?[([XV}랗GI+V S@x`/]z룿Ys˚X\s![`;=B y o?a}$/۾Iӑ>Bub_p{Y)]65^Lr5suށCԽkߺbQzNޮۘyǥZE ܐޓΝT!/ʘ~ _y}7e@ux]41$mh49u7REz'j*紷f߿8T (X&o>`sD2+H!v j՚W৅1r./pͭ"'' EKR,O!ј-jPx.o|# `7w~dp4Dm[iZye]1DgEMbFӋF={hwdU*T4j>|] =Ĭ1y4+n;m(l-p Wms< Or@>Tȩ?{h]C|Fpw%DfR_#=ꌺ:b>Π6J_6xoN /b7 K[rChKi*oITmS|T.Nckk2n3`_ lsΎ(=|oZwȱ?V/__>t4`45gt}QZ~}%GgJBӮ<}^v#U{/wp9 VV=r@68bjXƈJ"Dmd7R$." hX4JH0! [TCTcx򅷿!Tk|W%sFqy߼y\ RIFAmmͤjx<.5PH\/@vm CoxWPsΒAdYa),=)YUly敩oMM.NQSo_7F~%='KaQOuDmM^?-X47( opeČo\de?ȡ.i{~܏`$/h4fiz;0Jg9e3ֵ_K{zmZxy,-N\:`s\vwwc1K/q~ ٭Ikew/G-_9e\{^VhKK#ux?uwVٶ6%`7k@|s/h>/cܛ߃Ȗk _ " *W|Γ1:ߘYpDuC_Ss(^ uٕܽ>vZۚ6 9_xl}V,xc( {=s {4*kڟށ^cJx$Y"p"X)l gGT ) @eq60؟+o7kV}"8IVc!m29) 1]57ȉZצ;;-29X '@El.6NYp|"A ҧ؆$vՂ]+<9jC{5ɹWC3 dEjO.H0usf%D7eJ@KO=#V#[ϻ"` W،=Q؆JiTJ!vk; PKkcAVaAJ-[)M\2<%}Ȗ./u ,(a[:"A/m4^"gONdz%]C(mekZ8%\9 ,:b,޲IׯKNm'vFTDZL !i` 5̻JTf2h/(ᇱ 7}EcfNQ 7>fɍ%s\yѝ ]</+LO7R7NqYJy_ DE7 eZEmӿo"M'`"^<{f+; {ɐ:/z+_htdl)BZTmNOc02yϿr$\c vXsܺ>IKVǦk^LOۓmMuzn׮6%m]_?j~/c !ipyA,O#d]rbe:=MOYĹm-_nuxc&e/^ytRY%qƩ9M`C3zun}~N./>m=54ᮢJL-PkkKCBFطzarmZZ4Ȏfʋdn7ؗS[j"O-ނ+{Ͼ3-6"B`5(wW7Q2"$ww~W Ŏ7?U@f.-, !M _)lQk>֜~}iEٺ?oW㵠 hiq9xpw?{"1Ȧ"ȑr wޅ襅DN[ _|4L;@\}HJe[,r9=3e8SD?kV|cߞfuJNJymQ P\EppI"@<q'L$}|ϻ>/[ܜg]b*CFeei=N_B{S2Ž6i*!*n(ͺ=4FpWYO#{^,uŜl @X<@O6+c KXDwGXu8x 1$Zzj;Q)_vRK\T/݃d3(8#k0aXQZ"ɦ>ej9nF # 200:)຀a6fCh{/B=_4fSAwz E26:TKJ-Ӊ:pBg'*Q$Yl @h%ƳZ+OQ{nS/;򰠯=rSO!zlGc I6=т%ZXD8ġ=u?i;澧cVȕ+RC?ܬ,ab]l 5u4LW dѓkn2n6\.UZ0qmӤZuvTx`];?CvWZR8+=lR,V׶Wg6̧W[Cա1}u(W d "dk‘X*Yt[h 39;̣xw:#ɦJ)s*dTg@?z}MOMQJE͛ôѻբc[}~\]٤N&=~83x#=3G&]3J:y5lrffsrH?S&Z!x=Nzyb0hV |^. {bnm$o62(\qb[}o$40T 9@`<ӟ¨;27& y:b1iiEf^m*G\fvzWZ p obPPg'&01 m;lƢݪ?moydZO87OZKodh8!arxKûfLP(Lv@+db W[n NfUdЫIYE), z҉labLV*I65U =k~خ 6C !S]`\Npf ?y kH4b'DLjk+RXHWLD؏XMܳWcS\8/k$7H;@c݃-g $/~_P'+: gl#1XX-U1r͐y/`>BXw$ Pj]UFJ$dWnx^6=Tܸ9,>躣 _{o Z Z"&EWt}pl@y#uF`(&iahk(ONHQW;@џf V H0:9xPOcѡ(&ÿåײy[N69JNb~67@a)Zk\ ÊΟF"R"Wp\l DFnOúPX$ T]5ݸ8K/ u|g߳V\1 z]9vHs͊Ǔ/vtjH2ӛL&=^,cIvhCmo id3@M4SC(5t.l YiͭӞ~fG<~W5.."e ?c/^a1-;z}dzeXhܵG^g[AILy`G6oMkesR"?>qDs 04 lfb0j9bN؄'[O"!YnCS/d6#;^4c`{C1|(6ց_/l$+냮|#ӟʤ4T,9/;11:暒B$ R[H|Ɨne6 {YI- BxҽQ U恂(N5(tF.Edjh|X6FbfXըΖڲwdjΗYq%rV?rV0-zкk`FH./٭D྅8qƞbVqmtsu {d@GSlyث ?~qmd_6衎 ̾te> O8.؂3T);^CL&XER.`lU[J2=-T\;@gpS% s0`O-;+yc[N֖ˬ Jϊ?QٴXFTqI;0mܱPoZW&@{j^anvCEx^cS w n(aHL籥/ Ul_'' H)CM5X E޺=fͶ7)ԾY!1R[*<\@J%L &g{|PumL,-=^_gA6#u*}ks|oN&}E,!{bJ8_a$VXGEdE%"7eUZ{W6 : D"m DT7Ɛ!!lt*|PL&#:bƉ%`.Y jdYᑽObؘ`n8c3iI^zdjjN!-jҪ+Oy0`'gڶrgb,Xܺ iOG'aG6 bs{.p49*Xb<[L,GOac̶t7ːkbf#%n+,ߺ=L.'dz޷OHy}pp:o4L.HIO͓d]d9^-5Vo֛M>|_iQ-bfgfhkx-DQӶ#=Y{tF M'=7&Sެ7uyi݉Edz*V*dnPCE䌇4=+In3q35:JNJ㉱,!?y @A &gϮ>ޞ=bT a M[VqY{j(d9cwkU"1fO$e9-"4v[XXcl%fRu߃c}e^,CQsr9L2׀Pp?T&ghH v\gHyv^jᤞMK!.Gm.}wm,''Tۘ^TM? @KJk9<0F&OpyYMW2XzY[w$qrI wDyp%rG κ8 sx E6l!ch<ƦLВk '0ugvuhd`o[Bf$x+@՗,h$Fg[F;b(1D #Yk!"1Sن&BώM/HHkUH>qձ'w@(쳫YW63ެe,̶3Aby k+F*5-5FWOvF\\B0u'*OY"_>I`O[9s6`<Κ]w,{jHH 8+2=B0ʡҘx"+X&/h4F<X[HoI$6`S 94֖{D#piqdɪrD" b/վgF\sG )T^$YD^achưq zYW¤~߳%^jAjj+,B7G3=6Ux2[F‘Kc|z_^ٷ d:Wydž! S=¾Wujad7)N$2k>km)uI=?Hj.鈽8%^*b*q=1C %Tl2RU $ݕYwKL HbGSE/ljauaҢ djͧkH| zfWdrWhe!CA⧕9T.=[Ʌ+\ |l,'^o5+.'${l{n>A5!ެן]8sl=~ʿU7ԛ\nȶRդW & ^9W FEyPD4Lҵ$[Y^ٳkA4VXln0|{D"!%D-`8jГy+ 8]sGriu? CB|l$ .U6:lE+̃${ZWjo(KanRFϸ܋]~ƛ=-YgH6Y PRjɋ_Ȁ_6\堐4)OSهa1uK !jfmD쫰.$5ȩn3W*C^;Q*ŵ$7eR<W+P嵑}`ĞT̵ө'td]E[$xu6^"ٿJC0jQ_i W7"`Z@fec@(^ ._Esjl_*O "/3x^/;J%ݦ/ڢ'$-Dsu{gS /PTܨK3y_N<%)dab~ΐ,䰊 Hzm`"+(43)r:u87ECC@~6$-#֨Em k6lɶdޫRYA怀C"R }Vdq8nLIbtR'2^+YV^A|PПGEl)}WKVɺA1fi|pNAuFrHAּ,l1Մ؇م 2׮ Sp};~r"Ք;KO=XLVCQcd 6[aNxKa"Nʟcp̚)z'}fXlƢ*ZZNƩ9TCsԧTH]`!" m~rbdE`"M x\}chҞ߁bKiRKQC#O2y5ߞ*C1F"2"HH@[ݹb 'R'H9 : [_?d,r'FK7̬qƆ /B^ȃ~qms_ۊ; ʽ$oU.^,\`cݝh7:/dy]͕KJDV6X,s:,` ғ(rkƓf)zP,Jٹ/ރ|tY)< w _W TvU[nyOr$قE׮%d30w)Z qC4ǔ RN)d*UUf,ha/ӛo\p9>b<ۑvZJHi5o2pCQ27ˤ@-x7$=#I;Da$Z)wIX`(L$ D֋e;cpd-X,K(5|ryå׸W[C!t傧\@oIi<ŋdHwo?,/=WQb}qmdߙ?_<\ ߲rj's5j+ZWEn Nf?2 C}"܈kq>|}5?^8AB)B+7J~6盳T_e Ѯ ~K~ɔGB. :Ei6bPe< *=a621{8;q-'s=_C Z5))5gT@ fݩBOuIV$ya4%drݍD[|zC8| 百 b@ٷd#٬2l%<}0ԌmAH'R;cޞz. [r0DSnx>3F ~vϝ#M6`yl|E,fŨs U@$ gGp,vAF7?nmxΆH!z\,ZflazOQ6 ڿ@!16#_8Y/]>!w-w<NwU,䫕 ٳ(.dB #D1f U͗q̳ ,xAltN]p ԃK+"%!tLAo1$I "~. MZ'DLh RCϑ" xS}X3}n"qKp:c^nEO,xb Y럹/] =7}wt "l%eY<`|ޖڲw 8W@ .ރSoR@.xD,GF#lRCIbt!;u\,![NmC^>g (ε֕pyN: Bݿ\SMBRұt޷4s@ED"1gZ 0߻D0]&i#DpV$+]o$uIitaxKy߯,+V}UaNW&Qx܌hC!˙7o>16+.-RRf%5KaKZ ;3C0=|쭌7뜜?RE%BSa+Ɩj)=_Qz]7uK٫)n^Kw3}Z< bn"i;+›aaKY^Ywa >1V80}lt7! Q-ɂ_hҽصX)HKs5iV.ﲾ QŊŢhcuI>nVKm|c;{ zU]JaPy7-n|$E^\^FPtXg#oDjm dYH(J‹tYתt6.IYVBO5+Hl%UDQ o95 ^Z?J-$daN %_Zlxd,CfrF!?'a5%90\+ʨv7X<ˢX,F&'$/V[v]53fnϚہ!m;lXcIx!Ƌ㡀Z[[ Y)V,4r{9 4M$@骚pT1fo\xY׬F^l6pk%oĻ@6*ы#řy_g%bfumu'nzf!ш4+)<ɦ\ 'S E\\cn8SjafhA(xHѧYsγ>V\o.d?CP:dbO*AWC(VuE )L2݇$I ~ w4UldѸ!աvf=?IY])4,啜iఛDv3\,SڲctW rfjrC;^ǝ~|q@x8jj+b̈`ih dE*TϊY-uYA+U J 81rY-z¬pv<Or7Aeq0S'Lة,Kkb'pJ%mUH|#Y#yX ȵdraE#1rKAej!`6Gf'l% ܯe2O]7TxWH̺Y{}(MMus+_Y"摩'OܺT!QƘw? یvF=/EY`ԖJ*ܽYB_ad5|%SdAբ}){`OV,Y "&4/.~`<W mz  ^,Csj9I;J{6q 쟻LȕEeP8rWɹ,aƙy:%{Ju Bo;g lyp݅Xs XUrW> xJLp^1k~0JV[=c-[(}H<#٫}/ Cj.-ɥCZP cb5ZErʋ,0^l4,Wȥ(uXn {Q,nv1\[Rv*iZvQ]d?zkfŖZ5 Q}ٵ=8ۇ1q%Pda<^] -%\Ha;ۈDBvW!N=b@ŦTZ|wl? ay'溴W2K^`X\E]뒥v_Yww$ bإ]{_8.>"Ap#OVo0^!a^ڊU7zກ!(Kd"##Wh\]_D50T)abAޔ wP;J'dgøJ [vE 0GPODM Xl"u4Ul:b@BDX"Y=AK΋M~.صMoG31[k&_,ʌgݑ=/nlҭ3]]FQl vPa)>g_ .2n0g=X<.!Wȗ32lˀ?8,ޱW: PJI CFjbAKڍk3̣Ԫ±_';4-x-l&9^\2PxQTbA. B+Y> DH߭ۂOLuX-|#;w{Q-7TΊ\e)jqr 3PG{`D+024\s8,:e¯tjp6%^Y>vlju|R8G\da^b᳦bFGɖI}kޅk#o2`NЮlF;2-,rKb*--؅TW$+HY(s%8f٥Pv hv)Icrv}ԜQVsY[d} i.,R*t]ٗTRyϟo냮7&y;ha&YjW: 6ǹ/6].y[ d+}-@P_$nF2׋\&@㭾_[n9n $ټ?>S7+J ߣ,Ó8/ %7+XYrN.pV<Y{>fv,W!l;*@`4[l_ xz^[+iTlf]7hLW1mHH`ҩ{:H Jh[$sDž +?ޡmF'ʑ@Uʬ8yN&zֽb].'+CˑtF2:;+xPU<`]Ӝ.ko ;RT| b:y<#1ZJmҩzV9?HYHc[}K)cCP/\ƪYKe'+^C{ Ӻ9A{򧞗W'g617oD+ΰZ hXK y?| cɂ#f[]\!!7e\06.]EzONТ`,~Cr WT$=#sG|nݞ:!x]]/yW daRx3kgY&0Ap_[ [c6j&-;چD,Z7\3׎r -j5Rs2VMGM.,P@l=yTy3z?=S-N6or ၨ퉹^P)@RuN,q}|e|~z9OE,I/P۪ (vAxZ1]EP%ڸY@{7"ڕY_+_An*^Uj:IU,L## `KpR_"&j;ze:e'f%kt?`[\Ϯ=Xj Q(v@vI6h3=-10ާJش7 9zx(_1ڧfDȁr&=mXM@ nw d؞QΆdhU_ 6Gw<IRR|:T)3^/@^UG ;c]3BqTLLg@ H cD)Hc|l"<rb68ϲ\ryVWLXY\CJf9u#6իHzdoCj@>v54Y71D~gl6D9<qwYNd!l _PiH:RS[A YF Fkb\G|' u-O~?śδ+k =ǨM@ fO׮!<ܸ6HΦSj-!(h"evJ{P8`W斲WC5<`gÙ|[#VP*ڛٻwb@n8T&u=PW"3h! rY RȿBopC&ވ`{UkW@]3p\& G6r+hP7./1 ov  tzLzgBe!,e$ཎNmםjg _r#|`G{ܲGI{F-E Г<mzbPz<=dxxD›S3:ppeUL2!XLM͒+z@O9N}FjȁGOyzQl&B.ܷqR*x!~OEwxz}+T 'TCTjŦr`]vp`VF#1hBUՐJi$D;w^dU36=\]⾗.Ny;G5C{C}_,韨{4Ѥ^w !䅾^ୂZS4.V?`< >o"ؐ `Ю׏}|tbGEI+$zjQ)֕Rɡ?^^}33 HZQKl~!ZCd0J Uz4 QbP0l4%j#(R}ǷZ,C4Q,Q+\G+8\l=/o6z.gGfr) 0t7D"&jAz~ו(ؚduzjÑM7X7Awosto~@(,T[Nq@ n >s_^y-74@H*Y!\fȤh! 61>Cf E9%[xO+^+!l2R5_MOٮV^!l+{*nOYׇ7ᨵnNLwn*IvKqwC?t/vAeR J5VEd2Yb+*]sfG-EYX ^7 -c$˞7|!‚N8H)Z򘣕ПL//z/KiVI_xc@(CEGY,KaX%CjPD$I2m[yCaڇThBK !`9*kZSr=I] І!V>?H\!Lα;*N$Bfx|oJBO>@ WӞb3Tb#C ÕHh2r7vWP NeY2 5BwG=V7V܎ʣjvj|^Qog *0\Ƴ/>y $_\5̜w_.""/&J6r֪B3X}H2_Ku3_WzR!g<\CJл Km6? Zb]Ty@gBˏ; ~P1,$+H[O]p03"+Rw½eOt bN$"M,)AQ|M,Cy~iUQb<}5'/o O=׫5JR9RSƣ @6}Ƨ#1+^Q! #{˥IPe;谝ې7Ȣ/"`oPB*4C H910:o%KW¹Bx ]j]pgj Zѕ|R̄` Z+<AZF99d5wta1 COtJPenN qFM7'yq$)Fgs@b}o]waG$7q./Ս>QNT£QʝzrW ΎxǦ!o<^BK U"^XQ:HA1Zzw47I.)8X,EhyMyh@)RGuR\jܥTowm%Z{po%$ E0j[j@ErXpHSv ͮ_GE b\D!#`>,`;qgd-̜o`ažpW$Y |hJJуQ @E 7&hg55=OڱRUIS@=ѓ£DE dB$ٖ$O.l4= b,Qpc2Cp,`y#%sQ@ " d;_B8Xe;w=;@ dˋ!]UЎq,@NQ&:[^?z@ECcnY,/X"\\y`,G'$(S䮽mկ$@EdB (5= #$Y'eBAe :Y$%&OV$"vәA;"S@ dEJ߰p,`֫{K.d,`ԩzIxF J'"$YDxneF/4@EjW*dn\i$(:8J)w˙+IQy<B;2@E%0)_x$q,(Qf՟hPh F\sGq,H=Ym:q,(UBG.Ž.4@E%6ù|yhp+IQ(ϋY(~qUF HDULw`?~IQth-s_6H[?Q@ dE;@0'%>xz[al6&1ɻ'7[Z7]]Wi.""C{޷ݹf!H]}ٹJD9=YO;IEe)J׽N"`$YౣT"Κ6Kf3]ʻH[,^CȎ]ͤCU!).x}#gnϲ͊VٔL.%&PHId ~^F6J}7I0]^݃;@ "}ǧyTB-eEIB8n%*Pφ Brw @E x֪љW&^^[ƈA&I be!$ut)R ZZժ~y\O3+ $@ O|^<8^ܷWxD* ɉIphʔ@ GDs{pD'M!'@ "Bκ{ƇPdzȭa-o1 GI$b:4w (` !nk%IQm DrЀ`>䨋 D⯾ͤw @E x'xQf:KMd'HC%(zmĘw#J!:$@DY{!bq$ Tr2#7k7oBuUvk;!UDz~v7o> К\M: Ks=>2AFI0#8S@E _]1U0k!o:xrLC:[^Vq,(psxWM-z-mёIvqsM):!$YD_W+/Jgռ' $YD{C~1658s$bqf8]u?T*NIX O<>9 DRF5_W򮠕3I]>8198h"P$jKhH8*尛`Q@E b@ "$@ I@ $Y@ d@ "$@ ,@ $Y@ d@E $@ ,@ H@ d@E I@ ,@  4渜IENDB`hy-1.0.0/docs/_static/cuddles.png000066400000000000000000001321621467401276000166530ustar00rootroot00000000000000PNG  IHDRoM.tEXtSoftwareAdobe ImageReadyqe<hiTXtXML:com.adobe.xmp ZHIDATxipcy콵u˲-knlIJrCjfjRTe*_?%UI%3ɭM;lŊےmYKK-n8:Zl Ab#WEI<}%  AeAA@APAAeAA@APAAd @2 Bw ;XDzmv?oG%E|C{HX~>RwOH r|1,[7m0e5S5,Ӷ ӶǶ{ߩOHG 1$MSAXp%A`hwbA@H?f$-'jf gSI=gqaYw gdlGND:38"qNA@ vd,JZV@ o<8~c~.|P d @I ÙV@\1AfP Lw /iɒ`{!eDό4n>@P~Aֲ y۪uqt-0G8Fe! eCA@@.{jY뚡(|nUC  ˴rE3ϱXehrɞoj2$T 8!(LE͊i@𮩺,ka-[U5RUo &yxxG".y`z`4E6<e=YSe%QTMPK!"* nv+$qL Pl,xA> (H/IZ  ] 0,M%QE\ K B?Hp|<xYQ??Q ۔U +]Tkfg]7E+RI+2|(+^`&3,TX A@i]$kٶ]*IiA/#Xc MReiShШdYK&slIT~\$I¾sPlM~xɐmA@akJ(wn?p8ax< J0$H$pj*p4Dp' hTTc9].HP[T,V6֓ X(rl,<0r=08F|A@I$НdV,Ʒ\XՎ3$ޅ{ vA ئ0vEK%sxNoM^aԭ# "cόA@=Q5ֳbtϗcbbCԐ@FGCgB"ōfR/Cw0JI;%3z2B쉓ӣT/hXf"^ |@Vڶ].IX&)bvumHBz/~?Ϡ)@PD rtZ͖VbGΜ!=5pAn OYS h'Ed20 =8 }NBOP 0x%20؎SxQNպx5M_YN C>A GF ooz3f#^<A.4JЀF&:ˢ(/-޾"n?y칹p 6w^% xn20 dIi)O% O +۪5oE}s/LNMuvP˺$ ?q9)20@`+Ȣj4 #E@hHnUx6.TaS:@`*,rRe`L{3'j˜0~Ѐv4-CΫDssS#թ(LT5ד!a6[7AJRe/*Vz{;35: XS'NwN <{gf#^p8@ >v||F;wbtaJDԔ陙ٱNWQ$DXA[tJ3Z,c8 CNp‰&‰/$2ЇDm Eӧ8Bc(۷*/Q7fg^/ x7 A'%F4 *l'DQ+^%`Yh9 -xb2ṕ*)[y]8A6S\^ޖe/;=p?rscc{ F\㑖~Iַ r^TͮBKZ&J/LOOG{>O੖! y>bċ[%vmԀ.q'ق̍&B.&E@z I7E9UV4- [),ѶJOpjzjj<hsoO A1 &ݼ궮cAQe03#D M049w+e=^Ar ݹ' 0cS 8 SaQ Pg#f Awvu ,Lo'Qzǟ93; -$(` <b2t ݴ3 &sG7պkkV5 "ROud`: !6(HM@I1Iar=Dumh<[C /h38h]'6P!^  ()+EYzd/mtr[ϜzyXڐ-,VC@e;9QK\5J$mI$skq\z .,L{<xu8P#('K(8N2[[K(w 4?=̎<9q q\㬏gޱ A(HV͜X{ :jگ899770UQ?7G:F@@1,0Gܼ5گ,GiX/hwx:ey=y=!@߉WVWe K_̩ӓ#=-D쉨4)@@@dQdTh(XX/ܙӳc㶙-t(]$͌dI1[6ѕ_RHxKΜ }* \7p SQt M. u]}0߻˕#s~eوw2 %h EY,5-`Vl+ϗ]IDh2us-+e* bӫB]LtJщF }^e_ D .ȷS%xQ ݴ׳b^lvI>hu&8N2XOl2?Qe0:LҜxQ|at^;L`}U.i6e`(zE5Ieuk+-IX=t(0 km=Q, QoddU8&Ai(0YL=WEml(e;`cT,f6.2rr"a@  9qWN 'X2>.2 @ȀiEzkrI)h`Q !87p07rbreٕl]t݌og~غ7tVY쩐@taXv/$(y<]7 JţQcmI4{YwMT쮝KYV%I iSy};]j[ n'Ue;EEKzCDJ.W,̀XeYM#9`k'̍G%E7pl%QߏNgG@S.!Ȥ-&1 l7 -`pʞ$BQe\#+| A+3N)FV*e<] ΉZ eulH yH܎eP+7vKqr?XSt=1AN\G#k@Uj.HS*b ODԱ`;=@5} n}C5@ZQEdA]7|(g 8(t2F|Q7F oŲbxZҢ/Ki S,!-oPag}_3P xXu1n{ՉeZ劌Ɛ0lzNT{}`G k5SGŻ0+--PupΐAp϶S,ZXJ;R((@7{BׄPU@EٶԢ|~6:Xv\K2fd݄s(l@(b6 Ф Jν^1^;k ѭlUiT'ix+*81 nS|Z^%컎yQ=+ӽc99dpg*C?K'L<a2ВE9]VCo б"QZ!_DWjkʋXed*jbϹ1]l۶GK,Xt V =z i&Xm׀]Oh!(*(nJ z˸vep `9MZv ĪȠiizvʮѡo!҇lEKc3аHhpރUj^V[v%[^2,nʮ $,m&4ŝqbdTUk:E6+sJ P撗]Qrks.˲+* 0KMZ#bOվ|` tY9DjܶKB &'@}(ؼF׍bI{LbAS0 z7}( 5#]Vvn p%5mP,T߻]T-GJ<ǹbSjiHj}a70 \& !}v-@pO@ո.E@(JfQPHQ G(lhLD3 9#[`}e : $(?3T` H(֎D`^0X=epq,yfԯ+j^jpLAj~ӾԫtQC!!eKLE5'cc% 9N NS䨟 Gf1rvwtYVTS-D!SMn!/+ @h51Bk+/1h @ xoA Q?yPj _ @ c ,l ԰,whar& A!3OqA@H<鱀iEYoDX[:,3s#>0,2H4xVd֊ W׌Q(VZ;rJnAaGpA2dzl 2(#iF ˪;qFoje!& =)DEF 'KQ]9*j&EhD쒬 Ƕ zn{ a޴X`>X`Z O%C4 Y3N߿$8K vy$0` ƴŰz!TN$C ^O>oL?X({d{Ybddw?/8.2 -D8K `^TY ԑ<) @e7+#xaIf2seaAr ۛci:6 O ӕa9 Գ]v,K|8= ^/ם?Pd遠#[_aC!@I~u tFwXeljP$1|\3@ZꑫItE(`!X!I쾟 e`_h!K( 20 `S82p!ɐWo]6A0]@h/$^ <4X |OV@@jF7 4 Gs#>6+%bkC A༽,>}(tT ( GEObB8<{TMAG۔'4nrA(vwnbw[枣q eHŵËaU dB'v y/`|3@3.z);B %+!ϳ:bV }W 8ʅƣCNCCR&p$F8e84߈făC7Ip3M9QT:Jc/dyDQPʅJ1a9/ ^a m+R9^{\Hbԉ ?~ikRњ hԡϏ8_9x /DmӀYQl tB  ؂Q:C7V$ImmoܺVSc{a}k?E.- ;_m:ϓo/cg8u OHp? tvd*JNTg7Օx,nXX;?A+>? z xhb{olw&щٯ?+r{H\&E#>D($1N/NFEBa|lITEzoWRxF$nw`rU+K~eGxQ}>s܅gƃԀ@!^{bei𡐀Iv nŊG}/վy~ttt||ܲ,4M-C:yOd߽ڿ;FU,,,<裲,S^%}öH$05g&B܈;-RK׍pKxE7 =q "QT0' ?~o]%Wβ,?s\:`ǟ֛47޸Aq@zw~ַ*)C 1 {$oq~>c" ʹ*KYo gu8܌*}I x~gԯ2࣮+/}ri[Y?y>9 {ſG>M "/ollL%&~#N^C;cϐA) EX&Y5ť)%U=X+o_JQ~_?i0D?ϝ;wʕl6{  Nh,;AR4ӧo@ Ihe{ڵRJ쩋P?==ڢ7q߉o<(n J^fshӑZ,3 blz,O==ug%@ mxǻ2GΎ2??k__Thr]ѭ D3S_H`!B@'g0.LZTȤ fﲩMgΜ'xb :@ W|ٶm,߿*D__җTw뗿eЉFIƝWy]Aߑ'Y<3|dvd< @?ӂ)L`sԛ~ʏSU计P~ ?p)[eOwK!koo/SSS_ hΞ={ɺ-݆ |iA|>ho'0`Xc2J0JK~|"rpV8 Ko ;t/of3yYSQ@Ko{oq]Un]{4>u#<f-Om,u G̑qB fe`@7;Z%#I"~@@qOu_j}_ a,--AdujjkobߪS.d[+;<|>X,X5_3`f!`F ! l+!)8fd$OlZ]}533SO5uOu~n~4LbRf4G (R)޵~KKo7ôL˹[ `:T+F!*+nVڙ-9owܱ'K&BP{ʕ+X?2ѼޗF VA( E<}hthŢ뷤Jao ,//{߫[3`OR)BVn*l;q{r-d`o܄^,BWjSgIbq!wST 2fq({툪 G1a Gd߸CZp@o[Ӏ;IoUJy}eYbvOO{EQ l6d-aFdY5 ܁S+F𼁖dhs_Eņ=rqOxiޕi=H46 +dS't`j1{nzz#40n޼ {?N;N!˅5G5M7Ќl8BpX.㔊"tF;yGԽ]ǿ;c7$zQ%]˶( AȀ1K7/JJ-..&S)U9GS+im۱ ϾWz`;=Pf بs&6 ~yO}S𝥥o~W\ifZx/L" %`3,Ӑr#Q*-*Gغf8nr bߡ+Š4UM)4]0  CԾ}jUY3sItʾуR7޽8XtLöQD*D)ܻq67PKXxP_ y[Ն*86sH д0sx\Mo@&f&(2!!|!4'!zғi];ҎEj>µn]i6aYf?YvSoW E4Q;ҚާFN~\-W2Х$KEqmFee`V 9Mwc?޵w ?KQayhn,jI\-20$^b?i_4$P1a&賜pwV`hFZ LݞںjK-,Ia対t2|4!8p]]0ʨ(C$</7i9`Mw/2:՝B#GpohV041`XA 4MN)C '<Op0:yU"SXxAx7c nc# FFy'a8ȳᑉ$Ca?%![|Sg'sQexy3 ,/L<סBP*\l)X0mhH˷l£S 9 L CcM(B&Ba/۠;H`dİS?c)zuǑ#/3_`xHD> +rg. Yɐpa*tLLQ$}XA!fs'?VayG} NEgm[\q3C.>nd&h] Vo瘅TH5F !>~q#O}W-A]#/}OCգ`_&(HM~DTxvwpZn1~O}]<ȗEq8ayǞkssWgc\#̃HY_\2GShșx@zi8C?RSz!ERg> Bc%BGFY`mc`vl/Aw Y0Ϻ%Ⱥ)دJETJt|ܣO|e۲Ͻ¥}."H…~oʕ];P0AIB{S()qT`g> ފiP];ىS=I5WYyڪXJƭkeOEӂ7GǦO.\;0' F#/?{ia*g eQOnHVc-Z]ܼs`ٖ4^?4DoErmLh)mYǡH C|{_ 3QB|A G`՟qwʽʃI<ا&Nw4( [LT E™}d~r4`] '"Scv.*ʊn, xݟ*V+d! vmL<ݩ}y?}ڛ8Nm}g%ߺ;#Ep8 y'ɩhgQCS㡉#g31)m% ٢XU0ﭜX|B6@_9~(jO?ٯ6 j:m8B~~f< ~jft|ϱ>q' s,J$*J,$( jQD695U0Sʅlj6BFOԆ;Iݱ%7n^gy/^jİ'KxXFfh6}p@!p̙g_ޏ}=|>/dTI \m {VO?@Lg9} 'x!En5PPSB:v"Q)/h*20,ۛ\QDfTgerW_J9{jĉ4֖{>HT5`9~0ZxZH`Ĝ.95;:GjKJ|&MwcZ>̻S,*8chAga^O (mw8#Ua"SypacfA+ |P: cVz{MW厾m䦸vC`d8_&7Q !(EO\|a߳>[#c3hm\ɧ 펾*g>ԉ8~ 20,'NF|q CS۫F;`B&^&Y=&~{rO+XA!/}~/ ӵ`R[+l^KSڢws _?{?y{#&20@=;3p0Ag}FKJR.M߳ .G& OeIv+߻έX$Y`CS#g~lsq!%Hl_{o`%I8Vc^1QRQ Ac?~t^8uC4վM2ۚ)[l"qZùN=4>0bOnose#M7)} 3QO 8(?ҍweI$s sD-[W YMl-y۰KO.ghHd;?zrB7LB7CuU3ӥߺ{FKw.,\XYZ;U#s`Gi~ [D{bSEg ? 6￲s5mFC'>\K~bDZJ;?-S;GB#㗟/go/?!tqIJbY~'uݚ騵CK$y'ϭ߾v"X{ocS?*qZy5Xȳ_=MR8&Y͕Bҡ@nZOm[d'ۉku}g9{;XZ[ʕuMY_zkw{H:g(q 2R,:RXaXQ^?=t=eكM̩S' c~KWonN41wßHs=JrW]ӉcA萉lGWnH73 x>"l*@@s^x|4E\5 G'>K96ub-hrbϣN@zt7_xdˁ/}H=wYG*r9j~2B&Ϟs]>쩋Tܸ42U3w%^~s#3;pR.+ŌVCW3wƭ]5鼾_/ ơ0wEkŽ'pW %_Y6v/̞E]~f|4t,\\߸# /G0-nE*>|.B@-{+U+To.r՟>o= ǍNGf8^Pr|V}R A#wy'ݥAH~婐(H^~-۠x9$.d/=7ˋo';U@'~?̟C,- τ^(H1-{=]C h9YV^SxIk"N/\觿/YO`ygk":jƋ-oQ>²rCM]1ۆ@_(+f<.fK,~/pX>l~&:3 ,EG e+>j }fHO} mON v ܯlW˛QVm/I,C:rbl\=@l|bEND_0F RT[XNӍ6[PhG;;VFpP7@[PT/Ub "ІEqy+ b`qݰ70-;7O>qfb$@. ,Mϡ }݌o??c.KYR.mT$I@T!I¹;btyôn܉3?~S^(}m;kְr/_rյΥdˉlז6f:8EQl|V׆d-L0LUUES!W'[Td{ J+wWˢWgފq!ďjf$P?0366° `{mpu4]|TUlrWTeM7|*;\XYe+D~t{ z<`f'-۝ ~+77t C>9?A-8GEf&EQN&2۱aX__  c B2T$u#a5y3ʏ߼ }-d>p(҇ ~j,6>P:$ Pp~2P "kv'nUļnZyA n% Մ@ Cjt~zz#$)2𞿸cg.MDL*Mt]Tzi5` CgΞ)wEP;@dP(K2: ;'mY\M^!+:e%QP0,CtüOnT,.'jɈw8=~~fnnӵ*e)reQ݊tneKmN:95raaܷJ)@P5c=p@! MY.Ih wM.- 4๗oT:˾wj3SE(+[L&kUV\gcAYV!c{߰ĀJYT/̳, V2$m5Ml [!&ܰhK+7 (1[x~lt4bEnQT׶~DdQQ zj`b; NL Z*g$)BP(K4̗婱`\ô7XPt.lj,jf"ш˳@BC̈1 0!q `m>P˲V>WqBv eٲv??Y7@Q{ ^>JТ %es=ϑdי(gwL ǐ)ԮËxً؈ۈ}J I ݐ}ByҾY=@ 7`*; -UijhJ LJDClE>9LJwڕX=L'%CЗc`6zj|AqkPRqv2OO,KOG~j` Q=LTmYzZ"e (J\\|#0TTR\OuHqDZWN@QJrO+5h06k1 @E!#sfh(;ij{Twf:T*"Q8 yGjUyQlP`Ǘ}:4Cݸz1qyxV;1 XSU}4"5p3:=b"w @&gzBpj|7l_,uHXX ?\q󓁿{M; HR͝GF <bej:4ճa!%j~!4U861ł4w=l4x\ {׃>Gz`DW˗-j^uȤɣLOs$Td~Q;3_Ajl_>}V~W!-LP<8IL+4%ӊ_̉C40,$^l4043 Fb=z~|;-.oeH͹!ʊ6ޮ5_}mxIs`1sdIΤ Oo87c(DH 5R_}U*Uuˡh`H_:Cs&8N{B~L0'o N  yUV t:g6%ZM*ZG@u(l773n7,o.śV%X4r4ຳ6Î2EPm%N4hEQJQfKzc&*.q2V{!ds39C%iPt M~qEq78ڵoxW8ts{&+z$A*KXpb2q 8nsj&lDQ%>V(ɏ7O=):;H.[[b*-wb+sanX}9PА_՞~\ O޽6\PiX567nD?YC0PSxUx|"?xv5Q,¹P74 %tQ0φO^2ilhp&P-:f㍣)Mo?kAR[N\$s*Th`x4+B=ɥ7lL;ʉ\ |_~t+w ^&i\ܠlf7=A^VJᰬ̅Ֆ#bPDAQ/WJ=H`Rktc|cera"pul+8<~HS'P(z—Ov^b4MEbAzU}j}gPs sa2:ѩAH WaӃ:Oj9l/d UqIలa$|tjg'}K 4GݎAuQeִ^-D4p {l窍WO9|y:r,R bcf<:5z$ IST,'$Q0n-D}.h'4 A@gU 6Ȗ>{SvAQLU$p}/dR~c.÷޳ ;Of22YQz2:8])Jfub[ fu~ yO7t(:֭KHo{Gl)Q z"0Ow6hS6Nz/z  )2@?5eH;΃%~+8`ID 61a8F02py~8T]M }Ld,vna"@FY)D#8ml䲦 ޥ(W}lT6DZƍKG@REQwKߜL܆s0݌oJu)z!p3`@* l݅ɀq!LиOL儬$n%O)by:4yr Oc%O$= >jomTj u cYv8--*Ţ>c٘H,Q.ˑ?.:) Kt*v;VV :`Yڍ)hIVg|xKY+өaPiF! zsir]Ufxy&t<Auܜ6^*+˽ +G_ L l+QDu FIZyYCS!AX~Uk;ڄ H߮&j9uoyxlB!ߝ8~q?\;PMGv+c[xAFY -?"pfX xZm㘉hFn`>{sk/huw?P zfN&JaV| S b Bv")vw[N1k1ŰOBTE@e,U %\`" VZh:ԍTMfӒGyʹ2 Dh\Mr,OMG>%XR72X44v X :n@:8 yhfRirπFLmD%P^̐qH,ɇzA9e̾@iO6Jvѐ H A)o4 +|e/D`>(n#D`c|o[ h!0c0+嫧{RzESSAvB}.V HOútmm@j؍纗XcZjfj4^ ],^QQ]Qg*(GR"0.p!Dk+*=S 8-Sue|8f_=Io'*c/\6c]$s6*~-7=-5 DN7^^9tq e h@`(;3^jT=dJKAˊ4R(JKkjm"ԩa5.cP0YνD";]SEE^Hcj"6f`р￾` +&{ 2?;2tFs^_3{7ҵzXqFer]2Xx ߫8j̓U kcs9c_E4` 4;L,Mo/F1jfm"VºX:|(k]klS6`X9y{5_HUk~ L_T> dupdcR<'ldzW l5(y I܎|O^,CY9cGf jzJ?ᴑ@YS.Uװ 8Gߙ|'Y{Ѳ 0`O.N6W(J v 5`lOb,PI$|8~zCuuԗ&9h:rɗ[SEΰy 4Lb!d2k<,GɈ7^L& 4u:ij{G\= j۬Յ՝dGpVXo!%2 T̔k޷ &vJ3C2}_]W*,T$ G/NsRl\SǧgcV+gޥ(kGbw';M.;gZlڐ 0U#r;,EX^9pQDe˗$H4rx滖.,N p}7]z #Èؒ^"׏{is?-QѦ  e]\P(9@k1 $p JY MĈ$Zte% /C 8cՈ H:5ܣ_єJ}bhu'"DJe}=<<#jd/CKqcJ;x\CZRJ#Ca7|TjOQttXZ-v p4Cn6LB-mR4_˪c kFKp0[mhhG-%N,sFЭB^G,blQ=1k Ibk"!;6h3GkuvO0GH GʺWQ+ `.L.k=2C=_44|Cx%z( GŔ&d-_,!V΀ndRm(o8vȮTǍia4E4` K`/APrO%xD8_yn N(.i4Ek*G!d0/YP*S(dh L~]US/IVijwnu">R>WE[hj3Bk(EŰxv}/5@B{~!oT Bu6nepcA4`&\,!86ӏbT1-cdW\ȟ,Ev87a&0R Z)cʒӽR1Y4b p UtLkSŖ~2XIXoߚ,YC b Wrc񉉰Ǔ5#%u^P:k6 ciEƒ.pAOzmhc}P$˥ֱqj|FӄO,0{y1/ k4t~/JI:Ym\$jir rn?%+oވc:徜Q.a]RUS8ft1hTLc3Qإp 0;9R(>'|^nhv=TwqᴱF7sೡQ7 um@Ghd Iр8w:0E*л=ː!CNjBN5ZXmU.Lf]h@F)ƾxCr ٨o*~~ehFV kF˴ ba)6~Ƣe@vSx3 4ЇWGp,uK{?I%YHӊ%w2Cp jS$iqf0ZԪ- v)TDfHyCʘB˶;RmH}f;cJ&B,T\."4Ž٣/kun(w,eR VZOPS9,,m р;gV֤ׯ°mX#(YYʝxOehd4ոQZ4q3HEk~L gw]ڀy1Q 2~26Kb7C^uv}Œ#Ada4ZOɱd% [5h^tBFm!kϑ U3k;T,8``ehPnϼ.Y"03 nBd.Mct`(`'gZk[|kwXis͢Q|֥VXPg*/?{AD-Lz|k?=2!k5H{ݿT0$c{EAD{ofr():Y_pS zV&[`|w_?r[V2ZBu.GvW6Shmb8Nڦ.੗da}?wEY;wٿ]2CS|%ҤN#VU o1/i^Y" M,7&*]ƅ}\:6sk8[[fϾ] ؋C:)@󴨎lX9c}(Puw 4v9|? AX4t8HAm`p`ɬj شdh@F?):SӁor*%W^:}) ny:{UZu % "Nv$7jGpب mv$_wk[yS24ʬւoº^=8g1B!jL.A86Z9_Dh27;+, R;PSoӜ^Z<4 k5vdY6FY8ᰲW&+c5(rz&j9;Eh{;qd;k0Юφ'n3F4I>~ŚN|0 <Jk%^c yvhSрPݱTʭOSΝâ( t~--fz)䴱`ϩJj)Ω |>Jdi}{}eξyM+MKO\W^ hH+IUuuZ$:HWE eShqX9S=ǟ5vN$sełnDDz1L.K2@daLCk tΕe=5 R@¾ Gg3ltS@}[kQ"O߻nU]Au&XA՘[-1fRJ%^,xYRr" hQڃC]5Sx*2f#JSn+ [bM#)YLu^g$ր!uiqg| ޽;w-< A$`X'.4̓ :E0;x~GDPyʴ W3w9Zi"H+: GS%l&}$D6 Lo ^}g\ؿxa{~h om߻6~s>Ҧ4 IaD}F{TxBEQ2jI*ȭ:['^3 U|X;QcȦӥQY'y#kޙ.cz&:;6IPgT[3LpiT^QeiÆ `^SdꙣpƂ\r59^ݎSBfTeyg M7cA9)! ͛Sq>1r"p@sA\>MG"egNh(I*l"ˡ\b)ZZdHvDɣ6ʱwuED4`V$(WRMw0[^P:R!Qׅev!|ݍiUu^y1)36CWouDc򺬰 kͩQ 7,o6E3sRdXZ`C>ǟuWdR'c6/u#,$YI˂gi#޵<:Ewj ٦!(ISgy]p-9 nUv2y,86> Sg٪vjlm:\J븬phb.} (-d!E+ƴ%# [P jgaz ^i\j^ aRsDM_,I\ ޿7dVgs^ͬP0) V!,T߻I2Q \(t[DZԉo&2jw `NV'#9! zv s JLqҖijbgLj]puZ.}-+|V\3!4ǝi3eBZ@ri:7*:?oMop?^Xp)mGV'x:+xwyӗ2/[\mPSP+[G 'I(>P5/ ?\rS9ƱZ8Fx,@@C]5`\PC܄8i`/L|7ᓇiղwaij7לL6'J" ؄ޝ] XtE4!|씇)Ӗv;K<hA2?6t- AZ%t*m󹭧}lZ8YbVWZ k7qT>Ƈi?~|b\ӌ"Ir|"4=,[MQjuX('Y+z)Coτ}=W,SHi6i3*Ipf$}Rn2[v8hԔZR) LFC o+hhFkGQh+;+aiꍛS31?AF09yL4 2L0s N('@BvR౿~}ry:NWjj_NDfڱdm%Ԇs!{{8EݥZs7ЌZ'u/ҥfhySѸ\060 yvɗkr׽^LqR}5>/4 (J98 8ݯ^naߘ4n$qm^99Krُ6PT5P>I[vmr*ҥߙɗKU4AϩG=&2&. pBtȦ@Yq:{j]84ױc}vg'U$PU"/,J ~z|5G̍^_d t1ESd4\t8̣=0ӄ@Mjw_sB7arzO*7$}φGYǏnmgxVji׶)v&:NSMF?$kffa[l!N(T57`3i?<+siah2 ؠ±{d¶HX5rr })S |F>Τ 1l{v&0y7,Fh 舊ũD ͣT8*+͇")Zn[*00=ln8T h4Һ_Q ,NP'dEb,Jv|;拵mˎws831+{G;e=Cr^<][_aSPyMɵܸG뇩\#]E(0'KSaci*wg"ɠ`(6NjajTJg4ax[՝$l n%vrW_ںu~o0-oT'O%[F.{V wٸt |L&sb3k綂 ; рZ&sZu:(<i N{0;D΀>^R'C >37eu\@XT2[أ~[@ƙBP劵RQnSr 'B;uR qBH4aVUu&DG8p2Ho7r9jpɮZcRMt:+Rjt mQua)n-FAu09´:p;,1"Aj T%B^7uآ\mTj> G[;9C>|Vq y nneX<4WR {O= is{z$>wٹ7V,PWw;s l$Th1Tgه2融f AZ"w)`Ҽuu'MyPmlBA*(Ųj(Tjfr/W/J/# l&.qX$[rv'C`Say؈q=V,7*6Ũ͎Y,vEI~v0rR~?tߘ\\+ň6.R]5zMX&G40h\ cʩ:6sΒGLQd0dڴ5/`J~z8b H犭+'lp*# /(m\p,}g1qY$j06Ia\vsBz)4ca.bV?}t`@.$߮ 'Ci̎w%N]J:O8gShz]" ~l?y`Z`XşeC&V v3 A&N%j]0іSMvOLh"Aa =>rDKJP%K.:icJLApf[>:}ȫG9PmK0P45 ;ࡺf ϶ KK2:v j&[0w2AVDÇTäm6e?h.$;lTF1*'TӔT Ol 嶣Թ.IY@ROl3,LnGQdB!c2~VSNH1`/t99A&(DFWRH 6қH0EY?6x΋jAQ.gcLjaY^{͚;>~s粝-6l`s}EG [M5 W\6D …#bҿ@$^ir|N݌ύ&B\ԠÃ^HWfBg.䀭=KXKe_;'xp Jj1yKq}6|{1# cܘp,;l!O O/qff/G;ȩh,r@/Y\{x9,?Ri{k$IOgX~ r2|[% {'zՉogg. n fuꏇmc$ۋ18X1;C (hS?r[rM'~~a0ue {lA.;c6uf.xhnq/ b&?O4S :!tCSX̖~}Go/4,h\L흟m%jXqmeΡ*ѧs RuscZjLM-[L}QTjd>*\ܡ5ĝ z%_?_~+v^; [@mjEg @N6" B֪&g3/?{47?`O*(rb2V sz6v;tuRW*+dt`zߝٻ㭹n%$#s@ a󽻳^(d (~'|fƝ.;uOT?G@:/HRۀx/M-gĪymwعNT#h20a|u  fe52X{?L]\ C{as_AYAhm &_6sw_OOUoܘダh@tvrRl,6 IZQFYl,>[NKb"r8ml(N&7;:ߛ[ ] ro1ƺpCSoݚ<偢)JG;?ީNrc@{G4|r IQhg}"vMw{N 7 @sb9$+ک2u}&te*sCծ$?Uk&*I',oC~m'o6EsiMDiqW_Qr:%RO#fQ`x " f>twQ ;C?`%j>JV*AOT_>{cj\;˱Q\ܭu `X,?~ 1:  $ISRM؋>n8 "Lx*W[״{-KQh~4\}@חT#M༞ hum&4;w9r7Ϸٗ]RAGo,.N 4U4^O`!Y_Kqp7xO5ENVV루2͍'aI|DB nX m[˳1DW .Ah=Tp/>KUAO+C>L=!neɑEQtR3J.q1ۭtp:-TaW/xQT4aMe)?t\Vb Ȕdc r9,D؍v>0 K\ xl@ J#W&d>-=u12Je&;H/E8acj)aL v{h# @zd rϗB!SdrF.VRz$앲EdVAs|N >2CKPuX#_$^11Œ" 0@^4^e:zCˀ$Q5ZW?j >4fEI+Vg | I$1͍ s,L:Ml JX7'eZ Z:m}_V3Bb15%Tn6FߥE¿MJ%9)s?~TGyA\0P]WMC;v0DFʖ X;KhҿVJպfmO?97.RPqY[3Q/F@4pd%WJlkǰSDAC~LJ/,N/Ph@ŪA.&u7bADC/jc䗁aX48(7 JƏ#D'߻uZQ<(OYׅ8IGF"K6sha%߷9|b 6 8h hL{gc( B@40hMU 8o,^Ah@ zXmÛS'0# @K@ohaa_}| GX҄p1߰4ZyDalw}eGo-34 f\[wR$@\p쀟~p8`0\^EKwQ@(U40vqJ:٘[ӈ ujрp x:oح 5F@@4``#> 01\Zcs~L$Hy Rc i#bœ a,C!@@@4`&`fh $66sIQƪu^A h\`.E*Ckhd`>N<+Ɉ \6<ĩ\Rk%E@@4`&s6 VWN3\m'hI V [ }@dB^6EQv٣t-)3 \Wj|!!#ysevZUDaN9?8r ! @0WR<̗jha p,wxsy5Iw?Kʨ  aa\"TeO6 ^B#AvrϷT>GJ:^A0`O?} z #wىgv7-,2-N`xG;i"0JB>_~p+pr?rk?}SmCM,C٭!cr=_`H3A@40T<[g1lq*]: \VAB~:Z({)X' @@aALHaKwSo 2 qk<*O,$<^Wt $y7p"\ƃVJvk1cS8g}~A|׻a]1 y㇩|X9l,B@40\m$̈́Vw@xjhvDc@CQdߡ?-B° _6̖*5^S j^d:>H|'Y\OR ^ &c,MDS;D'ũ^$R[ a GrZ `o~clva,vCRTi! @@A06Ʊ;gb"]zð UBP˧Ozq,,NV} Վ#ǷUZzDFGx_V4aS3Qk8ޞ bY$ZeݝJ~8x]?X8XxtC8` 5/K7FlZͅ~xȍ?kI!a("ܨ7uvw/+ Dq񌍌.TD5]HI[ѦEBuƙ8B i@~3grz\asSn0W~PSOGH ntzh5B܉!KA fV^tWy{|iN ZQD:q'\B p(i%qherY+BI# &@JY wfg0wy+\z}ߗjE)V̄4d[݁2>ǣ95"v?{@p2XcZӜR:ޅ"!|~ETH ?0@, 2 2 2 2J_ $tIENDB`hy-1.0.0/docs/_static/custom.css000066400000000000000000000011741467401276000165440ustar00rootroot00000000000000div.body, div.related, div.footer, code, div.code-block-caption, div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6, a.toc-backref {color: black} div.body p, div.body dd, div.body li, div.body blockquote {hyphens: manual} div.related {background: none; text-shadow: none; line-height: normal; padding-bottom: .25em; border-bottom: thin solid black;} a, div.related a {color: navy} a:visited, div.related a:visited {color: purple} code {background-color: #dfd} .highlight .s, .highlight .s2 {color: #060} .highlight .ss {color: #040} .highlight .c1 {color: #630} hy-1.0.0/docs/_static/hy-logo-full.png000066400000000000000000001323621467401276000175500ustar00rootroot00000000000000PNG  IHDR  phsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxwBR""F,ML,ј21)M5=11}o4[b[b Ҥw-3wny?`ܙ3{DSS""""""Pw"""""R9THQ""""""DDDDDD F ) """""R0*@DDDDD`THĝH,۩<7kJ`]O^DD%AD$2c0E_ g|L12x{<㊈- "RV,LG`F2 i>0 |DDJe;݁pjcM*՛.;8dObN'Ss{}s>"""DDJe;]s16L!roƝHgXr"r3ܝq'#""% "R,8>l f {wn;(bN,S`PTI$`@݀45mj3 ; 76 Gs vWP""Eòy}G0q C={AMUvq6l3M*xa>E[,nn,)*@D$vqj༉fyuzaHO.=wV$EDDBDD"eYiB3?\}YQ?jD`'k໾Fs<HX{^myoomv Xpizt*apMJz3Rwɀ ɛe;]ىWo}wj%pÑmӛ|G!""' "3vt#F]jÈV:/_<<3rIDDrbَh>.;.B{; r]|8 wiDDbρŒ{ 3bi kݻѐW vGx*@D$#~vO gvµܺUDD* ie;݀o_:Ipfn&S9 ૾YлHQ""i5w1 0< G<46E/-,=YDD* Ia(QO$LgD|l>3;_?=7%""RqTH+ =tL>sVrU>ED,,۩\O;;nWYټ`REe ""eC`N_"rthQޡ| |Vn--7+,DDpt^Gyq{CMUw)oK6u–?\s7ƚ,`$ۈ#_{o 9~xɲ}bBDDJTgEy^]ۧ@Qޥr4xtY0xٲq'"""GHlS Qޣ9.1E9pe;7D5cgwL G? ".bEDLDyI{y-:v0 ϯf"V |& DDJWNТB)Egzw))*@D*ϗ ~!NrgΨ.""%EHYUyژ5Ňǚ"" "z`yA{to`I<ȑݾ{lwD[\v܋'aGl]y, ˭@<V{xnpIЩֶ"" "J`{ApQ/{,m""RTT(sSs ƘG*DDdlsqpa"a+ɐ ʖWr$@Skߣ6E~) *@D*sp _:6DDl E*#G)GOdEDl/8f'T$*YG}]Id)K*@D${HTHT•DŽv'УHYS""Y8j GI?N/Ԑ=P#HS""H$ Q"QdJwC$""Et#GjG 8:P! "rD&'%Gm"p[8وHQ"R, dA0o I\6tSAɕ vb'Jpy\:T "-d0_ԩH1ӥ 7$*@D*e;297.3H0=KgDT "wtR9J.%W/;IJWTT vgrBmu Iq9~HP"R hUT[Y_ve;_ )s*@D*e;=290w IQ8qߜYe;Z"""YQ"RyndrWD>{x֗UwXsPHR"RA,۹4s{u1۴J将&xȲg$""HHl`PGAlgوH9 kh2;ӥ,;܌DDܨ)s[prva9~bp3rDYxuG% >26X2rDLY p|I4ffiX9 n΍EDDʒ 2dN_IlIJˀp谼< PDDP"Rv,9 x8, jIJө9jNHYR"R&,۩ls\h$M{B&""%MHlg< ㇆r+@B*Ʀ !!""%FHi[S-B\mV"""9P"R,ܙ˵%Ԇ "-;BD*FSSDDDJ g7zm!&#%!!""%FH lDr9)X[34TDD)Qn٠DrH5pixH)Q"RD*sT "%ȲaR"ڔ,yd"""DHiF  6""RJT ؔsC9`+7APB}+(""R2TdC}>P-92h""RT1a ͤTօPHQS"Rz ;`o&wv:QDD b`GqUHB;-F*@DJKӯm%jτQDD 2(`զ("K9Z-۩ = "eET5 K2ѦDD,*"%XdODD Ҳk[ET)G {FYDD {`UZMZ.XɃ 4a("K9Y 6n,|"HP"Rz*K,⽕7""RTۣ 沨"KfD]DD {[Q~gi2'Μ |V""@Hi- hH ۾F7ED4D2V1S@$k`Wc$EYDD { (bk!/3""RTT:o vT)Es$% "%amvT),@_ \zT)j*@DJW:UrlᗴHQ"R|m@ ?Xg"ILH1HqS"R|}vq5 "-}=pEEDd))̘oh7,i!6&""HH=_Ê,$NM PW;m@-P]7P} {t?Mav+XKaBx qpcc,HS"RF|mlB pfm̭i MNڳug{)п{B~u C&""%ISDʌ ǁo e+gZ75ܸ ΅k0Ӂ$﭂7hi ⅳ})P"HIS"R|m=Q\dS4_?7xm{Vw5Hf-=X)XjF7|J$)i*@Dʘ﹯;rE{dzۺwx\tVϟ~ƭ/ABj@8HQ"R|ϭ=o66o7e}35  GafߜG?}m; "l/h\ߤoPH?XW!L8@H%MӇȂ]/GkPE[y~6+ "%@V6o4g[^!6XwC6oySq`^aoTsiKHey9L460F@2w8 Aǎ)4Wy%$""eIHeoyz, #XVxbfIHdΊH  "~ 7 g)[ËWiqSt}gJu<tTTsWP]K6kBzsZ2elC2sﹷ! "'ymG@HRtSѥ&W+ "\x*ۮ {ޢƫ׶tccYHQ"RyءmF'M$ nJѭSztNZ-?=7}""RTT s/sWlj|psԆRl`Eusteif#""JHpW~- {nCk4:l7CDDʕ  {2i;hcnH ^*P<4 ^_d񡍔ED$c*@D*/hiܺb<&ҌNSƦn{5%CDDrD8 Xy-w:z 'G8obq*V1wO~4Lݪ`;Ut""  "wHۍcvzv+`Npv Ø?ηZ/h7? RN {} .""1 "Ӂ]A](B>eA<M 'eGK>|y^n=1o;=ps5ODS>òˀ6~(|O~&{|t\2,z,ye1)jXhӋ:i;|}$dEDV,۹1۰OYojfW#ٴ6y}䱭[3:8#IXDD* Iaη1SnZSXaRhխ4ƌ|3+%r,$ C3LBiVve.""@l#-s w M앦yNMaŕ^]}p \+OhuhHDD* ɉe;}-e_ED$;{{-\|Y0a@5?zƒTH(|]{/˒f2!R\M9vĐT  "׀`s6rh?ivR_  ^@mI~Ca)|vlG5]Ako@_;"mt~ " v`6_^;_3p JDDʕ ɛe;~cXp΄r}f-|n|s %MXiZǍVQjw=Sg83"""7HN,I?nhy|)M/u)/kbHIDDʈ ɚe;݀ny|A)гsT|HҾSM!vYx6~GS|!<--pHQ'tͲighȋ$rH˲z[?dV_֒ 6o-?lٯ{01~XCYDDʐ i@-mv?1>lUVS[1e; Vcc]kc& ٺOρ9Z֥.qt8b0!]-"RTHKO~1qg*R^}`݇:~.,9m}.NI>Oq:pP>pt31$]vK|*("4[5_7.t"ATHKw4:*O2 ߣU2$s.+UpI[p˩<YDNS<8 MyWE.vѺ(IX{زiC O>߫o;'KE)P>+$$\y 6LὕT4o.߇WD(uuVk h~=wcR""Iݓ1)jR >y{5|DJEde;ozLIS""I0IMMkioB(@>v-4>Nw[JUe;0Ts5PBF"@,ͮKG} @^>jǙ-;r Xf{pdpkU|HE\<l6v>iNX;IR""--K~TK%$sqeȢ7S`3$K a|2)y} {5 nSVd7 "/^ ubքLw$n5V0; x;7筁kSMMЬLv<eq'#C8P%) wPb Q {t[3#&&RW?Ǟ!hV"p9nmuVlK` oq'#S""x"|K 4Z/7>>\< ~ pgw$`e;r4I{Ð,6}}I1kxt2y\ǀw,۹β֎I R""AJ~f*S0k'1$*\q74Epn}ϝygyỤ#/D\7'Lv9  r0`N7n}3/Ja6[~xe1\-Ԭj`^l͚'R4wWs "ܝ{'b1ydjM5[է+3xn5F8:Qwi7Q?4`A#㇘m-`$MDBw!e;WZBhI6`>@NA{ebޚkE8|}3S䡷c*=z3o.O!#y+`dAjfX~RTH (Egv=3`ˎ5Upǟ;xo_fDf["݃;D> * <ȲfJ][cL *VD`^l焸hXf$A8txmmk ~pHg嗭>];͚k% GXv )+{v Xl<֎Ί,2(gUq'"Q""iyow fGo{d3]pK#Qj{S=A'ecޚc/{`)5o-۹ղ;x̎Xs6̠A݆m `j3Fi}nzƼaK)Xl'xL:hHiu?O7J[/Kg-b⥔ ".s˓X SƘPΙw|oBSS>Q;jAUS]4StEmvz~ >6_ ?hV(";xͲIq'"Q""=tX0;4}氎[^vp#uk0&;| ~$-amvo=H-hu56Yk|D$cC,۹$D$*@D$S_V /ŜM >H)iFA{^H$i<ԡF">nNcPkʵ90IAӯ=H jONDDD2{zgއ7D.`?kL/xA ]mW;pV9]F IC?DJu\w "1sI>).bۭG.p7m K-LLӀobXt54/$=-gIDrCviRTH6fĜM>9sf, ~ pZiX+r)WGol\hL:ٲ4 lg)&X|B@7M7l缸ܨ!Yfav:l8yFK=G?Æ_\X|ƴ_Tr۱TW]6nk}lc[~%R,95D${*@D$yog*8atM_ ݔRakm"0^U|:(@ iX |~  fLDJR-pe;GƝdGdF HFQiiEL֤$py =p -I3]vo'K{F2"R4[A Z)*@D$k| YcM_b4OFBݭzuI},Ç$?:<-Qv.H>?<ն@\HysY᧮Hk^;&%@d̲v\G4OoO^dW}Z?c @Nߐ/X 6=Ӗd|-Upj;k35 lqY) *@D$#> S69 Z7m>EOZ)u/vx ]j'> a_$v@[ն@Lv^"QC P@RqʤӰԚI;amG&흺&%)O?1ʳ'e;WW%O=Sz,PE$gZF#T iW¾ht{Vԙy͞=G{m rLL9}A5s)4xd <͜εl'퟾e;6f0qpn&Xբ30xW"ϖdІVDDҲld`0,}l&|d2 cgid.bZns /~)L_>e;,L>;ifY>O?Dq'!@\ <;/R25-4o: c\Ų3,lv|໘8xiG3[v+򻏈,ɩDOhTxc[CK) 3 AZH{ [Hpձ0!A`́-O8b\vDy܂wzqY#"evr[DDvlֲ`>ۿ^ #Jx2-@v6n0 i#}wŷ =%eHm5|0h%M$<77H-$$  < ͲK,Eaox#gvJ 18 LZ HO49p0 V5b՛"Ȝbq'!t|:vZnMx)pe;k1=n2oDEuUfSoXjֲt)y¾o6nF _{qaiQfZʹW?5f ˡa҈̋L3 ן1pxҲܥq'" 2g/ lIzަ/08EeZi=|􀃇W{g&ƶe|# ]|TWͣPVlǤkH\G^C/M)c NE^KcxoG={Ly%:`>~g4uYs^& ¢ID皛JP"R, 'O΀kO*\.OΆmiZu}^[ MiL;[V~_zNqvҊMf+߾icL).7NB  "eȲN}A'? ;4c\lmg/ړm^Eզ3z mB5"jv=ȲfT[38$DH:|rPڢGQ$KiiX'!t7 G8` r 3%PD*Vڸ "/vw|j 악_:5! @[5#T늧+t0U"7 )1R"Rf,ۙLN>x5BO.zTeSoF3u'ǻkf-wfy{臈NH=ѹ* xW-۳Q#aS}+e6&zWDZe;r)#$Ϗosx#wcy+ҿ֫y%/@}DVHM۩VĝD%S"R^F=O>26vn{T홽2¥u pkSlzxHQq, ,D4 Wr6.Ks,@LGb(#TT""zgƝDR"R^I~1o(T:.P|#jl},( MMॅS6m_?T*@DdOMYﱭ/L-)@YkUtv&i$|"~H8ŲvڹJTTSFKR z: Vӊt$'"ؕ,lMMe""jOŝD%R"R&, n^B"7>X l})(iX]2X֎;gO.""0* '{LwbNآܲv:t8yVo]ş8)slk?Z ۏnΘ4*@Dʀe;<#i`;IzEa})3q|@Yù"i̱!oYq,am[Ӱvg[sTSYay]وH t T "%β/<֥l[h &}8(h0si8Z^slDsUn}ɼ`]81d /7=$rPeQwDH lg<Ƕۛ~tԨهt|Nwz@8dh9-}DbvsW*I7H,I z=dho[^; Ӵ?>:.0#WI뷚#F7Wik_7dp(9W]Y[<~_;F"_dvL i4=wMs,-gىĦgG/n^VlN)MHb&W[3y[{tfV.sf$"0EYQl4Q }nQR*@DJe;Á;?u[{GWL/0$cGÅp)9N0op۪7# Q\{Rt3ʔC]] .Nͣ>]͏F8V'R|] vLْw"TL+)E$fwtnU|՛L?| L}YgOy-?;txph)?bo֝O=Qײ*`4~ 2.`9X ,^=΋vlQ7{n}gJ^4"R:n&Ԙ+M1=;;+̴f;1.WwR/\|, 7zBiϬen=;y KGg`#X,=wW!rTܼ9m1T:cl9D5++@d² 0뷿66LQ/ s,9x y{).sjtz|ϝ|Hw"Eb?ॸ(g%Rd^=Vn'gY~$,mS {={@fdNرɯ7oE`:XLQv}<":nΗ1$}rʥC}JCED*e;S{{}I3 "EƲ/['p7E9S:Ȓ pYuro<{n?-۹ LtqFmEf}Ki^0s+DDZl.ܸ({NI3M)"~E; iٻ§S7kGy?uQ\c 929Ȫx7ϰLeGm2~a`p W31MC+րDL# "EIJ'yS;>u;fmŞ=ړ`T÷62&We;0HNM?.{!~VcҔ. cFD$ɲƝG̶5:*@De;B}.X,2o0=Cx_)|w'c}6m͏@mֿ{NB[s׃0N2ytܖ`#cf{qms{ =wqI+ "E0ݵ{Åqӯhkxi*gS]~/}h^fd?ث {kS)l+ͯVaF>6aaѲH>zD熳ɲHv^;L12y[vc }"sFGHl>G76~X[c;?|4J_w=cTGJN>2pf՝]18r1w"%G j>\˷lI?Q,k7w>o8}6@ r>K77:ޛbYս2_j@D2q>L=^KSmf;nfdr֏[YBR.&9dt2s8YգȠ-w9a՝7O[G@ HԀd,9=7fMqKwR_~xڽ^~8)ꎹd[·}f3DR7E왒k^;<ᾎ6KnZ04j@D{`-n(K?X^U}V̷ܴnN ~~x}z֝w_Qo48x~Kk8c6ԀÁs8~f}j2k,f@:DdʜGai6򵼑|>~3;Ik|;2C;X$w3s0%)V׾ 5 5 "S| `=(prէrp>n9G}JWe$ad{5z}%Xҗt c=t/p`p*~OAϜiXsú緝֧yd\O4!̀Lpd+|X8zJr>bA׆.>$Úꛍs2LaSӲn 0] PQ<9vɝ8,k!5 "~`!z)V?u>'u9؜|6;Blg@3%+Z%137GXM˯1Sq/"4ӱq tbݿHi+!zӜp>FJG %R:|x9OzꞩX 5 "r>8+w8,Ga·:.ΡߗI"jHZN}鐖`toP>]tPY?O: s~h "UxpQG,H#;GO< bA·=O{| Gց rW>86*wx8-.Hs^ϒ:Q4j@O3 R"ҍw/'ޝbuq?=iI lϒېfzr>l%{a-5 bN 1ÖH.W9XFe~bJ,Z7'O0dj@Dg`!zS|8ص˟#|xqR15 /¨N,w!S"by z_=p6_|xpu?G8#w)Ws葹sL,X`ԀKz7Pl}Ϩ] )ΘFeݝVEcm@4!5 "FGaQ`QS]w>l|X Qh|0|Mi g@ ,̀tH ;@vm]nX|q\2&+7ƗLYîsd`1AR!5 "StQ;p=.Ko#2XY+N'F{?d~·Ql.K<8,w)Xg,f@܀hCj@D/`Ko:غa+ l/ݒ~˦a_`92 T"P"2 )wQ׬6#/^f][z8fCH1ܐZ@y`uH Yw.> j8_!5ΑO hD:Dd/~#w}·?β+1Qo8>wL6zNf-J׺tϬʠ|!wn̍]Fuڀ<žEP"2·CQMºֵePgKop>l;GF?4Q7Xu>%e@:z?XỲ-u~Vݵ()ˢ-W0Ydݼg͘_ a'{ȊYw;@0=.K(9vD'ỲYs(M?Xu>i6"t ibȘ̀tl"J- :vڲ.srn:j@3j@D֒uas/ZlF? lnYWFg/|x9Oco `uL =q|i㡆n03yY 5 "kjtǪiO, :eM(4QϷy6YN ]uآ2Nd^;D݀0ُC 5p>qߨ:S/ra!sZ~UA17 wX=;ЩY?tlX]Aݿ6젮׆)CHw'Q;R~nPg -p>}̣.|p""ht|<㽵{&W0{detT[tT[dUs><'w75D3 SD|Ư_XҺaw늬!s|X;GA݀86|Jܓr 5 "Kb";-&=c&2OZ;X[ɝ@=q늬77{@NntB6Ԩy1jQm>as9 6}Ԁ5{~;w':o]Wd4O'QD3 25j@Dꋬ0Qc:-.^|xY8Yl@A> PHz訮\eG;Q[S~bPgs> ܚ;Ęl;HN·WQwQ뢶ԃOrX7N=/ә.3sQWdlbTG?o=<"iƪp>l4(F a2. ;^F٬= \ c3ga/`9 rk7 >wQ"c8e⿦XuLG{?3s(̼g? ԩQr>l zh{'vQ{d>#piӹÈLKs|sFuހܟbu{cDmֹC;|=qt>r@C)Vͷa;yL@꽹6]v>섖NՓKޠCFN۬f? ,0Wf@dt/ϝ0bLGذCv"ac9 8רؗ_f@ mѴ6=`K?^Cl;DAb#ZckCf@dT/Ν0ýE;g=, -l30r> lQǾbXcD=hoGuQf]|8%ADJ|82w,.0WZ~^d4wQIzضC8Q͇, s(ȧS~nTK 6gDL`! . ;AeWCd6~fraC꣍N3 h }˗DkH!DJ|?wG}V!ZgPOn>N}5w1 mF՗:}[. J/e2hѻ&wty:;_H)VQыZ· Es\K ہ-s(H] )VO0p`! c^dQ#35 2XF;ŃrǦXݟ;Z ! rAIO%)VˌjiVN ٛQ./|pl{n9sYҼ%sd=!9z_1vj@dtn[>})V>;@a4?]'f-{/sNLofj@dGQ.|!0le>Y*nxa!FD _쇖_4Q5 2T:zˋߏ^CX-d4 Ҧ)p>X_ٻ*w ]f?E1s(}_v4^27Sn(\ȻE{j0NցeN ]<ZSb>R˝ ;1ij쇽/X=;a҈|[':6n@doaDw;9B#3-D:iU!ԀȠ8^!w.NQw)V?y@'N ·Mr05xm_Nۢa'u@ DF{?ږtxn_}2j0)V?̝ [1DͥoZǣcҡdw2 s((|8p]: M{q ! 4pa=-}'#s(7;ُ?M7wD#vC R~fQ9@˯ DD˯ںx!:Qeo]hN{Ĉac9 cyձL HAԀ 4;Āk| 8Ԣh0j@d(uTеfӦnFoKöQsrڣg¨s> VR"}vُ_6v\buk]2w|"wRv{W iՄ奎bH a=DjXu>urJz4wl^։atpȊXj>chzԀH_ wbdu^A>|r(ai4l;Da,/<ư^}&J3R"}nޝ WQ;#jھmwZ}WS"|86w|.6·}gs(A.sٽ"ُ2 !D `! ُm;DA Mk`! fV+L _MG·Y9d60ϝwJ IDAT {;^;Do iXpj@Wɝ NZAwY|xNY;@ar·Cr(䣬 N+~L,>d]!oss(a.+wzil;DA0pa=m>x|vԀH 8wt1)Mcjz|'Bb)1C#{Z[G9u^ˡa9 s~3ZSB֞)ݨWpauul 5whV؟]R &D`z}$wY7j@XI2GQS,|8ҲȌ%WRnn̝ ;^;DFh3zz|'/v)P"%[|^@9[[Q͂%+c'!vŎ2j@d^{Yt>lWs(9! 9*ultl_f/;0 +ZbebN`3c#W)V?ƸQ.#QM)V1wa!dݩRK2Q=˜{QW{{[PgԀHFHeAÁYhLQc#SB,_mleԀHqkُwwPsRh4w2f˩s+)V?(|Xz~Ŏ2Ej@DZS?͛GB4E/s>:whVXÁQ ;.vS"Eq>,Nɝ WX̸;mo,/kCԡ@3yiUA}rL_9i·s(Asd=!| 86w|.>B·,[Kw;ʔhg5^b\Sj 0Cl;Da,Gt,IZ;̝)a 9 $)|-_jX} -w!:'O2v]S"%98wX/zpeMxsr(6 %#L\bE![,j w/v ԀHI46qk3lBʹ8wœ:4Y.Zl`X|"8v̝ ,S5QHmi9η=DzkS2j@g-~a5ekIM˰| 'w\bE!k-j WSBO/|RA})5)VwYzɄΥPSj'9M,7HmP"9|:̺^~:]lHzT8:w 3r(O Y2a}dDJc5 8mq>4wN?xA9sޜ/5פXݛ;P"Y5'œ;GA.JzԪXjyj'. G8€~g\"4FՊ6``! b]k9Y2z)VOQCG9 sgu'[%35 F&>g\7{p@iXm}y`! c9Ba2!Ď Rx2b·ZՓ9K/P7R{a!A۳iUwj`ԀHNQWN}$sJbZGh)6#2, 9BG3=JLD X}Ϫa}DͤeXm}}JE&,S %V;J9ԀH·#r(Q5eni.;s%·}sN-ۦWDrYxRYR!5͂jaW`9 s}՝/`Qk ~\;S"hgh·GZՓy9'TCczoc˯t|V;JYԀ9v^;GA|nX&wlO}_O_wi;GA6NbuSs(%Y~5`j@d46qK׬9^ nUO|xU2ÁQ˚fj -)VBD%Y~7]:5>bSj8J^>Yz>6~ ^Vd0nwZՓ,jZ^sm}pz+| 86w<\aQ|5 j@N LFx&I۰ޱ I7v ZV3`! s"|۾bu[-5 2·-cr(6W/SbU@/t>hsbq)eٗrH1yB3h(ią)V {&޻M˰Nw>|zWXѹ)Vˌjm*!{9v̝ f˯}T(wN)V_ɝ /~Wg|,w5 2 ߵg\cXx`{z2;t„^:ڊx)u>;GanKEf>߉1QʧB"L 1é{_7%ӳ> , '9J3gl;X=;Laosr >G#gJmK}(gP(W·"̈́^&bua>{r(6~vHE&nHըցF. CE:ӌyo&1z2}_2KI5R;mƟٴ;OZ;LNC)`U1ݭI6gvj6)VO`x?,N''[3\JiȨ.ig eXKy12\POE&b#Z[.mP"p>lB}^[J'ԨO|j|5l˯t1o,j@+Q 띀>!9Pr#-_`'%9쁧 , 5zbQk bߙ'KOs·#r(U[ՓbaoX}v~gmZ~5Rj@ r(}Նt0yVCD mSi &:Q#3&Z !yEFR1?i#t,LP{*E·i f.MQEݞ87!$5 b;9 bJw Fss(S9M˯W#D l)V;˰MiV[Vk==3*B·_^mQk QzF XS2ayCB! ranOzڨtȩ3·r(Խ6Rh@lJ0/9=WXS"4 eXKʵUCD˰ںz<}CZB·/5צX wK p>,DKfzR<-c4:||رmIrZZ~զDl;D!c؀P4C j)pnY<;c-k+zhyLO!+A>Nzc\v@3^3iV%2qsՍFRBS"|8&w/5;GAq>eXOkYn>6-@ 88wBn ͂ͫyhce(̖6io)V)w)^e46ۛMw(w , '96_>buQcb3siQ"s|xEbM/Q_L(-_lag @ ̇^&.7?·=r(^b >zL<\dQ-pE&!,j@dN]uE)VOZ5L9đ·ml[-N ,j@dv ˯2%FsdCnI5-feԀ\h>stċQS %·o5X=;G 3&vיKzƨQ?yGCîwi6%' J8$d9ua-SQe Zh9,}dV˯&G'*RLnhGQ39zEb,j Ĺ)VO!eR"pD2-j)V;GAp>v-3;O?Khu2d dԀȺZD}CԴJh6Om jOd)e~ՍR;K +N|;?v507P3C}ò9V78g%M R"k;9 bJl;Daܬ9 \z̦Y;M }M,lǁR߳"75 HMm+mP=L_9֣^,SnBʦDօR2`[ZCXlh)LOQcglJQj dԀZq>(dRSf6֗Y^&)V̝ R_f+;ϦWX ;O -=x8Ϣ9;JۯDDj{9 jPVlP"Kh7N5WX=;O QG9 j<ǨP<O/m! rar j lmTkDvCD˯uy/V-iKZь@O$rl8Ex,w}Ro@Y#h~/O?Jv 0>O"K- 5 N\bC!dr>l3]bQFAUgoӾFW9*Þhhe2ܣpQ!3Z֚Ys(Խ_v~ՓϨHF'{CH˯q!?ԀȚhu! Bero50|ljvdM7/}ǯanG}˜X-BC'Y%sՌίEԗ]hMKl)V_͝CvnFN605z6:Q"s:I˯8jM']]NODj;tÄ^du6W7Xݔ;^.eu4:q/pE!]$7_(lb! r.E"+-:B7X5ԀJ9^;GAKzƨFf!{k6 4 B?;lZ%Z 5 *zinڗHm_CD hD&ǹCHYqeC09#h.Pc~|.sj@M˯L_9PyI[#h+9=9pl(pguGCH D5 |x-9 r{׌jbTk(4)2Fm߹lB·=c=]̩POXM넪˩H-·s(ݹ1!dxԀa!Z""WP(mSAk?BLP2q:.w|+{FCn0|ז4۴ bu?9Č·YeM/+j@I˯,_VqqHm_Kr(᰾CkR~; q>l 9 ·M,j 3Y+~ܵidR!VZ~5F]:d|N6 W lfTk(N7֗g99dެ8Ё ǁ sR2>Z~զWvP7s|x}Qwq+JgԀa7`9 KFAmGXǰ ڦeXζ*<Ï7jХSj@E7]bQ j %)VeΰzNmֹ^s 3Y:)N:d\ԀiUX:wg IDATlK]jGe>l\O˯&M]9)5 #|85w\eQ#pEL diV#F@~W&[ln\;GA6N su 9YԐTr(-)V߰(L;ZrKUbRm W#n sZ ưށ.nq2j@Ai[%_X.;Qge>be9cWקXݞ;s>lNi9#<Dsd=8`Zr}XFCes]b3z볥Cx>riU}oܚ;GA^|xe)wW~lyC˯&Lz w5 |x>ppy8Ϣa=we)>%M'V[R hozUгQJ Ȱ<)V:ѨP\b!`1:ruӚfzRR~=f26D2 YMvGR~\;GAvB3;썦ga3hZr!֑^2۴ b}ksȯX~ lo\,,Ԁ Fw,ZҶ8Ūo'KE}+R{A/ez)+^_tcMC8t?Ŋt`zW wl;DAO!kxt:·oUozP2<^;DA,f0ZrCr#}iV#~9|թF5jpN2^j@G˯,O)>ay%CϿCkR~;q>O}Ԟ^JaE˯ڞ>;\5V4vFx$w3h,klJ Ȱ <7w\,7.{s' }eX_1Ra'.BM Ȱh4`a!Z7s(n·QԒ5iUo.Mdd QGPXޚ^24 29@v1gzIvj@(`! ra{{[KR'H!J}N=uC7kjsQ2Z~զ?5PtKm;! 2|)pe͞;7JwHvj@9 #zZ~v!%M˰)V;H;DA_ُ;:WקXݞ;ȳԀM,v>۶ x9fIY7C 9^ eY@KQԀa`9 rMՏ, 96N5 צXݙ;D%ݦӰ)V1wx iR!DfRoC(`KzC0ϝ p>Ή db|4{7ӕ)V!2~vqBzl۞bJ٦Y+10_:8d#kiR5 =|ĥ)VXr>lxZry/rsӝRԈOKϵja!DV/6]8hFR͝ ;QlA&8sX)R2q?izl.bFp% H#wh5O62gzIԀa_`9 9ButJbdSv|$wh-Z~5q@S ArI`t#h)V?p,-cr(&:[- :^޲f-NZ;ʨf3)sѩVܵ|)wL٦Ӱ)V,;cwGaR,5 s0C1{:^.v\9)Vs2! vCD/ws=*˂·hL7Xݔ;Ȫmn| 8 ]츢ў{ w,+%޳6b\ KԀa}9 rm 멹k!{Cd/6-jXL= }G5jpN"_bltVB/ 2wQQ?XҲaS$˚=wMՏsY5 r\y 8߰f?ڞ>;Dn2ڦYKGsgq+ IԀ_M\jE H)VQ};@)r葋S~A]~5(s"P·g\~gKw#r(·r(V֞ѻ·3bX"k?jArzh{ H/mZ5q5!z/wP `ՇsYj@C˯&oHbG-vFf2R;0w47M~Zێjĭ)V!6Ԁag5s䙃Ŏ+h e>;GAޒ;DA7zZu>xu~ĝ4醵>t6-jX}n)VOuP쫧~N5 nu>lhQk@X=;D.L/c[Q͂ܓ6wi]ǮɅ'j@ |ؒz,27zZG}&dpd`XAӀ-;WZ~%|oٳRn1ӯnIFS֦eX~/Q wnC 5 ˻?67z^s(A·r(ھ1l>uXӹC 5 k(;G!;԰^-QKC9tuuovTJzG H6W#KS۵)Vwn2ђgE{3}%!D֕ QgöaVB/k)ks(·WQW6`f?,apLX \fXd4J9SyC|x>YQmm>%\(2 j@ʵ/YפX°ۮ0|@eNs>wr(GR.|xu !Db_% 9vhUo t:J.ẅ+j.d9 jky_Io)߭-.1X`X. m̛ЭOXj]5pu;#=@·]Q/X԰_bcCl;`! юjkMϥԀQWxU,~úni 8;G!̖_ov2wb(}4Qς`! w}30fvo!bA HYDSWݹ:!F3Jmm@o͝ 7Xuun>o hU)V7Yr>,N5 zY6ޘ;GAvw>.wk· ɝ0~8v`ʬhUD HYtar[Щb3=\;@k,Q%~AGs·=s(N%)V1PPP,E͌ 8^9 898$X9-AQR7PRaQXSmHՏQ#r4RA{fz `G^Q>=[d`ԀC˯j1wb e^;DaXQn_ݜ'2j@ МuNΒgrs(1·s>QEQþ]eٹCXSRWYr>l@|R.͝ '1W)zg 3G)ͧSQm~]%")߭]beD0-V{kdyQIa 5 ePRxz> ܟ;DAnN?ȝ@קX}EGtsgs̜o̝?>oQ#R[,b,R͝ ѳîexe ;HWԀF`! pifT=ӵ)Vw120gMDM@-;G ^;4wBhՔX} -w\W^/8g]9!D$PG05)!FJ_[6ptXQ];Gˀ;HԀdܮe)VOZ)"Cݴތtp:9 WdWw.KA"]6:;ZCI·vΝ /I/?Q.\)+$/-oҢUK-j ăCޝGUFႌE"EQ".[QuEʵ ( b!8&0_0LCq~>3|^k=+)],W1/[gW6x7?a L[bWYɭ=u,vZX؄[D:No4سOs ŃKwT@li~b+Q)fu^Y Wֵp4[.l:-=bs\hnkC4+}Xgir ŃKtj ~u:+",q9i;WP|w>^1G3Ń ޕQ28:D"pEy myus}!DovG hV:HS\Sɖ/2EwWT@  h ufiCd"#2ѻ :HT@ll^~:?{!(Wz&9!D|*ny]cc]<0~H蛲 nվ-hR " lo8gSX"uS?mZ_Io | X8N[]O=v!Տ}rn1:HT@l<c!(A-h0:H\Q | 88N\8&6K[ENf(_ laXՏ :H]\Qt i n_c+]rn b[RQ!* |pa~%r{>jkr|Տs-yqE1tƣ!DrpEfR lu/s vE.`[oCXQ_19X)kB+}і%CU.666v^~!ZX܏XR+ʕwX0jt_ˁ'C < <`WE=ޕz X: +5d;b7X(MrR ~VZXϴ!bI^}?rsvڪ!0>:X,`W;ѻ"mknN0G󍠥?:o12&!bM^}_h |cӬC ʕXK(ޙcvYiz ZK+ClKY^;5DZX1[C4 H3ޅNo{l\2H2a(Fw^-ɱDB&(7ֵaDOڴjcӭCPRm۸fkcqa)"Ii̧-7;# 9,snXi!\x{,]4CH*RcgՏ]u&Q ލ̾lBƋä^r uE?C-w?D-XOw&{]jRuE*s;!DF>}^.E[R75*R32fV?D@(`P?VO %*t:Lr!eꡮ(|-:HcK`uC)NX=.NCw?kdfZY zyls:.1۳IRrz+=/!T* Sc3ѹ]i!r+U!Zy* H* 08:1xgu餛q~xKmwS ?C4 H=2tYnR V$2~l |=ѭ"B$3W-sJ6`L9mN;.puIE8 X3ܶ8:Hө9uC) _ 2@*)_I=tWߖ I0G'`!oL9')gv]/C H~[0tg}ZSވ? \aCZ! s@Vy%ǥ~FT@:/Qa} ]QfʙryN$T@1J9m6>-9r>uibf{" sK!DB$JQ@6K04gb/Cd<@u?GR(wN=#~nBMT@ pސ`Fױ)kpH ٔC]QrfǜcBMT@k1fœFO0d 8:Hۨ-Xw%cCZ1񩇺<(SbC H~}]#ќ>LO!T@eYnꡮ(7=#ښ&BT@2rE=1Ds>{np9&K=hceI=Cw?DV H^}~D9}] kBAET6ࣙ~>5ܮ&DV H^}~\QE]nBa,@" z'e(|-!Q"}W HMh+=bZF;+J~>X:]d8` gTm4CH44ufwhuHdh* yid8}=puEK>|sc+!'!v* y Ջ}ݲE9b?1[sGYk8}|{pui$*u3IDATpWro!* yudf s2.dI.t2Z)>m2!DB$TwqDOeb 9V,<8,r ~uPɫ$п)1۬CHc/W{g!/!%* y$dOOβ w-]/%vLW;cr}FG!5* ye ged.puisH6E g9?Ts:o!5* y"$lYM6)uim,燸xmoY"!epVJY&ӐuIE->;~rCI<@Rnud̈́{$D3!Uu$=?%?#:XD2Pɫ$ Ȕ|]n%i<խգ |)_ T(W.v:1!DJ[R^ N2x_JB|:b:>#@UE$Z@F -pwY (pui%7.?Zz`? dQ|Rt]WVDFSmb(|_'[:i HSlY!1sRhc)[5 8:H%T@r T3 !! ybw>~ Vgw1uH&"* D7^'c^~Ȱzh# 41,\AoX miJ9,:gkBmP(Sf1mX>6~NJ] % "}קuo[3<n}09u鄳/Xo}+ʷP]2&-v| f"}`hOH=׈_I1aGn:YM15|u pu H^ӭʱu`Sa6ed" |:D U ~u~x2T[f[\@6pEi Cc#(?lrv.φ7Q ΍Jǝarcty ?_oB+Wbi$Pme2=T>u-"}L.?+8X#d4d"fePtL?dg\Qڂt K^ʹ"W* y}dWGf(C;r}NBbCH'M~ bF?Rm2!E(pޭ뛟b(bH$u]ks~H .`'W!Pۯ$tWsۘ_4nn?gj9\88~Ok{Y;!syY ]Qnh?SmlS@4vח Ǎ-m_itE p:/uc/XQ###:T(,1->΃7R̬Ų4_ߗuI >;1"'އKsg_,/rnrڿ?kBDT@rE 0<|.zrE?_cxgSDZQP6y6uQڇcDDI\Q Fu$ql%!De6 _gDD \QB8]wʇHhi~wYho ^-WovEy"i|"0RY` ""uo7y 1/8ZN +OX+\QIub*!*97?:,N+ ida|ppgH縢kՎՌ4Dt Vf(G/[gibZIap;,1hDDL([hUBD\QtOTjWA31SCe=&uEaDD^+71pX6,*"ͧReak]Q r4+uGtjѿ"-X5pE!uWbWY~rE -UmJt""N&(oGZɱ95]. an;imը,08+1Tb%:k!.Z+ʏBW77`b]``~]9![\J1:,;x:GG< << <3zx0Hjc ;dpd41z,`X,44>""GF(o!"18?}A(@RHAӓz]m@DZe#fhHʇH{kuiCh%]6(r* x:(7vH 1yD,APQ EW9DUF&mXfӁb3pT@wuiT۰.@'I;snPC!DUvqECb3#Ru٠HwlpYX":'mX6?:bC?JU@&'%?ZT@ mCDZm(v`yDrt ^7&t ."+Z X ^w׈t q<"*Ia%b𳬃H* FbPDdYm%Ui>1x-a*  "j8Dn`SAD$/C1끋sHr԰Cb H {"""<6ޕhaI<??:CX 9DUR K@/gb[41DUtECbO!A"Po""Ri˭sHkha9GcX4VADdy: b@I_bDDl4D : "\m!1 ,1x1f*,}6,LADĖ H'[H :DDI{>&X{* spui\k!1`l<"Kr7! H>MOVD䕬haI*:4 H:BmX1;R8x_ q ",* u'F"jrD " :4 HCgZgFh9KfI?)D ~ui&8:4)CɊ{v""ͦ|n!"nW$mX"voDDOmEDk%DO4KN`DDAbSOPYTa&%p-k  "* -?8:4֎(ߜhaɲ8v󧬃HH g?[J2:0 x8,g ~uiuiT۰S̒ιx Qi#3CHlfhtvdF H Gd p[YzE$l8:4J@@/ >?:t H ^:4ƛ\Qh8`$,iwgQisv,/פ%r9] GvT@: <RDc(S}6xT ~aDT@:"??(q{͚讇l?:t H';w[gSa= \b4T`!1ǬÈH?tP V`G ,"b森($;AT'\]bFDEbӁ{&u10=, pu ~\ ~u Q##zg\Q~լH?b+ |6,18pi`m*I?: pfɊyW!,Y+-/Dt\/b;FCSNKqY"W7#"| )@YZ:#q$T@\ժqDd]WA(N1Kh6p!p:pn+Qer%C݌6?0 WkkJ^0 0qlT@d܉jEC=692bA(>bVO͢ 1ep(H穀 <?A`]D"*n'9^)f-Te b3󈈘P$\Q 6,Ŗ1r4 *W6#"* +_F8wt \QbVˍP] MS4 dT[vlGu;) 4"gXh7M%""* Rv e.y{ \QF+FNz^joMDV  ::rs2*ɆT OJugC$>i6<8ł%?mRDT@1bӀiE{H֣*$/FՓVR=9tysy_9bோ^BD9KDDDDDj--"""""R FDDDDDDj""""""QڨHmT@DDDDD6* """""R?`p&IENDB`hy-1.0.0/docs/_static/hy-logo-small.png000066400000000000000000000454251467401276000177210ustar00rootroot00000000000000PNG  IHDRXsRGB pHYs B(xtIME14 bC IDATxw\e?3[ z %3WagvLb^^ zի*v)݀"V$3zZH${ $ d^ ۦߧ?Jes[t&ܰRs #OT}M1(U..ybsuRYܼ&@^+S"+|A2kF*kd|>ѻRgڌeW LnOs$m>DƷ8}Fc Ǹ]HgsKM4RGpi0'ԾaOfa:qX{C7Ů{$7`33 S\{OK*]hnv /uYo+~F:۹KM: 61KltX[ |fMOijXk$Z|]e⽽݅n%逧엂}-Rx蘉xS 7,1?*Z0@Ărwqq:ۉ=o&@v^ǀ a@[ {:(8a6of$5<208|X)> .؁VbgS&ّ.죂ˀ$U3&K? ;6l|Xim-p~X/$0Љ` uÎ~hyOegj]pdrT$3ʝn}Sėa 80oAƒWG/A R ޞƒLrw3(Ϙߞg@md<OWTxpJxOa0$!P?ӵ,ɨ/o/6,I}8d7_3p`f$$}G ]_-zjTtS s, %vm&@Fu%O_@oOf<}'v[= ݁vy,{"R᏿w0}۔X߸vy}?Ͽ͏RQ?33@o3 Ky*Eii Kx:l T᷽n֚Xv'NjMO:UZ՘6㻕mdr$'CMxA]c[׽P. Lm-Ǖ?dz Q3@8xx@ZL؆վ 7!6:xYE֮\I Qu{Vkm]tZe[D6lSg829I[y'߲П66#hϵ/C<M<6{,ֹp^Оv`|43>ݵ~&8v =k뽯I`%hmȬ3-iK`UԾ_>׼;J(/8G.춖\ʼnO>ԃs"HkwKKnOk~s4x6MIer[<ྦྷ۾>(3A'ݹ@ϛW|8E&,B~)û]L zÐD|19,?{O~Lv2֖@<w ke*ă?TsVfv75;@%N3+XΣܤ޹[||Ɖoc)7 f/<>wwM?Y*~p+?O\/`HL;.\=“Ig7/[rwaH 餏X:bWa&<pĘ6GaɽF,9eaƶ: *_mafoRܒp@* ZkgB#Z t1|J/*?\~79aКObM xUߏ;;|^!}ISB*qd2/%ikfW |[7U9>  7>ou~ïz$:U¾9@[̄wy=/_ȄXaZ=G41y zU% L&>%?ϛ|< CM6,-v՝'D,;ڠx>|\޼EZl?q# NmkO >L> |oY5n5ݵ ȗk23O vY(\;!|j{%!%p-09^3vmΉn|^H80]xm[7݄)v6n2Ot6?( 69=oM󚽷*}z! Z9QRLrOldG r7>u76諟\ljo=_D G̥[ nx0a88xW1׈50|6Uackq8GN[;-dѧNX-HhVJz{/ZqV9AqR^Es{͏uW؜/q:ʙ5n+oq4A3Bj~iq74o?`}YҚpٍ o6a$1'Ց䘳啷CSٓɱ,h/A:y8ՠWW}ϖZds9 7p'{OkҘA8(gݼ"`Qm}Hef>;OcϘL1ƵBd{Yky9sMlfw)옴#RST_ЩdGӒ?"S-ܿ " xG3Ӹ <ʦO.SZGx̒ @B6´ Τi^(AD8mCpԦ45ȶZ{ 856޻Cx|v؍@FB]{ "xb6 vx718zZp[4>sW{;9kp4=ƶQW-;a !Y h7i g|d}oΰzkʏHQ _}5jp=wl>a|ah,c˫<#AQ`-Q_dH:vT3y z3"K+ìAG0\y oNW C@d?|wcpW gp~e ]dhkIcYI I0'XR^'\ O<[ϖH +|oƴqjhsQyf!a}+Aa̐`Ċwjްtƭd|-{K?%`U} ԟu-CPrЦ*uƇ&*3*F_.-۟e6v.N1Kj[g1̶u>~VaFPX% {XZ*<H$6y%aU_z8\mm cbtGzU'9 TnӘVѿMC̤OY"ϛqCx=94}Lmh76lѕ>]2#hK@qOca\}fwr| \. lݖ~Y3/ӧm7Uo m-Dn_T-0t \xdf܇No*Ͱc~m}mu;Y`6}7V2]O|Ωf:q8j]^:{~%`%AaI4{ok;s ,}.ٕ< >Z3\~Y{2p'm\w3v=d 1hMw?)[sC@swn隺d0C6yYuF{?sϋ:`hT*ۉ0/||F\0ւ>tomwiMexz}8<@n:] -T t/"ɽ qi#+t}~d86ӕ Cڱg#0qUkY7'JX8q~{Mw摼j L|p^cJ'fHBf!*;ĩTs mMR;bfnR)ܑ%|-8*s= a*=^N;Hv,/r}Rv p-T7sζ:vLe.I]z #^Ȳ\^S*7Ysq<: @n}aw`Q gg!{ rO8AR؃ Z@!5X[y՟icʵZxd yBK=/m7 l/HgsI:_oo´'3mHS QѠv~֧/,h%dbl_ԣ]O'3'D=q^a5aRL[8N.b1cr}sR}|[Tz7d&?NةE{ʓ![Ծhh2RT|s5*W|)EV%7 3'M,ܽ8Ee2aF(̤2 }LR{PcVΣ~zJnNeDoMct r=vƤT&U$^`GT++G-o!VYF֟~Yit&3h-2k]N` ]('2Kerq4 k,FaHLv$5,ęLjR=>7bUwtG~7;A*yZow I^U}B¹j)$Sa.l/~?(O1m`}I (yy065MaP78=DfXv̮Mg5F@ߗW}XBbVcvTuaG/Is9g@ymg0cdv6!FDy*9 ȉȟ3=]̂Lbjޚ&@A q8qlbГfۖJ/ok F~vi;{56E{#eGhȁ݉QfrcEM<Ǽ1cA k؎-qt]z|eTV+Rj4Vtl=:P.-^VfvQPEsO9&@WE|\uQю|dJOa#~=O>Qw XoԎ׿ o{ASK*=]kkڿs,0/.5e]M668Bq$?~tN`p'.uS,įS1Hf6h:3=#xYIπZ+'@*T0&s(&Hg5TuF56B/#u/Lz2ITy [aݘh2xY/"YO X~ލnXjpFɌ5k %D/O|`v&`՘ܠ-w/z 5so/I垮 ]w˘LHd >eFBIf ;ppPi^;졑u}пmVg#ZޣKϻ_x90Se=#o~ߪHm}GXL0)֘LqqHlbS. w%rv_:c:?H FtKf[0IfY'`o5pHh3<1i;Efg̫ӄHXZZv3y(;}˞oۋ?Aafv ہ!M,dC&TۇI* Ւ#}^rH`w naĦD[^(_YBgHg\ OHZZOJSX mEo3#M3a7..<8?NO)ZH)VG z'B*"x%~t?9AHg;_81!Fumr;Q2IM"WP-N7+n>6# &C:*A8zcf*ީע]~K9@~)idǹ{z^ȑ}{xs^; I tgUwľLV3io2o:Fy*B)nZS8fuf;\ Mz-_5C"n1{s#xtldU׺$={̽bfr,Z\,Al:-}rzA[SMPAMܙ&mn7d3i0u_ AW)iX:x@>55!ͽѰaoF Nܺ&YMLX=d fT+.:s\ .]]Bn-Z2yb6KWlμ;2舴H}JO׆&@ {И`I/I*"$e̞Ϛi13CL5/U~lCS\lVM> 0. >\GȚl,Pt'HeI|b,t9܅[e6$?:-2;!5a>ۀݒέn'F Io4BHT^s eJv_Pd\Z=|%in\)/do2[^Z;D-Τ]Wom4={V&Jf[#=C$#U9q[91˸!ѣzfmJO6MEkZldq TVZa)qY{ޗr*j yHJgr H[L[\^q3kY]|0߅%~ »ln_dyDqzֻ Jd ]਎b-v+m28,(ȵr$&tG~VhoƢ8|Dz'+ŋt舀&4ZN6=I-GנP#,` s2R3.>sa+fV1W/ ial4ƝRa~`1Tg *=eSia4(g)"` x_ 4]0 5pe3L z.pV` "e*#͏{K{4qHu7tG>u~ҐJh8dW|wrVm/VԄG((2畞bH=hD"-3hTJŏਔ+餘#x,+1HjSZ#b ;|TN~~2;f5 L$ǂ|T*6mHh';rs% "u~c 3 Û@7:$rS\g7+Y.\_wO 3[?8Lݒ&_3MSaX}q 0voȖouw 4`0x…7.ᰕ+Ç|aTzws `0F.:Iݮn05>3,k/ءg~e9s~xHj7*ka g ta 9j&~ye::O \2Ǐ_w1i7j;f~nV!6Rèk9( iKʿ }}l P4ғŰڱ g3kWR\><툣;}$q@V/~oQSKgcbj8R肣A:v4PФ0ȃ\.p =yڢO K o5/OqPɝ} +ڰJO=[ړ]{Ar`2 hd䰀 kGIlo6^^@c ]vuHQy)O{CɇRȶDsrwaMLg; ;HXT9kD&V:I3i[ rQ垮4d6S8AX-ӝCI_]iKD'9ό=M[ lZ_]E_nkFؿ_5cQq3*" Cº~s`#-r{i*g.N[}ʰ4i]|qm;5yZչfw,.rI|$|Ez_gRX]sҙN*EL9vƽߗ\?σ mx5!XZ6 .PZC$-kb93OHsyW*lrfO[t^ ;̅*oUI]Z%²k߇|Z\"Fxge9z)"l![diЊT6WՔ%G -4!=]XB7_uIwkhw]X4RِE#7Ϧ>AϾ˥"Lޞe>xh; &vW=viۏEx63[f+]Q1:&1OQ  h1pk rߕu/o-_s~ykwdˌ{mIz̖nq:_ K` }_NI]PR8S\7IYtG~`f,.ѡνLt tl2g_ә/ Ħm婻IS򔻋队07mek|m7^VEȹ{,zUTX:8,8Kj" EKbi,Zf 8Jrւ$((0wTTsDYLVDԱgHMu?XQɗS- ]ޙS9aI}cMZZ6);~7fkY-0b)d5F=?=;81w+ 7^yI 7񭍪6wfW ,ΉR)Tc{0{,DG qvT7rMޜ=3δJfsB X=K`m79#?d]1*Gf[*:_A!b! bh$o>fHkBWbГV(=?$'B13 "f샹~(C5Bï*>1%hW, ~uDoY!~ܻeJhk\ݵ̮ǨZt/oq?C+O~pFa|M"JÌĆCb5u/hz8!0|+?N~dQe:^g"a_7cM\O. q!]1gM¯ŋȼVOk:/]LT6w &v6"պ1Շ5T'=]k\ծvLtх#FDc+_3: %n_?]dM ݞ$jTZWdwC#"ᤴѹU( MD1E001c)z´6>q5_H "(_i@9L1ћ€VHJʢP&ϋ+fYT:zތLf^kU}08ӹ\_!B3iA Q<5+ſ9ӻ T&O"1f$0Gؽ'e$Șb1G[kKn{3ߵ K VpyNIT˯!O] |FEVK/ӂ~B2;QTP2N#0[ -t=nw<6gp]F+ ^E $%( Y@Jery<}y74-'pay8pfqx}Bq=km͚`Yd #$4 η"5K$_m4 {ӣiZF${20D ^ʒ8=Sk:4Vj0X-ǷAYAn#zx^)2qPl4z7]# ltGx/4)*uW!/=rH["ii() \6(/궼ЛCvÆM!w'ŽºRTs}RhfHTb0 +RT60WژDn^^kDck2!v6_9M/FR"n1b`)Ʃ2?Z=lfY@tva\'YEϯ>tb|Lha}rwae*JMǟ Ms\Ss[H#R`8eXxRzh$=S^\!CS0֒ ^hXvB {(-[x¡ɎQ[# 6d^ X[ #⃄w'Ě]}`YG 7FG#ɑ=l~lN7i #?\rw\`Rhw- irݲ%? p]o&ְA Z!=9{8c])Rjyt6?9;>H>-LnÁm0$0Kv!9$;Ivi[I%F a({ ֗k$nY? ƨ|wEɸq%\c:cYCdRDwl\*<iXo8ѤȎt [X 9Jx-I &}`N6ϲifv9 ZS Q/s< ".Q &9~N&7tl؃9l7¹/UpjnB'F+R)RvG:]Ug2qaryowaYw39ҧ[ cDs_#߼Yfj]~y,w c"R0Sf+~dg44'BIXgc#7I|lҎn M]-_wת7taHd!Mȿl6Yո1?Rگo pnAԼ7 \*@_|io3|(lx]P)KR9*IvtOh^ ̂(&}&@6SD A߅" 9UnjMO45clŁW^|ɸIfV-Wzr*=E>s'>cpDǕ:`$^{"fw& E xִ3L1lJd6.ɾǝ=3 IDAT'~\vki-垮\X%+3PH$peNnz GF qqZw??0)\PԸ\*;ʓCoRXowp>vk5 IgroNsj4ZK'L%18J6A ݑJ%9'=3?K WGDfªT6Oa&K 'ƒ锎߁ɨwځI2垀4 ưdJwWP۞kax׽`8U57TJGѹ?Riaf{xwcN}" &6z]xцh wvN}^!c 4]&epoy2Hngw0Rʕc ;]Xaz>vRNgvvAG?0e؆< ['J-rO;Þos>%i~<;7,Vۉ5g}AoWorÕȷ1 CLF,6&'gWCyV-#lyo+mWHy]9qD)t.D2 ?7;ُ7]9;bWA`[HuU8cMP7MBE랁@29 ޤ)è5rCp6 tqs挸B9^9|hm`Vyd#,@AnizcY_"s&FBvAgLI[0a|뢡!֭{O#JF!`(ѮԒ;-M0zlA'w"h!\>"0(bZR>Eu. @B '؅T}W!}ڍ؜Gj#(P\ $} &HǠT1q~G %q:FNc"WZL!#ʐl{"i)=9?,z>AG \k4 c0=])_m.Z"]n/+ Js/| Uu0Xj J<@SAL5Ͻ|jƿ+Q)wO1.Z`dRwk]Zb~IENDB`hy-1.0.0/docs/_static/hy-logo.svg000066400000000000000000000330551467401276000166220ustar00rootroot00000000000000 image/svg+xml hy-1.0.0/docs/_static/hy_logo-about.txt000066400000000000000000000015141467401276000200270ustar00rootroot00000000000000Hy Logo is TeXGyreBonum font with the two parentheses, and two copies of the lambda character all flipped around and rotated! Aside from the font design (which was not done by any Hy authors!) the Hy logo is authored by Christopher Allan Webber in 2013 and released into the public domain under CC0... see CC0_1.0.txt! To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this logo to the public domain worldwide. This software is distributed without any warranty. You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . 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 hy-1.0.0/docs/_static/hy_logo-paths.svg000066400000000000000000000142401467401276000200140ustar00rootroot00000000000000 image/svg+xml hy-1.0.0/docs/_static/hy_logo-smaller.png000066400000000000000000000074211467401276000203240ustar00rootroot00000000000000PNG  IHDRrdZRdsBIT|d pHYsmmtEXtSoftwarewww.inkscape.org<IDATxyU?Rq\p)Kq+. ѶZ6m[Fqkijk.ՊZEuAH*vǹ3}_~3wΜy;ۙ33Kg+B9h$h$h$h$h$h$h$ .8d`ͪ:CtI숈}TƨCDz?~lxu{lU: ,`^~f|[UuftJ#Ed `|cY7 ' P DM@0ZD6nRN|P;("~Z``kg\ l|#򡪹 @Sbҭo̭\τHw|UUA5 s*VS}x oLz8HEp44@{eCOkL:UkfcRlZEdg๪7TuPY732fCW/Fgt}Z $/o2^Op-^ ٩D V [~\ie_sYF.Hoi}EdiT^+ŁʰV(K`EDf`&,"r|GDzGAU^8|VGY"qdHeX <_C;ۊXsе<: 3 |8  &ˇa%Wc dޓP:XuwevBB|^#k K']@'u6A ~9D^"y2;@OqZFK:r G;ywp г& U-V^%W@¤@{))"#QgPթpi`:A[%{Q؀5PXW`mPg7N}ke7=<݀asU!"GD.ؼ<Y("&;jlmDd"}D.  KGث "]D$l01k6ymSl4[D*[ܞ"B}D|VSI%Tά4czE2HcBdt[gOĜ^:U.P؇vIV u̵3=sȑpg69*\ey C@DcVzmY5}/Ⱦ:Wv^G_+߰ѫ`h%rl|_Aw 2௘nYŚj -":Or*>/8I꿓,pEsdȮsObSw`)iC+;DȤ;hXSiU="R9=Ivh+H$i}8(VoӪMk6%-SU5RSZ<<(W6<܋>U"rz .|~^)􍎨"iqOm^'yxCGDbvjuQgp@gYs@d{qCDd!3"z Z0oZ+@j9kTOΉyD@xWQnfxa !v^[yT`:SK∊Pgn'RUq"5v (^+]JmmvJ2^1B쨒< 7ǯGd<{"_f?*2?q=hqb|,!SV-Q' Zj/,N4kH>%/Ff"">dzЩ꫘3%2<x%|6I\$W޼W0+X-RǞs n׼Dj^sD"+i$fȌQ)D>"u(qD q8WsX&м꛸2D-2}Hi^c|3,*&nc.H%U+Gd>O |LJ껄z5Jl"bJdHfoL1zP?YJpրj9? .hi^(_F9y sEZO'gN_ȹ)D>n f@T; xVt<;o#u>ji9]>Usz[+S;Pmľ6ZdЩ$,JD "͵.lOyxR;,o߂^3U-E$j~\'[;vr+m~y xR|sP~?yt S(w>:GJ1It7Ug; mH'͙6}ZƬ;{v D`c+.$2#UC@Z}ZuEdNW6y_V`jGUqD29Q<:,dzI(Pr?H3"<гp#z^#HŁ<$_ࢪKD+{jLdvq)T aMæQW?"fׄ AcڨkHZp8/5>gtϦ#lW!.B 2F TnעBۧ Z. $"7Y"r\."/bǽ[.oySI:Xn,ULD`\+#"pl+ =pOI>'\2}cG"6kvv붺ж6Ջ9|? niW*<$҉kG7۪:Z敹hIηBTEacE,A N&gvl8C;=H?Fܷ@QzpڝRBU4U}0DL"2[Dn udr]淔#vP+|6kvփXKH<l0NG:d_WT3 ׏oi\"u.P|Gԥj"µ쿮94yU̧9T#߲,\ƈu:!t%>u)vF*=;Bjȗ "23?nxm F#ع5>xFUK 5F`~Ai˜Sx}Aa_D Uxw h>fBsz +vx蔎Me+ƱDHp9K۱E¨S rT9}[m̈́6-ӕGV) s{}-u!`ܕ҉̔9suczW #ȶVv OG\՝=+fNאYLnΉŻmRk9DdsUØPu5r$KY: 7M!GH_>`\ݡvB "WffGէ7::-tZD6 ZD6 ZD6 ZD6 ZD6 ZD6 `ܯn=ƤZIENDB`hy-1.0.0/docs/_static/hy_logo.png000066400000000000000000000353361467401276000166750ustar00rootroot00000000000000PNG  IHDRv-KQsBIT|d pHYsnu>tEXtSoftwarewww.inkscape.org< IDATxwU?ߐB%@ ]RDP"(ꌢeԱ 8XAtTd RH {MY?x唵'pZ޼wגQ( Hll_Owy\jf+Xvϡ"B$ xp &`M= ef,)^(:B b~!ߚٳ ,E B|3Lnkfgh 17} mkfxEkhE ˆHZn4I^z5%U/dvI+)Y ~0)`Ê3sdvI;;, ]!i-B)byxTBĸ$  6n cfwWUX8Qђz1u!K jue)} 7Nzm>,I+V::b 㐿+BC`'༘%k oa/ńvx'Iӫ^H$vIo#E) ?bߖ^Gl \7waS`Mqk $4-3,z!U*Nڝ.Bh)&pvI+Vk_v4~Ҫ׀Zޕ = Bij:fIa-6V1B3h|KRU˲uKIˁ-|fkW Bri3ͺ x X,Mb-r]?Li`pu8. 13)`"p%p=1$M h6~h[Gߨz!*bU0˵l #i{i/ggfvc II~ʟ63[:v\(Cl?:Tv^oJ:6@,^(f}en! `ՋHIG{vO#x=նBx$M 8<U$tE[E ̞4wνTrOzu>DՁ[mP1'|_PA$}}fN'/-^(0/xߐ45V.iv;=4v. %8zla/?*^(F3Sw=vo]63:x_Pb%-fSEi'bE hcG+ZŽ^UowZvI =a:P(fvIg+J{hmPwBЇ٩T "iNJ|2f]D`jfuBП+p5~#ut&'uBЧCTe88Ѵ"o]P()fwo|}TL|$ZLfa + 5 ). cEo3Q .0{V3ua,a? E B|Idg-Qػ:kP(mh+L:a={ Ba0? %mZ߮K;.[( cf_OWkvI[ta{P=P튑"nu(^(1/UzJ] F]؝mfWuBPx72Xf1KE쒦ӝ/nBfBW#Vi åbvfl6 Ba8~TC+1 {8LP(3[ |ׯ^~;"E~_/ 5 4;ώyӦvi{PH->Ww 34b6Z) 9]yofBK^(bNn&Pa]"B=NfmK`:&F$IIZ_ IS+eev;8$϶y䩤` 3;N!ie`+e1+ yM&` A$mH8 i5N'.q=*l|M\_\\֤#Ӆ$||ȭ2}FmBKJ`b̷'w1u@'%!bBa>8s=CvzxZAi"$BvH (zfvGF-3@`'}I30nI09@1vTB! .+ϖJߦbMذ KvM atDŽE(^B#fv Į~<^0ڤcRg@{:;j;20ؙGK,-"W'IO"/W$>h83mq)ڔ-vLhPʉ&g7")`fɉO'NEn"E 02^R*aoj/uxr0 of01J9cXƑF~if9ګ3[df_'"*~л-D`S)pU,YcH-FG[H' a$ؾqy3 gMm zI#Ћl^6ᆎmw2[)ƙY>؅BKH:8>% 3݌ HccvOo{k(FTੌ&UB;4Gؽ(^P84AT;f ՓULVGx4IRyy7A>"j&T͗L> 8Y=p#+)}Y`DXCaӁI\*Ęٳə_Ra*mJ!)FU/Ftkrg/]ؽ$m !騲C{u*ad6]K^!&H0PTg{1IfMǤْRSzྲྀ zIN~tg)Rpp|  ;1O7O:^4$QrD7'H*|'ϊLR ;ƴ*8 B-o~S"fbf ms-Sݗ"!W+PSh&Of/֯{a3[,-TGh5xFO; X*S҅fvFk(tY _frI}1gRIirۚz3Cһbaj %dRJmfwe[3{Jҹޙ\#<6¾vlϻSdObfWK_cf4SD=23̾kfo | .aRA6Lvo:[اHJ1f(asfOzsfS`sBt4땶N,Nrfv8Rݟ{(S "i} $O 3,82.(~$%)6O<Y(nJn"> \:FfkIۉA3~Hőf1G{-!imaia_#=)qY6k(8"i ઌ.NYP2pn 1+[K x)ʆt1K#I2ݳ`fOrI;=2j́Հ]͞af#bfWK|SߦR@=5[uvO܏ ~;{6uv1_GICsIcf$0U+i3K"UpU{GBah¾N|I% D_ԇc"&KF] $}R |'̞!XKK mW1wJOt9.L&D?Ji|I3{@"Hya3u PRTTQ׈}>HIkȇUJ^P788**X@-DVž-l ?®+IJ΂J٭oaߨ&]2{q@oÿp2NPJHZ .!_C"VΒު%%4Œ^n\loէ!܃]|n]R[f7p0~#X>v5b<}I߫I6'tH< 9ˉj7wˑaQ4 OWz!C Ewƃ7fv'+HOcr zfd.+c$mDuώK){{ܵHZ=߮gK`z5B^6P#}sΛΙL}mtU "b(ygB)fhfl piG"\K^Ly?¿B B#^[/j!fv y[> lm4wV%MNH}p6p(gfGEf`փ_ e`f|xCU}m_mpǿp"V3=fՁ.83ۂО DSrO]12&6;%N<z1J$.? r+lbf*wKo&CIu=6,fva'+RT#=Y0g7C~Nx_3$=?dYScpdfs״f}ֻwYoHOc]o񫅰 ]4s58"l _eFZ`x2+fv2vBe'&53)aj$wS0tPaUS;a7f73PO5ۚYtذٯ ṡ72>)w6ۋޫ%q9SX_To"E|r'XͼN5mF~6SۃSEkXevnFxPqif/1KMIW죳i6RBu){N 5"&4'5bDl dkӕ1q3\ز[#K {K7O%Nd;5qIm;Apk#ƥcFu^ܭYN!wSz\)ΒM[اRyOM",dF173f6bgRbfInjN=u朅1PaAX}ٍU5Q'a1'.OzM{91Wwc`Xapl-iJ&_Hx : A%$l[g8W>Eo :;Kc56)=]ϝx%FW}c `4Tx_ix:&yWK5%rM_m 7⿹X+aF]uz=tg{Mد"Lgqs{_Kc1 ӝnM$߾jxoՄzZg=ROeq&cDaKF6Q{OBmoaB6[7[Ox%;<2/h3 lS"W<`gU}Lf9&u: ng7мuRoFK5c ]&_}6uY8Jg[?Nふ\S Q=QwѨM@[اIf*JCRu_rF3%9ѯyہǝm6>\PS1*@S>Z*cm]oU&s?hK2gMII`/'/?1uq8s_"v׆` dOzIؽ vlU=w4vj֘nfs;NoaQ"]՝m;j%a}yIyCoأFJtCFD/ͪ u_rJǴ*91k0ω T3{D!Ӿ+˶2uvI٣ uɳ{ j;xGKZ٦;155C2&;SmsF$m߰#f|ll3y#/7PF#̳C}~P29gMɳ{ 4I;>!ZVv邙-2YĞ?;nQaW8m^tL˙g_[:c}S[nf?l`4^%ibF#3*i-gUї51E}]4go~ۗʝ3dXlb Jex ZulA`/fvi4b$/P xf~̳o3ZGN#vOF`^@5bgMǜmJ:&,FbHaq^-O~iOIDAT\/6Nnjԅxf{Eؽ5`_*g>bӭl/WF_#WHx {R!x/cZ;8643P7ށ?UՔt&6aƑa'n"v_Su}N*oM쁙=}S`^H\O؎SneFحۥ<;{CTN&{wlާp)an}ene3ւ;tL'j;">Nz|p2v45ܻ$imgh~ɝcIgoL:N%DQḘu\w@HxҔg"9} fKId¾bΛ4)mMh;'9xpg [ !3`f8/)~#a9%=Kef^l3)fF{Chk= IKaTҲ^/ddWևcP2SrW%v%17?cZ l&%v<с~̗I;#KL[$хbpGRcHZ)Ges9EҊf-t&] g;Ir.0$݄n"R _J3;o-J#⺓Z\qIok2)>:}G]0{doJ7,>hfcm{ d`)Ȳ#γolj fv3!Ǵ *w{fv3UŁgF`un2W}X}2hpB͂Iɿt#fvR+bPEvfew-ӔZפ4Ow>fvѹU.3|_@M 9{m6twԛ{)aM"fI"X=[}Ct Jd; f(g!֑kW̬'0eC T~9yC 0+E_#퀓୒%lf-`=c/iK[٥] r&p51@NaQr̻z {"'1Oh?fI8e^$‰ΜF!io:NH7ǍB<`j"Y|~e2ح u^Lrvs+aiyQܑt=@_6O;KTOe Mv4c - WxP7'ʑAgЯ"^#ԕHxei'鷄9^bfw IG%2EafLyӁ䝿ZhfNΑt}?1s^Gx^JRnf< wuvH4y(%,iľ }(kW%i75WfWhryp-%M>z?vDb?cfߗ~nfgh_bڼf:{IWKzW IHG)9Yg6mf%TAMe-1ol.>K$}JJZGO%t^PO}=݋7@kߖ74`7ҵ-t}[.~54~_5 ^8"ۿ&# Ah+5%f6WVH:)U{_^(z;w[]\ /)B٘z GJJuWNLvd{oS쯄\UŁk]쑪Rs=n=a7b"bkfveՋi,wX;K a ò}. &⥴M7nL8|k! cf ];Fh0; a7I/E:Za7;3-阆Er 2s03녽ܬl1g{=aN#iJf޸x'_b&&c&wjK,M`Oxy.F{eRDU.fv7nK:ՄCm&Mјldf?-A ara>I+eYp1;o!̏Lo "xb~k[| :6Ϳ2+$BkC$^.,e鐴_,ߤM؀|﫵xhfJ@{`fggHz1Aw!0Z_C8 rws>p]o}[$@%݃oj!+KZdYHLȿoTU ]>C ղ;m8 {]r'cb<\3̮7{oa?^.nwWaM=F](M.&4Ӳ)!{,#[/} Yv WV#<='lfs[a66ƷxWZ*;ms(E -Z{0I'5}Z&7TL==;#u 7;;HgsV$mh3^x PCa[g>0GFvs_>h*z?bF.mHgLϒ^v2$r5q0!챃O3-v΋ j#M 8^x BDz {Z'L~/ }tM&j%tL#%}N$-C?hf) /.eY6Q M\S)&'> =>GDh$vIIk6tWY vm{#nE[> } 7_5!i<&>k{8 yO%NХu{vIb3˖,q/_.e ]aQRʓ- I;vab MTΓ 82'^hٹU/ $l؅c݃)9vzd ,fISƒG<=_4&bg~RB#ٓpWaE4۹kI4aLh;5뫁=t $ VQK3Irl4NcR1f4 \oA._(d1#l3GS C̣^16>VD})["vx~Px-PXQJIϝ_<;2|3{IG -6M$p@ffU<1 c;;M/n'",wþ#B:3{ɰv*/"}]Pn6139BRbt?X0byN#Q%{z8G(t)fa6QV'̎oP!y݀u6L6OEw} - n]D,fv% \GhT( ЍT 7YljfWP(4ѽ:;>146b0+?Tz BBP] *-쑪zCRQ(:4/ ]JX?5-P(wۀ1V5^# Ȋ| "i)N-zB?T. `:;\y7ΘGʵ 8^Һ/ fl+rǡH:I "LT/G °HzAZG8it+K;T;hK  sHV=H:8%|̾YBPC$ ,["7Z! C{+_(/G#Ѓ;Rͤ|̾U B }6(!Z7bĎjZ 曒>[ B=:h-Q$\ TRGP$M%]_ͷ6d`OFfvѪAgxm^HP/Ҿә=Pqm`_!Bl$mIh-Lo=:a\,WZ"?ofTBIvhmقH:vBD EtNe_P[ֻ}I/}'6Aؤh"WWBcc3;=\Z4)h¾8.vڗ`f$ k{IdfbPȈ~VA`7B{Xc B5 ̎L尯 8굴II ` ?þv8z/v`4zB%įodf S:,1fv:ŒrIӪ^DPLν15E؇`f'/z-pvR(Z$`3Yi8B+^W_NIPP]A< p:0S 1("fҾX%K/Ÿ́ BSM$- H(Wzy\: {HؓP+| :-P()E؝4X{d___ n,İIENDB`hy-1.0.0/docs/_static/hy_logo.svg000066400000000000000000000117651467401276000167100ustar00rootroot00000000000000 image/svg+xml λ λ ( ) hy-1.0.0/docs/api.rst000066400000000000000000001453001467401276000143750ustar00rootroot00000000000000API reference ============= This chapter describes most of Hy's public-facing macros, functions, and classes. It refers to Python's own documentation when appropriate rather than recapitulating the details of Python semantics. .. contents:: Contents :local: .. _core-macros: Core macros ----------- The following macros are automatically imported into all Hy modules as their base names, such that ``hy.core.macros.foo`` can be called as just ``foo``. Macros that are also available as functions are described as functions under :ref:`pyop`. Fundamentals ~~~~~~~~~~~~ .. hy:macro:: (do [#* body]) ``do`` (called ``progn`` in some Lisps) takes any number of forms, evaluates them, and returns the value of the last one, or ``None`` if no forms were provided. :: (+ 1 (do (setv x (+ 1 1)) x)) ; => 3 .. hy:macro:: (do-mac [#* body]) ``do-mac`` evaluates its arguments (in order) at compile time, and leaves behind the value of the last argument (``None`` if no arguments were provided) as code to be run. The effect is similar to defining and then immediately calling a nullary macro, hence the name, which stands for "do macro". :: (do-mac `(setv ~(hy.models.Symbol (* "x" 5)) "foo")) ; Expands to: (setv xxxxx "foo") (print xxxxx) ; => "foo" Contrast with :hy:func:`eval-and-compile`, which evaluates the same code at compile-time and run-time, instead of using the result of the compile-time run as code for run-time. ``do-mac`` is also similar to Common Lisp's SHARPSIGN DOT syntax (``#.``), from which it differs by evaluating at compile-time rather than read-time. .. hy:macro:: (eval-and-compile [#* body]) ``eval-and-compile`` takes any number of forms as arguments. The input forms are evaluated as soon as the ``eval-and-compile`` form is compiled, then left in the program so they can be executed at run-time as usual; contrast with :hy:func:`eval-when-compile`. So, if you compile and immediately execute a program (as calling ``hy foo.hy`` does when ``foo.hy`` doesn't have an up-to-date byte-compiled version), ``eval-and-compile`` forms will be evaluated twice. For example, the following program :: (eval-when-compile (print "Compiling")) (print "Running") (eval-and-compile (print "Hi")) prints .. code-block:: text Compiling Hi Running Hi The return value of ``eval-and-compile`` is its final argument, as for :hy:func:`do`. One possible use of ``eval-and-compile`` is to make a function available both at compile-time (so a macro can call it while expanding) and run-time (so it can be called like any other function):: (eval-and-compile (defn add [x y] (+ x y))) (defmacro m [x] (add x 2)) (print (m 3)) ; prints 5 (print (add 3 6)) ; prints 9 Had the ``defn`` not been wrapped in ``eval-and-compile``, ``m`` wouldn't be able to call ``add``, because when the compiler was expanding ``(m 3)``, ``add`` wouldn't exist yet. While ``eval-and-compile`` executes the same code at both compile-time and run-time, bear in mind that the same code can have different meanings in the two contexts. Consider, for example, issues of scoping:: (eval-when-compile (print "Compiling")) (print "Running") (eval-and-compile (setv x 1)) (defn f [] (setv x 2) (eval-and-compile (setv x 3)) (print "local x =" x)) (f) (eval-and-compile (print "global x =" x)) The form ``(setv x 3)`` above refers to the global ``x`` at compile-time, but the local ``x`` at run-time, so the result is: .. code-block:: text Compiling global x = 3 Running local x = 3 global x = 1 .. hy:macro:: (eval-when-compile [#* body]) ``eval-when-compile`` executes the given forms at compile-time, but discards them at run-time and simply returns :data:`None` instead; contrast :hy:func:`eval-and-compile`. Hence, while ``eval-when-compile`` doesn't directly contribute code to the final program, it can change Hy's state while compiling, as by defining a function:: (eval-when-compile (defn add [x y] (+ x y))) (defmacro m [x] (add x 2)) (print (m 3)) ; prints 5 (print (add 3 6)) ; raises NameError: name 'add' is not defined .. hy:macro:: (py [string]) ``py`` parses the given Python code at compile-time and inserts the result into the generated abstract syntax tree. Thus, you can mix Python code into a Hy program. Only a Python expression is allowed, not statements; use :hy:func:`pys ` if you want to use Python statements. The value of the expression is returned from the ``py`` form. :: (print "A result from Python:" (py "'hello' + 'world'")) The code must be given as a single string literal, but you can still use macros, :hy:func:`hy.eval `, and related tools to construct the ``py`` form. If having to backslash-escape internal double quotes is getting you down, try a :ref:`bracket string `. If you want to evaluate some Python code that's only defined at run-time, try the standard Python function :func:`eval`. The code is implicitly wrapped in parentheses so Python won't give you grief about indentation. After all, Python's indentation rules are only useful for grouping statements, whereas ``py`` only allows an expression. Python code need not syntactically round-trip if you use :ref:`hy2py` on a Hy program that uses ``py`` or ``pys``. For example, comments will be removed. .. hy:macro:: (pys [string]) As :hy:func:`py `, but the code can consist of zero or more statements, including compound statements such as ``for`` and ``def``. ``pys`` always returns ``None``. :: (pys "myvar = 5") (print "myvar is" myvar) Unlike ``py``, no parentheses are added, because Python doesn't allow statements to be parenthesized. Instead, the code string is dedented with :func:`textwrap.dedent` before parsing. Thus you can indent the code to match the surrounding Hy code when Python would otherwise forbid this, but beware that significant leading whitespace in embedded string literals will be removed. .. hy:macro:: (pragma [#* args]) ``pragma`` is used to adjust the state of the compiler. It's called for its side-effects, and returns ``None``. The arguments are key-value pairs, like a function call with keyword arguments:: (pragma :prag1 value1 :prag2 (get-value2)) Each key is a literal keyword giving the name of a pragma. Each value is an arbitrary form, which is evaluated as ordinary Hy code but at compile-time. The effect of each pragma is locally scoped to its containing function, class, or comprehension form (other than ``for``), if there is one. Only one pragma is currently implemented: .. _warn-on-core-shadow: - ``:warn-on-core-shadow``: If true (the default), :hy:func:`defmacro` and :hy:func:`require` will raise a warning at compile-time if you define a macro with the same name as a core macro. Shadowing a core macro in this fashion is dangerous, because other macros may call your new macro when they meant to refer to the core macro. Quoting ~~~~~~~~~~~~ .. hy:macro:: (quote [model]) Return the given :ref:`model ` without evaluating it. Or to be more pedantic, ``quote`` complies to code that produces and returns the model it was originally called on. Thus ``quote`` serves as syntactic sugar for model constructors:: (quote a) ; Equivalent to: (hy.models.Symbol "a") (quote (+ 1 1)) ; Equivalent to: (hy.models.Expression [ ; (hy.models.Symbol "+") ; (hy.models.Integer 1) ; (hy.models.Integer 1)]) ``quote`` itself is conveniently :ref:`abbreviated ` as the single-quote character ``'``, which needs no parentheses, allowing one to instead write:: 'a '(+ 1 1) See also: - :hy:func:`quasiquote` to substitute values into a quoted form - :hy:func:`hy.eval` to evaluate models as code - :hy:func:`hy.repr` to stringify models into Hy source text that uses ``'`` .. hy:macro:: (quasiquote [model]) .. hy:macro:: (unquote [model]) .. hy:macro:: (unquote-splice [model]) ``quasiquote`` is like :hy:func:`quote` except that it treats the model as a template, in which certain special :ref:`expressions ` indicate that some code should be evaluated and its value substituted there. The idea is similar to C's ``sprintf`` or Python's various string-formatting constructs. For example:: (setv x 2) (quasiquote (+ 1 (unquote x))) ; => '(+ 1 2) ``unquote`` indicates code to be evaluated, so ``x`` becomes ``2`` and the ``2`` gets inserted in the parent model. ``quasiquote`` can be :ref:`abbreviated ` as a backtick (\`), with no parentheses, and likewise ``unquote`` can be abbreviated as a tilde (``~``), so one can instead write simply :: `(+ 1 ~x) (In the bulk of Lisp tradition, unquotation is written ``,``. Hy goes with Clojure's choice of ``~``, which has the advantage of being more visible in most programming fonts.) Quasiquotation is convenient for writing macros:: (defmacro set-foo [value] `(setv foo ~value)) (set-foo (+ 1 2 3)) (print foo) ; => 6 Another kind of unquotation operator, ``unquote-splice``, abbreviated ``~@``, is analogous to ``unpack-iterable`` in that it splices an iterable object into the sequence of the parent :ref:`sequential model `. Compare the effects of ``unquote`` to ``unquote-splice``:: (setv X [1 2 3]) (hy.repr `[a b ~X c d ~@X e f]) ; => '[a b [1 2 3] c d 1 2 3 e f] If ``unquote-splice`` is given any sort of false value (such as ``None``), it's treated as an empty list. To be precise, ``~@x`` splices in the result of ``(or x [])``. Note that while a symbol name can begin with ``@`` in Hy, ``~@`` takes precedence in the parser, so if you want to unquote the symbol ``@foo`` with ``~``, you must use whitespace to separate ``~`` and ``@``, as in ``~ @foo``. Assignment, mutation, and annotation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. hy:macro:: (setv [#* args]) ``setv`` compiles to an :ref:`assignment statement ` (see :hy:func:`setx` for assignment expressions), which sets the value of a variable or some other assignable expression. It requires an even number of arguments, and always returns ``None``. The most common case is two arguments, where the first is a symbol:: (setv websites 103) (print websites) ; => 103 Additional pairs of arguments are equivalent to several two-argument ``setv`` calls, in the given order. Thus, the semantics are like Common Lisp's ``setf`` rather than ``psetf``. :: (setv x 1 y x x 2) (print x y) ; => 2 1 All the same kinds of complex assignment targets are allowed as in Python. So, you can use list assignment to assign in parallel. (As in Python, tuple and list syntax are equivalent for this purpose; Hy differs from Python merely in that its list syntax is shorter than its tuple syntax.) :: (setv [x y] [y x]) ; Swaps the values of `x` and `y` Unpacking assignment looks like this (see :hy:func:`unpack-iterable`):: (setv [letter1 letter2 #* others] "abcdefg") (print letter1 letter2 (hy.repr others)) ; => a b ["c" "d" "e" "f" "g"] See :hy:func:`let` to simulate more traditionally Lispy block-level scoping. .. hy:macro:: (setx [target value]) ``setx`` compiles to an assignment expression (:pep:`572`). Thus, unlike :hy:func:`setv`, it returns the assigned value. It takes exactly two arguments, and the target must be a bare symbol. :: (when (> (setx x (+ 1 2)) 0) (print x "is greater than 0")) ; => 3 is greater than 0 .. hy:macro:: (let [bindings #* body]) ``let`` is a macro for simulating traditional block scoping as seen in other Lisps. Since it coexists with ordinary Python scoping, its consequences can be complex, so it's wise to get a solid understanding of Python scoping before you use it. Beginners to Python should note particularly that :hy:func:`setv` inside a function or class typically creates a local variable, so ``let`` isn't required for local variables or closures as it is in many other Lisps. That disclaimer aside, ``let`` creates local variables with lexically scoped names. The macro takes a list of binding pairs followed by a ``body`` which gets executed. A let-bound name ceases to refer to that local outside the ``let`` form, but arguments in nested functions, and bindings in nested ``let`` forms, can shadow these names. :: (let [x 5 y 6] ; Create `x` and `y` (print x y) ; => 5 6 (let [x 7] ; Create a variable that shadows the earlier `x` (print x y)) ; => 7 6 (print x y)) ; => 5 6 The left-hand item of a binding pair is typically a plain symbol, but it can also use extended iterable unpacking (:pep:`3132`):: (let [[head #* tail] #(0 1 2)] [head tail]) ; => [0 [1 2]] Basic assignments, as with :hy:func:`setv` or ``+=``, will update the local variable named by a let binding when they assign to a let-bound name. But assignments via :hy:func:`import` are always hoisted to normal Python scope, and likewise, :hy:func:`defn` or :hy:func:`defclass` will assign the function or class in the Python scope, even if it shares the name of a let binding. To avoid this hoisting, use :func:`importlib.import_module`, :hy:func:`fn`, or :class:`type` (or whatever metaclass) instead. If :hy:func:`lfor`, :hy:func:`sfor`, :hy:func:`dfor`, or :hy:func:`gfor` (but not :hy:func:`for`) is in the body of a ``let``, assignments in iteration clauses and ``:setv`` clauses will create a new variable in the comprehenion form's own scope, without touching any outer let-bound variable of the same name. Like the ``let*`` of many other Lisps, ``let`` executes the variable assignments one-by-one, in the order written:: (let [x 5 y (+ x 1)] (print x y)) ; => 5 6 (let [x 1 x (fn [] x)] (x)) ; => 1 Note that let-bound variables continue to exist in the surrounding Python scope. As such, let-bound objects may not be eligible for garbage collection as soon as the ``let`` ends. To ensure there are no references to let-bound objects as soon as possible, use ``del`` at the end of the ``let``, or wrap the ``let`` in a function. .. hy:macro:: (global [#* syms]) ``global`` compiles to a :py:keyword:`global` statement, which declares one or more names as referring to global (i.e., module-level) variables. The arguments are symbols; with no arguments, ``global`` has no effect. The return value is always ``None``. :: (setv a 1 b 10) (print a b) ; => 1 10 (defn f [] (global a) (setv a 2 b 20)) (f) (print a b) ; => 2 10 .. hy:macro:: (nonlocal [#* syms]) Similar to :hy:func:`global`, but names can be declared in any enclosing scope. ``nonlocal`` compiles to a :py:keyword:`global` statement for any names originally defined in the global scope, and a :py:keyword:`nonlocal` statement for all other names. :: (setv a 1 b 1) (defn f [] (setv c 10 d 10) (defn g [] (nonlocal a c) (setv a 2 b 2 c 20 d 20)) (print a b c d) ; => 1 1 10 10 (g) (print a b c d)) ; => 2 1 20 10 (f) .. hy:macro:: (del [#* args]) ``del`` compiles to a :py:keyword:`del` statement, which deletes variables or other assignable expressions. It always returns ``None``. :: (del foo (get mydict "mykey") myobj.myattr) .. hy:macro:: (annotate [value type]) ``annotate`` and its shorthand form ``#^`` are used to denote annotations, including type hints, in three different contexts: - Standalone variable annotations (:pep:`526`) - Variable annotations in a :hy:func:`setv` call - Function-parameter annotations (:pep:`3107`) The difference between ``annotate`` and ``#^`` is that ``annotate`` requires parentheses and takes the name to be annotated first (like Python), whereas ``#^`` doesn't require parentheses (it only applies to the next two forms) and takes the name second:: (setv (annotate x int) 1) (setv #^ int x 1) The order difference is not merely visual: ``#^`` actually evaluates the type first. Here are examples with ``#^`` for all the places you can use annotations:: ; Annotate the variable `x` as an `int` (equivalent to `x: int`). #^ int x ; You can annotate with expressions (equivalent to `y: f(x)`). #^(f x) y ; Annotations with an assignment: each annotation `(int, str)` ; covers the term that immediately follows. ; Equivalent to `x: int = 1; y = 2; z: str = 3` (setv #^ int x 1 y 2 #^ str z 3) ; Annotate `a` as an `int`, `c` as an `int`, and `b` as a `str`. ; Equivalent to `def func(a: int, b: str = None, c: int = 1): ...` (defn func [#^ int a #^ str [b None] #^ int [c 1]] ...) ; Function return annotations come before the function name (if ; it exists). (defn #^ int add1 [#^ int x] (+ x 1)) (fn #^ int [#^ int y] (+ y 2)) For annotating items with generic types, the :hy:func:`of ` macro will likely be of use. An issue with type annotations is that, as of this writing, we know of no Python type-checker that can work with :py:mod:`ast` objects or bytecode files. They all need Python source text. So you'll have to translate your Hy with :ref:`hy2py` in order to actually check the types. .. hy:macro:: (deftype [args]) ``deftype`` compiles to a :py:keyword:`type` statement, which defines a type alias. It requires Python 3.12. Its arguments optionally begin with ``:tp`` and a list of type parameters (as in :hy:func:`defn`), then specify the name for the new alias and its value. :: (deftype IntOrStr (| int str)) (deftype :tp [T] ListOrSet (| (get list T) (get set T))) Subsetting ~~~~~~~~~~~~ .. _dot: .. hy:data:: . The dot macro ``.`` compiles to one or more :ref:`attribute references `, which select an attribute of an object. The first argument, which is required, can be an arbitrary form. With no further arguments, ``.`` is a no-op. Additional symbol arguments are understood as a chain of attributes, so ``(. foo bar)`` compiles to ``foo.bar``, and ``(. a b c d)`` compiles to ``a.b.c.d``. As a convenience, ``.`` supports two other kinds of arguments in place of a plain attribute. A parenthesized expression is understood as a method call: ``(. foo (bar a b))`` compiles to ``foo.bar(a, b)``. A bracketed form is understood as a subscript: ``(. foo ["bar"])`` compiles to ``foo["bar"]``. All these options can be mixed and matched in a single ``.`` call, so :: (. a (b 1 2) c [d] [(e 3 4)]) compiles to .. code-block:: python a.b(1, 2).c[d][e(3, 4)] :ref:`Dotted identifiers ` provide syntactic sugar for common uses of this macro. In particular, syntax like ``foo.bar`` ends up meaning the same thing in Hy as in Python. Also, :hy:func:`get ` is another way to subscript in Hy. .. hy:macro:: (unpack-iterable [form]) .. hy:macro:: (unpack-mapping [form]) (Also known as the splat operator, star operator, argument expansion, argument explosion, argument gathering, and varargs, among others...) ``unpack-iterable`` and ``unpack-mapping`` allow an iterable or mapping object (respectively) to provide positional or keywords arguments (respectively) to a function. :: => (defn f [a b c d] [a b c d]) => (f (unpack-iterable [1 2]) (unpack-mapping {"c" 3 "d" 4})) [1 2 3 4] ``unpack-iterable`` is usually written with the shorthand ``#*``, and ``unpack-mapping`` with ``#**``. :: => (f #* [1 2] #** {"c" 3 "d" 4}) [1 2 3 4] Unpacking is allowed in a variety of contexts, and you can unpack more than once in one expression (:pep:`3132`, :pep:`448`). :: => (setv [a #* b c] [1 2 3 4 5]) => [a b c] [1 [2 3 4] 5] => [#* [1 2] #* [3 4]] [1 2 3 4] => {#** {1 2} #** {3 4}} {1 2 3 4} => (f #* [1] #* [2] #** {"c" 3} #** {"d" 4}) [1 2 3 4] Conditionals and basic loops ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. hy:macro:: (if [test true-value false-value]) ``if`` compiles to an :py:keyword:`if` expression (or compound ``if`` statement). The form ``test`` is evaluated and categorized as true or false according to :py:class:`bool`. If the result is true, ``true-value`` is evaluated and returned. Othewise, ``false-value`` is evaluated and returned. :: (if (has-money-left account) (print "Let's go shopping!") (print "Back to work.")) See also: - :hy:func:`do`, to execute several forms as part of any of ``if``'s three arguments. - :hy:func:`when `, for shorthand for ``(if condition (do …) None)``. - :hy:func:`cond `, for shorthand for nested ``if`` forms. .. hy:automacro:: hy.core.macros.when .. hy:automacro:: hy.core.macros.cond .. hy:macro:: (while [condition #* body]) ``while`` compiles to a :py:keyword:`while` statement, which executes some code as long as a condition is met. The first argument to ``while`` is the condition, and any remaining forms constitute the body. It always returns ``None``. :: (while True (print "Hello world!")) The last form of a ``while`` loop can be an ``else`` clause, which is executed after the loop terminates, unless it exited abnormally (e.g., with ``break``). So, :: (setv x 2) (while x (print "In body") (-= x 1) (else (print "In else"))) prints :: In body In body In else If you put a ``break`` or ``continue`` form in the condition of a ``while`` loop, it will apply to the very same loop rather than an outer loop, even if execution is yet to ever reach the loop body. (Hy compiles a ``while`` loop with statements in its condition by rewriting it so that the condition is actually in the body.) So, :: (for [x [1]] (print "In outer loop") (while (do (print "In condition") (break) (print "This won't print.") True) (print "This won't print, either.")) (print "At end of outer loop")) prints :: In outer loop In condition At end of outer loop .. hy:macro:: (break) ``break`` compiles to a :py:keyword:`break` statement, which terminates the enclosing loop. The following example has an infinite ``while`` loop that ends when the user enters "k":: (while True (if (= (input "> ") "k") (break) (print "Try again"))) In a loop with multiple iteration clauses, such as ``(for [x xs y ys] …)``, ``break`` only breaks out of the innermost iteration, not the whole form. To jump out of the whole form, enclose it in a :hy:func:`block ` and use ``block-ret`` instead of ``break``. In the case of :hy:func:`for`, but not :hy:func:`lfor` and the other comprehension forms, you may also enclose it in a function and use :hy:func:`return`. .. hy:macro:: (continue) ``continue`` compiles to a :py:keyword:`continue` statement, which returns execution to the start of a loop. In the following example, ``(.append output x)`` is executed on each iteration, whereas ``(.append evens x)`` is only executed for even numbers. :: (setv output [] evens []) (for [x (range 10)] (.append output x) (when (% x 2) (continue)) (.append evens x)) In a loop with multiple iteration clauses, such as ``(for [x xs y ys] …)``, ``continue`` applies to the innermost iteration, not the whole form. To jump to the next step of an outer iteration, try rewriting your loop as multiple nested loops and interposing a :hy:func:`block `, as in ``(for [x xs] (block (for [y ys] …)))``. You can then use ``block-ret`` in place of ``continue``. Comprehensions ~~~~~~~~~~~~~~ .. hy:macro:: (for [#* args]) ``for`` compiles to one or more :py:keyword:`for` statements, which execute code repeatedly for each element of an iterable object. The return values of the forms are discarded and the ``for`` form returns ``None``. :: (for [x [1 2 3]] (print "iterating") (print x)) ; Output: iterating 1 iterating 2 iterating 3 The first argument of ``for``, in square brackets, specifies how to loop. A simple and common case is ``[variable values]``, where ``values`` is an iterable object (such as a list) and ``variable`` is a symbol specifiying the name for each element. Subsequent arguments to ``for`` are body forms to be evaluated for each iteration of the loop. More generally, the first argument of ``for`` allows the same types of clauses as :hy:func:`lfor`:: (for [x [1 2 3] :if (!= x 2) y [7 8]] (print x y)) ; Output: 1 7 1 8 3 7 3 8 In particular, you can use an ``:async`` clause to get the equivalent of Python's :py:keyword:`async for`:: (import asyncio) (defn :async numbers [] (yield 1) (yield 2)) (asyncio.run ((fn :async [] (for [:async x (numbers)] (print x))))) The last argument of ``for`` can be an ``(else …)`` form. This form is executed after the last iteration of the ``for``\'s outermost iteration clause, but only if that outermost loop terminates normally. If it's jumped out of with e.g. ``break``, the ``else`` is ignored. :: (for [x [1 2 3]] (print x) (when (= x 2) (break)) (else (print "loop finished"))) .. hy:macro:: (lfor [#* args]) The comprehension forms ``lfor``, :hy:func:`sfor`, :hy:func:`dfor`, :hy:func:`gfor`, and :hy:func:`for` are used to produce various kinds of loops, including Python-style :ref:`comprehensions `. ``lfor`` in particular can create a list comprehension. A simple use of ``lfor`` is:: (lfor x (range 5) (* 2 x)) ; => [0 2 4 6 8] ``x`` is the name of a new variable, which is bound to each element of ``(range 5)``. Each such element in turn is used to evaluate the value form ``(* 2 x)``, and the results are accumulated into a list. Here's a more complex example:: (lfor x (range 3) y (range 3) :if (!= x y) :setv total (+ x y) [x y total]) ; => [[0 1 1] [0 2 2] [1 0 1] [1 2 3] [2 0 2] [2 1 3]] When there are several iteration clauses (here, the pairs of forms ``x (range 3)`` and ``y (range 3)``), the result works like a nested loop or Cartesian product: all combinations are considered in lexicographic order. The general form of ``lfor`` is:: (lfor CLAUSES VALUE) where the ``VALUE`` is an arbitrary form that is evaluated to produce each element of the result list, and ``CLAUSES`` is any number of clauses. There are several types of clauses: - Iteration clauses, which look like ``LVALUE ITERABLE``. The ``LVALUE`` is usually just a symbol, but could be something more complicated, like ``[x y]``. - ``:async LVALUE ITERABLE``, which is an asynchronous form of iteration clause per Python's :py:keyword:`async for`. - ``:do FORM``, which simply evaluates the ``FORM``. If you use ``(continue)`` or ``(break)`` here, it will apply to the innermost iteration clause before the ``:do``. - ``:setv LVALUE RVALUE``, which is equivalent to ``:do (setv LVALUE RVALUE)``. - ``:if CONDITION``, which is equivalent to ``:do (when (not CONDITION) (continue))``. For ``lfor``, ``sfor``, ``gfor``, and ``dfor``, variables defined by an iteration clause or ``:setv`` are not visible outside the form. However, variables defined within the body, as with a ``setx`` expression, will be visible outside the form. In ``for``, by contrast, iteration and ``:setv`` clauses share the caller's scope and are visible outside the form. .. hy:macro:: (dfor [#* args]) ``dfor`` creates a :ref:`dictionary comprehension `. Its syntax is the same as that of :hy:func:`lfor` except that it takes two trailing arguments. The first is a form producing the key of each dictionary element, and the second produces the value. Thus:: (dfor x (range 5) x (* x 10)) ; => {0 0 1 10 2 20 3 30 4 40} .. hy:macro:: (gfor [#* args]) ``gfor`` creates a :ref:`generator expression `. Its syntax is the same as that of :hy:func:`lfor`. The difference is that ``gfor`` returns an iterator, which evaluates and yields values one at a time:: (import itertools [count take-while]) (setv accum []) (list (take-while (fn [x] (< x 5)) (gfor x (count) :do (.append accum x) x))) ; => [0 1 2 3 4] accum ; => [0 1 2 3 4 5] .. hy:macro:: (sfor [#* args]) ``sfor`` creates a :ref:`set comprehension `. ``(sfor CLAUSES VALUE)`` is equivalent to ``(set (lfor CLAUSES VALUE))``. See :hy:func:`lfor`. Context managers and pattern-matching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. hy:macro:: (with [managers #* body]) ``with`` compiles to a :py:keyword:`with` or an :py:keyword:`async with` statement, which wraps some code with one or more :ref:`context managers `. The first argument is a bracketed list of context managers, and the remaining arguments are body forms. The manager list can't be empty. If it has only one item, that item is evaluated to obtain the context manager to use. If it has two, the first argument (a symbol) is bound to the result of the second. Thus, ``(with [(f)] …)`` compiles to ``with f(): …`` and ``(with [x (f)] …)`` compiles to ``with f() as x: …``. :: (with [o (open "file.txt" "rt")] (print (.read o))) If the manager list has more than two items, they're understood as variable-manager pairs; thus :: (with [v1 e1 v2 e2 v3 e3] ...) compiles to .. code-block:: python with e1 as v1, e2 as v2, e3 as v3: ... The symbol ``_`` is interpreted specially as a variable name in the manager list: instead of binding the context manager to the variable ``_`` (as Python's ``with e1 as _: …``), ``with`` will leave it anonymous (as Python's ``with e1: …``). Finally, any variable-manager pair may be preceded with the keyword ``:async`` to use an asynchronous context manager:: (with [:async v1 e1] …) ``with`` returns the value of its last form, unless it suppresses an exception (because the context manager's ``__exit__`` method returned true), in which case it returns ``None``. So, the first example could also be written :: (print (with [o (open "file.txt" "rt")] (.read o))) .. hy:macro:: (match [subject #* cases]) ``match`` compiles to a :ref:`match statement `. It requires Python 3.10 or later. The first argument should be the subject, and any remaining arguments should be pairs of patterns and results. The ``match`` form returns the value of the corresponding result, or ``None`` if no case matched. :: (match (+ 1 1) 1 "one" 2 "two" 3 "three") ; => "two" You can use :hy:func:`do` to build a complex result form. Patterns, as in Python match statements, are interpreted specially and can't be arbitrary forms. Use ``(| …)`` for OR patterns, ``PATTERN :as NAME`` for AS patterns, and syntax like the usual Hy syntax for literal, capture, value, sequence, mapping, and class patterns. Guards are specified with ``:if FORM``. Here's a more complex example:: (match #(100 200) [100 300] "Case 1" [100 200] :if flag "Case 2" [900 y] f"Case 3, y: {y}" [100 (| 100 200) :as y] f"Case 4, y: {y}" _ "Case 5, I match anything!") This will match case 2 if ``flag`` is true and case 4 otherwise. ``match`` can also match against class instances by keyword (or positionally if its ``__match_args__`` attribute is defined; see :pep:`636`):: (import dataclasses [dataclass]) (defclass [dataclass] Point [] #^ int x #^ int y) (match (Point 1 2) (Point 1 x) :if (= (% x 2) 0) x) ; => 2 It's worth emphasizing that ``match`` is a pattern-matching construct rather than a generic `switch `_ construct, and retains all of Python's limitations on match patterns. For example, you can't match against the value of a variable. For more flexible branching constructs, see Hyrule's :hy:func:`branch ` and :hy:func:`case `, or simply use :hy:func:`cond `. Exception-handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. hy:macro:: (raise [exception :from other]) ``raise`` compiles to a :py:keyword:`raise` statement, which throws an exception. With no arguments, the current exception is reraised. With one argument, an exception, that exception is raised. :: (try (raise KeyError) (except [KeyError] (print "gottem"))) ``raise`` supports one other syntax, ``(raise EXCEPTION_1 :from EXCEPTION_2)``, which compiles to ``raise EXCEPTION_1 from EXCEPTION_2``. .. hy:macro:: (try [#* body]) ``try`` compiles to a :py:keyword:`try` statement, which can catch exceptions and run cleanup actions. It begins with any number of body forms. Then follows any number of ``except`` or ``except*`` (:pep:`654`) forms, which are expressions that begin with the symbol in question, followed by a list of exception types, followed by more body forms. Finally there are an optional ``else`` form and an optional ``finally`` form, which again are expressions that begin with the symbol in question and then comprise body forms. Note that ``except*`` requires Python 3.11, and ``except*`` and ``except`` may not both be used in the same ``try``. Here's an example of several of the allowed kinds of child forms:: (try (error-prone-function) (another-error-prone-function) (except [ZeroDivisionError] (print "Division by zero")) (except [[IndexError KeyboardInterrupt]] (print "Index error or Ctrl-C")) (except [e ValueError] (print "ValueError:" (repr e))) (except [e [TabError PermissionError ReferenceError]] (print "Some sort of error:" (repr e))) (else (print "No errors")) (finally (print "All done"))) Exception lists can be in any of several formats: - ``[]`` to catch any subtype of ``Exception``, like Python's ``except:`` - ``[ETYPE]`` to catch only the single type ``ETYPE``, like Python's ``except ETYPE:`` - ``[[ETYPE1 ETYPE2 …]]`` to catch any of the named types, like Python's ``except ETYPE1, ETYPE2, …:`` - ``[VAR ETYPE]`` to catch ``ETYPE`` and bind it to ``VAR``, like Python's ``except ETYPE as VAR:`` - ``[VAR [ETYPE1 ETYPE2 …]]`` to catch any of the named types and bind it to ``VAR``, like Python's ``except ETYPE1, ETYPE2, … as VAR:`` The return value of ``try`` is the last form evaluated among the main body, ``except`` forms, ``except*`` forms, and ``else``. Functions ~~~~~~~~~ .. hy:macro:: (defn [name #* args]) ``defn`` compiles to a :ref:`function definition ` (or possibly to an assignment of a :ref:`lambda expression `). It always returns ``None``. It requires two arguments: a name (given as a symbol; see :hy:func:`fn` for anonymous functions) and a "lambda list", or list of parameters (also given as symbols). Any further arguments constitute the body of the function:: (defn name [params] bodyform1 bodyform2…) An empty body is implicitly ``(return None)``. If there are at least two body forms, and the first of them is a string literal, this string becomes the :term:`py:docstring` of the function. The final body form is implicitly returned; thus, ``(defn f [] 5)`` is equivalent to ``(defn f [] (return 5))``. There is one exception: due to Python limitations, no implicit return is added if the function is an asynchronous generator (i.e., defined with ``(defn :async …)`` or ``(fn :async …)`` and containing at least one :hy:func:`yield`). ``defn`` accepts a few more optional arguments: a literal keyword ``:async`` (to create a coroutine like Python's :keyword:`async def`), a bracketed list of :term:`decorators `, a list of type parameters (see below), and an annotation (see :hy:func:`annotate`) for the return value. These are placed before the function name (in that order, if several are present):: (defn :async [decorator1 decorator2] :tp [T1 T2] #^ annotation name [params] …) ``defn`` lambda lists support all the same features as Python parameter lists and hence are complex in their full generality. The simplest case is a (possibly empty) list of symbols, indicating that all parameters are required, and can be set by position, as in ``(f value)``, or by name, as in ``(f :argument value)``. To set a default value for a parameter, replace the parameter with the bracketed list ``[pname value]``, where ``pname`` is the parameter name as a symbol and ``value`` is an arbitrary form. Beware that, per Python, ``value`` is evaluated when the function is defined, not when it's called, and if the resulting object is mutated, all calls will see the changes. Further special lambda-list syntax includes: ``/`` If the symbol ``/`` is given in place of a parameter, it means that all the preceding parameters can only be set positionally. ``*`` If the symbol ``*`` is given in place of a parameter, it means that all the following parameters can only be set by name. ``#* args`` If the parameter list contains ``#* args`` or ``(unpack-iterable args)``, then ``args`` is set to a tuple containing all otherwise unmatched positional arguments. The name ``args`` is merely cherished Python tradition; you can use any symbol. ``#** kwargs`` ``#** kwargs`` (a.k.a. ``(unpack-mapping kwargs)``) is like ``#* args``, but collects unmatched keyword arguments into a dictionary. Each of these special constructs is allowed only once, and has the same restrictions as in Python; e.g., ``#* args`` must precede ``#** kwargs`` if both are present. Here's an example with a complex lambda list:: (defn f [a / b [c 3] * d e #** kwargs] [a b c d e kwargs]) (print (hy.repr (f 1 2 :d 4 :e 5 :f 6))) ; => [1 2 3 4 5 {"f" 6}] Type parameters require Python 3.12, and have the semantics specified by :pep:`695`. The keyword ``:tp`` introduces the list of type parameters. Each item of the list is a symbol, an annotated symbol (such as ``#^ int T``), or an unpacked symbol (such as ``#* T`` or ``#** T``). As in Python, unpacking and annotation can't be used with the same parameter. .. hy:macro:: (fn [args]) As :hy:func:`defn`, but no name for the new function is required (or allowed), and the newly created function object is returned. Decorators and type parameters aren't allowed, either. However, the function body is understood identically to that of :hy:func:`defn`, without any of the restrictions of Python's :py:keyword:`lambda`. ``:async`` is also allowed. .. hy:macro:: (return [object]) ``return`` compiles to a :py:keyword:`return` statement. It exits the current function, returning its argument if provided with one, or ``None`` if not. :: (defn f [x] (for [n (range 10)] (when (> n x) (return n)))) (f 3.9) ; => 4 Note that in Hy, ``return`` is necessary much less often than in Python. The last form of a function is returned automatically, so an explicit ``return`` is only necessary to exit a function early. To get Python's behavior of returning ``None`` when execution reaches the end of a function, just put ``None`` there yourself:: (defn f [] (setv d (dict :a 1 :b 2)) (.pop d "b") None) (print (f)) ; Prints "None", not "2" .. hy:macro:: (yield [arg1 arg2]) ``yield`` compiles to a :ref:`yield expression `, which returns a value as a generator. For a plain yield, provide one argument, the value to yield, or omit it to yield ``None``. :: (defn naysayer [] (while True (yield "nope"))) (list (zip "abc" (naysayer))) ; => [#("a" "nope") #("b" "nope") #("c" "nope")] For a yield-from expression, provide two arguments, where the first is the literal keyword ``:from`` and the second is the subgenerator. :: (defn myrange [] (setv r (range 10)) (while True (yield :from r))) (list (zip "abc" (myrange))) ; => [#("a" 0) #("b" 1) #("c" 2)] .. hy:macro:: (await [obj]) ``await`` creates an :ref:`await expression `. It takes exactly one argument: the object to wait for. :: (import asyncio) (defn :async main [] (print "hello") (await (asyncio.sleep 1)) (print "world")) (asyncio.run (main)) Macros ~~~~~~~~~~~~ .. hy:macro:: (defmacro [name lambda-list #* body]) Define a macro, at both compile-time and run-time. The syntax is a subset allowed of that by :hy:func:`defn`: no decorator or return-type annotations are allowed, and the only types of parameter allowed are ``symbol``, ``[symbol default-value]``, ``/``, and ``#* args``. See :ref:`macros` for details and examples. .. hy:automacro:: hy.core.macros.defreader .. hy:automacro:: hy.core.macros.get-macro .. hy:automacro:: hy.core.macros.local-macros Classes ~~~~~~~~~~~~ .. hy:macro:: (defclass [arg1 #* args]) ``defclass`` compiles to a :py:keyword:`class` statement, which creates a new class. It always returns ``None``. Only one argument, specifying the name of the new class as a symbol, is required. A list of :term:`decorators ` (and type parameters, in the same way as for :hy:func:`defn`) may be provided before the class name. After the name comes a list of superclasses (use the empty list ``[]`` for the common case of no superclasses) and any number of body forms, the first of which may be a :term:`py:docstring`. A simple class declaration and its uses might look like this:: (defclass MyClass [] "A simple example class." (setv i 12345) (defn f [self] "hello world")) (setv instance (MyClass)) (print instance.i) ; => 12345 (print (.f instance)) ; => hello world A more complex declaration might look like this:: (defclass [decorator1 decorator2] :tp [T1 T2] MyClass [SuperClass1 SuperClass2] "A class that does things at times." (setv attribute1 value1 attribute2 value2) (defn method1 [self arg1 arg2] …) (defn method2 [self arg1 arg2] …)) Modules ~~~~~~~~~~~~ .. hy:macro:: (import [#* forms]) ``import`` compiles to an :py:keyword:`import` statement, which makes objects in a different module available in the current module. It always returns ``None``. Hy's syntax for the various kinds of import looks like this:: ;; Import each of these modules. ;; Python: import sys, os.path (import sys os.path) ;; Import several names from a single module. ;; Python: from os.path import exists, isdir as is_dir, isfile (import os.path [exists isdir :as dir? isfile]) ;; Import a module with an alias for the whole module. ;; Python: import sys as systest (import sys :as systest) ;; Import all objects from a module into the current namespace. ;; Python: from sys import * (import sys *) ;; You can list as many imports as you like of different types. ;; Python: ;; from tests.resources import kwtest, function_with_a_dash ;; from os.path import exists, isdir as is_dir, isfile as is_file ;; import sys as systest ;; from math import * (import tests.resources [kwtest function-with-a-dash] os.path [exists isdir :as dir? isfile :as file?] sys :as systest math *) ``__all__`` can be set to control what's imported by ``(import module-name *)``, as in Python, but beware that all names in ``__all__`` must be :ref:`mangled `. The macro :hy:func:`export ` is a handy way to set ``__all__`` in a Hy program. .. hy:macro:: (require [#* args]) ``require`` is a version of :hy:func:`import` for macros. It allows all the same syntax as :hy:func:`import`, and brings the requested macros into the current scope at compile-time as well as run-time. The following are all equivalent ways to call a macro named ``foo`` in the module ``mymodule``:: (require mymodule) (mymodule.foo 1) (require mymodule :as M) (M.foo 1) (require mymodule [foo]) (foo 1) (require mymodule *) (foo 1) (require mymodule [foo :as bar]) (bar 1) There's a bit of a trick involved in syntax such as ``mymodule.foo``. Namely, there is no object named ``mymodule``. Instead, ``(require mymodule)`` assigns every macro ``foo`` in ``mymodule`` to the name ``(hy.mangle "mymodule.foo")`` in ``_hy_macros``. Reader macros have a different namespace from regular macros, so they need to be specified with the added syntax ``:readers […]``. You could require a reader macro named ``spiff`` with the call ``(require mymodule :readers [spiff])``, or star-require reader macros with ``(require mymodule :readers *)``. For legibility, a regular-macros specification may analogously be prefixed ``:macros``:: (require mymodule :macros [foo] :readers [spiff]) ``require`` with reader macros is more limited than with regular macros. You can't access reader macros with dotted names, and you can't rename them with ``:as``. Note that ``(require mymodule :readers [spiff])`` doesn't imply ``(require mymodule)``; that is, ``mymodule.foo`` won't be made available. If you want that, use something like :: (require mymodule mymodule :readers [spiff]) To define which macros are collected by ``(require mymodule *)``, set the variable ``_hy_export_macros`` (analogous to Python's ``__all__``) to a list of :ref:`mangled ` macro names, which is accomplished most conveniently with :hy:func:`export `. The default behavior is analogous to ``(import mymodule *)``: all macros are collected other than those whose mangled names begin with an underscore (``_``), .. hy:automacro:: hy.core.macros.export Miscellany ~~~~~~~~~~~~ .. hy:macro:: (chainc [#* args]) ``chainc`` creates a :ref:`comparison expression `. It isn't required for unchained comparisons, which have only one comparison operator, nor for chains of the same operator. For those cases, you can use the comparison operators directly with Hy's usual prefix syntax, as in ``(= x 1)`` or ``(< 1 2 3)``. The use of ``chainc`` is to construct chains of heterogeneous operators, such as ``x <= y < z``. It uses an infix syntax with the general form :: (chainc ARG OP ARG OP ARG…) Hence, ``(chainc x <= y < z)`` is equivalent to ``(and (<= x y) (< y z))``, including short-circuiting, except that ``y`` is only evaluated once. Each ``ARG`` is an arbitrary form, which does not itself use infix syntax. Use :hy:func:`py ` if you want fully Python-style operator syntax. You can also nest ``chainc`` forms, although this is rarely useful. Each ``OP`` is a literal comparison operator; other forms that resolve to a comparison operator are not allowed. At least two ``ARG``\ s and one ``OP`` are required, and every ``OP`` must be followed by an ``ARG``. As elsewhere in Hy, the equality operator is spelled ``=``, not ``==`` as in Python. .. hy:macro:: (assert [condition [label None]]) ``assert`` compiles to an :py:keyword:`assert` statement, which checks whether a condition is true. The first argument, specifying the condition to check, is mandatory, whereas the second, which will be passed to :py:class:`AssertionError`, is optional. The whole form is only evaluated when :py:data:`__debug__` is true, and the second argument is only evaluated when :py:data:`__debug__` is true and the condition fails. ``assert`` always returns ``None``. :: (assert (= 1 2) "one should equal two") ; AssertionError: one should equal two Placeholder macros ~~~~~~~~~~~~~~~~~~ There are a few core macros that are unusual in that all they do, when expanded, is crash, regardless of their arguments: - ``else`` - ``except`` - ``except*`` - ``finally`` - ``unpack-mapping`` - ``unquote`` - ``unquote-splice`` The purpose of these macros is merely to reserve their names. Each symbol is interpreted specially by one or more other core macros (e.g., ``else`` in ``while``) and thus, in these contexts, any definition of these names as a function or macro would be ignored. If you really want to, you can override these names like any others, but beware that, for example, trying to call your new ``else`` inside ``while`` may not work. Hy --- A few core functions, mostly related to the manipulation of Hy code, are available through the module ``hy``. .. hy:autofunction:: hy.read .. hy:autofunction:: hy.read-many .. hy:autofunction:: hy.eval .. hy:autofunction:: hy.repr .. hy:autofunction:: hy.repr-register .. hy:autofunction:: hy.mangle .. hy:autofunction:: hy.unmangle .. hy:autofunction:: hy.macroexpand .. hy:autofunction:: hy.macroexpand-1 .. hy:autofunction:: hy.gensym .. hy:autofunction:: hy.as-model .. hy:autoclass:: hy.I .. hy:class:: (hy.R) There is no actual object named ``hy.R``. Rather, this syntax is :ref:`recognized specially by the compiler ` as a shorthand for requiring and calling a macro. Readers ------- Hy's reader (i.e., parser) classes are most interesting to the user in the context of :ref:`reader macros `. .. autoclass:: hy.HyReader :members: parse, parse_one_form, parse_forms_until, read_default, fill_pos .. autoclass:: hy.Reader :members: .. autoexception:: hy.PrematureEndOfInput .. _pyop: Python operators ---------------- .. hy:automodule:: hy.pyops :members: hy-1.0.0/docs/cli.rst000066400000000000000000000047121467401276000143740ustar00rootroot00000000000000====================== Command-line interface ====================== Hy provides a handful of command-line programs for working with Hy code. .. contents:: Contents :local: .. _hy-cli: hy -- ``hy`` is a command-line interface for Hy that in general imitates the program ``python`` provided by CPython. For example, ``hy`` without arguments launches the :ref:`REPL ` if standard input is a TTY and runs the standard input as a script otherwise, whereas ``hy foo.hy a b`` runs the Hy program ``foo.hy`` with ``a`` and ``b`` as command-line arguments. See ``hy --help`` for a complete list of options and :py:ref:`Python's documentation ` for many details. Here are some Hy-specific details: .. cmdoption:: -m Much like Python's ``-m``, but the input module name will be :ref:`mangled `. .. cmdoption:: --spy Print equivalent Python code before executing each piece of Hy code in the REPL:: => (+ 1 2) 1 + 2 ------------------------------ 3 .. cmdoption:: --repl-output-fn Set the :ref:`REPL output function `. This can be the name of a Python builtin, most likely ``repr``, or a dotted name like ``foo.bar.baz``. In the latter case, Hy will attempt to import the named object with code like ``(import foo.bar [baz])``. .. _hy2py: hy2py ----- ``hy2py`` is a program to convert Hy source code into Python source code. Use ``hy2py --help`` for usage instructions. It can take its input from standard input, or from a file or module name provided as a command-line argument. In the case of a module name, the current working directory should be the parent directory of that module, and the output parameter (``--output/-o``) is required. When the output parameter is provided, the output will be written into the given folder or file. Otherwise, the result is written to standard output. .. warning:: ``hy2py`` can execute arbitrary code (via macros, :hy:func:`eval-when-compile`, etc.). Don't give it untrusted input. .. _hyc: hyc --- ``hyc`` is a program to compile files of Hy code into Python bytecode. Use ``hyc --help`` for usage instructions. The generated bytecode files are named and placed according to the usual scheme of your Python executable, as indicated by :py:func:`importlib.util.cache_from_source`. .. warning:: ``hyc`` can execute arbitrary code (via macros, :hy:func:`eval-when-compile`, etc.). Don't give it untrusted input. hy-1.0.0/docs/conf.py000066400000000000000000000027421467401276000143730ustar00rootroot00000000000000import os, re, sys, time, html sys.path.insert(0, os.path.abspath('..')) import hy; hy.I = type(hy.I) # A trick to enable `hy:autoclass:: hy.I` extensions = [ 'sphinx.ext.napoleon', 'sphinx.ext.intersphinx', 'sphinx.ext.autodoc', 'sphinxcontrib.hydomain'] import warnings; import sphinx.deprecation as SD for c in (SD.RemovedInSphinx60Warning, SD.RemovedInSphinx70Warning): warnings.filterwarnings('ignore', category = c) project = 'Hy' copyright = '%s the authors' % time.strftime('%Y') version = '.'.join(hy.__version__.split('.')[:-1]) # The short dotted version identifier release = hy.__version__ + ('' if hy.nickname is None else f' ({hy.nickname})') # The full version identifier, including alpha, beta, and RC tags html_title = f'Hy {release} manual' # Ultimately this will only appear on the page itself. The actual HTML title # will be simplified in post-processing. hyrule_version = 'v0.7.0' source_suffix = '.rst' master_doc = 'index' exclude_patterns = ['_build', 'coreteam.rst'] html_theme = 'nature' html_theme_options = dict( nosidebar = True, body_min_width = 0, body_max_width = 'none') html_css_files = ['custom.css'] html_static_path = ['_static'] html_copy_source = False html_show_sphinx = False add_module_names = True smartquotes = False nitpicky = True highlight_language = 'hylang' intersphinx_mapping = dict( py = ('https://docs.python.org/3/', None), hyrule = (f'http://hylang.org/hyrule/doc/{hyrule_version}', None)) hy-1.0.0/docs/coreteam.rst000066400000000000000000000010611467401276000154160ustar00rootroot00000000000000* `Peter Andreev `_ * `Kodi B. Arfer `_ * `Allie Jo Casey `_ * `Sunjay Cauligi `_ * `Julien Danjou `_ * `Simon Gomizelj `_ * `Ryan Gonzalez `_ * `Morten Linderud `_ * `Matthew Odendahl `_ * `Paul Tagliamonte `_ * `Brandon T. Willard `_ hy-1.0.0/docs/env_var.rst000066400000000000000000000016171467401276000152660ustar00rootroot00000000000000===================== Environment variables ===================== Hy treats the following environment variables specially. Boolean environment variables are interpreted as false when set to the empty string and true when set to anything else. .. envvar:: HYSTARTUP (Default: nothing) Path to a file containing Hy source code to execute when starting the REPL. .. envvar:: HY_SHOW_INTERNAL_ERRORS (Default: false) Whether to show some parts of tracebacks that point to internal Hy code and won't be helpful to the typical Hy user. .. envvar:: HY_HISTORY (Default: ``~/.hy-history``) Path to which REPL input history will be saved. .. envvar:: HY_MESSAGE_WHEN_COMPILING (Default: false) Whether to print "Compiling FILENAME" to standard error before compiling each file of Hy source code. This is helpful for debugging whether files are being loaded from bytecode or re-compiled. hy-1.0.0/docs/hacking.rst000066400000000000000000000003601467401276000152240ustar00rootroot00000000000000.. _hacking: =============== Developing Hy =============== .. contents:: Contents :local: .. include:: ../CONTRIBUTING.rst Core team ========= Hy's core development team consists of the following people: .. include:: coreteam.rst hy-1.0.0/docs/index.rst000066400000000000000000000017361467401276000147370ustar00rootroot00000000000000Contents ======== .. image:: _static/hy-logo-small.png :alt: Hy :align: left :PyPI: https://pypi.python.org/pypi/hy :Source: https://github.com/hylang/hy :Discussions: https://github.com/hylang/hy/discussions :Stack Overflow: `The [hy] tag `_ Hy is a Lisp dialect that's embedded in Python. Since Hy transforms its Lisp code into Python abstract syntax tree (AST) objects, you have the whole beautiful world of Python at your fingertips, in Lisp form. .. Changes to the below paragraph should be mirrored on Hy's homepage and the README. To install the latest release of Hy, just use the command ``pip3 install --user hy``. Then you can start an interactive read-eval-print loop (REPL) with the command ``hy``, or run a Hy program with ``hy myprogram.hy``. .. toctree:: :maxdepth: 3 whyhy versioning tutorial syntax semantics macros model_patterns repl env_var cli interop api hacking hy-1.0.0/docs/interop.rst000066400000000000000000000122211467401276000152770ustar00rootroot00000000000000.. _interop: ======================= Python interoperability ======================= This chapter describes how to interact with Python code from Hy code and vice versa. .. contents:: Contents :local: Mangling ======== :ref:`Mangling ` allows variable names to be spelled differently in Hy and Python. For example, Python's ``str.format_map`` can be written ``str.format-map`` in Hy, and a Hy function named ``valid?`` would be called ``hyx_valid_Xquestion_markX`` in Python. You can call :hy:func:`hy.mangle` and :hy:func:`hy.unmangle` from either language. Keyword mincing --------------- Another kind of mangling may be necessary in Python to refer to variables with the same name as reserved words. For example, while ``(setv break 13)`` is legal Hy, ``import hy, my_hy_module; print(my_hy_module.break)`` is syntactically invalid Python. String literals work, as in ``getattr(my_hy_module, "break")``, but to use what is syntactically a Python identifier, you'll have to take advantage of Python's Unicode normalization (via NFKC) and write something like ``my_hy_module.𝐛reak``. Here are all the MATHEMATICAL BOLD SMALL letters (U+1D41A through U+1D433) for convenient copying: .. code-block:: text 𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳 Libraries that expect Python ============================ There are various means by which Hy may interact poorly with a Python library because the library doesn't account for the possibility of Hy. For example, when you run the program :ref:`hy-cli`, ``sys.executable`` will be set to this program rather than the original Python binary. This is helpful more often than not, but will lead to trouble if e.g. the library tries to call :py:data:`sys.executable` with the ``-c`` option. In this case, you can try setting :py:data:`sys.executable` back to ``hy.sys-executable``, which is a saved copy of the original value. More generally, you can use :ref:`hy2py`, or you can put a simple Python wrapper script like ``import hy, my_hy_program`` in front of your code. See `the wiki `_ for tips on using specific packages. Packaging a Hy library ====================== Generally, the same infrastructure used for Python packages, such as ``setup.py`` files and the `Python Package Index (PyPI) `__, is applicable to Hy. Don't write the setup file itself in Hy, since you'll be declaring your package's dependence on Hy there, likely in the ``install_requires`` argument of ``setup``. Similarly, at the top level of the package, use ``__init__.py`` rather than ``__init__.hy``, and begin it with ``import hy`` to set up the import hooks for Hy. You can still import a Hy file from there in order to write the real logic in Hy. If you want allow users to import or require from the top level of your module, as in ``from my_module import my_function`` or ``(require my-module [my-macro])``, use an ``__init__.py`` such as .. code-block:: python import hy from my_module.hy_init import * hy.eval(hy.read('(require my-module.hy-init :macros * :readers *)')) If you want to compile your Hy code into Python bytecode at installation-time (in case e.g. the code is being installed to a directory where the bytecode can't be automatically written later, due to permissions issues), see Hy's own ``setup.py`` for an example. For PyPI packages, use the trove classifier ``Programming Language :: Hy`` for libraries meant to be useful for Hy specifically (e.g., a library that provides Hy macros) or other projects that are about Hy somehow (e.g., an instructive example Hy program). Don't use it for a package that just happens to be written in Hy. Using Python from Hy ==================== To use a Python module from Hy, just :hy:func:`import` it. In most cases, no additional ceremony is required. You can embed Python code directly into a Hy program with the macros :hy:func:`py ` and :hy:func:`pys `, and you can use standard Python tools like :func:`eval` or :func:`exec` to execute or manipulate Python code in strings. .. _using-hy-from-python: Using Hy from Python ==================== To use a Hy module from Python, you can just :py:keyword:`import` it, provided that ``hy`` has already been imported first, whether in the current module or in some earlier module executed by the current Python process. As mentioned previously, you can put ``import hy`` in a package's ``__init__.py`` to make this happen automatically. You can use :ref:`hy2py` to convert a Hy program to Python. The output will still import ``hy``, and thus require Hy to be installed in order to run; see :ref:`implicit-names` for details and workarounds. To execute Hy code from a string, use :hy:func:`hy.read-many` to convert it to :ref:`models ` and :hy:func:`hy.eval` to evaluate it: .. code-block:: python >>> hy.eval(hy.read_many("(setv x 1) (+ x 1)")) 2 There is no Hy equivalent of :func:`exec` because :hy:func:`hy.eval` works even when the input isn't equivalent to a single Python expression. You can use :meth:`hy.REPL.run` to launch the Hy REPL from Python, as in ``hy.REPL(locals = {**globals(), **locals()}).run()``. hy-1.0.0/docs/macros.rst000066400000000000000000000416611467401276000151150ustar00rootroot00000000000000.. _macros: ====== Macros ====== Macros, and the metaprogramming they enable, are one of the characteristic features of Lisp, and one of the main advantages Hy offers over vanilla Python. Much of the material covered in this chapter will be familiar to veterans of other Lisps, but there are also a lot of Hyly specific details. .. contents:: Contents :local: What are macros for? -------------------- The gist of `metaprogramming `_ is that it allows you to program the programming language itself (hence the word). You can create new control structures, like :ref:`do-while `, or other kinds of new syntax, like a concise literal notation for your favorite data structure. You can also modify how existing syntax is understood within a region of code, as by making identifiers that start with a capital letter implicitly imported from a certain module. Finally, metaprogramming can improve performance in some cases by effectively inlining functions, or by computing something once at compile-time rather than several times at run-time. With a Lisp-like macro system, you can metaprogram in a slicker and less error-prone way than generating code as text with conventional string formatting, or with lexer-level macros like those provided by the C preprocessor. Types of macros --------------- Hy offers two types of macros: regular macros and reader macros. **Regular macros**, typically defined with :hy:func:`defmacro`, are the kind Lispers usually mean when they talk about "macros". Regular macros are called like a function, with an :ref:`expression ` whose head is the macro name: for example, ``(foo a b)`` could call a macro named ``foo``. A regular macro is called at compile-time, after the entire top-level form in which it appears is parsed, and receives parsed :ref:`models ` as arguments. Regular macros come in :ref:`three varieties, which vary in scope `. **Reader macros**, typically defined with :hy:func:`defreader `, are lower-level than regular macros. They're called with the hash sign ``#``; for example, ``#foo`` calls a reader macro named ``foo``. A reader macro is called at parse-time. It doesn't receive conventional arguments. Instead, it uses an implicitly available parser object to parse the subsequent source text. When it returns, the standard Hy parser picks up where it left off. Related constructs ~~~~~~~~~~~~~~~~~~ There are three other constructs that perform compile-time processing much like macros, and hence are worth mentioning here. - :hy:func:`do-mac` is essentially shorthand for defining and then immediately calling a regular macro with no arguments. - :hy:func:`eval-when-compile` evaluates some code at compile-time, but contributes no code to the final program, like a macro that returns ``None`` in a context where the ``None`` doesn't do anything. - :hy:func:`eval-and-compile` evaluates some code at compile-time, like :hy:func:`eval-when-compile`, but also leaves the same code to be re-evaluated at run-time. When to use what ~~~~~~~~~~~~~~~~ The variety of options can be intimidating. In addition to all of Hy's features listed above, Python is a dynamic programming language that allows you to do a lot of things at run-time that other languages would blanch at. For example, you can dynamically define a new class by calling :class:`type`. So, watch out for cases where your first thought is to use a macro, but you don't actually need one. When deciding what to use, a good rule of thumb is to use the least powerful option that suffices for the syntax, semantics, and performance that you want. So first, see if Python's dynamic features are enough. If they aren't, try a macro-like construct or a regular macro. If even those aren't enough, try a reader macro. Using the least powerful applicable option will help you avoid the :ref:`macro pitfalls described below `, as well as other headaches such as wanting to use a macro where a Python API needs a function. But for the sake of providing simpler examples, much of the below discussion will ignore this advice and consider example macros that could easily be written as functions. The basics ---------- A regular macro can be defined with :hy:func:`defmacro` using a syntax similar to that of :hy:func:`defn`. Here's how you could define and call a trivial macro that takes no arguments and returns a constant:: (defmacro seventeen [] 17) (print (seventeen)) To see that ``seventeen`` is expanded at compile-time, run ``hy2py`` on this script and notice that it ends with ``print(17)`` rather than ``print(seventeen())``. If you insert a ``print`` call inside the macro definition, you'll also see that the print happens when the file is compiled, but not when it's rerun (so long as an up-to-date bytecode file exists). A more useful macro returns code. You can construct a model the long way, like this:: (defmacro addition [] (hy.models.Expression [ (hy.models.Symbol "+") (hy.models.Integer 1) (hy.models.Integer 1)])) or more concisely with :hy:func:`quote`, like this:: (defmacro addition [] '(+ 1 1)) You don't need to always return a model because the compiler calls :hy:func:`hy.as-model` on everything before trying to compile it. Thus, the ``17`` above works fine in place of ``(hy.models.Integer 17)``. But trying to compile something that ``hy.as-model`` chokes on, like a function object, is an error. Arguments are always passed in as models. You can use quasiquotation (see :hy:func:`quasiquote`) to concisely define a model with partly literal and partly evaluated components:: (defmacro set-to-2 [variable] `(setv ~variable 2)) (set-to-2 foobar) (print foobar) Macros don't understand keyword arguments like functions do. Rather, the :ref:`keyword objects ` themselves are passed in literally. This gives you flexibility in how to handle them. Thus, ``#** kwargs`` and ``*`` aren't allowed in the parameter list of a macro, although ``#* args`` and ``/`` are. See :hy:func:`hyrule.defmacro-kwargs` if you want to handle keyword arguments like a function. On the inside, macros are functions, and obey the usual Python semantics for functions. For example, :hy:func:`setv` inside a macro will define or modify a variable local to the current macro call, and :hy:func:`return` ends macro execution and uses its argument as the expansion. Macros from other modules can be brought into the current scope with :hy:func:`require`. .. _macro-pitfalls: Pitfalls -------- Macros are powerful, but with great power comes great potential for anguish. There are a few characteristic issues you need to guard against to write macros well, and, to a lesser extent, even to use macros well. Name games ~~~~~~~~~~ A lot of these issues are variations on the theme of names not referring to what you intend them to, or in other words, surprise shadowing. For example, the macro below was intended to define a new variable named ``x``, but it ends up modifying a preexisting variable. :: (defmacro upper-twice [arg] `(do (setv x (.upper ~arg)) (+ x x))) (setv x "Okay guys, ") (setv salutation (upper-twice "bye")) (print (+ x salutation)) ; Intended result: "Okay guys, BYEBYE" ; Actual result: "BYEBYEBYE" If you avoid the assignment entirely, by using an argument more than once, you can cause a different problem: surprise multiple evaluation. :: (defmacro upper-twice [arg] `(+ (.upper ~arg) (.upper ~arg))) (setv items ["a" "b" "c"]) (print (upper-twice (.pop items))) ; Intended result: "CC" ; Actual result: "CB" A better approach is to use :hy:func:`hy.gensym` to choose your variable name:: (defmacro upper-twice [arg] (setv g (hy.gensym)) `(do (setv ~g (.upper ~arg)) (+ ~g ~g))) Hyrule provides some macros that make using gensyms more convenient, like :hy:func:`defmacro! ` and :hy:func:`with-gensyms `. On the other hand, you can write a macro that advertises a specific name (or set of names) as part of its interface. For example, Hyrule's `anaphoric macro `_ :hy:func:`ap-if ` assigns the result of a test form to ``it``, and allows the caller to include forms that refer to ``it``:: (import os) (ap-if (.get os.environ "PYTHONPATH") (print "Your PYTHONPATH is" it)) Macro subroutines ~~~~~~~~~~~~~~~~~ A case where you could want something to be in the scope of a macro's expansion, and then it turns out not to be, is when you want to call a function or another macro in the expansion:: (defmacro hypotenuse [a b] (import math) `(math.sqrt (+ (** ~a 2) (** ~b 2)))) (print (hypotenuse 3 4)) ; NameError: name 'math' is not defined The form ``(import math)`` here appears in the wrong context, in the macro call itself rather than the expansion. You could use ``import`` or ``require`` to bind the module name or one of its members to a gensym, but an often more convenient option is to use the one-shot import syntax :hy:class:`hy.I` or the one-shot require syntax :ref:`hy.R `:: (defmacro hypotenuse [a b] `(hy.I.math.sqrt (+ (** ~a 2) (** ~b 2)))) (print (hypotenuse 3 4)) A related but distinct issue is when you want to use a function (or other ordinary Python object) in a macro's code, but it isn't available soon enough:: (defn subroutine [x] (hy.models.Symbol (.upper x))) (defmacro uppercase-symbol [x] (subroutine x)) (setv (uppercase-symbol foo) 1) ; NameError: name 'subroutine' is not defined Here, ``subroutine`` is only defined at run-time, so ``uppercase-symbol`` can't see it when it's expanding (unless you happen to be calling ``uppercase-symbol`` from a different module). This is easily worked around by wrapping ``(defn subroutine …)`` in :hy:func:`eval-and-compile` (or :hy:func:`eval-when-compile` if you want ``subroutine`` to be invisible at run-time). By the way, despite the need for ``eval-and-compile``, extracting a lot of complex logic out of a macro into a function is often a good idea. Functions are typically easier to debug and to make use of in other macros. The important take-home big fat WARNING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A typical macro should use only four kinds of names in its expansion: gensyms, core macros, objects that Python puts in scope by default (like its built-in functions), and ``hy`` and its attributes. It's possible to rebind nearly all these names, so surprise shadowing is still theoretically possible. Unfortunately, the only way to prevent these pathological rebindings from coming about is… don't do that. Don't make a new macro named ``setv`` or name a function argument ``type`` unless you're ready for every macro you call to break, the same way you wouldn't monkey-patch a built-in Python module without thinking carefully. This kind of thing is the responsibility of the macro caller; the macro writer can't do much to defend against it. There is at least a pragma :ref:`warn-on-core-shadow `, enabled by default, that causes ``defmacro`` and ``require`` to warn you if you give your new macro the same name as a core macro. .. _reader-macros: Reader macros ------------- Reader macros allow you to hook directly into Hy's parser to customize how text is parsed into models. They're defined with :hy:func:`defreader `, or, like regular macros, brought in from other modules with :hy:func:`require`. Rather than receiving function arguments, a reader macro has access to a :py:class:`hy.HyReader` object named ``&reader``, which provides all the text-parsing logic that Hy uses to parse itself (see :py:class:`hy.HyReader` and its base class :py:class:`hy.Reader` for the available methods). A reader macro is called with the hash sign ``#``, and like a regular macro, it should return a model or something convertible to a model. The simplest kind of reader macro doesn't read anything:: (defreader hi `(print "Hello.")) #hi #hi #hi A less trivial, and more common, usage of reader macros is to call :func:`hy.HyReader.parse_one_form` to get a single form from the following source text. Such a reader macro is like a unary regular macro that's called with ``#`` instead of parentheses. :: (defreader do-twice (setv x (.parse-one-form &reader)) `(do ~x ~x)) #do-twice (print "This line prints twice.") Here's a moderately complex example of a reader macro that couldn't be implemented as a regular macro. It reads in a list of lists in which the inner lists are newline-separated, but newlines are allowed inside elements. :: (defreader matrix (.slurp-space &reader) (setv start (.getc &reader)) (assert (= start "[")) (.slurp-space &reader) (setv out [[]]) (while (not (.peek-and-getc &reader "]")) (cond (any (gfor c " \t" (.peek-and-getc &reader c))) None (.peek-and-getc &reader "\n") (.append out []) True (.append (get out -1) (.parse-one-form &reader)))) (lfor line out :if line line)) (print (hy.repr #matrix [ 1 (+ 1 1) 3 4 ["element" "containing" "a" "newline"] 6 7 8 9])) ; => [[1 2 3] [4 ["element" "containing" "a" "newline"] 6] [7 8 9]] Note that because reader macros are evaluated at parse-time, and top-level forms are completely parsed before any further compile-time execution occurs, you can't use a reader macro in the same top-level form that defines it:: (do (defreader up (.slurp-space &reader) (.upper (.read-one-form &reader))) (print #up "hello?")) ; LexException: reader macro '#up' is not defined Of the potential problems discussed above that apply to regular macros, such as surprise shadowing, most also apply to reader macros. .. _macro-namespaces: Macro namespaces and operations on macros ----------------------------------------- Macros don't share namespaces with ordinary Python objects. That's why something like ``(defmacro m []) (print m)`` fails with a ``NameError``, and how :hy:mod:`hy.pyops` can provide a function named ``+`` without hiding the core macro ``+``. There are three scoped varieties of regular macro. First are **core macros**, which are built into Hy; :ref:`the set of core macros ` is fixed. They're available by default. You can inspect them in the dictionary ``bulitins._hy_macros``, which is attached to Python's usual :py:mod:`builtins` module. The keys are strings giving :ref:`mangled ` names and the values are the function objects implementing the macros. **Global macros** are associated with modules, like Python global variables. They're defined when you call ``defmacro`` or ``require`` in a global scope. You can see them in the global variable ``_hy_macros`` associated with the same module. You can manipulate ``_hy_macros`` to list, add, delete, or get help on macros, but be sure to use :hy:func:`eval-and-compile` or :hy:func:`eval-when-compile` when you need the effect to happen at compile-time, which is often. (Modifying ``bulitins._hy_macros`` is of course a risky proposition.) Here's an example, which also demonstrates the core macro :hy:func:`get-macro `. ``get-macro`` provides syntactic sugar for getting all sorts of macros as objects. :: (defmacro m [] "This is a docstring." `(print "Hello, world.")) (print (in "m" _hy_macros)) ; => True (help (get-macro m)) (m) ; => "Hello, world." (eval-and-compile (del (get _hy_macros "m"))) (m) ; => NameError (eval-and-compile (setv (get _hy_macros (hy.mangle "new-mac")) (fn [] '(print "Goodbye, world.")))) (new-mac) ; => "Goodbye, world." **Local macros** are associated with function, class, or comprehension scopes, like Python local variables. They come about when you call ``defmacro`` or ``require`` in an appropriate scope. You can call :hy:func:`local-macros ` to view local macros, but adding or deleting elements is ineffective. Beware that local macro definitions apply to the results of expanding other macros in the given context, and hence may not be as local as you expect:: (defmacro number [] 1) (defmacro uses-number [] '(number)) (defn f [] (defmacro number [] 2) (uses-number)) (print (uses-number)) ; => 1 (print (f)) ; => 2 (print (uses-number)) ; => 1 For this reason, shadowing a core macro is risky even with a local macro. Finally, ``_hy_reader_macros`` is a per-module dictionary like ``_hy_macros`` for reader macros, but here, the keys aren't mangled. There are no local reader macros, and there's no official way to introspect on Hy's handful of core reader macros. So, of the three scoped varieties of regular macro, reader macros most resemble global macros. hy-1.0.0/docs/model_patterns.rst000066400000000000000000000067531467401276000166540ustar00rootroot00000000000000============== Model patterns ============== The module ``hy.model-patterns`` provides a library of parser combinators for parsing complex trees of Hy models. Model patterns exist mostly to help implement the compiler, but they can also be useful for writing macros. A motivating example -------------------- The kind of problem that model patterns are suited for is the following. Suppose you want to validate and extract the components of a form like:: (setv form '(try (foo1) (foo2) (except [EType1] (foo3)) (except [e EType2] (foo4) (foo5)) (except [] (foo6)) (finally (foo7) (foo8)))) You could do this with loops and indexing, but it would take a lot of code and be error-prone. Model patterns concisely express the general form of a model tree to be matched, like what a regular expression does for text. Here's a pattern for a ``try`` form of the above kind:: (import funcparserlib.parser [maybe many] hy.model-patterns *) (setv parser (whole [ (sym "try") (many (notpexpr "except" "else" "finally")) (many (pexpr (sym "except") (| (brackets) (brackets FORM) (brackets SYM FORM)) (many FORM))) (maybe (dolike "else")) (maybe (dolike "finally"))])) You can run the parser with ``(.parse parser form)``. The result is:: #( ['(foo1) '(foo2)] [ '([EType1] [(foo3)]) '([e EType2] [(foo4) (foo5)]) '([] [(foo6)])] None '((foo7) (foo8))) which is conveniently utilized with an assignment such as ``(setv [body except-clauses else-part finally-part] result)``. Notice that ``else-part`` will be set to ``None`` because there is no ``else`` clause in the original form. Usage ----- Model patterns are implemented as funcparserlib_ parser combinators. We won't reproduce funcparserlib's own documentation, but here are some important built-in parsers: - ``(+ ...)`` matches its arguments in sequence. - ``(| ...)`` matches any one of its arguments. - ``(>> parser function)`` matches ``parser``, then feeds the result through ``function`` to change the value that's produced on a successful parse. - ``(skip parser)`` matches ``parser``, but doesn't add it to the produced value. - ``(maybe parser)`` matches ``parser`` if possible. Otherwise, it produces the value ``None``. - ``(some function)`` takes a predicate ``function`` and matches a form if it satisfies the predicate. Some of the more important of Hy's own parsers are: - :data:`FORM ` matches anything. - :data:`SYM ` matches any symbol. - :func:`sym ` matches and discards (per ``skip``) the named symbol or keyword. - :func:`brackets ` matches the arguments in square brackets. - :func:`pexpr ` matches the arguments in parentheses. Here's how you could write a simple macro using model patterns:: (defmacro pairs [#* args] (import funcparserlib.parser [many] hy.model-patterns [whole SYM FORM]) (setv [args] (.parse (whole [(many (+ SYM FORM))]) args)) `[~@(gfor [a1 a2] args #((str a1) a2))]) (print (hy.repr (pairs a 1 b 2 c 3))) ; => [#("a" 1) #("b" 2) #("c" 3)] A failed parse will raise ``funcparserlib.parser.NoParseError``. .. _funcparserlib: https://github.com/vlasovskikh/funcparserlib Reference --------- .. automodule:: hy.model_patterns :members: hy-1.0.0/docs/repl.rst000066400000000000000000000055661467401276000145770ustar00rootroot00000000000000.. _repl: =========== The Hy REPL =========== Hy's `read-eval-print loop `_ (REPL) is implemented in the class :class:`hy.REPL`. The REPL can be started interactively :doc:`from the command line ` or programmatically with the instance method :meth:`hy.REPL.run`. Two :doc:`environment variables ` useful for the REPL are ``HY_HISTORY``, which specifies where the REPL input history is saved, and ``HYSTARTUP``, which specifies :ref:`a file to run when the REPL starts `. Due to Python limitations, a Python :class:`code.InteractiveConsole` launched inside the Hy REPL, or a Hy REPL inside another Hy REPL, may malfunction. .. autoclass:: hy.REPL :members: run .. _repl-output-function: Output functions ---------------- By default, the return value of each REPL input is printed with :hy:func:`hy.repr`. To change this, you can set the REPL output function with e.g. the command-line argument ``--repl-output-fn``. Use :func:`repr` to get Python representations, like Python's own REPL. Regardless of the output function, no output is produced when the value is ``None``, as in Python. .. _repl-specials: Special variables ----------------- The REPL maintains a few special convenience variables. ``*1`` holds the result of the most recent input, like ``_`` in the Python REPL. ``*2`` holds the result of the input before that, and ``*3`` holds the result of the input before that. Finally, ``*e`` holds the most recent uncaught exception. .. _startup-file: Startup files ------------- Any macros or Python objects defined in the REPL startup file will be brought into the REPL's namespace. A few variables are special in the startup file: ``repl-spy`` If true, print equivalent Python code before executing each piece of Hy code. ``repl-output-fn`` The :ref:`output function `, as a unary callable object. ``repl-ps1``, ``repl-ps2`` Strings to use as the prompt strings :data:`sys.ps1` and :data:`sys.ps2` for the Hy REPL. Hy startup files can do a number of other things like set banner messages or change the prompts. The following example shows a number of possibilities:: ;; Wrapping in an `eval-and-compile` ensures these Python packages ;; are available in macros defined in this file as well. (eval-and-compile (import sys os) (sys.path.append "~/")) (import re json pathlib [Path] hy.pyops * hyrule [pp pformat]) (require hyrule [unless]) (setv repl-spy True repl-output-fn pformat ;; Make the REPL prompt `=>` green. repl-ps1 "\x01\x1b[0;32m\x02=> \x01\x1b[0m\x02" ;; Make the REPL prompt `...` red. repl-ps2 "\x01\x1b[0;31m\x02... \x01\x1b[0m\x02") (defn slurp [path] (setv path (Path path)) (when (path.exists) (path.read-text))) (defmacro greet [person] `(print ~person)) hy-1.0.0/docs/semantics.rst000066400000000000000000000105641467401276000156150ustar00rootroot00000000000000============== Semantics ============== This chapter describes features of Hy semantics that differ from Python's and aren't better categorized elsewhere, such as in the chapter :doc:`macros`. Each is a potential trap for the unwary. .. contents:: Contents :local: .. _implicit-names: Implicit names -------------- Every compilation unit (basically, module) implicitly begins with ``(import hy)``. You can see it in the output of ``hy2py``. The purpose of this is to ensure Hy can retrieve any names it needs to compile your code. For example, the code ``(print '(+ 1 1))`` requires constructing a :class:`hy.models.Expression`. Thus you should be wary of assigning to the name ``hy``, even locally, because then the wrong thing can happen if the generated code tries to access ``hy`` expecting to get the module. As a bonus, you can say things like ``(print (hy.repr #(1 2)))`` without explicitly importing ``hy`` first. If you restrict yourself to a subset of Hy, it's possible to write a Hy program, translate it to Python with ``hy2py``, remove the ``import hy``, and get a working Python program that doesn't depend on Hy itself. Unfortunately, Python is too dynamic for the Hy compiler to be able to tell in advance when this will work, which is why the automatic import is unconditional. Hy needs to create temporary variables to accomplish some of its tricks. For example, in order to represent ``(print (with …))`` in Python, the result of the ``with`` form needs to be set to a temporary variable. These names begin with ``_hy_``, so it's wise to avoid this prefix in your own variable names. Such temporary variables are scoped in the same way surrounding ordinary variables are, and they aren't explicitly cleaned up, so theoretically, they can waste memory and lead to :py:meth:`object.__del__` being called later than you expect. When in doubt, check the ``hy2py`` output. .. _order-of-eval: Order of evaluation ------------------- Like many programming languages, but unlike Python, Hy doesn't guarantee in all cases the order in which function arguments are evaluated. More generally, the evaluation order of the child models of a :class:`hy.models.Sequence` is unspecified. For example, ``(f (g) (h))`` might evaluate (part of) ``(h)`` before ``(g)``, particularly if ``f`` is a function whereas ``h`` is a macro that produces Python-level statements. So if you need to be sure that ``g`` is called first, call it before ``f``. When bytecode is regenerated ---------------------------- The first time Hy is asked to execute a file, whether directly or indirectly (as in the case of an import), it will produce a bytecode file (unless :std:envvar:`PYTHONDONTWRITEBYTECODE` is set). Subsequently, if the source file hasn't changed, Hy will load the bytecode instead of recompiling the source. Python also makes bytecode files, but the difference between recompilation and loading bytecode is more consequential in Hy because of how Hy lets you run and generate code at compile-time with things like macros, reader macros, and :hy:func:`eval-and-compile`. You may be surprised by behavior like the following: .. code-block:: sh $ echo '(defmacro m [] 1)' >a.hy $ echo '(require a) (print (a.m))' >b.hy $ hy b.hy 1 $ echo '(defmacro m [] 2)' >a.hy $ hy b.hy 1 Why didn't the second run of ``b.hy`` print ``2``? Because ``b.hy`` was unchanged, so it didn't get recompiled, so its bytecode still had the old expansion of the macro ``m``. Traceback positioning --------------------- When an exception results in a traceback, Python uses line and column numbers associated with AST nodes to point to the source code associated with the exception: .. code-block:: text Traceback (most recent call last): File "cinco.py", line 4, in find() File "cinco.py", line 2, in find print(chippy) ^^^^^^ NameError: name 'chippy' is not defined This position information is stored as attributes of the AST nodes. Hy tries to set these attributes appropriately so that it can also produce correctly targeted tracebacks, but there are cases where it can't, such as when evaluating code that was built at runtime out of explicit calls to :ref:`model constructors `. Python still requires line and column numbers, so Hy sets these to 1 as a fallback; consequently, tracebacks can point to the beginning of a file even though the relevant code isn't there. hy-1.0.0/docs/syntax.rst000066400000000000000000000560051467401276000151550ustar00rootroot00000000000000.. _syntax: ============== Syntax ============== This chapter describes how Hy source code is understood at the level of text, as well as the abstract syntax objects that the reader (a.k.a. the parser) turns text into, as when invoked with :hy:func:`hy.read`. The basic units of syntax at the textual level are called **forms**, and the basic objects representing forms are called **models**. Following Python, Hy is in general case-sensitive. For example, ``foo`` and ``FOO`` are different symbols, and the Python-level variables they refer to are also different. .. contents:: Contents :local: .. _models: An introduction to models ------------------------- Reading a Hy program produces a nested structure of model objects. Models can be very similar to the kind of value they represent (such as :class:`Integer `, which is a subclass of :class:`int`) or they can be somewhat different (such as :class:`Set `, which is ordered, unlike actual :class:`set`\s). All models inherit from :class:`Object `, which stores textual position information, so tracebacks can point to the right place in the code. The compiler takes whatever models are left over after parsing and macro-expansion and translates them into Python :mod:`ast` nodes (e.g., :class:`Integer ` becomes :class:`ast.Constant`), which can then be evaluated or rendered as Python code. Macros (that is, regular macros, as opposed to reader macros) operate on the model level, taking some models as arguments and returning more models for compilation or further macro-expansion; they're free to do quite different things with a given model than the compiler does, if it pleases them to, like using an :class:`Integer ` to construct a :class:`Symbol `. In general, a model doesn't count as equal to the value it represents. For example, ``(= (hy.models.String "foo") "foo")`` returns :data:`False`. But you can promote a value to its corresponding model with :hy:func:`hy.as-model`, or you can demote a model with the usual Python constructors like :py:class:`str` or :py:class:`int`, or you can evaluate a model as Hy code with :hy:func:`hy.eval`. Models can be created with the constructors, with the :hy:func:`quote` or :hy:func:`quasiquote` macros, or with :hy:func:`hy.as-model`. Explicit creation is often not necessary, because the compiler will automatically promote (via :hy:func:`hy.as-model`) any object it's trying to evaluate. Note that when you want plain old data structures and don't intend to produce runnable Hy source code, you'll usually be better off using Python's basic data structures (:class:`tuple`, :class:`list`, :class:`dict`, etc.) than models. Yes, "homoiconicity" is a fun word, but a Hy :class:`List ` won't provide any advantage over a Python :class:`list` when you're managing a list of email addresses or something. The default representation of models (via :hy:func:`hy.repr`) uses quoting for readability, so ``(hy.models.Integer 5)`` is represented as ``'5``. Python representations (via :func:`repr`) use the constructors, and by default are pretty-printed; you can disable this globally by setting ``hy.models.PRETTY`` to ``False``, or temporarily with the context manager ``hy.models.pretty``. .. _hyobject: .. autoclass:: hy.models.Object .. autoclass:: hy.models.Lazy Non-form syntactic elements --------------------------- .. _shebang: Shebang ~~~~~~~ If a Hy program begins with ``#!``, Hy assumes the first line is a `shebang line `_ and ignores it. It's up to your OS to do something more interesting with it. Shebangs aren't real Hy syntax, so :hy:func:`hy.read-many` only allows them if its option ``skip_shebang`` is enabled. Whitespace ~~~~~~~~~~ Hy has lax whitespace rules less similar to Python's than to those of most other programming languages. Whitespace can separate forms (e.g., ``a b`` is two forms whereas ``ab`` is one) and it can occur inside some forms (like string literals), but it's otherwise ignored by the reader, producing no models. The reader only grants this special treatment to the ASCII whitespace characters, namely U+0009 (horizontal tab), U+000A (line feed), U+000B (vertical tab), U+000C (form feed), U+000D (carriage return), and U+0020 (space). Non-ASCII whitespace characters, such as U+2009 (THIN SPACE), are treated as any other character. So yes, you can have exotic whitespace characters in variable names, although this is only especially useful for obfuscated code contests. Comments ~~~~~~~~ Comments begin with a semicolon (``;``) and continue through the end of the line. There are no multi-line comments in the style of C's ``/* … */``, but you can use the :ref:`discard prefix ` or :ref:`string literals ` for similar purposes. .. _discard-prefix: Discard prefix ~~~~~~~~~~~~~~ Like Clojure, Hy supports the Extensible Data Notation discard prefix ``#_``, which is kind of like a structure-aware comment. When the reader encounters ``#_``, it reads and then discards the following form. Thus ``#_`` is like ``;`` except that reader macros still get executed, and normal parsing resumes after the next form ends rather than at the start of the next line: ``[dilly #_ and krunk]`` is equivalent to ``[dilly krunk]``, whereas ``[dilly ; and krunk]`` is equivalent to just ``[dilly``. Comments indicated by ``;`` can be nested within forms discarded by ``#_``, but ``#_`` has no special meaning within a comment indicated by ``;``. Identifiers ----------- Identifiers are a broad class of syntax in Hy, comprising not only variable names, but any nonempty sequence of characters that aren't ASCII whitespace nor one of the following: ``()[]{};"'`~``. The reader will attempt to read an identifier as each of the following types, in the given order: 1. a :ref:`numeric literal ` 2. a :ref:`keyword ` 3. a :ref:`dotted identifier ` 4. a :ref:`symbol ` .. _numeric-literals: Numeric literals ~~~~~~~~~~~~~~~~ All of :ref:`Python's syntax for numeric literals ` is supported in Hy, resulting in an :class:`Integer `, :class:`Float `, or :class:`Complex `. Hy also provides a few extensions: - Commas (``,``) can be used like underscores (``_``) to separate digits without changing the result. Thus, ``10_000_000_000`` may also be written ``10,000,000,000``. Hy is also more permissive about the placement of separators than Python: several may be in a row, and they may be after all digits, after ``.``, ``e``, or ``j``, or even inside a radix prefix. Separators before the first digit are still forbidden because e.g. ``_1`` is a legal Python variable name, so it's a symbol in Hy rather than an integer. - Integers can begin with leading zeroes, even without a radix prefix like ``0x``. Leading zeroes don't automatically cause the literal to be interpreted in octal like they do in C. For octal, use the prefix ``0o``, as in Python. - ``NaN``, ``Inf``, and ``-Inf`` are understood as literals. Each produces a :class:`Float `. These are case-sensitive, unlike other uses of letters in numeric literals (``1E2``, ``0XFF``, ``5J``, etc.). - Hy allows complex literals as understood by the constructor for :class:`complex`, such as ``5+4j``. (This is also legal Python, but Hy reads it as a single :class:`Complex `, and doesn't otherwise support infix addition or subtraction, whereas Python parses it as an addition expression.) .. autoclass:: hy.models.Integer .. autoclass:: hy.models.Float .. autoclass:: hy.models.Complex .. _keywords: Keywords ~~~~~~~~ An identifier starting with a colon (``:``), such as ``:foo``, is a :class:`Keyword `. Literal keywords are most often used for their special treatment in :ref:`expressions ` that aren't macro calls: they set :std:term:`keyword arguments `, rather than being passed in as values. For example, ``(f :foo 3)`` calls the function ``f`` with the parameter ``foo`` set to ``3``. The keyword is also :ref:`mangled ` at compile-time. To prevent a literal keyword from being treated specially in an expression, you can :hy:func:`quote` the keyword, or you can use it as the value for another keyword argument, as in ``(f :foo :bar)``. Otherwise, keywords are simple model objects that evaluate to themselves. Users of other Lisps should note that it's often a better idea to use a string than a keyword, because the rest of Python uses strings for cases in which other Lisps would use keywords. In particular, strings are typically more appropriate than keywords as the keys of a dictionary. Notice that ``(dict :a 1 :b 2)`` is equivalent to ``{"a" 1 "b" 2}``, which is different from ``{:a 1 :b 2}`` (see :ref:`dict-literals`). The empty keyword ``:`` is syntactically legal, but you can't compile a function call with an empty keyword argument because of Python limitations. Thus ``(foo : 3)`` must be rewritten to use runtime unpacking, as in ``(foo #** {"" 3})``. .. autoclass:: hy.models.Keyword :members: __bool__, __lt__, __call__ .. _dotted-identifiers: Dotted identifiers ~~~~~~~~~~~~~~~~~~ Dotted identifiers are named for their use of the dot character ``.``, also known as a period or full stop. They don't have their own model type because they're actually syntactic sugar for :ref:`expressions `. Syntax like ``foo.bar.baz`` is equivalent to ``(. foo bar baz)``. The general rule is that a dotted identifier looks like two or more :ref:`symbols ` (themselves not containing any dots) separated by single dots. The result is an expression with the symbol ``.`` as its first element and the constituent symbols as the remaining elements. A dotted identifier may also begin with one or more dots, as in ``.foo.bar`` or ``..foo.bar``, in which case the resulting expression has the appropriate head (``.`` or ``..`` or whatever) and the symbol ``None`` as the following element. Thus, ``..foo.bar`` is equivalent to ``(.. None foo bar)``. In the leading-dot case, you may also use only one constitutent symbol. Thus, ``.foo`` is a legal dotted identifier, and equivalent to ``(. None foo)``. See :ref:`the dot macro ` for what these expressions typically compile to. See also the special behavior for :ref:`expressions ` that begin with a dotted identifier that itself begins with a dot. Note that Hy provides definitions of ``.`` and ``...`` by default, but not ``..``, ``....``, ``.....``, etc., so ``..foo.bar`` won't do anything useful by default outside of macros that treat it specially, like :hy:func:`import`. .. _symbols: Symbols ~~~~~~~ Symbols are the catch-all category of identifiers. In most contexts, symbols are compiled to Python variable names, after being :ref:`mangled `. You can create symbol objects with the :hy:func:`quote` operator or by calling the :class:`Symbol ` constructor (thus, :class:`Symbol ` plays a role similar to the ``intern`` function in other Lisps). Some example symbols are ``hello``, ``+++``, ``3fiddy``, ``$40``, ``just✈wrong``, and ``🦑``. Dots are only allowed in a symbol if every character in the symbol is a dot. Thus, ``a..b`` and ``a.`` are neither dotted identifiers nor symbols; they're syntax errors. As a special case, the symbol ``...`` compiles to the :data:`Ellipsis` object, as in Python. .. autoclass:: hy.models.Symbol .. _mangling: Mangling ~~~~~~~~ Since the rules for Hy symbols and keywords are much more permissive than the rules for Python identifiers, Hy uses a mangling algorithm to convert its own names to Python-legal names. The steps are as follows: #. Remove any leading underscores. Underscores are typically the ASCII underscore ``_``, but they may also be any Unicode character that normalizes (according to NFKC) to ``_``. Leading underscores have special significance in Python, and Python normalizes all Unicode before this test, so we'll process the remainder of the name and then add the leading underscores back onto the final mangled name. #. Convert ASCII hyphens (``-``) to underscores (``_``). Thus, ``foo-bar`` becomes ``foo_bar``. If the name at this step starts with a hyphen, this *first* hyphen is not converted, so that we don't introduce a new leading underscore into the name. Thus ``--has-dashes?`` becomes ``-_has_dashes?`` at this step. #. If the name still isn't Python-legal, make the following changes. A name could be Python-illegal because it contains a character that's never legal in a Python name or it contains a character that's illegal in that position. - Prepend ``hyx_`` to the name. - Replace each illegal character with ``XfooX``, where ``foo`` is the Unicode character name in lowercase, with spaces replaced by underscores and hyphens replaced by ``H``. Replace leading hyphens and ``X`` itself the same way. If the character doesn't have a name, use ``U`` followed by its code point in lowercase hexadecimal. Thus, ``green☘`` becomes ``hyx_greenXshamrockX`` and ``-_has_dashes`` becomes ``hyx_XhyphenHminusX_has_dashes``. #. Take any leading underscores removed in the first step, transliterate them to ASCII, and add them back to the mangled name. Thus, ``__green☘`` becomes ``__hyx_greenXshamrockX``. #. Finally, normalize any leftover non-ASCII characters. The result may still not be ASCII (e.g., ``α`` is already Python-legal and normalized, so it passes through the whole mangling procedure unchanged), but it is now guaranteed that any names are equal as strings if and only if they refer to the same Python identifier. You can invoke the mangler yourself with the function :hy:func:`hy.mangle`, and try to undo this (perhaps not quite successfully) with :hy:func:`hy.unmangle`. Mangling isn't something you should have to think about often, but you may see mangled names in error messages, the output of ``hy2py``, etc. A catch to be aware of is that mangling, as well as the inverse "unmangling" operation offered by :hy:func:`hy.unmangle`, isn't one-to-one. Two different symbols, like ``foo-bar`` and ``foo_bar``, can mangle to the same string and hence compile to the same Python variable. .. _string-literals: .. _hystring: String literals --------------- Hy allows double-quoted strings (e.g., ``"hello"``), but not single-quoted strings like Python. The single-quote character ``'`` is reserved for preventing the evaluation of a form, (e.g., ``'(+ 1 1)``), as in most Lisps (see :ref:`more-sugar`). Python's so-called triple-quoted strings (e.g., ``'''hello'''`` and ``"""hello"""``) aren't supported, either. However, in Hy, unlike Python, any string literal can contain newlines; furthermore, Hy has :ref:`bracket strings `. For consistency with Python's triple-quoted strings, all literal newlines in literal strings are read as in ``"\n"`` (U+000A, line feed) regardless of the newline style in the actual code. String literals support :ref:`a variety of backslash escapes `. Unrecognized escape sequences are a syntax error. To create a "raw string" that interprets all backslashes literally, prefix the string with ``r``, as in ``r"slash\not"``. By default, all string literals are regarded as sequences of Unicode characters. The result is the model type :class:`String `. You may prefix a string literal with ``b`` to treat it as a sequence of bytes, producing :class:`Bytes ` instead. Unlike Python, Hy only recognizes string prefixes (``r``, ``b``, and ``f``) in lowercase, and doesn't allow the no-op prefix ``u``. :ref:`F-strings ` are a string-like compound construct documented further below. .. autoclass:: hy.models.String .. autoclass:: hy.models.Bytes .. _bracket-strings: Bracket strings ~~~~~~~~~~~~~~~ Hy supports an alternative form of string literal called a "bracket string" similar to Lua's long brackets. Bracket strings have customizable delimiters, like the here-documents of other languages. A bracket string begins with ``#[FOO[`` and ends with ``]FOO]``, where ``FOO`` is any string not containing ``[`` or ``]``, including the empty string. (If ``FOO`` is exactly ``f`` or begins with ``f-``, the bracket string is interpreted as an :ref:`f-string `.) For example:: (print #[["That's very kind of yuo [sic]" Tom wrote back.]]) ; "That's very kind of yuo [sic]" Tom wrote back. (print #[==[1 + 1 = 2]==]) ; 1 + 1 = 2 Bracket strings are always raw Unicode strings, and don't allow the ``r`` or ``b`` prefixes. A bracket string can contain newlines, but if it begins with one, the newline is removed, so you can begin the content of a bracket string on the line following the opening delimiter with no effect on the content. Any leading newlines past the first are preserved. .. _hysequence: Sequential forms ---------------- Sequential forms (:class:`Sequence `) are nested forms comprising any number of other forms, in a defined order. .. autoclass:: hy.models.Sequence .. _expressions: Expressions ~~~~~~~~~~~ Expressions (:class:`Expression `) are denoted by parentheses: ``( … )``. The compiler evaluates expressions by checking the first element, called the head. - If the head is a symbol, and the symbol is the name of a currently defined macro, the macro is called. - Exception: if the symbol is also the name of a function in :hy:mod:`hy.pyops`, and one of the arguments is an :hy:func:`unpack-iterable` form, the ``pyops`` function is called instead of the macro. This makes reasonable-looking expressions work that would otherwise fail. For example, ``(+ #* summands)`` is understood as ``(hy.pyops.+ #* summands)``, because Python provides no way to sum a list of unknown length with a real addition expression. - If the head is itself an expression of the form ``(. None …)`` (typically produced with a :ref:`dotted identifier ` like ``.add``), it's used to construct a method call with the element after ``None`` as the object: thus, ``(.add my-set 5)`` is equivalent to ``((. my-set add) 5)``, which becomes ``my_set.add(5)`` in Python. .. _hy.R: - Exception: expressions like ``((. hy R module-name macro-name) …)``, or equivalently ``(hy.R.module-name.macro-name …)``, get special treatment. They :hy:func:`require` the module ``module-name`` and call its macro ``macro-name``, so ``(hy.R.foo.bar 1)`` is equivalent to ``(require foo) (foo.bar 1)``, but without bringing ``foo`` or ``foo.bar`` into scope. Thus ``hy.R`` is convenient syntactic sugar for macros you'll only call once in a file, or for macros that you want to appear in the expansion of other macros without having to call :hy:func:`require` in the expansion. As with :hy:class:`hy.I`, dots in the module name must be replaced with slashes. - Otherwise, the expression is compiled into a Python-level call, with the head being the calling object. (So, you can call a function that has the same name as a macro with an expression like ``((do setv) …)``.) The remaining forms are understood as arguments. Use :hy:func:`unpack-iterable` or :hy:func:`unpack-mapping` to break up data structures into individual arguments at runtime. The empty expression ``()`` is legal at the reader level, but has no inherent meaning. Trying to compile it is an error. For the empty tuple, use ``#()``. .. autoclass:: hy.models.Expression .. _hylist: List, tuple, and set literals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Literal :class:`list`\s (:class:`List `) are denoted by ``[ … ]``. - Literal :class:`tuple`\s (:class:`Tuple `) are denoted by ``#( … )``. - Literal :class:`set`\s (:class:`Set `) are denoted by ``#{ … }``. .. autoclass:: hy.models.List .. autoclass:: hy.models.Tuple .. autoclass:: hy.models.Set .. _dict-literals: Dictionary literals ~~~~~~~~~~~~~~~~~~~ Literal dictionaries (:class:`dict`, :class:`Dict `) are denoted by ``{ … }``. Even-numbered child forms (counting the first as 0) become the keys whereas odd-numbered child forms become the values. For example, ``{"a" 1 "b" 2}`` produces a dictionary mapping ``"a"`` to ``1`` and ``"b"`` to ``2``. Trying to compile a :class:`Dict ` with an odd number of child models is an error. As in Python, calling :class:`dict` with keyword arguments is often more convenient than using a literal dictionary. .. autoclass:: hy.models.Dict .. _syntax-fstrings: Format strings ~~~~~~~~~~~~~~ A format string (or "f-string", or "formatted string literal") is a string literal with embedded code, possibly accompanied by formatting commands. The result is an :class:`FString `, Hy f-strings work much like :ref:`Python f-strings ` except that the embedded code is in Hy rather than Python. :: (print f"The sum is {(+ 1 1)}.") ; => The sum is 2. Since ``=``, ``!``, and ``:`` are identifier characters in Hy, Hy decides where the code in a replacement field ends (and any debugging ``=``, conversion specifier, or format specifier begins) by parsing exactly one form. You can use ``do`` to combine several forms into one, as usual. Whitespace may be necessary to terminate the form:: (setv foo "a") (print f"{foo:x<5}") ; => NameError: name 'hyx_fooXcolonXxXlessHthan_signX5' is not defined (print f"{foo :x<5}") ; => axxxx Unlike Python, whitespace is allowed between a conversion and a format specifier. Also unlike Python, comments and backslashes are allowed in replacement fields. The same reader is used for the form to be evaluated as for elsewhere in the language. Thus e.g. ``f"{"a"}"`` is legal, and equivalent to ``"a"``. .. autoclass:: hy.models.FString .. autoclass:: hy.models.FComponent .. _more-sugar: Additional sugar ---------------- Syntactic sugar is available to construct two-item :ref:`expressions ` with certain heads. When the sugary characters are encountered by the reader, a new expression is created with the corresponding macro name as the first element and the next parsed form as the second. No parentheses are required. Thus, since ``'`` is short for ``quote``, ``'FORM`` is read as ``(quote FORM)``. Whitespace is allowed, as in ``' FORM``. This is all resolved at the reader level, so the model that gets produced is the same whether you take your code with sugar or without. ========================== ================ Macro Syntax ========================== ================ :hy:func:`quote` ``'FORM`` :hy:func:`quasiquote` ```FORM`` :hy:func:`unquote` ``~FORM`` :hy:func:`unquote-splice` ``~@FORM`` :hy:func:`unpack-iterable` ``#* FORM`` :hy:func:`unpack-mapping` ``#** FORM`` ========================== ================ Reader macros ------------- A hash (``#``) followed by a :ref:`symbol ` invokes the :ref:`reader macro ` named by the symbol. (Trying to call an undefined reader macro is a syntax error.) Parsing of the remaining source code is under control of the reader macro until it returns. hy-1.0.0/docs/tutorial.rst000066400000000000000000000343161467401276000154730ustar00rootroot00000000000000======== Tutorial ======== .. figure:: _static/cuddles-transparent-small.png :alt: Karen Rustard's Cuddles Hy's mascot, Cuddles the cuttlefish. This chapter provides a quick introduction to Hy. It assumes a basic background in programming, but no specific prior knowledge of Python or Lisp. .. contents:: Contents :local: Lisp-stick on a Python ====================== Let's start with the classic:: (print "Hy, world!") This program calls the :func:`print` function, which, like all of Python's :ref:`built-in functions `, is available in Hy. All of Python's :ref:`binary and unary operators ` are available, too, although ``==`` is spelled ``=`` in deference to Lisp tradition. Here's how we'd use the addition operator ``+``:: (+ 1 3) This code returns ``4``. It's equivalent to ``1 + 3`` in Python and many other languages. Languages in the `Lisp `_ family, including Hy, use a prefix syntax: ``+``, just like ``print`` or ``sqrt``, appears before all of its arguments. The call is delimited by parentheses, but the opening parenthesis appears before the operator being called instead of after it, so instead of ``sqrt(2)``, we write ``(sqrt 2)``. Multiple arguments, such as the two integers in ``(+ 1 3)``, are separated by whitespace. Many operators, including ``+``, allow more than two arguments: ``(+ 1 2 3)`` is equivalent to ``1 + 2 + 3``. Here's a more complex example:: (- (* (+ 1 3 88) 2) 8) This code returns ``176``. Why? You can see the infix equivalent with the command ``echo "(- (* (+ 1 3 88) 2) 8)" | hy2py``, which returns the Python code corresponding to the given Hy code. Or you can pass the ``--spy`` option to Hy when starting the interactive read-eval-print loop (REPL), which shows the Python equivalent of each input line before the result. The infix equivalent in this case is: .. code-block:: python ((1 + 3 + 88) * 2) - 8 To evaluate this infix expression, you'd of course evaluate the innermost parenthesized expression first and work your way outwards. The same goes for Lisp. Here's what we'd get by evaluating the above Hy code one step at a time:: (- (* (+ 1 3 88) 2) 8) (- (* 92 2) 8) (- 184 8) 176 The basic unit of Lisp syntax, which is similar to a C or Python expression, is the **form**. ``92``, ``*``, and ``(* 92 2)`` are all forms. A Lisp program consists of a sequence of forms nested within forms. Forms are typically separated from each other by whitespace, but some forms, such as string literals (``"Hy, world!"``), can contain whitespace themselves. An :ref:`expression ` is a form enclosed in parentheses; its first child form, called the **head**, determines what the expression does, and should generally be a function or macro. :py:term:`Functions `, the most ordinary sort of head, constitute reusable pieces of code that can take in arguments and return a value. Macros (described in more detail :ref:`below `) are a special kind of function that's executed at compile-time and returns code to be executed at run-time. Comments start with a ``;`` character and continue till the end of the line. A comment is functionally equivalent to whitespace. :: (setv password "susan") ; My daughter's name Although ``#`` isn't a comment character in Hy, a Hy program can begin with a :ref:`shebang line `, which Hy itself will ignore:: #!/usr/bin/env hy (print "Make me executable, and run me!") Literals ======== Hy has :ref:`literal syntax ` for all of the same data types that Python does. Here's an example of Hy code for each type and the Python equivalent. ============== ================ ================= Hy Python Type ============== ================ ================= ``1`` ``1`` :class:`int` ``1.2`` ``1.2`` :class:`float` ``4j`` ``4j`` :class:`complex` ``True`` ``True`` :class:`bool` ``None`` ``None`` ``NoneType`` ``"hy"`` ``'hy'`` :class:`str` ``b"hy"`` ``b'hy'`` :class:`bytes` ``#(1 2 3)`` ``(1, 2, 3)`` :class:`tuple` ``[1 2 3]`` ``[1, 2, 3]`` :class:`list` ``#{1 2 3}`` ``{1, 2, 3}`` :class:`set` ``{1 2 3 4}`` ``{1: 2, 3: 4}`` :class:`dict` ============== ================ ================= The Hy REPL prints output in Hy syntax by default, with the function :hy:func:`hy.repr`:: => [1 2 3] [1 2 3] But if you start Hy like this:: $ hy --repl-output-fn=repr the REPL will use Python's native :py:func:`repr` function instead, so you'll see values in Python syntax:: => [1 2 3] [1, 2, 3] Basic operations ================ Set variables with :hy:func:`setv`:: (setv zone-plane 8) Access the elements of a list, dictionary, or other data structure with :hy:func:`get `:: (setv fruit ["apple" "banana" "cantaloupe"]) (print (get fruit 0)) ; => apple (setv (get fruit 1) "durian") (print (get fruit 1)) ; => durian Access a range of elements in an ordered structure with :hy:func:`cut `:: (print (cut "abcdef" 1 4)) ; => bcd Conditional logic can be built with :hy:func:`if`:: (if (= 1 1) (print "Math works. The universe is safe.") (print "Math has failed. The universe is doomed.")) As in this example, ``if`` is called like ``(if CONDITION THEN ELSE)``. It executes and returns the form ``THEN`` if ``CONDITION`` is true (according to :class:`bool`) and ``ELSE`` otherwise. What if you want to use more than form in place of the ``THEN`` or ``ELSE`` clauses, or in place of ``CONDITION``, for that matter? Use the macro :hy:func:`do` (known more traditionally in Lisp as ``progn``), which combines several forms into one, returning the last:: (if (do (print "Let's check.") (= 1 1)) (do (print "Math works.") (print "The universe is safe.")) (do (print "Math has failed.") (print "The universe is doomed."))) For branching on more than one case, try :hy:func:`cond `:: (setv somevar 33) (cond (> somevar 50) (print "That variable is too big!") (< somevar 10) (print "That variable is too small!") True (print "That variable is jussssst right!")) The macro ``(when CONDITION THEN-1 THEN-2 …)`` is shorthand for ``(if CONDITION (do THEN-1 THEN-2 …) None)``. Hy's basic loops are :hy:func:`while` and :hy:func:`for`:: (setv x 3) (while (> x 0) (print x) (setv x (- x 1))) ; => 3 2 1 (for [x [1 2 3]] (print x)) ; => 1 2 3 A more functional way to iterate is provided by the comprehension forms such as :hy:func:`lfor`. Whereas ``for`` always returns ``None``, ``lfor`` returns a list with one element per iteration. :: (print (lfor x [1 2 3] (* x 2))) ; => [2, 4, 6] Functions, classes, and modules =============================== Define named functions with :hy:func:`defn`:: (defn fib [n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2))))) (print (fib 8)) ; => 21 Define anonymous functions with :hy:func:`fn`:: (print (list (filter (fn [x] (% x 2)) (range 10)))) ; => [1, 3, 5, 7, 9] Special symbols in the parameter list of ``defn`` or ``fn`` allow you to indicate optional arguments, provide default values, and collect unlisted arguments:: (defn test [a b [c None] [d "x"] #* e] [a b c d e]) (print (test 1 2)) ; => [1, 2, None, 'x', ()] (print (test 1 2 3 4 5 6 7)) ; => [1, 2, 3, 4, (5, 6, 7)] Set a function parameter by name with a ``:keyword``:: (test 1 2 :d "y") ; => [1, 2, None, 'y', ()] Note that unlike Python, Hy doesn't always evaluate function arguments (or the items in a literal list, or the items in a literal dictionary, etc.) :ref:`in the order they appear in the code `. But you can always force a particular evaluation order with :hy:func:`do`, or with other macros that provide an implicit :hy:func:`do`, like :hy:func:`when ` or :hy:func:`fn`. Define classes with :hy:func:`defclass`:: (defclass FooBar [] (defn __init__ [self x] (setv self.x x)) (defn get-x [self] self.x)) Here we create a new instance ``fb`` of ``FooBar`` and access its attributes by various means:: (setv fb (FooBar 15)) (print fb.x) ; => 15 (print (. fb x)) ; => 15 (print (.get-x fb)) ; => 15 (print (fb.get-x)) ; => 15 Note that syntax like ``fb.x`` and ``fb.get-x`` only works when the object being invoked (``fb``, in this case) is a simple variable name. To get an attribute or call a method of an arbitrary form ``FORM``, you must use the syntax ``(. FORM x)`` or ``(.get-x FORM)``, or call :py:func:`getattr`. Access an external module, whether written in Python or Hy, with :hy:func:`import`:: (import math) (print (math.sqrt 2)) ; => 1.4142135623730951 Or use the one-shot import syntax :hy:class:`hy.I`:: (print (hy.I.math.sqrt 2)) Python can import a Hy module like any other module so long as Hy itself has been imported first, which, of course, must have already happened if you're running a Hy program. .. _tutorial-macros: Macros ====== Macros are the basic metaprogramming tool of Lisp. A macro is a function that is called at compile time (i.e., when a Hy program is being translated to Python :mod:`ast` objects) and returns code, which becomes part of the final program. Here's a simple example:: (print "Executing") (defmacro m [] (print "Now for a slow computation") (setv x (% (** 10 10 7) 3)) (print "Done computing") x) (print "Value:" (m)) (print "Done executing") If you run this program twice in a row, you'll see this: .. code-block:: text $ hy example.hy Now for a slow computation Done computing Executing Value: 1 Done executing $ hy example.hy Executing Value: 1 Done executing The slow computation is performed while compiling the program on its first invocation. Only after the whole program is compiled does normal execution begin from the top, printing "Executing". When the program is called a second time, it is run from the previously compiled bytecode, which is equivalent to simply:: (print "Executing") (print "Value:" 1) (print "Done executing") Our macro ``m`` has an especially simple return value, an integer (:py:class:`int`), which at compile-time is converted to an integer model (:class:`hy.models.Integer`). In general, macros can return arbitrary Hy models to be executed as code. There are several helper macros that make it easy to construct forms programmatically, such as :hy:func:`quote` (``'``), :hy:func:`quasiquote` (`````), :hy:func:`unquote` (``~``), :hy:func:`unquote-splice` (``~@``), and :hy:func:`defmacro! `. The previous chapter has :ref:`a simple example ` of using ````` and ``~@`` to define a new control construct ``do-while``. What if you want to use a macro that's defined in a different module? ``import`` won't help, because it merely translates to a Python ``import`` statement that's executed at run-time, and macros are expanded at compile-time, that is, during the translation from Hy to Python. Instead, use :hy:func:`require `, which imports the module and makes macros available at compile-time. ``require`` uses the same syntax as ``import``. :: (require some-module.macros) (some-module.macros.rev (1 2 3 +)) ; => 6 Hy also supports reader macros, which are similar to ordinary macros, but operate on raw source text rather than pre-parsed Hy forms. They can choose how much of the source code to consume after the point they are called, and return any code. Thus, reader macros can add entirely new syntax to Hy. For example, you could add a literal notation for Python's :class:`decimal.Decimal` class like so:: (defreader d (.slurp-space &reader) `(hy.I.decimal.Decimal ~(.read-ident &reader))) (print (repr #d .1)) ; => Decimal('0.1') (import fractions [Fraction]) (print (Fraction #d .1)) ; => 1/10 ;; Contrast with the normal floating-point .1: (print (Fraction .1)) ; => 3602879701896397/36028797018963968 ``require`` can pull in a reader macro defined in a different module with syntax like ``(require mymodule :readers [d])``. Recommended libraries ===================== `Hyrule `_ is Hy's standard utility library. It provides a variety of functions and macros that are useful for writing Hy programs. :: (import hyrule [inc]) (list (map inc [1 2 3])) ; => [2 3 4] (require hyrule [case]) (setv x 2) (case x 1 "a" 2 "b" 3 "c") ; => "b" `toolz `_ and its Cython variant `cytoolz `_ provide lots of utilities for functional programming and working with iterables. :: (import toolz [partition]) (list (partition 2 [1 2 3 4 5 6])) ; => [#(1 2) #(3 4) #(5 6)] `metadict `_ allows you to refer to the elements of a dictionary as attributes. This is handy when frequently referring to elements with constant strings as keys, since plain indexing is a bit verbose in Hy. :: (import metadict [MetaDict]) (setv d (MetaDict)) (setv d.foo 1) ; i.e., (setv (get d "foo") 1) d.foo ; i.e., (get d "foo") ; => 1 (list (.keys d)) ; => ["foo"] Next steps ========== You now know enough to be dangerous with Hy. You may now smile villainously and sneak off to your Hydeaway to do unspeakable things. Refer to Python's documentation for the details of Python semantics. In particular, :ref:`the Python tutorial ` can be helpful even if you have no interest in writing your own Python code, because it will introduce you to the semantics, and you'll need a reading knowledge of Python syntax to understand example code for Python libraries. Refer to the rest of this manual for Hy-specific features. See `the wiki `_ for tips on getting Hy to work with other software. For an official full-blown example Hy program, see `Infinitesimal Quest 2 + ε `_. hy-1.0.0/docs/versioning.rst000066400000000000000000000032101467401276000160000ustar00rootroot00000000000000============================ Versioning and compatibility ============================ Starting with Hy 1.0.0, Hy is `semantically versioned `_. Refer to `the NEWS file `_ for a summary of user-visible changes brought on by each version, and how to update your code in case of breaking changes. Be sure you're reading the version of this manual (shown at the top of each page) that matches the version of Hy you're running. Hy is tested on `all released and currently maintained versions of CPython `_ (on Linux, Windows, and Mac OS), and on recent versions of PyPy and Pyodide. We usually find that for Hy, unlike most Python packages, we need to change things to fully support each new 3.x release of Python. We may drop compatibility with a version of Python after the CPython guys cease maintaining it, and note that we construe such a change as non-breaking, so we won't bump Hy's major version for it. But we will at least bump the minor version, and ``python_requires`` in Hy's ``setup.py`` should prevent you from installing a Hy version that won't work with your Python version. Starting with Hy 1.0.0, each version of Hy also has a nickname, such as "Afternoon Review". Nicknames are used in alphabetical order, with a nickname starting with "Z" then wrapping around to "A". Nicknames are provided mostly for the amusement of the maintainer, but can be useful as a conspicuous sign that you're not using the version you expected. In code, you can get the current nickname as a string (or ``None``, for unreleased commits of Hy) with ``hy.nickname``. hy-1.0.0/docs/whyhy.rst000066400000000000000000000160501467401276000147730ustar00rootroot00000000000000======= Why Hy? ======= .. Changes to the below paragraph should be mirrored on Hy's homepage. Hy (or "Hylang" for long; named after the insect order Hymenoptera, since Paul Tagliamonte was studying swarm behavior when he created the language) is a multi-paradigm general-purpose programming language in the `Lisp family `_. It's implemented as a kind of alternative syntax for Python. Compared to Python, Hy offers a variety of new features, generalizations, and syntactic simplifications, as would be expected of a Lisp. Compared to other Lisps, Hy provides direct access to Python's built-ins and third-party Python libraries, while allowing you to freely mix imperative, functional, and object-oriented styles of programming. .. contents:: Contents :local: Hy versus Python ---------------- The first thing a Python programmer will notice about Hy is that it has Lisp's traditional parenthesis-heavy prefix syntax in place of Python's C-like infix syntax. For example, .. code-block:: python print("The answer is", 2 + object.method(arg)) could be written :: (print "The answer is" (+ 2 (.method object arg))) in Hy. Consequently, Hy is free-form: structure is indicated by punctuation rather than whitespace, making it convenient for command-line use. As in other Lisps, the value of a simplistic syntax is that it facilitates Lisp's signature feature: `metaprogramming `_ through :doc:`macros `, which are functions that manipulate code objects at compile time to produce new code objects, which are then executed as if they had been part of the original code. In fact, Hy allows arbitrary computation at compile-time. For example, here's a simple macro that implements a C-style do-while loop, which executes its body for as long as the condition is true, but at least once. .. _do-while: :: (defmacro do-while [condition #* body] `(do ~@body (while ~condition ~@body))) (setv x 0) (do-while x (print "This line is executed once.")) Hy also removes Python's restrictions on mixing expressions and statements, allowing for more direct and functional code. For example, Python doesn't allow :keyword:`with` blocks, which close a resource once you're done using it, to return values. They can only execute a set of statements: .. code-block:: python with open("foo") as o: f1 = o.read() with open("bar") as o: f2 = o.read() print(len(f1) + len(f2)) In Hy, :hy:func:`with` returns the value of its last body form, so you can use it like an ordinary function call:: (print (+ (len (with [o (open "foo")] (.read o))) (len (with [o (open "bar")] (.read o))))) To be even more concise, you can put a ``with`` form in a :hy:func:`gfor`:: (print (sum (gfor filename ["foo" "bar"] (len (with [o (open filename)] (.read o)))))) Finally, Hy offers several generalizations to Python's binary operators. Operators can be given more than two arguments (e.g., ``(+ 1 2 3)``), including augmented assignment operators (e.g., ``(+= x 1 2 3)``). They are also provided as ordinary first-class functions of the same name, allowing them to be passed to higher-order functions: ``(sum xs)`` could be written ``(reduce + xs)``, after importing the function ``+`` from the module :hy:mod:`hy.pyops`. The Hy compiler works by reading Hy source code into Hy :ref:`model objects ` and compiling them into Python abstract syntax tree (:py:mod:`ast`) objects. Python AST objects can then be compiled and run by Python itself, byte-compiled for faster execution later, or rendered into Python source code. You can :ref:`mix Python and Hy code in the same project, or even the same file,` which can be a good way to get your feet wet in Hy. Hy versus other Lisps --------------------- At run-time, Hy is essentially Python code. Thus, while Hy's design owes a lot to `Clojure `_, it is more tightly coupled to Python than Clojure is to Java; a better analogy is `CoffeeScript's `_ relationship with JavaScript. Python's built-in :ref:`functions ` and :ref:`data structures ` are directly available:: (print (int "deadbeef" :base 16)) ; 3735928559 (print (len [1 10 100])) ; 3 The same goes for third-party Python libraries from `PyPI `_ and elsewhere. Here's a tiny `CherryPy `_ web application in Hy:: (import cherrypy) (defclass HelloWorld [] (defn [cherrypy.expose] index [self] "Hello World!")) (cherrypy.quickstart (HelloWorld)) You can even run Hy on `PyPy `_ for a particularly speedy Lisp. Like all Lisps, Hy is `homoiconic `_. Its syntax is represented not with cons cells or with Python's basic data structures, but with simple subclasses of Python's basic data structures called :ref:`models `. Using models in place of plain ``list``\s, ``set``\s, and so on has two purposes: models can keep track of their line and column numbers for the benefit of error messages, and models can represent syntactic features that the corresponding primitive type can't, such as the order in which elements appear in a set literal. However, models can be concatenated and indexed just like plain lists, and you can return ordinary Python types from a macro or give them to :hy:func:`hy.eval` and Hy will automatically promote them to models. Hy takes much of its semantics from Python. For example, functions use the same namespace as objects that aren't functions, so a variable named ``globals`` can shadow the Python built-in function :py:func:`globals`. In general, any Python code should be possible to literally translate to Hy. At the same time, Hy goes to some lengths to allow you to do typical Lisp things that aren't straightforward in Python. For example, Hy provides the aforementioned mixing of statements and expressions, :ref:`name mangling ` that transparently converts symbols with names like ``valid?`` to Python-legal identifiers, and a :hy:func:`let` macro to provide block-level scoping in place of Python's usual function-level scoping. What Hy is not -------------- Hy isn't minimal or elegant. Hy is big and ugly and proud of it; it's an unopinionated big-tent language that lets you do what you want. It has all of Python's least-motivated semantic features, plus more features, plus various kinds of syntactic sugar. (The syntax isn't as complex as Python's, but there are a lot of details beyond plain old S-expressions.) If you're interested in a more small-and-beautiful approach to Lisp, in the style of Scheme, check out `Hissp `_, another Lisp embedded in Python that was created by a Hy developer. Also, Hy isn't a reimplementation of an older Lisp. It is its own language. It looks kind of like Clojure and kind of like Common Lisp, but nontrivial programs that run in one of these langauges can't be expected to run on another unaltered. hy-1.0.0/fastentrypoints.py000066400000000000000000000077071467401276000160000ustar00rootroot00000000000000# noqa: D300,D400 # Copyright (c) 2016, Aaron Christianson # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' Monkey patch setuptools to write faster console_scripts with this format: import sys from mymodule import entry_function sys.exit(entry_function()) This is better. (c) 2016, Aaron Christianson http://github.com/ninjaaron/fast-entry_points ''' from setuptools.command import easy_install import re TEMPLATE = r''' # -*- coding: utf-8 -*- # EASY-INSTALL-ENTRY-SCRIPT: '{3}','{4}','{5}' __requires__ = '{3}' import re import sys from {0} import {1} if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) sys.exit({2}()) '''.lstrip() @classmethod def get_args(cls, dist, header=None): # noqa: D205,D400 """ Yield write_script() argument tuples for a distribution's console_scripts and gui_scripts entry points. """ if header is None: # pylint: disable=E1101 header = cls.get_header() spec = str(dist.as_requirement()) for type_ in 'console', 'gui': group = type_ + '_scripts' for name, ep in dist.get_entry_map(group).items(): # ensure_safe_name if re.search(r'[\\/]', name): raise ValueError("Path separators not allowed in script names") script_text = TEMPLATE.format( ep.module_name, ep.attrs[0], '.'.join(ep.attrs), spec, group, name) # pylint: disable=E1101 args = cls._get_script_args(type_, name, header, script_text) for res in args: yield res # pylint: disable=E1101 easy_install.ScriptWriter.get_args = get_args def main(): import os import shutil import sys dests = sys.argv[1:] or ['.'] filename = re.sub(r'\.pyc$', '.py', __file__) for dst in dests: shutil.copy(filename, dst) manifest_path = os.path.join(dst, 'MANIFEST.in') setup_path = os.path.join(dst, 'setup.py') # Insert the include statement to MANIFEST.in if not present with open(manifest_path, 'a+') as manifest: manifest.seek(0) manifest_content = manifest.read() if 'include fastentrypoints.py' not in manifest_content: manifest.write(('\n' if manifest_content else '') + 'include fastentrypoints.py') # Insert the import statement to setup.py if not present with open(setup_path, 'a+') as setup: setup.seek(0) setup_content = setup.read() if 'import fastentrypoints' not in setup_content: setup.seek(0) setup.truncate() setup.write('import fastentrypoints\n' + setup_content) hy-1.0.0/hy/000077500000000000000000000000001467401276000125575ustar00rootroot00000000000000hy-1.0.0/hy/__init__.py000066400000000000000000000050201467401276000146650ustar00rootroot00000000000000__version__ = '1.0.0' nickname = 'Afternoon Review' def _initialize_env_var(env_var, default_val): import os return bool(os.environ.get(env_var, default_val)) import hy.importer # NOQA hy.importer._inject_builtins() # we import for side-effects. class I: """``hy.I`` is an object that provides syntactic sugar for imports. It allows syntax like ``(hy.I.math.sqrt 2)`` to mean ``(import math) (math.sqrt 2)``, except without bringing ``math`` or ``math.sqrt`` into scope. (See :ref:`hy.R ` for a version that requires a macro instead of importing a Python object.) This is useful in macros to avoid namespace pollution. To refer to a module with dots in its name, use slashes instead: ``hy.I.os/path.basename`` gets the function ``basename`` from the module ``os.path``. You can also call ``hy.I`` like a function, as in ``(hy.I "math")``, which is useful when the module name isn't known until run-time. This interface just calls :py:func:`importlib.import_module`, avoiding (1) mangling due to attribute lookup, and (2) the translation of ``/`` to ``.`` in the module name. The advantage of ``(hy.I modname)`` over ``importlib.import_module(modname)`` is merely that it avoids bringing ``importlib`` itself into scope.""" def __call__(self, module_name): import importlib return importlib.import_module(module_name) def __getattr__(self, s): from hy.reader.mangling import slashes2dots return self(slashes2dots(s)) I = I() # Import some names on demand so that the dependent modules don't have # to be loaded if they're not needed. _jit_imports = dict( pyops=["hy.pyops", None], read="hy.reader", read_many="hy.reader", mangle="hy.reader", unmangle="hy.reader", eval=["hy.compiler", "hy_eval_user"], repr=["hy.core.hy_repr", "hy_repr"], repr_register=["hy.core.hy_repr", "hy_repr_register"], gensym="hy.core.util", macroexpand="hy.core.util", macroexpand_1="hy.core.util", disassemble="hy.core.util", as_model="hy.models", REPL="hy.repl", Reader="hy.reader.reader", HyReader="hy.reader.hy_reader", PrematureEndOfInput="hy.reader.exceptions" ) def __getattr__(k): if k not in _jit_imports: raise AttributeError(f"module {__name__!r} has no attribute {k!r}") v = _jit_imports[k] module, original_name = v if isinstance(v, list) else (v, k) import importlib module = importlib.import_module(module) globals()[k] = getattr(module, original_name) if original_name else module return globals()[k] hy-1.0.0/hy/__main__.py000066400000000000000000000002421467401276000146470ustar00rootroot00000000000000import sys from hy.cmdline import hy_main # Running hy as a module (e.g. `python -m hy`) # is equivalent to running the main `hy` command. sys.exit(hy_main()) hy-1.0.0/hy/cmdline.py000066400000000000000000000401151467401276000145450ustar00rootroot00000000000000import argparse import ast import importlib import io import os import platform import py_compile import re import runpy import sys import types from contextlib import nullcontext from pathlib import Path import hy from hy.compat import PY3_9 from hy.compiler import hy_compile, hy_eval from hy.errors import HyLanguageError, filtered_hy_exceptions, hy_exc_handler from hy.importer import runhy from hy.macros import require from hy.reader import read_many from hy.repl import REPL def set_path(filename): """Emulate Python cmdline behavior by setting `sys.path` relative to the executed file's location.""" if sys.path[0] == "": sys.path.pop(0) sys.path.insert(0, str(Path(filename).parent.resolve())) def run_command(source, filename=None): __main__ = importlib.import_module("__main__") require("hy.cmdline", __main__, assignments="ALL") with filtered_hy_exceptions(): try: hy_eval( read_many(source, filename=filename), __main__.__dict__, __main__, filename=filename, source=source, ) except HyLanguageError: hy_exc_handler(*sys.exc_info()) return 1 return 0 USAGE = "hy [-h | -v | -i | -c CMD | -m MODULE | FILE | -] [ARG]..." VERSION = "hy " + hy.__version__ EPILOG = """ FILE program read from script - program read from stdin [ARG]... arguments passed to program in (cut sys.argv 1) """ class HyArgError(Exception): pass def cmdline_handler(argv): # We need to terminate interpretation of options after certain # options, such as `-c`. So, we can't use `argparse`. defs = [ dict( name=["-B"], action="store_true", help="don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x", ), dict( name=["-c"], dest="command", terminate=True, help="program passed in as string", ), dict( name=["-E"], action="store_true", help="ignore PYTHON* environment variables (such as PYTHONPATH)", ), dict( name=["-h", "--help"], action="help", help="print this help message and exit", ), dict( name=["-i"], action="store_true", help="launch REPL after running script; forces a prompt even if stdin does not appear to be a terminal", ), dict( name=["-m"], dest="mod", terminate=True, help="run library module as a script", ), dict( name=["--repl-output-fn"], dest="repl_output_fn", help="function for printing REPL output (e.g., repr)", ), dict( name=["--spy"], action="store_true", help="print equivalent Python code before executing", ), dict( name=["-u", "--unbuffered"], action="store_true", help="force the stdout and stderr streams to be unbuffered; this option has no effect on stdin; also PYTHONUNBUFFERED=x", ), dict( name=["-v", "--version"], action="version", help="print the Hy version number and exit", ), ] # Get the path of the Hy cmdline executable and swap it with # `sys.executable` (saving the original, just in case). # The `__main__` module will also have `__file__` set to the # entry-point script. Currently, I don't see an immediate problem, but # that's not how the Python cmdline works. hy.executable = argv[0] hy.sys_executable = sys.executable sys.executable = hy.executable program = argv[0] argv = list(argv[1:]) options = {} def err(fmt, *args): raise HyArgError("hy: " + fmt.format(*args)) def proc_opt(opt, arg=None, item=None, i=None): matches = [o for o in defs if opt in o["name"]] if not matches: err("unrecognized option: {}", opt) [match] = matches if "dest" in match: if arg: pass elif i is not None and i + 1 < len(item): arg = item[i + 1 + (item[i + 1] == "=") :] elif argv: arg = argv.pop(0) else: err("option {}: expected one argument", opt) options[match["dest"]] = arg else: options[match["name"][-1].lstrip("-")] = True if "terminate" in match: return "terminate" return "dest" in match # Collect options. while argv: item = argv.pop(0) if item == "--": break elif item.startswith("--"): # One double-hyphen option. opt, _, arg = item.partition("=") if proc_opt(opt, arg=arg) == "terminate": break elif item.startswith("-") and item != "-": # One or more single-hyphen options. for i in range(1, len(item)): x = proc_opt("-" + item[i], item=item, i=i) if x: break if x == "terminate": break else: # We're done with options. Add the item back. argv.insert(0, item) break if "E" in options: _remove_python_envs() if "B" in options: sys.dont_write_bytecode = True if "unbuffered" in options: for k in "stdout", "stderr": setattr( sys, k, io.TextIOWrapper( open(getattr(sys, k).fileno(), "wb", 0), write_through=True ), ) if "help" in options: print("usage:", USAGE) print("") print("optional arguments:") for o in defs: print( ", ".join(o["name"]) + ("=" + o["dest"].upper() if "dest" in o else "") ) print( " " + o["help"] + (" (terminates option list)" if o.get("terminate") else "") ) print(EPILOG) return 0 if "version" in options: print(VERSION) return 0 action, action_arg = ( # If the `command` or `mod` options were provided, we'll run # the corresponding code. ["eval_string", options["command"]] if "command" in options else ["run_module", options["mod"]] if "mod" in options else # Otherwise, we'll run any provided filename as a script (or # standard input, if the filename is "-"). ["run_script_stdin", None] if argv and argv[0] == "-" else ["run_script_file", argv[0]] if argv else # With none of those arguments, we'll launch the REPL (if # standard input is a TTY) or run a script from standard input # (otherwise). ["just_repl", None] if sys.stdin.isatty() else ["run_script_stdin", None]) repl = ( REPL( spy = options.get("spy"), output_fn = options.get("repl_output_fn")) if "i" in options or action == "just_repl" else None) source = '' if action == "eval_string": sys.argv = ["-c"] + argv if repl: source = action_arg filename = '' else: return run_command(action_arg, filename="") elif action == "run_module": if repl: raise ValueError() set_path("") sys.argv = [program] + argv runpy.run_module(hy.mangle(action_arg), run_name="__main__", alter_sys=True) return 0 elif action == "run_script_stdin": sys.argv = argv if not repl: return run_command(sys.stdin.read(), filename="") elif action == "run_script_file": sys.argv = argv filename = Path(action_arg) set_path(filename) # Ensure __file__ is set correctly in the code we're about # to run. if PY3_9: if not filename.is_absolute(): filename = Path.cwd() / filename if platform.system() == "Windows": filename = os.path.normpath(filename) if repl: source = Path(filename).read_text() repl.compile.compiler.skip_next_shebang = True else: try: with filtered_hy_exceptions(): runhy.run_path(str(filename), run_name="__main__") return 0 except FileNotFoundError as e: print( "hy: Can't open file '{}': [Errno {}] {}".format( e.filename, e.errno, e.strerror ), file=sys.stderr, ) sys.exit(e.errno) except HyLanguageError: hy_exc_handler(*sys.exc_info()) sys.exit(1) else: assert action == "just_repl" # If we didn't return earlier, we'll be using the REPL. if source: # Execute `source` in the REPL before entering interactive mode. res = None filename = str(filename) with filtered_hy_exceptions(): accum = '' for chunk in ([source] if isinstance(source, str) else source): accum += chunk res = repl.runsource(accum, filename=filename) if not res: accum = '' # If the command was prematurely ended, show an error (just like Python # does). if res: hy_exc_handler(sys.last_type, sys.last_value, sys.last_traceback) return repl.run() # entry point for cmd line script "hy" def hy_main(): sys.path.insert(0, "") try: sys.exit(cmdline_handler(sys.argv)) except HyArgError as e: print(e) exit(1) def hyc_main(): parser = argparse.ArgumentParser(prog="hyc") parser.add_argument("files", metavar="FILE", nargs="+", help="File(s) to compile") parser.add_argument("-v", action="version", version=VERSION) options = parser.parse_args(sys.argv[1:]) rv = 0 for filename in options.files: set_path(filename) try: print( "Compiling {!r} --> {!r}".format( filename, importlib.util.cache_from_source(filename) ), file=sys.stderr, ) py_compile.compile(filename, doraise=True) except py_compile.PyCompileError as error: # return value to indicate at least one failure rv = 1 print(error.msg, file=sys.stderr) sys.path.pop(0) return rv def hy2py_worker(source, options, filename=None, parent_module=None, output_filepath=None): source_path = None if isinstance(source, Path): source_path = source source = source.read_text(encoding="UTF-8") if parent_module is None: set_path(source_path) if not output_filepath and options.output: output_filepath = options.output with ( open(output_filepath, "w", encoding="utf-8") if output_filepath else nullcontext() ) as output_file: def printing_source(hst): def _printing_gen(hst): for node in hst: if options.with_source: print(node, file=output_file) yield node printing_hst = hy.models.Lazy(_printing_gen(hst)) printing_hst.source = hst.source printing_hst.filename = hst.filename printing_hst.reader = hst.reader return printing_hst hst = printing_source(read_many(source, filename, skip_shebang=True)) with filtered_hy_exceptions(): module_name = source_path.stem if source_path else Path(filename).name if parent_module: module_name = f"{parent_module}.{module_name}" module = types.ModuleType(module_name) sys.modules[module_name] = module try: _ast = hy_compile( hst, module, filename=filename, source=source) finally: del sys.modules[module_name] if options.with_source: print() print() if options.with_ast: print(ast.dump(_ast, **(dict(indent=2) if PY3_9 else {})), file=output_file) print() print() if not options.without_python: print(ast.unparse(_ast), file=output_file) # entry point for cmd line script "hy2py" def hy2py_main(): options = dict( prog="hy2py", usage="%(prog)s [options] [-m MODULE | FILE | -]", formatter_class=argparse.RawDescriptionHelpFormatter, ) parser = argparse.ArgumentParser(**options) gp = parser.add_argument_group().add_mutually_exclusive_group() gp.add_argument("-m", dest="module", help="convert Hy module (or all files in module)") gp.add_argument( "FILE", type=str, nargs="?", help='convert Hy source file', ) gp.add_argument("-", dest="use_stdin", action="store_true", help="read Hy from stdin") parser.add_argument( "--with-source", "-s", action="store_true", help="Show the parsed source structure", ) parser.add_argument( "--with-ast", "-a", action="store_true", help="Show the generated AST" ) parser.add_argument( "--without-python", "-np", action="store_true", help=("Do not show the Python code generated " "from the AST"), ) parser.add_argument( "--output", "-o", type=str, nargs="?", help="output file / directory", ) options = parser.parse_args(sys.argv[1:]) if options.use_stdin or (options.FILE is None and options.module is None): sys.path.insert(0, "") filename = "" hy2py_worker(sys.stdin.read(), options, filename) elif options.module: if options.module[:1] == ".": raise ValueError( "Relative module names not supported" ) sys.path.insert(0, "") filename = options.module.replace(".", os.sep) if os.path.isdir(filename): # handle recursively if --output is specified if not options.output: raise ValueError( f"{filename} is a directory but the output directory is not specified. Use --output or -o in command line arguments to specify the output directory." ) os.makedirs(options.output, exist_ok=True) for path, _, files in os.walk(filename): for name in files: filename_raw, filename_ext = os.path.splitext(name) if filename_ext == ".hy": filepath = os.path.join(path, name) # make sure to follow original file structure subdirectory = os.path.relpath(path, filename) output_directory_path = os.path.join( options.output, subdirectory ) os.makedirs(output_directory_path, exist_ok=True) hy2py_worker( Path(filepath), options, parent_module=path.replace(os.sep, "."), output_filepath=os.path.join( output_directory_path, filename_raw + ".py" ), ) else: filename += ".hy" parent_module = ".".join(options.module.split(".")[:-1]) hy2py_worker(Path(filename), options, parent_module=parent_module) else: hy2py_worker(Path(options.FILE), options, options.FILE) parser.exit(0) # remove PYTHON* environment variables, # such as "PYTHONPATH" def _remove_python_envs(): for key in list(os.environ.keys()): if key.startswith("PYTHON"): os.environ.pop(key) hy-1.0.0/hy/compat.py000066400000000000000000000046631467401276000144250ustar00rootroot00000000000000import ast import platform import sys PY3_9 = sys.version_info >= (3, 9) PY3_10 = sys.version_info >= (3, 10) PY3_11 = sys.version_info >= (3, 11) PY3_12 = sys.version_info >= (3, 12) PY3_12_6 = sys.version_info >= (3, 12, 6) PY3_13 = sys.version_info >= (3, 13) PYPY = platform.python_implementation() == "PyPy" PYODIDE = platform.system() == "Emscripten" if not PY3_9: # Shim `ast.unparse`. import astor.code_gen ast.unparse = astor.code_gen.to_source if "def" in ast.unparse(ast.parse("𝕕𝕖𝕗 = 1")): # Overwrite `ast.unparse` to backport https://github.com/python/cpython/pull/31012 import copy import keyword true_unparse = ast.unparse def rewriting_unparse(ast_obj): ast_obj = copy.deepcopy(ast_obj) for node in ast.walk(ast_obj): if type(node) is ast.Constant: # Don't touch string literals. continue for field in node._fields: v = getattr(node, field, None) if ( type(v) is str and keyword.iskeyword(v) and v not in ("True", "False", "None") ): # We refer to this transformation as "keyword mincing" # in documentation. setattr(node, field, chr(ord(v[0]) - ord("a") + ord("𝐚")) + v[1:]) return true_unparse(ast_obj) ast.unparse = rewriting_unparse if True: import pydoc, inspect, re true_getdoc = pydoc.getdoc if not PY3_9: pydoc._getdoc = inspect.getdoc def getdoc(object): """A monkey-patched `pydoc.getdoc` that tries to avoid calling `inspect.getcomments` for an object defined in Hy code, which would try to parse the Hy as Python. The implementation is based on Python 3.12.3's `getdoc`.""" result = pydoc._getdoc(object) if not result: can_get_comments = True try: file_path = inspect.getfile(object) except TypeError: None else: can_get_comments = not file_path.endswith('.hy') if can_get_comments: result = inspect.getcomments(object) return result and re.sub('^ *\n', '', result.rstrip()) or '' pydoc.getdoc = getdoc def reu(x): '(R)eplace an (e)rror (u)nderline. This is only used for testing Hy.' return x.replace('-', '^') if PY3_13 else x hy-1.0.0/hy/compiler.py000077500000000000000000000751261467401276000147610ustar00rootroot00000000000000import ast import builtins import copy import importlib import inspect import traceback import types import warnings from contextlib import contextmanager from funcparserlib.parser import NoParseError, many import hy from hy.errors import HyCompileError, HyLanguageError, HySyntaxError from hy.macros import macroexpand from hy.model_patterns import FORM, KEYWORD, unpack from hy.models import ( Bytes, Complex, Dict, Expression, FComponent, Float, FString, Integer, Keyword, Lazy, List, Object, Set, String, Symbol, Tuple, as_model, is_unpack, ) from hy.reader import mangle, HyReader from hy.scoping import ResolveOuterVars, ScopeGlobal hy_ast_compile_flags = 0 def ast_compile(a, filename, mode): """Compile AST. Args: a (ast.AST): instance of `ast.AST` filename (str): Filename used for run-time error messages mode (str): `compile` mode parameter Returns: types.CodeType: instance of `types.CodeType` """ return compile(a, filename, mode, hy_ast_compile_flags) def calling_module(n=1): """Get the module calling, if available. As a fallback, this will import a module using the calling frame's globals value of `__name__`. Args: n (int): The number of levels up the stack from this function call. The default is `1` (level up). Returns: types.ModuleType: The module at stack level `n + 1` or `None`. """ frame_up = inspect.stack(0)[n + 1][0] module = inspect.getmodule(frame_up) if module is None: # This works for modules like `__main__` module_name = frame_up.f_globals.get("__name__", None) if module_name: try: module = importlib.import_module(module_name) except ImportError: pass return module _model_compilers = {} def builds_model(*model_types): "Assign the decorated method to _model_compilers for the given types." def _dec(fn): for t in model_types: _model_compilers[t] = fn return fn return _dec # Provide asty.Foo(x, ...) as shorthand for # ast.Foo(..., lineno=x.start_line, col_offset=x.start_column) or # ast.Foo(..., lineno=x.lineno, col_offset=x.col_offset) # Also provides asty.parse(x, ...) which recursively # copies x's position data onto the parse result. class Asty: POS_ATTRS = { "lineno": "start_line", "col_offset": "start_column", "end_lineno": "end_line", "end_col_offset": "end_column", } @staticmethod def _get_pos(node): return { attr: getattr(node, hy_attr, getattr(node, attr, None)) for attr, hy_attr in Asty.POS_ATTRS.items() } @staticmethod def _replace_pos(node, pos): for attr, value in pos.items(): if hasattr(node, attr): setattr(node, attr, value) for child in ast.iter_child_nodes(node): Asty._replace_pos(child, pos) def parse(self, x, *args, **kwargs): res = ast.parse(*args, **kwargs) Asty._replace_pos(res, Asty._get_pos(x)) return res def __getattr__(self, name): setattr( Asty, name, staticmethod( lambda x, **kwargs: getattr(ast, name)(**Asty._get_pos(x), **kwargs) ), ) return getattr(Asty, name) asty = Asty() class Result: """ Smart representation of the result of a hy->AST compilation This object tries to reconcile the hy world, where everything can be used as an expression, with the Python world, where statements and expressions need to coexist. To do so, we represent a compiler result as a list of statements `stmts`, terminated by an expression context `expr`. The expression context is used when the compiler needs to use the result as an expression. Results are chained by addition: adding two results together returns a Result representing the succession of the two Results' statements, with the second Result's expression context. We make sure that a non-empty expression context does not get clobbered by adding more results, by checking accesses to the expression context. We assume that the context has been used, or deliberately ignored, if it has been accessed. The Result object is interoperable with python AST objects: when an AST object gets added to a Result object, it gets converted on-the-fly. """ __slots__ = ("stmts", "temp_variables", "_expr", "__used_expr") def __init__(self, *, stmts=(), expr=None, temp_variables=()): self.stmts = list(stmts) self.temp_variables = list(temp_variables) self._expr = expr self.__used_expr = False @property def expr(self): self.__used_expr = True return self._expr @expr.setter def expr(self, value): self.__used_expr = False self._expr = value @property def lineno(self): if self._expr is not None: return self._expr.lineno if self.stmts: return self.stmts[-1].lineno return None @property def col_offset(self): if self._expr is not None: return self._expr.col_offset if self.stmts: return self.stmts[-1].col_offset return None @property def end_col_offset(self): if self._expr is not None: return self._expr.end_col_offset if self.stmts: return self.stmts[-1].end_col_offset return None @property def end_lineno(self): if self._expr is not None: return self._expr.end_lineno if self.stmts: return self.stmts[-1].end_lineno return None def is_expr(self): """Check whether I am a pure expression""" return self._expr and not self.stmts @property def force_expr(self): """Force the expression context of the Result. If there is no expression context, we return a "None" expression. """ if self.expr: return self.expr return ast.Constant( value=None, lineno=self.stmts[-1].lineno if self.stmts else 0, col_offset=self.stmts[-1].col_offset if self.stmts else 0, ) def expr_as_stmt(self): """Convert the Result's expression context to a statement This is useful when we want to use the stored expression in a statement context (for instance in a code branch). We drop ast.Names if they are appended to statements, as they can't have any side effect. "Bare" names still get converted to statements. If there is no expression context, return an empty result. """ if self.expr and not (isinstance(self.expr, ast.Name) and self.stmts): return Result() + asty.Expr(self.expr, value=self.expr) return Result() def rename(self, compiler, new_name): """Rename the Result's temporary variables to a `new_name`. We know how to handle ast.Names and ast.FunctionDefs. """ new_name = mangle(new_name) for var in self.temp_variables: if isinstance(var, ast.Name): var.id = new_name compiler.scope.assign(var) elif isinstance(var, (ast.FunctionDef, ast.AsyncFunctionDef)): var.name = new_name else: raise TypeError( "Don't know how to rename a %s!" % (var.__class__.__name__) ) self.temp_variables = [] def __add__(self, other): # If we add an ast statement, convert it first if isinstance(other, ast.stmt): return self + Result(stmts=[other]) # If we add an ast expression, clobber the expression context if isinstance(other, ast.expr): return self + Result(expr=other) if isinstance(other, ast.excepthandler): return self + Result(stmts=[other]) if not isinstance(other, Result): raise TypeError(f"Can't add {self!r} with non-compiler result {other!r}") # Check for expression context clobbering if self.expr and not self.__used_expr: traceback.print_stack() print( "Bad boy clobbered expr {} with {}".format( ast.dump(self.expr), ast.dump(other.expr) ) ) # Fairly obvious addition result = Result() result.stmts = self.stmts + other.stmts result.expr = other.expr result.temp_variables = other.temp_variables return result def __str__(self): return "Result(stmts=[%s], expr=%s)" % ( ", ".join(ast.dump(x) for x in self.stmts), ast.dump(self.expr) if self.expr else None, ) def make_hy_model(outer, x, rest): return outer( [Symbol(a) if type(a) is str else a[0] if type(a) is list else a for a in x] + (rest or []) ) def mkexpr(*items, **kwargs): return make_hy_model(Expression, items, kwargs.get("rest")) def is_annotate_expression(model): return isinstance(model, Expression) and model and model[0] == Symbol("annotate") class HyASTCompiler: """A Hy-to-Python AST compiler""" def __init__(self, module, filename=None, source=None, extra_macros=None): """ Args: module (Union[str, types.ModuleType]): Module name or object in which the Hy tree is evaluated. filename (Optional[str]): The name of the file for the source to be compiled. This is optional information for informative error messages and debugging. source (Optional[str]): The source for the file, if any, being compiled. This is optional information for informative error messages and debugging. extra_macros (Optional[dict]): More macros to use during lookup. They take precedence over macros in `module`. """ self.anon_var_count = 0 self.temp_if = None self.extra_macros = extra_macros or {} # Make a list of dictionaries with local compiler settings, # such as the definitions of local macros. The last element is # considered the top of the stack. self.local_state_stack = [] self.new_local_state() if not inspect.ismodule(module): self.module = importlib.import_module(module) else: self.module = module self.module.hy = hy # The `hy` module itself should always be in scope. self.module_name = self.module.__name__ self.filename = filename self.source = source self.this = None # Set in `macroexpand` to the current expression being # macro-expanded, so it can be accessed as `&compiler.this`. # Hy expects this to be present, so we prep the module for Hy # compilation. self.module.__dict__.setdefault("_hy_macros", {}) self.module.__dict__.setdefault("_hy_reader_macros", {}) self.scope = ScopeGlobal(self) def new_local_state(self): 'Add a new local state to the top of the stack.' self.local_state_stack.append(dict(macros = {})) def is_in_local_state(self): return len(self.local_state_stack) > 1 def get_local_option(self, key, default): 'Get the topmost available value of a local-state setting.' return next( (s[key] for s in reversed(self.local_state_stack) if key in s), default) def warn_on_core_shadow(self, name): if ( mangle(name) in getattr(builtins, "_hy_macros", {}) and self.get_local_option('warn_on_core_shadow', True)): warnings.warn( f"New macro `{name}` will shadow the core macro of the same name", RuntimeWarning ) def get_anon_var(self, base="_hy_anon_var"): self.anon_var_count += 1 return f"{base}_{self.anon_var_count}" def compile_atom(self, atom): # Compilation methods may mutate the atom, so copy it first. atom = copy.copy(atom) return Result() + _model_compilers[type(atom)](self, atom) def compile(self, tree): if tree is None: return Result() try: ret = self.compile_atom(tree) return ret except HyCompileError: # compile calls compile, so we're going to have multiple raise # nested; so let's re-raise this exception, let's not wrap it in # another HyCompileError! raise except HyLanguageError as e: # These are expected errors that should be passed to the user. raise e except Exception as e: # These are unexpected errors that will--hopefully--never be seen # by the user. f_exc = traceback.format_exc() exc_msg = "Internal Compiler Bug\n {}".format(f_exc) raise HyCompileError(exc_msg) def _syntax_error(self, expr, message): return HySyntaxError(message, expr, self.filename, self.source) def _compile_collect(self, exprs, with_kwargs=False, dict_display=False): """Collect the expression contexts from a list of compiled expression. This returns a list of the expression contexts, and the sum of the Result objects passed as arguments. """ compiled_exprs = [] ret = Result() keywords = [] exprs_iter = iter(exprs) for expr in exprs_iter: if is_unpack("mapping", expr): ret += self.compile(expr[1]) if dict_display: compiled_exprs.append(None) compiled_exprs.append(ret.force_expr) elif with_kwargs: keywords.append(asty.keyword(expr, arg=None, value=ret.force_expr)) elif with_kwargs and isinstance(expr, Keyword): try: value = next(exprs_iter) except StopIteration: raise self._syntax_error( expr, "Keyword argument {kw} needs a value.".format(kw=expr) ) if not expr: raise self._syntax_error( expr, "Can't call a function with the empty keyword" ) compiled_value = self.compile(value) ret += compiled_value arg = str(expr)[1:] keywords.append( asty.keyword(expr, arg=mangle(arg), value=compiled_value.force_expr) ) else: ret += self.compile(expr) compiled_exprs.append(ret.force_expr) return compiled_exprs, ret, keywords @builds_model(Lazy) def _compile_branch(self, exprs): """Make a branch out of an iterable of Result objects This generates a Result from the given sequence of Results, forcing each expression context as a statement before the next result is used. We keep the expression context of the last argument for the returned Result """ result = Result() last = None for node in exprs: if last is not None: result += last.expr_as_stmt() last = self.compile(node) result += last return result def _storeize(self, expr, name, func=None): """Return a new `name` object with an ast.Store() context""" if not func: func = ast.Store if isinstance(name, Result): if not name.is_expr(): raise self._syntax_error( expr, "Can't assign or delete a non-expression" ) name = name.expr if isinstance(name, (ast.Tuple, ast.List)): typ = type(name) new_elts = [] for x in name.elts: new_elts.append(self._storeize(expr, x, func)) new_name = typ(elts=new_elts) elif isinstance(name, ast.Name): new_name = ast.Name(id=name.id) if func == ast.Store: self.scope.assign(new_name) elif isinstance(name, ast.Subscript): new_name = ast.Subscript(value=name.value, slice=name.slice) elif isinstance(name, ast.Attribute): new_name = ast.Attribute(value=name.value, attr=name.attr) elif isinstance(name, ast.Starred): new_name = ast.Starred(value=self._storeize(expr, name.value, func)) else: raise self._syntax_error( expr, "Can't assign or delete a " + ( "constant" if isinstance(name, ast.Constant) else type(expr).__name__ ), ) new_name.ctx = func() ast.copy_location(new_name, name) return new_name def _nonconst(self, name): if str(name) in ("None", "True", "False"): raise self._syntax_error(name, "Can't assign to constant") return name def eval(self, model): return hy_eval( model, locals = self.module.__dict__, module = self.module, filename = self.filename, source = self.source, import_stdlib = False) @contextmanager def local_state(self): self.new_local_state() try: yield finally: self.local_state_stack.pop() @builds_model(Expression) def compile_expression(self, expr): # Perform macro expansions expr = macroexpand(expr, self.module, self) if isinstance(expr, (Result, ast.AST)): # Use this as-is. return expr elif not isinstance(expr, Expression): # Go through compile again if we have a different type of model. return self.compile(expr) if not expr: raise self._syntax_error( expr, "empty expressions are not allowed at top level" ) args = list(expr) root = args.pop(0) func = None if ( isinstance(root, Expression) and len(root) >= 2 and isinstance(root[0], Symbol) and not str(root[0]).strip(".") and root[1] == Symbol("None") ): # ((. None a1 a2) obj v1 v2) -> ((. obj a1 a2) v1 v2) # (The reader already parsed `.a1.a2` as `(. None a1 a2)`.) # Find the object we're calling the method on. i = 0 while i < len(args): if isinstance(args[i], Keyword): if i == 0 and len(args) == 1: break i += 2 elif is_unpack("iterable", args[i]): raise self._syntax_error( args[i], "can't call a method on an `unpack-iterable` form" ) elif is_unpack("mapping", args[i]): i += 1 else: break else: raise self._syntax_error(expr, "attribute access requires object") func = self.compile( Expression([Symbol("."), args.pop(i), *root[2:]]).replace(root) ) if is_annotate_expression(root): # Flatten and compile the annotation expression. ann_expr = Expression(root + args).replace(root) return self.compile_expression(ann_expr) if not func: func = self.compile(root) args, ret, keywords = self._compile_collect(args, with_kwargs=True) return ( func + ret + asty.Call(expr, func=func.expr, args=args, keywords=keywords) ) @builds_model(Integer, Float, Complex) def compile_numeric_literal(self, x): return asty.Constant(x, value = {Integer: int, Float: float, Complex: complex}[type(x)](x)) @builds_model(Symbol) def compile_symbol(self, symbol): if symbol == Symbol("..."): return asty.Constant(symbol, value=Ellipsis) # By this point, `symbol` should be either all dots or # dot-free. assert not symbol.strip(".") or "." not in symbol if mangle(symbol) in ("None", "False", "True"): return asty.Constant(symbol, value=ast.literal_eval(mangle(symbol))) return self.scope.access(asty.Name(symbol, id=mangle(symbol), ctx=ast.Load())) @builds_model(Keyword) def compile_keyword(self, obj): ret = Result() ret += asty.Call( obj, func=asty.Attribute( obj, value=asty.Attribute( obj, value=asty.Name(obj, id="hy", ctx=ast.Load()), attr="models", ctx=ast.Load(), ), attr="Keyword", ctx=ast.Load(), ), args=[asty.Constant(obj, value=obj.name)], keywords=[], ) return ret @builds_model(String, Bytes) def compile_string(self, string): return asty.Constant(string, value = (bytes if type(string) is Bytes else str)(string)) @builds_model(FComponent) def compile_fcomponent(self, fcomponent): conversion = ord(fcomponent.conversion) if fcomponent.conversion else -1 root, *rest = fcomponent value = self.compile(root) elts, ret, _ = self._compile_collect(rest) if elts: spec = asty.JoinedStr(fcomponent, values=elts) else: spec = None return ( value + ret + asty.FormattedValue( fcomponent, value=value.expr, conversion=conversion, format_spec=spec ) ) @builds_model(FString) def compile_fstring(self, fstring): elts, ret, _ = self._compile_collect(fstring) return ret + asty.JoinedStr(fstring, values=elts) @builds_model(List, Set) def compile_list(self, expression): elts, ret, _ = self._compile_collect(expression) node = {List: asty.List, Set: asty.Set}[type(expression)] return ret + node( expression, elts = elts, **({} if node is asty.Set else dict(ctx = ast.Load()))) @builds_model(Dict) def compile_dict(self, m): keyvalues, ret, _ = self._compile_collect(m, dict_display=True) return ret + asty.Dict(m, keys=keyvalues[::2], values=keyvalues[1::2]) @builds_model(Tuple) def compile_tuple(self, expression): elts, ret, _ = self._compile_collect(expression) return ret + asty.Tuple(expression, elts=elts, ctx=ast.Load()) def get_compiler_module(module=None, compiler=None, calling_frame=False): """Get a module object from a compiler, given module object, string name of a module, and (optionally) the calling frame; otherwise, raise an error.""" module = getattr(compiler, "module", None) or module if isinstance(module, str): module = importlib.import_module(mangle(module)) if calling_frame and not module: module = calling_module(n=2) if not inspect.ismodule(module): raise TypeError("Invalid module type: {}".format(type(module))) return module def hy_eval( hytree, locals, module=None, compiler=None, filename=None, source=None, import_stdlib=True, globals=None, extra_macros=None, ): module = get_compiler_module(module, compiler, True) # Does the Hy AST object come with its own information? filename = getattr(hytree, "filename", filename) or "" source = getattr(hytree, "source", source) _ast, expr = hy_compile( hytree, module, get_expr=True, compiler=compiler, filename=filename, source=source, import_stdlib=import_stdlib, extra_macros=extra_macros, ) if globals is None: globals = module.__dict__ # Two-step eval: eval() the body of the exec call eval(ast_compile(_ast, filename, "exec"), globals, locals) # Then eval the expression context and return that return eval(ast_compile(expr, filename, "eval"), globals, locals) def hy_eval_user(model, globals = None, locals = None, module = None, macros = None): # This function is advertised as `hy.eval`. """An equivalent of Python's :func:`eval` for evaluating Hy code. The chief difference is that the first argument should be a :ref:`model ` rather than source text. If you have a string of source text you want to evaluate, convert it to a model first with :hy:func:`hy.read` or :hy:func:`hy.read-many`:: (hy.eval '(+ 1 1)) ; => 2 (hy.eval (hy.read "(+ 1 1)")) ; => 2 The optional arguments ``globals`` and ``locals`` work as in the case of :func:`eval`. Another optional argument, ``module``, can be a module object or a string naming a module. The module's ``__dict__`` attribute can fill in for ``globals`` (and hence also for ``locals``) if ``module`` is provided but ``globals`` isn't, but the primary purpose of ``module`` is to control where macro calls are looked up. Without this argument, the calling module of ``hy.eval`` is used instead. :: (defmacro my-test-mac [] 3) (hy.eval '(my-test-mac)) ; => 3 (import hyrule) (hy.eval '(my-test-mac) :module hyrule) ; NameError (hy.eval '(list-n 3 1) :module hyrule) ; => [1 1 1] Finally, finer control of macro lookup can be achieved by passing in a dictionary of macros as the ``macros`` argument. The keys of this dictionary should be mangled macro names, and the values should be function objects to implement those macros. This is the same structure as is produced by :hy:func:`local-macros `, and in fact, ``(hy.eval … :macros (local-macros))`` is useful to make local macros visible to ``hy.eval``, which otherwise doesn't see them. :: (defn f [] (defmacro lmac [] 1) (hy.eval '(lmac)) ; NameError (print (hy.eval '(lmac) :macros (local-macros)))) ; => 1 (f) In any case, macros provided in this dictionary will shadow macros of the same name that are associated with the provided or implicit module. You can shadow a core macro, too, so be careful: there's no warning for this as there is in the case of :hy:func:`defmacro`.""" if locals is None: locals = globals hy_was = None if locals and 'hy' in locals: hy_was = (locals['hy'],) try: value = hy_eval( hytree = model, globals = globals, locals = (inspect.getargvalues(inspect.stack()[1][0]).locals if locals is None and module is None else locals), module = get_compiler_module(module, None, True), extra_macros = macros) finally: if locals is not None: if hy_was: # Restore the old value of `hy`. locals['hy'], = hy_was else: # Remove the implicitly added `hy` (if execution # reached far enough to add it). locals.pop('hy', None) return value def hy_compile( tree, module, root=ast.Module, get_expr=False, compiler=None, filename=None, source=None, import_stdlib=True, extra_macros=None, ): """Compile a hy.models.Object tree into a Python AST Module. Args: tree (Object): The Hy AST object to compile. module (Union[str, types.ModuleType]): Module, or name of the module, in which the Hy tree is evaluated. The module associated with `compiler` takes priority over this value. root (Type[ast.AST]): Root object for the Python AST tree. get_expr (bool): If true, return a tuple with `(root_obj, last_expression)`. compiler (Optional[HyASTCompiler]): An existing Hy compiler to use for compilation. Also serves as the `module` value when given. filename (Optional[str]): The filename corresponding to the source for `tree`. This will be overridden by the `filename` field of `tree`, if any; otherwise, it defaults to "". When `compiler` is given, its `filename` field value is always used. source (Optional[str]): A string containing the source code for `tree`. This will be overridden by the `source` field of `tree`, if any; otherwise, if `None`, an attempt will be made to obtain it from the module given by `module`. When `compiler` is given, its `source` field value is always used. extra_macros (Optional[dict]): Passed through to `HyASTCompiler`, if it's called. Returns: ast.AST: A Python AST tree """ module = get_compiler_module(module, compiler, False) filename = getattr(tree, "filename", filename) source = getattr(tree, "source", source) reader = getattr(tree, "reader", None) tree = as_model(tree) if not isinstance(tree, Object): raise TypeError( "`tree` must be a hy.models.Object or capable of " "being promoted to one" ) compiler = compiler or HyASTCompiler( module, filename = filename, source = source, extra_macros = extra_macros) with HyReader.using_reader(reader, create=False), compiler.scope: result = compiler.compile(tree) expr = result.force_expr if not get_expr: result += result.expr_as_stmt() result.stmts = list(map(ResolveOuterVars().visit, result.stmts)) body = [] if issubclass(root, ast.Module): # Pull out a single docstring and prepend to the resulting body. if ( result.stmts and isinstance(result.stmts[0], ast.Expr) and isinstance(result.stmts[0].value, ast.Constant) and isinstance(result.stmts[0].value.value, str) ): body += [result.stmts.pop(0)] # Pull out any __future__ imports, since they are required to be at the beginning. while ( result.stmts and isinstance(result.stmts[0], ast.ImportFrom) and result.stmts[0].module == "__future__" ): body += [result.stmts.pop(0)] # Import hy for runtime. if import_stdlib: body.append(ast.fix_missing_locations(ast.Import([ast.alias("hy", None)]))) body += result.stmts ret = root( body = body, **({} if root is ast.Interactive else dict(type_ignores = []))) if get_expr: expr = ast.Expression(body=expr) ret = (ret, expr) return ret hy-1.0.0/hy/completer.py000066400000000000000000000077011467401276000151300ustar00rootroot00000000000000import builtins import contextlib import os import re import sys from hy import mangle, unmangle # Lazily import `readline` to work around # https://github.com/python/cpython/issues/46927#issuecomment-1093418916 readline = None def init_readline(): global readline try: import readline except AttributeError as e: # https://github.com/pyreadline/pyreadline/issues/65 if "module 'collections' has no attribute 'Callable'" not in str(e): raise except ImportError: pass def maybe_unmangle(text): try: unmangled = unmangle(text) except KeyError: unmangled = text return (unmangled, text) def canonicalize(text): try: return unmangle(mangle(text)) if text else "" except KeyError: return text class Completer: def __init__(self, namespace={}): if not isinstance(namespace, dict): raise TypeError("namespace must be a dictionary") self.namespace = namespace self.path = [builtins.__dict__, namespace] namespace.setdefault("_hy_macros", {}) namespace.setdefault("_hy_reader_macros", {}) self.path.append(namespace["_hy_macros"]) def attr_matches(self, text): # Borrowed from IPython's completer m = re.match(r"(\S+(\.[\S]+)*)\.([\S]*)$", text) if m: expr, orig_attr = m.group(1, 3) attr = canonicalize(orig_attr) else: return [] try: sym, *syms = expr.split(".") obj = self.namespace[mangle(sym)] for sym in syms: obj = getattr(obj, mangle(sym)) words = map(maybe_unmangle, dir(obj)) except Exception: return [] matches = [ f"{expr}.{unmangled}" for unmangled, w in words if unmangled.startswith(attr) or w.startswith(orig_attr) ] return matches def global_matches(self, text): canonicalized = canonicalize(text) matches = [] for p in self.path: for k in p.keys(): if isinstance(k, str): unmangled, k = maybe_unmangle(k) if unmangled.startswith(canonicalized) or k.startswith(text): matches.append(unmangled) return matches def complete(self, text, state): if "." in text: matches = self.attr_matches(text) else: matches = self.global_matches(text) try: return matches[state] except IndexError: return None @contextlib.contextmanager def completion(completer=None): delims = "()[]{} " init_readline() if not readline: # We have nothing to do. Act like a null context manager. yield return if not completer: completer = Completer() if sys.platform == "darwin" and "libedit" in readline.__doc__: readline_bind = "bind ^I rl_complete" else: readline_bind = "tab: complete" readline.set_completer(completer.complete) readline.set_completer_delims(delims) history = os.environ.get("HY_HISTORY", os.path.expanduser("~/.hy-history")) readline.parse_and_bind("set blink-matching-paren on") # Save and clear any existing history. history_was = [] for _ in range(readline.get_current_history_length()): history_was.append(readline.get_history_item(1)) readline.remove_history_item(0) # Yes, the first item is numbered 1 by one method and 0 by the # other. try: readline.read_history_file(history) except OSError: pass readline.parse_and_bind(readline_bind) try: yield finally: try: readline.write_history_file(history) except OSError: pass # Restore the previously saved history. readline.clear_history() for item in history_was: readline.add_history(item) hy-1.0.0/hy/core/000077500000000000000000000000001467401276000135075ustar00rootroot00000000000000hy-1.0.0/hy/core/__init__.py000066400000000000000000000000001467401276000156060ustar00rootroot00000000000000hy-1.0.0/hy/core/hy_repr.hy000066400000000000000000000237611467401276000155320ustar00rootroot00000000000000(import math [isnan] fractions [Fraction] re datetime collections _collections_abc [dict-keys dict-values dict-items]) (setv _registry {}) (defn hy-repr-register [types f [placeholder None]] #[[``hy.repr-register`` lets you set the function that :hy:func:`hy.repr` calls to represent a type:: (defclass C) (hy.repr-register C (fn [x] "cuddles")) (hy.repr [1 (C) 2]) ; => "[1 cuddles 2]" Registered functions often call ``hy.repr`` themselves. ``hy.repr`` will automatically detect self-references, even deeply nested ones, and output ``"..."`` for them instead of calling the usual registered function. To use a placeholder other than ``"..."``, pass a string of your choice as the ``placeholder`` argument:: (defclass Container) (hy.repr-register Container :placeholder "HY THERE" (fn [x] f"(Container {(hy.repr x.value)})")) (setv container (Container)) (setv container.value container) (hy.repr container) ; => "(Container HY THERE)"]] (for [typ (if (isinstance types list) types [types])] (setv (get _registry typ) #(f placeholder)))) (setv _quoting False) (setv _seen (set)) (defn hy-repr [obj] #[[This function is Hy's equivalent of Python's :func:`repr`. It returns a string representing the input object in Hy syntax. :: (hy.repr [1 2 3]) ; => "[1 2 3]" (repr [1 2 3]) ; => "[1, 2, 3]" Like ``repr`` in Python, ``hy.repr`` can round-trip many kinds of values. Round-tripping implies that given an object ``x``, ``(hy.eval (hy.read (hy.repr x)))`` returns ``x``, or at least a value that's equal to ``x``. A notable exception to round-tripping is that if a model contains a non-model, the latter will be promoted to a model in the output:: (setv x (hy.models.List [5]) output (hy.repr x) y (hy.eval (hy.read output))) (print output) ; '[5] (print (type (get x 0))) ; (print (type (get y 0))) ; When ``hy.repr`` doesn't know how to represent an object, it falls back on :func:`repr`. Use :hy:func:`hy.repr-register` to add your own conversion function for a type instead.]] (setv [f placeholder] (.get _registry (type obj) [_base-repr None])) (global _quoting) (setv started-quoting False) (when (and (not _quoting) (isinstance obj hy.models.Object) (not (isinstance obj hy.models.Keyword))) (setv _quoting True) (setv started-quoting True)) (setv oid (id obj)) (when (in oid _seen) (return (if (is placeholder None) "..." placeholder))) (.add _seen oid) (try (+ (if started-quoting "'" "") (f obj)) (finally (.discard _seen oid) (when started-quoting (setv _quoting False))))) (hy-repr-register [tuple hy.models.Tuple] (fn [x] (+ "#(" (_cat x) ")"))) (hy-repr-register dict :placeholder "{...}" (fn [x] (setv text (.join " " (gfor [k v] (.items x) (+ (hy-repr k) " " (hy-repr v))))) (+ "{" text "}"))) (hy-repr-register hy.models.Dict :placeholder "{...}" (fn [x] (setv text (.join " " (gfor [i item] (enumerate x) (+ (if (and i (= (% i 2) 0)) " " "") (hy-repr item))))) (+ "{" text "}"))) (hy-repr-register hy.models.Expression (fn [x] (setv syntax { 'quote "'" 'quasiquote "`" 'unquote "~" 'unquote-splice "~@" 'unpack-iterable "#* " 'unpack-mapping "#** "}) (setv x0 (when x (get x 0))) (setv x1 (when (> (len x) 1) (get x 1))) (cond (and (>= (len x) 3) (all (gfor e x (is (type e) hy.models.Symbol))) (or (= x0 '.) (and (= x1 'None) (not (.strip (str x0) "."))))) (+ (if (= x1 'None) (str x0) "") (.join "." (map hy-repr (cut x (if (= x1 'None) 2 1) None)))) (and (= (len x) 2) (in x0 syntax)) (if (and (= x0 'unquote) (isinstance x1 hy.models.Symbol) (.startswith x1 "@")) ; This case is special because `~@b` would be wrongly ; interpreted as `(unquote-splice b)` instead of `(unquote @b)`. (+ "~ " (hy-repr x1)) (+ (get syntax x0) (hy-repr x1))) True (+ "(" (_cat x) ")")))) (hy-repr-register [hy.models.Symbol hy.models.Keyword] str) (hy-repr-register [hy.models.String str hy.models.Bytes bytes] (fn [x] (setv r (.lstrip (_base-repr x) "ub")) (if (is-not None (getattr x "brackets" None)) f"#[{x.brackets}[{x}]{x.brackets}]" (+ (if (isinstance x bytes) "b" "") (if (.startswith "\"" r) ; If Python's built-in repr produced a double-quoted string, use ; that. r ; Otherwise, we have a single-quoted string, which isn't valid Hy, so ; convert it. (+ "\"" (.replace (cut r 1 -1) "\"" "\\\"") "\"")))))) (hy-repr-register bytearray (fn [x] f"(bytearray {(hy-repr (bytes x))})")) (hy-repr-register bool str) (hy-repr-register [hy.models.Float float] (fn [x] (setv fx (float x)) (cond (isnan fx) "NaN" (= fx Inf) "Inf" (= fx -Inf) "-Inf" True (_base-repr x)))) (hy-repr-register [hy.models.Complex complex] (fn [x] (.replace (.replace (.strip (_base-repr x) "()") "inf" "Inf") "nan" "NaN"))) (hy-repr-register [range slice] (fn [x] (setv op (. (type x) __name__)) (defn r [attr] (hy.repr (getattr x attr))) (if (= x.step (if (is (type x) range) 1 None)) (if (= x.start (if (is (type x) range) 0 None)) f"({op} {(r "stop")})" f"({op} {(r "start")} {(r "stop")})") f"({op} {(r "start")} {(r "stop")} {(r "step")})"))) (hy-repr-register hy.models.FComponent (fn [x] (+ "{" (hy-repr (get x 0)) (if x.conversion f" !{x.conversion}" "") (if (> (len x) 1) (+ " :" (if (isinstance (get x 1) hy.models.String) (get x 1) (hy-repr (get x 1)))) "") "}"))) (hy-repr-register hy.models.FString (fn [fstring] (if (is-not None fstring.brackets) (+ "#[" fstring.brackets "[" #* (lfor component fstring (if (isinstance component hy.models.String) (.replace (.replace (str component) "{" "{{") "}" "}}") (hy-repr component))) "]" fstring.brackets "]") (+ "f\"" #* (lfor component fstring :setv s (hy-repr component) (if (isinstance component hy.models.String) (.replace (.replace (cut s 1 -1) "{" "{{") "}" "}}") s)) "\"")))) (setv _matchobject-type (type (re.match "" ""))) (hy-repr-register _matchobject-type (fn [x] (.format "<{}.{} object; :span {} :match {}>" _matchobject-type.__module__ _matchobject-type.__name__ (hy-repr (.span x)) (hy-repr (.group x 0))))) (hy-repr-register re.Pattern (fn [x] (setv flags (& x.flags (bnot re.UNICODE))) ; We remove re.UNICODE since it's redundant with the type ; of the pattern, and Python's `repr` omits it, too. (.format "(re.compile {}{})" (hy-repr x.pattern) (if flags (+ " " (do ; Convert `flags` from an integer to a list of names. (setv flags (re.RegexFlag flags)) (setv flags (lfor f (sorted re.RegexFlag) :if (& f flags) (+ "re." f.name))) (if (= (len flags) 1) (get flags 0) (.format "(| {})" (.join " " flags))))) "")))) (hy-repr-register datetime.datetime (fn [x] (.format "(datetime.datetime {}{})" (_strftime-0 x "%Y %m %d %H %M %S") (_repr-time-innards x)))) (hy-repr-register datetime.date (fn [x] (_strftime-0 x "(datetime.date %Y %m %d)"))) (hy-repr-register datetime.time (fn [x] (.format "(datetime.time {}{})" (_strftime-0 x "%H %M %S") (_repr-time-innards x)))) (defn _repr-time-innards [x] (.rstrip (+ " " (.join " " (filter (fn [x] x) [ (when x.microsecond (str x.microsecond)) (when (is-not x.tzinfo None) (+ ":tzinfo " (hy-repr x.tzinfo))) (when x.fold (+ ":fold " (hy-repr x.fold)))]))))) (defn _strftime-0 [x fmt] ; Remove leading 0s in `strftime`. This is a substitute for the `-` ; flag for when Python isn't built with glibc. (re.sub r"(\A| )0([0-9])" r"\1\2" (.strftime x fmt))) (hy-repr-register collections.ChainMap (fn [x] (.format "(ChainMap {})" (_cat x.maps)))) (hy-repr-register collections.Counter (fn [x] (.format "(Counter {})" (hy-repr (dict x))))) (hy-repr-register collections.OrderedDict (fn [x] (.format "(OrderedDict {})" (hy-repr (list (.items x)))))) (hy-repr-register collections.defaultdict (fn [x] (.format "(defaultdict {} {})" (hy-repr x.default-factory) (hy-repr (dict x))))) (hy-repr-register Fraction (fn [x] f"(Fraction {x.numerator} {x.denominator})")) (for [[types fmt] [ [[list hy.models.List] "[...]"] [[set hy.models.Set] "#{...}"] [frozenset "(frozenset #{...})"] [collections.deque "(deque [...])"] [dict-keys "(dict-keys [...])"] [dict-values "(dict-values [...])"] [dict-items "(dict-items [...])"]]] (defn mkrepr [fmt] (fn [x] (.replace fmt "..." (_cat x) 1))) (hy-repr-register types :placeholder fmt (mkrepr fmt))) (defn _cat [obj] (.join " " (map hy-repr obj))) (defn _base-repr [x] (when (and (isinstance x tuple) (hasattr x "_fields")) ; It's a named tuple. (We can't use `isinstance` or so because ; generated named-tuple classes don't actually inherit from ; collections.namedtuple.) (return (.format "({} {})" (. (type x) __name__) (.join " " (gfor [k v] (zip x._fields x) (+ ":" k " " (hy-repr v))))))) (when (not (isinstance x hy.models.Object)) (return (repr x))) ; Call (.repr x) using the first class of x that doesn't inherit from ; hy.models.Object. (.__repr__ (next (gfor t (. (type x) __mro__) :if (not (issubclass t hy.models.Object)) t)) x)) hy-1.0.0/hy/core/macros.hy000066400000000000000000000151151467401276000153400ustar00rootroot00000000000000;;; Hy core macros ;;; These macros form the hy language ;;; They are automatically required in every module, except inside hy.core (defmacro cond [#* args] "Shorthand for a nested sequence of :hy:func:`if` forms, like an :py:keyword:`if`-:py:keyword:`elif`-:py:keyword:`else` ladder in Python. Syntax such as :: (cond condition1 result1 condition2 result2) is equivalent to :: (if condition1 result1 (if condition2 result2 None)) Notice that ``None`` is returned when no conditions match; use ``True`` as the final condition to change the fallback result. Use :hy:func:`do` to execute several forms as part of a single condition or result. With no arguments, ``cond`` returns ``None``. With an odd number of arguments, ``cond`` raises an error." (defn _cond [args] (if args `(if ~(get args 0) ~(get args 1) ~(_cond (cut args 2 None))) 'None)) (if (% (len args) 2) (raise (TypeError "`cond` needs an even number of arguments")) (_cond args))) (defmacro when [test #* body] #[[Shorthand for ``(if test (do …) None)``. See :hy:func:`if`. For a logically negated version, see Hyrule's :hy:func:`unless `. :: (when panic (log.write panic) (print "Process returned:" panic.msg) (return panic))]] `(if ~test (do ~@body) None)) (defmacro defreader [_hy_compiler key #* body] "Define a reader macro, at both compile-time and run-time. After the name, all arguments are body forms: there is no parameter list as for ``defmacro``, since it's up to the reader macro to decide how to parse the source text following its call position. See :ref:`reader-macros` for details and examples." (when (not (isinstance _hy_compiler.scope hy.scoping.ScopeGlobal)) (raise (_hy_compiler._syntax-error _hy_compiler.this f"Cannot define reader macro outside of global scope."))) (when (not (isinstance key hy.models.Symbol)) (raise (ValueError f"expected a name, but got {key}"))) (if (and body (isinstance (get body 0) hy.models.String)) (setv [docstr #* body] body) (setv docstr None)) (setv dispatch-key (str key)) `(do (eval-and-compile (hy.macros.reader-macro ~dispatch-key (fn [&reader &key] ~@(if docstr [docstr] []) ~@body))) (eval-when-compile (setv (get (. (hy.reader.HyReader.current-reader) reader-macros) ~dispatch-key) (get _hy_reader_macros ~dispatch-key))))) (defmacro get-macro [_hy_compiler arg1 [arg2 None]] "Get the function object used to implement a macro. This works for all sorts of macros: core macros, global (i.e., module-level) macros, local macros, and reader macros. For regular (non-reader) macros, ``get-macro`` is called with one argument, a symbol or string literal, which can be premangled or not according to taste. For reader macros, this argument must be preceded by the literal keyword ``:reader`` (and note that the hash mark, ``#``, is not included in the name of the reader macro). :: (get-macro my-macro) (get-macro :reader my-reader-macro) Except when retrieving a local macro, ``get-macro`` expands to a :hy:func:`get ` form on the appropriate object, such as ``_hy_macros``, selected at the time of expanding ``get-macro``. This means you can say ``(del (get-macro …))``, perhaps wrapped in :hy:func:`eval-and-compile` or :hy:func:`eval-when-compile`, to delete a macro, but it's easy to get confused by the order of evaluation and number of evaluations. For more predictable results in complex situations, use ``(del (get …))`` directly instead of ``(del (get-macro …))``." (import builtins) (setv [name reader?] (cond (= arg1 ':reader) [(str arg2) True] (isinstance arg1 hy.models.Expression) [(hy.mangle (.join "." (cut arg1 1 None))) False] True [(hy.mangle arg1) False])) (setv namespace (if reader? "_hy_reader_macros" "_hy_macros")) (cond (and (not reader?) (setx local (.get (_local-macros _hy_compiler) name))) local (in name (getattr _hy_compiler.module namespace {})) `(get ~(hy.models.Symbol namespace) ~name) (in name (getattr builtins namespace {})) `(get (. hy.I.builtins ~(hy.models.Symbol namespace)) ~name) True (raise (NameError (.format "no such {}macro: {!r}" (if reader? "reader " "") name))))) (defmacro local-macros [_hy_compiler] #[[Expands to a dictionary mapping the mangled names of local macros to the function objects used to implement those macros. Thus, ``local-macros`` provides a rough local equivalent of ``_hy_macros``. :: (defn f [] (defmacro m [] "This is the docstring for the macro `m`." 1) (help (get (local-macros) "m"))) (f) The equivalency is rough in the sense that ``local-macros`` returns a literal dictionary, not a preexisting object that Hy uses for resolving macro names. So, modifying the dictionary will have no effect. See also :hy:func:`get-macro `.]] (_local-macros _hy_compiler)) (defn _local_macros [_hy_compiler] (setv seen #{}) (dfor state _hy_compiler.local_state_stack m (get state "macros") :if (not-in m seen) :do (.add seen m) m (hy.models.Symbol (hy.macros.local-macro-name m)))) (defmacro export [#* args] "A convenience macro for defining ``__all__`` and ``_hy_export_macros``, which control which Python objects and macros (respectively) are collected by ``*`` imports in :hy:func:`import` and :hy:func:`require` (respectively). ``export`` allows you to provide the names as symbols instead of strings, and it calls :hy:func:`hy.mangle` for you on each name. The syntax is ``(export objects macros)``, where ``objects`` refers to Python objects and ``macros`` to macros. Keyword arguments are allowed. For example, :: (export :objects [my-fun MyClass] :macros [my-macro]) exports the function ``my-fun``, the class ``MyClass``, and the macro ``my-macro``." (defn f [[objects None] [macros None]] `(do ~(when (is-not objects None) `(setv __all__ ~(lfor x objects (hy.models.String (hy.mangle x))))) ~(when (is-not macros None) `(setv _hy_export_macros ~(lfor x macros (hy.models.String (hy.mangle x))))))) (hy.eval `(f ~@(gfor a (map hy.as-model args) (if (isinstance a hy.models.Keyword) a (if (isinstance a hy.models.List) (lfor x a (hy.models.String x)) (raise (TypeError "arguments must be keywords or lists of symbols")))))))) hy-1.0.0/hy/core/result_macros.py000066400000000000000000002077431467401276000167600ustar00rootroot00000000000000"""This file contains Hy's core macros that are written in Python and return compiler Result objects (or Python AST objects) rather than Hy model trees. These macros serve the role of special forms in other Lisps: all ordinary macros should eventually compile down to one of these, or to one of the model builders in hy.compiler.""" # ------------------------------------------------ # * Imports # ------------------------------------------------ import ast import textwrap from contextlib import nullcontext from itertools import dropwhile from funcparserlib.parser import finished, forward_decl, many, maybe, oneplus, some from hy.compat import PY3_11, PY3_12 from hy.compiler import Result, asty, mkexpr from hy.errors import HyEvalError, HyInternalError, HyTypeError from hy.macros import pattern_macro, require, require_reader, local_macro_name from hy.model_patterns import ( FORM, KEYWORD, LITERAL, STR, SYM, Tag, braces, brackets, dolike, in_tuple, keepsym, notpexpr, parse_if, pexpr, sym, tag, times, unpack, ) from hy.models import ( Bytes, Complex, Dict, Expression, FComponent, Float, FString, Integer, Keyword, List, Sequence, String, Symbol, Tuple, as_model, is_unpack, ) from hy.reader import mangle from hy.scoping import OuterVar, ScopeFn, ScopeGen, ScopeLet, is_function_scope, is_inside_function_scope, nearest_python_scope # ------------------------------------------------ # * Helpers # ------------------------------------------------ Inf = float("inf") def pvalue(root, wanted): return pexpr(sym(root) + wanted) >> (lambda x: x[0]) def maybe_annotated(target): return ( pexpr(sym("annotate") + target + FORM).named('`annotate` form') | (target >> (lambda x: (x, None)))) def dotted(name): return Expression(map(Symbol, [".", *name.split(".")])) type_params = sym(":tp") + brackets(many( maybe_annotated(SYM) | unpack("either", Symbol))) def digest_type_params(compiler, tp): "Return a `type_params` attribute for `FunctionDef` etc." if tp: if not PY3_12: compiler._syntax_error(tp, "`:tp` requires Python 3.12 or later") tp, = tp elif not PY3_12: return {} return dict(type_params = [ asty.TypeVarTuple(x[1], name = mangle(x[1])) if is_unpack("iterable", x) else asty.ParamSpec(x[1], name = mangle(x[1])) if is_unpack("mapping", x) else asty.TypeVar(x[0], name = mangle(x[0]), bound = x[1] and compiler.compile(x[1]).force_expr) for x in (tp or [])]) # ------------------------------------------------ # * Fundamentals # ------------------------------------------------ @pattern_macro("do", [many(FORM)]) def compile_do(compiler, expr, root, body): return compiler._compile_branch(body) @pattern_macro(["eval-and-compile", "eval-when-compile", "do-mac"], [many(FORM)]) def compile_eval_foo_compile(compiler, expr, root, body): new_expr = Expression([Symbol("do").replace(expr[0])]).replace(expr) try: value = compiler.eval(new_expr + body) except HyInternalError: # Unexpected "meta" compilation errors need to be treated # like normal (unexpected) compilation errors at this level # (or the compilation level preceding this one). raise except Exception as e: # These could be expected Hy language errors (e.g. syntax errors) # or regular Python runtime errors that do not signify errors in # the compilation *process* (although compilation did technically # fail). # We wrap these exceptions and pass them through. raise HyEvalError(str(e), compiler.filename, body, compiler.source) return ( compiler.compile(as_model(value).replace(expr)) if root == "do-mac" else compiler._compile_branch(body) if root == "eval-and-compile" else Result() ) @pattern_macro(["py", "pys"], [STR]) def compile_inline_python(compiler, expr, root, code): exec_mode = root == "pys" try: o = asty.parse( expr, textwrap.dedent(code) if exec_mode else "(" + code + "\n)", compiler.filename, "exec" if exec_mode else "eval", ).body except (SyntaxError, ValueError) as e: raise compiler._syntax_error( expr, "Python parse error in '{}': {}".format(root, e) ) return Result(stmts=o) if exec_mode else o @pattern_macro("pragma", [many(KEYWORD + FORM)]) def compile_pragma(compiler, expr, root, kwargs): for kw, value in kwargs: if kw == Keyword("warn-on-core-shadow"): compiler.local_state_stack[-1]['warn_on_core_shadow'] = ( bool(compiler.eval(value))) else: raise compiler._syntax_error(kw, f"Unknown pragma `{kw}`. Perhaps it's implemented by a newer version of Hy.") return Result() # ------------------------------------------------ # * Quoting # ------------------------------------------------ @pattern_macro(["quote", "quasiquote"], [FORM]) def compile_quote(compiler, expr, root, arg): return compiler.compile(render_quoted_form(compiler, arg, level = Inf if root == "quote" else 0)[0]) # Only quasiquotes can unquote def render_quoted_form(compiler, form, level): """ Render a quoted form as a new hy Expression. `level` is the level of quasiquoting of the current form. We can unquote if level is 0. Returns a two-tuple (`expression`, `splice`). The `splice` return value is used to mark `unquote-splice`d forms. We need to distinguish them as want to concatenate them instead of just nesting them. """ op = None if isinstance(form, Expression) and form and isinstance(form[0], Symbol): op = mangle(form[0]).replace('_', '-') if op in ("unquote", "unquote-splice", "quasiquote"): if level == 0 and op != "quasiquote": if len(form) != 2: raise HyTypeError( "`%s' needs 1 argument, got %s" % op, len(form) - 1, compiler.filename, form, compiler.source, ) return form[1], op == "unquote-splice" level += 1 if op == "quasiquote" else -1 name = form.__class__.__name__ body = [form] if isinstance(form, Sequence): contents = [] for x in form: f_contents, splice = render_quoted_form(compiler, x, level) if splice: if is_unpack("iterable", f_contents): raise compiler._syntax_error(f_contents, "`unpack-iterable` is not allowed here") f_contents = Expression( [ Symbol("unpack-iterable"), Expression([Symbol("or"), f_contents, List()]), ] ) contents.append(f_contents) body = [List(contents)] if isinstance(form, FString) and form.brackets is not None: body.extend([Keyword("brackets"), String(form.brackets)]) elif isinstance(form, FComponent) and form.conversion is not None: body.extend([Keyword("conversion"), String(form.conversion)]) elif isinstance(form, Symbol): body = [String(form), Keyword("from_parser"), Symbol("True")] elif isinstance(form, Keyword): body = [String(form.name), Keyword("from_parser"), Symbol("True")] elif isinstance(form, String): if form.brackets is not None: body.extend([Keyword("brackets"), String(form.brackets)]) return (Expression([dotted("hy.models." + name), *body]).replace(form), False) # ------------------------------------------------ # * Python operators # ------------------------------------------------ @pattern_macro(["not", "bnot"], [FORM], shadow=True) def compile_unary_operator(compiler, expr, root, arg): ops = {"not": ast.Not, "bnot": ast.Invert} operand = compiler.compile(arg) return operand + asty.UnaryOp(expr, op=ops[root](), operand=operand.force_expr) @pattern_macro(["and", "or"], [many(FORM)], shadow=True) def compile_logical_or_and_and_operator(compiler, expr, operator, args): ops = {"and": (ast.And, True), "or": (ast.Or, None)} opnode, default = ops[operator] if len(args) == 0: return asty.Constant(expr[0], value=default) ret = None var = None # A temporary variable for assigning results to assignment = None # The current assignment to `var` stmts = None # The current statement list can_append = False # Whether the current expression is the compiled boolop def put(node, value): # Save the result of the operation so far to `var`. nonlocal var, assignment, can_append if var is None: var = compiler.get_anon_var() name = asty.Name(node, id=var, ctx=ast.Store()) ret.temp_variables.append(name) can_append = False return (assignment := asty.Assign(node, targets=[name], value=value)) def get(node): # Get the value of `var`, creating it if necessary. if var is None: stmts.append(put(node, ret.force_expr)) name = asty.Name(node, id=var, ctx=ast.Load()) ret.temp_variables.append(name) return name for value in map(compiler.compile, args): if ret is None: # This is the first iteration. Don't actually introduce a # `BoolOp` yet; the unary case doesn't need it. ret = value stmts = ret.stmts can_append = False elif value.stmts: # Save the result of the statements to the temporary # variable. Use an `if` statement to implement # short-circuiting from this point. node = value.stmts[0] cond = get(node) if operator == "or": # Negate the conditional. cond = asty.UnaryOp(node, op=ast.Not(), operand=cond) branch = asty.If(node, test=cond, body=value.stmts, orelse=[]) stmts.append(branch) stmts = branch.body stmts.append(put(node, value.force_expr)) else: # Add this value to the current `BoolOp`, or create a new # one if we don't have one. value = value.force_expr def enbool(expr): nonlocal can_append if can_append: expr.values.append(value) return expr can_append = True return asty.BoolOp(expr, op=opnode(), values=[expr, value]) if assignment: assignment.value = enbool(assignment.value) else: ret.expr = enbool(ret.expr) if var: ret.expr = get(expr) return ret c_ops = { "=": ast.Eq, "!=": ast.NotEq, "<": ast.Lt, "<=": ast.LtE, ">": ast.Gt, ">=": ast.GtE, "is": ast.Is, "is-not": ast.IsNot, "in": ast.In, "not-in": ast.NotIn, } c_ops = {mangle(k): v for k, v in c_ops.items()} def get_c_op(compiler, sym): k = mangle(sym) if k not in c_ops: raise compiler._syntax_error(sym, "Illegal comparison operator: " + str(sym)) return c_ops[k]() @pattern_macro(["=", "is", "<", "<=", ">", ">="], [oneplus(FORM)], shadow=True) @pattern_macro(["!=", "is-not", "in", "not-in"], [times(2, Inf, FORM)], shadow=True) def compile_compare_op_expression(compiler, expr, root, args): if len(args) == 1: return compiler.compile(args[0]) + asty.Constant(expr, value=True) ops = [get_c_op(compiler, root) for _ in args[1:]] exprs, ret, _ = compiler._compile_collect(args) return ret + asty.Compare(expr, left=exprs[0], ops=ops, comparators=exprs[1:]) @pattern_macro("chainc", [FORM, many(SYM + FORM)]) def compile_chained_comparison(compiler, expr, root, arg1, args): ret = compiler.compile(arg1) arg1 = ret.force_expr ops = [get_c_op(compiler, op) for op, _ in args] args, ret2, _ = compiler._compile_collect([x for _, x in args]) return ret + ret2 + asty.Compare(expr, left=arg1, ops=ops, comparators=args) # The second element of each tuple below is an aggregation operator # that's used for augmented assignment with three or more arguments. m_ops = { "+": (ast.Add, "+"), "/": (ast.Div, "*"), "//": (ast.FloorDiv, "*"), "*": (ast.Mult, "*"), "-": (ast.Sub, "+"), "%": (ast.Mod, None), "**": (ast.Pow, "**"), "<<": (ast.LShift, "+"), ">>": (ast.RShift, "+"), "|": (ast.BitOr, "|"), "^": (ast.BitXor, None), "&": (ast.BitAnd, "&"), "@": (ast.MatMult, "@"), } @pattern_macro(["+", "*", "|"], [many(FORM)], shadow=True) @pattern_macro(["-", "/", "&", "@"], [oneplus(FORM)], shadow=True) @pattern_macro(["**", "//", "<<", ">>"], [times(2, Inf, FORM)], shadow=True) @pattern_macro(["%", "^"], [times(2, 2, FORM)], shadow=True) def compile_maths_expression(compiler, expr, root, args): if len(args) == 0: # Return the identity element for this operator. return asty.Constant(expr, value=({"+": 0, "|": 0, "*": 1}[root])) if len(args) == 1: if root == "/": # Compute the reciprocal of the argument. args = [Integer(1).replace(expr), args[0]] elif root in ("+", "-"): # Apply unary plus or unary minus to the argument. op = {"+": ast.UAdd, "-": ast.USub}[root]() ret = compiler.compile(args[0]) return ret + asty.UnaryOp(expr, op=op, operand=ret.force_expr) else: # Return the argument unchanged. return compiler.compile(args[0]) op = m_ops[root][0] right_associative = root == "**" ret = compiler.compile(args[-1 if right_associative else 0]) for child in args[-2 if right_associative else 1 :: -1 if right_associative else 1]: left_expr = ret.force_expr ret += compiler.compile(child) right_expr = ret.force_expr if right_associative: left_expr, right_expr = right_expr, left_expr ret += asty.BinOp(expr, left=left_expr, op=op(), right=right_expr) return ret a_ops = {x + "=": v for x, v in m_ops.items()} @pattern_macro( [x for x, (_, v) in a_ops.items() if v is not None], [FORM, oneplus(FORM)] ) @pattern_macro( [x for x, (_, v) in a_ops.items() if v is None], [FORM, times(1, 1, FORM)] ) def compile_augassign_expression(compiler, expr, root, target, values): if len(values) > 1: return compiler.compile( mkexpr(root, [target], mkexpr(a_ops[root][1], rest=values)).replace(expr) ) op = a_ops[root][0] target = compiler._storeize(target, compiler.compile(target)) ret = compiler.compile(values[0]) return ret + asty.AugAssign(expr, target=target, value=ret.force_expr, op=op()) # ------------------------------------------------ # * Assignment, mutation, and annotation # ------------------------------------------------ @pattern_macro("setv", [many(maybe_annotated(FORM) + FORM)]) @pattern_macro("setx", [times(1, 1, SYM + FORM)]) def compile_def_expression(compiler, expr, root, decls): if not decls: return asty.Constant(expr, value=None) result = Result() is_assignment_expr = root == "setx" for decl in decls: if is_assignment_expr: ann = None name, value = decl else: (name, ann), value = decl result += compile_assign( compiler, ann, name, value, is_assignment_expr=is_assignment_expr ) return result @pattern_macro("let", [brackets(many(maybe_annotated(FORM) + FORM)), many(FORM)]) def compile_let(compiler, expr, root, bindings, body): res = Result() bindings = bindings[0] scope = compiler.scope.create(ScopeLet) for (target, ann), value in bindings: res += compile_assign(compiler, ann, target, value, let_scope=scope) with scope: return res + compiler.compile(mkexpr("do", *body).replace(expr)) @pattern_macro(["annotate"], [FORM, FORM]) def compile_basic_annotation(compiler, expr, root, target, ann): return compile_assign(compiler, ann, target, None) def compile_assign( compiler, ann, name, value, *, is_assignment_expr=False, let_scope=None ): # Ensure that assignment expressions have a result and no annotation. assert not is_assignment_expr or (value is not None and ann is None) annotate_only = value is None if annotate_only: result = Result() else: with let_scope or nullcontext(): result = compiler.compile(value) if let_scope: name = let_scope.add(name) ld_name = compiler.compile(name) if result.temp_variables and isinstance(name, Symbol): result.rename(compiler, compiler._nonconst(name)) if not is_assignment_expr: # Throw away .expr to ensure that (setv ...) returns None. result.expr = None else: st_name = compiler._storeize(name, ld_name) if ann is not None: ann_result = compiler.compile(ann) result = ann_result + result target = dict(target = st_name) if is_assignment_expr: node = asty.NamedExpr elif ann is not None: node = lambda x, **kw: asty.AnnAssign( x, annotation=ann_result.force_expr, simple=int(isinstance(name, Symbol)), **kw, ) else: node = asty.Assign target = dict(targets = [st_name]) result += node( name if hasattr(name, "start_line") else result, value=result.force_expr if not annotate_only else None, **target ) return result @pattern_macro(((3, 12), "deftype"), [maybe(type_params), SYM, FORM]) def compile_deftype(compiler, expr, root, tp, name, value): return asty.TypeAlias(expr, name = asty.Name(name, id = mangle(name), ctx = ast.Store()), value = compiler.compile(value).force_expr, **digest_type_params(compiler, tp)) @pattern_macro(["global", "nonlocal"], [many(SYM)]) def compile_global_or_nonlocal(compiler, expr, root, syms): if not syms: return asty.Pass(expr) names = [mangle(s) for s in syms] if root == "global": ret = asty.Global(expr, names=names) else: ret = OuterVar(expr, compiler.scope, names) try: compiler.scope.define_nonlocal(ret, root) except SyntaxError as e: raise compiler._syntax_error(expr, e.msg) return ret if syms else Result() @pattern_macro("del", [many(FORM)]) def compile_del_expression(compiler, expr, name, args): if not args: return asty.Pass(expr) del_targets = [] ret = Result() for target in args: compiled_target = compiler.compile(target) ret += compiled_target del_targets.append(compiler._storeize(target, compiled_target, ast.Del)) return ret + asty.Delete(expr, targets=del_targets) # ------------------------------------------------ # * Subsetting # ------------------------------------------------ @pattern_macro("get", [FORM, oneplus(FORM)], shadow=True) def compile_index_expression(compiler, expr, name, obj, indices): indices, ret, _ = compiler._compile_collect(indices) ret += compiler.compile(obj) for ix in indices: ret += asty.Subscript( expr, value=ret.force_expr, slice=ast.Index(value=ix), ctx=ast.Load() ) return ret notsym = lambda *dissallowed: some( lambda x: isinstance(x, Symbol) and str(x) not in dissallowed ) @pattern_macro( ".", [ FORM, many( SYM | brackets(FORM) | pexpr(notsym("unpack-iterable", "unpack-mapping"), many(FORM)) ), ], ) def compile_attribute_access(compiler, expr, name, invocant, keys): ret = compiler.compile(invocant) for attr in keys: if isinstance(attr, Symbol): ret += asty.Attribute( attr, value=ret.force_expr, attr=mangle(attr), ctx=ast.Load() ) elif isinstance(attr, Expression): root, args = attr func = asty.Attribute( root, value=ret.force_expr, attr=mangle(root), ctx=ast.Load() ) args, funcret, keywords = compiler._compile_collect(args, with_kwargs=True) ret += ( funcret + func + asty.Call(expr, func=func, args=args, keywords=keywords) ) else: # attr is a hy List compiled_attr = compiler.compile(attr[0]) ret = ( compiled_attr + ret + asty.Subscript( attr, value=ret.force_expr, slice=ast.Index(value=compiled_attr.force_expr), ctx=ast.Load(), ) ) return ret @pattern_macro("cut", [FORM, maybe(FORM), maybe(FORM), maybe(FORM)]) def compile_cut_expression(compiler, expr, name, obj, lower, upper, step): ret = [Result()] def c(e): ret[0] += compiler.compile(e) return ret[0].force_expr if upper is None: # cut with single index is an upper bound, # this is consistent with slice and islice upper = lower lower = Symbol("None") s = asty.Subscript( expr, value=c(obj), slice=asty.Slice(expr, lower=c(lower), upper=c(upper), step=c(step)), ctx=ast.Load(), ) return ret[0] + s @pattern_macro("unpack-iterable", [FORM]) def compile_unpack_iterable(compiler, expr, root, arg): ret = compiler.compile(arg) ret += asty.Starred(expr, value=ret.force_expr, ctx=ast.Load()) return ret # ------------------------------------------------ # * `if` # ------------------------------------------------ @pattern_macro("if", [FORM, FORM, FORM]) def compile_if(compiler, expr, _, cond, body, orel_expr): cond = compiler.compile(cond) body = compiler.compile(body) nested = root = False orel = Result() if ( isinstance(orel_expr, Expression) and isinstance(orel_expr[0], Symbol) and orel_expr[0] == Symbol("if*") ): # Nested ifs: don't waste temporaries root = compiler.temp_if is None nested = True compiler.temp_if = compiler.temp_if or compiler.get_anon_var() orel = compiler.compile(orel_expr) if not cond.stmts and isinstance(cond.force_expr, ast.Name): name = cond.force_expr.id branch = None if name == "True": branch = body elif name in ("False", "None"): branch = orel if branch is not None: if compiler.temp_if and branch.stmts: name = asty.Name(expr, id=mangle(compiler.temp_if), ctx=ast.Store()) branch += asty.Assign(expr, targets=[name], value=body.force_expr) return branch # We want to hoist the statements from the condition ret = cond if body.stmts or orel.stmts: # We have statements in our bodies # Get a temporary variable for the result storage var = compiler.temp_if or compiler.get_anon_var() name = asty.Name(expr, id=mangle(var), ctx=ast.Store()) # Store the result of the body body += asty.Assign(expr, targets=[name], value=body.force_expr) # and of the else clause if not nested or not orel.stmts or (not root and var != compiler.temp_if): orel += asty.Assign(expr, targets=[name], value=orel.force_expr) # Then build the if ret += asty.If(expr, test=ret.force_expr, body=body.stmts, orelse=orel.stmts) # And make our expression context our temp variable expr_name = asty.Name(expr, id=mangle(var), ctx=ast.Load()) ret += Result(expr=expr_name, temp_variables=[expr_name, name]) else: # Just make that an if expression ret += asty.IfExp( expr, test=ret.force_expr, body=body.force_expr, orelse=orel.force_expr ) if root: compiler.temp_if = None return ret # ------------------------------------------------ # * The `for` family # ------------------------------------------------ loopers = many( tag("setv", sym(":setv") + FORM + FORM) | tag("if", sym(":if") + FORM) | tag("do", sym(":do") + FORM) | tag("afor", sym(":async") + FORM + FORM) | tag("for", FORM + FORM) ) @pattern_macro(["for"], [ brackets(loopers, name = 'square-bracketed loop clauses'), many(notpexpr("else")) + maybe(dolike("else"))]) @pattern_macro(["lfor", "sfor", "gfor"], [loopers, FORM]) @pattern_macro(["dfor"], [loopers, finished]) # Here `finished` is a hack replacement for FORM + FORM: # https://github.com/vlasovskikh/funcparserlib/issues/75 def compile_comprehension(compiler, expr, root, parts, final): node_class = { "for": asty.For, "lfor": asty.ListComp, "dfor": asty.DictComp, "sfor": asty.SetComp, "gfor": asty.GeneratorExp, }[root] is_for = root == "for" ctx = nullcontext() if is_for else compiler.scope.create(ScopeGen) mac_con = nullcontext() if is_for else compiler.local_state() with mac_con, ctx as scope: # Compile the parts. if is_for: parts = parts[0] if node_class is asty.DictComp: if not (parts and parts[-1].tag == "for"): raise compiler._syntax_error( parts[-1] if parts else parts, "`dfor` must end with key and value forms", ) final = parts.pop().value if not parts: return Result( expr=asty.parse( expr, { asty.For: "None", asty.ListComp: "[]", asty.DictComp: "{}", asty.SetComp: "{1}.__class__()", asty.GeneratorExp: "(_ for _ in [])", }[node_class], ) .body[0] .value ) new_parts = [] for p in parts: if p.tag in ("if", "do"): tag_value = compiler.compile(p.value) else: tag_value = [ compiler._storeize(p.value[0], compiler.compile(p.value[0])), compiler.compile(p.value[1]), ] if not is_for: scope.iterator(tag_value[0]) new_parts.append(Tag(p.tag, tag_value)) parts = new_parts orel = [] if is_for: # Get the `else`. body, else_expr = final if else_expr is not None: orel.append(compiler._compile_branch(else_expr)) orel[0] += orel[0].expr_as_stmt() else: # Get the final value (and for dictionary # comprehensions, the final key). if node_class is asty.DictComp: key, elt = map(compiler.compile, final) else: key = None elt = compiler.compile(final) # Produce a result. if ( is_for or elt.stmts or (key is not None and key.stmts) or any( p.tag == "do" or ( p.value[1].stmts if p.tag in ("for", "afor", "setv") else p.value.stmts ) for p in parts ) ): # The desired comprehension can't be expressed as a # real Python comprehension. We'll write it as a nested # loop in a function instead. def f(parts): # This function is called recursively to construct # the nested loop. if not parts: if is_for: if body: bd = compiler._compile_branch(body) return bd + bd.expr_as_stmt() return Result(stmts=[asty.Pass(expr)]) if node_class is asty.DictComp: ret = key + elt val = asty.Tuple( key, ctx=ast.Load(), elts=[key.force_expr, elt.force_expr] ) else: ret = elt val = elt.force_expr return ret + asty.Expr(elt, value=asty.Yield(elt, value=val)) (tagname, v), parts = parts[0], parts[1:] if tagname in ("for", "afor"): orelse = orel and orel.pop().stmts node = asty.AsyncFor if tagname == "afor" else asty.For return v[1] + node( v[1], target=v[0], iter=v[1].force_expr, body=f(parts).stmts, orelse=orelse, ) elif tagname == "setv": return ( v[1] + asty.Assign(v[1], targets=[v[0]], value=v[1].force_expr) + f(parts) ) elif tagname == "if": return v + asty.If( v, test=v.force_expr, body=f(parts).stmts, orelse=[] ) elif tagname == "do": return v + v.expr_as_stmt() + f(parts) else: raise ValueError("can't happen") if is_for: return f(parts) fname = compiler.get_anon_var() # Define the generator function. stmts = [] ret = Result() assignment_names = scope.finalize() if scope.exposing_assignments and assignment_names: # expose inner assignments to outer scope unlocal_type = ( asty.Nonlocal if is_inside_function_scope(scope.parent) else asty.Global ) stmts.append(unlocal_type(expr, names=assignment_names)) # create a fake assignment statement so python places these # names in the immediately outer scope if_body = [] if scope.nonlocal_vars: if_body.append( asty.Nonlocal(expr, names=list(sorted(scope.nonlocal_vars))) ) assignments = asty.Tuple( expr, elts=[ asty.Name(expr, id=var, ctx=ast.Store()) for var in assignment_names ], ctx=ast.Store(), ) if_body.append( asty.Assign( expr, targets=[assignments], value=asty.Constant(expr, value=None), ) ) ret += asty.If( expr, test=asty.Constant(expr, value=False), body=if_body, orelse=[] ) ret += asty.FunctionDef( expr, name=fname, args=ast.arguments( args=[], vararg=None, kwarg=None, posonlyargs=[], kwonlyargs=[], kw_defaults=[], defaults=[], ), body=stmts + f(parts).stmts, decorator_list=[], **({"type_params": []} if PY3_12 else {}), ) # Immediately call the new function. Unless the user asked # for a generator, wrap the call in `[].__class__(...)` or # `{}.__class__(...)` or `{1}.__class__(...)` to get the # right type. We don't want to just use e.g. `list(...)` # because the name `list` might be rebound. return ret + Result( expr=asty.parse( expr, "{}({}())".format( { asty.ListComp: "[].__class__", asty.DictComp: "{}.__class__", asty.SetComp: "{1}.__class__", asty.GeneratorExp: "", }[node_class], fname, ), ) .body[0] .value ) # We can produce a real comprehension. generators = [] for tagname, v in parts: if tagname in ("for", "afor"): generators.append( ast.comprehension( target=v[0], iter=v[1].expr, ifs=[], is_async=int(tagname == "afor"), ) ) elif tagname == "setv": generators.append( ast.comprehension( target=v[0], iter=asty.Tuple(v[1], elts=[v[1].expr], ctx=ast.Load()), ifs=[], is_async=0, ) ) elif tagname == "if": generators[-1].ifs.append(v.expr) else: raise ValueError("can't happen") if node_class is asty.DictComp: return asty.DictComp( expr, key=key.expr, value=elt.expr, generators=generators ) return node_class(expr, elt=elt.expr, generators=generators) # ------------------------------------------------ # * More looping # ------------------------------------------------ @pattern_macro(["while"], [FORM, many(notpexpr("else")), maybe(dolike("else"))]) def compile_while_expression(compiler, expr, root, cond, body, else_expr): cond_compiled = compiler.compile(cond) body = compiler._compile_branch(body) body += body.expr_as_stmt() body_stmts = body.stmts or [asty.Pass(expr)] if cond_compiled.stmts: # We need to ensure the statements for the condition are # executed on every iteration. Rewrite the loop to use a # single anonymous variable as the condition, i.e.: # anon_var = True # while anon_var: # condition stmts... # anon_var = condition expr # if anon_var: # while loop body cond_var = asty.Name(cond, id=compiler.get_anon_var(), ctx=ast.Load()) def make_not(operand): return asty.UnaryOp(cond, op=ast.Not(), operand=operand) body_stmts = cond_compiled.stmts + [ asty.Assign( cond, targets=[compiler._storeize(cond, cond_var)], # Cast the condition to a bool in case it's mutable and # changes its truth value, but use (not (not ...)) instead of # `bool` in case `bool` has been redefined. value=make_not(make_not(cond_compiled.force_expr)), ), asty.If(cond, test=cond_var, body=body_stmts, orelse=[]), ] cond_compiled = ( Result() + asty.Assign( cond, targets=[compiler._storeize(cond, cond_var)], value=asty.Constant(cond, value=True), ) + cond_var ) orel = Result() if else_expr is not None: orel = compiler._compile_branch(else_expr) orel += orel.expr_as_stmt() ret = cond_compiled + asty.While( expr, test=cond_compiled.force_expr, body=body_stmts, orelse=orel.stmts ) return ret @pattern_macro(["break", "continue"], []) def compile_break_or_continue_expression(compiler, expr, root): return (asty.Break if root == "break" else asty.Continue)(expr) # ------------------------------------------------ # * `with` # ------------------------------------------------ @pattern_macro("with", [ brackets(oneplus(maybe(keepsym(":async")) + FORM + FORM)) | brackets((maybe(keepsym(":async")) + FORM) >> (lambda x: [(x[0], Symbol("_"), x[1])])), many(FORM)]) def compile_with_expression(compiler, expr, root, args, body): # We'll store the result of the body in a tempvar temp_var = compiler.get_anon_var() name = asty.Name(expr, id=mangle(temp_var), ctx=ast.Store()) # Initialize the tempvar to None in case the `with` exits # early with an exception. initial_assign = asty.Assign( expr, targets=[name], value=asty.Constant(expr, value=None) ) [args] = args ret = Result(stmts=[initial_assign]) items = [] was_async = None cbody = None for i, (is_async, variable, ctx) in enumerate(args): is_async = bool(is_async) if was_async is None: was_async = is_async elif is_async != was_async: # We're compiling a `with` that mixes synchronous and # asynchronous context managers. Python doesn't support # this directly, so start a new `with` inside the body. cbody = compile_with_expression(compiler, expr, root, [args[i:]], body) break if not isinstance(ctx, Result): # In a non-recursive call, `ctx` has not yet been compiled, # and thus is not yet a `Result`. ctx = compiler.compile(ctx) if i == 0: ret += ctx elif ctx.stmts: # We need to include some statements as part of this # context manager, but this `with` already has at # least one prior context manager. So, put our # statements in the body and then start a new `with`. cbody = ctx + compile_with_expression( compiler, expr, root, [((is_async, variable, ctx), *args[i + 1:])], body) break variable = ( None if variable == Symbol("_") else compiler._storeize(variable, compiler.compile(variable)) ) items.append( ast.withitem(context_expr=ctx.force_expr, optional_vars=variable) ) if not cbody: cbody = compiler._compile_branch(body) cbody += asty.Assign(expr, targets=[name], value=cbody.force_expr) node = asty.AsyncWith if was_async else asty.With ret += node(expr, body=cbody.stmts, items=items) # And make our expression context our temp variable expr_name = asty.Name(expr, id=mangle(temp_var), ctx=ast.Load()) ret += Result(expr=expr_name) # We don't give the Result any temp_vars because we don't want # Result.rename to touch `name`. Otherwise, initial_assign will # clobber any preexisting value of the renamed-to variable. return ret # ------------------------------------------------ # * `match` # ------------------------------------------------ _pattern = forward_decl() _pattern.define( ( SYM | KEYWORD | LITERAL | brackets(many(_pattern | unpack("iterable"))) | in_tuple(many(_pattern | unpack("iterable"))) | pexpr(keepsym("."), many(SYM)) | pexpr(keepsym("|"), many(_pattern)) | braces(many(LITERAL + _pattern), maybe(pvalue("unpack-mapping", SYM))) | pexpr( pexpr(keepsym("."), oneplus(SYM)) | notsym(".", "|", "unpack-mapping", "unpack-iterable"), many(parse_if(lambda x: not isinstance(x, Keyword), _pattern)), many(KEYWORD + _pattern), ) ) + maybe(sym(":as") + SYM) ) match_clause = _pattern + maybe(sym(":if") + FORM) @pattern_macro(((3, 10), "match"), [FORM, many(match_clause + FORM)]) def compile_match_expression(compiler, expr, root, subject, clauses): subject = compiler.compile(subject) return_var = asty.Name(expr, id=mangle(compiler.get_anon_var()), ctx=ast.Store()) lifted_if_defs = [] match_cases = [] for *pattern, guard, body in clauses: if guard and body == Keyword("as"): raise compiler._syntax_error(body, ":as clause cannot come after :if guard") body = compiler._compile_branch([body]) body += asty.Assign(pattern[0], targets=[return_var], value=body.force_expr) body += body.expr_as_stmt() body = body.stmts pattern = compile_pattern(compiler, pattern) if guard: guard = compiler.compile(guard) if guard.stmts: fname = compiler.get_anon_var() guardret = Result() + asty.FunctionDef( guard, name=fname, args=ast.arguments( args=[], kwarg=None, posonlyargs=[], kwonlyargs=[], kw_defaults=[], defaults=[], ), body=guard.stmts + [asty.Return(guard.expr, value=guard.expr)], decorator_list=[], **({"type_params": []} if PY3_12 else {}), ) lifted_if_defs.append(guardret) guard = Result(expr=asty.parse(guard, f"{fname}()").body[0].value) match_cases.append( ast.match_case( pattern=pattern, guard=guard.force_expr if guard else None, body=body, ) ) returnable = Result( expr=asty.Name(expr, id=return_var.id, ctx=ast.Load()), temp_variables=[return_var], ) ret = Result() + subject ret += asty.Assign( expr, targets=[return_var], value=asty.Constant(expr, value=None) ) if not match_cases: return ret + returnable for lifted_if in lifted_if_defs: ret += lifted_if ret += asty.Match(expr, subject=subject.force_expr, cases=match_cases) return ret + returnable def compile_pattern(compiler, pattern): value, assignment = pattern if assignment is not None: return compiler.scope.assign( asty.MatchAs( value, pattern=compile_pattern(compiler, (value, None)), name=mangle(compiler._nonconst(assignment)), ) ) if str(value) in ("None", "True", "False"): return asty.MatchSingleton( value, value=compiler.compile(value).force_expr.value, ) elif isinstance(value, (String, Integer, Float, Complex, Bytes)): return asty.MatchValue( value, value=compiler.compile(value).expr, ) elif value == Symbol("_"): return asty.MatchAs(value) elif isinstance(value, Symbol): return compiler.scope.assign(asty.MatchAs(value, name=mangle(value))) elif isinstance(value, Expression) and value[0] == Symbol("|"): return asty.MatchOr( value, patterns=[compile_pattern(compiler, v) for v in value[1]], ) elif isinstance(value, Expression) and value[0] == Symbol("."): root, syms = value dotform = mkexpr(root, *syms).replace(value) return asty.MatchValue( value, value=compiler.compile(dotform).expr, ) elif isinstance(value, (Tuple, List)): patterns = value[0] patterns = [ compile_pattern(compiler, (v, None) if is_unpack("iterable", v) else v) for v in patterns ] return asty.MatchSequence(value, patterns=patterns) elif is_unpack("iterable", value): return compiler.scope.assign(asty.MatchStar(value, name=mangle(value[1]))) elif isinstance(value, Dict): kvs, rest = value keys, values = zip(*kvs) if kvs else ([], []) # Call `scope.assign` for the assignment to `rest`. return compiler.scope.assign( asty.MatchMapping( value, keys=[compiler.compile(key).expr for key in keys], patterns=[compile_pattern(compiler, v) for v in values], rest=mangle(rest) if rest else None, ) ) elif isinstance(value, Expression): head, args, kwargs = value keywords, values = zip(*kwargs) if kwargs else ([], []) return asty.MatchClass( value, cls=compiler.compile( # `head` could be a symbol or a dotted form. (head[:1] + head[1]).replace(head) if type(head) is Expression else head).expr, patterns=[compile_pattern(compiler, v) for v in args], kwd_attrs=[kwd.name for kwd in keywords], kwd_patterns=[compile_pattern(compiler, value) for value in values], ) elif isinstance(value, Keyword): return asty.MatchClass( value, cls=compiler.compile(dotted("hy.models.Keyword")).expr, patterns=[ asty.MatchValue(value, value=asty.Constant(value, value=value.name)) ], kwd_attrs=[], kwd_patterns=[], ) else: raise compiler._syntax_error(value, "unsupported") # ------------------------------------------------ # * `raise` and `try` # ------------------------------------------------ @pattern_macro("raise", [maybe(FORM), maybe(sym(":from") + FORM)]) def compile_raise_expression(compiler, expr, root, exc, cause): ret = Result() if exc is not None: exc = compiler.compile(exc) ret += exc exc = exc.force_expr if cause is not None: cause = compiler.compile(cause) ret += cause cause = cause.force_expr return ret + asty.Raise(expr, exc=exc, cause=cause) @pattern_macro( "try", [ many(notpexpr("except", "except*", "else", "finally")), many( pexpr( keepsym("except") | keepsym("except*"), brackets() | brackets(FORM) | brackets(SYM, FORM), many(FORM), ) ), maybe(dolike("else")), maybe(dolike("finally")), ], ) def compile_try_expression(compiler, expr, root, body, catchers, orelse, finalbody): if orelse is not None and not catchers: # Python forbids `else` when there are no `except` clauses. # But we can get the same effect by appending the `else` forms # to the body. body += list(orelse) orelse = None body = compiler._compile_branch(body) if not (catchers or finalbody): # Python forbids this, so just return the body, per `do`. return body return_var = asty.Name(expr, id=mangle(compiler.get_anon_var()), ctx=ast.Store()) handler_results = Result() handlers = [] except_syms_seen = set() for catcher in catchers: # exceptions catch should be either: # [[list of exceptions]] # or # [variable [list of exceptions]] # or # [variable exception] # or # [exception] # or # [] except_sym, exceptions, ebody = catcher if not PY3_11 and except_sym == Symbol("except*"): hy_compiler._syntax_error(except_sym, "`{}` requires Python 3.11 or later") except_syms_seen.add(str(except_sym)) if len(except_syms_seen) > 1: raise compiler._syntax_error( except_sym, "cannot have both `except` and `except*` on the same `try`" ) name = None if len(exceptions) == 2: name = mangle(compiler._nonconst(exceptions[0])) exceptions_list = exceptions[-1] if exceptions else List() if isinstance(exceptions_list, List): if len(exceptions_list): # [FooBar BarFoo] → catch Foobar and BarFoo exceptions elts, types, _ = compiler._compile_collect(exceptions_list) types += asty.Tuple(exceptions_list, elts=elts, ctx=ast.Load()) else: # [] → all exceptions caught types = Result() else: types = compiler.compile(exceptions_list) # Create a "fake" scope for the exception variable. # See: https://docs.python.org/3/reference/compound_stmts.html#the-try-statement with compiler.scope.create(ScopeLet) as scope: if name: scope.add(name, name) ebody = compiler._compile_branch(ebody) ebody += asty.Assign(catcher, targets=[return_var], value=ebody.force_expr) ebody += ebody.expr_as_stmt() handler_results += types + asty.ExceptHandler( catcher, type=types.expr, name=name, body=ebody.stmts or [asty.Pass(catcher)], ) handlers.append(handler_results.stmts.pop()) if orelse is None: orelse = [] else: orelse = compiler._compile_branch(orelse) orelse += asty.Assign(expr, targets=[return_var], value=orelse.force_expr) orelse += orelse.expr_as_stmt() orelse = orelse.stmts if finalbody is None: finalbody = [] else: finalbody = compiler._compile_branch(finalbody) finalbody += finalbody.expr_as_stmt() finalbody = finalbody.stmts returnable = Result( expr=asty.Name(expr, id=return_var.id, ctx=ast.Load()), temp_variables=[return_var], ) body += ( body.expr_as_stmt() if orelse else asty.Assign(expr, targets=[return_var], value=body.force_expr) ) body = body.stmts or [asty.Pass(expr)] x = (asty.TryStar if "except*" in except_syms_seen else asty.Try)( expr, body=body, handlers=handlers, orelse=orelse, finalbody=finalbody ) return handler_results + x + returnable # ------------------------------------------------ # * Functions and macros # ------------------------------------------------ NASYM = some(lambda x: isinstance(x, Symbol) and x not in (Symbol("/"), Symbol("*"))) argument = maybe_annotated(NASYM | brackets(NASYM, FORM)) varargs = lambda unpack_type, wanted: maybe_annotated(pvalue(unpack_type, wanted)) kwonly_delim = some(lambda x: x == Symbol("*")) lambda_list = brackets( maybe(many(argument) + sym("/")), many(argument), maybe(kwonly_delim | varargs("unpack-iterable", NASYM)), many(argument), maybe(varargs("unpack-mapping", NASYM)), ) @pattern_macro("fn", [ maybe(keepsym(":async")), maybe(type_params), maybe_annotated(lambda_list), many(FORM)]) def compile_function_lambda(compiler, expr, root, is_async, tp, params, body): params, returns = params posonly, args, rest, kwonly, kwargs = params has_annotations = returns is not None or any( isinstance(param, tuple) and param[1] is not None for param in (posonly or []) + args + kwonly + [rest, kwargs] ) args, ret = compile_lambda_list(compiler, params) with compiler.local_state(), compiler.scope.create(ScopeFn, args, is_async) as scope: body = compiler._compile_branch(body) # Compile to lambda if we can if not (has_annotations or tp or body.stmts or is_async): return ret + asty.Lambda(expr, args=args, body=body.force_expr) # Otherwise create a standard function node = asty.AsyncFunctionDef if is_async else asty.FunctionDef name = compiler.get_anon_var() ret += compile_function_node( compiler, expr, node, [], tp, name, args, returns, body, scope ) # return its name as the final expr return ret + Result(expr=ret.temp_variables[0]) @pattern_macro("defn", [ maybe(keepsym(":async")), maybe(brackets(many(FORM))), maybe(type_params), maybe_annotated(SYM), lambda_list, many(FORM)]) def compile_function_def(compiler, expr, root, is_async, decorators, tp, name, params, body): name, returns = name node = asty.AsyncFunctionDef if is_async else asty.FunctionDef decorators, ret, _ = compiler._compile_collect(decorators[0] if decorators else []) args, ret2 = compile_lambda_list(compiler, params) ret += ret2 name = mangle(compiler._nonconst(name)) compiler.scope.define(name) with compiler.local_state(), compiler.scope.create(ScopeFn, args, is_async) as scope: body = compiler._compile_branch(body) return ret + compile_function_node( compiler, expr, node, decorators, tp, name, args, returns, body, scope ) def compile_function_node(compiler, expr, node, decorators, tp, name, args, returns, body, scope): ret = Result() if body.expr: # implicitly return final expression, # except for async generators enode = asty.Expr if scope.is_async and scope.has_yield else asty.Return body += enode(body.expr, value=body.expr) ret += node( expr, name=name, args=args, body=body.stmts or [asty.Pass(expr)], decorator_list=decorators, returns=compiler.compile(returns).force_expr if returns is not None else None, **digest_type_params(compiler, tp), ) ast_name = asty.Name(expr, id=name, ctx=ast.Load()) return ret + Result(temp_variables=[ast_name, ret.stmts[-1]]) @pattern_macro( "defmacro", [ SYM, brackets( maybe(many(argument) + sym("/")), many(argument), maybe(varargs("unpack-iterable", NASYM)), ), many(FORM), ], ) def compile_macro_def(compiler, expr, root, name, params, body): def E(*x): return Expression(x) S = Symbol compiler.warn_on_core_shadow(name) fn_def = E(S("fn"), List(expr[2]), *body).replace(expr) if compiler.is_in_local_state(): # We're in a local scope, so define the new macro locally. state = compiler.local_state_stack[-1] # Produce code that will set the macro to a local variable. ret = compiler.compile(E( S("setv"), S(local_macro_name(name)), fn_def).replace(expr)) # Also evaluate the macro definition now, and put it in # state['macros']. state['macros'][mangle(name)] = compiler.eval(fn_def) return ret + ret.expr_as_stmt() # Otherwise, define the macro module-wide. ret = compiler.compile(E(S("eval-and-compile"), E( E(dotted("hy.macros.macro"), str(name)), fn_def)).replace(expr)) return ret + ret.expr_as_stmt() def compile_lambda_list(compiler, params): ret = Result() posonly_parms, args_parms, rest_parms, kwonly_parms, kwargs_parms = params if not (posonly_parms or posonly_parms is None): raise compiler._syntax_error( params, "at least one argument must precede /" ) posonly_parms = posonly_parms or [] is_positional_arg = lambda x: isinstance(x[0], Symbol) invalid_non_default = next( ( arg for arg in dropwhile(is_positional_arg, posonly_parms + args_parms) if is_positional_arg(arg) ), None, ) if invalid_non_default: raise compiler._syntax_error( invalid_non_default[0], "non-default argument follows default argument" ) posonly_ast, posonly_defaults, ret = compile_arguments_set( compiler, posonly_parms, ret ) args_ast, args_defaults, ret = compile_arguments_set(compiler, args_parms, ret) kwonly_ast, kwonly_defaults, ret = compile_arguments_set( compiler, kwonly_parms, ret, True ) rest_ast = kwargs_ast = None if rest_parms == Symbol("*"): # rest is a positional only marker if not kwonly_parms: raise compiler._syntax_error( rest_parms, "named arguments must follow bare *" ) rest_ast = None elif rest_parms: # rest is capturing varargs [rest_ast], _, ret = compile_arguments_set(compiler, [rest_parms], ret) if kwargs_parms: [kwargs_ast], _, ret = compile_arguments_set(compiler, [kwargs_parms], ret) return ( ast.arguments( args=args_ast, defaults=[*posonly_defaults, *args_defaults], vararg=rest_ast, posonlyargs=posonly_ast, kwonlyargs=kwonly_ast, kw_defaults=kwonly_defaults, kwarg=kwargs_ast, ), ret, ) def compile_arguments_set(compiler, decls, ret, is_kwonly=False): args_ast = [] args_defaults = [] for decl, ann in decls: default = None # funcparserlib will check to make sure that the only times we # ever have a hy List here are due to a default value. if isinstance(decl, List): sym, default = decl else: sym = decl if ann is not None: ret += compiler.compile(ann) ann_ast = ret.force_expr else: ann_ast = None if default is not None: ret += compiler.compile(default) args_defaults.append(ret.force_expr) # Kwonly args without defaults are considered required elif not isinstance(decl, List) and is_kwonly: args_defaults.append(None) elif isinstance(decl, List): # Note that the only time any None should ever appear here # is in kwargs, since the order of those with defaults vs # those without isn't significant in the same way as # positional args. args_defaults.append(None) args_ast.append( asty.arg(sym, arg=mangle(compiler._nonconst(sym)), annotation=ann_ast) ) return args_ast, args_defaults, ret @pattern_macro("return", [maybe(FORM)]) def compile_return(compiler, expr, root, arg): ret = Result() if arg is None: return asty.Return(expr, value=None) ret += compiler.compile(arg) return ret + asty.Return(expr, value=ret.force_expr) @pattern_macro("yield", [times(0, 2, FORM)]) def compile_yield_expression(compiler, expr, root, args): if is_inside_function_scope(compiler.scope): nearest_python_scope(compiler.scope).has_yield = True yield_from = False if len(args) == 2: from_kw, x = args if from_kw != Keyword("from"): raise compiler._syntax_error(from_kw, "two-argument `yield` requires `:from`") yield_from = True args = [x] ret = Result() if args: ret += compiler.compile(args[0]) return ret + (asty.YieldFrom if yield_from else asty.Yield)(expr, value=ret.force_expr) @pattern_macro("await", [FORM]) def compile_yield_from_or_await_expression(compiler, expr, root, arg): ret = Result() + compiler.compile(arg) return ret + asty.Await(expr, value=ret.force_expr) # ------------------------------------------------ # * `defclass` # ------------------------------------------------ @pattern_macro( "defclass", [ maybe(brackets(many(FORM))), maybe(type_params), SYM, maybe(brackets(many(FORM)) + maybe(STR) + many(FORM)), ], ) def compile_class_expression(compiler, expr, root, decorators, tp, name, rest): base_list, docstring, body = rest or ([[]], None, []) decorators, ret, _ = compiler._compile_collect(decorators[0] if decorators else []) bases_expr, ret2, keywords = compiler._compile_collect( base_list[0], with_kwargs=True ) ret += ret2 bodyr = Result() if docstring is not None: bodyr += compiler.compile(docstring).expr_as_stmt() name = mangle(compiler._nonconst(name)) compiler.scope.define(name) with compiler.local_state(), compiler.scope.create(ScopeFn): e = compiler._compile_branch(body) bodyr += e + e.expr_as_stmt() return ret + asty.ClassDef( expr, decorator_list=decorators, name=name, keywords=keywords, bases=bases_expr, body=bodyr.stmts or [asty.Pass(expr)], **digest_type_params(compiler, tp) ) # ------------------------------------------------ # * `import` and `require` # ------------------------------------------------ module_name_pattern = SYM | pexpr( some(lambda x: isinstance(x, Symbol) and not str(x[0]).strip(".")) + oneplus(SYM) ) def module_name_str(x): return ( ".".join(map(mangle, x[1][x[1][0] == Symbol("None") :])) if isinstance(x, Expression) else str(x) if isinstance(x, Symbol) and not x.strip(".") else mangle(x) ) importlike = ( keepsym("*") | (keepsym(":as") + SYM) | brackets(many(SYM + maybe(sym(":as") + SYM))) ) def assignment_shape(module, rest): prefix = "" assignments = "EXPORTS" if rest is None: # (import foo) prefix = module_name_str(module) elif rest == Symbol("*"): # (import foo *) pass elif rest[0] == Keyword("as"): # (import foo :as bar) prefix = mangle(rest[1]) else: # (import foo [bar baz :as MyBaz bing]) assignments = [(k, v or k) for k, v in rest[0]] return prefix, assignments @pattern_macro( "require", [ many( module_name_pattern + times( 0, 2, (maybe(sym(":macros")) + importlike) | (keepsym(":readers") + (keepsym("*") | brackets(many(SYM)))), ) ) ], ) def compile_require(compiler, expr, root, entries): ret = Result() for entry in entries: module, assignments = entry readers, rest = ( [names for key, names in assignments if (key == Keyword("readers")) == flag] for flag in (True, False) ) if len(rest) > 1 or len(readers) > 1: raise compiler._syntax_error( entry, f"redefinition of ':{'macros' if len(rest) > 1 else 'readers'}' brackets.", ) rest = rest[0] if rest else None # None used for prefixed macro import readers = readers and readers[0] prefix, assignments = assignment_shape(module, rest) module_name = module_name_str(module) if isinstance(module, Expression) and module[1][0] == Symbol("None"): # Prepend leading dots to `module_name`. module_name = str(module[0]) + module_name # we don't want to import all macros as prefixed if we're specifically # importing readers but not macros # (require a-module :readers ["!"]) if (rest or not readers) and compiler.is_in_local_state(): reqs = require( module_name, compiler.local_state_stack[-1]['macros'], assignments = assignments, prefix = prefix, compiler = compiler) ret += compiler.compile(Expression([ Symbol("setv"), List([Symbol(local_macro_name(m)) for m, _, _ in reqs]), Expression([ dotted("hy.macros.require_vals"), String(module_name), Dict(), Keyword("assignments"), List([(String(m), String(m)) for _, m, _ in reqs])])]).replace(expr)) ret += ret.expr_as_stmt() elif (rest or not readers) and require( module_name, compiler.module, assignments = assignments, prefix = prefix, compiler = compiler): # Actually calling `require` is necessary for macro expansions # occurring during compilation. # The `require` we're creating in AST is the same as above, but used at # run-time (e.g. when modules are loaded via bytecode). ret += compiler.compile( Expression( [ dotted("hy.macros.require"), String(module_name), Symbol("None"), Keyword("target_module_name"), String(compiler.module.__name__), Keyword("assignments"), ( String("EXPORTS") if assignments == "EXPORTS" else List([List([String(k), String(v)]) for k, v in assignments]) ), Keyword("prefix"), String(prefix), ] ).replace(expr) ) ret += ret.expr_as_stmt() if readers: reader_assignments = ( "ALL" if readers == Symbol("*") else [str(reader) for reader in readers[0]] ) if require_reader(module_name, compiler.module, reader_assignments): ret += compiler.compile( mkexpr( "do", mkexpr( dotted("hy.macros.require-reader"), String(module_name), "None", [reader_assignments], ), mkexpr( "eval-when-compile", mkexpr( dotted("hy.macros.enable-readers"), "None", mkexpr(dotted("hy.reader.HyReader.current-reader")), [reader_assignments], ), ), ).replace(expr) ) ret += ret.expr_as_stmt() return ret @pattern_macro("import", [many(module_name_pattern + maybe(importlike))]) def compile_import(compiler, expr, root, entries): ret = Result() for entry in entries: module, _ = entry prefix, assignments = assignment_shape(*entry) module_name = module_name_str(module) if assignments == "EXPORTS" and prefix == "": node = asty.ImportFrom names = [asty.alias(module, name="*", asname=None)] elif assignments == "EXPORTS": compiler.scope.define(prefix) node = asty.Import names = [asty.alias( module, name = module_name, asname = prefix if prefix != module_name else None)] else: node = asty.ImportFrom names = [] for k, v in assignments: compiler.scope.define(mangle(v)) names.append(asty.alias( module, name = mangle(k), asname = None if v == k else mangle(v))) ret += node( expr, names = names, **({} if node is asty.Import else dict( module = module_name if module_name and module_name.strip(".") else None, level = len(module[0]) if isinstance(module, Expression) and module[1][0] == Symbol("None") else len(module) if isinstance(module, Symbol) and not module.strip(".") else 0))) return ret # ------------------------------------------------ # * Miscellany # ------------------------------------------------ @pattern_macro("assert", [FORM, maybe(FORM)]) def compile_assert_expression(compiler, expr, root, test, msg): test = compiler.compile(test) if msg: msg = compiler.compile(msg) if not (test.stmts or (msg and msg.stmts)): return asty.Assert(expr, test=test.force_expr, msg=msg and msg.force_expr) return asty.If( expr, test=asty.Name(expr, id="__debug__", ctx=ast.Load()), orelse=[], body=test.stmts + [ asty.If( test, test=asty.UnaryOp(test, op=ast.Not(), operand=test.force_expr), orelse=[], body=(msg.stmts if msg else []) + [ asty.Assert( expr, test=asty.Constant(test, value=False), msg=msg and msg.force_expr, ) ], ) ], ) @pattern_macro( "unquote unquote-splice unpack-mapping except except* finally else".split(), [many(FORM)], ) def compile_placeholder(compiler, expr, root, body): raise ValueError(f"`{root}` is not allowed here") hy-1.0.0/hy/core/util.hy000066400000000000000000000062221467401276000150300ustar00rootroot00000000000000(import itertools) (import collections.abc [Iterable]) (import hy.compiler [HyASTCompiler calling-module]) (import threading [Lock]) (setv _gensym_counter 0) (setv _gensym_lock (Lock)) (defn gensym [[g ""]] #[[Generate a symbol with a unique name. The argument, if provided, will be included in the generated symbol name, as an aid to debugging. The below example uses the return value of ``f`` twice but calls it only once, and uses ``hy.gensym`` for the temporary variable to avoid collisions with any other variable names. :: (defmacro selfadd [x] (setv g (hy.gensym)) `(do (setv ~g ~x) (+ ~g ~g))) (defn f [] (print "This is only executed once.") 4) (print (selfadd (f)))]] (.acquire _gensym_lock) (try (global _gensym_counter) (+= _gensym_counter 1) (setv n _gensym_counter) (finally (.release _gensym_lock))) (setv g (hy.mangle (.format "_hy_gensym_{}_{}" g n))) (hy.models.Symbol (if (.startswith g "_hyx_") ; Remove the mangle prefix, if it's there, so the result always ; starts with our reserved prefix `_hy_`. (+ "_" (cut g (len "_hyx_") None)) g))) (defn _calling-module-name [[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 _macroexpand [model module macros #** kwargs] (if (and (isinstance model hy.models.Expression) model) (hy.macros.macroexpand :tree model :module module :compiler (HyASTCompiler module :extra-macros macros) :result-ok False #** kwargs) model)) (defn macroexpand [model [module None] [macros None]] "As :hy:func:`hy.macroexpand-1`, but the expansion process is repeated until it has no effect. :: (defmacro m [x] (and (int x) `(m ~(- x 1)))) (print (hy.repr (hy.macroexpand-1 '(m 5)))) ; => '(m 4) (print (hy.repr (hy.macroexpand '(m 5)))) ; => '0 Note that in general, macro calls in the arguments of the expression still won't expanded. To expand these, too, try Hyrule's :hy:func:`macroexpand-all `." (_macroexpand model (or module (calling-module)) macros)) (defn macroexpand-1 [model [module None] [macros None]] "Check if ``model`` is an :class:`Expression ` specifying a macro call. If so, expand the macro and return the expansion; otherwise, return ``model`` unchanged. :: (defmacro m [x] `(do ~x ~x ~x)) (print (hy.repr (hy.macroexpand-1 '(m (+= n 1))))) ; => '(do (+= n 1) (+= n 1) (+= n 1)) An exceptional case is if the macro is a core macro that returns one of Hy's internal compiler result objects instead of a real model. Then, you just get the original back, as if the macro hadn't been expanded. The optional arguments ``module`` and ``macros`` can be provided to control where macros are looked up, as with :hy:func:`hy.eval`. See also :hy:func:`hy.macroexpand`." (_macroexpand model (or module (calling-module)) macros :once True)) (setv __all__ []) hy-1.0.0/hy/errors.py000066400000000000000000000234241467401276000144520ustar00rootroot00000000000000import os import re import sys import traceback import importlib.util from contextlib import contextmanager from hy import _initialize_env_var from hy.compat import PYPY, PY3_13 _hy_show_internal_errors = _initialize_env_var("HY_SHOW_INTERNAL_ERRORS", False) class HyError(Exception): pass class HyInternalError(HyError): """Unexpected errors occurring during compilation or parsing of Hy code. Errors sub-classing this are not intended to be user-facing, and will, hopefully, never be seen by users! """ class HyLanguageError(HyError): """Errors caused by invalid use of the Hy language. This, and any errors inheriting from this, are user-facing. """ def __init__( self, message, expression=None, filename=None, source=None, lineno=1, colno=1, ): """ Args: message (str): The message to display for this error. expression (Optional[Object]): The Hy expression generating this error. filename (Optional[str]): The filename for the source code generating this error. Expression-provided information will take precedence of this value. Defaults to `None`. source (Optional[str]): The actual source code generating this error. Expression-provided information will take precedence of this value. Defaults to `None`. lineno (int): The line number of the error. Expression-provided information will take precedence of this value. Defaults to `1`. colno (int): The column number of the error. Expression-provided information will take precedence of this value. Defaults to `1`. """ self.msg = message self.compute_lineinfo(expression, filename, source, lineno, colno) if isinstance(self, SyntaxError): syntax_error_args = (self.filename, self.lineno, self.offset, self.text) super().__init__(message, syntax_error_args) else: super().__init__(message) def compute_lineinfo(self, expression, filename, source, lineno, colno): # NOTE: We use `SyntaxError`'s field names (i.e. `text`, `offset`, # `msg`) for compatibility and print-outs. self.text = getattr(expression, "source", source) self.filename = getattr(expression, "filename", filename) if self.text: lines = self.text.splitlines() self.lineno = getattr(expression, "start_line", lineno) self.offset = getattr(expression, "start_column", colno) end_column = getattr(expression, "end_column", len(lines[self.lineno - 1])) end_line = getattr(expression, "end_line", self.lineno) # Trim the source down to the essentials. self.text = "\n".join(lines[self.lineno - 1 : end_line]) if end_column: if self.lineno == end_line: self.arrow_offset = end_column else: self.arrow_offset = len(self.text[0]) self.arrow_offset -= self.offset else: self.arrow_offset = None else: # We could attempt to extract the source given a filename, but we # don't. self.lineno = lineno self.offset = colno self.arrow_offset = None def __str__(self): """Provide an exception message that includes SyntaxError-like source line information when available. """ # Syntax errors are special and annotate the traceback (instead of what # we would do in the message that follows the traceback). if isinstance(self, SyntaxError): return super().__str__() # When there isn't extra source information, use the normal message. elif not self.text: return super().__str__() # Re-purpose Python's builtin syntax error formatting. output = traceback.format_exception_only( SyntaxError, SyntaxError(self.msg, (self.filename, self.lineno, self.offset, self.text)), ) arrow_idx, _ = next( ((i, x) for i, x in enumerate(output) if set(x.strip()) == {"^"}), (None, None) ) if arrow_idx: msg_idx = arrow_idx + 1 else: msg_idx, _ = next( (i, x) for i, x in enumerate(output) if x.startswith("SyntaxError: ") ) # Get rid of erroneous error-type label. output[msg_idx] = re.sub("^SyntaxError: ", "", output[msg_idx]) # Extend the text arrow, when given enough source info. We # don't do this on newer Pythons because they make their own # underlines. if arrow_idx and self.arrow_offset and not PY3_13: output[arrow_idx] = "{}{}^\n".format( output[arrow_idx].rstrip("\n"), "-" * (self.arrow_offset - 1) ) # This resulting string will come after a ":" prompt, so # put it down a line. output.insert(0, "\n") return "".join(output) class HyCompileError(HyInternalError): """Unexpected errors occurring within the compiler.""" class HyTypeError(HyLanguageError, TypeError): """TypeError occurring during the normal use of Hy.""" class HyRequireError(HyLanguageError): """Errors arising during the use of `require` This, and any errors inheriting from this, are user-facing. """ class HyMacroExpansionError(HyLanguageError): """Errors caused by invalid use of Hy macros. This, and any errors inheriting from this, are user-facing. """ class HyEvalError(HyLanguageError): """Errors occurring during code evaluation at compile-time. These errors distinguish unexpected errors within the compilation process (i.e. `HyInternalError`s) from unrelated errors in user code evaluated by the compiler (e.g. in `eval-and-compile`). This, and any errors inheriting from this, are user-facing. """ class HySyntaxError(HyLanguageError, SyntaxError): """Error during the Lexing of a Hython expression.""" class HyWrapperError(HyError, TypeError): """Errors caused by language model object wrapping. These can be caused by improper user-level use of a macro, so they're not really "internal". If they arise due to anything else, they're an internal/compiler problem, though. """ def _module_filter_name(module_name): try: spec = importlib.util.find_spec(module_name) if not spec: return None compiler_loader = spec.loader if not compiler_loader: return None filename = compiler_loader.get_filename(module_name) if not filename: return None if compiler_loader.is_package(module_name): # Use the package directory (e.g. instead of `.../__init__.py`) so # that we can filter all modules in a package. return os.path.dirname(filename) else: # Normalize filename endings, because tracebacks will use `pyc` when # the loader says `py`. return filename.replace(".pyc", ".py") except Exception: return None _tb_hidden_modules = { m for m in map( _module_filter_name, [ "hy.compiler", "hy.reader", "hy.cmdline", "hy.repl", "hy.reader.parser", "hy.importer", "hy.compat", "hy.macros", "hy.models", "hy.core.result_macros", ], ) if m is not None } # We can't derive these easily from just their module names due # to missing magic attributes in internal importlib modules _tb_hidden_modules.update( f"/frozen {x}" if PYPY else f"" for x in ("importlib._bootstrap", "importlib._bootstrap_external") ) def hy_exc_filter(exc_type, exc_value, exc_traceback): """Produce exceptions print-outs with all frames originating from the modules in `_tb_hidden_modules` filtered out. The frames are actually filtered by each module's filename and only when a subclass of `HyLanguageError` is emitted. This does not remove the frames from the actual tracebacks, so debugging will show everything. """ # frame = (filename, line number, function name*, text) new_tb = [] for frame in traceback.extract_tb(exc_traceback): if not ( frame[0].replace(".pyc", ".py") in _tb_hidden_modules or os.path.dirname(frame[0]) in _tb_hidden_modules or os.path.basename(os.path.dirname(frame[0])) == "hy.exe" ): new_tb += [frame] lines = traceback.format_list(new_tb) lines.insert(0, "Traceback (most recent call last):\n") lines.extend(traceback.format_exception_only(exc_type, exc_value)) output = "".join(lines) return output def hy_exc_handler(exc_type, exc_value, exc_traceback): """A `sys.excepthook` handler that uses `hy_exc_filter` to remove internal Hy frames from a traceback print-out, so long as `_hy_show_internal_errors` is false. """ if _hy_show_internal_errors: return sys.__excepthook__(exc_type, exc_value, exc_traceback) try: output = hy_exc_filter(exc_type, exc_value, exc_traceback) sys.stderr.write(output) sys.stderr.flush() except Exception: sys.__excepthook__(exc_type, exc_value, exc_traceback) @contextmanager def filtered_hy_exceptions(): """Temporarily apply a `sys.excepthook` that filters Hy internal frames from tracebacks. Filtering can be controlled by the variable `hy.errors._hy_show_internal_errors` and environment variable `HY_SHOW_INTERNAL_ERRORS`. """ current_hook = sys.excepthook sys.excepthook = hy_exc_handler yield sys.excepthook = current_hook hy-1.0.0/hy/importer.py000066400000000000000000000136471467401276000150050ustar00rootroot00000000000000import builtins import importlib import inspect import os import pkgutil import sys import types import zipimport from contextlib import contextmanager from functools import partial import hy from hy.compiler import hy_compile from hy.reader import read_many, HyReader @contextmanager def loader_module_obj(loader): """Use the module object associated with a loader. This is intended to be used by a loader object itself, and primarily as a work-around for attempts to get module and/or file code from a loader without actually creating a module object. Since Hy currently needs the module object for macro importing, expansion, and whatnot, using this will reconcile Hy with such attempts. For example, if we're first compiling a Hy script starting from `runpy.run_path`, the Hy compiler will need a valid module object in which to run, but, given the way `runpy.run_path` works, there might not be one yet (e.g. `__main__` for a .hy file). We compensate by properly loading the module here. The function `inspect.getmodule` has a hidden-ish feature that returns modules using their associated filenames (via `inspect.modulesbyfile`), and, since the Loaders (and their delegate Loaders) carry a filename/path associated with the parent package, we use it as a more robust attempt to obtain an existing module object. When no module object is found, a temporary, minimally sufficient module object is created for the duration of the `with` body. """ tmp_mod = False try: module = inspect.getmodule(None, _filename=loader.path) except KeyError: module = None if module is None: tmp_mod = True module = sys.modules.setdefault(loader.name, types.ModuleType(loader.name)) module.__file__ = loader.path module.__name__ = loader.name try: yield module finally: if tmp_mod: del sys.modules[loader.name] def _hy_code_from_file(filename, loader_type=None): """Use PEP-302 loader to produce code for a given Hy source file.""" full_fname = os.path.abspath(filename) fname_path, fname_file = os.path.split(full_fname) modname = os.path.splitext(fname_file)[0] sys.path.insert(0, fname_path) try: if loader_type is None: loader = pkgutil.get_loader(modname) else: loader = loader_type(modname, full_fname) code = loader.get_code(modname) finally: sys.path.pop(0) return code def _get_code_from_file(run_name, fname=None, hy_src_check=lambda x: x.endswith(".hy")): """A patch of `runpy._get_code_from_file` that will also run and cache Hy code. """ if fname is None and run_name is not None: fname = run_name # Check for bytecode first. (This is what the `runpy` version does!) with open(fname, "rb") as f: code = pkgutil.read_code(f) if code is None: if hy_src_check(fname): code = _hy_code_from_file(fname, loader_type=HyLoader) else: # Try normal source with open(fname, "rb") as f: # This code differs from `runpy`'s only in that we # force decoding into UTF-8. source = f.read().decode("utf-8") code = compile(source, fname, "exec") return code if hy.compat.PY3_12_6 else (code, fname) importlib.machinery.SOURCE_SUFFIXES.insert(0, ".hy") _py_source_to_code = importlib.machinery.SourceFileLoader.source_to_code def _could_be_hy_src(filename): return os.path.isfile(filename) and ( os.path.splitext(filename)[1] not in set(importlib.machinery.SOURCE_SUFFIXES) - {".hy"} ) def _hy_source_to_code(self, data, path, _optimize=-1): if _could_be_hy_src(path): if os.environ.get("HY_MESSAGE_WHEN_COMPILING"): print("Compiling", path, file=sys.stderr) source = data.decode("utf-8") hy_tree = read_many(source, filename=path, skip_shebang=True, reader=HyReader()) with loader_module_obj(self) as module: data = hy_compile(hy_tree, module) return _py_source_to_code(self, data, path, _optimize=_optimize) importlib.machinery.SourceFileLoader.source_to_code = _hy_source_to_code if (".hy", False, False) not in zipimport._zip_searchorder: zipimport._zip_searchorder += ((".hy", False, False),) _py_compile_source = zipimport._compile_source def _hy_compile_source(pathname, source): if not pathname.endswith(".hy"): return _py_compile_source(pathname, source) mname = f"" sys.modules[mname] = types.ModuleType(mname) return compile( hy_compile( read_many(source.decode("UTF-8"), filename=pathname, skip_shebang=True, reader=HyReader()), sys.modules[mname], ), pathname, "exec", dont_inherit=True, ) zipimport._compile_source = _hy_compile_source # This is actually needed; otherwise, pre-created finders assigned to the # current dir (i.e. `''`) in `sys.path` will not catch absolute imports of # directory-local modules! sys.path_importer_cache.clear() # Do this one just in case? importlib.invalidate_caches() class HyLoader(importlib.machinery.SourceFileLoader): pass # We create a separate version of runpy, "runhy", that prefers Hy source over # Python. runhy = importlib.import_module("runpy") runhy._get_code_from_file = partial(_get_code_from_file, hy_src_check=_could_be_hy_src) del sys.modules["runpy"] runpy = importlib.import_module("runpy") _runpy_get_code_from_file = runpy._get_code_from_file runpy._get_code_from_file = _get_code_from_file def _inject_builtins(): """Inject the Hy core macros into Python's builtins if necessary""" if hasattr(builtins, "__hy_injected__"): return hy.macros.load_macros(builtins) # Set the marker so we don't inject again. builtins.__hy_injected__ = True hy-1.0.0/hy/macros.py000066400000000000000000000346011467401276000144210ustar00rootroot00000000000000import builtins import importlib import inspect import os import re import sys import traceback from ast import AST from funcparserlib.parser import NoParseError import hy.compiler from hy.compat import PY3_11 from hy.errors import ( HyLanguageError, HyMacroExpansionError, HyRequireError, HyTypeError, ) from hy.model_patterns import whole from hy.models import Expression, Symbol, as_model, is_unpack, replace_hy_obj from hy.reader import mangle from hy.reader.mangling import slashes2dots EXTRA_MACROS = ["hy.core.result_macros", "hy.core.macros"] def macro(name): """Decorator to define a macro called `name`.""" return lambda fn: install_macro(name, fn, fn) def reader_macro(name, fn): fn = rename_function(fn, name) fn.__globals__.setdefault("_hy_reader_macros", {})[name] = fn def pattern_macro(names, pattern, shadow=None): pattern = whole(pattern) py_version_required = None if isinstance(names, tuple): py_version_required, names = names def dec(fn): def wrapper_maker(name): def wrapper(_hy_compiler, *args): if shadow and any(is_unpack("iterable", x) for x in args): # Try a shadow function call with this name instead. return Expression( [Expression(map(Symbol, [".", "hy", "pyops", name])), *args] ).replace(_hy_compiler.this) expr = _hy_compiler.this if py_version_required and sys.version_info < py_version_required: raise _hy_compiler._syntax_error( expr, "`{}` requires Python {} or later".format( name, ".".join(map(str, py_version_required)) ), ) try: parse_tree = pattern.parse(args) except NoParseError as e: raise _hy_compiler._syntax_error( expr[min(e.state.pos + 1, len(expr) - 1)], "parse error for pattern macro '{}': {}".format( name, e.msg.replace("end of input", "end of macro call") ), ) return fn(_hy_compiler, expr, name, *parse_tree) return wrapper for name in [names] if isinstance(names, str) else names: install_macro(name, wrapper_maker(name), fn) return fn return dec def install_macro(name, fn, module_of): name = mangle(name) fn = rename_function(fn, name) module_of.__globals__.setdefault("_hy_macros", {})[name] = fn return fn def _same_modules(source_module, target_module): """Compare the filenames associated with the given modules names. This tries to not actually load the modules. """ if not (source_module or target_module): return False if target_module is source_module: return True def get_filename(module): if inspect.ismodule(module): return inspect.getfile(module) elif ( (spec := importlib.util.find_spec(module)) and isinstance(spec.loader, importlib.machinery.SourceFileLoader)): return spec.loader.get_filename() try: return os.path.samefile( get_filename(source_module), get_filename(target_module)) except (ValueError, TypeError, ImportError, FileNotFoundError): return False def derive_target_module(target_module, parent_frame): if target_module is None: target_namespace = parent_frame.f_globals target_module = target_namespace.get("__name__", None) elif isinstance(target_module, str): target_module = importlib.import_module(target_module) target_namespace = target_module.__dict__ elif inspect.ismodule(target_module): target_namespace = target_module.__dict__ else: raise HyTypeError( "`target_module` is not a recognized type: {}".format(type(target_module)) ) return target_module, target_namespace def import_module_from_string(module_name, package_module): package = None if module_name.startswith("."): source_dirs = module_name.split(".") target_dirs = getattr(package_module, "__name__", package_module).split(".") while len(source_dirs) > 1 and source_dirs[0] == "" and target_dirs: source_dirs.pop(0) target_dirs.pop() package = ".".join(target_dirs + source_dirs[:-1]) try: return importlib.import_module(module_name, package) except ImportError as e: raise HyRequireError(e.args[0]).with_traceback(None) def require_reader(source_module, target_module, assignments): target_module, target_namespace = derive_target_module( target_module, inspect.stack()[1][0] ) if _same_modules(source_module, target_module): return False if not inspect.ismodule(source_module): source_module = import_module_from_string(source_module, target_module) source_macros = source_module.__dict__.setdefault("_hy_reader_macros", {}) target_macros = target_namespace.setdefault("_hy_reader_macros", {}) assignments = ( source_macros.keys() if assignments == "ALL" else assignments ) for name in assignments: if name in source_module._hy_reader_macros: target_macros[name] = source_macros[name] else: raise HyRequireError(f"Could not require name {name} from {source_module}") return True def enable_readers(module, reader, names): _, namespace = derive_target_module(module, inspect.stack()[1][0]) names = ( namespace["_hy_reader_macros"].keys() if names == "ALL" else names ) for name in names: if name not in namespace["_hy_reader_macros"]: raise NameError(f"reader {name} is not defined") reader.reader_macros[name] = namespace["_hy_reader_macros"][name] def require(source_module, target, assignments, prefix="", target_module_name=None, compiler=None): """Load macros from a module. Return a list of (new name, source name, macro object) tuples, including only macros that were actually transferred. - `target` can be a a string (naming a module), a module object, a dictionary, or `None` (meaning the calling module). - `assignments` can be "ALL", "EXPORTS", or a list of (macro name, alias) pairs.""" if type(target) is dict: target_module = None else: target_module, target_namespace = derive_target_module( target, inspect.stack()[1][0] ) # Let's do a quick check to make sure the source module isn't actually # the module being compiled (e.g. when `runpy` executes a module's code # in `__main__`). # We use the module's underlying filename for this (when they exist), since # it's the most "fixed" attribute. if _same_modules(source_module, target_module): return [] if not inspect.ismodule(source_module): source_module = import_module_from_string(source_module, target_module_name or target_module or '') source_macros = source_module.__dict__.setdefault("_hy_macros", {}) source_exports = getattr( source_module, "_hy_export_macros", [k for k in source_macros.keys() if not k.startswith("_")], ) if not source_module._hy_macros: if assignments in ("ALL", "EXPORTS"): return [] out = [] for name, alias in assignments: try: out.extend(require( f"{source_module.__name__}.{mangle(name)}", target_module or target, "ALL", prefix=alias, )) except HyRequireError as e: raise HyRequireError( f"Cannot import name '{name}'" f" from '{source_module.__name__}'" f" ({source_module.__file__})" ) return out target_macros = target_namespace.setdefault("_hy_macros", {}) if target_module else target if prefix: prefix += "." out = [] for name, alias in ( assignments if assignments not in ("ALL", "EXPORTS") else ( (k, k) for k in source_macros.keys() if assignments == "ALL" or k in source_exports ) ): _name = mangle(name) if compiler: compiler.warn_on_core_shadow(prefix + alias) alias = mangle(prefix + alias) if _name in source_module._hy_macros: target_macros[alias] = source_macros[_name] out.append((alias, _name, source_macros[_name])) else: raise HyRequireError( "Could not require name {} from {}".format(_name, source_module) ) return out def require_vals(*args, **kwargs): return [value for _, _, value in require(*args, **kwargs)] def local_macro_name(original): return '_hy_local_macro__' + (mangle(original) # We have to do a bit of extra mangling beyond `mangle` itself to # handle names with periods. .replace('D', 'DN') .replace('.', 'DD')) def load_macros(module): """Load the hy builtin macros into module `module_name`, removing any prior macros set. It is an error to call this on any module in `hy.core`. """ builtin_macros = EXTRA_MACROS module._hy_macros = {} module._hy_reader_macros = {} for builtin_mod_name in builtin_macros: builtin_mod = importlib.import_module(builtin_mod_name) # This may overwrite macros in the module. if hasattr(builtin_mod, "_hy_macros"): module._hy_macros.update(getattr(builtin_mod, "_hy_macros", {})) if hasattr(builtin_mod, "_hy_reader_macros"): module._hy_reader_macros.update( getattr(builtin_mod, "_hy_reader_macros", {}) ) class MacroExceptions: """wrap non ``HyLanguageError``'s in ``HyMacroExpansionError`` preserving stack trace used in lieu of ``@contextmanager`` to ensure stack trace contains only internal hy modules for consistent filtering. """ def __init__(self, module, macro_tree, compiler=None): self.module = module self.macro_tree = macro_tree self.compiler = compiler def __enter__(self): return self def __exit__(self, exc_type, exc_value, exc_traceback): if exc_type is None: return True elif not issubclass(exc_type, HyLanguageError): if self.compiler: filename = self.compiler.filename source = self.compiler.source else: filename = None source = None exc_msg = " ".join( traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1]) ) msg = "expanding macro {}\n ".format(str(self.macro_tree[0])) msg += exc_msg raise HyMacroExpansionError(msg, self.macro_tree, filename, source) else: return False def macroexpand(tree, module, compiler=None, once=False, result_ok=True): '''If `tree` isn't an `Expression` that might be a macro call, return it unchanged. Otherwise, try to expand it. Do this repeatedly unless `once` is true. Call `as_model` after each expansion. If the return value is a compiler `Result` object, and `result_ok` is false, return the previous value. Otherwise, return the final expansion.''' if not inspect.ismodule(module): module = importlib.import_module(module) assert not compiler or compiler.module == module while isinstance(tree, Expression) and tree: fn = tree[0] if isinstance(fn, Expression) and fn and fn[0] == Symbol("."): fn = ".".join(map(mangle, fn[1:])) elif isinstance(fn, Symbol): fn = mangle(fn) else: break if fn.startswith('hy.R.'): # Special syntax for a one-shot `require`. req_from, _, fn = fn[len('hy.R.'):].partition('.') req_from = slashes2dots(req_from) try: m = importlib.import_module(req_from)._hy_macros[fn] except ImportError as e: raise HyRequireError(e.args[0]).with_traceback(None) except (AttributeError, KeyError): raise HyRequireError(f'Could not require name {fn} from {req_from}') else: # Choose the first namespace with the macro. m = ((compiler and next( (d[fn] for d in [ compiler.extra_macros, *(s['macros'] for s in reversed(compiler.local_state_stack))] if fn in d), None)) or next( (mod._hy_macros[fn] for mod in (module, builtins) if fn in getattr(mod, "_hy_macros", ())), None)) if not m: break with MacroExceptions(module, tree, compiler): if compiler: compiler.this = tree obj = m( # If the macro's first parameter is named # `_hy_compiler`, pass in the current compiler object # in its place. *([compiler] if m.__code__.co_varnames[:1] == ('_hy_compiler',) else []), *map(as_model, tree[1:])) if isinstance(obj, (hy.compiler.Result, AST)): return obj if result_ok else tree tree = replace_hy_obj(obj, tree) if once: break return tree def rename_function(f, new_name): """Create a copy of a function, but with a new name.""" f = type(f)( f.__code__.replace( co_name=new_name, **( { "co_qualname": re.sub( r"\.[^.+]\Z", "." + new_name, f.__code__.co_qualname ) if "." in f.__code__.co_qualname else new_name } if PY3_11 else {} ), ), f.__globals__, str(new_name), f.__defaults__, f.__closure__, ) f.__dict__.update(f.__dict__) return f hy-1.0.0/hy/model_patterns.py000066400000000000000000000127061467401276000161570ustar00rootroot00000000000000"Parser combinators for pattern-matching Hy model trees." from collections import namedtuple from functools import reduce from itertools import repeat from math import isinf from operator import add from funcparserlib.parser import ( NoParseError, Parser, State, a, finished, many, skip, some, ) from hy.models import ( Bytes, Complex, Dict, Expression, Float, Integer, Keyword, List, String, Symbol, Tuple, ) #: Match any token. FORM = some(lambda _: True).named('form') #: Match a :class:`Symbol `. SYM = some(lambda x: isinstance(x, Symbol)).named('Symbol') #: Match a :class:`Keyword `. KEYWORD = some(lambda x: isinstance(x, Keyword)).named('Keyword') #: Match a :class:`String `. STR = some(lambda x: isinstance(x, String)).named('String') #: Match any model type denoting a literal. LITERAL = some(lambda x: isinstance(x, (String, Integer, Float, Complex, Bytes))).named('literal') def sym(wanted): """Match and skip a symbol with a name equal to the string ``wanted``. You can begin the string with ``":"`` to check for a keyword instead.""" return _sym(wanted, skip) def keepsym(wanted): """As :func:`sym`, but the object is kept instead of skipped.""" return _sym(wanted) def _sym(wanted, f=lambda x: x): name = '`' + wanted + '`' if wanted.startswith(":"): return f(a(Keyword(wanted[1:]))).named(name) return f(some(lambda x: x == Symbol(wanted))).named(name) def whole(parsers): """Match the parsers in the given list one after another, then expect the end of the input.""" if len(parsers) == 0: return finished >> (lambda x: []) if len(parsers) == 1: return parsers[0] + finished >> (lambda x: x[:-1]) return reduce(add, parsers) + skip(finished) def _grouped(group_type, syntax_example, name, parsers): return ( some(lambda x: isinstance(x, group_type)).named(name or f'{group_type.__name__} (i.e., `{syntax_example}`)') >> (lambda x: group_type(whole(parsers).parse(x)).replace(x, recursive=False)) ) def brackets(*parsers, name = None): """Match the given parsers inside square brackets (a :class:`List `).""" return _grouped(List, '[ … ]', name, parsers) def in_tuple(*parsers, name = None): "Match the given parsers inside a :class:`Tuple `." return _grouped(Tuple, '#( … )', name, parsers) def braces(*parsers, name = None): """Match the given parsers inside curly braces (a :class:`Dict `).""" return _grouped(Dict, '{ … }', name, parsers) def pexpr(*parsers, name = None): """Match the given parsers inside a parenthesized :class:`Expression `.""" return _grouped(Expression, '( … )', name, parsers) def dolike(head): """Parse a :hy:func:`do`-like expression. ``head`` is a string used to construct a symbol for the head.""" return pexpr(sym(head), many(FORM)) def notpexpr(*disallowed_heads): """Parse any object other than an expression headed by a symbol whose name is equal to one of the given strings.""" disallowed_heads = list(map(Symbol, disallowed_heads)) return some( lambda x: not (isinstance(x, Expression) and x and x[0] in disallowed_heads) ) def unpack(kind, content_type = None): """Parse an unpacking form, returning it unchanged. ``kind`` should be ``"iterable"``, ``"mapping"``, or ``"either"``. If ``content_type`` is provided, the parser also checks that the unpacking form has exactly one argument and that argument inherits from ``content_type``.""" return some(lambda x: isinstance(x, Expression) and len(x) > 0 and x[0] in [Symbol("unpack-" + tail) for tail in (["iterable", "mapping"] if kind == "either" else [kind])] and (content_type is None or (len(x) == 2 and isinstance(x[1], content_type)))) def times(lo, hi, parser): """Parse ``parser`` several times (from ``lo`` to ``hi``, inclusive) in a row. ``hi`` can be ``float('inf')``. The result is a list no matter the number of instances.""" @Parser def f(tokens, s): result = [] for _ in range(lo): (v, s) = parser.run(tokens, s) result.append(v) end = s.max try: for _ in repeat(1) if isinf(hi) else range(hi - lo): (v, s) = parser.run(tokens, s) result.append(v) except NoParseError as e: end = e.state.max return result, State(s.pos, end) return f Tag = namedtuple("Tag", ["tag", "value"]) Tag.__doc__ = 'A named tuple; see :func:`collections.namedtuple` and :func:`tag`.' def tag(tag_name, parser): """Match on ``parser`` and produce an instance of :class:`Tag` with ``tag`` set to ``tag_name`` and ``value`` set to result of matching ``parser``.""" return parser >> (lambda x: Tag(tag_name, x)) def parse_if(pred, parser): """Return a parser that parses a token with ``parser`` if it satisfies the predicate ``pred``.""" @Parser def _parse_if(tokens, s): some(pred).run(tokens, s) return parser.run(tokens, s) return _parse_if __all__ = [ 'FORM', 'SYM', 'KEYWORD', 'STR', 'LITERAL', 'sym', 'keepsym', 'whole', 'brackets', 'in_tuple', 'braces', 'pexpr', 'dolike', 'notpexpr', 'unpack', 'times', 'Tag', 'tag', 'parse_if'] hy-1.0.0/hy/models.py000066400000000000000000000437421467401276000144260ustar00rootroot00000000000000import operator from contextlib import contextmanager from functools import reduce, total_ordering from itertools import groupby from math import isinf, isnan from hy import _initialize_env_var from hy.errors import HyWrapperError PRETTY = True @contextmanager def pretty(pretty=True): """ Context manager to temporarily enable or disable pretty-printing of Hy model reprs. """ global PRETTY old, PRETTY = PRETTY, pretty try: yield finally: PRETTY = old class Object: "An abstract base class for Hy models, which represent forms." """ The position properties (`start_line`, `end_line`, `start_column`, `end_column`) are each 1-based and inclusive. For example, a symbol `abc` starting at the first column would have `start_column` 1 and `end_column` 3. """ properties = ["_start_line", "_end_line", "_start_column", "_end_column"] def replace(self, other, recursive=False): if isinstance(other, Object): for attr in self.properties: if not hasattr(self, attr) and hasattr(other, attr): setattr(self, attr, getattr(other, attr)) else: raise TypeError( "Can't replace a non Hy object '{}' with a Hy object '{}'".format( repr(other), repr(self) ) ) return self @property def start_line(self): return getattr(self, "_start_line", 1) @start_line.setter def start_line(self, value): self._start_line = value @property def start_column(self): return getattr(self, "_start_column", 1) @start_column.setter def start_column(self, value): self._start_column = value @property def end_line(self): return getattr(self, "_end_line", 1) @end_line.setter def end_line(self, value): self._end_line = value @property def end_column(self): return getattr(self, "_end_column", 1) @end_column.setter def end_column(self, value): self._end_column = value def __repr__(self): return ( f"hy.models.{self.__class__.__name__}" f"({super(Object, self).__repr__()})" ) def __eq__(self, other): return type(self) is type(other) and super().__eq__(other) def __ne__(self, other): # We need this in case another superclass of our subclass # overrides `__ne__`. return object.__ne__(self, other) def __hash__(self): return super().__hash__() _wrappers = {} _seen = set() def as_model(x): """Convert ``x`` and any elements thereof into :ref:`models ` recursively. This function is called implicitly by Hy in many situations, such as when inserting the expansion of a macro into the surrounding code, so you don't often need to call it. One use is to ensure that models are used on both sides of a comparison:: (= 7 '7) ; => False (= (hy.as-model 7) '7) ; => True It's an error to call ``hy.as-model`` on an object that contains itself, or an object that isn't representable as a Hy literal, such as a function.""" if id(x) in _seen: raise HyWrapperError("Self-referential structure detected in {!r}".format(x)) new = _wrappers.get(type(x), lambda y: y)(x) if not isinstance(new, Object): raise HyWrapperError("Don't know how to wrap {!r}: {!r}".format(type(x), x)) if isinstance(x, Object): new = new.replace(x, recursive=False) return new def replace_hy_obj(obj, other): return as_model(obj).replace(other) def repr_indent(obj): return repr(obj).replace("\n", "\n ") def is_unpack(kind, x): return isinstance(x, Expression) and len(x) > 0 and x[0] == Symbol("unpack-" + kind) class String(Object, str): """ Represents a literal string (:class:`str`). :ivar brackets: The custom delimiter used by the bracket string that parsed to this object, or :data:`None` if it wasn't a bracket string. The outer square brackets and ``#`` aren't included, so the ``brackets`` attribute of the literal ``#[[hello]]`` is the empty string. """ def __new__(cls, s=None, brackets=None): value = super().__new__(cls, s) if brackets is not None and f"]{brackets}]" in value: raise ValueError(f"Syntactically illegal bracket string: {s!r}") value.brackets = brackets return value def __repr__(self): return "hy.models.String({}{})".format( super(Object, self).__repr__(), "" if self.brackets is None else f", brackets={self.brackets!r}", ) def __add__(self, other): return self.__class__(super().__add__(other)) _wrappers[str] = String class Bytes(Object, bytes): """ Represents a literal bytestring (:class:`bytes`). """ pass _wrappers[bytes] = Bytes class Symbol(Object, str): """ Represents a symbol. Symbol objects behave like strings under operations like :hy:func:`get `, :func:`len`, and :class:`bool`; in particular, ``(bool (hy.models.Symbol "False"))`` is true. Use :hy:func:`hy.eval` to evaluate a symbol. """ def __new__(cls, s, from_parser=False): s = str(s) if not from_parser: # Check that the symbol is syntactically legal. # import here to prevent circular imports. from hy.reader.hy_reader import as_identifier sym = as_identifier(s) if not isinstance(sym, Symbol): raise ValueError(f"Syntactically illegal symbol: {s!r}") return sym return super().__new__(cls, s) _wrappers[bool] = lambda x: Symbol("True") if x else Symbol("False") _wrappers[type(None)] = lambda _: Symbol("None") @total_ordering class Keyword(Object): """ Represents a keyword, such as ``:foo``. :ivar name: The string content of the keyword, not including the leading ``:``. No mangling is performed. """ __match_args__ = ("name",) def __init__(self, value, from_parser=False): value = str(value) if not from_parser: # Check that the keyword is syntactically legal. # import here to prevent circular imports. from hy.reader.hy_reader import HyReader from hy.reader.reader import isnormalizedspace if value and ( "." in value or any(isnormalizedspace(c) for c in value) or HyReader.NON_IDENT.intersection(value) ): raise ValueError(f'Syntactically illegal keyword: {":" + value!r}') self.name = value def __repr__(self): return f"hy.models.{self.__class__.__name__}({self.name!r})" def __str__(self): return ":%s" % self.name def __hash__(self): return hash(self.name) def __eq__(self, other): if not isinstance(other, Keyword): return NotImplemented return self.name == other.name def __ne__(self, other): if not isinstance(other, Keyword): return NotImplemented return self.name != other.name def __lt__(self, other): """Keywords behave like strings under comparison operators, but are incomparable to actual ``str`` objects.""" if not isinstance(other, Keyword): return NotImplemented return self.name < other.name def __bool__(self): """The empty keyword ``:`` is false. All others are true.""" return bool(self.name) _sentinel = object() def __call__(self, data, default=_sentinel): """Get the element of ``data`` named ``(hy.mangle self.name)``. Thus, ``(:foo bar)`` is equivalent to ``(get bar "foo")`` (which is different from ``(get bar :foo)``; dictionary keys are typically strings, not :class:`hy.models.Keyword` objects). The optional second parameter is a default value; if provided, any :class:`KeyError` from :hy:func:`get ` will be caught, and the default returned instead.""" from hy.reader import mangle try: return data[mangle(self.name)] except KeyError: if default is Keyword._sentinel: raise return default def strip_digit_separators(number): # Don't strip a _ or , if it's the first character, as _42 and # ,42 aren't valid numbers return ( number[0] + number[1:].replace("_", "").replace(",", "") if isinstance(number, str) and len(number) > 1 else number ) class Integer(Object, int): """ Represents a literal integer (:class:`int`). """ def __new__(cls, number, *args, **kwargs): return super().__new__( cls, int( strip_digit_separators(number), **( {"base": 0} if isinstance(number, str) and not number.isdigit() # `not number.isdigit()` is necessary because `base = 0` # fails on decimal integers starting with a leading 0. else {} ), ), ) _wrappers[int] = Integer def check_inf_nan_cap(arg, value): if isinstance(arg, str): if isinf(value) and "i" in arg.lower() and "Inf" not in arg: raise ValueError('Inf must be capitalized as "Inf"') if isnan(value) and "NaN" not in arg: raise ValueError('NaN must be capitalized as "NaN"') class Float(Object, float): """ Represents a literal floating-point real number (:class:`float`). """ def __new__(cls, num, *args, **kwargs): value = super().__new__(cls, strip_digit_separators(num)) check_inf_nan_cap(num, value) return value _wrappers[float] = Float class Complex(Object, complex): """ Represents a literal floating-point complex number (:class:`complex`). """ def __new__(cls, real, imag=0, *args, **kwargs): if isinstance(real, str): value = super().__new__(cls, strip_digit_separators(real)) p1, _, p2 = real.lstrip("+-").replace("-", "+").partition("+") check_inf_nan_cap(p1, value.imag if "j" in p1.lower() else value.real) if p2: check_inf_nan_cap(p2, value.imag) return value return super().__new__(cls, real, imag) _wrappers[complex] = Complex class Sequence(Object, tuple): """ An abstract base class for sequence-like forms. Sequence models can be operated on like tuples: you can iterate over them, index into them, and append them with ``+``, but you can't add, remove, or replace elements. Appending a sequence to another iterable object reuses the class of the left-hand-side object, which is useful when e.g. you want to concatenate models in a macro. When you're recursively descending through a tree of models, testing a model with ``(isinstance x hy.models.Sequence)`` is useful for deciding whether to iterate over ``x``. You can also use the Hyrule function :hy:func:`coll? ` for this purpose. """ _extra_kwargs = () def replace(self, other, recursive=True): return ( Object.replace( Object.replace( type(self)( (replace_hy_obj(x, other) for x in self), **{k: getattr(self, k) for k in self._extra_kwargs}), self), other) if recursive else Object.replace(self, other)) def __add__(self, other): return self.__class__( super().__add__(tuple(other) if isinstance(other, list) else other) ) def __getslice__(self, start, end): return self.__class__(super().__getslice__(start, end)) def __getitem__(self, item): ret = super().__getitem__(item) if isinstance(item, slice): return self.__class__(ret) return ret def __repr__(self): return self._pretty_str() if PRETTY else super().__repr__() def __str__(self): return self._pretty_str() def _pretty_str(self): with pretty(): return "hy.models.{}({})".format( self.__class__.__name__, "[\n {}]".format(",\n ".join(map(repr_indent, self))) if self else "" ) class FComponent(Sequence): """ An analog of :class:`ast.FormattedValue`. The first node in the contained sequence is the value being formatted. The rest of the sequence contains the nodes in the format spec (if any). """ _extra_kwargs = ("conversion",) def __new__(cls, s=None, conversion=None): value = super().__new__(cls, s) value.conversion = conversion return value def replace(self, other, recursive=True): super().replace(other, recursive) if hasattr(other, "conversion"): self.conversion = other.conversion return self def __repr__(self): return "hy.models.FComponent({})".format( super(Object, self).__repr__() + ", conversion=" + repr(self.conversion) ) def _string_in_node(string, node): if isinstance(node, String) and string in node: return True elif isinstance(node, (FComponent, FString)): return any(_string_in_node(string, node) for node in node) else: return False class FString(Sequence): """ Represents a format string as an iterable collection of :class:`hy.models.String` and :class:`hy.models.FComponent`. The design mimics :class:`ast.JoinedStr`. :ivar brackets: As in :class:`hy.models.String`. """ _extra_kwargs = ("brackets",) def __new__(cls, s=None, brackets=None): value = super().__new__( cls, # Join adjacent string nodes for the sake of equality # testing. ( node for is_string, components in groupby(s, lambda x: isinstance(x, String)) for node in ( [reduce(operator.add, components)] if is_string else components ) ), ) if brackets is not None and _string_in_node(f"]{brackets}]", value): raise ValueError(f"Syntactically illegal bracket string: {s!r}") value.brackets = brackets return value def __repr__(self): return self._suffixize(super().__repr__()) def __str__(self): return self._suffixize(super().__str__()) def _suffixize(self, x): if self.brackets is None: return x return "{}{}brackets={!r})".format( x[:-1], # Clip off the final close paren "" if x[-2] == "(" else ", ", self.brackets, ) class List(Sequence): """ Represents a literal :class:`list`. Many macros use this model type specially, for something other than defining a :class:`list`. For example, :hy:func:`defn` expects its function parameters as a square-bracket-delimited list, and :hy:func:`for` expects a list of iteration clauses. """ pass def recwrap(f): def lambda_to_return(l): _seen.add(id(l)) try: return f(as_model(x) for x in l) finally: _seen.remove(id(l)) return lambda_to_return _wrappers[FComponent] = recwrap(FComponent) _wrappers[FString] = lambda fstr: FString( (as_model(x) for x in fstr), brackets=fstr.brackets ) _wrappers[List] = recwrap(List) _wrappers[list] = recwrap(List) class Dict(Sequence): """ Represents a literal :class:`dict`. ``keys``, ``values``, and ``items`` methods are provided, each returning a list, although this model type does none of the normalization of a real :class:`dict`. In the case of an odd number of child models, ``keys`` returns the last child whereas ``values`` and ``items`` ignore it. """ def _pretty_str(self): with pretty(): if self: pairs = [] for k, v in zip(self[::2], self[1::2]): k, v = repr_indent(k), repr_indent(v) pairs.append( ("{0},\n {1}\n " if "\n" in k + v else "{0}, {1}").format( k, v ) ) if len(self) % 2 == 1: pairs.append( "{} # odd\n".format(repr_indent(self[-1])) ) return "hy.models.Dict([\n {}])".format( ",\n ".join(pairs), ) else: return "hy.models.Dict()" def keys(self): return list(self[0::2]) def values(self): return list(self[1::2]) def items(self): return list(zip(self.keys(), self.values())) def _dict_wrapper(d): _seen.add(id(d)) try: return Dict(as_model(x) for x in sum(d.items(), ())) finally: _seen.remove(id(d)) _wrappers[Dict] = recwrap(Dict) _wrappers[dict] = _dict_wrapper class Expression(Sequence): """ Represents a parenthesized Hy expression. """ pass _wrappers[Expression] = recwrap(Expression) class Set(Sequence): """ Represents a literal :class:`set`. Unlike actual sets, the model retains duplicates and the order of elements. """ pass _wrappers[Set] = recwrap(Set) _wrappers[set] = recwrap(Set) class Tuple(Sequence): """ Represents a literal :class:`tuple`. """ pass _wrappers[Tuple] = recwrap(Tuple) _wrappers[tuple] = recwrap(Tuple) class Lazy(Object): """ The output of :hy:func:`hy.read-many`. It represents a sequence of forms, and can be treated as an iterator. Reading each form lazily, only after evaluating the previous form, is necessary to handle reader macros correctly; see :hy:func:`hy.read-many`. """ def __init__(self, gen): super().__init__() self._gen = gen def __iter__(self): yield from self._gen def __next__(self): return self._gen.__next__() hy-1.0.0/hy/pyops.hy000066400000000000000000000220661467401276000143010ustar00rootroot00000000000000"Python provides various :ref:`binary and unary operators `. These are usually invoked in Hy using core macros of the same name: for example, ``(+ 1 2)`` calls the core macro named ``+``, which uses Python's addition operator. There are a few exceptions to the names being the same: - ``==`` in Python is :hy:func:`= ` in Hy. - ``~`` in Python is :hy:func:`bnot ` in Hy. - ``is not`` in Python is :hy:func:`is-not ` in Hy. - ``not in`` in Python is :hy:func:`not-in ` in Hy. For Python's subscription expressions (like ``x[2]``), Hy has two named macros, :hy:func:`get ` and :hy:func:`cut `. By importing from the module ``hy.pyops`` (typically with a star import, as in ``(import hy.pyops *)``), you can also use these operators as functions. Functions are first-class objects, so you can say things like ``(map - xs)`` to negate all the numbers in the list ``xs``. Since macros shadow functions, forms like ``(- 1 2)`` will still call the macro instead of the function. The functions in ``hy.pyops`` have the same semantics as their macro equivalents, with one exception: functions can't short-circuit, so the functions for operators such as ``and`` and ``!=`` unconditionally evaluate all arguments. Hy also provides macros for :ref:`Python's augmented assignment operators ` (but no equivalent functions, because Python semantics don't allow for this). These macros require at least two arguments even if the parent operator doesn't; for example, ``(-= x)`` is an error even though ``(- x)`` is legal. If the parent operator supports more than two arguments, though, so does the augmented-assignment version, using an aggregation operator to bind up all arguments past the first into a single rvalue. Typically, the aggregator is the same as the original operator: for example, ``(+= count n1 n2 n3)`` is equivalent to ``(+= count (+ n1 n2 n3))``. Exceptions (such as ``-=``, which uses the aggregator :hy:func:`+ `, so ``(-= count n1 n2 n3)`` is equivalent to ``(-= count (+ n1 n2 n3))``) are noted in the documentation for the parent operator (such as :hy:func:`- ` for ``-=``)." ;;;; Hy shadow functions (import functools [reduce] operator) (defmacro defop [op lambda-list doc #* body] "An internal macro for concisely describing the docstrings of operators." (setv name (get doc 0)) (setv d (dfor [k v] (zip (cut doc 1 None 2) (cut doc 2 None 2)) k.name (if (= v 'None) None (str v)))) (setv pyop (.get d "pyop" (.replace (str op) "-" " "))) `(defn ~op ~lambda-list ~(.format "The {} operator. {}\n\n{}{}" name "Its effect can be defined by the equivalent Python:" (.join "\n" (filter (fn [x] x) [ (when (in "nullary" d) f"- ``({op})`` → ``{(:nullary d)}``") (when (in "unary" d) f"- ``({op} x)`` → ``{(:unary d)}``") (when (.get d "binary" True) f"- ``({op} x y)`` → ``x {pyop} y``") (when (.get d "n-ary" True) f"- ``({op} a1 a2 … an)`` → ``a1 {pyop} a2 {pyop} … {pyop} an``")])) (if (not-in "agg" d) "" f"\n\nAggregator for augmented assignment: :hy:func:`{(:agg d)} `")) ~@body)) (defop + [#* args] ["addition" :nullary "0" :unary "+x"] (if (= (len args) 0) 0 (if (= (len args) 1) (+ (get args 0)) (reduce operator.add args)))) (defop - [a1 #* a-rest] ["subtraction" :pyop "-" :unary "-x" :agg "+"] (if a-rest (reduce operator.sub a-rest a1) (- a1))) (defop * [#* args] ["multiplication" :nullary "1" :unary "x"] (if (= (len args) 0) 1 (if (= (len args) 1) (get args 0) (reduce operator.mul args)))) (defop ** [a1 a2 #* a-rest] ["exponentiation"] ; We use `_foldr` instead of `reduce` because exponentiation ; is right-associative. (_foldr operator.pow (+ #(a1 a2) a-rest))) (defn _foldr [f xs] (reduce (fn [x y] (f y x)) (cut xs None None -1))) (defop / [a1 #* a-rest] ["division" :unary "1 / x" :agg "*"] (if a-rest (reduce operator.truediv a-rest a1) (/ 1 a1))) (defop // [a1 a2 #* a-rest] ["floor division"] (reduce operator.floordiv (+ #(a2) a-rest) a1)) (defop % [x y] ["modulus" :n-ary None] (% x y)) (defop @ [a1 #* a-rest] ["matrix multiplication"] (reduce operator.matmul a-rest a1)) (defop << [a1 a2 #* a-rest] ["left shift" :agg "+"] (reduce operator.lshift (+ #(a2) a-rest) a1)) (defop >> [a1 a2 #* a-rest] ["right shift" :agg "+"] (reduce operator.rshift (+ #(a2) a-rest) a1)) (defop & [a1 #* a-rest] ["bitwise AND" :unary "x"] (if a-rest (reduce operator.and_ a-rest a1) a1)) (defop | [#* args] ["bitwise OR" :nullary "0" :unary "x"] (if (= (len args) 0) 0 (if (= (len args) 1) (get args 0) (reduce operator.or_ args)))) (defop ^ [x y] ["bitwise XOR" :n-ary None] (^ x y)) (defop bnot [x] ["bitwise NOT" :pyop "~" :unary "~x" :binary None :n-ary None] (bnot x)) (defn comp-op [op a1 a-rest] "Helper for shadow comparison operators" (if a-rest (and #* (gfor #(x y) (zip (+ #(a1) a-rest) a-rest) (op x y))) True)) (defop < [a1 #* a-rest] ["less-than" :unary "True"] (comp-op operator.lt a1 a-rest)) (defop <= [a1 #* a-rest] ["less-than-or-equal-to" :unary "True"] (comp-op operator.le a1 a-rest)) (defop = [a1 #* a-rest] ["equality" :pyop "==" :unary "True"] (comp-op operator.eq a1 a-rest)) (defop is [a1 #* a-rest] ["identicality test" :unary "True"] (comp-op operator.is_ a1 a-rest)) (defop != [a1 a2 #* a-rest] ["inequality"] (comp-op operator.ne a1 (+ #(a2) a-rest))) (defop is-not [a1 a2 #* a-rest] ["negated identicality test"] (comp-op operator.is-not a1 (+ #(a2) a-rest))) (defop in [a1 a2 #* a-rest] ["membership test"] (comp-op (fn [x y] (in x y)) a1 (+ #(a2) a-rest))) (defop not-in [a1 a2 #* a-rest] ["negated membership test"] (comp-op (fn [x y] (not-in x y)) a1 (+ #(a2) a-rest))) (defop >= [a1 #* a-rest] ["greater-than-or-equal-to" :unary "True"] (comp-op operator.ge a1 a-rest)) (defop > [a1 #* a-rest] ["greater-than" :unary "True"] (comp-op operator.gt a1 a-rest)) (defop and [#* args] ["logical conjuction" :nullary "True" :unary "x"] (if (= (len args) 0) True (if (= (len args) 1) (get args 0) (reduce (fn [x y] (and x y)) args)))) (defop or [#* args] ["logical disjunction" :nullary "None" :unary "x"] (if (= (len args) 0) None (if (= (len args) 1) (get args 0) (reduce (fn [x y] (or x y)) args)))) (defn not [x] ["logical negation" :unary "not x" :binary None :n-ary None] (not x)) (defn get [coll key1 #* keys] #[[``get`` compiles to one or more :ref:`subscription expressions `, which select an element of a data structure. The first two arguments are the collection object and a key; for example, ``(get person name)`` compiles to ``person[name]``. Subsequent arguments indicate chained subscripts, so ``(get person name "surname" 0)`` becomes ``person[name]["surname"][0]``. You can assign to a ``get`` form, as in :: (setv real-estate {"price" 1,500,000}) (setv (get real-estate "price") 0) but this doesn't work with the function version of ``get`` from ``hy.pyops``, due to Python limitations on lvalues. If you're looking for the Hy equivalent of Python list slicing, as in ``foo[1:3]``, note that this is just Python's syntactic sugar for ``foo[slice(1, 3)]``, and Hy provides its own syntactic sugar for this with a different macro, :hy:func:`cut `. See also: - The :ref:`dot macro ` ``.``, which can also subscript - Hyrule's :hy:func:`assoc `, to easily assign multiple elements of a single collection]] (setv coll (get coll key1)) (for [k keys] (setv coll (get coll k))) coll) (defn cut [coll / [arg1 'sentinel] [arg2 'sentinel] [arg3 'sentinel]] #[[``cut`` compiles to a :ref:`slicing expression `, which selects multiple elements of a sequential data structure. The first argument is the object to be sliced. The remaining arguments are optional, and understood the same way as in a Python slicing expression. :: (setv x "abcdef") (cut x) ; => "abcdef" (cut x 2) ; => "ab" (cut x 2 None) ; => "cdef" (cut x 3 5) ; => "de" (cut x -3 None) ; => "def" (cut x 0 None 2) ; => "ace" A call to the ``cut`` macro (but not its function version in ``hy.pyops``) is a valid target for assignment (with :hy:func:`setv`, ``+=``, etc.) and for deletion (with :hy:func:`del`).]] (cond (= arg1 'sentinel) (cut coll) (= arg2 'sentinel) (cut coll arg1) (= arg3 'sentinel) (cut coll arg1 arg2) True (cut coll arg1 arg2 arg3))) (setv __all__ (list (map hy.mangle [ '+ '- '* '** '/ '// '% '@ '<< '>> '& '| '^ 'bnot '< '> '<= '>= '= '!= 'and 'or 'not 'is 'is-not 'in 'not-in 'get 'cut]))) hy-1.0.0/hy/reader/000077500000000000000000000000001467401276000140215ustar00rootroot00000000000000hy-1.0.0/hy/reader/__init__.py000066400000000000000000000041031467401276000161300ustar00rootroot00000000000000import re from io import StringIO import hy.models from .hy_reader import HyReader from .mangling import mangle, unmangle __all__ = ["mangle", "unmangle", "read", "read_many"] def read_many(stream, filename="", reader=None, skip_shebang=False): """Parse all the Hy source code in ``stream``, which should be a textual file-like object or a string. ``filename``, if provided, is used in error messages. If no ``reader`` is provided, a new :class:`hy.HyReader` object is created. If ``skip_shebang`` is true and a :ref:`shebang line ` is present, it's detected and discarded first. Return a value of type :class:`hy.models.Lazy`. If you want to evaluate this, be careful to allow evaluating each model before reading the next, as in ``(hy.eval (hy.read-many o))``. By contrast, forcing all the code to be read before evaluating any of it, as in ``(hy.eval `(do [~@(hy.read-many o)]))``, will yield the wrong result if one form defines a reader macro that's later used in the same stream to produce new forms. .. warning:: Thanks to reader macros, reading can execute arbitrary code. Don't read untrusted input.""" if isinstance(stream, str): stream = StringIO(stream) pos = stream.tell() source = stream.read() stream.seek(pos) reader = reader or HyReader() m = hy.models.Lazy(reader.parse( stream, filename, skip_shebang)) m.source = source m.filename = filename m.reader = reader return m def read(stream, filename=None, reader=None): """Like :hy:func:`hy.read-many`, but only one form is read, and shebangs are forbidden. The model corresponding to this specific form is returned, or, if there are no forms left in the stream, :class:`EOFError` is raised. ``stream.pos`` is left where it was immediately after the form.""" it = read_many(stream, filename, reader) try: m = next(it) except StopIteration: raise EOFError() else: m.source, m.filename, m.reader = it.source, it.filename, it.reader return m hy-1.0.0/hy/reader/exceptions.py000066400000000000000000000005601467401276000165550ustar00rootroot00000000000000from hy.errors import HySyntaxError class LexException(HySyntaxError): @classmethod def from_reader(cls, message, reader): return cls( message, None, reader._filename, reader._source, *reader._eof_tracker ) class PrematureEndOfInput(LexException): "Raised when input ends unexpectedly during parsing." __module__ = 'hy' hy-1.0.0/hy/reader/hy_reader.py000066400000000000000000000435421467401276000163450ustar00rootroot00000000000000"Character reader for parsing Hy source." import codecs import inspect from contextlib import contextmanager, nullcontext from itertools import islice import hy from hy.models import ( Bytes, Complex, Dict, Expression, FComponent, Float, FString, Integer, Keyword, List, Set, String, Symbol, Tuple, as_model, ) from .exceptions import LexException, PrematureEndOfInput from .mangling import mangle from .reader import Reader, isnormalizedspace def sym(name): return Symbol(name, from_parser=True) # Note: This is subtly different from # the `mkexpr` in hy/compiler.py ! def mkexpr(root, *args): return Expression((sym(root) if isinstance(root, str) else root, *args)) def as_identifier(ident, reader=None): """Generate a Hy model from an identifier. Also verifies the syntax of dot notation and validity of symbol names. Parameters ---------- ident : str Text to convert. reader : Reader, optional The reader to use, if any; used for generating position data for errors. Returns ------- out : a hy.models.Object subtype corresponding to the parsed text. """ try: return Integer(ident) except ValueError: pass try: return Float(ident) except ValueError: pass if ident not in ("j", "J"): try: return Complex(ident) except ValueError: pass if "." in ident: if not ident.strip("."): # It's all dots. Return it as a symbol. return sym(ident) def err(msg): raise ( ValueError(msg) if reader is None else LexException.from_reader(msg, reader) ) if ident.lstrip(".").find("..") > 0: err( "In a dotted identifier, multiple dots in a row are only allowed at the start" ) if ident.endswith("."): err("A dotted identifier can't end with a dot") head = "." * (len(ident) - len(ident.lstrip("."))) args = [as_identifier(a, reader=reader) for a in ident.lstrip(".").split(".")] if any(not isinstance(a, Symbol) for a in args): err("The parts of a dotted identifier must be symbols") return ( mkexpr(sym("."), *args) if head == "" else mkexpr(head, Symbol("None"), *args) ) if reader is None: if ( not ident or ident[0] in ":#" or any(isnormalizedspace(c) for c in ident) or HyReader.NON_IDENT.intersection(ident) ): raise ValueError(f"Syntactically illegal symbol: {ident!r}") return sym(ident) class HyReader(Reader): """A modular reader for Hy source. It inherits from :py:class:`hy.Reader`. When ``use_current_readers`` is true, initialize this reader with all reader macros from the calling module.""" __module__ = 'hy' ### # Components necessary for Reader implementation ### NON_IDENT = set("()[]{};\"'`~") _current_reader = None def __init__(self, *, use_current_readers=False): super().__init__() # move any reader macros declared using # `reader_for("#...")` to the macro table self.reader_macros = {} for tag in list(self.reader_table.keys()): if tag[0] == '#' and tag[1:]: self.reader_macros[tag[1:]] = self.reader_table.pop(tag) if use_current_readers: self.reader_macros.update( inspect.stack()[1].frame.f_globals.get("_hy_reader_macros", {}) ) @classmethod def current_reader(cls, override=None, create=True): return override or HyReader._current_reader or (cls() if create else None) @contextmanager def as_current_reader(self): old_reader = HyReader._current_reader HyReader._current_reader = self try: yield finally: HyReader._current_reader = old_reader @classmethod @contextmanager def using_reader(cls, override=None, create=True): reader = cls.current_reader(override, create) with reader.as_current_reader() if reader else nullcontext(): yield def fill_pos(self, model, start): """Set position information for ``model``. ``start`` should be a (line number, column number) tuple for the start position, whereas the end position is set to the current cursor position.""" model.start_line, model.start_column = start model.end_line, model.end_column = self.pos return model.replace(model) # `replace` will recurse into submodels and set any model # positions that are still unset the same way. def read_default(self, key): """Try to read an identifier. If the next character after that is ``"``, then instead parse it as a string with the given prefix (e.g., ``r"..."``). (This method is the default reader handler, for when nothing in the read table matches.)""" ident = key + self.read_ident() if self.peek_and_getc('"'): return self.prefixed_string('"', ident) return as_identifier(ident, reader=self) def parse(self, stream, filename=None, skip_shebang=False): """Yield all models in ``stream``. The parameters are understood as in :hy:func:`hy.read-many`.""" self._set_source(stream, filename) if skip_shebang and "".join( islice(self.peeking(eof_ok = True), len("#!"))) == "#!": for c in self.chars(): if c == "\n": break yield from self.parse_forms_until("") ### # Reading forms ### def try_parse_one_form(self): """Attempt to parse a single Hy form. Read one (non-space) character from the stream, then call the corresponding handler. Returns: hy.models.Object | None: Model optionally returned by the called handler. Handlers may return `None` to signify no parsed form (e.g., for comments). Raises: PrematureEndOfInput: If the reader hits the end of the file before fully parsing a form. LexException: If there is an error during form parsing. """ with self.as_current_reader(): try: self.slurp_space() c = self.getc() start = self._pos if not c: raise PrematureEndOfInput.from_reader( "Premature end of input while attempting to parse one form", self ) handler = self.reader_table.get(c) model = handler(self, c) if handler else self.read_default(c) if model is not None: model = self.fill_pos(model, start) model.reader = self return model return None except LexException: raise except Exception as e: raise LexException.from_reader( str(e) or "Exception thrown attempting to parse one form", self ) def parse_one_form(self): """Parse the next form in the stream and return its model. Any preceding whitespace and comments are skipped over.""" model = None while model is None: model = self.try_parse_one_form() return model def parse_forms_until(self, closer): """Yield models until the character ``closer`` is seen. This method is useful for reading sequential constructs such as lists.""" while True: self.slurp_space() if self.peek_and_getc(closer): break model = self.try_parse_one_form() if model is not None: yield model ### # Basic atoms ### @reader_for(")") @reader_for("]") @reader_for("}") def INVALID(self, key): raise LexException.from_reader( f"Ran into a '{key}' where it wasn't expected.", self ) @reader_for(";") def line_comment(self, _): any(c == "\n" for c in self.chars(eof_ok=True)) return None @reader_for(":") def keyword(self, _): ident = self.read_ident() if "." in ident: raise LexException.from_reader( "Cannot access attribute on anything other" " than a name (in order to get attributes of expressions," " use `(. )` or `(. )`)", self, ) return Keyword(ident, from_parser=True) @reader_for('"') def prefixed_string(self, _, prefix=""): prefix_chars = set(prefix) if ( len(prefix_chars) != len(prefix) or prefix_chars - set("bfr") or set("bf") <= prefix_chars ): raise LexException.from_reader(f"invalid string prefix {prefix!r}", self) escaping = False def quote_closing(c): nonlocal escaping if c == "\\": escaping = not escaping return 0 if c == '"' and not escaping: return 1 if ( escaping and "r" not in prefix and # https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals c not in ("\n\r\\'\"abfnrtv01234567x" + ("" if "b" in prefix else "NuU")) ): raise LexException.from_reader("invalid escape sequence \\" + c, self) escaping = False return 0 return self.read_string_until(quote_closing, prefix, "f" in prefix.lower()) ### # Special annotations ### @reader_for("'", ("quote",)) @reader_for("`", ("quasiquote",)) def tag_as(root): return lambda self, _: mkexpr(root, self.parse_one_form()) @reader_for("~") def unquote(self, key): return mkexpr( "unquote" + ("-splice" if self.peek_and_getc("@") else ""), self.parse_one_form(), ) ### # Sequences ### @reader_for("(", (Expression, ")")) @reader_for("[", (List, "]")) @reader_for("{", (Dict, "}")) @reader_for("#{", (Set, "}")) @reader_for("#(", (Tuple, ")")) def sequence(seq_type, closer): return lambda self, _: seq_type(self.parse_forms_until(closer)) ### # Reader tag-macros ### @reader_for("#") def tag_dispatch(self, key): """General handler for reader macros (and tag macros). Reads a full identifier after the `#` and calls the corresponding handler (this allows, e.g., `#reads-multiple-forms foo bar baz`). """ if not self.peekc().strip(): raise PrematureEndOfInput.from_reader( "Premature end of input while attempting dispatch", self ) # try dispatching tagged ident ident = self.read_ident() or self.getc() if ident in self.reader_macros: tree = self.reader_macros[ident](self, ident) return as_model(tree) if tree is not None else None raise LexException.from_reader( f"reader macro '{key + ident}' is not defined", self ) @reader_for("#_") def discard(self, _): """Discards the next parsed form.""" self.parse_one_form() return None @reader_for("#*") @reader_for("#**") def hash_star(self, stars): """Unpacking forms `#*` and `#**`, corresponding to `*` and `**` in Python.""" return mkexpr( "unpack-" + {"*": "iterable", "**": "mapping"}[stars], self.parse_one_form(), ) @reader_for("#^") def annotate(self, _): """Annotate a symbol, usually with a type.""" typ = self.parse_one_form() target = self.parse_one_form() return mkexpr("annotate", target, typ) ### # Strings # (these are more complicated because f-strings # form their own sublanguage) ### @reader_for("#[") def bracketed_string(self, _): """Bracketed strings. See the Hy docs for full details.""" delim = [] for c in self.chars(): if c == "[": break elif c == "]": raise LexException.from_reader( "Ran into a ']' where it wasn't expected.", self ) delim.append(c) delim = "".join(delim) is_fstring = delim == "f" or delim.startswith("f-") # discard single initial newline, if any, accounting for all # three styles of newline self.peek_and_getc("\x0d") self.peek_and_getc("\x0a") index = -1 def delim_closing(c): nonlocal index if c == "]": if index == len(delim): # this is the second bracket at the end of the delim return len(delim) + 2 else: # reset state, this may be the first bracket of closing delim index = 0 elif 0 <= index <= len(delim): # we're inside a possible closing delim if index < len(delim) and c == delim[index]: index += 1 else: # failed delim, reset state index = -1 return 0 return self.read_string_until(delim_closing, "r", is_fstring, brackets=delim) def read_string_until(self, closing, prefix, is_fstring, **kwargs): if is_fstring: components = self.read_fcomponents_until(closing, prefix) return FString(components, **kwargs) s = self.read_chars_until(closing, prefix, is_fstring=False) return (Bytes if isinstance(s, bytes) else String)(s, **kwargs) def read_chars_until(self, closing, prefix, is_fstring): s = [] in_named_escape = False for c in self.chars(): s.append(c) # check if c is closing n_closing_chars = closing(c) if n_closing_chars: # string has ended s = s[:-n_closing_chars] break if is_fstring: # handle braces in f-strings if c == "{": if "r" not in prefix and s[-3:] == ["\\", "N", "{"]: # ignore "\N{...}" in_named_escape = True elif not self.peek_and_getc("{"): # start f-component if not "{{" s.pop() break elif c == "}": if in_named_escape: in_named_escape = False elif not self.peek_and_getc("}"): raise SyntaxError("f-string: single '}' is not allowed") res = "".join(s).replace("\x0d\x0a", "\x0a").replace("\x0d", "\x0a") if "b" in prefix: try: res = res.encode('ascii') except UnicodeEncodeError: raise SyntaxError("bytes can only contain ASCII literal characters") if "r" not in prefix: # perform string escapes if "b" in prefix: res = codecs.escape_decode(res)[0] else: # formula taken from https://stackoverflow.com/a/57192592 # encode first to ISO-8859-1 ("Latin 1") due to a Python bug, # see https://github.com/python/cpython/issues/65530 res = res.encode('ISO-8859-1', errors='backslashreplace').decode('unicode_escape') if is_fstring: return res, n_closing_chars return res def read_fcomponents_until(self, closing, prefix): components = [] start = self.pos while True: s, closed = self.read_chars_until(closing, prefix, is_fstring=True) if s: components.append(self.fill_pos(String(s), start)) if closed: break components.extend(self.read_fcomponent(prefix)) return components def read_fcomponent(self, prefix): """May return one or two components, since the `=` debugging syntax will create a String component.""" start = self.pos values = [] conversion = None has_debug = False # read the expression, saving the text verbatim # in case we encounter debug `=` space_before = self.slurp_space() with self.saving_chars() as form_text: model = self.parse_one_form() space_between = self.slurp_space() # check for and handle debug syntax: # we emt the verbatim text before we emit the value if self.peek_and_getc("="): has_debug = True space_after = self.slurp_space() dbg_prefix = ( space_before + "".join(form_text) + space_between + "=" + space_after ) values.append(self.fill_pos(String(dbg_prefix), start)) # handle conversion code if self.peek_and_getc("!"): conversion = self.getc() self.slurp_space() def component_closing(c): if c == "}": return 1 return 0 # handle formatting options format_components = [] if self.peek_and_getc(":"): format_components = self.read_fcomponents_until(component_closing, prefix) else: if has_debug and conversion is None: conversion = "r" if not self.getc() == "}": raise LexException.from_reader("f-string: trailing junk in field", self) return values + [ self.fill_pos(FComponent((model, *format_components), conversion), start) ] hy-1.0.0/hy/reader/mangling.py000066400000000000000000000074241467401276000161760ustar00rootroot00000000000000import re import unicodedata MANGLE_DELIM = "X" normalizes_to_underscore = "_︳︴﹍﹎﹏_" def mangle(s): """Stringify the argument (with :class:`str`, not :func:`repr` or :hy:func:`hy.repr`) and convert it to a valid Python identifier according to :ref:`Hy's mangling rules `. :: (hy.mangle 'foo-bar) ; => "foo_bar" (hy.mangle "🦑") ; => "hyx_XsquidX" If the stringified argument is already both legal as a Python identifier and normalized according to Unicode normalization form KC (NFKC), it will be returned unchanged. Thus, ``hy.mangle`` is idempotent. :: (setv x '♦-->♠) (= (hy.mangle (hy.mangle x)) (hy.mangle x)) ; => True Generally, the stringifed input is expected to be parsable as a symbol. As a convenience, it can also have the syntax of a :ref:`dotted identifier `, and ``hy.mangle`` will mangle the dot-delimited parts separately. :: (hy.mangle "a.c!.d") ; => "a.hyx_cXexclamation_markX.d" """ assert s s = str(s) if "." in s and s.strip("."): return ".".join(mangle(x) if x else "" for x in s.split(".")) # Remove and save leading underscores s2 = s.lstrip(normalizes_to_underscore) leading_underscores = "_" * (len(s) - len(s2)) s = s2 # Convert hyphens without introducing a new leading underscore s = s[0] + s[1:].replace("-", "_") if s else s # Convert invalid characters or reserved words if not (leading_underscores + s).isidentifier(): # Replace illegal characters with their Unicode character # names, or hexadecimal if they don't have one. s = "hyx_" + "".join( c if c != MANGLE_DELIM and ("S" + c).isidentifier() # We prepend the "S" because some characters aren't # allowed at the start of an identifier. else "{0}{1}{0}".format( MANGLE_DELIM, unicodedata.name(c, "").lower().replace("-", "H").replace(" ", "_") or "U{:x}".format(ord(c)), ) for c in s ) # Step 5: Add back leading underscores s = leading_underscores + s # Normalize Unicode per PEP 3131. s = unicodedata.normalize("NFKC", s) assert s.isidentifier() return s def unmangle(s): """Stringify the argument and try to convert it to a pretty unmangled form. See :ref:`Hy's mangling rules `. :: (hy.unmangle "hyx_XsquidX") ; => "🦑" Unmangling may not round-trip, because different Hy symbol names can mangle to the same Python identifier. In particular, Python itself already considers distinct strings that have the same normalized form (according to NFKC), such as ``hello`` and ``𝔥𝔢𝔩𝔩𝔬``, to be the same identifier. It's an error to call ``hy.unmangle`` on something that looks like a properly mangled name but isn't. For example, ``(hy.unmangle "hyx_XpizzazzX")`` is erroneous, because there is no Unicode character named "PIZZAZZ" (yet).""" s = str(s) prefix = "" suffix = "" m = re.fullmatch(r"(_+)(.*?)(_*)", s, re.DOTALL) if m: prefix, s, suffix = m.groups() if s.startswith("hyx_"): s = re.sub( "{0}(U)?([_a-z0-9H]+?){0}".format(MANGLE_DELIM), lambda mo: chr(int(mo.group(2), base=16)) if mo.group(1) else unicodedata.lookup( mo.group(2).replace("_", " ").replace("H", "-").upper() ), s[len("hyx_") :], ) s = s.replace("_", "-") return prefix + s + suffix def slashes2dots(s): 'Interpret forward slashes as a substitute for periods.' return mangle(re.sub( r'/(-*)', lambda m: '.' + '_' * len(m.group(1)), unmangle(s))) hy-1.0.0/hy/reader/reader.py000066400000000000000000000155451467401276000156470ustar00rootroot00000000000000"Tooling for reading/parsing source character-by-character." import itertools import re from collections import deque from contextlib import contextmanager from .exceptions import PrematureEndOfInput _whitespace = re.compile(r"[ \t\n\r\f\v]+") def isnormalizedspace(s): return bool(_whitespace.match(s)) class ReaderMeta(type): """Provides a class with a dispatch map `DEFAULT_TABLE` and a decorator `@reader_for`.""" @classmethod def __prepare__(cls, name, bases): namespace = super().__prepare__(cls, name, bases) namespace["reader_for"] = cls._attach_reader return namespace @staticmethod def _attach_reader(char, args=None): def wrapper(f): handler = f if args is None else f(*args) f._readers = {**getattr(f, "_readers", {}), char: handler} return f return wrapper def __new__(cls, name, bases, namespace): del namespace["reader_for"] default_table = {} for method in namespace.values(): if callable(method) and hasattr(method, "_readers"): default_table.update(method._readers) namespace["DEFAULT_TABLE"] = default_table return super().__new__(cls, name, bases, namespace) class Reader(metaclass=ReaderMeta): """An abstract base class for reading input character-by-character. See :py:class:`hy.HyReader` for an example of creating a reader class. Attributes: ends_ident (set[str]): The set of characters that indicate the end of an identifier reader_table (dict[str, Callable]): A dictionary mapping a reader-macro key to its dispatch function pos (tuple[int, int]): A read-only (line, column) tuple indicating the current cursor position of the source being read""" __module__ = 'hy' def __init__(self): self._source = None self._filename = None self.ends_ident = set(self.NON_IDENT) self.reader_table = self.DEFAULT_TABLE.copy() def _set_source(self, stream=None, filename=None): if filename is not None: self._filename = filename if stream is not None: pos = stream.tell() self._source = stream.read() stream.seek(pos) self._stream = stream self._peek_chars = deque() self._saved_chars = [] self._pos = (1, 0) self._eof_tracker = self._pos @property def pos(self): return self._pos @contextmanager def end_identifier(self, character): """A context manager to temporarily add a new character to the :py:attr:`ends_ident` set.""" prev_ends_ident = self.ends_ident.copy() self.ends_ident.add(character) try: yield finally: self.ends_ident = prev_ends_ident ### # Character streaming ### @contextmanager def saving_chars(self): """A context manager to save all read characters. The value is a list of characters, rather than a single string.""" self._saved_chars.append([]) yield self._saved_chars[-1] saved = self._saved_chars.pop() if self._saved_chars: # `saving_chars` is being used recursively. The # characters we collected for the inner case should also # be saved for the outer case. self._saved_chars[-1].extend(saved) def peekc(self): "Peek at the next character, returning it but not consuming it." if self._peek_chars: return self._peek_chars[-1] nc = self._stream.read(1) self._peek_chars.append(nc) return nc def peeking(self, eof_ok=False): """As :func:`chars`, but without consuming any of the returned characters. This method is useful for looking several characters ahead.""" for nc in reversed(self._peek_chars): yield nc while True: c = self._stream.read(1) if not c: break self._peek_chars.appendleft(c) yield c if not c and not eof_ok: raise PrematureEndOfInput.from_reader( "Premature end of input while peeking", self ) def getc(self): """Consume one character from the stream and return it. This method does the bookkeeping for position data, so all character consumption should go through it.""" c = self.peekc() self._peek_chars.pop() if c: line, col = self._pos col += 1 if c == "\n": line += 1 col = 0 self._pos = (line, col) if not isnormalizedspace(c): self._eof_tracker = self._pos if self._saved_chars: self._saved_chars[-1].append(c) return c def peek_and_getc(self, target): """Peek at the next character and check if it's equal to ``target``, only consuming it if it's equal. A :py:class:`bool` is returned.""" nc = self.peekc() if nc == target: self.getc() return True return False def chars(self, eof_ok=False): """Consume and yield characters of the stream. If ``eof_ok`` is false (the default) and the end of the stream is reached, raise :exc:`hy.PrematureEndOfInput`.""" while True: c = self.getc() if not c: break yield c if not c and not eof_ok: raise PrematureEndOfInput.from_reader( "Premature end of input while streaming chars", self ) ### # Reading multiple characters ### def getn(self, n): "Consume and return ``n`` characters." return "".join(itertools.islice(self.chars(), n)) def slurp_space(self): "Consume and return zero or more whitespace characters." n = 0 for c in self.peeking(eof_ok=True): if not isnormalizedspace(c): break n += 1 return self.getn(n) def read_ident(self, just_peeking=False): """Read characters until we hit something in :py:attr:`ends_ident`. The characters are consumed unless ``just_peeking`` is true.""" ident = [] for nc in self.peeking(eof_ok=True): if not nc or nc in self.ends_ident or isnormalizedspace(nc): # `not nc` means EOF, but that's okay. break ident.append(nc) if not just_peeking: self.getn(len(ident)) return "".join(ident) ### # Reader dispatch logic ### def dispatch(self, tag): """Call the handler for the reader macro with key ``tag`` (a string). Return the model it produces, if any.""" return self.reader_table[tag](self, tag) hy-1.0.0/hy/repl.py000066400000000000000000000355711467401276000141060ustar00rootroot00000000000000import ast import builtins import code import codeop import hashlib import importlib import linecache import os import platform import sys import time import traceback import types from contextlib import contextmanager import hy from hy.compiler import HyASTCompiler, hy_compile from hy.completer import Completer, completion from hy.errors import ( HyLanguageError, HyMacroExpansionError, HyRequireError, filtered_hy_exceptions, ) from hy.importer import HyLoader from hy.macros import enable_readers, require, require_reader from hy.reader import mangle, read_many from hy.reader.exceptions import PrematureEndOfInput from hy.reader.hy_reader import HyReader class HyQuitter: def __init__(self, name): self.name = name def __repr__(self): return "Use (%s) or Ctrl-D (i.e. EOF) to exit" % (self.name) __str__ = __repr__ def __call__(self, code=None): try: sys.stdin.close() except: pass raise SystemExit(code) class HyHelper: def __repr__(self): return ( "Use (help) for interactive help, or (help object) for help " "about object." ) def __call__(self, *args, **kwds): import pydoc return pydoc.help(*args, **kwds) sys.last_type = None sys.last_value = None sys.last_traceback = None @contextmanager def extend_linecache(add_cmdline_cache): _linecache_checkcache = linecache.checkcache def _cmdline_checkcache(*args): _linecache_checkcache(*args) linecache.cache.update(add_cmdline_cache) linecache.checkcache = _cmdline_checkcache yield linecache.checkcache = _linecache_checkcache _codeop_maybe_compile = codeop._maybe_compile codeop._maybe_compile = (lambda compiler, source, filename, symbol: compiler(source, filename, symbol) if isinstance(compiler, HyCompile) else _codeop_maybe_compile(compiler, source, filename, symbol)) class HyCompile(codeop.Compile): """This compiler uses `linecache` like `IPython.core.compilerop.CachingCompiler`. """ def __init__( self, module, locals, ast_callback=None, hy_compiler=None, cmdline_cache={} ): self.module = module self.locals = locals self.ast_callback = ast_callback self.hy_compiler = hy_compiler self.reader = HyReader() self.skip_next_shebang = False super().__init__() if hasattr(self.module, "_hy_reader_macros"): enable_readers( self.module, self.reader, self.module._hy_reader_macros.keys() ) self.cmdline_cache = cmdline_cache def _cache(self, source, name): entry = ( len(source), time.time(), [line + "\n" for line in source.splitlines()], name, ) linecache.cache[name] = entry self.cmdline_cache[name] = entry def _update_exc_info(self): self.locals["_hy_last_type"] = sys.last_type self.locals["_hy_last_value"] = sys.last_value # Skip our frame. sys.last_traceback = getattr(sys.last_traceback, "tb_next", sys.last_traceback) self.locals["_hy_last_traceback"] = sys.last_traceback def __call__(self, source, filename="", symbol=None): symbol = "exec" # This parameter is required by `codeop.Compile`, but we # ignore it in favor of always using "exec". hash_digest = hashlib.sha1(source.encode("utf-8").strip()).hexdigest() name = "{}-{}".format(filename.strip("<>"), hash_digest) self._cache(source, name) try: # Our compiler doesn't correspond to a real, fixed source file, so # we need to [re]set these. self.hy_compiler.filename = name self.hy_compiler.source = source hy_ast = read_many( source, filename=name, reader=self.reader, skip_shebang=self.skip_next_shebang, ) self.skip_next_shebang = False exec_ast, eval_ast = hy_compile( hy_ast, self.module, root=ast.Module, get_expr=True, compiler=self.hy_compiler, filename=name, source=source, import_stdlib=False, ) if self.ast_callback: self.ast_callback(exec_ast, eval_ast) exec_code = super().__call__(exec_ast, name, symbol) eval_code = super().__call__(eval_ast, name, "eval") except Exception as e: # Capture and save the error before we handle further sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info() self._update_exc_info() if isinstance(e, (PrematureEndOfInput, SyntaxError)): raise else: # Hy will raise exceptions during compile-time that Python would # raise during run-time (e.g. import errors for `require`). In # order to work gracefully with the Python world, we convert such # Hy errors to code that purposefully reraises those exceptions in # the places where Python code expects them. # Capture a traceback without the compiler/REPL frames. exec_code = super(HyCompile, self).__call__( "raise _hy_last_value.with_traceback(_hy_last_traceback)", name, symbol, ) eval_code = super(HyCompile, self).__call__("None", name, "eval") return exec_code, eval_code class HyCommandCompiler(codeop.CommandCompiler): def __init__(self, *args, allow_incomplete = True, **kwargs): self.compiler = HyCompile(*args, **kwargs) self.allow_incomplete = allow_incomplete def __call__(self, *args, **kwargs): try: return super().__call__(*args, **kwargs) except PrematureEndOfInput: # We have to do this here, because `codeop._maybe_compile` won't # take `None` for a return value (at least not in Python 2.7) and # this exception type is also a `SyntaxError`, so it will be caught # by `code.InteractiveConsole` base methods before it reaches our # `runsource`. if not self.allow_incomplete: raise class REPL(code.InteractiveConsole): """A subclass of :class:`code.InteractiveConsole` for Hy. A convenient way to use this class to interactively debug code is to insert the following in the code you want to debug:: (.run (hy.REPL :locals {#** (globals) #** (locals)})) Or in Python: .. code-block:: python import hy; hy.REPL(locals = {**globals(), **locals()}).run() Note that as with :func:`code.interact`, changes to local variables inside the REPL are not propagated back to the original scope.""" __module__ = 'hy' def __init__(self, spy=False, spy_delimiter=('-' * 30), output_fn=None, locals=None, filename="", allow_incomplete=True): # Create a proper module for this REPL so that we can obtain it easily # (e.g. using `importlib.import_module`). # We let `InteractiveConsole` initialize `self.locals` when it's # `None`. super().__init__(locals=locals, filename=filename) module_name = self.locals.get("__name__", "__console__") # Make sure our newly created module is properly introduced to # `sys.modules`, and consistently use its namespace as `self.locals` # from here on. self.module = sys.modules.setdefault(module_name, types.ModuleType(module_name)) self.module.__dict__.update(self.locals) self.locals = self.module.__dict__ self.ps1 = "=> " self.ps2 = "... " if os.environ.get("HYSTARTUP"): try: loader = HyLoader("__hystartup__", os.environ.get("HYSTARTUP")) spec = importlib.util.spec_from_loader(loader.name, loader) mod = importlib.util.module_from_spec(spec) sys.modules.setdefault(mod.__name__, mod) loader.exec_module(mod) imports = mod.__dict__.get( "__all__", [name for name in mod.__dict__ if not name.startswith("_")], ) imports = {name: mod.__dict__[name] for name in imports} spy = spy or imports.get("repl_spy") output_fn = output_fn or imports.get("repl_output_fn") self.ps1 = imports.get("repl_ps1", self.ps1) self.ps2 = imports.get("repl_ps2", self.ps2) # Load imports and defs self.locals.update(imports) # load module macros require(mod, self.module, assignments="ALL") require_reader(mod, self.module, assignments="ALL") except Exception as e: print(e) self.hy_compiler = HyASTCompiler(self.module, module_name) self.cmdline_cache = {} self.compile = HyCommandCompiler( self.module, self.locals, ast_callback=self.ast_callback, hy_compiler=self.hy_compiler, cmdline_cache=self.cmdline_cache, allow_incomplete=allow_incomplete, ) self.spy = spy self.spy_delimiter = spy_delimiter self.last_value = None self.print_last_value = True if output_fn is None: self.output_fn = hy.repr elif callable(output_fn): self.output_fn = output_fn elif "." in output_fn: parts = [mangle(x) for x in output_fn.split(".")] module, f = ".".join(parts[:-1]), parts[-1] self.output_fn = getattr(importlib.import_module(module), f) else: self.output_fn = getattr(builtins, mangle(output_fn)) # Pre-mangle symbols for repl recent results: *1, *2, *3 self._repl_results_symbols = [mangle("*{}".format(i + 1)) for i in range(3)] self.locals.update({sym: None for sym in self._repl_results_symbols}) # Allow access to the running REPL instance self.locals["_hy_repl"] = self # Compile an empty statement to load the standard prelude exec_ast = hy_compile( read_many(""), self.module, compiler=self.hy_compiler, import_stdlib=True ) if self.ast_callback: self.ast_callback(exec_ast, None) def ast_callback(self, exec_ast, eval_ast): if self.spy: try: # Mush the two AST chunks into a single module for # conversion into Python. new_ast = ast.Module( exec_ast.body + ([] if eval_ast is None else [ast.Expr(eval_ast.body)]), type_ignores=[], ) print(ast.unparse(new_ast)) print(self.spy_delimiter) except Exception: msg = "Exception in AST callback:\n{}\n".format(traceback.format_exc()) self.write(msg) def _error_wrap(self, exc_info_override=False, *args, **kwargs): sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info() if exc_info_override: # Use a traceback that doesn't have the REPL frames. sys.last_type = self.locals.get("_hy_last_type", sys.last_type) sys.last_value = self.locals.get("_hy_last_value", sys.last_value) sys.last_traceback = self.locals.get( "_hy_last_traceback", sys.last_traceback ) sys.excepthook(sys.last_type, sys.last_value, sys.last_traceback) self.locals[mangle("*e")] = sys.last_value def showsyntaxerror(self, filename=None, source=None): if filename is None: filename = self.filename self.print_last_value = False self._error_wrap(exc_info_override=True, filename=filename) def showtraceback(self): self._error_wrap() def runcode(self, code): try: eval(code[0], self.locals) self.last_value = eval(code[1], self.locals) # Don't print `None` values. self.print_last_value = self.last_value is not None except SystemExit: raise except Exception as e: # Set this to avoid a print-out of the last value on errors. self.print_last_value = False self.showtraceback() def runsource(self, source, filename="", symbol="exec"): try: res = super().runsource(source, filename, symbol) except (HyMacroExpansionError, HyRequireError): # We need to handle these exceptions ourselves, because the base # method only handles `OverflowError`, `SyntaxError` and # `ValueError`. self.showsyntaxerror(filename) return False except (HyLanguageError): # Our compiler will also raise `TypeError`s self.showtraceback() return False # Shift exisitng REPL results if not res: next_result = self.last_value for sym in self._repl_results_symbols: self.locals[sym], next_result = next_result, self.locals[sym] # Print the value. if self.print_last_value: try: output = self.output_fn(self.last_value) except Exception: self.showtraceback() return False print(output) return res def run(self): "Start running the REPL. Return 0 when done." sentinel = [] saved_values = ( getattr(sys, "ps1", sentinel), getattr(sys, "ps2", sentinel), builtins.quit, builtins.exit, builtins.help, ) try: sys.ps1 = self.ps1 sys.ps2 = self.ps2 builtins.quit = HyQuitter("quit") builtins.exit = HyQuitter("exit") builtins.help = HyHelper() namespace = self.locals with filtered_hy_exceptions(), extend_linecache( self.cmdline_cache ), completion(Completer(namespace)): self.interact(self.banner()) finally: sys.ps1, sys.ps2, builtins.quit, builtins.exit, builtins.help = saved_values for a in "ps1", "ps2": if getattr(sys, a) is sentinel: delattr(sys, a) return 0 def banner(self): return "Hy {version}{nickname} using {py}({build}) {pyversion} on {os}".format( version=hy.__version__, nickname="" if hy.nickname is None else f' ({hy.nickname})', py=platform.python_implementation(), build=platform.python_build()[0], pyversion=platform.python_version(), os=platform.system(), ) hy-1.0.0/hy/scoping.py000066400000000000000000000327501467401276000146020ustar00rootroot00000000000000"Scope and variable tracking for Hy/Python scopes." import ast import itertools from abc import ABC, abstractmethod import hy.compat from hy.models import Expression, List, Symbol, Tuple from hy.reader import mangle def is_function_scope(scope): return isinstance(scope, ScopeFn) and scope.is_fn def is_inside_function_scope(scope): "True if any enclosing scope (including this one) is a function scope." cur = scope while not isinstance(cur, ScopeGlobal): if is_function_scope(cur): return True cur = cur.parent return False def nearest_python_scope(scope): "Return the closest enclosing scope that corresponds to a Python scope." cur = scope while isinstance(cur, (ScopeLet, ScopeGen)): cur = cur.parent return cur class OuterVar(ast.stmt): "Custom AST node that can compile to either Nonlocal or Global." def __init__(self, expr, scope, names): from hy.compiler import asty super().__init__() self.__dict__.update(asty._get_pos(expr)) self._scope = scope self.names = names class ResolveOuterVars(ast.NodeTransformer): "Find all OuterVar nodes and replace with Nonlocal or Global as necessary." def visit_OuterVar(self, node): from hy.compiler import asty scope = node._scope defined = set() undefined = list(node.names) # keep order, so can't use set while undefined and scope.parent: scope = scope.parent has = set() if isinstance(scope, ScopeFn): has = scope.defined elif isinstance(scope, ScopeLet): has = set(scope.bindings.keys()) elif isinstance(scope, ScopeGlobal): res = [] if not scope.defined.issuperset(undefined): # emit nonlocal, let python raise the error break if undefined: res.append(asty.Global(node, names=list(undefined))) if defined: res.append(asty.Nonlocal(node, names=list(defined))) return res defined.update(has.intersection(undefined)) undefined = [name for name in undefined if name not in has] return [asty.Nonlocal(node, names=node.names)] if node.names else [] class NodeRef: """ Wrapper for AST nodes that have symbol names, so that we can rename them if necessary. Each `NodeRef` corresponds to one Python identifier. `ast` nodes that accept multiple identifier names (`global`, `nonlocal`, etc) have their specific identifier referenced by their `index` in the list of `names` provided by the `ast` node. """ ACCESSOR = { ast.Name: "id", ast.Global: "names", ast.Nonlocal: "names", OuterVar: "names", } if hy.compat.PY3_10: ACCESSOR.update( { ast.MatchAs: "name", ast.MatchStar: "name", ast.MatchMapping: "rest", } ) def __init__(self, node, index=None): self.node = node self.index = index self._accessor = NodeRef.ACCESSOR[type(self.node)] @property def name(self): res = getattr(self.node, self._accessor) if self.index is not None: return res[self.index] return res @name.setter def name(self, new_name): "Used to rename `ast` identifiers" if self.index is not None: getattr(self.node, self._accessor)[self.index] = new_name else: setattr(self.node, self._accessor, new_name) @staticmethod def wrap(f): "Decorator to convert AST node parameter to NodeRef." def _wrapper(self, node, index=None): if not isinstance(node, NodeRef): node = NodeRef(node, index) return f(self, node) return _wrapper def __repr__(self): return ( f"NodeRef(name={self.name}, node={type(self.node).__name__}," f" index={self.index})" ) class ScopeBase(ABC): def __init__(self, compiler): self.parent = None self.compiler = compiler self.children = [] def create(self, scope_type, *args): "Create new scope from this one." return scope_type(self.compiler, *args) def __enter__(self): if self.compiler.scope is not self: self.parent = self.compiler.scope if self not in self.parent.children: self.parent.children.append(self) self.compiler.scope = self return self def __exit__(self, *args): if self.parent: self.compiler.scope = self.parent return False # Scope interface @abstractmethod def access(self, node, index=None): "Called when a symbol is accessed." ... @abstractmethod def assign(self, node, index=None): "Called when a symbol is assigned to." ... @abstractmethod def define(self, name): """ Called when a symbol is defined. (e.g., function names, class names, imports) """ ... @abstractmethod def define_nonlocal(self, node, root): "Called when a symbol is declared nonlocal or global." ... class ScopeGlobal(ScopeBase): """Global scope.""" def __init__(self, compiler): super().__init__(compiler) self.defined = set() "set: of all symbols created or assigned to in this scope" self.nonlocal_vars = [] """ list: of all `nonlocal`'s defined in this scope. Explicitly not a `set` so that we maintain the order they were defined in """ def __exit__(self, *args): nonlocal_vars = self.nonlocal_vars self.nonlocal_vars = [] if not self.defined.issuperset(nonlocal_vars): raise SyntaxError(f"no binding for nonlocal '{nonlocal_vars[0]}'") return super().__exit__(*args) @NodeRef.wrap def access(self, node, index=0): return node.node @NodeRef.wrap def assign(self, node): self.define(node.name) return node.node def define(self, name): self.defined.add(name) def define_nonlocal(self, node, root): if root == "nonlocal": self.nonlocal_vars.extend(node.names) else: self.defined.update(node.names) class ScopeLet(ScopeBase): """ Scope that supports let-binding by renaming bound symbols as they are accessed/assigned. Defined symbols are never renamed. """ def __init__(self, compiler): super().__init__(compiler) self.bindings = {} def _rename_if_bound(self, node): if node.name in self.bindings: node.name = self.bindings[node.name] return True return False @NodeRef.wrap def access(self, node): self._rename_if_bound(node) or self.parent.access(node) return node.node @NodeRef.wrap def assign(self, node): self._rename_if_bound(node) or self.parent.assign(node) return node.node def define(self, name): self.bindings.pop(name, None) self.parent.define(name) def define_nonlocal(self, node, root): # remove nonlocal defs of any let scopes in this Python scope cur = self while isinstance(cur, ScopeLet): for name in node.names: if root == "nonlocal": if name in cur.bindings and cur is not self: node.names.remove(name) else: cur.bindings[name] = name cur = cur.parent cur.define_nonlocal(node, root) def add(self, target, new_name=None): """Add a new let-binding target, mapped to a new, unique name.""" if isinstance(target, (str, Symbol)): if "." in target: raise ValueError("binding target may not contain a dot") name = mangle(target) if new_name is None: new_name = self.compiler.get_anon_var(f"_hy_let_{name}") self.bindings[name] = new_name if isinstance(target, Symbol): return Symbol(new_name).replace(target) return new_name if new_name is not None: raise ValueError("cannot specify name for compound targets") if isinstance(target, (List, Tuple)): return target.__class__(map(self.add, target)).replace(target) if ( isinstance(target, Expression) and target and target[0] == Symbol("unpack-iterable") ): return Expression([target[0], *map(self.add, target[1:])]).replace(target) raise ValueError(f"invalid binding target: {type(target)}") class ScopeFn(ScopeBase): """Scope that corresponds to Python's own function or class scopes.""" def __init__(self, compiler, args=None, is_async=False): super().__init__(compiler) self.defined = set() "set: of all vars defined in this scope" self.seen = [] "list: of all vars accessedto in this scope" self.nonlocal_vars = {} "set: of all `nonlocal`'s defined in this scope" self.is_fn = args is not None """ bool: `True` if this scope is being used to track a python function `False` for classes """ self.is_async = is_async """bool: `True` if this scope is for an async function, which may need special handling during compilation""" self.has_yield = False """bool: `True` if this scope is tracking a function that has `yield` statements, as generator functions may need special handling""" if args: for arg in itertools.chain( args.args, args.posonlyargs, args.kwonlyargs, [args.vararg, args.kwarg] ): if arg: self.define(arg.arg) def __exit__(self, *args): self.defined.difference_update(self.nonlocal_vars.keys()) for node in self.seen: if node.name not in self.defined: # pass unbound/nonlocal names up to parent scope self.parent.access(node) return super().__exit__(*args) @NodeRef.wrap def access(self, node): self.seen.append(node) return node.node @NodeRef.wrap def assign(self, node): self.access(node) self.define(node.name) return node.node def define(self, name): self.defined.add(name) def define_nonlocal(self, node, root): if root == "nonlocal": self.nonlocal_vars.update({name: node for name in node.names}) else: self.defined.update(node.names) for n in self.seen: if n.name in node.names: raise SyntaxError( f"name '{n.name}' is declared {root} after being used" ) if root == "nonlocal": # toss all nonlocal names up to parent scope for i in range(len(node.names)): self.parent.access(node, i) class ScopeGen(ScopeFn): """ Scope that supports generator forms (`lfor`, `gfor`, ...). If this scope is contained within a function scope or the global scope, then any variable assignments are "leaked out" to the parent scope, mimicking Python's inline generator semantics. .. note:: see :pep:`572#why-not-use-a-sublocal-scope-and-prevent-namespace-pollution` for more details on how and when variables are leaked into enclosing scopes. """ def __init__(self, compiler): super().__init__(compiler) self.iterators = set() self.assignments = [] self.exposing_assignments = False def __enter__(self): super().__enter__() enclosing = nearest_python_scope(self.parent) if isinstance(enclosing, ScopeGlobal) or is_function_scope(enclosing): # only "leak out" assignments if we're contained in a function # (or global) scope self.exposing_assignments = True return self def finalize(self): """ Access and return all the identifiers created and potentially leaked out by this generator. This should be called once and only once we've processed all of the iterators in this generators so as not to leak out any unwanted identifiers """ res = set() for node in self.assignments: if node.name not in self.nonlocal_vars: self.parent.access(node) res.add(node.name) return sorted(res) @NodeRef.wrap def assign(self, node): self.access(node) if node.name not in self.defined: self.assignments.append(node) return node.node @NodeRef.wrap def access(self, node): if not node.name in self.iterators: self.seen.append(node) return node.node def iterator(self, target): """ Declare an iteration variable name for this scope; as in Python, the iteration variable(s) cannot be reassigned. """ self.iterators.update( name.id for name in ast.walk(target) if isinstance(name, ast.Name) ) # remove potentially leakable identifiers that were actually iteration # variables found in `target` self.assignments = [ node for node in self.assignments if node.name not in self.iterators ] self.seen = [node for node in self.seen if node.name not in self.iterators] hy-1.0.0/issue_template.md000066400000000000000000000007001467401276000155010ustar00rootroot00000000000000Hy's issue list is for bugs, complaints, and feature requests. For help with Hy, ask on our Github Discussions page or Stack Overflow with the `[hy]` tag. If you're reporting a bug, make sure you can reproduce it with the very latest, bleeding-edge version of Hy from the `master` branch on GitHub. Bugs in stable versions of Hy are fixed on `master` before the fix makes it into a new stable release. You can delete this text after reading it. hy-1.0.0/requirements-dev.txt000066400000000000000000000001711467401276000161760ustar00rootroot00000000000000pytest >= 7 # documentation Pygments >= 2.18.0 Sphinx == 5.0.2 git+https://github.com/hylang/sphinxcontrib-hydomain.git hy-1.0.0/setup.cfg000066400000000000000000000004061467401276000137600ustar00rootroot00000000000000[tool:pytest] # Be sure to include Hy test functions with mangled names. python_functions=test_* hyx_test_* filterwarnings = once::DeprecationWarning once::PendingDeprecationWarning ignore::SyntaxWarning ignore::pytest.PytestReturnNotNoneWarning hy-1.0.0/setup.py000077500000000000000000000052301467401276000136540ustar00rootroot00000000000000#!/usr/bin/env python # Set both `setup_requires` and `install_requires` with our # dependencies, since we need to compile Hy files during setup. And # put this as the first statement in the file so it's easy to parse # out without executing the file. requires = [ "funcparserlib ~= 1.0", 'astor>=0.8 ; python_version < "3.9"', ] import os import fastentrypoints # Monkey-patches setuptools. from setuptools import find_packages, setup from setuptools.command.install import install os.chdir(os.path.split(os.path.abspath(__file__))[0]) PKG = "hy" long_description = """Hy is a Lisp dialect that's embedded in Python. Since Hy transforms its Lisp code into Python abstract syntax tree (AST) objects, you have the whole beautiful world of Python at your fingertips, in Lisp form.""" class install(install): def run(self): super().run() import py_compile import hy # for compile hooks for path in set(self.get_outputs()): if path.endswith(".hy"): py_compile.compile( path, invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, ) setup( name=PKG, version='1.0.0', setup_requires=["wheel"] + requires, install_requires=requires, python_requires=">= 3.8, < 3.14", entry_points={ "console_scripts": [ "hy = hy.cmdline:hy_main", "hyc = hy.cmdline:hyc_main", "hy2py = hy.cmdline:hy2py_main" ] }, packages=find_packages(exclude=["tests*"]), package_data={ "": ["*.hy"], }, author="Paul Tagliamonte", author_email="tag@pault.ag", long_description=long_description, description="A Lisp dialect embedded in Python", license="Expat", url="http://hylang.org/", platforms=["any"], classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: DFSG approved", "License :: OSI Approved :: MIT License", # Really "Expat". Ugh. "Operating System :: OS Independent", "Programming Language :: Hy", "Programming Language :: Lisp", "Programming Language :: Python", "Programming Language :: Python :: Implementation :: PyPy", "Environment :: WebAssembly :: Emscripten", "Topic :: Software Development :: Code Generators", "Topic :: Software Development :: Compilers", "Topic :: Software Development :: Libraries", ], project_urls={ "Documentation": "http://hylang.org/hy/doc", "Source": "https://github.com/hylang/hy", }, cmdclass={ "install": install, }, ) hy-1.0.0/tests/000077500000000000000000000000001467401276000133015ustar00rootroot00000000000000hy-1.0.0/tests/__init__.py000066400000000000000000000000001467401276000154000ustar00rootroot00000000000000hy-1.0.0/tests/compilers/000077500000000000000000000000001467401276000152765ustar00rootroot00000000000000hy-1.0.0/tests/compilers/__init__.py000066400000000000000000000000001467401276000173750ustar00rootroot00000000000000hy-1.0.0/tests/compilers/test_ast.py000066400000000000000000000432501467401276000175020ustar00rootroot00000000000000import ast from textwrap import dedent import pytest import hy from hy.compat import PY3_11 from hy.compiler import hy_compile from hy.errors import HyError, HyLanguageError from hy.reader import read_many from hy.reader.exceptions import LexException def _ast_spotcheck(arg, root, secondary): if "." in arg: local, full = arg.split(".", 1) return _ast_spotcheck(full, getattr(root, local), getattr(secondary, local)) assert getattr(root, arg) == getattr(secondary, arg) def can_compile(expr, import_stdlib=False, iff=True): return (hy_compile(read_many(expr), __name__, import_stdlib=import_stdlib) if iff else cant_compile(expr)) def can_eval(expr): return hy.eval(read_many(expr)) def cant_compile(expr): with pytest.raises(HyError) as excinfo: hy_compile(read_many(expr), __name__) # Anything that can't be compiled should raise a user friendly # error, otherwise it's a compiler bug. assert issubclass(excinfo.type, HyLanguageError) assert excinfo.value.msg return excinfo.value def s(x): return can_compile('"module docstring" ' + x).body[-1].value.value def test_ast_bad_type(): "Make sure AST breakage can happen" class C: pass with pytest.raises(TypeError): hy_compile(C(), __name__, filename="", source="") def test_empty_expr(): "Empty expressions should be illegal at the top level." cant_compile("(print ())") can_compile("(print '())") def test_dot_unpacking(): can_compile("(.meth obj #* args az)") cant_compile("(.meth #* args az)") cant_compile("(. foo #* bar baz)") can_compile("(.meth obj #** args az)") can_compile("(.meth #** args obj)") cant_compile("(. foo #** bar baz)") def test_ast_bad_if(): cant_compile("(if)") cant_compile("(if foobar)") cant_compile("(if 1 2 3 4 5)") def test_ast_valid_if(): can_compile("(if foo bar baz)") def test_ast_bad_while(): cant_compile("(while)") def test_ast_good_do(): can_compile("(do)") can_compile("(do 1)") def test_ast_good_raise(): can_compile("(raise)") can_compile("(raise Exception)") can_compile("(raise e)") def test_ast_raise_from(): can_compile("(raise Exception :from NameError)") def test_ast_bad_raise(): cant_compile("(raise Exception Exception)") def test_ast_good_try(): can_compile("(try 1 (except []) (else 1))") can_compile("(try 1 (finally 1))") can_compile("(try 1 (except []) (finally 1))") can_compile("(try 1 (except [x]) (except [y]) (finally 1))") can_compile("(try 1 (except []) (else 1) (finally 1))") can_compile("(try 1 (except [x]) (except [y]) (else 1) (finally 1))") can_compile(iff = PY3_11, expr = "(try 1 (except* [x]))") can_compile(iff = PY3_11, expr = "(try 1 (except* [x]) (else 1) (finally 1))") def test_ast_bad_try(): cant_compile("(try (do) (else 1) (else 2))") cant_compile("(try 1 (else 1) (except []))") cant_compile("(try 1 (finally 1) (except []))") cant_compile("(try 1 (except []) (finally 1) (else 1))") cant_compile("(try 1 (except* [x]) (except [x]))") cant_compile("(try 1 (except [x]) (except* [x]))") def test_ast_good_except(): can_compile("(try 1 (except []))") can_compile("(try 1 (except [Foobar]))") can_compile("(try 1 (except [[]]))") can_compile("(try 1 (except [x FooBar]))") can_compile("(try 1 (except [x [FooBar BarFoo]]))") can_compile("(try 1 (except [x [FooBar BarFoo]]))") def test_ast_bad_except(): cant_compile("(except 1)") cant_compile("(try 1 (except))") cant_compile("(try 1 (except 1))") cant_compile("(try 1 (except [1 3]))") cant_compile("(try 1 (except [(f) [IOError ValueError]]))") cant_compile("(try 1 (except [x [FooBar] BarBar]))") def test_ast_good_assert(): can_compile("(assert 1)") can_compile('(assert 1 "Assert label")') can_compile('(assert 1 (+ "spam " "eggs"))') can_compile("(assert 1 12345)") can_compile("(assert 1 None)") can_compile('(assert 1 (+ 2 "incoming eggsception"))') def test_ast_bad_assert(): cant_compile("(assert)") cant_compile("(assert 1 2 3)") cant_compile("(assert 1 [1 2] 3)") def test_ast_good_global(): can_compile("(global)") can_compile("(global a)") can_compile("(global foo bar)") def test_ast_bad_global(): cant_compile("(global (foo))") def test_ast_good_nonlocal(): can_compile("(nonlocal)") can_compile("(do (setv a 0) (nonlocal a))") can_compile("(do (setv foo 0 bar 0) (nonlocal foo bar))") def test_ast_bad_nonlocal(): cant_compile("(nonlocal (foo))") def test_ast_good_defclass(): can_compile("(defclass a)") can_compile("(defclass a [])") can_compile("(defclass a [] None 42)") can_compile('(defclass a [] None "test")') can_compile('(defclass a [] None (print "foo"))') def test_ast_good_defclass_with_metaclass(): can_compile("(defclass a [:metaclass b])") can_compile("(defclass a [:b c])") def test_ast_bad_defclass(): cant_compile("(defclass)") cant_compile("(defclass a None)") cant_compile("(defclass a None None)") # https://github.com/hylang/hy/issues/1920 cant_compile("(defclass a [] (setv x))") cant_compile("(defclass a [] (setv x 1 y))") def test_ast_good_lambda(): can_compile("(fn [])") can_compile("(fn [] 1)") def test_ast_bad_lambda(): cant_compile("(fn)") cant_compile("(fn ())") cant_compile("(fn () 1)") cant_compile("(fn (x) 1)") cant_compile('(fn "foo")') def test_ast_good_yield(): can_compile("(yield 1)") def test_ast_bad_yield(): cant_compile("(yield 1 2)") def test_ast_import_mangle_dotted(): """Mangling a module name with a period shouldn't create a spurious `asname`.""" code = can_compile("(import a-b.c)") assert code.body[0].names[0].name == "a_b.c" assert code.body[0].names[0].asname is None def test_ast_good_import_from(): can_compile("(import x [y])") def test_ast_require(): can_compile("(require tests.resources.tlib)") can_compile("(require tests.resources.tlib [qplah parald])") can_compile("(require tests.resources.tlib *)") can_compile("(require tests.resources.tlib :as foobar)") can_compile("(require tests.resources.tlib [qplah :as quiz])") can_compile("(require tests.resources.tlib [qplah :as quiz parald])") cant_compile("(require [tests.resources.tlib])") cant_compile("(require tests.resources.tlib [#taggart]") def test_ast_import_require_dotted(): """As in Python, it should be a compile-time error to attempt to import a dotted name.""" cant_compile("(import spam [foo.bar])") cant_compile("(require spam [foo.bar])") def test_ast_multi_require(): # https://github.com/hylang/hy/issues/1903 x = can_compile( """(require tests.resources.tlib [qplah] tests.resources.macros [test-macro])""" ) assert sum(1 for stmt in x.body if isinstance(stmt, ast.Expr)) == 2 dump = ast.dump(x) assert "qplah" in dump assert "test-macro" in dump def test_ast_good_get(): can_compile("(get x y)") def test_ast_bad_get(): cant_compile("(get)") cant_compile("(get 1)") def test_ast_good_cut(): can_compile("(cut x)") can_compile("(cut x y)") can_compile("(cut x y z)") can_compile("(cut x y z t)") def test_ast_bad_cut(): cant_compile("(cut)") cant_compile("(cut 1 2 3 4 5)") def test_ast_bad_with(): cant_compile("(with)") cant_compile("(with [])") cant_compile("(with [] (pass))") def test_ast_valid_while(): can_compile("(while foo bar)") can_compile("(while foo bar (else baz))") def test_ast_valid_for(): can_compile("(for [a 2] (print a))") def test_nullary_break_continue(): can_compile("(while 1 (break))") cant_compile("(while 1 (break 1))") can_compile("(while 1 (continue))") cant_compile("(while 1 (continue 1))") def test_ast_expression_basics(): """Ensure basic AST expression conversion works.""" code = can_compile("(foo bar)").body[0] tree = ast.Expr( value=ast.Call( func=ast.Name( id="foo", ctx=ast.Load(), ), args=[ast.Name(id="bar", ctx=ast.Load())], keywords=[], ) ) _ast_spotcheck("value.func.id", code, tree) def test_ast_anon_fns_basics(): code = can_compile("(fn [x] (* x x))").body[0].value assert type(code) == ast.Lambda code = can_compile('(fn [x] (print "multiform") (* x x))').body[0] assert type(code) == ast.FunctionDef can_compile("(fn [x])") cant_compile("(fn)") def test_ast_lambda_lists(): cant_compile("(fn [[a b c]] a)") cant_compile("(fn [[1 2]] (list 1 2))") def test_ast_print(): code = can_compile('(print "foo")').body[0] assert type(code.value) == ast.Call def test_ast_tuple(): code = can_compile("#(1 2 3)").body[0].value assert type(code) == ast.Tuple def test_lambda_list_keywords_rest(): can_compile("(fn [x #* xs] (print xs))") cant_compile("(fn [x #* xs #* ys] (print xs))") can_compile("(fn [[a None] #* xs] (print xs))") def test_lambda_list_keywords_kwargs(): can_compile("(fn [x #** kw] (list x kw))") cant_compile("(fn [x #** xs #** ys] (list x xs ys))") can_compile("(fn [[x None] #** kw] (list x kw))") def test_lambda_list_keywords_kwonly(): kwonly_demo = "(fn [* a [b 2]] (print 1) (print a b))" code = can_compile(kwonly_demo) for i, kwonlyarg_name in enumerate(("a", "b")): assert kwonlyarg_name == code.body[0].args.kwonlyargs[i].arg assert code.body[0].args.kw_defaults[0] is None assert code.body[0].args.kw_defaults[1].value == 2 def test_lambda_list_keywords_mixed(): can_compile("(fn [x #* xs #** kw] (list x xs kw))") cant_compile('(fn [x #* xs &fasfkey {bar "baz"}])') can_compile("(fn [x #* xs kwoxs #** kwxs]" " (list x xs kwxs kwoxs))") def test_missing_keyword_argument_value(): with pytest.raises(HyLanguageError) as excinfo: can_compile("((fn [x] x) :x)") assert excinfo.value.msg == "Keyword argument :x needs a value." def test_ast_unicode_strings(): def _compile_string(s): hy_s = hy.models.String(s) code = hy_compile( [hy_s], __name__, filename="", source=s, import_stdlib=False ) # We put hy_s in a list so it isn't interpreted as a docstring. # code == ast.Module(body=[ast.Expr(value=ast.List(elts=[ast.Constant(value=xxx)]))]) return code.body[0].value.elts[0].value assert _compile_string("test") == "test" assert _compile_string("\u03b1\u03b2") == "\u03b1\u03b2" assert _compile_string("\xc3\xa9") == "\xc3\xa9" def test_ast_unicode_vs_bytes(): assert s('"hello"') == "hello" assert type(s('"hello"')) is str assert s('b"hello"') == b"hello" assert type(s('b"hello"')) is bytes assert s('b"\\xa0"') == bytes([160]) def test_format_string(): assert can_compile('f"hello world"') assert can_compile('f"hello {(+ 1 1)} world"') assert can_compile('f"hello world {(+ 1 1)}"') assert cant_compile('f"hello {(+ 1 1) world"') assert cant_compile('f"hello (+ 1 1)} world"') assert cant_compile('f"hello {(+ 1 1} world"') assert can_compile(r'f"hello {"n"} world"') assert can_compile(r'f"hello {"\\n"} world"') def test_ast_bracket_string(): assert s(r"#[[empty delims]]") == "empty delims" assert s(r"#[my delim[fizzle]my delim]") == "fizzle" assert s(r"#[[]]") == "" assert s(r"#[my delim[]my delim]") == "" assert type(s("#[X[hello]X]")) is str assert s(r"#[X[raw\nstring]X]") == "raw\\nstring" assert s(r"#[foozle[aa foozli bb ]foozle]") == "aa foozli bb " assert s(r"#[([unbalanced](]") == "unbalanced" assert s(r"#[(1💯@)} {a![hello world](1💯@)} {a!]") == "hello world" assert (s(r'''#[X[ Remove the leading newline, please. ]X]''') == 'Remove the leading newline, please.\n') assert (s(r'''#[X[ Only one leading newline should be removed. ]X]''') == '\n\nOnly one leading newline should be removed.\n') def test_literal_newlines(): # https://github.com/hylang/hy/issues/2239 assert s('"\r\nhello\r\nworld"') == "\nhello\nworld" assert s('r"\r\nhello\r\nworld"') == "\nhello\nworld" assert s('b"\r\nhello\r\nworld"') == b"\nhello\nworld" assert s('br"\r\nhello\r\nworld"') == b"\nhello\nworld" assert s("#[[\r\nhello\r\nworld]]") == "hello\nworld" assert s("#[[\rhello\rworld]]") == "hello\nworld" def test_linear_boolop(): """Operations like `(and 1 2 3)` should use only one `BoolOp`, instead of an equivalent nested sequence of `BoolOp`s.""" for op in ("and", "or"): node = can_compile(f'({op} 1 2.0 True "hi" 5)').body[0].value assert len(node.values) == 5 assert all(isinstance(v, ast.Constant) for v in node.values) def test_compile_error(): """Ensure we get compile error in tricky cases""" with pytest.raises(HyLanguageError) as excinfo: can_compile("(fn [] (in [1 2 3]))") def test_for_compile_error(): """Ensure we get compile error in tricky 'for' cases""" with pytest.raises(hy.PrematureEndOfInput) as excinfo: can_compile("(fn [] (for)") assert excinfo.value.msg.startswith("Premature end of input") with pytest.raises(LexException) as excinfo: can_compile("(fn [] (for [x y] x)))") assert excinfo.value.msg == "Ran into a ')' where it wasn't expected." cant_compile("(fn [] (for [x] x))") def test_attribute_access(): 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_misplaced_dots(): cant_compile("foo.") cant_compile("foo..") cant_compile("foo.bar.") cant_compile("foo.bar..") cant_compile("foo..bar") def test_bad_setv(): cant_compile("(setv (a b) [1 2])") def test_defn(): cant_compile('(defn "hy" [] 1)') cant_compile("(defn :hy [] 1)") can_compile("(defn &hy [] 1)") cant_compile('(defn hy "foo")') def test_setv_builtins(): """Ensure that assigning to a builtin fails, unless in a class""" cant_compile("(setv None 42)") can_compile("(defclass A [] (defn get [self] 42))") can_compile( """ (defclass A [] (defn get [self] 42) (defclass B [] (defn get [self] 42)) (defn if [self] 0)) """ ) def placeholder_macro(x, ename=None): with pytest.raises(HyLanguageError) as e: can_compile(f"({x})") assert f"`{ename or x}` is not allowed here" in e.value.msg def test_top_level_unquote(): placeholder_macro("unquote") placeholder_macro("unquote-splice") placeholder_macro("unquote_splice", "unquote-splice") def test_bad_exception(): placeholder_macro("except") placeholder_macro("except*") placeholder_macro(hy.mangle("except*"), "except*") def test_lots_of_comment_lines(): # https://github.com/hylang/hy/issues/1313 can_compile(1000 * ";\n") def test_compiler_macro_tag_try(): # https://github.com/hylang/hy/issues/1350 can_compile("(defmacro foo [] (try None (except [] None)) `())") def test_ast_good_yield_from(): can_compile("(yield :from [1 2])") can_compile("(yield :from)") def test_ast_bad_yield_from(): cant_compile("(yield :ploopy [1 2])") def test_eval_generator_with_return(): can_eval("(fn [] (yield 1) (yield 2) (return))") def test_futures_imports(): """Make sure __future__ imports go first.""" hy_ast = can_compile( "(import __future__ [print_function])" "(import sys)" "(setv some [1 2])" "(print (cut some 1 None))" ) assert hy_ast.body[0].module == "__future__" def test_py(): def py(x): assert ( ast.dump(can_compile(f'(py "{x}")')) == ast.dump(ast.parse('(' + x + '\n)'))) py("1 + 1") # https://github.com/hylang/hy/issues/2406 py(" 1 + 1 ") py(""" 1 + 1 """) py(""" 1 + 2 + 3 + 4 + 5 + # hi! 6 # bye """) cant_compile('(py "1 +")') cant_compile('(py "if 1:\n 2")') def test_pys(): def pys(x): assert ( ast.dump(can_compile(f'(pys "{x}")')) == ast.dump(ast.parse(dedent(x)))) pys("") pys("1 + 1") pys("if 1:\n 2") pys("if 1: 2") pys(" if 1: 2 ") pys(''' if 1: 2 elif 3: 4''') cant_compile('(pys "if 1\n 2")') cant_compile('''(pys " if 1: 2 elif 3: 4")''') def test_models_accessible(): # https://github.com/hylang/hy/issues/1045 can_eval("hy.models.Symbol") can_eval("hy.models.List") can_eval("hy.models.Dict") def test_module_prelude(): """Make sure the hy prelude appears at the top of a compiled module.""" for code, n in ("", 1), ("(setv flag (- hy.models.Symbol 1))", 2): x = can_compile(code, import_stdlib=True).body assert len(x) == n assert isinstance(x[0], ast.Import) x = x[0].names[0] assert x.name == "hy" assert x.asname is None def test_pragma(): can_compile("(pragma)") can_compile("(pragma :warn-on-core-shadow True)") cant_compile("(pragma :native-code True)") def test_error_with_expectation(): def check(code, expected): assert cant_compile(code).msg.endswith("expected: " + expected) check("(defmacro)", "Symbol") check("(quote)", "form") check("(py)", "String") check("(py a)", "String") check('(py "foo" a)', "end of macro call") check('(for a)', "square-bracketed loop clauses") hy-1.0.0/tests/compilers/test_compiler.py000066400000000000000000000052601467401276000205240ustar00rootroot00000000000000import ast import types from hy import compiler from hy.models import Expression, Integer, List, Symbol from hy.reader import read_many from hy.compiler import hy_compile def make_expression(*args): h = Expression(args) h.start_line = 1 h.end_line = 1 h.start_column = 1 h.end_column = 1 return h.replace(h) def hy2py(s): return ast.unparse(hy_compile(read_many(s), "test", import_stdlib=False)) def test_compiler_bare_names(): """ Check that the compiler doesn't drop bare names from code branches """ e = make_expression(Symbol("do"), Symbol("a"), Symbol("b"), Symbol("c")) ret = compiler.HyASTCompiler(types.ModuleType("test")).compile(e) # We expect two statements and a final expr. assert len(ret.stmts) == 2 for stmt, symbol in zip(ret.stmts, "ab"): assert isinstance(stmt, ast.Expr) assert isinstance(stmt.value, ast.Name) assert stmt.value.id == symbol assert isinstance(ret.expr, ast.Name) assert ret.expr.id == "c" def test_compiler_yield_return(): """ Check that the compiler correctly generates return statements for a generator function. In Python versions prior to 3.3, the return statement in a generator can't take a value, so the final expression should not generate a return statement. From 3.3 onwards a return value should be generated. """ e = make_expression( Symbol("fn"), List(), Expression([Symbol("yield"), Integer(2)]), Expression([Symbol("+"), Integer(1), Integer(1)]), ) ret = compiler.HyASTCompiler(types.ModuleType("test")).compile_atom(e) assert len(ret.stmts) == 1 (stmt,) = ret.stmts assert isinstance(stmt, ast.FunctionDef) body = stmt.body assert len(body) == 2 assert isinstance(body[0], ast.Expr) assert isinstance(body[0].value, ast.Yield) assert isinstance(body[1], ast.Return) assert isinstance(body[1].value, ast.BinOp) # https://github.com/hylang/hy/issues/854 def test_compact_logic(): """ Check that we don't generate multiple unnecessary if-statements when compiling the short-circuiting operators. """ py = hy2py("(and 1 2 3 (do (setv x 4) x) 5 6)") assert py.count("if") == 1 py = hy2py("(or 1 2 3 (do (setv x 4) x) 5 6 (do (setv y 7)))") assert py.count("if") == 2 def test_correct_logic(): """ Check that we're not overzealous when compacting boolean operators. """ py = hy2py("(or (and 1 2) (and 3 4))") assert py.count("and") == 2 assert py.count("or") == 1 py = hy2py("(and (do (setv x 4) (or x 3)) 5 6)") assert py.count("x = 4") == 1 assert py.count("x or 3") == 1 assert py.count("and") == 2 hy-1.0.0/tests/importer/000077500000000000000000000000001467401276000151425ustar00rootroot00000000000000hy-1.0.0/tests/importer/__init__.py000066400000000000000000000000001467401276000172410ustar00rootroot00000000000000hy-1.0.0/tests/importer/test_importer.py000066400000000000000000000210361467401276000204160ustar00rootroot00000000000000import ast import importlib import runpy import sys from importlib import reload from pathlib import Path import pytest import hy from hy.compiler import hy_compile from hy.errors import HyLanguageError, hy_exc_handler from hy.importer import HyLoader from hy.reader import read_many def test_basics(): "Make sure the basics of the importer work" resources_mod = importlib.import_module("tests.resources") assert hasattr(resources_mod, "kwtest") bin_mod = importlib.import_module("tests.resources.bin") assert hasattr(bin_mod, "_null_fn_for_import_test") def test_runpy(): # `runpy` won't update cached bytecode. It's not clear if that's # intentional. basic_ns = runpy.run_path("tests/resources/importer/basic.hy") assert "square" in basic_ns main_ns = runpy.run_path("tests/resources/bin") assert main_ns["visited_main"] == 1 del main_ns main_ns = runpy.run_module("tests.resources.bin") assert main_ns["visited_main"] == 1 with pytest.raises(IOError): runpy.run_path("tests/resources/foobarbaz.py") def test_stringer(): _ast = hy_compile( read_many("(defn square [x] (* x x))"), __name__, import_stdlib=False ) assert type(_ast.body[0]) == ast.FunctionDef def test_imports(): testLoader = HyLoader("tests.resources.importer.a", "tests/resources/importer/a.hy") spec = importlib.util.spec_from_loader(testLoader.name, testLoader) mod = importlib.util.module_from_spec(spec) with pytest.raises(NameError) as excinfo: testLoader.exec_module(mod) assert "thisshouldnotwork" in excinfo.value.args[0] def test_import_error_reporting(): "Make sure that (import) reports errors correctly." with pytest.raises(HyLanguageError): hy_compile(read_many('(import "sys")'), __name__) def test_import_error_cleanup(): "Failed initial imports should not leave dead modules in `sys.modules`." with pytest.raises(hy.errors.HyMacroExpansionError): importlib.import_module("tests.resources.fails") assert "tests.resources.fails" not in sys.modules @pytest.mark.skipif(sys.dont_write_bytecode, reason="Bytecode generation is suppressed") def test_import_autocompiles(tmp_path): "Test that (import) byte-compiles the module." p = tmp_path / "mymodule.hy" p.write_text('(defn pyctest [s] (+ "X" s "Y"))') def import_from_path(path): spec = importlib.util.spec_from_file_location("mymodule", path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module assert import_from_path(p).pyctest("flim") == "XflimY" assert Path(importlib.util.cache_from_source(p)).exists() # Try running the bytecode. assert ( import_from_path(importlib.util.cache_from_source(p)).pyctest("flam") == "XflamY" ) def test_eval(): def eval_str(s): return hy.eval(hy.read(s)) assert eval_str("[1 2 3]") == [1, 2, 3] assert eval_str('{"dog" "bark" "cat" "meow"}') == {"dog": "bark", "cat": "meow"} assert eval_str("#(1 2 3)") == (1, 2, 3) assert eval_str("#{3 1 2}") == {1, 2, 3} assert eval_str('(.strip " fooooo ")') == "fooooo" assert ( eval_str('(if True "this is if true" "this is if false")') == "this is if true" ) assert eval_str("(lfor num (range 100) :if (= (% num 2) 1) (pow num 2))") == [ pow(num, 2) for num in range(100) if num % 2 == 1 ] def test_reload(tmp_path, monkeypatch): """Generate a test module, confirm that it imports properly (and puts the module in `sys.modules`), then modify the module so that it produces an error when reloaded. Next, fix the error, reload, and check that the module is updated and working fine. Rinse, repeat. This test is adapted from CPython's `test_import.py`. """ def unlink(filename): Path(source).unlink() bytecode = importlib.util.cache_from_source(source) if Path(bytecode).is_file(): Path(bytecode).unlink() TESTFN = "testfn" source = tmp_path / (TESTFN + ".hy") source.write_text("(setv a 1) (setv b 2)") monkeypatch.syspath_prepend(tmp_path) try: mod = importlib.import_module(TESTFN) assert TESTFN in sys.modules assert mod.a == 1 assert mod.b == 2 # On WinXP, just replacing the .py file wasn't enough to # convince reload() to reparse it. Maybe the timestamp didn't # move enough. We force it to get reparsed by removing the # compiled file too. unlink(source) # Now damage the module. source.write_text("(setv a 10) (setv b (// 20 0))") with pytest.raises(ZeroDivisionError): reload(mod) # But we still expect the module to be in sys.modules. mod = sys.modules.get(TESTFN) assert mod is not None # We should have replaced a w/ 10, but the old b value should # stick. assert mod.a == 10 assert mod.b == 2 # Now fix the issue and reload the module. unlink(source) source.write_text("(setv a 11) (setv b (// 20 1))") reload(mod) mod = sys.modules.get(TESTFN) assert mod is not None assert mod.a == 11 assert mod.b == 20 # Now cause a syntax error (a missing parenthesis) unlink(source) source.write_text("(setv a 11 (setv b (// 20 1))") with pytest.raises(hy.PrematureEndOfInput): reload(mod) mod = sys.modules.get(TESTFN) assert mod is not None assert mod.a == 11 assert mod.b == 20 # Fix it and retry unlink(source) source.write_text("(setv a 12) (setv b (// 10 1))") reload(mod) mod = sys.modules.get(TESTFN) assert mod is not None assert mod.a == 12 assert mod.b == 10 finally: if TESTFN in sys.modules: del sys.modules[TESTFN] def test_reload_reexecute(capsys): """A module is re-executed when it's reloaded, even if it's unchanged. https://github.com/hylang/hy/issues/712""" import tests.resources.hello_world assert capsys.readouterr().out == "hello world\n" assert capsys.readouterr().out == "" reload(tests.resources.hello_world) assert capsys.readouterr().out == "hello world\n" def test_circular(monkeypatch): """Test circular imports by creating a temporary file/module that calls a function that imports itself.""" monkeypatch.syspath_prepend("tests/resources/importer") assert runpy.run_module("circular")["f"]() == 1 def test_shadowed_basename(monkeypatch): """Make sure Hy loads `.hy` files instead of their `.py` counterparts (.e.g `__init__.py` and `__init__.hy`). """ monkeypatch.syspath_prepend("tests/resources/importer") foo = importlib.import_module("foo") assert Path(foo.__file__).name == "__init__.hy" assert foo.ext == "hy" some_mod = importlib.import_module("foo.some_mod") assert Path(some_mod.__file__).name == "some_mod.hy" assert some_mod.ext == "hy" def test_docstring(monkeypatch): """Make sure a module's docstring is loaded.""" monkeypatch.syspath_prepend("tests/resources/importer") mod = importlib.import_module("docstring") expected_doc = "This module has a docstring.\n\n" "It covers multiple lines, too!\n" assert mod.__doc__ == expected_doc assert mod.a == 1 def test_hy_python_require(): # https://github.com/hylang/hy/issues/1911 test = "(do (require tests.resources.macros [test-macro]) (test-macro) blah)" assert hy.eval(hy.read(test)) == 1 def test_filtered_importlib_frames(capsys): testLoader = HyLoader( "tests.resources.importer.compiler_error", "tests/resources/importer/compiler_error.hy", ) spec = importlib.util.spec_from_loader(testLoader.name, testLoader) mod = importlib.util.module_from_spec(spec) with pytest.raises(hy.PrematureEndOfInput) as execinfo: testLoader.exec_module(mod) hy_exc_handler(execinfo.type, execinfo.value, execinfo.tb) captured_w_filtering = capsys.readouterr()[-1].strip() assert "importlib._" not in captured_w_filtering def test_zipimport(tmp_path): from zipfile import ZipFile zpath = tmp_path / "archive.zip" with ZipFile(zpath, "w") as o: o.writestr("example.hy", '(setv x "Hy from ZIP")') try: sys.path.insert(0, str(zpath)) import example finally: sys.path = [p for p in sys.path if p != str(zpath)] assert example.x == "Hy from ZIP" assert example.__file__ == str(zpath / "example.hy") hy-1.0.0/tests/macros/000077500000000000000000000000001467401276000145655ustar00rootroot00000000000000hy-1.0.0/tests/macros/__init__.py000066400000000000000000000000001467401276000166640ustar00rootroot00000000000000hy-1.0.0/tests/macros/test_macro_processor.py000066400000000000000000000035531467401276000214040ustar00rootroot00000000000000import pytest from hy.compiler import HyASTCompiler from hy.errors import HyMacroExpansionError from hy.macros import macro, macroexpand from hy.models import Expression, Float, List, String, Symbol from hy.reader import read @macro("test") def tmac(*tree): """Turn an expression into a list""" return List(tree) def test_preprocessor_simple(): """Test basic macro expansion""" obj = macroexpand(read('(test "one" "two")'), __name__, HyASTCompiler(__name__)) assert obj == List([String("one"), String("two")]) assert type(obj) == List def test_preprocessor_expression(): """Test that macro expansion doesn't recurse""" obj = macroexpand( read('(test (test "one" "two"))'), __name__, HyASTCompiler(__name__) ) assert type(obj) == List assert type(obj[0]) == Expression assert obj[0] == Expression([Symbol("test"), String("one"), String("two")]) obj = List([String("one"), String("two")]) obj = read('(shill ["one" "two"])')[1] assert obj == macroexpand(obj, __name__, HyASTCompiler(__name__)) def test_preprocessor_exceptions(): """Test that macro expansion raises appropriate exceptions""" with pytest.raises(HyMacroExpansionError) as excinfo: macroexpand(read("(when)"), __name__, HyASTCompiler(__name__)) assert "TypeError: when()" in excinfo.value.msg def test_macroexpand_nan(): # https://github.com/hylang/hy/issues/1574 import math NaN = float("nan") x = macroexpand(Float(NaN), __name__, HyASTCompiler(__name__)) assert type(x) is Float assert math.isnan(x) def test_macroexpand_source_data(): # https://github.com/hylang/hy/issues/1944 ast = Expression([Symbol("when"), String("a")]) ast.start_line = 3 ast.start_column = 5 bad = macroexpand(ast, "hy.core.macros", once = True) assert bad.start_line == 3 assert bad.start_column == 5 hy-1.0.0/tests/native_tests/000077500000000000000000000000001467401276000160115ustar00rootroot00000000000000hy-1.0.0/tests/native_tests/__init__.py000066400000000000000000000002731467401276000201240ustar00rootroot00000000000000# Note that __init__.py is intentional so pytest (more specifically py.path) # will detect us as a Python package. There's logic there that involves looking # specifically for this file. hy-1.0.0/tests/native_tests/beside.hy000066400000000000000000000002751467401276000176120ustar00rootroot00000000000000;; This file has no tests of its own, and only exists to be required ;; by `import.hy`, and sit in the same directory to test ;; single-period relative `require`. (defmacro xyzzy [] '1) hy-1.0.0/tests/native_tests/break_continue.hy000066400000000000000000000004311467401276000213410ustar00rootroot00000000000000(defn test-break-breaking [] (defn holy-grail [] (for [x (range 10)] (when (= x 5) (break))) x) (assert (= (holy-grail) 5))) (defn test-continue-continuation [] (setv y []) (for [x (range 10)] (when (!= x 5) (continue)) (.append y x)) (assert (= y [5]))) hy-1.0.0/tests/native_tests/comprehensions.hy000066400000000000000000000226551467401276000214210ustar00rootroot00000000000000(import types asyncio pytest tests.resources [async-test]) (defn test-comprehension-types [] ; Forms that get compiled to real comprehensions (assert (is (type (lfor x "abc" x)) list)) (assert (is (type (sfor x "abc" x)) set)) (assert (is (type (dfor x "abc" x x)) dict)) (assert (is (type (gfor x "abc" x)) types.GeneratorType)) ; Forms that get compiled to loops (assert (is (type (lfor x "abc" :do (setv y 1) x)) list)) (assert (is (type (sfor x "abc" :do (setv y 1) x)) set)) (assert (is (type (dfor x "abc" :do (setv y 1) x x)) dict)) (assert (is (type (gfor x "abc" :do (setv y 1) x)) types.GeneratorType))) (defn [(pytest.mark.parametrize "specialop" ["for" "lfor" "sfor" "gfor" "dfor"])] test-fors [specialop] (setv cases [ ['(f x [] x) []] ['(f j [1 2 3] j) [1 2 3]] ['(f x (range 3) (* x 2)) [0 2 4]] ['(f x (range 2) y (range 2) #(x y)) [#(0 0) #(0 1) #(1 0) #(1 1)]] ['(f #(x y) (.items {"1" 1 "2" 2}) (* y 2)) [2 4]] ['(f x (do (setv s "x") "ab") y (do (+= s "y") "def") (+ x y s)) ["adxy" "aexy" "afxy" "bdxyy" "bexyy" "bfxyy"]] ['(f x (range 4) :if (% x 2) (* x 2)) [2 6]] ['(f x "abc" :setv y (.upper x) (+ x y)) ["aA" "bB" "cC"]] ['(f x "abc" :do (setv y (.upper x)) (+ x y)) ["aA" "bB" "cC"]] ['(f x (range 3) y (range 3) :if (> y x) z [7 8 9] :setv s (+ x y z) :if (!= z 8) #(x y z s)) [#(0 1 7 8) #(0 1 9 10) #(0 2 7 9) #(0 2 9 11) #(1 2 7 10) #(1 2 9 12)]] ['(f x [0 1] :setv l [] y (range 4) :do (.append l #(x y)) :if (>= y 2) z [7 8 9] :if (!= z 8) #(x y (tuple l) z)) [#(0 2 #(#(0 0) #(0 1) #(0 2)) 7) #(0 2 #(#(0 0) #(0 1) #(0 2)) 9) #(0 3 #(#(0 0) #(0 1) #(0 2) #(0 3)) 7) #(0 3 #(#(0 0) #(0 1) #(0 2) #(0 3)) 9) #(1 2 #(#(1 0) #(1 1) #(1 2)) 7) #(1 2 #(#(1 0) #(1 1) #(1 2)) 9) #(1 3 #(#(1 0) #(1 1) #(1 2) #(1 3)) 7) #(1 3 #(#(1 0) #(1 1) #(1 2) #(1 3)) 9)]] ['(f x (range 4) :do (when (not (% x 2)) (continue)) (* x 2)) [2 6]] ['(f x (range 4) :setv p 9 :do (when (not (% x 2)) (continue)) (* x 2)) [2 6]] ['(f x (range 20) :do (when (= x 3) (break)) (* x 2)) [0 2 4]] ['(f x (range 20) :setv p 9 :do (when (= x 3) (break)) (* x 2)) [0 2 4]] ['(f x [4 5] y (range 20) :do (when (> y 1) (break)) z [8 9] #(x y z)) [#(4 0 8) #(4 0 9) #(4 1 8) #(4 1 9) #(5 0 8) #(5 0 9) #(5 1 8) #(5 1 9)]]]) (for [[expr answer] cases] ; Mutate the case as appropriate for the operator before ; evaluating it. (setv expr (+ (hy.models.Expression [(hy.models.Symbol specialop)]) (cut expr 1 None))) (when (= specialop "dfor") (+= expr `(1))) (when (= specialop "for") (setv expr `(do (setv out []) (for [~@(cut expr 1 -1)] (.append out ~(get expr -1))) out))) (setv result (hy.eval expr)) (when (= specialop "dfor") (setv result (.keys result))) (assert (= (sorted result) answer) (str expr)))) (defn test-fors-no-loopers [] (setv l []) (for [] (.append l 1)) (assert (= l [])) (assert (= (lfor 1) [])) (assert (= (sfor 1) #{})) (assert (= (list (gfor 1)) [])) (assert (= (dfor 1 2) {}))) (defn test-raise-in-comp [] (defclass E [Exception] []) (setv l []) (import pytest) (with [(pytest.raises E)] (lfor x (range 10) :do (.append l x) :do (when (= x 5) (raise (E))) x)) (assert (= l [0 1 2 3 4 5]))) (defn test-scoping [] (setv x 0) (for [x [1 2 3]]) (assert (= x 3)) ; An `lfor` that gets compiled to a real comprehension (setv x 0) (assert (= (lfor x [1 2 3] (+ x 1)) [2 3 4])) (assert (= x 0)) ; An `lfor` that gets compiled to a loop (setv x 0 l []) (assert (= (lfor x [4 5 6] :do (.append l 1) (+ x 1)) [5 6 7])) (assert (= l [1 1 1])) (assert (= x 0)) ; An `sfor` that gets compiled to a real comprehension (setv x 0) (assert (= (sfor x [1 2 3] (+ x 1)) #{2 3 4})) (assert (= x 0)) (setv x 20) (lfor n (range 10) (setv x n)) (assert (= x 9)) (lfor n (range 10) (setv y n)) (assert (= y 9)) (lfor n (range 0) (setv z n)) (with [(pytest.raises UnboundLocalError)] z) (defn foo [] (defclass Foo [] (lfor x #(2) (setv z 3)) (with [(pytest.raises NameError)] z)) (assert (not-in "z" (locals)))) (foo)) (defn test-for-loop [] (setv count1 0 count2 0) (for [x [1 2 3 4 5]] (setv count1 (+ count1 x)) (setv count2 (+ count2 x))) (assert (= count1 15)) (assert (= count2 15)) (setv count 0) (for [x [1 2 3 4 5] y [1 2 3 4 5]] (setv count (+ count x y)) (else (+= count 1))) (assert (= count 151)) (setv count 0) ; multiple statements in the else branch should work (for [x [1 2 3 4 5] y [1 2 3 4 5]] (setv count (+ count x y)) (else (+= count 1) (+= count 10))) (assert (= count 161)) ; don't be fooled by constructs that look like else (setv s "") (setv else True) (for [x "abcde"] (+= s x) [else (+= s "_")]) (assert (= s "a_b_c_d_e_")) (setv s "") (with [(pytest.raises TypeError)] (for [x "abcde"] (+= s x) ("else" (+= s "z")))) (assert (= s "az")) (assert (= (list ((fn [] (for [x [[1] [2 3]] y x] (yield y))))) (lfor x [[1] [2 3]] y x y))) (assert (= (list ((fn [] (for [x [[1] [2 3]] y x z (range 5)] (yield z))))) (lfor x [[1] [2 3]] y x z (range 5) z)))) (defn test-nasty-for-nesting [] ;; This test and feature is dedicated to @nedbat. ;; 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-empty-for [] (setv l []) (defn f [] (for [x (range 3)] (.append l "a") (yield x))) (for [x (f)]) (assert (= l ["a" "a" "a"])) (setv l []) (for [x (f)] (else (.append l "z"))) (assert (= l ["a" "a" "a" "z"]))) (defn test-multidimensional-for-break-continue [] "`break` and `continue` only affect the innermost generated loop." (setv out "") (for [x "abc" y "123"] (+= out x y) (when (= (+ x y) "b2") (break))) (assert (= out "a1a2a3b1b2c1c2c3")) (setv out "") (for [c "xyz" d "12"] (+= out c d) (when (= (+ c d) "y1") (continue)) (+= out "-")) (assert (= out "x1-x2-y1y2-z1-z2-"))) (defmacro eval-isolated [#* body] `(hy.eval '(do ~@body) :module (hy.I.types.ModuleType "") :locals {})) (defn test-lfor-nonlocal [] (with [err (pytest.raises SyntaxError)] (eval-isolated (lfor i (range 20) (do (nonlocal x) i)))) (assert (in "no binding for nonlocal 'x'" err.value.msg)) (with [err (pytest.raises SyntaxError)] (eval-isolated (lfor i (range 20) (do (nonlocal x) (setv x i))))) (assert (in "no binding for nonlocal 'x'" err.value.msg)) (with [err (pytest.raises SyntaxError)] (eval-isolated (lfor i (range 20) (do (nonlocal i) (setv i 2))))) (assert (in "name 'i' is assigned to before nonlocal declaration" err.value.msg)) (with [err (pytest.raises SyntaxError)] (eval-isolated (defn foo [] (lfor i (range 20) (do (nonlocal x) (setv x i)))))) (assert (in "no binding for nonlocal 'x'" err.value.msg)) (eval-isolated (setv x 2) (defn foo [] (lfor i (range 20) (do (global x) (setv x i)))) (foo) (assert (= x 19))) (defn bar [] (setv x 2) (defn foo [] (lfor i (range 20) (do (nonlocal x) (setv x i)))) (foo) (assert (= x 19))) (bar)) (defn test-lfor-global [] (with [err (pytest.raises SyntaxError)] (eval-isolated (lfor i (range 20) (do (global i) (setv i 2))))) (assert (in "name 'i' is assigned to before global declaration" err.value.msg)) (eval-isolated (lfor i (range 20) (do (global x) (setv x i))) (assert (= x 19))) (eval-isolated (defn foo [] (lfor i (range 20) (do (global x) (setv x i)))) (foo) (assert (= x 19))) (eval-isolated (defn bar [] (setv x 2) (defn foo [] (lfor i (range 20) (do (global x) (setv x i)))) (foo) (assert (= x 2))) (bar) (assert (= x 19)))) (defn test-for-do [] (do (do (do (do (do (do (do (do (do (setv #(x y) #(0 0))))))))))) (for [- [1 2]] (do (setv x (+ x 1)) (setv y (+ y 1)))) (assert (= y x 2))) (defn test-for-else [] (setv x 0) (for [a [1 2]] (setv x (+ x a)) (else (setv x (+ x 50)))) (assert (= x 53)) (setv x 0) (for [a [1 2]] (setv x (+ x a)) (else)) (assert (= x 3))) (defn [async-test] test-for-async [] (defn :async numbers [] (for [i [1 2]] (yield i))) (asyncio.run ((fn :async [] (setv x 0) (for [:async a (numbers)] (setv x (+ x a))) (assert (= x 3)))))) (defn [async-test] test-for-async-else [] (defn :async numbers [] (for [i [1 2]] (yield i))) (asyncio.run ((fn :async [] (setv x 0) (for [:async a (numbers)] (setv x (+ x a)) (else (setv x (+ x 50)))) (assert (= x 53)))))) hy-1.0.0/tests/native_tests/conditional.hy000066400000000000000000000126371467401276000206670ustar00rootroot00000000000000;; Tests of `if`, `cond`, `when`, and `while` (import pytest) (defn test-branching [] (if True (assert (= 1 1)) (assert (= 2 1)))) (defn test-branching-with-do [] (if False (assert (= 2 1)) (do (assert (= 1 1)) (assert (= 1 1)) (assert (= 1 1))))) (defn test-branching-expr-count-with-do [] "Ensure we execute the right number of expressions in a branch." (setv counter 0) (if False (assert (= 2 1)) (do (setv counter (+ counter 1)) (setv counter (+ counter 1)) (setv counter (+ counter 1)))) (assert (= counter 3))) (defn test-cond [] (cond (= 1 2) (assert (is True False)) (is None None) (do (setv x True) (assert x))) (assert (is (cond) None)) (assert (= (cond False 1 [] 2 True 8) 8)) (setv x 0) (assert (is (cond False 1 [] 2 x 3) None)) (with [e (pytest.raises hy.errors.HyMacroExpansionError)] (hy.eval '(cond 1))) (assert (in "needs an even number of arguments" e.value.msg)) ; Make sure each test is only evaluated once, and `cond` ; short-circuits. (setv x 1) (assert (= "first" (cond (do (*= x 2) True) (do (*= x 3) "first") (do (*= x 5) True) (do (*= x 7) "second")))) (assert (= x 6))) (defn test-if [] (assert (= 1 (if 0 -1 1)))) (defn test-returnable-ifs [] (assert (= True (if True True True)))) (defn test-if-return-branching [] ; thanks, kirbyfan64 (defn f [] (if True (setv x 1) 2) 1) (assert (= 1 (f)))) (defn test-nested-if [] (for [x (range 10)] (if (in "foo" "foobar") (do (if True True True)) (do (if False False False))))) (defn test-if-in-if [] (assert (= 42 (if (if 1 True False) 42 43))) (assert (= 43 (if (if 0 True False) 42 43)))) (defn test-when [] (assert (= (when True 1) 1)) (assert (= (when True 1 2) 2)) (assert (= (when True 1 3) 3)) (assert (= (when False 2) None)) (assert (= (when (= 1 2) 42) None)) (assert (= (when (= 2 2) 42) 42)) (assert (is (when (do (setv x 3) True)) None)) (assert (= x 3))) (defn test-while-loop [] (setv count 5) (setv fact 1) (while (> count 0) (setv fact (* fact count)) (setv count (- count 1))) (assert (= count 0)) (assert (= fact 120)) (setv l []) (defn f [] (.append l 1) (len l)) (while (!= (f) 4)) (assert (= l [1 1 1 1])) (setv l []) (defn f [] (.append l 1) (len l)) (while (!= (f) 4) (do)) (assert (= l [1 1 1 1])) ; only compile the condition once ; https://github.com/hylang/hy/issues/1790 (global while-cond-var) (setv while-cond-var 10) (hy.eval '(do (defmacro while-cond [] (global while-cond-var) (assert (= while-cond-var 10)) (+= while-cond-var 1) `(do (setv x 3) False)) (while (while-cond)) (assert (= x 3))))) (defn test-while-loop-else [] (setv count 5) (setv fact 1) (setv myvariable 18) (while (> count 0) (setv fact (* fact count)) (setv count (- count 1)) (else (setv myvariable 26))) (assert (= count 0)) (assert (= fact 120)) (assert (= myvariable 26)) ; multiple statements in a while loop should work (setv count 5) (setv fact 1) (setv myvariable 18) (setv myothervariable 15) (while (> count 0) (setv fact (* fact count)) (setv count (- count 1)) (else (setv myvariable 26) (setv myothervariable 24))) (assert (= count 0)) (assert (= fact 120)) (assert (= myvariable 26)) (assert (= myothervariable 24)) ; else clause shouldn't get run after a break (while True (break) (else (setv myvariable 53))) (assert (= myvariable 26)) ; don't be fooled by constructs that look like else clauses (setv x 2) (setv a []) (setv else True) (while x (.append a x) (-= x 1) [else (.append a "e")]) (assert (= a [2 "e" 1 "e"])) (setv x 2) (setv a []) (with [(pytest.raises TypeError)] (while x (.append a x) (-= x 1) ("else" (.append a "e")))) (assert (= a [2 "e"]))) (defn test-while-multistatement-condition [] ; The condition should be executed every iteration, before the body. ; `else` should be executed last. (setv s "") (setv x 2) (while (do (+= s "a") x) (+= s "b") (-= x 1) (else (+= s "z"))) (assert (= s "ababaz")) ; `else` should still be skipped after `break`. (setv s "") (setv x 2) (while (do (+= s "a") x) (+= s "b") (-= x 1) (when (= x 0) (break)) (else (+= s "z"))) (assert (= s "abab")) ; `continue` should jump to the condition. (setv s "") (setv x 2) (setv continued? False) (while (do (+= s "a") x) (+= s "b") (when (and (= x 1) (not continued?)) (+= s "c") (setv continued? True) (continue)) (-= x 1) (else (+= s "z"))) (assert (= s "ababcabaz")) ; `break` in a condition applies to the `while`, not an outer loop. (setv s "") (for [x "123"] (+= s x) (setv y 0) (while (do (when (and (= x "2") (= y 1)) (break)) (< y 3)) (+= s "y") (+= y 1))) (assert (= s "1yyy2y3yyy")) ; The condition is still tested appropriately if its last variable ; is set to a false value in the loop body. (setv out []) (setv x 0) (setv a [1 1]) (while (do (.append out 2) (setv x (and a (.pop a))) x) (setv x 0) (.append out x)) (assert (= out [2 0 2 0 2])) (assert (is x a))) hy-1.0.0/tests/native_tests/decorators.hy000066400000000000000000000026251467401276000205250ustar00rootroot00000000000000(import asyncio tests.resources [async-test]) (defn test-decorated-1line-function [] (defn foodec [func] (fn [] (+ (func) 1))) (defn [foodec] tfunction [] (* 2 2)) (assert (= (tfunction) 5))) (defn test-decorated-multiline-function [] (defn bazdec [func] (fn [] (+ (func) "x"))) (defn [bazdec] f [] (setv intermediate "i") (+ intermediate "b")) (assert (= (f) "ibx"))) (defn test-decorated-class [] (defn bardec [cls] (setv cls.attr2 456) cls) (defclass [bardec] cls [] (setv attr1 123)) (assert (= cls.attr1 123)) (assert (= cls.attr2 456))) (defn test-stacked-decorators [] (defn dec1 [f] (fn [] (+ (f) "a"))) (defn dec2 [f] (fn [] (+ (f) "b"))) (defn [dec1 dec2] f [] "c") (assert (= (f) "cba"))) (defn test-evaluation-order [] (setv l []) (defn foo [f] (.append l "foo") (fn [] (.append l "foo fn") (f))) (defn [(do (.append l "dec") foo)] ; Decorator list bar ; Function name [[arg (do (.append l "arg") 1)]] ; Lambda list (.append l "bar body") arg) ; Body (.append l (bar)) (assert (= l ["dec" "arg" "foo" "foo fn" "bar body" 1]))) (defn [async-test] test-decorated-defn-a [] (defn decorator [func] (fn :async [] (/ (await (func)) 2))) (defn :async [decorator] coro-test [] (await (asyncio.sleep 0)) 42) (assert (= (asyncio.run (coro-test)) 21))) hy-1.0.0/tests/native_tests/defclass.hy000066400000000000000000000065271467401276000201510ustar00rootroot00000000000000(defn test-defclass [] (defclass A) (assert (isinstance (A) A))) (defn test-defclass-inheritance [] (defclass A []) (assert (isinstance (A) object)) (defclass A [object]) (assert (isinstance (A) object)) (defclass B [A]) (assert (isinstance (B) A)) (defclass C [object]) (defclass D [B C]) (assert (isinstance (D) A)) (assert (isinstance (D) B)) (assert (isinstance (D) C)) (assert (not (isinstance (A) D)))) (defn test-defclass-attrs [] (defclass A [] (setv x 42)) (assert (= A.x 42)) (assert (= (getattr (A) "x") 42))) (defn test-defclass-attrs-fn [] (defclass B [] (setv x 42) (setv y (fn [self value] (+ self.x value)))) (assert (= B.x 42)) (assert (= (.y (B) 5) 47)) (setv b (B)) (setv B.x 0) (assert (= (.y b 1) 1))) (defn test-defclass-dynamic-inheritance [] (defclass A [((fn [] (if True list dict)))] (setv x 42)) (assert (isinstance (A) list)) (defclass A [((fn [] (if False list dict)))] (setv x 42)) (assert (isinstance (A) dict))) (defn test-defclass-no-fn-leak [] (defclass A [] (setv x (fn [] 1))) (try (do (x) (assert False)) (except [NameError]))) (defn test-defclass-docstring [] (defclass A [] (setv __doc__ "doc string") (setv x 1)) (setv a (A)) (assert (= a.__doc__ "doc string")) (defclass B [] "doc string" (setv x 1)) (setv b (B)) (assert (= b.x 1)) (assert (= b.__doc__ "doc string")) (defclass MultiLine [] "begin a very long multi-line string to make sure that it comes out the way we hope and can span 3 lines end." (setv x 1)) (setv mL (MultiLine)) (assert (= mL.x 1)) (assert (in "begin" mL.__doc__)) (assert (in "end" mL.__doc__))) (defn test-defclass-macroexpand [] (defmacro M [] `(defn x [self x] (setv self._x x))) (defclass A [] (M)) (setv a (A)) (a.x 1) (assert (= a._x 1))) (defn test-defclass-syntax [] "defclass syntax with properties and methods and side-effects" (setv foo 1) (defclass A [] (setv x 1) (setv y 2) (global foo) (setv foo 2) (defn greet [self] "Greet the caller" "hello!")) (setv a (A)) (assert (= a.x 1)) (assert (= a.y 2)) (assert foo 2) (assert (.greet a) "hello")) (defn test-class-sideeffects [] "defclass should run all expressions." (defn set-sentinel [] (setv set-sentinel.set True)) (setv set-sentinel.set False) (defclass A [] (set-sentinel)) (assert set-sentinel.set)) (defn test-pep-3115 [] "Test setting a metaclass with `:metaclass`, and using `__prepare__`." (defclass MyDict [dict] (defn __setitem__ [self key value] (dict.__setitem__ self (+ "prefixed_" key) value))) (defclass MyMetaclass [type] (defn [classmethod] __prepare__ [metacls name bases] (MyDict))) (defclass MyClass [:metaclass MyMetaclass] (defn [classmethod] method [self] 1)) (assert (= (MyClass.prefixed-method) 1))) (defn test-pep-487 [] (defclass QuestBase [] (defn __init-subclass__ [cls swallow #** kwargs] (setv cls.swallow swallow))) (defclass Quest [QuestBase :swallow "african"]) (assert (= (. (Quest) swallow) "african"))) (do-mac (when hy.compat.PY3_12 '(defn test-type-params [] (import tests.resources.tp :as ttp) (defclass :tp [#^ int A #** B] C) (assert (= (ttp.show C) [ [ttp.TypeVar "A" int #()] [ttp.ParamSpec "B" None #()]]))))) hy-1.0.0/tests/native_tests/deftype.hy000066400000000000000000000006561467401276000200220ustar00rootroot00000000000000(do-mac (when hy.compat.PY3_12 '(do (import tests.resources.tp :as ttp) (defn test-deftype [] (deftype Foo int) (assert (is (type Foo) ttp.TypeAliasType)) (assert (= Foo.__value__) int) (deftype Foo (| int bool)) (assert (is (type Foo.__value__ hy.I.types.UnionType))) (deftype :tp [#^ int A #** B] Foo int) (assert (= (ttp.show Foo) [ [ttp.TypeVar "A" int #()] [ttp.ParamSpec "B" None #()]]))))) ) hy-1.0.0/tests/native_tests/del.hy000066400000000000000000000004431467401276000171200ustar00rootroot00000000000000(import pytest) (defn test-del [] (setv foo 42) (assert (= foo 42)) (del foo) (with [(pytest.raises NameError)] foo) (setv test (list (range 5))) (del (get test 4)) (assert (= test [0 1 2 3])) (del (get test 2)) (assert (= test [0 1 3])) (assert (= (del) None))) hy-1.0.0/tests/native_tests/do.hy000066400000000000000000000003701467401276000167550ustar00rootroot00000000000000(defn test-empty [] (assert (is (do) None)) (assert (is (if True (do) (do)) None))) (defn test-nonempty [] (assert (= (do 1 2 3) 3)) (assert (= (do 3 2 1) 1)) (setv x "a") (assert (= (do (setv x "b") "c") "c")) (assert (= x "b"))) hy-1.0.0/tests/native_tests/dots.hy000066400000000000000000000044151467401276000173300ustar00rootroot00000000000000(import os) (defn test-dotted-identifiers [] (assert (= (.join " " ["one" "two"]) "one two")) (defclass X [object] []) (defclass M [object] (defn meth [self #* args #** kwargs] (.join " " (+ #("meth") args (tuple (map (fn [k] (get kwargs k)) (sorted (.keys kwargs)))))))) (setv x (X)) (setv m (M)) (assert (= (.meth m) "meth")) (assert (= (.meth m "foo" "bar") "meth foo bar")) (assert (= (.meth :b "1" :a "2" m "foo" "bar") "meth foo bar 2 1")) (assert (= (.meth m #* ["foo" "bar"]) "meth foo bar")) (setv x.p m) (assert (= (.p.meth x) "meth")) (assert (= (.p.meth x "foo" "bar") "meth foo bar")) (assert (= (.p.meth :b "1" :a "2" x "foo" "bar") "meth foo bar 2 1")) (assert (= (.p.meth x #* ["foo" "bar"]) "meth foo bar")) (setv x.a (X)) (setv x.a.b m) (assert (= (.a.b.meth x) "meth")) (assert (= (.a.b.meth x "foo" "bar") "meth foo bar")) (assert (= (.a.b.meth :b "1" :a "2" x "foo" "bar") "meth foo bar 2 1")) (assert (= (.a.b.meth x #* ["foo" "bar"]) "meth foo bar")) (assert (= (.__str__ :foo) ":foo"))) (defn test-dot-macro [] (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")) (assert (= (. os (getcwd) (isalpha) __class__ __name__ [0]) "b")) (assert (= (. "ab hello" (strip "ab ") (upper)) "HELLO")) (assert (= (. "hElLO\twoRld" (expandtabs :tabsize 4) (lower)) "hello world")) (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-multidot [] (setv a 1 b 2 c 3) (defn .. [#* args] (.join "~" (map str args))) (assert (= ..a.b.c "None~1~2~3")) (defmacro .... [#* args] (.join "@" (map str args))) (assert (= ....uno.dos.tres "None@uno@dos@tres"))) (defn test-ellipsis [] (global Ellipsis) (assert (is ... Ellipsis)) (setv e Ellipsis) (setv Ellipsis 14) (assert (= Ellipsis 14)) (assert (!= ... 14)) (assert (is ... e))) hy-1.0.0/tests/native_tests/eval_foo_compile.hy000066400000000000000000000016741467401276000216650ustar00rootroot00000000000000;; Tests of `eval-when-compile`, `eval-and-compile`, and `do-mac` (defn test-eval-foo-compile-return-values [] (eval-and-compile (setv jim 0)) (setv derrick (eval-and-compile (+= jim 1) 2)) (assert (= jim 1)) (assert (= derrick 2)) (setv derrick (eval-and-compile)) (assert (is derrick None)) (setv derrick 3) (setv derrick (eval-when-compile (+= jim 1) 2)) (assert (= jim 1)) (assert (is derrick None))) (defn test-do-mac [] (assert (is (do-mac) None)) (setv x 2) (setv x-compile-time (do-mac (setv x 3) x)) (assert (= x 2)) (assert (= x-compile-time 3)) (eval-when-compile (setv x 4)) (assert (= x 2)) (assert (= (do-mac x) 4)) (defmacro m [] (global x) (setv x 5)) (m) (assert (= x 2)) (assert (= (do-mac x) 5)) (setv l []) (do-mac `(do ~@(* ['(.append l 1)] 5))) (assert (= l [1 1 1 1 1])) (do-mac `(setv ~(hy.models.Symbol (* "x" 5)) "foo")) (assert (= xxxxx "foo"))) hy-1.0.0/tests/native_tests/functions.hy000066400000000000000000000207121467401276000203650ustar00rootroot00000000000000;; Tests of `fn`, `defn`, `return`, and `yield` (import asyncio typing [List] pytest tests.resources [async-test]) (defn test-fn [] (setv square (fn [x] (* x x))) (assert (= 4 (square 2))) (setv lambda_list (fn [test #* args] #(test args))) (assert (= #(1 #(2 3)) (lambda_list 1 2 3)))) (defn test-immediately-call-lambda [] (assert (= 2 ((fn [] (+ 1 1)))))) (defn test-fn-return [] (setv fn-test ((fn [] (fn [] (+ 1 1))))) (assert (= (fn-test) 2)) (setv fn-test (fn [])) (assert (= (fn-test) None))) (defn [async-test] test-fn-async [] (assert (= (asyncio.run ((fn :async [] (await (asyncio.sleep 0)) [1 2 3]))) [1 2 3]))) (defn test-defn-evaluation-order [] (setv acc []) (defn my-fun [] (.append acc "Foo") (.append acc "Bar") (.append acc "Baz")) (my-fun) (assert (= acc ["Foo" "Bar" "Baz"]))) (defn test-defn-return [] (defn my-fun [x] (+ x 1)) (assert (= 43 (my-fun 42)))) (defn test-defn-lambdakey [] "Test defn with a `&symbol` function name." (defn &hy [] 1) (assert (= (&hy) 1))) (defn test-defn-evaluation-order-with-do [] (setv acc []) (defn my-fun [] (do (.append acc "Foo") (.append acc "Bar") (.append acc "Baz"))) (my-fun) (assert (= acc ["Foo" "Bar" "Baz"]))) (defn test-defn-do-return [] (defn my-fun [x] (do (+ x 42) ; noop (+ x 1))) (assert (= 43 (my-fun 42)))) (defn test-defn-dunder-name [] "`defn` should preserve `__name__`." (defn phooey [x] (+ x 1)) (assert (= phooey.__name__ "phooey")) (defn mooey [x] (+= x 1) x) (assert (= mooey.__name__ "mooey"))) (defn test-defn-annotations [] (defn #^ int f [#^ (get List int) p1 p2 #^ str p3 #^ str [o1 None] #^ int [o2 0] #^ str #* rest #^ str k1 #^ int [k2 0] #^ bool #** kwargs]) (assert (is (. f __annotations__ ["return"]) int)) (for [[k v] (.items (dict :p1 (get List int) :p3 str :o1 str :o2 int :k1 str :k2 int :kwargs bool))] (assert (= (. f __annotations__ [k]) v)))) (do-mac (when hy.compat.PY3_12 '(defn test-type-params [] (import tests.resources.tp :as ttp) (defn foo []) (assert (= (ttp.show foo) [])) ; `defn` with a type parameter (defn :tp [T] #^ T foo [#^ T x] (+ x 1)) (assert (= (foo 3) 4)) (assert (= (ttp.show foo) [[ttp.TypeVar "T" None #()]])) ; `fn` with a type parameter (setv foo (fn :tp [T] #^ T [#^ T x] (+ x 2))) (assert (= (foo 3) 5)) (assert (= (ttp.show foo) [[ttp.TypeVar "T" None #()]])) ; Bounds and constraints (defn :tp [#^ int T] foo []) (assert (= (ttp.show foo) [[ttp.TypeVar "T" int #()]])) (defn :tp [#^ #(int str) T] foo []) (assert (= (ttp.show foo) [[ttp.TypeVar "T" None #(int str)]])) ; `TypeVarTuple`s and `ParamSpec`s (defn :tp [#* T] foo []) (assert (= (ttp.show foo) [[ttp.TypeVarTuple "T" None #()]])) (defn :tp [#** T] foo []) (assert (= (ttp.show foo) [[ttp.ParamSpec "T" None #()]])) ; A more complex case (defn :tp [A #^ int B #* C #** D #* E #^ #(bool float) F] foo []) (assert (= (ttp.show foo) [ [ttp.TypeVar "A" None #()] [ttp.TypeVar "B" int #()] [ttp.TypeVarTuple "C" None #()] [ttp.ParamSpec "D" None #()] [ttp.TypeVarTuple "E" None #()] [ttp.TypeVar "F" None #(bool float)]])) ; Illegal attempts to annotate unpacking (with [(pytest.raises hy.errors.HySyntaxError)] (hy.eval '(defn :tp [#^ int #* T] foo []))) (with [(pytest.raises hy.errors.HySyntaxError)] (hy.eval '(defn :tp [#^ #(int str) #** T] foo [])))))) (defn test-lambda-keyword-lists [] (defn foo [x #* xs #** kw] [x xs kw]) (assert (= (foo 10 20 30) [10 #(20 30) {}]))) (defn test-optional-arguments [] (defn foo [a b [c None] [d 42]] [a b c d]) (assert (= (foo 1 2) [1 2 None 42])) (assert (= (foo 1 2 3) [1 2 3 42])) (assert (= (foo 1 2 3 4) [1 2 3 4]))) (defn test-kwonly [] ;; keyword-only with default works (defn kwonly-foo-default-false [* [foo False]] foo) (assert (= (kwonly-foo-default-false) False)) (assert (= (kwonly-foo-default-false :foo True) True)) ;; keyword-only without default ... (defn kwonly-foo-no-default [* foo] foo) (with [e (pytest.raises TypeError)] (kwonly-foo-no-default)) (assert (in "missing 1 required keyword-only argument: 'foo'" (. e value args [0]))) ;; works (assert (= (kwonly-foo-no-default :foo "quux") "quux")) ;; keyword-only with other arg types works (defn function-of-various-args [a b #* args foo #** kwargs] #(a b args foo kwargs)) (assert (= (function-of-various-args 1 2 3 4 :foo 5 :bar 6 :quux 7) #(1 2 #(3 4) 5 {"bar" 6 "quux" 7})))) (defn test-only-parse-lambda-list-in-defn [] (with [(pytest.raises NameError)] (setv x [#* spam] y 1))) (defn [async-test] test-defn-async [] (defn :async coro-test [] (await (asyncio.sleep 0)) [1 2 3]) (assert (= (asyncio.run (coro-test)) [1 2 3]))) (defn [async-test] test-no-async-gen-return [] ; https://github.com/hylang/hy/issues/2523 (defn :async runner [gen] (setv vals []) (for [:async val (gen)] (.append vals val)) vals) (defn :async naysayer [] (yield "nope")) (assert (= (asyncio.run (runner naysayer)) ["nope"])) (assert (= (asyncio.run (runner (fn :async [] (yield "dope!")) ["dope!"]))))) (defn test-root-set-correctly [] ; https://github.com/hylang/hy/issues/2475 ((. defn) not-async [] "ok") (assert (= (not-async) "ok")) (require builtins) (builtins.defn also-not-async [] "ok") (assert (= (also-not-async) "ok"))) (defn test-return [] ; `return` in main line (defn f [x] (return (+ x "a")) (+ x "b")) (assert (= (f "q") "qa")) ; Nullary `return` (defn f [x] (return) 5) (assert (is (f "q") None)) ; `return` in `when` (defn f [x] (when (< x 3) (return [x 1])) [x 2]) (assert (= (f 2) [2 1])) (assert (= (f 4) [4 2])) ; `return` in a loop (setv accum []) (defn f [x] (while True (when (= x 0) (return)) (.append accum x) (-= x 1)) (.append accum "this should never be appended") 1) (assert (is (f 5) None)) (assert (= accum [5 4 3 2 1])) ; `return` of a `do` (setv accum []) (defn f [] (return (do (.append accum 1) 3)) 4) (assert (= (f) 3)) (assert (= accum [1])) ; `return` of an `if` that will need to be compiled to a statement (setv accum []) (defn f [x] (return (if (= x 1) (do (.append accum 1) "a") (do (.append accum 2) "b"))) "c") (assert (= (f 2) "b")) (assert (= accum [2]))) (defn test-yield [] (defn gen [] (for [x [1 2 3 4]] (yield x))) (setv ret 0) (for [y (gen)] (setv ret (+ ret y))) (assert (= ret 10))) (defn test-yield-with-return [] (defn gen [] (yield 3) "goodbye") (setv gg (gen)) (assert (= 3 (next gg))) (with [e (pytest.raises StopIteration)] (next gg)) (assert (= e.value.value "goodbye"))) (defn test-yield-in-try [] (setv hit-finally False) (defn gen [] (setv x 1) (try (yield x) (finally (nonlocal hit-finally) (setv hit-finally True)))) (setv output (list (gen))) (assert (= [1] output)) (assert hit-finally)) (defn test-midtree-yield [] "Test yielding with a returnable." (defn kruft [] (yield) (+ 1 1))) (defn test-midtree-yield-in-for [] "Test yielding in a for with a return." (defn kruft-in-for [] (for [i (range 5)] (yield i)) (+ 1 2))) (defn test-midtree-yield-in-while [] "Test yielding in a while with a return." (defn kruft-in-while [] (setv i 0) (while (< i 5) (yield i) (setv i (+ i 1))) (+ 2 3))) (defn test-multi-yield [] (defn multi-yield [] (for [i (range 3)] (yield i)) (yield "a") (yield "end")) (assert (= (list (multi-yield)) [0 1 2 "a" "end"]))) (defn test-yield-from [] (defn yield-from-test [] (for [i (range 3)] (yield i)) (yield :from [1 2 3])) (assert (= (list (yield-from-test)) [0 1 2 1 2 3]))) (defn test-yield-from-exception-handling [] (defn yield-from-subgenerator-test [] (yield 1) (yield 2) (yield 3) (/ 1 0)) (defn yield-from-test [] (for [i (range 3)] (yield i)) (try (yield :from (yield-from-subgenerator-test)) (except [e ZeroDivisionError] (yield 4)))) (assert (= (list (yield-from-test)) [0 1 2 1 2 3 4]))) (defn test-yield-from-notreally [] (defn f [] (yield :from) (yield :from)) (assert (= (list (f)) [:from :from]))) hy-1.0.0/tests/native_tests/hy_eval.hy000066400000000000000000000165011467401276000200050ustar00rootroot00000000000000"Tests of the user-facing function `hy.eval`." (import re pytest) (defn test-eval [] (assert (= 2 (hy.eval (quote (+ 1 1))))) (setv x 2) (assert (= 4 (hy.eval (quote (+ x 2))))) (setv test-payload (quote (+ x 2))) (setv x 4) (assert (= 6 (hy.eval test-payload))) (assert (= 9 ((hy.eval (quote (fn [x] (+ 3 3 x)))) 3))) (assert (= 1 (hy.eval (quote 1)))) (assert (= "foobar" (hy.eval (quote "foobar")))) (setv x (quote 42)) (assert (= 42 (hy.eval x))) (assert (= 27 (hy.eval (+ (quote (*)) (* [(quote 3)] 3))))) (assert (= None (hy.eval (quote (print ""))))) ;; https://github.com/hylang/hy/issues/1041 (assert (is (hy.eval 're) re)) (assert (is ((fn [] (hy.eval 're))) re))) (defn test-eval-false [] (assert (is (hy.eval 'False) False)) (assert (is (hy.eval 'None) None)) (assert (= (hy.eval '0) 0)) (assert (= (hy.eval '"") "")) (assert (= (hy.eval 'b"") b"")) (assert (= (hy.eval ':) :)) (assert (= (hy.eval '[]) [])) (assert (= (hy.eval '#()) #())) (assert (= (hy.eval '{}) {})) (assert (= (hy.eval '#{}) #{}))) (defn test-eval-quasiquote [] ; https://github.com/hylang/hy/issues/1174 (for [x [ None False True 5 5.1 5j 5.1j 2+1j 1.2+3.4j "" b"" "apple bloom" b"apple bloom" "⚘" b"\x00" [] #{} {} [1 2 3] #{1 2 3} {"a" 1 "b" 2}]] (assert (= (hy.eval `(get [~x] 0)) x)) (assert (= (hy.eval x) x))) (setv kw :mykeyword) (assert (= (get (hy.eval `[~kw]) 0) kw)) (assert (= (hy.eval kw) kw)) (assert (= (hy.eval #()) #())) (assert (= (hy.eval #(1 2 3)) #(1 2 3))) (assert (= (hy.eval `(+ "a" ~(+ "b" "c"))) "abc")) (setv l ["a" "b"]) (setv n 1) (assert (= (hy.eval `(get ~l ~n) "b"))) (setv d {"a" 1 "b" 2}) (setv k "b") (assert (= (hy.eval `(get ~d ~k)) 2))) (setv outer "O") (defn test-globals [] (assert (= (hy.eval 'foo {"foo" 2}) 2)) (with [(pytest.raises NameError)] (hy.eval 'foo {})) (assert (= outer "O")) (assert (= (hy.eval 'outer) "O")) (with [(pytest.raises NameError)] (hy.eval 'outer {})) (hy.eval '(do (global outer) (setv outer "O2"))) (assert (= outer "O2")) (hy.eval :globals {"outer" "I"} '(do (global outer) (setv outer "O3"))) (assert (= outer "O2")) ; If `globals` is provided but not `locals`, then `globals` ; substitutes in for `locals`. (defn try-it [#** eval-args] (setv d (dict :g1 1 :g2 2)) (hy.eval :globals d #** eval-args '(do (global g2 g3) (setv g2 "newv" g3 3 l 4))) (del (get d "__builtins__")) d) (setv ls {}) (assert (= (try-it) (dict :g1 1 :g2 "newv" :g3 3 :l 4))) (assert (= (try-it :locals ls) (dict :g1 1 :g2 "newv" :g3 3))) (assert (= ls {"l" 4})) ; If `module` is provided but `globals` isn't, the dictionary of ; `module` is used for globals. If `locals` also isn't provided, ; the same dictionary is used for that, too. (import string) (assert (= (hy.eval 'digits :module string) "0123456789")) (assert (= (hy.eval 'digits :module "string") "0123456789")) (assert (= (hy.eval 'digits :module string :globals {"digits" "boo"}) "boo")) (with [(pytest.raises NameError)] (hy.eval 'digits :module string :globals {})) (hy.eval :module string '(do (global hytest-string-g) (setv hytest-string-g "hi") (setv hytest-string-l "bye"))) (assert (= string.hytest-string-g "hi")) (assert (= string.hytest-string-l "bye"))) (defn test-locals [] (assert (= (hy.eval 'foo :locals {"foo" 2}) 2)) (with [(pytest.raises NameError)] (hy.eval 'foo :locals {})) (setv d (dict :l1 1 :l2 2 :hippopotamus "local_v")) (hy.eval :locals d '(do (global hippopotamus) (setv l2 "newv" l3 3 hippopotamus "global_v"))) (assert (= d (dict :l1 1 :l2 "newv" :l3 3 :hippopotamus "local_v"))) (assert (= (get (globals) "hippopotamus") "global_v")) (assert (= hippopotamus "global_v")) ; `hy` is implicitly available even when `locals` and `globals` are ; provided. (assert (= (hy.eval :locals {"foo" "A"} :globals {"bar" "B"} '(hy.repr (+ foo bar))) #[["AB"]])) ; Even though `hy.eval` deletes the `hy` implicitly added to ; `locals`, references in returned code still work. (setv d {"a" 1}) (setv f (hy.eval '(fn [] (hy.repr "hello")) :locals d)) (assert (= d {"a" 1})) (assert (= (f) #[["hello"]]))) (defn test-globals-and-locals [] (setv gd (dict :g1 "apple" :g2 "banana")) (setv ld (dict :l1 "Austin" :l2 "Boston")) (hy.eval :globals gd :locals ld '(do (global g2 g3) (setv g2 "newg-val" g3 "newg-var" l2 "newl-val" l3 "newl-var"))) (del (get gd "__builtins__")) (assert (= gd (dict :g1 "apple" :g2 "newg-val" :g3 "newg-var"))) (assert (= ld (dict :l1 "Austin" :l2 "newl-val" :l3 "newl-var")))) (defn test-no-extra-hy-removal [] "`hy.eval` shouldn't remove `hy` from a provided namespace if it was already there." (setv g {}) (exec "import hy" g) (assert (= (hy.eval '(hy.repr [1 2]) g) "[1 2]")) (assert (in "hy" g))) (defmacro test-macro [] '(setv blah "test from here")) (defmacro cheese [] "gorgonzola") (defn test-macros [] (setv M "tests.resources.macros") ; Macros defined in `module` can be called. (assert (= (hy.eval '(do (test-macro) blah)) "test from here")) (assert (= (hy.eval '(do (test-macro) blah) :module M) 1)) ; `defmacro` creates a new macro in the module. (hy.eval '(defmacro bilb-ono [] "creative consulting") :module M) (assert (= (hy.eval '(bilb-ono) :module M) "creative consulting")) (with [(pytest.raises NameError)] (hy.eval '(bilb-ono))) ; When `module` is provided, implicit access to macros in the ; current scope is lost. (assert (= (hy.eval '(cheese)) "gorgonzola")) (with [(pytest.raises NameError)] (hy.eval '(cheese) :module M)) ; You can still use `require` inside `hy.eval`. (hy.eval '(require tests.resources.tlib [qplah])) (assert (= (hy.eval '(qplah 1)) [8 1]))) (defn test-extra-macros [] (setv ab 15) (assert (= (hy.eval '(chippy a b) :macros (dict :chippy (fn [arg1 arg2] (hy.models.Symbol (+ (str arg1) (str arg2)))))) 15)) ; By default, `hy.eval` can't see local macros. (defmacro oh-hungee [arg1 arg2] (hy.models.Symbol (+ (str arg1) (str arg2)))) (with [(pytest.raises NameError)] (hy.eval '(oh-hungee a b))) ; But you can pass them in with the `macros` argument. (assert (= (hy.eval '(oh-hungee a b) :macros (local-macros)) 15)) (assert (= (hy.eval '(oh-hungee a b) :macros {"oh_hungee" (get-macro oh-hungee)} 15))) ; You can shadow a global macro. (assert (= (hy.eval '(cheese)) "gorgonzola")) (assert (= (hy.eval '(cheese) :macros {"cheese" (fn [] "cheddar")}) "cheddar")) ; Or even a core macro, and with no warning. (assert (= (hy.eval '(+ 1 1) :macros {(hy.mangle "+") (fn [#* args] (.join "" (gfor x args (str (int x)))))}) "11"))) (defn test-filename [] (setv m (hy.read "(/ 1 0)" :filename "bad_math.hy")) (with [e (pytest.raises ZeroDivisionError)] (hy.eval m)) (assert (in "bad_math.hy" (get (hy.I.traceback.format-tb e.tb) -1)))) (defn test-eval-failure [] ; yo dawg (with [(pytest.raises TypeError)] (hy.eval '(hy.eval))) (defclass C) (with [(pytest.raises TypeError)] (hy.eval (C))) (with [(pytest.raises TypeError)] (hy.eval 'False [])) (with [(pytest.raises TypeError)] (hy.eval 'False {} 1))) hy-1.0.0/tests/native_tests/hy_misc.hy000066400000000000000000000114331467401276000200100ustar00rootroot00000000000000;; Tests of `hy.gensym`, `hy.macroexpand`, `hy.macroexpand-1`, ;; `hy.read`, `hy.I`, and `hy.R` (import pytest) (defn test-gensym [] (setv s1 (hy.gensym)) (assert (isinstance s1 hy.models.Symbol)) (assert (.startswith s1 "_hy_gensym__")) (setv s2 (hy.gensym "xx")) (setv s3 (hy.gensym "xx")) (assert (.startswith s2 "_hy_gensym_xx_")) (assert (!= s2 s3)) (assert (!= (str s2) (str s3))) (assert (.startswith (hy.gensym "•ab") "_hy_gensym_XbulletXab_"))) (defmacro mac [x expr] `(~@expr ~x)) (defn test-macroexpand [] (assert (= (hy.macroexpand '(mac (a b) (x y))) '(x y (a b)))) (assert (= (hy.macroexpand '(mac (a b) (mac 5))) '(a b 5))) (assert (= (hy.macroexpand '(qplah "phooey") :module hy.I.tests.resources.tlib) '[8 "phooey"])) (assert (= (hy.macroexpand '(chippy 1) :macros {"chippy" (fn [x] `[~x ~x])}) '[1 1])) ; Non-Expressions just get returned as-is. (defn f []) (assert (is (hy.macroexpand f) f)) ; Likewise Expressions that aren't macro calls. (setv model '(wmbatt 1 2)) (assert (is (hy.macroexpand model) model)) ; If the macro expands to a `Result`, the user gets the original ; back instead of the `Result`. (setv model '(+ 1 1)) (assert (is (hy.macroexpand model) model))) (defmacro m-with-named-import [] (import math [pow]) (pow 2 3)) (defn test-macroexpand-with-named-import [] ; https://github.com/hylang/hy/issues/1207 (assert (= (hy.macroexpand '(m-with-named-import)) (hy.models.Float (** 2 3))))) (defn test-macroexpand-1 [] (assert (= (hy.macroexpand-1 '(mac (a b) (mac 5))) '(mac 5 (a b))))) (defn test-read-file-object [] (import io [StringIO]) (setv stdin-buffer (StringIO "(+ 2 2)\n(- 2 2)")) (assert (= (hy.eval (hy.read stdin-buffer)) 4)) (assert (isinstance (hy.read stdin-buffer) hy.models.Expression)) ; Multiline test (setv stdin-buffer (StringIO "(\n+\n41\n1\n)\n(-\n2\n1\n)")) (assert (= (hy.eval (hy.read stdin-buffer)) 42)) (assert (= (hy.eval (hy.read stdin-buffer)) 1)) ; EOF test (setv stdin-buffer (StringIO "(+ 2 2)")) (hy.read stdin-buffer) (with [(pytest.raises EOFError)] (hy.read stdin-buffer))) (defn test-read-str [] (assert (= (hy.read "(print 1)") '(print 1))) (assert (is (type (hy.read "(print 1)")) (type '(print 1)))) ; Watch out for false values: https://github.com/hylang/hy/issues/1243 (assert (= (hy.read "\"\"") '"")) (assert (is (type (hy.read "\"\"")) (type '""))) (assert (= (hy.read "[]") '[])) (assert (is (type (hy.read "[]")) (type '[]))) (assert (= (hy.read "0") '0)) (assert (is (type (hy.read "0")) (type '0)))) (defn test-hyI [] (defmacro no-name [name] `(with [(pytest.raises NameError)] ~name)) ; `hy.I` doesn't bring the imported stuff into scope. (assert (= (hy.I.math.sqrt 4) 2)) (assert (= (.sqrt (hy.I "math") 4) 2)) (no-name math) (no-name sqrt) ; It's independent of bindings to such names. (setv math (type "Dummy" #() {"sqrt" "hello"})) (assert (= (hy.I.math.sqrt 4) 2)) (assert (= math.sqrt "hello")) ; It still works in a macro expansion. (defmacro frac [a b] `(hy.I.fractions.Fraction ~a ~b)) (assert (= (* 6 (frac 1 3)) 2)) (no-name fractions) (no-name Fraction) ; You can use `/` for dotted module names. (assert (= (hy.I.os/path.basename "foo/bar") "bar")) (no-name os) (no-name path) ; `hy.I.__getattr__` attempts to cope with mangling. (with [e (pytest.raises ModuleNotFoundError)] (hy.I.a-b☘c-d/e.z)) (assert (= e.value.name (hy.mangle "a-b☘c-d"))) ; `hy.I.__call__` doesn't. (with [e (pytest.raises ModuleNotFoundError)] (hy.I "a-b☘c-d/e.z")) (assert (= e.value.name "a-b☘c-d/e"))) (defn test-hyI-mangle-chain [tmp-path monkeypatch] ; We can get an object from a submodule with various kinds of ; mangling in the name chain. (setv p tmp-path) (for [e ["foo" "foo?" "_foo" "☘foo☘"]] (/= p (hy.mangle e)) (.mkdir p :exist-ok True) (.write-text (/ p "__init__.py") "")) (.write-text (/ p "foo.hy") "(setv foo 5)") (monkeypatch.syspath-prepend (str tmp-path)) ; Python will reuse any `foo` imported in an earlier test if we ; don't reload it explicitly. (import foo) (import importlib) (importlib.reload foo) (assert (= hy.I.foo/foo?/_foo/☘foo☘/foo.foo 5))) (defn test-hyR [] (assert (= (hy.R.tests/resources/tlib.qplah "x") [8 "x"])) (assert (= (hy.R.tests/resources/tlib.✈ "x") "plane x")) (with [(pytest.raises NameError)] (hy.eval '(tests.resources.tlib.qplah "x"))) (with [(pytest.raises NameError)] (hy.eval '(qplah "x"))) (with [(pytest.raises hy.errors.HyRequireError)] (hy.eval '(hy.R.tests/resources/tlib.nonexistent-macro "x"))) (with [(pytest.raises hy.errors.HyRequireError)] (hy.eval '(hy.R.nonexistent-module.qplah "x")))) hy-1.0.0/tests/native_tests/hy_repr.hy000066400000000000000000000114111467401276000200210ustar00rootroot00000000000000(import math [isnan]) (defn test-hy-repr-roundtrip-from-str [] ; Test that a variety of objects round-trip from strings. (import collections [deque ChainMap OrderedDict] fractions [Fraction] re) (for [original-str (lfor x (with [o (open "tests/resources/hy_repr_str_tests.txt")] (list o)) :setv x (.rstrip x) :if (and x (not (.startswith x ";"))) x (if (in (get x 0) "':") [x] [x (+ "'" x)]) x)] (setv rep (hy.repr (hy.eval (hy.read original-str)))) (assert (= rep original-str)))) (defn test-hy-repr-roundtrip-from-value [] ; As the previous test, but round-tripping of objects themselves ; instead of their string representations. This is a weaker test ; appropriate for objects that have different, but equivalent, ; hy-reprs from the input syntax. (setv values [ ':mykeyword {"a" 1 "b" 2 "a" 3} '{"a" 1 "b" 2 "a" 3} 'f"the answer is {(+ 2 2) = }" 'f"the answer is {(+ 2 2) = !r :4}"]) (for [original-val values] (setv evaled (hy.eval (hy.read (hy.repr original-val)))) (assert (= evaled original-val)) (assert (is (type evaled) (type original-val))))) (defn test-hy-repr-no-roundtrip [] ; Test one of the corner cases in which hy-repr doesn't ; round-trip: when a Hy model contains a non-model, we ; promote the constituent to a model. (setv orig `[a ~5.0]) (setv reprd (hy.repr orig)) (assert (= reprd "'[a 5.0]")) (setv result (hy.eval (hy.read reprd))) (assert (is (type (get orig 1)) float)) (assert (is (type (get result 1)) hy.models.Float))) (defn test-dict-views [] (assert (= (hy.repr (.keys {1 2})) "(dict-keys [1])")) (assert (= (hy.repr (.values {1 2})) "(dict-values [2])")) (assert (= (hy.repr (.items {1 2})) "(dict-items [#(1 2)])"))) (defn test-datetime [] (import datetime :as D) (assert (= (hy.repr (D.datetime 2009 1 15 15 27 5 0)) "(datetime.datetime 2009 1 15 15 27 5)")) (assert (= (hy.repr (D.datetime 2009 1 15 15 27 5 123)) "(datetime.datetime 2009 1 15 15 27 5 123)")) (assert (= (hy.repr (D.datetime 2009 1 15 15 27 5 123 :tzinfo D.timezone.utc)) "(datetime.datetime 2009 1 15 15 27 5 123 :tzinfo datetime.timezone.utc)")) (assert (= (hy.repr (D.datetime 2009 1 15 15 27 5 :fold 1)) "(datetime.datetime 2009 1 15 15 27 5 :fold 1)")) (assert (= (hy.repr (D.datetime 2009 1 15 15 27 5 :fold 1 :tzinfo D.timezone.utc)) "(datetime.datetime 2009 1 15 15 27 5 :tzinfo datetime.timezone.utc :fold 1)")) (assert (= (hy.repr (D.date 2015 11 3)) "(datetime.date 2015 11 3)")) (assert (= (hy.repr (D.time 1 2 3)) "(datetime.time 1 2 3)")) (assert (= (hy.repr (D.time 1 2 3 4567)) "(datetime.time 1 2 3 4567)")) (assert (= (hy.repr (D.time 1 2 3 4567 :fold 1 :tzinfo D.timezone.utc)) "(datetime.time 1 2 3 4567 :tzinfo datetime.timezone.utc :fold 1)"))) (defn test-collections [] (import collections) (assert (= (hy.repr (collections.defaultdict :a 8)) "(defaultdict None {\"a\" 8})")) (assert (= (hy.repr (collections.defaultdict int :a 8)) "(defaultdict {\"a\" 8})")) (assert (= (hy.repr (collections.Counter [15 15 15 15])) "(Counter {15 4})")) (setv C (collections.namedtuple "Fooey" ["cd" "a_b"])) (assert (= (hy.repr (C 11 12)) "(Fooey :cd 11 :a_b 12)"))) (defn test-hy-model-constructors [] (assert (= (hy.repr (hy.models.Integer 7)) "'7")) (assert (= (hy.repr (hy.models.String "hello")) "'\"hello\"")) (assert (= (hy.repr (hy.models.List [1 2 3])) "'[1 2 3]")) (assert (= (hy.repr (hy.models.Dict [1 2 3])) "'{1 2 3}"))) (defn test-hy-repr-self-reference [] (setv x [1 2 3]) (setv (get x 1) x) (assert (= (hy.repr x) "[1 [...] 3]")) (setv x {1 2 3 [4 5] 6 7}) (setv (get x 3 1) x) (assert (= (hy.repr x) "{1 2 3 [4 {...}] 6 7}"))) (defn test-matchobject [] (import re) (setv mo (re.search "b+" "aaaabbbccc")) (assert (= (hy.repr mo) #[[]]))) (defn test-hy-repr-custom [] (defclass C [object]) (hy.repr-register C (fn [x] "cuddles")) (assert (= (hy.repr (C)) "cuddles")) ; https://github.com/hylang/hy/issues/1873 (defclass D [C]) (assert (not-in "cuddles" (hy.repr (D)))) (defclass Container [object] (defn __init__ [self value] (setv self.value value))) (hy.repr-register Container :placeholder "(Container ...)" (fn [x] (+ "(Container " (hy.repr x.value) ")"))) (setv container (Container 5)) (setv container.value container) (assert (= (hy.repr container) "(Container (Container ...))")) (setv container.value [1 container 3]) (assert (= (hy.repr container) "(Container [1 (Container ...) 3])"))) (defn test-hy-repr-fallback [] (defclass D [object] (defn __repr__ [self] "cuddles")) (assert (= (hy.repr (D)) "cuddles"))) hy-1.0.0/tests/native_tests/import.hy000066400000000000000000000133341467401276000176710ustar00rootroot00000000000000;; Tests of `import`, `require`, and `export` (import importlib os.path os.path [exists isdir isfile] sys :as systest sys pytest hy.compat [PYODIDE]) (defn test-imported-bits [] (assert (is (exists ".") True)) (assert (is (isdir ".") True)) (assert (is (isfile ".") False))) (defn test-importas [] (assert (!= (len systest.path) 0))) (defn test-import-syntax [] ;; Simple import (import sys os) ;; from os.path import basename (import os.path [basename]) (assert (= (basename "/some/path") "path")) ;; import os.path as p (import os.path :as p) (assert (= p.basename basename)) ;; from os.path import basename as bn (import os.path [basename :as bn]) (assert (= bn basename)) ;; Multiple stuff to import (import sys os.path [dirname] os.path :as op os.path [dirname :as dn]) (assert (= (dirname "/some/path") "/some")) (assert (= op.dirname dirname)) (assert (= dn dirname))) (defn test-relative-import [] (import ..resources [tlib in-init]) (assert (= tlib.SECRET-MESSAGE "Hello World")) (assert (= in-init "chippy")) (import .. [resources]) (assert (= resources.in-init "chippy"))) (defn test-import-init-hy [] (import tests.resources.bin) (assert (in "_null_fn_for_import_test" (dir tests.resources.bin)))) (require tests.resources.tlib tests.resources.tlib :as TL tests.resources.tlib [qplah] tests.resources.tlib [parald :as parald-alias] tests.resources [tlib macros :as TM exports-none] os [path]) ; The last one is a no-op, since the module `os.path` exists but ; contains no macros. (defn test-require-global [] (assert (= (tests.resources.tlib.parald 1 2 3) [9 1 2 3])) (assert (= (tests.resources.tlib.✈ "silly") "plane silly")) (assert (= (tests.resources.tlib.hyx_XairplaneX "foolish") "plane foolish")) (assert (is (get-macro tests.resources.tlib.✈) (get _hy_macros (hy.mangle "tests.resources.tlib.✈")))) (assert (= (TL.parald 1 2 3) [9 1 2 3])) (assert (= (TL.✈ "silly") "plane silly")) (assert (= (TL.hyx_XairplaneX "foolish") "plane foolish")) (assert (= (qplah 1 2 3) [8 1 2 3])) (assert (= (parald-alias 1 2 3) [9 1 2 3])) (assert (in "tlib.qplah" _hy_macros)) (assert (in (hy.mangle "TM.test-macro") _hy_macros)) (assert (in (hy.mangle "exports-none.cinco") _hy_macros)) (with [(pytest.raises NameError)] (parald 1 2 3 4)) (with [(pytest.raises hy.errors.HyRequireError)] (hy.eval '(require tests.resources [does-not-exist])))) (require tests.resources.more-test-macros *) (defn test-require-global-star-without-exports [] (assert (= (bairn 1 2 3) [14 1 2 3])) (assert (= (cairn 1 2 3) [15 1 2 3])) (with [(pytest.raises NameError)] (_dairn 1 2 3 4))) (require tests.resources.exports *) (defn test-require-global-star-with-exports [] (assert (= (casey 1 2 3) [11 1 2 3])) (assert (= (☘ 1 2 3) [13 1 2 3])) (with [(pytest.raises NameError)] (brother 1 2 3 4))) (require ..resources.macros [test-macro-2] .beside [xyzzy] . [beside :as BS]) (defn test-require-global-relative [] (assert (in "test_macro_2" _hy_macros)) (assert (in "xyzzy" _hy_macros)) (assert (in "BS.xyzzy" _hy_macros))) ;; `remote-test-macro` is a macro used within ;; `tests.resources.macro-with-require.test-module-macro`. ;; Here, we introduce an equivalently named version that, when ;; used, will expand to a different output string. (defmacro remote-test-macro [x] "this is the home version of `remote-test-macro`!") (require tests.resources.macro-with-require *) (defmacro home-test-macro [x] (.format "This is the home version of `remote-test-macro` returning {}!" (int x))) (defn test-macro-namespace-resolution [] "Confirm that new versions of macro-macro dependencies do not shadow the versions from the macro's own module, but do resolve unbound macro references in expansions." ;; Was the above macro created properly? (assert (in "remote_test_macro" _hy_macros)) (setv remote-test-macro (get _hy_macros "remote_test_macro")) (setv module-name-var "tests.native_tests.native_macros.test-macro-namespace-resolution") (assert (= (+ "This macro was created in tests.resources.macros, " "expanded in tests.native_tests.native_macros.test-macro-namespace-resolution " "and passed the value 2.") (test-module-macro 2))) ;; Now, let's use a `require`d macro that depends on another macro defined only ;; in this scope. (assert (= "This is the home version of `remote-test-macro` returning 3!" (test-module-macro-2 3)))) (defn test-no-surprise-shadow [tmp-path monkeypatch] "Check that an out-of-module macro doesn't shadow a function." ; https://github.com/hylang/hy/issues/2451 (monkeypatch.syspath-prepend tmp-path) (.write-text (/ tmp-path "wexter_a.hy") #[[ (defmacro helper [] "helper a (macro)") (defmacro am [form] form)]]) (.write-text (/ tmp-path "wexter_b.hy") #[[ (require wexter-a [am]) (defn helper [] "helper b (function)") (setv v1 (helper)) (setv v2 (am (helper)))]]) (import wexter-b) (assert (= wexter-b.v1 "helper b (function)")) (assert (= wexter-b.v2 "helper b (function)"))) (defn test-recursive-require-star [] "(require foo *) should pull in macros required by `foo`." (require tests.resources.macro-with-require *) (test-macro) (assert (= blah 1))) (defn test-export-objects [] ; We use `hy.eval` here because of a Python limitation that ; importing `*` is only allowed at the module level. (hy.eval '(do (import tests.resources.exports *) (assert (= (jan) 21)) (assert (= (♥) 23)) (with [(pytest.raises NameError)] (wayne)) (import tests.resources.exports [wayne]) (assert (= (wayne) 22))))) hy-1.0.0/tests/native_tests/keywords.hy000066400000000000000000000061711467401276000202270ustar00rootroot00000000000000(import pickle pytest tests.resources [kwtest]) (defn test-keyword [] (assert (= :foo :foo)) (assert (not (!= :foo :foo))) (assert (!= :foo :bar)) (assert (= :foo ':foo)) (setv x :foo) (assert (is (type x) (type ':foo) hy.models.Keyword)) (assert (= (get {:foo "bar"} :foo) "bar")) (assert (= (get {:bar "quux"} (get {:foo :bar} :foo)) "quux"))) (defn test-keyword-clash [] "Keywords shouldn't clash with normal strings." (assert (= (get {:foo "bar" ":foo" "quux"} :foo) "bar")) (assert (= (get {:foo "bar" ":foo" "quux"} ":foo") "quux"))) (defn test-empty-keyword [] (assert (= : :)) (assert (isinstance ': hy.models.Keyword)) (assert (!= : ":")) (assert (= (. ': name) ""))) (defn test-order [] ; https://github.com/hylang/hy/issues/2594 (assert (< :a :b)) (assert (<= :a :b)) (assert (> :b :a)) (assert (= (sorted [:b :a :c]) [:a :b :c])) (with [(pytest.raises TypeError)] (< :a "b"))) (defn test-pickling-keyword [] ; https://github.com/hylang/hy/issues/1754 (setv x :test-keyword) (for [protocol (range 0 (+ pickle.HIGHEST-PROTOCOL 1))] (assert (= x (pickle.loads (pickle.dumps x :protocol protocol)))))) (defn test-keyword-get [] (assert (= (:foo (dict :foo "test")) "test")) (setv f :foo) (assert (= (f (dict :foo "test")) "test")) (assert (= (:foo-bar (dict :foo-bar "baz")) "baz")) (assert (= (:♥ (dict :♥ "heart")) "heart")) (defclass C [] (defn __getitem__ [self k] k)) (assert (= (:♥ (C)) "hyx_Xblack_heart_suitX")) (with [(pytest.raises KeyError)] (:foo (dict :a 1 :b 2))) (assert (= (:foo (dict :a 1 :b 2) 3) 3)) (assert (= (:foo (dict :a 1 :b 2 :foo 5) 3) 5)) (with [(pytest.raises TypeError)] (:foo "Hello World")) (with [(pytest.raises TypeError)] (:foo (object))) ; The default argument should work regardless of the collection type. (defclass G [object] (defn __getitem__ [self k] (raise KeyError))) (assert (= (:foo (G) 15) 15))) (defn test-keyword-creation [] (assert (= (hy.models.Keyword "foo") :foo)) (assert (= (hy.models.Keyword "foo_bar") :foo_bar)) (assert (= (hy.models.Keyword "foo-bar") :foo-bar)) (assert (!= :foo_bar :foo-bar)) (assert (= (hy.models.Keyword "") :))) (defn test-keywords-in-fn-calls [] (assert (= (kwtest) {})) (assert (= (kwtest :key "value") {"key" "value"})) (assert (= (kwtest :key-with-dashes "value") {"key_with_dashes" "value"})) (assert (= (kwtest :result (+ 1 1)) {"result" 2})) (assert (= (kwtest :key (kwtest :key2 "value")) {"key" {"key2" "value"}})) (assert (= ((get (kwtest :key (fn [x] (* x 2))) "key") 3) 6))) (defn test-kwargs [] (assert (= (kwtest :one "two") {"one" "two"})) (setv mydict {"one" "three"}) (assert (= (kwtest #** mydict) mydict)) (assert (= (kwtest #** ((fn [] {"one" "two"}))) {"one" "two"}))) (defmacro identify-keywords [#* elts] `(list (map (fn [x] (if (isinstance x hy.models.Keyword) "keyword" "other")) ~elts))) (defn test-keywords-and-macros [] "Macros should still be able to handle keywords as they best see fit." (assert (= (identify-keywords 1 "bloo" :foo) ["other" "other" "keyword"]))) hy-1.0.0/tests/native_tests/let.hy000066400000000000000000000367171467401276000171550ustar00rootroot00000000000000(import types pytest) (defn test-let-basic [] (assert (= (let [a 0] a) 0)) (setv a "a" b "b") (let [a "x" b "y"] (assert (= (+ a b) "xy")) (let [a "z"] (assert (= (+ a b) "zy"))) ;; let-shadowed variable doesn't get clobbered. (assert (= (+ a b) "xy"))) (let [q "q"] (assert (= q "q"))) (assert (= a "a")) (assert (= b "b")) (assert (in "a" (.keys (vars)))) ;; scope of q is limited to let body (assert (not-in "q" (.keys (vars))))) ;; let should substitute within f-strings ;; related to https://github.com/hylang/hy/issues/1843 (defn test-let-fstring [] (assert (= (let [a 0] a) 0)) (setv a "a" b "b") (let [a "x" b "y"] (assert (= f"res: {(+ a b)}!" "res: xy!")) (let [a 4] (assert (= f"double f >{b :^{(+ a 1)}}<" "double f > y <"))))) (defn test-let-sequence [] ;; assignments happen in sequence, not parallel. (setv a "x" b "y" c "z") (let [a "a" b "b" ab (+ a b)] (assert (= ab "ab")) (let [c "c" abc (+ ab c)] (assert (= abc "abc"))))) (defn test-let-early [] (setv a "a") (let [q (+ a "x") a 2 ; should not affect q b 3] (assert (= q "ax")) (let [q (* a b) a (+ a b) b (* a b)] (assert (= q 6)) (assert (= a 5)) (assert (= b 15)))) (assert (= a "a"))) (defn test-let-special [] ;; special forms in function position still work as normal (let [import 1] (assert (= #(import import) #(1 1))))) (defn test-let-if-result [] (let [af None] (setv af (if (> 5 3) (do 5 5) (do 3 3))) (assert (= af 5)))) (defn test-let-for [] (let [x 99] (for [x (range 20)]) (assert (= x 19)))) (defn test-let-generator [] (let [x 99] (lfor x (range 20) :do x x) ; force making a function (assert (= x 99)))) (defn test-let-comprehension-scope [] ; https://github.com/hylang/hy/issues/2224 (setv x 100) (let [x 10] (assert (= (lfor x (range 5) :if (> x 1) x) [2 3 4])) (assert (= x 10))) (let [x 15] (assert (= (lfor y (range 3) :setv x (* y 2) (+ y x)) [0 3 6])) (assert (= x 15))) (let [x 20] (assert (= (lfor z "abc" :do (setv x (.upper z)) (+ z x)) ["aA" "bB" "cC"])) (assert (= x "C"))) (let [x 25 l []] (for [x (range 5) :if (> x 1)] (.append l x)) (assert (= l [2 3 4])) (assert (= x 4))) (assert (= x 100))) (defn test-let-quasiquote [] (setv a-symbol 'a) (let [a "x"] (assert (= a "x")) (assert (= 'a a-symbol)) (assert (= `a a-symbol)) (assert (= (hy.as-model `(foo ~a)) '(foo "x"))) (assert (= (hy.as-model `(foo `(bar a ~a ~~a))) '(foo `(bar a ~a ~"x")))) (assert (= (hy.as-model `(foo ~@[a])) '(foo "x"))) (assert (= (hy.as-model `(foo `(bar [a] ~@[a] ~@~(hy.models.List [a 'a `a]) ~~@[a]))) '(foo `(bar [a] ~@[a] ~@["x" a a] ~"x")))))) (defn test-let-except [] (let [foo 42 bar 33] (assert (= foo 42)) (try (do (/ 1 0) (assert False)) (except [foo Exception] ;; let bindings should work in except block (assert (= bar 33)) ;; but exception bindings can shadow let bindings (assert (= (get (locals) "foo") foo)) (assert (isinstance foo Exception)))) ;; let binding did not get clobbered. (assert (= foo 42)))) (defn test-let-with [] (let [foo 42] (assert (= foo 42)) (with [foo (pytest.raises ZeroDivisionError)] (do (assert (!= foo 42)) (/ 1 0) (assert False))) (assert (is (. foo type) ZeroDivisionError)))) (defn test-let-mutation [] (setv foo 42) (setv error False) (let [foo 12 bar 13] (assert (= foo 12)) (setv foo 14) (assert (= foo 14)) (del foo) ;; deleting a let binding should not affect others (assert (= bar 13)) (try ;; foo=42 is still shadowed, but the let binding was deleted. (do foo (assert False)) (except [le UnboundLocalError] (setv error le))) (setv foo 16) (assert (= foo 16)) (setv [foo bar baz] [1 2 3]) (assert (= foo 1)) (assert (= bar 2)) (assert (= baz 3))) (assert error) (assert (= foo 42)) (assert (= baz 3))) (defn test-let-break [] (for [x (range 3)] (let [done (% x 2)] (when done (break)))) (assert (= x 1))) (defn test-let-continue [] (let [foo []] (for [x (range 10)] (let [odd (% x 2)] (when odd (continue)) (.append foo x))) (assert (= foo [0 2 4 6 8])))) (defn test-let-yield [] (defn grind [] (yield 0) (let [a 1 b 2] (yield a) (yield b))) (assert (= (tuple (grind)) #(0 1 2)))) (defn test-let-return [] (defn get-answer [] (let [answer 42] (return answer))) (assert (= (get-answer) 42))) (defn test-let-import [] (let [types 6] (assert (= types 6)) ;; imports shadow let-bound names (import types) (assert (in "types" (vars))) (assert (isinstance types types.ModuleType))) ;; import happened in Python scope. (assert (in "types" (vars))) (assert (isinstance types types.ModuleType))) (defn test-let-defn [] (let [foo 42 bar 99 quux "quux" baz "baz"] (assert (= foo 42)) ;; the name of the defn should be unaffected by the let (defn foo [bar] ; let bindings do not apply in param list ;; let bindings apply inside fn body (nonlocal baz) ;; nonlocal should allow access to outer let bindings (setv x foo) (assert (isinstance x types.FunctionType)) (assert (= (get (locals) "bar") bar)) (setv y baz) (setv baz bar) (setv baz f"foo's {baz = }") ;; quux is local, so should shadow the let binding (setv quux "foo quux") (assert (= (get (locals) "quux") quux)) quux) (assert (= quux "quux")) (assert (= foo (get (locals) "foo"))) (assert (isinstance foo types.FunctionType)) (assert (= baz "baz")) (assert (= (foo 2) "foo quux")) (assert (= baz "foo's baz = 2"))) ;; defn happened in Python scope (assert (= foo (get (locals) "foo"))) (assert (isinstance foo types.FunctionType)) (assert (= (foo 2) "foo quux"))) (defn test-nested-assign [] (let [fox 42] (defn bar [] (let [unrelated 99] (setv fox 3)) (assert (= (get (locals) "fox") fox)) (assert (= fox 3))) (bar) (assert (= fox 42)))) (defn test-top-level-let-nonlocal [] (hy.eval '(do (let [my-fuel 50] (defn propulse-me [distance] (nonlocal my-fuel) (-= my-fuel distance)) (defn check-fuel [] my-fuel)) (assert (= (check-fuel) 50)) (propulse-me 3) (assert (= (check-fuel) 47))) :globals {})) (defn test-let-nested-nonlocal [] (let [fox 42] (defn bar [] (let [unrelated 99] (defn baz [] (nonlocal fox) (setv fox 2))) (setv fox 3) (assert (= fox 3)) (baz) (assert (= fox 2))) (assert (= fox 42))) (bar)) (defn test-let-defclass [] (let [Foo 42 quux "quux" baz object] (assert (= Foo 42)) ;; the name of the class should be unaffected by the let (defclass Foo [baz] ; let bindings apply in inheritance list ;; let bindings apply inside class body (defn baz [self] Foo) ;; quux is local (setv quux "foo quux")) (assert (= quux "quux")) (assert (= Foo (get (locals) "Foo"))) (assert (= Foo.quux "foo quux")) (assert (= (.baz (Foo)) Foo))) ;; defclass happened in Python scope (assert (= Foo (get (locals) "Foo"))) (assert (= Foo.quux "foo quux")) (assert (= (.baz (Foo)) Foo))) (defn test-let-dot [] (setv foo (fn []) foo.a 42) (let [a 1 b [] bar (fn [])] (setv bar.a 13) (assert (= bar.a 13)) (setv (. bar a) 14) (assert (= bar.a 14)) (assert (= a 1)) (assert (= b [])) ;; method syntax not affected (.append b 2) (assert (= b [2])) ;; attrs access is not affected (assert (= foo.a 42)) (assert (= (. foo a) 42)) ;; but indexing is (assert (= (. [1 2 3] [a]) 2)))) (defn test-let-positional [] (let [a 0 b 1 c 2] (defn foo [a b] #(a b c)) (assert (= (foo 100 200) #(100 200 2))) (setv c 300) (assert (= (foo 1000 2000) #(1000 2000 300))) (assert (= a 0)) (assert (= b 1)) (assert (= c 300)))) (defn test-let-rest [] (let [xs 6 a 88 c 64 &rest 12] (defn foo [a b #* xs] (-= a 1) (setv xs (list xs)) (.append xs 42) #(&rest a b c xs)) (assert (= xs 6)) (assert (= a 88)) (assert (= (foo 1 2 3 4) #(12 0 2 64 [3 4 42]))) (assert (= xs 6)) (assert (= c 64)) (assert (= a 88)))) (defn test-let-kwargs [] (let [kws 6 &kwargs 13] (defn foo [#** kws] #(&kwargs kws)) (assert (= kws 6)) (assert (= (foo :a 1) #(13 {"a" 1}))))) (defn test-let-optional [] (let [a 1 b 6 d 2] (defn foo [[a a] [b None] [c d]] #(a b c)) (assert (= (foo) #(1 None 2))) (assert (= (foo 10 20 30) #(10 20 30))))) (defn test-let-closure [] (let [count 0] (defn +count [[x 1]] (nonlocal count) (+= count x) count)) ;; let bindings can still exist outside of a let body (assert (= 1 (+count))) (assert (= 2 (+count))) (assert (= 42 (+count 40)))) (defmacro triple [a] (setv g!a (hy.gensym a)) `(do (setv ~g!a ~a) (+ ~g!a ~g!a ~g!a))) (defmacro ap-triple [] '(+ a a a)) (defn test-let-macros [] (let [a 1 b (triple a) c (ap-triple)] (assert (= (triple a) 3)) (assert (= (ap-triple) 3)) (assert (= b 3)) (assert (= c 3)))) (defn test-let-rebind [] (let [x "foo" y "bar" x (+ x y) y (+ y x) x (+ x x)] (assert (= x "foobarfoobar")) (assert (= y "barfoobar")))) (defn test-let-unpacking [] (let [[a b] [1 2] [lhead #* ltail] (range 3) #(thead #* ttail) (range 3) [nhead #* #(c #* nrest)] [0 1 2]] (assert (= a 1)) (assert (= b 2)) (assert (= lhead 0)) (assert (= ltail [1 2])) (assert (= thead 0)) (assert (= ttail [1 2])) (assert (= nhead 0)) (assert (= c 1)) (assert (= nrest [2])))) (defn test-let-unpacking-rebind [] (let [[a b] [:foo :bar] [a #* c] (range 3) [head #* tail] [a b c]] (assert (= a 0)) (assert (= b :bar)) (assert (= c [1 2])) (assert (= head 0)) (assert (= tail [:bar [1 2]])))) (defn test-no-extra-eval-of-function-args [] ; https://github.com/hylang/hy/issues/2116 (setv l []) (defn f [] (.append l 1)) (let [a 1] (assert (= a 1)) (defn g [[b (f)]] 5)) (assert (= (g) 5)) (assert (= l [1]))) (defn test-let-optional-2 [] (let [a 1 b 6 d 2] (defn foo [* [a a] b [c d]] #(a b c)) (assert (= (foo :b "b") #(1 "b" 2))) (assert (= (foo :b 20 :a 10 :c 30) #(10 20 30))))) (defmacro eval-isolated [#* body] `(hy.eval '(do ~@body) :module (hy.I.types.ModuleType "") :locals {})) (defn test-let-bound-nonlocal [] (with [err (pytest.raises SyntaxError)] (eval-isolated (let [foo 99] (nonlocal undefined)))) (assert (in "no binding for nonlocal 'undefined'" err.value.msg)) (with [err (pytest.raises SyntaxError)] (eval-isolated (defn bax [] (let [other 99] (let [foo 99] (nonlocal undefined) other))))) (assert (in "no binding for nonlocal 'undefined'" err.value.msg)) (with [err (pytest.raises SyntaxError)] (eval-isolated (defn bax [] (let [other 99] (let [unrelated 99] (nonlocal unrelated) other))))) (assert (in "no binding for nonlocal 'unrelated'" err.value.msg)) (with [err (pytest.raises SyntaxError)] (eval-isolated (defn outer [] (let [fox 42] (defn bar [] (let [unrelated 99] (nonlocal something-else) ; error, nothing to bind to (setv fox 2)) (setv fox 3)))))) (assert (in "no binding for nonlocal 'something_else'" err.value.msg)) (eval-isolated (defn outer [] (defn bax [] (let [other 99] (let [unrelated 99] ; should be elided, allowing this to compile (nonlocal other) other))) (bax)) (assert (= 99 (outer)))) (let [fox 42] (defn baz [] (let [unrelated 99] (nonlocal fox) ; should bind to outer let from within bar (setv fox 2)) (setv fox 3)) ; `nonlocal` affects everything in the Python scope (assert (= fox 42)) (baz) (assert (= fox 3))) (setv hound 43) (defn bay [] (let [unrelated 99] (nonlocal hound) ; this should bind to outer `hound` as expected (setv hound 2)) (setv hound 3)) ; `nonlocal` affects everything in the Python scope (assert (= hound 43)) (bay) (assert (= hound 3)) (let [fox 42] (setv wolf 44) (defn bax [] (let [other 99] (let [unrelated 99] ; only binds `fox` and `wolf`; `other` is elided (allowing this to compile) (nonlocal fox wolf other) (setv fox 1) (setv wolf 2)) (setv fox 3)) (setv wolf 4)) (assert (= fox 42)) (assert (= wolf 44)) (bax) (assert (= fox 3)) (assert (= wolf 4)))) (defn test-let-bound-global [] (eval-isolated (defn outer [] (let [fox 42] (defn bar [] (let [something-else 33] (let [unrelated 99] (global something-else) (setv something-else 2)))) (bar))) (outer) (assert (= something-else 2))) (eval-isolated (defn bax [] (let [other 99] (let [unrelated 99] (global other) (setv other 2)) (setv other 3))) ; `global` affects everything in the Python scope (bax) (assert (= other 3))) (eval-isolated (let [fox 42] (defn baz [] (let [unrelated 99] (global fox) ; should bind outside any let scope (setv fox 2)) (setv fox 3)) ; `global` affects everything in the Python scope (assert (= fox 42)) (baz) (assert (= fox 42))) (assert (= fox 3))) (eval-isolated (setv hound 43) (defn bay [] (let [unrelated 99] (global hound) ; this should bind to global `hound` as expected (setv hound 2)) (setv hound 3)) ; `global` affects everything in the Python scope (assert (= hound 43)) (bay) (assert (= hound 3))) (eval-isolated (let [fox 42] (setv wolf 44) (defn bax [] (let [other 99] (let [unrelated 99] (global fox wolf other) (setv fox 1) (setv wolf 2) (setv other 3)) (setv fox 4)) (setv wolf 5)) (assert (= fox 42)) (assert (= wolf 44)) (bax) (assert (= fox 42)) (assert (= wolf 5)) (assert (= other 3))) (assert (= fox 4)) (assert (= wolf 5)) (assert (= other 3)))) hy-1.0.0/tests/native_tests/logic_short_circuit.hy000066400000000000000000000023451467401276000224150ustar00rootroot00000000000000;; More basic tests of `and` and `or` can be found in `operators.hy`. (defn test-and [] (setv a 1) (and 0 (setv a 2)) (assert (= a 1))) (defn test-and-#1151-do [] (setv a (and 0 (do 2 3))) (assert (= a 0)) (setv a (and 1 (do 2 3))) (assert (= a 3))) (defn test-and-#1151-for [] (setv l []) (setv x (and 0 (for [n [1 2]] (.append l n)))) (assert (= x 0)) (assert (= l [])) (setv x (and 15 (for [n [1 2]] (.append l n)))) (assert (= l [1 2]))) (defn test-and-#1151-del [] (setv l ["a" "b"]) (setv x (and 0 (del (get l 1)))) (assert (= x 0)) (assert (= l ["a" "b"])) (setv x (and 15 (del (get l 1)))) (assert (= l ["a"]))) (defn test-or [] (setv a 1) (or 1 (setv a 2)) (assert (= a 1))) (defn test-or-#1151-do [] (setv a (or 1 (do 2 3))) (assert (= a 1)) (setv a (or 0 (do 2 3))) (assert (= a 3))) (defn test-or-#1151-for [] (setv l []) (setv x (or 15 (for [n [1 2]] (.append l n)))) (assert (= x 15)) (assert (= l [])) (setv x (or 0 (for [n [1 2]] (.append l n)))) (assert (= l [1 2]))) (defn test-or-#1151-del [] (setv l ["a" "b"]) (setv x (or 15 (del (get l 1)))) (assert (= x 15)) (assert (= l ["a" "b"])) (setv x (or 0 (del (get l 1)))) (assert (= l ["a"]))) hy-1.0.0/tests/native_tests/macros.hy000066400000000000000000000120171467401276000176400ustar00rootroot00000000000000(import os sys warnings pytest hy.errors [HySyntaxError HyTypeError HyMacroExpansionError]) (defmacro rev [#* body] "Execute the `body` statements in reverse" (quasiquote (do (unquote-splice (list (reversed body)))))) (defmacro mac [x expr] `(~@expr ~x)) (defn test-macro-call-in-called-lambda [] (assert (= ((fn [] (mac 2 (- 10 1)))) 7))) (defn test-stararged-native-macro [] (setv x []) (rev (.append x 1) (.append x 2) (.append x 3)) (assert (= x [3 2 1]))) (defn test-macros-returning-constants [] (defmacro an-int [] 42) (assert (= (an-int) 42)) (defmacro a-true [] True) (assert (= (a-true) True)) (defmacro a-false [] False) (assert (= (a-false) False)) (defmacro a-float [] 42.) (assert (= (a-float) 42.)) (defmacro a-complex [] 42j) (assert (= (a-complex) 42j)) (defmacro a-string [] "foo") (assert (= (a-string) "foo")) (defmacro a-bytes [] b"foo") (assert (= (a-bytes) b"foo")) (defmacro a-list [] [1 2]) (assert (= (a-list) [1 2])) (defmacro a-tuple [#* b] b) (assert (= (a-tuple 1 2) #(1 2))) (defmacro a-dict [] {1 2}) (assert (= (a-dict) {1 2})) (defmacro a-set [] #{1 2}) (assert (= (a-set) #{1 2})) (defmacro a-none []) (assert (= (a-none) None))) ; A macro calling a previously defined function (eval-when-compile (defn foo [x y] (quasiquote (+ (unquote x) (unquote y))))) (defmacro bar [x y] (foo x y)) (defn test-macro-kw [] "An error is raised when * or #** is used in a macro" (with [(pytest.raises HySyntaxError)] (hy.eval '(defmacro f [* a b]))) (with [(pytest.raises HySyntaxError)] (hy.eval '(defmacro f [#** kw]))) (with [(pytest.raises HySyntaxError)] (hy.eval '(defmacro f [a b #* body c])))) (defn test-macro-bad-name [] (with [e (pytest.raises HySyntaxError)] (hy.eval '(defmacro :kw []))) (assert (in "got unexpected token: :kw" e.value.msg)) (with [(pytest.raises HySyntaxError)] (hy.eval '(defmacro foo.bar [])))) (defn test-macro-calling-fn [] (assert (= 3 (bar 1 2)))) (defn test-optional-and-unpacking-in-macro [] ; https://github.com/hylang/hy/issues/1154 (defn f [#* args] (+ "f:" (repr args))) (defmacro mac [[x None]] `(f #* [~x])) (assert (= (mac) "f:(None,)"))) (defn test-macro-autoboxing-docstring [] (defmacro m [] (setv mystring "hello world") `(fn [] ~mystring (+ 1 2))) (setv f (m)) (assert (= (f) 3)) (assert (= f.__doc__ "hello world"))) ; Macro that checks a variable defined at compile or load time (setv phase "load") (eval-when-compile (setv phase "compile")) (defmacro phase-when-compiling [] phase) (assert (= phase "load")) (assert (= (phase-when-compiling) "compile")) (setv initialized False) (eval-and-compile (setv initialized True)) (defmacro test-initialized [] initialized) (assert initialized) (assert (test-initialized)) (defmacro gensym-example [] `(setv ~(hy.gensym) 1)) (defn test-gensym-in-macros [] ; Call `gensym-example` twice, getting a distinct gensym each time. (defclass C [] (gensym-example) (gensym-example)) (assert (= (len (sfor a (dir C) :if (not (.startswith a "__")) a)) 2))) (defn test-macro-errors [] (import traceback hy.importer [read-many]) (setv test-expr (read-many "(defmacro blah [x] `(print ~@z)) (blah y)")) (with [excinfo (pytest.raises HyMacroExpansionError)] (hy.eval test-expr)) (setv output (traceback.format_exception_only excinfo.type excinfo.value)) (setv output (cut (.splitlines (.strip (get output 0))) 1 None)) (setv expected [" File \"\", line 1" " (defmacro blah [x] `(print ~@z)) (blah y)" (hy.compat.reu " ^------^") "expanding macro blah" " NameError: global name 'z' is not defined"]) (assert (= (cut expected 0 -1) (cut output 0 -1))) (assert (or (= (get expected -1) (get output -1)) ;; Handle PyPy's peculiarities (= (.replace (get expected -1) "global " "") (get output -1)))) ;; This should throw a `HyWrapperError` that gets turned into a ;; `HyMacroExpansionError`. (with [excinfo (pytest.raises HyMacroExpansionError)] (hy.eval '(do (defmacro wrap-error-test [] (fn [])) (wrap-error-test)))) (assert (in "HyWrapperError" (str excinfo.value)))) (defn macro-redefinition-warning-tester [local] (for [should-warn? [True False] head ["defmacro" "require"]] (with [(if should-warn? (pytest.warns RuntimeWarning :match "will shadow the core macro") (warnings.catch-warnings))] (when (not should-warn?) ; Elevate any warning to an error. (warnings.simplefilter "error")) (hy.eval `( ~@(if local '[defn f []] '[do]) ~(if should-warn? None '(pragma :warn-on-core-shadow False)) ~(if (= head "defmacro") '(defmacro when [] 1) '(require tests.resources.tlib [qplah :as when]))))))) (defn test-macro-redefinition-warning [] (macro-redefinition-warning-tester :local False)) hy-1.0.0/tests/native_tests/macros_first_class.hy000066400000000000000000000061551467401276000222420ustar00rootroot00000000000000"Tests of using macros as first-class objects: listing, creating, and deleting them, and retrieving their docstrings. We also test `get-macro` (with regular, non-reader macros)." (import builtins) ;; * Core macros (defn test-core [] (assert (in "when" (.keys builtins._hy_macros))) (assert (not-in "global1" (.keys builtins._hy_macros))) (assert (not-in "nonexistent" (.keys builtins._hy_macros))) (assert (is (get-macro when) (get-macro "when") (get builtins._hy_macros "when"))) (setv s (. (get-macro when) __doc__)) (assert s) (assert (is (type s) str))) ;; * Global macros ;; ** Creation ; There are three ways to define a global macro: ; 1. `defmacro` in global scope (defmacro global1 [] "global1 docstring" "from global1") ; 2. `require` in global scope (require tests.resources.tlib [qplah :as global2]) ; 3. Manually updating `_hy_macros` (eval-and-compile (setv (get _hy_macros "global3") (fn [] "from global3"))) (eval-and-compile (setv (get _hy_macros (hy.mangle "global☘")) (fn [] "global☘ docstring" "from global☘"))) (defn test-globals [] (assert (not-in "when" (.keys _hy_macros))) (assert (not-in "nonexistent" (.keys _hy_macros))) (assert (all (gfor k ["global1" "global2" "global3" "global☘"] (in (hy.mangle k) (.keys _hy_macros))))) (assert (= (global3) "from global3")) (assert (= (global☘) "from global☘")) (assert (= (. (get-macro global1) __doc__) "global1 docstring")) ; https://github.com/hylang/hy/issues/1946 (assert (= (. (get-macro global☘) __doc__) "global☘ docstring")) (assert (= (. (get-macro hyx_globalXshamrockX) __doc__) "global☘ docstring"))) ;; ** Deletion ; Try creating and then deleting a global macro. (defn global4 [] "from global4 function") (setv global4-f1 (global4)) ; Calls the function (defmacro global4 [] "from global4 macro") (setv global4-m (global4)) ; Calls the macro (eval-when-compile (del (get-macro global4))) (setv global4-f2 (global4)) ; Calls the function again (defn test-global-delete [] (assert (= (global4) global4-f1 global4-f2 "from global4 function")) (assert (= global4-m "from global4 macro"))) ;; ** Shadowing a core macro ; Try overriding a core macro, then deleting the override. (pragma :warn-on-core-shadow False) (defmacro / [a b] f"{(int a)}/{(int b)}") (setv div1 (/ 1 2)) (eval-when-compile (del (get-macro /))) (setv div2 (/ 1 2)) (defn test-global-shadowing-builtin [] (assert (= div1 "1/2")) (assert (= div2 0.5))) ;; * Local macros (defn test-local-get [] (defmacro local1 [] "local1 doc" 1) (defmacro local2 [] "local2 outer" 2) (require tests.resources.local-req-example :as LRE) (assert (= (. (get-macro local1) __doc__) "local1 doc")) (assert (= (. (get-macro local2) __doc__) "local2 outer")) (assert (= (. (get-macro LRE.wiz) __doc__) "remote wiz doc")) (defn inner [] (defmacro local2 [] "local2 inner" 2) (defmacro local3 [] "local3 doc" 2) (assert (= (. (get-macro local2) __doc__) "local2 inner")) (assert (= (. (get-macro local3) __doc__) "local3 doc")) (assert (= (. (get-macro LRE.wiz) __doc__) "remote wiz doc"))) (inner)) hy-1.0.0/tests/native_tests/macros_local.hy000066400000000000000000000063661467401276000210240ustar00rootroot00000000000000"Tests of local `defmacro` and `require`." (import tests.native-tests.macros [macro-redefinition-warning-tester] pytest) (defn test-nonleaking [] (defn fun [] (defmacro helper [] "helper macro in fun") (helper)) (defclass C [] (defmacro helper [] "helper macro in class") (setv attribute (helper))) (defn helper [] "helper function") (assert (= (helper) "helper function")) (assert (= (fun) "helper macro in fun")) (assert (= C.attribute "helper macro in class")) (assert (= (lfor x [1 2 3] :do (defmacro helper [] "helper macro in lfor") y [1 2 3] (if (= x y 2) (helper) (+ (* x 10) y))) [11 12 13 21 "helper macro in lfor" 23 31 32 33])) (assert (= (helper) "helper function"))) (defmacro shadowable [] "global version") (defn test-shadowing-global [] (defn inner [] (defmacro shadowable [] "local version") (shadowable)) (assert (= (shadowable) "global version")) (assert (= (inner) "local version")) (assert (= (shadowable) "global version"))) (defn test-nested-local-shadowing [] (defn inner1 [] (defmacro shadowable [] "local version 1") (defn inner2 [] (defmacro shadowable [] "local version 2") (shadowable)) [(inner2) (shadowable)]) (assert (= (shadowable) "global version")) (print (inner1)) (assert (= (inner1) ["local version 2" "local version 1"])) (assert (= (shadowable) "global version"))) (defmacro one-plus-two [] '(+ 1 2)) (defn test-local-macro-in-expansion-of-nonlocal [] (defn f [] (pragma :warn-on-core-shadow False) (defmacro + [a b] "Shadow the core macro `+`. #yolo" `f"zomg! {~a} {~b}") (one-plus-two)) (assert (= (f) "zomg! 1 2")) (assert (= (one-plus-two) 3))) (defmacro local-require-test [arg] `(do (defmacro wiz [] "local wiz") (defn fun [] (require tests.resources.local-req-example ~arg) [(get-wiz) (helper)]) (defn helper [] "local helper function") (assert (= [(wiz) (helper)] ["local wiz" "local helper function"])) (assert (= (fun) ["remote wiz" "remote helper macro"])) (assert (= [(wiz) (helper)] ["local wiz" "local helper function"])))) (defn test-require [] (local-require-test [get-wiz helper])) (defn test-require-star [] (local-require-test *)) (defn test-redefinition-warning [] (macro-redefinition-warning-tester :local True)) (defn test-get-local-macros [] "Test the core macro `local-macros`." (defn check [ms inner?] (assert (= (set (.keys ms)) #{"m1" "m2" "rwiz" #* (if inner? ["m3" "rhelp"] [])})) (assert (= (. ms ["m1"] __doc__) "m1 doc")) (assert (= (. ms ["m2"] __doc__) (if inner? "m2, inner" "m2, outer"))) (assert (= (. ms ["rwiz"] __doc__) "remote wiz doc")) (when inner? (assert (= (. ms ["m3"] __doc__) "m3 doc")) (assert (= (. ms ["rhelp"] __doc__) "remote helper doc")))) (defmacro m1 [] "m1 doc" 1) (defmacro m2 [] "m2, outer" 2) (require tests.resources.local-req-example [wiz :as rwiz]) (check (local-macros) False) (defn inner-f [] (defmacro m2 [] "m2, inner" 2) (defmacro m3 [] "m3 doc" 3) (require tests.resources.local-req-example [helper :as rhelp]) (check (local-macros) True)) (inner-f) (check (local-macros) False)) hy-1.0.0/tests/native_tests/mangling.hy000066400000000000000000000163311467401276000201530ustar00rootroot00000000000000(defn test-hyphen [] (setv a-b 1) (assert (= a-b 1)) (assert (= a_b 1)) (setv -a-_b- 2) (assert (= -a-_b- 2)) (assert (= -a--b- 2)) (assert (= -a__b- 2)) (setv -_- 3) (assert (= -_- 3)) (assert (= -__ 3))) (defn test-mangle-hyphen-underscore [] ;; https://github.com/hylang/hy/issues/1635 (assert (= (hy.mangle "-") "hyx_XhyphenHminusX")) (assert (= (hy.mangle "-a") "hyx_XhyphenHminusXa")) (assert (= (hy.mangle "-_a") "hyx_XhyphenHminusX_a")) (assert (= (hy.mangle "_-a") "_hyx_XhyphenHminusXa")) (assert (= (hy.mangle "__init__") "__init__")) (assert (= (hy.mangle "--init--") "hyx_XhyphenHminusX_init__")) (assert (= (hy.mangle "__dunder-name__") "__dunder_name__")) (assert (= (hy.mangle "-->") "hyx_XhyphenHminusX_XgreaterHthan_signX")) (assert (= (hy.mangle "<--") "hyx_XlessHthan_signX__")) ;; test various interactions (assert (= (hy.mangle "----") "hyx_XhyphenHminusX___")) (assert (= (hy.mangle "--__") "hyx_XhyphenHminusX___")) (assert (= (hy.mangle "__--") "__hyx_XhyphenHminusX_")) (assert (= (hy.mangle "__--__") "__hyx_XhyphenHminusX___")) ;; test unmangling choices (assert (= (hy.unmangle "hyx_XhyphenHminusX") "-")) (assert (= (hy.unmangle "_") "_")) (assert (= (hy.unmangle "__init__") "__init__")) (assert (= (hy.unmangle "hyx_XhyphenHminusX_init__") "--init--")) (assert (= (hy.unmangle "__dunder_name__") "__dunder-name__")) (assert (= (hy.unmangle "hyx_XhyphenHminusX_XgreaterHthan_signX") "-->")) (assert (= (hy.unmangle "hyx_XlessHthan_signX__") "<--"))) (defn test-underscore-number [] (setv _42 3) (assert (= _42 3)) (assert (!= _42 -42)) (assert (not (in "_hyx_42" (locals))))) (defn test-question-mark [] ; Once treated specially, but no more. (setv foo? "nachos") (assert (= foo? "nachos")) (assert (= hyx_fooXquestion_markX "nachos"))) (defn test-py-forbidden-ascii [] (setv $ "dosh") (assert (= $ "dosh")) (assert (= hyx_Xdollar_signX "dosh"))) (defn test-basic-multilingual-plane [] (setv ♥ "love" ⚘ab "flower") (assert (= (+ ⚘ab ♥) "flowerlove")) (assert (= (+ hyx_XflowerXab hyx_Xblack_heart_suitX) "flowerlove")) (setv ⚘-⚘ "doubleflower") (assert (= ⚘-⚘ "doubleflower")) (assert (= hyx_XflowerX_XflowerX "doubleflower"))) (defn test-higher-unicode [] (setv 😂 "emoji") (assert (= 😂 "emoji")) (assert (= hyx_Xface_with_tears_of_joyX "emoji"))) (defn test-nameless-unicode [] (setv  "private use") (assert (=  "private use")) (assert (= hyx_XUe000X "private use"))) (defn test-charname-with-hyphen [] (setv a= n 3))) (assert (= s "xxx")) (do-until (+= s "x") (until (+= n 1) (>= n 3))) (assert (= s "xxxx"))) (defmacro loop [#* args] (import hy.model-patterns [whole FORM sym SYM] funcparserlib.parser [many]) (setv [loopers body] (.parse (whole [ (many (| (>> (+ (sym "while") FORM) (fn [x] [x])) (+ (sym "for") SYM (sym "in") FORM) (+ (sym "for") SYM (sym "from") FORM (sym "to") FORM))) (sym "do") (many FORM)]) args)) (defn f [loopers] (setv head (if loopers (get loopers 0) None)) (setv tail (cut loopers 1 None)) (cond (is head None) `(do ~@body) (= (len head) 1) `(while ~@head ~(f tail)) (= (len head) 2) `(for [~@head] ~(f tail)) True (do ; (= (len head) 3) (setv [sym from to] head) `(for [~sym (range ~from (+ ~to 1))] ~(f tail))))) (f loopers)) (defn test-loop [] (setv l []) (loop for x in "abc" do (.append l x)) (assert (= l ["a" "b" "c"])) (setv l [] k 2) (loop while (> k 0) for n from 1 to 3 for p in [k n (* 10 n)] do (.append l p) (-= k 1)) (assert (= l [2 1 10 -1 2 20 -4 3 30]))) hy-1.0.0/tests/native_tests/nonlocal.hy000066400000000000000000000033721467401276000201650ustar00rootroot00000000000000(import pytest) (defn test-nonlocal-promotion [] (setv G {}) (hy.eval '(do (setv home "earth") (defn blastoff [] (nonlocal home) (setv home "saturn")) (blastoff)) :globals G) (assert (= (get G "home") "saturn")) (setv health (hy.eval '(do (defn make-ration-log [days intensity] (setv health 20 ration-log (list (map (fn [_] ;; only `rations` should be upgraded (nonlocal rations health) (-= rations intensity) (+= health (* 0.5 intensity)) rations) (range days)))) health) ;; "late" global binding should still work (setv rations 100) (make-ration-log 43 1.5)) :globals G)) (assert (= health (+ 20 (* 43 0.5 1.5)))) (assert (= (get G "rations") (- 100 (* 43 1.5))))) (defn test-nonlocal-must-have-def [] (with [err (pytest.raises SyntaxError)] (hy.eval '(do (defn make-ration-log [days intensity] (list (map (fn [_] (nonlocal rations) (-= rations intensity) rations) (range days)))) ;; oops! I forgot to pack my rations! (make-ration-log 43 1.5)) :globals {})) (assert (in "no binding for nonlocal 'rations'" err.value.msg))) hy-1.0.0/tests/native_tests/operators.hy000066400000000000000000000237261467401276000204030ustar00rootroot00000000000000(import pytest) (import hy.pyops *) (defmacro op-and-shadow-test [op #* body] ; Creates two tests with the given `body`, one where all occurrences ; of the symbol `f` are syntactically replaced with `op` (a test of ; the real operator), and one where the body is preceded by an ; assignment of `op` to `f` (a test of the shadow operator). ; ; `op` can also be a list of operators, in which case two tests are ; created for each operator. (import collections.abc [Iterable]) (setv defns []) (for [o (if (isinstance op hy.models.Symbol) [op] op)] (defn replace [x] (cond (= x 'f) o (and (isinstance x Iterable) (not (isinstance x #(str bytes)))) ((type x) (map replace x)) True x)) (.append defns `(defn ~(hy.models.Symbol (+ "test_operator_" o "_real")) [] (setv f-name ~(hy.models.String o)) ~@(replace body))) (.append defns `(defn ~(hy.models.Symbol (+ "test_operator_" o "_shadow")) [] (setv f-name ~(hy.models.String o)) (setv f ~o) ~@body))) `(do ~@defns)) (defmacro forbid [expr] (setv e (hy.gensym)) `(do (with [~e (pytest.raises Exception)] (hy.eval '~expr)) (assert (issubclass (. ~e type) #(TypeError SyntaxError hy.errors.HyMacroExpansionError))))) (op-and-shadow-test + (assert (= (f) 0)) (defclass C [object] (defn __pos__ [self] "called __pos__")) (assert (= (f (C)) "called __pos__")) (assert (= (f 1 2) 3)) (assert (= (f 1 2 3 4) 10)) (assert (= (f 1 2 3 4 5) 15)) (assert (= (f #* [1 2 3 4 5]) 15)) ; with strings (assert (= (f "a" "b" "c") "abc")) ; with lists (assert (= (f ["a"] ["b"] ["c"]) ["a" "b" "c"]))) (op-and-shadow-test - (forbid (f)) (assert (= (f 1) -1)) (assert (= (f 2 1) 1)) (assert (= (f 2 1 1) 0))) (op-and-shadow-test * (assert (= (f) 1)) (assert (= (f 3) 3)) (assert (= (f 3 3) 9)) (assert (= (f 2 3 4) 24)) (assert (= (f "ke" 4) "kekekeke")) (assert (= (f [1 2 3] 2) [1 2 3 1 2 3]))) (op-and-shadow-test ** (forbid (f)) (forbid (f 1)) (assert (= (f 3 2) 9)) (assert (= (f 5 4 3 2) (** 5 (** 4 (** 3 2)))))) ; Exponentiation is right-associative. (op-and-shadow-test / (forbid (f)) (assert (= (f 2) .5)) (assert (= (f 3 2) 1.5)) (assert (= (f 8 2) 4)) (assert (= (f 8 2 2) 2)) (assert (= (f 8 2 2 2) 1))) (op-and-shadow-test // (forbid (f)) (forbid (f 1)) (assert (= (f 16 5) 3)) (assert (= (f 8 2) 4)) (assert (= (f 8 2 2) 2))) (op-and-shadow-test % (forbid (f)) (forbid (f 1)) (assert (= (f 16 5) 1)) (assert (= (f 8 2) 0)) (assert (= (f "aa %s bb" 15) "aa 15 bb")) (assert (= (f "aa %s bb %s cc" #("X" "Y")) "aa X bb Y cc")) (forbid (f 1 2 3))) (op-and-shadow-test @ (defclass C [object] (defn __init__ [self content] (setv self.content content)) (defn __matmul__ [self other] (C (+ self.content other.content)))) (forbid (f)) (assert (do (setv c (C "a")) (is (f c) c))) (assert (= (. (f (C "b") (C "c")) content) "bc")) (assert (= (. (f (C "d") (C "e") (C "f")) content) "def"))) (op-and-shadow-test << (forbid (f)) (forbid (f 1)) (assert (= (f 0b101 2) 0b10100)) (assert (= (f 0b101 2 3) 0b10100000))) (op-and-shadow-test >> (forbid (f)) (forbid (f 1)) (assert (= (f 0b101 2) 0b1)) (assert (= (f 0b101000010 2 3) 0b1010))) (op-and-shadow-test & (forbid (f)) ; Binary AND has no identity element for the set of all ; nonnegative integers, because we can't have a 1 in every bit ; when there are infinitely many bits. (assert (= (f 17) 17)) (assert (= (f 0b0011 0b0101) 0b0001)) (assert (= (f 0b111 0b110 0b100) 0b100))) (op-and-shadow-test | (assert (= (f) 0)) (assert (= (f 17) 17)) (assert (= (f 0b0011 0b0101) 0b0111)) (assert (= (f 0b11100 0b11000 0b10010) 0b11110))) (op-and-shadow-test ^ (forbid (f)) (forbid (f 17)) (assert (= (f 0b0011 0b0101) 0b0110)) (forbid (f 0b111 0b110 0b100))) ; `xor` with 3 arguments is kill (https://github.com/hylang/hy/pull/1102), ; so we don't allow `^` with 3 arguments, either. (op-and-shadow-test bnot (forbid (f)) (assert (= (& (f 0b00101111) 0xFF) 0b11010000)) (forbid (f 0b00101111 0b11010000))) (op-and-shadow-test < (forbid (f)) (assert (is (f "hello") True)) (assert (is (f 1 2) True)) (assert (is (f 2 1) False)) (assert (is (f 1 1) False)) (assert (is (f 1 2 3) True)) (assert (is (f 3 2 1) False)) (assert (is (f 1 3 2) False)) (assert (is (f 1 2 2) False)) ; Make sure chained comparisons use `and`, not `&`. ; https://github.com/hylang/hy/issues/1191 (defclass C [object] (defn __init__ [self x] (setv self.x x)) (defn __lt__ [self other] self.x)) (assert (= (f (C "a") (C "b") (C "c")) "b"))) (op-and-shadow-test > (forbid (f)) (assert (is (f "hello") True)) (assert (is (f 1 2) False)) (assert (is (f 2 1) True)) (assert (is (f 1 1) False)) (assert (is (f 1 2 3) False)) (assert (is (f 3 2 1) True)) (assert (is (f 1 3 2) False)) (assert (is (f 2 1 1) False))) (op-and-shadow-test <= (forbid (f)) (assert (is (f "hello") True)) (assert (is (f 1 2) True)) (assert (is (f 2 1) False)) (assert (is (f 1 1) True)) (assert (is (f 1 2 3) True)) (assert (is (f 3 2 1) False)) (assert (is (f 1 3 2) False)) (assert (is (f 1 2 2) True))) (op-and-shadow-test >= (forbid (f)) (assert (is (f "hello") True)) (assert (is (f 1 2) False)) (assert (is (f 2 1) True)) (assert (is (f 1 1) True)) (assert (is (f 1 2 3) False)) (assert (is (f 3 2 1) True)) (assert (is (f 1 3 2) False)) (assert (is (f 2 1 1) True))) (op-and-shadow-test [= is] (forbid (f)) (assert (is (f "hello") True)) ; Unary comparison operators, despite always returning True, ; should evaluate their argument. (setv p "a") (assert (is (f (do (setv p "b") "hello")) True)) (assert (= p "b")) (defclass C) (setv x (get {"is" (C) "=" 0} f-name)) (setv y (get {"is" (C) "=" 1} f-name)) (assert (is (f x x) True)) (assert (is (f y y) True)) (assert (is (f x y) False)) (assert (is (f y x) False)) (assert (is (f x x x x x) True)) (assert (is (f x x x y x) False)) (setv n None) (assert (is (f n None) True)) (assert (is (f n "b") False))) (op-and-shadow-test [!= is-not] (forbid (f)) (forbid (f "hello")) (defclass C) (setv x (get {"is-not" (C) "!=" 0} f-name)) (setv y (get {"is-not" (C) "!=" 1} f-name)) (setv z (get {"is-not" (C) "!=" 2} f-name)) (assert (is (f x x) False)) (assert (is (f y y) False)) (assert (is (f x y) True)) (assert (is (f y x) True)) (assert (is (f x y z) True)) (assert (is (f x x x) False)) (assert (is (f x y x) True)) (assert (is (f x x y) False))) (op-and-shadow-test and (assert (is (f) True)) (assert (= (f 17) 17)) (assert (= (f 1 2) 2)) (assert (= (f 1 0) 0)) (assert (= (f 0 2) 0)) (assert (= (f 0 0) 0)) (assert (= (f 1 2 3) 3)) (assert (= (f 1 0 3) 0)) (assert (= (f "a" 1 True [1]) [1])) (assert (= (f #* [1 2 3]) 3))) (op-and-shadow-test or (assert (is (f) None)) (assert (= (f 17) 17)) (assert (= (f 1 2) 1)) (assert (= (f 1 0) 1)) (assert (= (f 0 2) 2)) (assert (= (f 0 0) 0)) (assert (= (f 1 2 3) 1)) (assert (= (f 0 0 3) 3)) (assert (= (f "" None 0 False []) []))) (op-and-shadow-test not (forbid (f)) (assert (is (f "hello") False)) (assert (is (f 0) True)) (assert (is (f None) True))) (op-and-shadow-test [in not-in] (forbid (f)) (forbid (f 3)) (assert (is (f 3 [1 2]) (!= f-name "in"))) (assert (is (f 2 [1 2]) (= f-name "in"))) (assert (is (f 2 [1 2] [[1 2] 3]) (= f-name "in"))) (assert (is (f 3 [1 2] [[2 2] 3]) (!= f-name "in")))) (op-and-shadow-test [get] (forbid (f)) (forbid (f "hello")) (assert (= (f "hello" 1) "e")) (assert (= (f [[1 2 3] [4 5 6] [7 8 9]] 1 2) 6)) (assert (= (f {"x" {"y" {"z" 12}}} "x" "y" "z") 12))) (defn test-setv-get [] (setv foo [0 1 2]) (setv (get foo 0) 12) (assert (= (get foo 0) 12))) (op-and-shadow-test [cut] (forbid (f)) (setv x "abcdef") (assert (= (f x) "abcdef")) (assert (= (f x 3) "abc")) (assert (= (f x -2) "abcd")) (assert (= (f x 3 None) "def")) (assert (= (f x -2 None) "ef")) (assert (= (f x 3 5) "de")) (assert (= (f x 0 None 2) "ace")) (assert (= (list (f (range 100) 20 80 13)) [20 33 46 59 72]))) (defn test-setv-cut [] (setv foo (list (range 20))) (setv (cut foo 2 18 3) (* [0] 6)) (assert (= foo [0 1 0 3 4 0 6 7 0 9 10 0 12 13 0 15 16 0 18 19]))) (defn test-chained-comparison [] (assert (chainc 2 = (+ 1 1) = (- 3 1))) (assert (not (chainc 2 = (+ 1 1) = (+ 3 1)))) (assert (chainc 2 = 2 > 1)) (assert (chainc 2 = (+ 1 1) > 1)) (setv x 2) (assert (chainc 2 = x > 1)) (assert (chainc 2 = x > (> 4 3))) (assert (not (chainc (> 4 3) = x > 1))) (assert (chainc 1 in [1] in [[1] [2 3]] not-in [5])) (assert (not (chainc 1 in [1] not-in [[1] [2 3]] not-in [5])))) (defn test-augassign [] (setv b 2 c 3 d 4) (defmacro same-as [expr1 expr2 expected-value] `(do (setv a 4) ~expr1 (setv expr1-value a) (setv a 4) ~expr2 (assert (= expr1-value a ~expected-value)))) (same-as (+= a b c d) (+= a (+ b c d)) 13) (same-as (-= a b c d) (-= a (+ b c d)) -5) (same-as (*= a b c d) (*= a (* b c d)) 96) (same-as (**= a b c) (**= a (** b c)) 65,536) (same-as (/= a b c d) (/= a (* b c d)) (/ 1 6)) (same-as (//= a b c d) (//= a (* b c d)) 0) (same-as (<<= a b c d) (<<= a (+ b c d)) 0b10_00000_00000) (same-as (>>= a b c d) (>>= a (+ b c d)) 0) (same-as (&= a b c d) (&= a (& b c d)) 0) (same-as (|= a b c d) (|= a (| b c d)) 0b111) (defclass C [object] (defn __init__ [self content] (setv self.content content)) (defn __matmul__ [self other] (C (+ self.content other.content)))) (setv a (C "a") b (C "b") c (C "c") d (C "d")) (@= a b c d) (assert (= a.content "abcd")) (setv a (C "a")) (@= a (@ b c d)) (assert (= a.content "abcd")) (setv a 15) (%= a 9) (assert (= a 6)) (setv a 0b1100) (^= a 0b1010) (assert (= a 0b0110))) hy-1.0.0/tests/native_tests/other.hy000066400000000000000000000047511467401276000175030ustar00rootroot00000000000000;; Tests miscellaneous features of the language not covered elsewhere (import textwrap [dedent] typing [get-type-hints] importlib pydoc pytest hy.errors [HyLanguageError]) (defn test-illegal-assignments [] (for [form '[ (setv (do 1 2) 1) (setv 1 1) (setv {1 2} 1) (del 1 1) ; https://github.com/hylang/hy/issues/1780 (setv None 1) (setv False 1) (setv True 1) (defn None [] (print "hello")) (defn True [] (print "hello")) (defn f [True] (print "hello")) (for [True [1 2 3]] (print "hello")) (lfor True [1 2 3] True) (lfor :setv True 1 True) (with [True x] (print "hello")) (try 1 (except [True AssertionError] 2)) (defclass True [])]] (with [e (pytest.raises HyLanguageError)] (hy.eval form)) (assert (in "Can't assign" e.value.msg)))) (defn test-no-str-as-sym [] "Don't treat strings as symbols in the calling position" (with [(pytest.raises TypeError)] ("setv" True 3)) ; A special form (with [(pytest.raises TypeError)] ("abs" -2)) ; A function (with [(pytest.raises TypeError)] ("when" 1 2))) ; A macro (defn test-undefined-name [] (with [(pytest.raises NameError)] xxx)) (defn test-variable-annotations [] (defclass AnnotationContainer [] (setv #^ int x 1 y 2) (#^ bool z)) (setv annotations (get-type-hints AnnotationContainer)) (assert (= (get annotations "x") int)) (assert (= (get annotations "z") bool))) (defn test-pydoc [tmp-path monkeypatch] ; https://github.com/hylang/hy/issues/2578 (monkeypatch.syspath-prepend tmp-path) (.write-text (/ tmp-path "pydoc_py.py") (dedent #[[ class C1: 'C1 docstring' # a comment class C2: pass]])) (importlib.invalidate-caches) (import pydoc-py) (assert (= (pydoc.getdoc pydoc-py.C1) "C1 docstring")) (assert (= (pydoc.getdoc pydoc-py.C2) "# a comment")) (.write-text (/ tmp-path "pydoc_hy.hy") (dedent #[[ (defclass C1 [] "C1 docstring") ; a comment (defclass C2)]])) (importlib.invalidate-caches) (import pydoc-hy) (assert (= (pydoc.getdoc pydoc-hy.C1) "C1 docstring")) (assert (= (pydoc.getdoc pydoc-hy.C2) ""))) ; `pydoc` shouldn't try to get a comment from Hy code, since it ; doesn't know how to parse Hy. (defn test-help-class-attr [] "Our tampering with `pydoc` shouldn't cause `help` to raise `TypeError` on classes with non-method attributes." (defclass C [] (setv attribute 1)) (help C)) hy-1.0.0/tests/native_tests/quote.hy000066400000000000000000000102061467401276000175070ustar00rootroot00000000000000"Tests of `quote` and `quasiquote`." (import pytest) (setv E hy.models.Expression) (setv S hy.models.Symbol) (defn test-quote-basic [] (assert (= '3 (hy.models.Integer 3))) (assert (= 'a (S "a"))) (assert (= 'False (S "False"))) (assert (= '"hello" (hy.models.String "hello"))) (assert (= 'b"hello" (hy.models.Bytes b"hello"))) (assert (= '(a b) (E [(S "a") (S "b")]))) (assert (= '{foo bar baz quux} (hy.models.Dict (map S ["foo" "bar" "baz" "quux"]))))) (defn test-quoted-hoistable [] (setv f '(if True True True)) (assert (= (get f 0) 'if)) (assert (= (cut f 1 None) '(True True True)))) (defn test-quasiquote-trivial [] "Quasiquote and quote are equivalent for simple cases." (assert (= `(a b c) '(a b c)))) (defn test-quoted-macroexpand [] "Don't expand macros in quoted expressions." (require tests.resources.macros [test-macro]) (setv q1 '(test-macro)) (setv q2 `(test-macro)) (assert (= q1 q2)) (assert (= (get q1 0) 'test-macro))) (defn test-quote-expr-in-dict [] (assert (= '{(foo bar) 0} (hy.models.Dict [(E [(S "foo") (S "bar")]) (hy.models.Integer 0)])))) (defn test-unquote [] (setv q '~foo) (assert (= (len q) 2)) (assert (= (get q 1) 'foo)) (setv qq `(a b c ~(+ 1 2))) (assert (= (hy.as-model qq) '(a b c 3)))) (defn test-unquote-splice [] (setv q '(c d e)) (setv qq `(a b ~@q f ~@q ~@0 ~@False ~@None g ~@(when False 1) h)) (assert (= qq '(a b c d e f c d e g h)))) (defmacro doodle [#* args] `[1 ~@args 2]) (defn test-unquote-splice-in-mac [] (assert (= (doodle (setv x 5) (+= x 1) x) [1 None None 6 2]))) (defn test-unquote-splice-unpack [] ; https://github.com/hylang/hy/issues/2336 (with [(pytest.raises hy.errors.HySyntaxError)] (hy.eval '`[~@ #* [[1]]]))) (defn test-nested-quasiquote [] (setv qq (hy.as-model `(1 `~(+ 1 ~(+ 2 3) ~@None) 4))) (setv q '(1 `~(+ 1 5) 4)) (assert (= (len q) 3)) (assert (= (get qq 1) '`~(+ 1 5))) (assert (= qq q))) (defn test-nested-quasiquote--nested-struct [] (assert (= (hy.as-model `(try ~@(lfor i [1 2 3] `(setv ~(S (+ "x" (str i))) (+ "x" (str ~i)))) (finally (print "done")))) '(try (setv x1 (+ "x" (str 1))) (setv x2 (+ "x" (str 2))) (setv x3 (+ "x" (str 3))) (finally (print "done")))))) (defmacro macroify-programs [#* names] `(do ~@(lfor name names `(defmacro ~name [#* args] `(.append imaginary-syscalls #(~'~(str name) ~@(map str args))))))) (defn test-nested-quasiquote--macro-writing-macro-1 [] "A test of the construct ~'~ (to substitute in a variable from a higher-level quasiquote) inspired by https://github.com/hylang/hy/discussions/2251" (setv imaginary-syscalls []) (macroify-programs ls mkdir touch) (mkdir mynewdir) (touch mynewdir/file) (ls -lA mynewdir) (assert (= imaginary-syscalls [ #("mkdir" "mynewdir") #("touch" "mynewdir/file") #("ls" "-lA" "mynewdir")]))) (defmacro def-caller [abbrev proc] `(defmacro ~abbrev [var form] `(~'~proc (fn [~var] ~form)))) (def-caller smoo-caller smoo) (defn test-nested-quasiquote--macro-writing-macro-2 [] "A similar test to the previous one, based on section 3.2 of Bawden, A. (1999). Quasiquotation in Lisp. ACM SIGPLAN Workshop on Partial Evaluation and Program Manipulation. Retrieved from http://web.archive.org/web/20230105083805id_/https://3e8.org/pub/scheme/doc/Quasiquotation%20in%20Lisp%20(Bawden).pdf" (setv accum []) (defn smoo [f] (.append accum "entered smoo") (f "in smoo") (.append accum "exiting smoo")) (smoo-caller arg (.append accum (+ "in the lambda: " arg))) (assert (= accum [ "entered smoo" "in the lambda: in smoo" "exiting smoo"]))) (defn test-nested-quasiquote--triple [] "N.B. You can get the same results with an analogous test in Emacs Lisp or Common Lisp." (setv a 1 b 1 c 1 x ```[~a ~~b ~~~c] ; `x` has been implicitly evaluated once. Let's explicitly ; evaluate it twice more, so no backticks are left. a 2 b 2 c 2 x (hy.eval x) a 3 b 3 c 3 x (hy.eval x)) (assert (= (hy.as-model x) '[3 2 1]))) hy-1.0.0/tests/native_tests/reader_macros.hy000066400000000000000000000125161467401276000211660ustar00rootroot00000000000000(import itertools sys types contextlib [contextmanager] hy.errors [HyMacroExpansionError] pytest) (defn [contextmanager] temp-module [module-name] (let [module (types.ModuleType module-name) old-module (sys.modules.get module-name)] (setv (get sys.modules module-name) module) (try (yield module) (finally (if old-module (setv (get sys.modules module-name) module) (sys.modules.pop module-name)))))) (defn eval-isolated [tree [module None]] (if module (hy.eval tree :locals {} :module module) (with [module (temp-module "")] (hy.eval tree :locals {} :module module)))) (defn eval-module [s] (eval-isolated (hy.read-many s))) (defn test-reader-macros [] (assert (= (eval-module #[[(defreader foo '1) #foo]]) 1)) (assert (in "foo" (eval-module #[[(defreader foo '1) _hy_reader_macros]]))) (assert (= (eval-module #[[(defreader ^foo '1) #^foo]]) 1)) (assert (not-in "rm___x" (eval-module #[[(defreader rm---x '1) _hy_reader_macros]]))) ;; Assert reader macros operating exclusively at read time (with [module (temp-module "")] (setv it (hy.read-many #[reader[ (defreader lower (hy.models.String (.lower (&reader.parse-one-form)))) #lower "HeLLO, WoRLd!" ]reader])) (eval-isolated (next it) module) (assert (= (next it) '"hello, world!")))) (defn test-bad-reader-macro-name [] (with [(pytest.raises HyMacroExpansionError)] (eval-module "(defreader :a-key '1)")) (with [(pytest.raises hy.PrematureEndOfInput)] (eval-module "# _ 3"))) (defn test-get-macro [] (assert (eval-module #[[ (defreader rm1 11) (defreader rm☘ 22) (and (is (get-macro :reader rm1) (get _hy_reader_macros "rm1")) (is (get-macro :reader rm☘) (get _hy_reader_macros "rm☘")))]]))) (defn test-docstring [] (assert (= (eval-module #[[ (defreader foo "docstring of foo" 15) #(#foo (. (get-macro :reader foo) __doc__))]]) #(15 "docstring of foo")))) (defn test-require-readers [] (with [module (temp-module "")] (setv it (hy.read-many #[[(require tests.resources.tlib :readers [upper!]) #upper! hello]])) (eval-isolated (next it) module) (assert (= (next it) 'HELLO))) ;; test require :readers & :macros is order independent (for [s ["[qplah] :readers [upper!]" ":readers [upper!] [qplah]" ":macros [qplah] :readers [upper!]" ":readers [upper!] :macros [qplah]"]] (assert (= (eval-module #[f[ (require tests.resources.tlib {s}) [(qplah 1) #upper! "hello"]]f]) [[8 1] "HELLO"]))) ;; test require :readers * (assert (= (eval-module #[=[ (require tests.resources.tlib :readers *) [#upper! "eVeRy" #lower "ReAdEr"]]=]) ["EVERY" "reader"])) ;; test can't redefine :macros or :readers assignment brackets (with [(pytest.raises hy.errors.HySyntaxError)] (eval-module #[[(require tests.resources.tlib [taggart] [upper!])]])) (with [(pytest.raises hy.errors.HySyntaxError)] (eval-module #[[(require tests.resources.tlib :readers [taggart] :readers [upper!])]])) (with [(pytest.raises hy.errors.HyRequireError)] (eval-module #[[(require tests.resources.tlib :readers [not-a-real-reader])]]))) (defn test-eval-read [] ;; https://github.com/hylang/hy/issues/2291 ;; hy.eval should not raise an exception when ;; defining readers using hy.read or with quoted forms (with [module (temp-module "")] (hy.eval (hy.read "(defreader r 5)") :module module) (hy.eval '(defreader test-read 4) :module module) (hy.eval '(require tests.resources.tlib :readers [upper!]) :module module) ;; these reader macros should not exist in any current reader (for [tag #("#r" "#test-read" "#upper!")] (with [(pytest.raises hy.errors.HySyntaxError)] (hy.read tag))) ;; but they should be installed in the module (hy.eval '(setv reader (hy.HyReader :use-current-readers True)) :module module) (setv reader module.reader) (for [[s val] [["#r" 5] ["#test-read" 4] ["#upper! \"hi there\"" "HI THERE"]]] (assert (= (hy.eval (hy.read s :reader reader) :module module) val)))) ;; passing a reader explicitly should work as expected (with [module (temp-module "")] (setv reader (hy.HyReader)) (defn eval1 [s] (hy.eval (hy.read s :reader reader) :module module)) (eval1 "(defreader fbaz 32)") (eval1 "(require tests.resources.tlib :readers [upper!])") (assert (= (eval1 "#fbaz") 32)) (assert (= (eval1 "#upper! \"hello\"") "HELLO")))) (defn test-interleaving-readers [] (with [module1 (temp-module "") module2 (temp-module "")] (setv stream1 (hy.read-many #[[(do (defreader foo "foo1") (defreader bar "bar1")) #foo #bar]]) stream2 (hy.read-many #[[(do (defreader foo "foo2") (defreader bar "bar2")) #foo #bar]]) valss [[None None] ["foo1" "foo2"] ["bar1" "bar2"]]) (for [[form1 form2 vals] (zip stream1 stream2 valss)] (assert (= vals [(hy.eval form1 :module module1) (hy.eval form2 :module module2)]))))) hy-1.0.0/tests/native_tests/repl.hy000066400000000000000000000126021467401276000173160ustar00rootroot00000000000000; Some other tests of the REPL are in `test_bin.py`. (import io sys re pytest) (defn [pytest.fixture] rt [monkeypatch capsys] "Do a test run of the REPL." (fn [[inp ""] [to-return 'out] [spy False] [py-repr False]] (monkeypatch.setattr "sys.stdin" (io.StringIO inp)) (.run (hy.REPL :spy spy :output-fn (when py-repr repr))) (setv result (capsys.readouterr)) (cond (= to-return 'out) result.out (= to-return 'err) result.err (= to-return 'both) result))) (defmacro has [haystack needle] "`in` backwards." `(in ~needle ~haystack)) (defn test-simple [rt] (assert (has (rt #[[(.upper "hello")]]) "HELLO"))) (defn test-spy [rt] (setv x (rt #[[(.upper "hello")]] :spy True)) (assert (has x ".upper()")) (assert (has x "HELLO")) ; `spy` should work even when an exception is thrown (assert (has (rt "(foof)" :spy True) "foof()"))) (defn test-multiline [rt] (assert (has (rt "(+ 1 3\n5 9)") " 18\n=> "))) (defn test-history [rt] (assert (has (rt #[[ (+ "a" "b") (+ "c" "d") (+ "e" "f") (.format "*1: {}, *2: {}, *3: {}," *1 *2 *3)]]) #[["*1: ef, *2: cd, *3: ab,"]])) (assert (has (rt #[[ (raise (Exception "TEST ERROR")) (+ "err: " (str *e))]]) #[["err: TEST ERROR"]]))) (defn test-comments [rt] (setv err-empty (rt "" 'err)) (setv x (rt #[[(+ "a" "b") ; "c"]] 'both)) (assert (has x.out "ab")) (assert (= x.err err-empty)) (assert (= (rt "; 1" 'err) err-empty))) (defn test-assignment [rt] "If the last form is an assignment, don't print the value." (assert (not (has (rt #[[(setv x (+ "A" "Z"))]]) "AZ"))) (setv x (rt #[[(setv x (+ "A" "Z")) (+ "B" "Y")]])) (assert (has x "BY")) (assert (not (has x "AZ"))) (setv x (rt #[[(+ "B" "Y") (setv x (+ "A" "Z"))]])) (assert (not (has x "BY"))) (assert (not (has x "AZ")))) (defn test-multi-setv [rt] ; https://github.com/hylang/hy/issues/1255 (assert (re.match r"=>\s+2\s+=>" (rt (.replace "(do (setv it 0 it (+ it 1) it (+ it 1)) it)" "\n" " "))))) (defn test-error-underline-alignment [rt] (setv err (rt "(defmacro mabcdefghi [x] x)\n(mabcdefghi)" 'err)) (setv msg-idx (.rindex err " (mabcdefghi)")) (setv [_ e1 e2 e3 #* _] (.splitlines (cut err msg_idx None))) (assert (.startswith e1 (hy.compat.reu " ^----------^"))) (assert (.startswith e2 "expanding macro mabcdefghi")) (assert (or ; PyPy can use a function's `__name__` instead of ; `__code__.co_name`. (.startswith e3 " TypeError: mabcdefghi") (.startswith e3 " TypeError: (mabcdefghi)")))) (defn test-except-do [rt] ; https://github.com/hylang/hy/issues/533 (assert (has (rt #[[(try (/ 1 0) (except [ZeroDivisionError] "hello"))]]) "hello")) (setv x (rt #[[(try (/ 1 0) (except [ZeroDivisionError] "aaa" "bbb" "ccc"))]])) (assert (not (has x "aaa"))) (assert (not (has x "bbb"))) (assert (has x "ccc")) (setv x (rt #[[(when True "xxx" "yyy" "zzz")]])) (assert (not (has x "xxx"))) (assert (not (has x "yyy"))) (assert (has x "zzz"))) (defn test-unlocatable-hytypeerror [rt] ; https://github.com/hylang/hy/issues/1412 ; The chief test of interest here is that the REPL isn't itself ; throwing an error. (assert (has (rt :to-return 'err #[[ (import hy.errors) (raise (hy.errors.HyTypeError (+ "A" "Z") None '[] None))]]) "AZ"))) (defn test-syntax-errors [rt] ; https://github.com/hylang/hy/issues/2004 (assert (has (rt "(defn foo [/])\n(defn bar [a a])" 'err) "SyntaxError: duplicate argument")) ; https://github.com/hylang/hy/issues/2014 (setv err (rt "(defn foo []\n(import re *))" 'err)) (assert (has err "SyntaxError: import * only allowed")) (assert (not (has err "PrematureEndOfInput")))) (defn test-bad-repr [rt] ; https://github.com/hylang/hy/issues/1389 (setv x (rt :to-return 'both #[[ (defclass BadRepr [] (defn __repr__ [self] (/ 0))) (BadRepr) (+ "A" "Z")]])) (assert (has x.err "ZeroDivisionError")) (assert (has x.out "AZ"))) (defn test-py-repr [rt] (assert (has (rt "(+ [1] [2])") "[1 2]")) (assert (has (rt "(+ [1] [2])" :py-repr True) "[1, 2]")) (setv x (rt "(+ [1] [2])" :py-repr True :spy True)) (assert (has x "[1] + [2]")) (assert (has x "[1, 2]")) ; --spy should work even when an exception is thrown (assert (has (rt "(+ [1] [2] (foof))" :py-repr True :spy True) "[1] + [2]"))) (defn test-builtins [rt] (assert (has (rt "quit") "Use (quit) or Ctrl-D (i.e. EOF) to exit")) (assert (has (rt "exit") "Use (exit) or Ctrl-D (i.e. EOF) to exit")) (assert (has (rt "help") "Use (help) for interactive help, or (help object) for help about object.")) ; The old values of these objects come back after the REPL ends. (assert (.startswith (str quit) "Use quit() or"))) (defn test-preserve-ps1 [rt] ; https://github.com/hylang/hy/issues/1323#issuecomment-1310837340 (setv sys.ps1 "chippy") (assert (= sys.ps1 "chippy")) (rt "(+ 1 1)") (assert (= sys.ps1 "chippy"))) (defn test-input-1char [rt] ; https://github.com/hylang/hy/issues/2430 (assert (= (rt "1\n") "=> 1\n=> "))) (defn test-no-shebangs-allowed [rt] (assert (has (rt "#!/usr/bin/env hy\n" 'err) "hy.reader.exceptions.LexException"))) (defn test-pass [rt] ; https://github.com/hylang/hy/issues/2601 (assert (has (rt "pass\n" 'err) "NameError"))) hy-1.0.0/tests/native_tests/setv.hy000066400000000000000000000054411467401276000173400ustar00rootroot00000000000000(import unittest.mock [Mock] pytest) (defn test-setv [] (setv x 1) (setv y 1) (assert (= x y)) (setv y 12) (setv x y) (assert (= x 12)) (assert (= y 12)) (setv y (fn [x] 9)) (setv x y) (assert (= (x y) 9)) (assert (= (y x) 9)) (try (do (setv a.b 1) (assert False)) (except [e [NameError]] (assert (in "name 'a' is not defined" (str e))))) (try (do (setv b.a (fn [x] x)) (assert False)) (except [e [NameError]] (assert (in "name 'b' is not defined" (str e))))) (import itertools) (setv foopermutations (fn [x] (itertools.permutations x))) (setv p (set [#(1 3 2) #(3 2 1) #(2 1 3) #(3 1 2) #(1 2 3) #(2 3 1)])) (assert (= (set (itertools.permutations [1 2 3])) p)) (assert (= (set (foopermutations [3 1 2])) p)) (setv permutations- itertools.permutations) (setv itertools.permutations (fn [x] 9)) (assert (= (itertools.permutations p) 9)) (assert (= (foopermutations foopermutations) 9)) (setv itertools.permutations permutations-) (assert (= (set (itertools.permutations [2 1 3])) p)) (assert (= (set (foopermutations [2 3 1])) p))) (defn test-setv-pairs [] (setv a 1 b 2) (assert (= a 1)) (assert (= b 2)) (setv y 0 x 1 y x) (assert (= y 1)) (with [(pytest.raises hy.errors.HyLanguageError)] (hy.eval '(setv a 1 b)))) (defn test-setv-pairs-eval-order [] "Each assignment pair should fully resolve before anything in the next is evaluated, even when statements need to be pulled out." (setv m (Mock)) (setv l (* [None] 5)) (setv (get l 0) m.call-count (get l 1) (do (m) m.call-count) (get l 2) m.call-count (get l 3) (do (m) m.call-count) (get l 4) m.call-count) (assert (= l [0 1 1 2 2]))) (defn test-setv-returns-none [] (defn an [x] (assert (is x None))) (an (setv)) (an (setv x 1)) (assert (= x 1)) (an (setv x 2)) (assert (= x 2)) (an (setv y 2 z 3)) (assert (= y 2)) (assert (= z 3)) (an (setv [y z] [7 8])) (assert (= y 7)) (assert (= z 8)) (an (setv #(y z) [9 10])) (assert (= y 9)) (assert (= z 10)) (setv p 11) (setv p (setv q 12)) (assert (= q 12)) (an p) (an (setv x (defn phooey [] (setv p 1) (+ p 6)))) (an (setv x (defclass C))) (an (setv x (for [i (range 3)] i (+ i 1)))) (an (setv x (assert True))) (an (setv x (with [(open "tests/resources/text.txt" "r")] 3))) (assert (= x 3)) (an (setv x (try (/ 1 2) (except [ZeroDivisionError] "E1")))) (assert (= x .5)) (an (setv x (try (/ 1 0) (except [ZeroDivisionError] "E2")))) (assert (= x "E2")) ; https://github.com/hylang/hy/issues/1052 (an (setv (get {} "x") 42)) (setv l []) (defclass Foo [object] (defn __setattr__ [self attr val] (.append l [attr val]))) (setv x (Foo)) (an (setv x.eggs "ham")) (assert (not (hasattr x "eggs"))) (assert (= l [["eggs" "ham"]]))) hy-1.0.0/tests/native_tests/setx.hy000066400000000000000000000022231467401276000173350ustar00rootroot00000000000000(import pytest) (defn test-setx [] (setx y (+ (setx x (+ "a" "b")) "c")) (assert (= x "ab")) (assert (= y "abc")) (setv l []) (for [x [1 2 3]] (when (>= (setx y (+ x 8)) 10) (.append l y))) (assert (= l [10 11])) (setv a ["apple" None "banana"]) (setv filtered (lfor i (range (len a)) :if (is-not (setx v (get a i)) None) v)) (assert (= filtered ["apple" "banana"])) (assert (= v "banana")) (with [(pytest.raises NameError)] i)) (defn test-setx-generator-scope [] ;; https://github.com/hylang/hy/issues/1994 (setv x 20) (lfor n (range 10) (setx x n)) (assert (= x 9)) (setv x 20) (lfor n (range 10) :do x (setx x n)) ; force making a function (assert (= x 9)) (lfor n (range 10) :do x (setx y n)) (assert (= y 9)) (lfor n (range 0) :do x (setx y n)) (with [(pytest.raises NameError)] n) (lfor n (range 0) :setv t (+ n 2) (setx y n)) (with [(pytest.raises NameError)] t) (lfor n (range 0) :do x (setx z n)) (with [(pytest.raises UnboundLocalError)] z)) (defn test-let-setx [] (let [x 40 y 13] (setv y (setx x 2)) (assert (= x 2)) (assert (= y 2)))) hy-1.0.0/tests/native_tests/strings.hy000066400000000000000000000123211467401276000200430ustar00rootroot00000000000000;; String literals (including bracket strings and docstrings), ;; plus f-strings (import re pytest) (defn test-encoding-nightmares [] (assert (= (len "ℵℵℵ♥♥♥\t♥♥\r\n") 11))) (defn test-quote-bracket-string-delim [] (assert (= (. '#[my delim[hello world]my delim] brackets) "my delim")) (assert (= (. '#[[squid]] brackets) "")) (assert (is (. '"squid" brackets) None))) (defn test-docstrings [] (defn f [] "docstring" 5) (assert (= (. f __doc__) "docstring")) ; a single string is the return value, not a docstring ; (https://github.com/hylang/hy/issues/1402) (defn f3 [] "not a docstring") (assert (is (. f3 __doc__) None)) (assert (= (f3) "not a docstring"))) (defn test-module-docstring [] (import tests.resources.module-docstring-example :as m) (assert (= m.__doc__ "This is the module docstring.")) (assert (= m.foo 5))) (defn test-format-strings [] (assert (= f"hello world" "hello world")) (assert (= f"hello {(+ 1 1)} world" "hello 2 world")) (assert (= f"a{ (.upper (+ "g" "k")) }z" "aGKz")) (assert (= f"a{1}{2}b" "a12b")) ; Referring to a variable (setv p "xyzzy") (assert (= f"h{p}j" "hxyzzyj")) ; Including a statement and setting a variable (assert (= f"a{(do (setv floop 4) (* floop 2))}z" "a8z")) (assert (= floop 4)) ; Comments (assert (= f"a{(+ 1 2 ; This is a comment. 3)}z" "a6z")) ; Newlines in replacement fields (assert (= f"ey {"bee cee"} dee" "ey bee\ncee dee")) ; Conversion characters and format specifiers (setv p:9 "other") (setv !r "bar") (assert (= f"a{p !r}" "a'xyzzy'")) (assert (= f"a{p :9}" "axyzzy ")) (assert (= f"a{p:9}" "aother")) (assert (= f"a{p !r :9}" "a'xyzzy' ")) (assert (= f"a{p !r:9}" "a'xyzzy' ")) (assert (= f"a{p:9 :9}" "aother ")) (assert (= f"a{!r}" "abar")) (assert (= f"a{!r !r}" "a'bar'")) ; Fun with `r` (assert (= f"hello {r"\n"}" r"hello \n")) (assert (= f"hello {"\n"}" "hello \n")) ; Braces escaped via doubling (assert (= f"ab{{cde" "ab{cde")) (assert (= f"ab{{cde}}}}fg{{{{{{" "ab{cde}}fg{{{")) (assert (= f"ab{{{(+ 1 1)}}}" "ab{2}")) ; Nested replacement fields (assert (= f"{2 :{(+ 2 2)}}" " 2")) (setv value 12.34 width 10 precision 4) (assert (= f"result: {value :{width}.{precision}}" "result: 12.34")) ; Nested replacement fields with ! and : (defclass C [object] (defn __format__ [self format-spec] (+ "C[" format-spec "]"))) (assert (= f"{(C) : {(str (+ 1 1)) !r :x<5}}" "C[ '2'xx]")) ; \N sequences ; https://github.com/hylang/hy/issues/2321 (setv ampersand "wich") (assert (= f"sand{ampersand} \N{ampersand} chips" "sandwich & chips")) ; Format bracket strings (assert (= #[f[a{p !r :9}]f] "a'xyzzy' ")) (assert (= #[f-string[result: {value :{width}.{precision}}]f-string] "result: 12.34")) ; https://github.com/hylang/hy/issues/2419 (assert (= #[f[{{escaped braces}} \n {"not escaped"}]f] "{escaped braces} \\n not escaped")) ; https://github.com/hylang/hy/issues/2474 (assert (= #[f["{0}"]f] "\"0\"")) ; Quoting shouldn't evaluate the f-string immediately ; https://github.com/hylang/hy/issues/1844 (setv quoted 'f"hello {world}") (assert (isinstance quoted hy.models.FString)) (with [(pytest.raises NameError)] (hy.eval quoted)) (setv world "goodbye") (assert (= (hy.eval quoted) "hello goodbye")) ;; '=' debugging syntax. (setv foo "bar") (assert (= f"{foo =}" "foo ='bar'")) ;; Whitespace is preserved. (assert (= f"xyz{ foo = }" "xyz foo = 'bar'")) ;; Explicit conversion is applied. (assert (= f"{ foo = !s}" " foo = bar")) ;; Format spec supercedes implicit conversion. (setv pi 3.141593 fill "_") (assert (= f"{pi = :{fill}^8.2f}" "pi = __3.14__")) ;; Format spec doesn't clobber the explicit conversion. (with [(pytest.raises ValueError :match r"Unknown format code '?f'? for object of type 'str'")] f"{pi =!s:.3f}") ;; Nested "=" is parsed, but fails at runtime, like Python. (setv width 7) (with [(pytest.raises ValueError :match r"I|invalid format spec(?:ifier)?")] f"{pi =:{fill =}^{width =}.2f}")) (defn test-format-string-repr-roundtrip [] (for [orig [ 'f"hello {(+ 1 1)} world" 'f"a{p !r:9}" 'f"{ foo = !s}"]] (setv new (eval (repr orig))) (assert (= (len new) (len orig))) (for [[n o] (zip new orig)] (when (hasattr o "conversion") (assert (= n.conversion o.conversion))) (assert (= n o))))) (defn test-repr-with-brackets [] (assert (= (repr '"foo") "hy.models.String('foo')")) (assert (= (repr '#[[foo]]) "hy.models.String('foo', brackets='')")) (assert (= (repr '#[xx[foo]xx]) "hy.models.String('foo', brackets='xx')")) (assert (= (repr '#[xx[]xx]) "hy.models.String('', brackets='xx')")) (for [g [repr str]] (defn f [x] (re.sub r"\n\s+" "" (g x) :count 1)) (assert (= (f 'f"foo") "hy.models.FString([hy.models.String('foo')])")) (assert (= (f '#[f[foo]f]) "hy.models.FString([hy.models.String('foo')], brackets='f')")) (assert (= (f '#[f-x[foo]f-x]) "hy.models.FString([hy.models.String('foo')], brackets='f-x')")) (assert (= (f '#[f-x[]f-x]) "hy.models.FString(brackets='f-x')")))) hy-1.0.0/tests/native_tests/try.hy000066400000000000000000000106541467401276000171770ustar00rootroot00000000000000;; Tests of `try` and `raise` (import pytest) (defn test-try-missing-parts [] (assert (is (try) None)) (assert (= (try 1) 1)) (assert (is (try (except [])) None)) (assert (is (try (finally)) None)) (assert (= (try 1 (finally 2)) 1)) (assert (is (try (else)) None)) (assert (= (try 1 (else 2)) 2))) (defn test-try-multiple-statements [] (setv value 0) (try (+= value 1) (+= value 2) (except [IOError]) (except [])) (assert (= value 3))) (defn test-try-multiple-expressions [] ; https://github.com/hylang/hy/issues/1584 (setv l []) (defn f [] (.append l 1)) (try (f) (f) (f) (except [IOError])) (assert (= l [1 1 1])) (setv l []) (try (f) (f) (f) (except [IOError]) (else (f))) (assert (= l [1 1 1 1]))) (defn test-raise-nullary [] ;; Test correct (raise) (setv passed False) (try (try (do) (raise IndexError) (except [IndexError] (raise))) (except [IndexError] (setv passed True))) (assert passed) ;; Test incorrect (raise) (setv passed False) (try (raise) (except [RuntimeError] (setv passed True))) (assert passed)) (defn test-try-clauses [] (defmacro try-it [body v1 v2] `(assert (= (_try-it (fn [] ~body)) [~v1 ~v2]))) (defn _try-it [callback] (setv did-finally-clause? False) (try (callback) (except [ZeroDivisionError] (setv out ["aaa" None])) (except [[IndexError NameError]] (setv out ["bbb" None])) (except [e TypeError] (setv out ["ccc" (type e)])) (except [e [KeyError AttributeError]] (setv out ["ddd" (type e)])) (except [] (setv out ["eee" None])) (else (setv out ["zzz" None])) (finally (setv did-finally-clause? True))) (assert did-finally-clause?) out) (try-it (/ 1 0) "aaa" None) (try-it (get "foo" 5) "bbb" None) (try-it unbound "bbb" None) (try-it (abs "hi") "ccc" TypeError) (try-it (get {1 2} 3) "ddd" KeyError) (try-it True.a "ddd" AttributeError) (try-it (raise ValueError) "eee" None) (try-it "hi" "zzz" None)) (defn test-finally-executes-for-uncaught-exception [] (setv x "") (with [(pytest.raises ZeroDivisionError)] (try (+= x "a") (/ 1 0) (+= x "b") (finally (+= x "c")))) (assert (= x "ac"))) (defn test-nonsyntactical-except [] #[[Test that [except ...] and ("except" ...) aren't treated like (except ...), and that the code there is evaluated normally.]] (setv x 0) (try (+= x 1) ("except" [IOError] (+= x 1)) (except [])) (assert (= x 2)) (setv x 0) (try (+= x 1) [except [IOError] (+= x 1)] (except [])) (assert (= x 2))) (defn test-try-except-return [] "Ensure we can return from an `except` form." (assert (= ((fn [] (try xxx (except [NameError] (+ 1 1))))) 2)) (setv foo (try xxx (except [NameError] (+ 1 1)))) (assert (= foo 2)) (setv foo (try (+ 2 2) (except [NameError] (+ 1 1)))) (assert (= foo 4))) (defn test-try-else-return [] "Ensure we can return from the `else` clause of a `try`." ; https://github.com/hylang/hy/issues/798 (assert (= "ef" ((fn [] (try (+ "a" "b") (except [NameError] (+ "c" "d")) (else (+ "e" "f"))))))) (setv foo (try (+ "A" "B") (except [NameError] (+ "C" "D")) (else (+ "E" "F")))) (assert (= foo "EF")) ; Check that the lvalue isn't assigned by the main `try` body ; when there's an `else`. (setv x 1) (setv y 0) (setv x (try (+ "G" "H") (except [NameError] (+ "I" "J")) (else (setv y 1) (assert (= x 1)) ; `x` still has its value from before the `try`. (+ "K" "L")))) (assert (= x "KL")) (assert (= y 1))) (do-mac (when hy.compat.PY3_11 '(defn test-except* [] (setv got "") (setv return-value (try (raise (ExceptionGroup "meep" [(KeyError) (ValueError)])) (except* [KeyError] (+= got "k") "r1") (except* [IndexError] (+= got "i") "r2") (except* [ValueError] (+= got "v") "r3") (else (+= got "e") "r4") (finally (+= got "f") "r5"))) (assert (= got "kvf")) (assert (= return-value "r3"))))) (defn test-raise-from [] (assert (is NameError (type (. (try (raise ValueError :from NameError) (except [e [ValueError]] e)) __cause__))))) hy-1.0.0/tests/native_tests/unpack.hy000066400000000000000000000025231467401276000176360ustar00rootroot00000000000000(defn test-star-unpacking [] (setv l [1 2 3]) (setv d {"a" "x" "b" "y"}) (defn fun [[x1 None] [x2 None] [x3 None] [x4 None] [a None] [b None] [c None]] [x1 x2 x3 x4 a b c]) (assert (= (fun 5 #* l) [5 1 2 3 None None None])) (assert (= (+ #* l) 6)) (assert (= (fun 5 #** d) [5 None None None "x" "y" None])) (assert (= (fun 5 #* l #** d) [5 1 2 3 "x" "y" None]))) (defn test-extended-unpacking-1star-lvalues [] (setv [x #* y] [1 2 3 4]) (assert (= x 1)) (assert (= y [2 3 4])) (setv [a #* b c] "ghijklmno") (assert (= a "g")) (assert (= b (list "hijklmn"))) (assert (= c "o"))) (defn test-unpacking-pep448-1star [] (setv l [1 2 3]) (setv p [4 5]) (assert (= ["a" #* l "b" #* p #* l] ["a" 1 2 3 "b" 4 5 1 2 3])) (assert (= #("a" #* l "b" #* p #* l) #("a" 1 2 3 "b" 4 5 1 2 3))) (assert (= #{"a" #* l "b" #* p #* l} #{"a" "b" 1 2 3 4 5})) (defn f [#* args] args) (assert (= (f "a" #* l "b" #* p #* l) #("a" 1 2 3 "b" 4 5 1 2 3))) (assert (= (+ #* l #* p) 15)) (assert (= (and #* l) 3))) (defn test-unpacking-pep448-2star [] (setv d1 {"a" 1 "b" 2}) (setv d2 {"c" 3 "d" 4}) (assert (= {1 "x" #** d1 #** d2 2 "y"} {"a" 1 "b" 2 "c" 3 "d" 4 1 "x" 2 "y"})) (defn fun [[a None] [b None] [c None] [d None] [e None] [f None]] [a b c d e f]) (assert (= (fun #** d1 :e "eee" #** d2) [1 2 3 4 "eee" None]))) hy-1.0.0/tests/native_tests/with.hy000066400000000000000000000062151467401276000173320ustar00rootroot00000000000000(import asyncio unittest.mock [Mock] pytest tests.resources [async-test AsyncWithTest async-exits]) (defn test-context [] (with [fd (open "tests/resources/text.txt" "r")] (assert fd)) (with [(open "tests/resources/text.txt" "r")] (do))) (defn test-with-return [] (defn read-file [filename] (with [fd (open filename "r" :encoding "UTF-8")] (.read fd))) (assert (= (read-file "tests/resources/text.txt") "TAARGÜS TAARGÜS\n"))) (setv exits []) (defclass WithTest [object] (defn __init__ [self val] (setv self.val val)) (defn __enter__ [self] self.val) (defn __exit__ [self type value traceback] (.append exits self.val))) (defn test-single-with [] (setv (cut exits) []) (with [t (WithTest 1)] (setv out t)) (assert (= out 1)) (assert (= exits [1]))) (defn test-quince-with [] (setv (cut exits) []) (with [t1 (WithTest 1) t2 (WithTest 2) t3 (WithTest 3) _ (WithTest 4)] (setv out [t1 t2 t3])) (assert (= out [1 2 3])) (assert (= exits [4 3 2 1]))) (defn [async-test] test-single-with-async [] (setv (cut async-exits) []) (setv out []) (asyncio.run ((fn :async [] (with [:async t (AsyncWithTest 1)] (.append out t))))) (assert (= out [1])) (assert (= async-exits [1]))) (defn [async-test] test-quince-with-async [] (setv (cut async-exits) []) (setv out []) (asyncio.run ((fn :async [] (with [ :async t1 (AsyncWithTest 1) :async t2 (AsyncWithTest 2) :async t3 (AsyncWithTest 3) :async _ (AsyncWithTest 4)] (.extend out [t1 t2 t3]))))) (assert (= out [1 2 3])) (assert (= async-exits [4 3 2 1]))) (defn [async-test] test-with-mixed-async [] (setv (cut exits) []) (setv out []) (asyncio.run ((fn :async [] (with [:async t1 (AsyncWithTest 1) t2 (WithTest 2) :async t3 (AsyncWithTest 3) _ (WithTest 4)] (.extend out [t1 t2 t3]))))) (assert (= out [1 2 3]))) (defn test-unnamed-context-with [] "`_` get compiled to unnamed context" (with [_ (WithTest 1) [b d] (WithTest (range 2 5 2)) _ (WithTest 3)] (assert (= [b d] [2 4])) (with [(pytest.raises NameError)] _))) (defclass SuppressZDE [object] (defn __enter__ [self]) (defn __exit__ [self exc-type exc-value traceback] (and (is-not exc-type None) (issubclass exc-type ZeroDivisionError)))) (defn test-exception-suppressing-with [] ; https://github.com/hylang/hy/issues/1320 (setv x (with [(SuppressZDE)] 5)) (assert (= x 5)) (setv y (with [(SuppressZDE)] (/ 1 0))) (assert (is y None)) (setv z (with [(SuppressZDE)] (/ 1 0) 5)) (assert (is z None)) (defn f [] (with [(SuppressZDE)] (/ 1 0))) (assert (is (f) None)) (setv w 7 l []) (setv w (with [(SuppressZDE)] (.append l w) (/ 1 0) 5)) (assert (is w None)) (assert (= l [7]))) (defn test-statements [] (setv m (Mock)) (with [t (do (m) (WithTest 2))] (setv out t)) (assert (= m.call-count 1)) (assert (= out 2)) ; https://github.com/hylang/hy/issues/2605 (with [t1 (WithTest 1) t2 (do (setv foo t1) (WithTest 2))] (setv out [t1 t2])) (assert (= out [1 2])) (assert (= foo 1))) hy-1.0.0/tests/resources/000077500000000000000000000000001467401276000153135ustar00rootroot00000000000000hy-1.0.0/tests/resources/__init__.py000066400000000000000000000012041467401276000174210ustar00rootroot00000000000000import pytest from hy.compat import PYODIDE in_init = "chippy" def kwtest(*args, **kwargs): return kwargs def function_with_a_dash(): pass can_test_async = not PYODIDE async_test = pytest.mark.skipif( not can_test_async, reason="`asyncio.run` not implemented" ) async_exits = [] class AsyncWithTest: def __init__(self, val): self.val = val async def __aenter__(self): return self.val async def __aexit__(self, exc_type, exc, traceback): async_exits.append(self.val) async def async_loop(items): import asyncio for x in items: yield x await asyncio.sleep(0) hy-1.0.0/tests/resources/argparse_ex.hy000077500000000000000000000004711467401276000201620ustar00rootroot00000000000000#!/usr/bin/env hy (import sys) (import argparse) (setv parser (argparse.ArgumentParser)) (.add_argument parser "-i") (.add_argument parser "-c") (setv args (.parse_args parser)) ;; using (cond) allows -i to take precedence over -c (cond args.i (print (str args.i)) args.c (print (str "got c"))) hy-1.0.0/tests/resources/bin/000077500000000000000000000000001467401276000160635ustar00rootroot00000000000000hy-1.0.0/tests/resources/bin/__init__.hy000066400000000000000000000000521467401276000201610ustar00rootroot00000000000000(defn _null-fn-for-import-test [] pass) hy-1.0.0/tests/resources/bin/__main__.hy000066400000000000000000000000711467401276000201430ustar00rootroot00000000000000(print "This is a __main__.hy") (setv visited_main True) hy-1.0.0/tests/resources/bin/bytecompile.hy000066400000000000000000000001321467401276000207350ustar00rootroot00000000000000(defmacro m [] (print "Hello from macro") "boink") (print "The macro returned:" (m)) hy-1.0.0/tests/resources/bin/circular_macro_require.hy000066400000000000000000000002541467401276000231470ustar00rootroot00000000000000(defmacro bar [expr] `(print (.upper ~expr))) (defmacro foo [expr] `(do (require tests.resources.bin.circular-macro-require [bar]) (bar ~expr))) (foo "wowie") hy-1.0.0/tests/resources/bin/nomain.hy000066400000000000000000000001251467401276000177040ustar00rootroot00000000000000(print "This Should Still Works") (defn main [] (print "This Should Not Work")) hy-1.0.0/tests/resources/bin/printenv.hy000066400000000000000000000000441467401276000202700ustar00rootroot00000000000000(import os) (print (. os environ)) hy-1.0.0/tests/resources/bin/require_and_eval.hy000066400000000000000000000001531467401276000217310ustar00rootroot00000000000000(require tests.resources.macros [test-macro]) (print (hy.eval '(do (test-macro) (cut "zabc" blah None)))) hy-1.0.0/tests/resources/exports.hy000066400000000000000000000003631467401276000173630ustar00rootroot00000000000000(defn jan [] 21) (defn wayne [] 22) (defn ♥ [] 23) (defmacro casey [#* tree] `[11 ~@tree]) (defmacro brother [#* tree] `[12 ~@tree]) (defmacro ☘ [#* tree] `[13 ~@tree]) (export :objects [jan ♥] :macros [casey ☘]) hy-1.0.0/tests/resources/exports_none.hy000066400000000000000000000002251467401276000203770ustar00rootroot00000000000000(defmacro cinco [#* tree] `[5 ~@tree]) (setv _hy_export_macros []) ; This should have no effect, because we never require `*` from this module. hy-1.0.0/tests/resources/fails.hy000066400000000000000000000001511467401276000167500ustar00rootroot00000000000000"This module produces an error when imported." (defmacro a-macro [x] (+ x 1)) (print (a-macro 'blah)) hy-1.0.0/tests/resources/hello_world.hy000066400000000000000000000000261467401276000201650ustar00rootroot00000000000000(print "hello world") hy-1.0.0/tests/resources/hy_repr_str_tests.txt000066400000000000000000000042611467401276000216410ustar00rootroot00000000000000;; One test case per line. ;; If the case doesn't begin with a single quote or a colon, it also ;; gets a variant case that does. ;; Lines starting with `;` are comments. ;; * Numeric None False True 5 5.1 Inf -Inf NaN 5j 5.1j 2+1j 1.2+3.4j Inf-Infj NaN+NaNj (Fraction 1 3) ;; * Symbols and keywords 'mysymbol 'my♥symbol? '. '.. '... '.... :mykeyword :my♥keyword? : '':mykeyword ; A keyword with only one single quote gets represented without the ; quote (which is equivalent), so it's tested in ; `test-hy-repr-roundtrip-from-value`. ;; * Dotted identifiers 'foo.bar '.foo.bar '..foo.bar '...foo.bar '....foo.bar ;; * Stringy thingies "" b"" (bytearray b"") "apple bloom" b"apple bloom" (bytearray b"apple bloom") "⚘" "single ' quotes" b"single ' quotes" "\"double \" quotes\"" b"\"double \" quotes\"" '#[[bracketed string]] '#[delim[bracketed string]delim] '#[delim[brack'eted string]delim] ;; * Collections [] #() #{} (frozenset #{}) {} ['[]] [1 2 3] #(1 2 3) #{1 2 3} '#{3 2 1 2} (frozenset #{1 2 3}) {"a" 1 "b" 2} {"b" 2 "a" 1} '[1 a 3] [1 'a 3] [1 '[2 3] 4] (deque []) (deque [1 2.5 None "hello"]) (ChainMap {}) (ChainMap {1 2} {3 4}) (OrderedDict []) (OrderedDict [#(1 2) #(3 4)]) ;; * Expressions '(+ 1 2) '(f a b) '(f #* args #** kwargs) '(.real 3) '(.a.b.c foo) '(math.sqrt 25) '(. a) '(. None) '(. 1 2) ; `(. a b)` will render as `a.b`. '(.. 1 2) '(.. a b) [1 [2 3] #(4 #('mysymbol :mykeyword)) {"a" b"hello"} '(f #* a #** b)] '(quote) ;; * Quasiquoting '[1 `[2 3] 4] '[1 `[~foo ~@bar] 4] '[1 `[~(+ 1 2) ~@(+ [1] [2])] 4] '[1 `[~(do (print x 'y) 1)] 4] '(quasiquote 1 2 3) '[1 `[2 (unquote foo bar)]] '[a ~@b] '[a ~ @b] ;; * F-strings 'f"a{:a}" 'f"a{{{{(+ 1 1)}}}}" 'f"the answer is {(+ 2 2)}" 'f"the answer is {(+ 2 2) !r :4}" 'f"the answer is {(+ 2 2) :{(+ 2 3)}}" '#[f-delim[the answer is {(+ 2 2) :{(+ 2 3)}}]f-delim] ;; * Ranges and slices (range 5) (slice 5) (range 2 5) (slice 2 5) (range 5 2) (slice 5 2) (range 0 5 2) (slice 0 5 2) (range 0 5 -2) (slice 0 5 -2) (slice [1 2 3] #(4 6) "hello") ;; * Regexen (re.compile "foo") (re.compile "\\Ax\\Z") (re.compile "'") (re.compile "\"") (re.compile "foo" re.IGNORECASE) (re.compile "foo" (| re.IGNORECASE re.MULTILINE)) hy-1.0.0/tests/resources/hystartup.hy000066400000000000000000000003101467401276000177120ustar00rootroot00000000000000(import os) (setv repl-spy True repl-output-fn (fn [x] (.replace (repr x) " " "_")) repl-ps1 "p1? ") (defmacro hello-world [] `(+ 1 1)) (defreader rad '(+ "totally" "rad")) hy-1.0.0/tests/resources/icmd_test_file.hy000066400000000000000000000000341467401276000206240ustar00rootroot00000000000000(setv species "cuttlefish") hy-1.0.0/tests/resources/importer/000077500000000000000000000000001467401276000171545ustar00rootroot00000000000000hy-1.0.0/tests/resources/importer/a.hy000066400000000000000000000000241467401276000177320ustar00rootroot00000000000000(thisshouldnotwork) hy-1.0.0/tests/resources/importer/basic.hy000066400000000000000000000001501467401276000205730ustar00rootroot00000000000000;; This is a comment. It shall be ignored by the parser. (setv square (fn [x] (* x x))) hy-1.0.0/tests/resources/importer/circular.hy000066400000000000000000000000701467401276000213170ustar00rootroot00000000000000(setv a 1) (defn f [] (import circular) circular.a) hy-1.0.0/tests/resources/importer/compiler_error.hy000066400000000000000000000000631467401276000225400ustar00rootroot00000000000000;; This should fail at compile time (print "hello) hy-1.0.0/tests/resources/importer/docstring.hy000066400000000000000000000001131467401276000215050ustar00rootroot00000000000000"This module has a docstring. It covers multiple lines, too! " (setv a 1) hy-1.0.0/tests/resources/importer/foo/000077500000000000000000000000001467401276000177375ustar00rootroot00000000000000hy-1.0.0/tests/resources/importer/foo/__init__.hy000066400000000000000000000000201467401276000220300ustar00rootroot00000000000000(setv ext "hy") hy-1.0.0/tests/resources/importer/foo/__init__.py000066400000000000000000000000131467401276000220420ustar00rootroot00000000000000ext = "py" hy-1.0.0/tests/resources/importer/foo/some_mod.hy000066400000000000000000000000201467401276000220730ustar00rootroot00000000000000(setv ext "hy") hy-1.0.0/tests/resources/importer/foo/some_mod.py000066400000000000000000000000131467401276000221050ustar00rootroot00000000000000ext = "py" hy-1.0.0/tests/resources/local_req_example.hy000066400000000000000000000002251467401276000213300ustar00rootroot00000000000000(defmacro wiz [] "remote wiz doc" "remote wiz") (defmacro get-wiz [] (wiz)) (defmacro helper [] "remote helper doc" "remote helper macro") hy-1.0.0/tests/resources/macro_with_require.hy000066400000000000000000000010561467401276000215470ustar00rootroot00000000000000;; Require all the macros and make sure they don't pollute namespaces/modules ;; that require `*` from this. (require tests.resources.macros *) (defmacro test-module-macro [a] "The variable `macro-level-var' here should not bind to the same-named symbol in the expansion of `remote-test-macro'." (setv macro-level-var "tests.resources.macros.macro-with-require") `(remote-test-macro ~a)) (defmacro test-module-macro-2 [a] "The macro `home-test-macro` isn't in this module's namespace, so it better be in the expansion's!" `(home-test-macro ~a)) hy-1.0.0/tests/resources/macros.hy000066400000000000000000000010071467401276000171370ustar00rootroot00000000000000(setv module-name-var "tests.resources.macros") (defmacro test-macro [] '(setv blah 1)) (defmacro test-macro-2 [] '(setv qup 2)) (defmacro remote-test-macro [x] "When called from `macro-with-require`'s macro(s), the first instance of `module-name-var` should resolve to the value in the module where this is defined, then the expansion namespace/module" `(.format (+ "This macro was created in {}, expanded in {} " "and passed the value {}.") ~module-name-var module-name-var ~x)) hy-1.0.0/tests/resources/module_docstring_example.hy000066400000000000000000000000561467401276000227320ustar00rootroot00000000000000"This is the module docstring." (setv foo 5) hy-1.0.0/tests/resources/more_test_macros.hy000066400000000000000000000001731467401276000212230ustar00rootroot00000000000000(defmacro bairn [#* tree] `[14 ~@tree]) (defmacro cairn [ #* tree] `[15 ~@tree]) (defmacro _dairn [] `[16 ~@tree]) hy-1.0.0/tests/resources/no_extension000066400000000000000000000000631467401276000177450ustar00rootroot00000000000000#!/usr/bin/env hy (print "This Should Still Work") hy-1.0.0/tests/resources/pydemo.hy000066400000000000000000000137651467401276000171660ustar00rootroot00000000000000;; This Hy module is intended to concisely demonstrate all of ;; Python's major syntactic features for the purpose of testing hy2py. ;; It also tries out macros and reader macros to ensure they work with ;; hy2py. "This is a module docstring." (setv mystring (* "foo" 3)) (setv long-string "This is a very long string literal, which would surely exceed any limitations on how long a line or a string literal can be. The string literal alone exceeds 256 characters. It also has a character outside the Basic Multilingual Plane: 😂. Here's a double quote: \". Here are some escaped newlines:\n\n\nHere is a literal newline: Call me Ishmael. Some years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people’s hats off—then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me.") (setv identifier-that-has☝️💯☝️-to-be-mangled "ponies") (setv 𝔫𝔬𝔯𝔪𝔞𝔩𝔦𝔷𝔢-𝔱𝔥𝔦𝔰 "ok") (setv def "variable") (setv 𝕚𝕗 "if") (setv mynumber (+ 1 2)) (setv myhex 0x123) (setv mylong 1234567890987654321234567890987654321) (setv myfloat 3.34e15) (setv mynan NaN) (setv pinf Inf) (setv ninf -Inf) (setv mycomplex -Inf+5j) (setv mycomplex2 NaN-Infj) (setv num-expr (+ 3 (* 5 2) (- 6 (// 8 2)) (* (+ 1 2) (- 3 5)))) ; = 9 (setv mylist [1 2 3]) (setv mytuple #("a" "b" "c")) (setv myset #{4 5 6}) (setv mydict {7 8 9 900 10 15}) (setv emptylist []) (setv emptytuple #()) (setv emptyset #{}) (setv emptydict {}) (setv mylistcomp (lfor x (range 10) :if (% x 2) x)) (setv mysetcomp (sfor x (range 5) :if (not (% x 2)) x)) (setv mydictcomp (dfor k "abcde" :if (!= k "c") k (.upper k))) (import itertools [cycle]) (setv mygenexpr (gfor x (cycle [1 2 3]) :if (!= x 2) x)) (setv attr-ref str.upper) (setv subscript (get "hello" 2)) (setv myslice (cut "hello" 1 None 2)) (setv call (len "hello")) (setv comparison (< "a" "b" "c")) (setv boolexpr (and (or True False) (not (and True False)))) (setv condexpr (if "" "x" "y")) (setv mylambda (fn [x] (+ x "z"))) (setv annotated-lambda-ret (fn #^ int [] 1)) (setv annotated-lambda-params (fn [#^ int a * #^ str [b "hello world!"]] #(a b))) (setv fstring1 f"hello {(+ 1 1)} world") (setv p "xyzzy") (setv fstring2 f"a{p !r :9}") (setv augassign 103) (//= augassign 4) (setv delstatement ["a" "b" "c" "d" "e"]) (del (get delstatement 1)) (import math) (import math [sqrt]) (import math [sin :as sine]) (import datetime *) (setv if-block "") (if 0 (do (+= if-block "a") (+= if-block "b")) (do (+= if-block "c") (+= if-block "d"))) (setv counter 4) (setv while-block "") (while counter (+= while-block "x") (-= counter 1) (else (+= while-block "e"))) (setv counter2 8) (setv cont-and-break "") (while counter2 (+= cont-and-break "x") (-= counter2 1) (when (= counter2 5) (continue)) (+= cont-and-break "y") (when (= counter2 3) (break)) (+= cont-and-break "z")) (setv for-block "") (for [x ["fo" "fi" "fu"]] (setv for-block (+ x for-block))) (try (assert (= 1 0)) (except [_ AssertionError] (setv caught-assertion True)) (finally (setv ran-finally True))) (try (raise (ValueError "payload")) (except [e ValueError] (setv myraise (str e)))) (try 1 (except [e ValueError] (raise)) (else (setv ran-try-else True))) (defn fun [a b [c 9] [from 10] #* args #** kwargs] "function docstring" [a b c from args (sorted (.items kwargs))]) (setv funcall1 (fun 1 2 3 4 "a" "b" "c" :k1 "v1" :k2 "v2")) (setv funcall2 (fun 7 8 #* [9 10 11] #** {"x1" "y1" "x2" "y2"})) (setv funcall3 (fun "x" "y" :from "spain")) (defn returner [] (return 1) (raise (ValueError)) 2) (setv myret (returner)) (defn generator [] (for [x "abc"] (yield x))) (setv myyield (list (generator))) (defn [(fn [f] (setv f.newattr "hello") f)] mydecorated []) (setv myglobal 102) (defn set-global [] (global myglobal) (+= myglobal 1)) (set-global) (defclass C1 []) ; Force the creation of a `pass` statement. (defclass C2 [C1] "class docstring" (setv attr1 5) (setv attr2 6)) (import contextlib [closing]) (setv closed []) (defclass Closeable [] (defn close [self] (.append closed self.x))) (with [c1 (closing (Closeable)) c2 (closing (Closeable))] (setv c1.x "v1") (setv c2.x "v2")) (setv closed1 (.copy closed)) (pys " closed = [] pys_accum = [] for i in range(5): with closing(Closeable()) as o: class C: pass o.x = C() pys_accum.append(i)") (setv py-accum (py "''.join(map(str, pys_accum))")) (defn :async coro [] (import asyncio tests.resources [AsyncWithTest async-loop]) (await (asyncio.sleep 0)) (setv values ["a"]) (with [:async t (AsyncWithTest "b")] (.append values t)) (for [:async item (async-loop ["c" "d"])] (.append values item)) (.extend values (lfor :async item (async-loop ["e" "f"]) item)) values) (defmacro macaroni [expr] `[~expr ~expr]) (setv cheese []) (setv mac-results (macaroni (do (.append cheese 1) "x"))) (defreader chicken-strips (setv expr (.parse-one-form &reader)) `[~expr ~expr]) (setv tendies []) (setv chicken-results #chicken-strips (do (.append tendies 2) "y")) hy-1.0.0/tests/resources/relative_import.hy000066400000000000000000000000641467401276000210620ustar00rootroot00000000000000(import bin.printenv) (import sys) (print sys.path) hy-1.0.0/tests/resources/relative_import_compile_time.hy000066400000000000000000000001161467401276000236060ustar00rootroot00000000000000(eval-when-compile (import bin.printenv) (import sys) (print sys.path)) hy-1.0.0/tests/resources/spy_off_startup.hy000066400000000000000000000001301467401276000210760ustar00rootroot00000000000000(setv repl-spy False repl-output-fn (fn [x] (.replace (repr x) " " "~"))) hy-1.0.0/tests/resources/text.txt000066400000000000000000000000221467401276000170320ustar00rootroot00000000000000TAARGÜS TAARGÜS hy-1.0.0/tests/resources/tlib.hy000066400000000000000000000011341467401276000166060ustar00rootroot00000000000000(setv SECRET_MESSAGE "Hello World") (defmacro qplah [#* tree] `[8 ~@tree]) (defmacro parald [#* tree] `[9 ~@tree]) (defmacro ✈ [arg] `(+ "plane " ~arg)) (defreader upper! (let [node (&reader.parse-one-form)] (if (isinstance node #(hy.models.Symbol hy.models.String)) (.__class__ node (.upper node)) (raise (TypeError f"Cannot uppercase {(type node)}"))))) (defreader lower (setv node (&reader.parse-one-form)) (if (isinstance node #(hy.models.Symbol hy.models.String)) (.__class__ node (.lower node)) (raise (TypeError f"Cannot lowercase {(type node)}")))) hy-1.0.0/tests/resources/tp.hy000066400000000000000000000004031467401276000162750ustar00rootroot00000000000000"Helpers for testing type parameters." (import typing [TypeVar TypeVarTuple ParamSpec TypeAliasType]) (defn show [f] (lfor tp f.__type_params__ [(type tp) tp.__name__ (getattr tp "__bound__" None) (getattr tp "__constraints__" #())])) hy-1.0.0/tests/test_bin.py000066400000000000000000000521221467401276000154640ustar00rootroot00000000000000#!/usr/bin/env python import builtins import os import platform import re import shlex import subprocess from importlib.util import cache_from_source from pathlib import Path import pytest from hy.compat import PY3_9, PYODIDE if PYODIDE: pytest.skip( '`subprocess.Popen` not implemented on Pyodide', allow_module_level = True) def pyr(s=""): return "hy --repl-output-fn=repr " + s def run_cmd( cmd, stdin_data=None, expect=0, dontwritebytecode=False, cwd=None, stdout=subprocess.PIPE, env=None): env = {**dict(os.environ), **(env or {})} if dontwritebytecode: env["PYTHONDONTWRITEBYTECODE"] = "1" else: env.pop("PYTHONDONTWRITEBYTECODE", None) # ensure hy root dir is in Python's path, # so that we can import/require modules within tests/ env["PYTHONPATH"] = str(Path().resolve()) + os.pathsep + env.get("PYTHONPATH", "") result = subprocess.run( shlex.split(cmd) if isinstance(cmd, str) else cmd, input=stdin_data, stdout=stdout, stderr=subprocess.PIPE, universal_newlines=True, shell=False, env=env, cwd=cwd, ) assert result.returncode == expect return (result.stdout, result.stderr) def rm(fpath): try: os.remove(fpath) except OSError: try: os.rmdir(fpath) except OSError: pass def test_simple(): run_cmd("hy", "") def test_stdin(): # https://github.com/hylang/hy/issues/2438 code = '(+ "P" "Q")\n(print (+ "R" "S"))\n(+ "T" "U")' # Without `-i`, the standard input is run as a script. out, _ = run_cmd("hy", code) assert "PQ" not in out assert "RS" in out assert "TU" not in out # With it, the standard input is fed to the REPL. out, _ = run_cmd("hy -i", code) assert "PQ" in out assert "RS" in out assert "TU" in out def test_i_flag_repl_env(): # If a program is passed in through standard input, it's evaluated # in the REPL environment. code = '(import sys) (if (hasattr sys "ps1") "Yeppers" "Nopers")' out, _ = run_cmd("hy -i", code) assert "Yeppers" in out # With `-c`, on the other hand, the code is run before the REPL is # launched. out, _ = run_cmd(['hy', '-i', '-c', code]) assert "Nopers" in out def test_mangle_m(): # https://github.com/hylang/hy/issues/1445 output, _ = run_cmd("hy -m tests.resources.hello_world") assert "hello world" in output output, _ = run_cmd("hy -m tests.resources.hello-world") assert "hello world" in output def test_ignore_python_env(): e = dict(PYTHONTEST = "0") output, _ = run_cmd("hy -c '(print (do (import os) (. os environ)))'", env = e) assert "PYTHONTEST" in output output, _ = run_cmd("hy -m tests.resources.bin.printenv", env = e) assert "PYTHONTEST" in output output, _ = run_cmd("hy tests/resources/bin/printenv.hy", env = e) assert "PYTHONTEST" in output output, _ = run_cmd("hy -E -c '(print (do (import os) (. os environ)))'", env = e) assert "PYTHONTEST" not in output output, _ = run_cmd("hy -E -m tests.resources.bin.printenv", env = e) assert "PYTHONTEST" not in output output, _ = run_cmd("hy -E tests/resources/bin/printenv.hy", env = e) assert "PYTHONTEST" not in output def test_cmd(): output, _ = run_cmd("""hy -c '(print (.upper "hello"))'""") assert "HELLO" in output _, err = run_cmd("""hy -c '(print (.upper "hello")'""", expect=1) assert "Premature end of input" in err # No shebang is allowed. _, err = run_cmd("""hy -c '#!/usr/bin/env hy'""", expect = 1) assert "LexException" in err # https://github.com/hylang/hy/issues/1879 output, _ = run_cmd( """hy -c '(setv x "bing") (defn f [] (+ "fiz" x)) (print (f))'""" ) assert "fizbing" in output # https://github.com/hylang/hy/issues/1894 output, _ = run_cmd(' '.join(('hy -c ', repr('(import sys) (print (+ "<" (.join "|" sys.argv) ">"))'), 'AA', 'ZZ', '-m'))) assert "<-c|AA|ZZ|-m>" in output def test_icmd_string(): output, _ = run_cmd("""hy -i -c '(.upper "hello")'""", '(.upper "bye")') assert "HELLO" in output assert "BYE" in output def test_icmd_file(): output, _ = run_cmd("hy -i tests/resources/icmd_test_file.hy", '(.upper species)') assert "CUTTLEFISH" in output def test_icmd_shebang(tmp_path): (tmp_file := tmp_path / 'icmd_with_shebang.hy').write_text('#!/usr/bin/env hy\n(setv order "Sepiida")') output, error = run_cmd(["hy", "-i", tmp_file], '(.upper order)') assert "#!/usr/bin/env" not in error assert "SEPIIDA" in output def test_icmd_and_spy(): output, _ = run_cmd('hy --spy -i -c "(+ [] [])"', "(+ 1 1)") assert "[] + []" in output def test_empty_file(tmp_path): # https://github.com/hylang/hy/issues/2427 (tmp_path / 'foo.hy').write_text('') run_cmd(['hy', (tmp_path / 'foo.hy')]) # This asserts that the return code is 0. def test_missing_file(): _, err = run_cmd("hy foobarbaz", expect=2) assert "No such file" in err def test_file_with_args(): cmd = "hy tests/resources/argparse_ex.hy" assert "usage" in run_cmd(f"{cmd} -h")[0] assert "got c" in run_cmd(f"{cmd} -c bar")[0] assert "foo" in run_cmd(f"{cmd} -i foo")[0] assert "foo" in run_cmd(f"{cmd} -i foo -c bar")[0] def test_ifile_with_args(): cmd = "hy -i tests/resources/argparse_ex.hy" assert "usage" in run_cmd(f"{cmd} -h")[0] assert "got c" in run_cmd(f"{cmd} -c bar")[0] assert "foo" in run_cmd(f"{cmd} -i foo")[0] assert "foo" in run_cmd(f"{cmd} -i foo -c bar")[0] def test_hyc(): output, _ = run_cmd("hyc -h") assert "usage" in output path = "tests/resources/argparse_ex.hy" _, err = run_cmd(["hyc", path]) assert "Compiling" in err assert os.path.exists(cache_from_source(path)) rm(cache_from_source(path)) def test_hyc_missing_file(): _, err = run_cmd("hyc foobarbaz", expect=1) assert "[Errno 2]" in err def test_no_main(): output, _ = run_cmd("hy tests/resources/bin/nomain.hy") assert "This Should Still Work" in output @pytest.mark.parametrize( "scenario", ["normal", "prevent_by_force", "prevent_by_env", "prevent_by_option"] ) @pytest.mark.parametrize( "cmd_fmt", [ ["hy", "{fpath}"], ["hy", "-m", "{modname}"], ["hy", "-c", "'(import {modname})'"], ], ) def test_byte_compile(scenario, cmd_fmt): modname = "tests.resources.bin.bytecompile" fpath = modname.replace(".", "/") + ".hy" if scenario == "prevent_by_option": cmd_fmt.insert(1, "-B") cmd = " ".join(cmd_fmt).format(**locals()) rm(cache_from_source(fpath)) if scenario == "prevent_by_force": # Keep Hy from being able to byte-compile the module by # creating a directory at the target location. os.mkdir(cache_from_source(fpath)) # Whether or not we can byte-compile the module, we should be able # to run it. output, _ = run_cmd(cmd, dontwritebytecode=(scenario == "prevent_by_env")) assert "Hello from macro" in output assert "The macro returned: boink" in output if scenario == "normal": # That should've byte-compiled the module. assert os.path.exists(cache_from_source(fpath)) elif scenario == "prevent_by_env" or scenario == "prevent_by_option": # No byte-compiled version should've been created. assert not os.path.exists(cache_from_source(fpath)) # When we run the same command again, and we've byte-compiled the # module, the byte-compiled version should be run instead of the # source, in which case the macro shouldn't be run. output, _ = run_cmd(cmd) assert ("Hello from macro" in output) ^ (scenario == "normal") assert "The macro returned: boink" in output def test_module_main_file(): output, _ = run_cmd("hy -m tests.resources.bin") assert "This is a __main__.hy" in output output, _ = run_cmd("hy -m .tests.resources.bin", expect=1) def test_file_main_file(): output, _ = run_cmd("hy tests/resources/bin") assert "This is a __main__.hy" in output def test_file_sys_path(): """The test resource `relative_import.hy` will perform an absolute import of a module in its directory: a directory that is not on the `sys.path` of the script executing the module (i.e. `hy`). We want to make sure that Hy adopts the file's location in `sys.path`, instead of the runner's current dir (e.g. '' in `sys.path`). """ file_path, _ = os.path.split("tests/resources/relative_import.hy") file_relative_path = os.path.realpath(file_path) output, _ = run_cmd("hy tests/resources/relative_import.hy") assert repr(file_relative_path) in output def testc_file_sys_path(): # similar to test_file_sys_path, test hyc and hy2py to make sure # they can find the relative import at compile time # https://github.com/hylang/hy/issues/2021 test_file = "tests/resources/relative_import_compile_time.hy" file_relative_path = os.path.realpath(os.path.dirname(test_file)) for binary in ("hy", "hyc", "hy2py"): # Ensure we hit the compiler rm(cache_from_source(test_file)) assert not os.path.exists(cache_from_source(file_relative_path)) output, _ = run_cmd([binary, test_file]) assert repr(file_relative_path) in output def test_module_no_main(): output, _ = run_cmd("hy -m tests.resources.bin.nomain") assert "This Should Still Work" in output def test_sys_executable(): output, _ = run_cmd("hy -c '(do (import sys) (print sys.executable))'") assert os.path.basename(output.strip()) == "hy" def test_file_no_extension(): """Confirm that a file with no extension is processed as Hy source""" output, _ = run_cmd("hy tests/resources/no_extension") assert "This Should Still Work" in output def test_circular_macro_require(): """Confirm that macros can require themselves during expansion and when run from the command line.""" # First, with no bytecode test_file = "tests/resources/bin/circular_macro_require.hy" rm(cache_from_source(test_file)) assert not os.path.exists(cache_from_source(test_file)) output, _ = run_cmd(["hy", test_file]) assert output.strip() == "WOWIE" # Now, with bytecode assert os.path.exists(cache_from_source(test_file)) output, _ = run_cmd(["hy", test_file]) assert output.strip() == "WOWIE" def test_macro_require(): """Confirm that a `require` will load macros into the non-module namespace (i.e. `exec(code, locals)`) used by `runpy.run_path`. In other words, this confirms that the AST generated for a `require` will load macros into the unnamed namespace its run in.""" # First, with no bytecode test_file = "tests/resources/bin/require_and_eval.hy" rm(cache_from_source(test_file)) assert not os.path.exists(cache_from_source(test_file)) output, _ = run_cmd(["hy", test_file]) assert output.strip() == "abc" # Now, with bytecode assert os.path.exists(cache_from_source(test_file)) output, _ = run_cmd(["hy", test_file]) assert output.strip() == "abc" def test_tracebacks(): """Make sure the printed tracebacks are correct.""" def req_err(x): assert x == "hy.errors.HyRequireError: No module named 'not_a_real_module'" # Modeled after # > python -c 'import not_a_real_module' # Traceback (most recent call last): # File "", line 1, in # ImportError: No module named not_a_real_module _, error = run_cmd("hy", "(require not-a-real-module)", expect=1) error_lines = error.splitlines() if error_lines[-1] == "": del error_lines[-1] assert len(error_lines) <= 10 # Rough check for the internal traceback filtering req_err(error_lines[-1]) _, error = run_cmd('hy -c "(require not-a-real-module)"', expect=1) error_lines = error.splitlines() assert len(error_lines) <= 4 req_err(error_lines[-1]) output, error = run_cmd('hy -i -c "(require not-a-real-module)"', '') assert output.startswith("=> ") req_err(error.splitlines()[2]) # Modeled after # > python -c 'print("hi' # File "", line 1 # print("hi # ^ # SyntaxError: EOL while scanning string literal _, error = run_cmd(r'hy -c "(print \""', expect=1) peoi_re = ( r"Traceback \(most recent call last\):\n" r' File "(?:|string-[0-9a-f]+)", line 1\n' r' \(print "\n' r" \^\n" r"hy.PrematureEndOfInput" ) assert re.search(peoi_re, error) # Modeled after # > python -i -c "print('" # File "", line 1 # print(' # ^ # SyntaxError: EOL while scanning string literal # >>> output, error = run_cmd(r'hy -c "(print \""', expect=1) assert output == '' assert re.match(peoi_re, error) # Modeled after # > python -c 'print(a)' # Traceback (most recent call last): # File "", line 1, in # NameError: name 'a' is not defined output, error = run_cmd('hy -c "(print a)"', expect=1) # Filter out the underline added by Python 3.11. error_lines = [x for x in error.splitlines() if not (set(x) <= {" ", "^", "~"})] assert error_lines[3] == ' File "", line 1, in ' # PyPy will add "global" to this error message, so we work around that. assert error_lines[-1].strip().replace(" global", "") == ( "NameError: name 'a' is not defined" ) # Modeled after # > python -c 'compile()' # Traceback (most recent call last): # File "", line 1, in # TypeError: Required argument 'source' (pos 1) not found output, error = run_cmd('hy -c "(compile)"', expect=1) error_lines = error.splitlines() assert error_lines[-2] == ' File "", line 1, in ' assert error_lines[-1].startswith("TypeError") def test_traceback_shebang(tmp_path): # https://github.com/hylang/hy/issues/2405 (tmp_path / 'ex.hy').write_text('#!my cool shebang\n(/ 1 0)') _, error = run_cmd(['hy', tmp_path / 'ex.hy'], expect = 1) assert 'ZeroDivisionError' assert 'my cool shebang' not in error assert '(/ 1 0)' in error def test_hystartup(): # spy == True and custom repl-output-fn env = dict(HYSTARTUP = "tests/resources/hystartup.hy") output, _ = run_cmd("hy -i", "[1 2]", env = env) assert "p1? " in output assert "[1, 2]" in output assert "[1,_2]" in output output, _ = run_cmd("hy -i", "(hello-world)", env = env) assert "(hello-world)" not in output assert "1 + 1" in output assert "2" in output output, _ = run_cmd("hy -i", "#rad", env = env) assert "#rad" not in output assert "'totally' + 'rad'" in output assert "'totallyrad'" in output output, _ = run_cmd("hy -i --repl-output-fn repr", "[1 2 3 4]", env = env) assert "[1, 2, 3, 4]" in output assert "[1 2 3 4]" not in output assert "[1,_2,_3,_4]" not in output # spy == False and custom repl-output-fn # Then overwrite spy with cmdline arg output, _ = run_cmd("hy -i --spy", "[1 2]", env = dict(HYSTARTUP = "tests/resources/spy_off_startup.hy")) assert "[1, 2]" in output assert "[1,~2]" in output def test_output_buffering(tmp_path): tf = tmp_path / "file.txt" pf = tmp_path / "program.hy" pf.write_text(f''' (print "line 1") (import sys pathlib [Path]) (print :file sys.stderr (.strip (.read-text (Path #[=[{tf}]=])))) (print "line 2")''') for flags, expected in ([], ""), (["--unbuffered"], "line 1"): with open(tf, "wb") as o: _, stderr = run_cmd(["hy", *flags, pf], stdout=o) assert stderr.strip() == expected assert tf.read_text().splitlines() == ["line 1", "line 2"] def test_uufileuu(tmp_path, monkeypatch): # `__file__` should be set the same way as in Python. # https://github.com/hylang/hy/issues/2318 (tmp_path / "realdir").mkdir() (tmp_path / "realdir" / "hyex.hy").write_text('(print __file__)') (tmp_path / "realdir" / "pyex.py").write_text('print(__file__)') def file_is(arg, expected_py3_9): expected = expected_py3_9 if PY3_9 else Path(arg) output, _ = run_cmd(["python3", arg + "pyex.py"]) assert output.rstrip() == str(expected / "pyex.py") output, _ = run_cmd(["hy", arg + "hyex.hy"]) assert output.rstrip() == str(expected / "hyex.hy") monkeypatch.chdir(tmp_path) file_is("realdir/", tmp_path / "realdir") monkeypatch.chdir(tmp_path / "realdir") file_is("", tmp_path / "realdir") (tmp_path / "symdir").symlink_to("realdir", target_is_directory = True) monkeypatch.chdir(tmp_path) file_is("symdir/", tmp_path / "symdir") (tmp_path / "realdir" / "child").mkdir() monkeypatch.chdir(tmp_path / "realdir" / "child") file_is("../", tmp_path / "realdir" if platform.system() == "Windows" else tmp_path / "realdir" / "child" / "..") def test_assert(tmp_path, monkeypatch): # Check when statements pulled out of `assert` are run. # https://github.com/hylang/hy/issues/1390 # https://github.com/hylang/hy/issues/2319 monkeypatch.chdir(tmp_path) for has_msg in False, True: Path("ex.hy").write_text( "(defn f [test] (assert {} {}))".format( '(do (print "testing") test)', '(do (print "msging") "bye")' if has_msg else "")) for optim, test in [(0, 0), (0, 1), (1, 0), (1, 1)]: out, err = run_cmd( cmd = "python3 {} {}".format( ("-O" if optim else ""), f"-c 'import hy, ex; ex.f({test})'"), expect = (1 if not optim and not test else 0)) assert ("testing" in out) == (not optim) show_msg = has_msg and not optim and not test assert ("msging" in out) == show_msg assert ("bye" in err) == show_msg def test_hy2py_stdin(): out, _ = run_cmd("hy2py", "(+ 482 223)") assert "482 + 223" in out assert "705" not in out def test_hy2py_compile_only(monkeypatch): def check(args): output, _ = run_cmd(f"hy2py {args}") assert not re.search(r"^hello world$", output, re.M) monkeypatch.chdir('tests/resources') check("hello_world.hy") check("-m hello_world") monkeypatch.chdir('..') check("resources/hello_world.hy") check("-m resources.hello_world") def test_hy2py_recursive(monkeypatch, tmp_path): (tmp_path / 'foo').mkdir() (tmp_path / 'foo/__init__.py').touch() (tmp_path / "foo/first.hy").write_text(""" (import foo.folder.second [a b]) (print a) (print b)""") (tmp_path / "foo/folder").mkdir() (tmp_path / "foo/folder/__init__.py").touch() (tmp_path / "foo/folder/second.hy").write_text(""" (setv a 1) (setv b "hello world")""") monkeypatch.chdir(tmp_path) _, err = run_cmd("hy2py -m foo", expect=1) assert "ValueError" in err run_cmd("hy2py -m foo --output bar") assert set((tmp_path / 'bar').rglob('*')) == { tmp_path / 'bar' / p for p in ('first.py', 'folder', 'folder/second.py')} output, _ = run_cmd("python3 first.py", cwd = tmp_path / 'bar') assert output == "1\nhello world\n" @pytest.mark.parametrize('case', ['hy -m', 'hy2py -m']) def test_relative_require(case, monkeypatch, tmp_path): # https://github.com/hylang/hy/issues/2204 (tmp_path / 'pkg').mkdir() (tmp_path / 'pkg' / '__init__.py').touch() (tmp_path / 'pkg' / 'a.hy').write_text(''' (defmacro m [] '(setv x (.upper "hello")))''') (tmp_path / 'pkg' / 'b.hy').write_text(''' (require .a [m]) (m) (print x)''') monkeypatch.chdir(tmp_path) if case == 'hy -m': output, _ = run_cmd('hy -m pkg.b') elif case == 'hy2py -m': run_cmd('hy2py -m pkg -o out') (tmp_path / 'out' / '__init__.py').touch() output, _ = run_cmd('python3 -m out.b') assert 'HELLO' in output def test_require_doesnt_pollute_core(monkeypatch, tmp_path): # https://github.com/hylang/hy/issues/1978 """Macros loaded from an external module should not pollute `_hy_macros` with macros from core.""" (tmp_path / 'aaa.hy').write_text(''' (defmacro foo [] '(setv x (.upper "argelfraster")))''') (tmp_path / 'bbb.hy').write_text(''' (require aaa :as A) (A.foo) (print x (not-in "if" _hy_macros) (not-in "cond" _hy_macros))''') # `if` is a result macro; `cond` is a regular macro. monkeypatch.chdir(tmp_path) # Try it without and then with bytecode. for _ in (1, 2): assert 'ARGELFRASTER True True' in run_cmd('hy bbb.hy')[0] def test_run_dir_or_zip(tmp_path): (tmp_path / 'dir').mkdir() (tmp_path / 'dir' / '__main__.hy').write_text('(print (+ "A" "Z"))') out, _ = run_cmd(['hy', tmp_path / 'dir']) assert 'AZ' in out from zipfile import ZipFile with ZipFile(tmp_path / 'zoom.zip', 'w') as o: o.writestr('__main__.hy', '(print (+ "B" "Y"))') out, _ = run_cmd(['hy', tmp_path / 'zoom.zip']) assert 'BY' in out hy-1.0.0/tests/test_completer.py000066400000000000000000000036171467401276000167130ustar00rootroot00000000000000import os import types import pytest import hy.completer from hy import mangle, unmangle hy.completer.init_readline() @pytest.mark.skipif( not hy.completer.readline, reason="Module 'readline' is not available." ) def test_history_custom_location(tmp_path): import readline expected_entry = '(print "Hy, custom history file!")' history_location = tmp_path / ".hy-custom-history" os.environ["HY_HISTORY"] = str(history_location) with hy.completer.completion(): readline.clear_history() readline.add_history(expected_entry) actual_entry = history_location.read_text() # yes, this is recommended way to check GNU readline vs libedit # see https://docs.python.org/3.11/library/readline.html if "libedit" in readline.__doc__: # libedit saves spaces as octal escapes, so convert them back actual_entry = actual_entry.replace("\\040", " ") assert expected_entry in actual_entry def test_completion(): completer = hy.completer.Completer( { "hy": None, "simple_pythonic_var_name": None, mangle("complicated->@#%!name"): types.SimpleNamespace( **{mangle("another$^@#$name"): None} ), "hyx_XaXaXaX": types.SimpleNamespace(**{"hyx_XbXbX": None}), } ) assert completer.complete("hy.", 0) is not None for test in [ ("simple_pyth", "simple-pythonic-var-name"), ("compli", "complicated->@#%!name"), ("complicated->@#", "complicated->@#%!name"), ("complicated->@#%!name", "complicated->@#%!name"), ("complicated->@#%!name.ano", "complicated->@#%!name.another$^@#$name"), ("complicated->@#%!name.another$^@", "complicated->@#%!name.another$^@#$name"), ("hyx_XaX", "hyx_XaXaXaX"), ("hyx_XaXaXaX.hyx_Xb", "hyx_XaXaXaX.hyx_XbXbX"), ]: assert completer.complete(test[0], 0) == test[1] hy-1.0.0/tests/test_hy2py.py000066400000000000000000000137351467401276000157760ustar00rootroot00000000000000import asyncio import itertools import math import pytest import hy.importer from hy import mangle from hy.compat import PYODIDE from tests.resources import can_test_async def test_direct_import(): import tests.resources.pydemo assert_stuff(tests.resources.pydemo) @pytest.mark.skipif(PYODIDE, reason="subprocess.check_call not implemented on Pyodide") def test_hy2py_import(): import contextlib import os import subprocess path = "tests/resources/pydemo_as_py.py" env = dict(os.environ) env["PYTHONIOENCODING"] = "UTF-8" env["PYTHONPATH"] = "." + os.pathsep + env.get("PYTHONPATH", "") try: with open(path, "wb") as o: subprocess.check_call( ["hy2py", "tests/resources/pydemo.hy"], stdout=o, env=env) import tests.resources.pydemo_as_py as m finally: with contextlib.suppress(FileNotFoundError): os.remove(path) assert_stuff(m) def assert_stuff(m): # This makes sure that automatically imported builtins go after docstrings. assert m.__doc__ == "This is a module docstring." assert m.mystring == "foofoofoo" assert ( m.long_string == "This is a very long string literal, which would surely exceed any limitations on how long a line or a string literal can be. The string literal alone exceeds 256 characters. It also has a character outside the Basic Multilingual Plane: 😂. Here's a double quote: \". Here are some escaped newlines:\n\n\nHere is a literal newline:\nCall me Ishmael. Some years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people’s hats off—then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me." ) assert getattr(m, mangle("identifier-that-has☝️💯☝️-to-be-mangled")) == "ponies" assert m.normalize_this == "ok" assert getattr(m, "def") == "variable" assert m.𝕕𝕖𝕗 == "variable" assert getattr(m, "if") == "if" assert m.mynumber == 3 assert m.myhex == 0x123 assert m.mylong - 1234567890987654321234567890987654320 == 1 assert m.myfloat == 3.34e15 assert math.isnan(m.mynan) assert math.isinf(m.pinf) assert m.pinf > 0 assert math.isinf(m.ninf) assert m.ninf < 0 assert math.isinf(m.mycomplex.real) assert m.mycomplex.real < 0 assert m.mycomplex.imag == 5 assert math.isnan(m.mycomplex2.real) assert math.isinf(m.mycomplex2.imag) assert m.mycomplex2.imag < 0 assert m.num_expr == 9 assert m.mylist == [1, 2, 3] assert m.mytuple == ("a", "b", "c") assert m.myset == {4, 5, 6} assert m.mydict == {7: 8, 9: 900, 10: 15} assert m.emptylist == [] assert m.emptytuple == () assert m.emptyset == set() assert m.emptydict == {} assert m.mylistcomp == [1, 3, 5, 7, 9] assert m.mysetcomp == {0, 2, 4} assert m.mydictcomp == dict(a="A", b="B", d="D", e="E") assert type(m.mygenexpr) is type(x for x in [1, 2, 3]) assert list(itertools.islice(m.mygenexpr, 5)) == [1, 3, 1, 3, 1] assert m.attr_ref is str.upper assert m.subscript == "l" assert m.myslice == "el" assert m.call == 5 assert m.comparison is True assert m.boolexpr is True assert m.condexpr == "y" assert type(m.mylambda) is type(lambda x: x + "z") assert m.mylambda("a") == "az" assert m.annotated_lambda_ret() == 1 assert m.annotated_lambda_params(1) == (1, "hello world!") assert m.fstring1 == "hello 2 world" assert m.fstring2 == "a'xyzzy' " assert m.augassign == 25 assert m.delstatement == ["a", "c", "d", "e"] assert m.math is math assert m.sqrt is math.sqrt assert m.sine is math.sin import datetime assert m.timedelta is datetime.timedelta assert m.if_block == "cd" assert m.while_block == "xxxxe" assert m.cont_and_break == "xyzxyzxxyzxy" assert m.for_block == "fufifo" assert m.caught_assertion is True assert m.ran_finally is True assert m.myraise == "payload" assert m.ran_try_else is True assert type(m.fun) is type(lambda x: x) assert m.fun.__doc__ == "function docstring" assert m.funcall1 == [1, 2, 3, 4, ("a", "b", "c"), [("k1", "v1"), ("k2", "v2")]] assert m.funcall2 == [7, 8, 9, 10, (11,), [("x1", "y1"), ("x2", "y2")]] assert m.funcall3 == ["x", "y", 9, "spain", (), []] assert m.myret == 1 assert m.myyield == ["a", "b", "c"] assert m.mydecorated.newattr == "hello" assert m.myglobal == 103 class C: pass assert type(m.C1) is type(C) assert m.C2.__doc__ == "class docstring" assert issubclass(m.C2, m.C1) assert (m.C2.attr1, m.C2.attr2) == (5, 6) assert m.closed1 == ["v2", "v1"] assert len(m.closed) == 5 for a, b in itertools.combinations(m.closed, 2): assert type(a) is not type(b) assert m.pys_accum == [0, 1, 2, 3, 4] assert m.py_accum == "01234" if can_test_async: assert asyncio.run(m.coro()) == list("abcdef") assert m.cheese == [1, 1] assert m.mac_results == ["x", "x"] assert m.tendies == [2, 2] assert m.chicken_results == ["y", "y"] hy-1.0.0/tests/test_models.py000066400000000000000000000162241467401276000162020ustar00rootroot00000000000000import copy import pytest import hy from hy.errors import HyWrapperError from hy.models import ( Complex, Dict, Expression, FComponent, Float, FString, Integer, Keyword, List, Set, String, Symbol, Tuple, as_model, pretty, replace_hy_obj, ) def test_symbol_or_keyword(): for x in ("foo", "foo-bar", "foo_bar", "✈é😂⁂"): assert str(Symbol(x)) == x assert Keyword(x).name == x for x in ("", ":foo", "5", "#foo"): # https://github.com/hylang/hy/issues/2383 with pytest.raises(ValueError): Symbol(x) assert Keyword(x).name == x for x in ("foo bar", "fib()"): with pytest.raises(ValueError): Symbol(x) with pytest.raises(ValueError): Keyword(x) def test_wrap_int(): wrapped = as_model(0) assert type(wrapped) == Integer def test_wrap_tuple(): wrapped = as_model((Integer(0),)) assert type(wrapped) == Tuple assert type(wrapped[0]) == Integer assert wrapped == Tuple([Integer(0)]) def test_wrap_nested_expr(): """Test conversion of Expressions with embedded non-HyObjects.""" wrapped = as_model(Expression([0])) assert type(wrapped) == Expression assert type(wrapped[0]) == Integer assert wrapped == Expression([Integer(0)]) def test_replace_int(): replaced = replace_hy_obj(0, Integer(13)) assert replaced == Integer(0) def test_invalid_bracket_strings(): for string, brackets in [("]foo]", "foo"), ("something ]f] else", "f")]: with pytest.raises(ValueError): String(string, brackets) for nodes, brackets in [ ([String("hello"), String("world ]foo]")], "foo"), ([String("something"), FComponent([String("world")]), String("]f]")], "f"), ([String("something"), FComponent([Integer(1), String("]f]")])], "f"), ]: with pytest.raises(ValueError): FString(nodes, brackets=brackets) def test_replace_str(): replaced = replace_hy_obj("foo", String("bar")) assert replaced == String("foo") def test_replace_tuple(): replaced = replace_hy_obj((0,), Integer(13)) assert type(replaced) == Tuple assert type(replaced[0]) == Integer assert replaced == Tuple([Integer(0)]) def test_list_add(): """Check that adding two Lists generates a List""" a = List([1, 2, 3]) b = List([3, 4, 5]) c = a + b assert c == List([1, 2, 3, 3, 4, 5]) assert type(c) is List def test_list_slice(): """Check that slicing a List produces a List""" a = List([1, 2, 3, 4]) sl1 = a[1:] sl5 = a[5:] assert type(sl1) == List assert sl1 == List([2, 3, 4]) assert type(sl5) == List assert sl5 == List([]) def test_hydict_methods(): hydict = Dict(["a", 1, "z", 9, "b", 2, "a", 3, "c", 4]) assert hydict.items() == [("a", 1), ("z", 9), ("b", 2), ("a", 3), ("c", 4)] assert hydict.keys() == ["a", "z", "b", "a", "c"] assert hydict.values() == [1, 9, 2, 3, 4] def test_set(): assert list(Set([3, 1, 2, 2])) == [3, 1, 2, 2] def test_equality(): # https://github.com/hylang/hy/issues/1363 assert String("foo") == String("foo") assert String("foo") == hy.as_model("foo") assert String("foo") != String("fo") assert String("foo") != "foo" assert "foo" != String("foo") assert Symbol("foo") == Symbol("foo") assert Symbol("foo") != Symbol("fo") assert Symbol("foo") != String("foo") assert Symbol("foo") != "foo" assert Integer(5) == Integer(5) assert Integer(5) == hy.as_model(5) assert Integer(5) != Integer(6) assert Integer(5) != 5 assert 5 != Integer(5) l = [Integer(1), Integer(2)] assert List(l) == List(l) assert List(l) == hy.as_model(l) assert List(l) != List(list(reversed(l))) assert List(l) != List([Integer(1), Integer(3)]) assert List(l) != l assert List(l) != tuple(l) def test_number_model_copy(): i = Integer(42) assert i == copy.copy(i) assert i == copy.deepcopy(i) f = Float(42.0) assert f == copy.copy(f) assert f == copy.deepcopy(f) c = Complex(42j) assert c == copy.copy(c) assert c == copy.deepcopy(c) PRETTY_STRINGS = { k % ("[1.0] {1.0} (1.0) #{1.0}",): v.format( """ hy.models.List([ hy.models.Float(1.0)]), hy.models.Dict([ hy.models.Float(1.0) # odd ]), hy.models.Expression([ hy.models.Float(1.0)]), hy.models.Set([ hy.models.Float(1.0)])""" ) for k, v in {"[%s]": "hy.models.List([{}])", "#{%s}": "hy.models.Set([{}])"}.items() } PRETTY_STRINGS.update( { "{[1.0] {1.0} (1.0) #{1.0}}": """hy.models.Dict([ hy.models.List([ hy.models.Float(1.0)]), hy.models.Dict([ hy.models.Float(1.0) # odd ]) , hy.models.Expression([ hy.models.Float(1.0)]), hy.models.Set([ hy.models.Float(1.0)]) ])""", "[1.0 1j [] {} () #{}]": """hy.models.List([ hy.models.Float(1.0), hy.models.Complex(1j), hy.models.List(), hy.models.Dict(), hy.models.Expression(), hy.models.Set()])""", "{{1j 2j} {1j 2j [][1j]} {[1j][] 1j 2j} {[1j][1j]}}": """hy.models.Dict([ hy.models.Dict([ hy.models.Complex(1j), hy.models.Complex(2j)]), hy.models.Dict([ hy.models.Complex(1j), hy.models.Complex(2j), hy.models.List(), hy.models.List([ hy.models.Complex(1j)]) ]) , hy.models.Dict([ hy.models.List([ hy.models.Complex(1j)]), hy.models.List() , hy.models.Complex(1j), hy.models.Complex(2j)]), hy.models.Dict([ hy.models.List([ hy.models.Complex(1j)]), hy.models.List([ hy.models.Complex(1j)]) ]) ])""", } ) def test_compound_model_repr(): HY_LIST_MODELS = (Expression, Dict, Set, List) with pretty(False): for model in HY_LIST_MODELS: assert eval(repr(model())).__class__ is model assert eval(repr(model([1, 2]))) == model([1, 2]) assert eval(repr(model([1, 2, 3]))) == model([1, 2, 3]) for k, v in PRETTY_STRINGS.items(): # `str` should be pretty, even under `pretty(False)`. assert str(hy.read(k)) == v for k in PRETTY_STRINGS.keys(): assert eval(repr(hy.read(k))) == hy.read(k) with pretty(True): for model in HY_LIST_MODELS: assert eval(repr(model())).__class__ is model assert eval(repr(model([1, 2]))) == model([1, 2]) assert eval(repr(model([1, 2, 3]))) == model([1, 2, 3]) for k, v in PRETTY_STRINGS.items(): assert repr(hy.read(k)) == v def test_recursive_model_detection(): """Check for self-references: https://github.com/hylang/hy/issues/2153 """ self_ref_list = [1, 2, 3] self_ref_dict = {1: 1, 2: 2} self_ref_list[1] = self_ref_list self_ref_dict[2] = self_ref_dict mutually_ref_list = [1, 2, 3] mutually_ref_dict = {1: 1, 2: 2} mutually_ref_list[1] = mutually_ref_dict mutually_ref_dict[2] = mutually_ref_list for structure in [ self_ref_list, self_ref_dict, mutually_ref_list, mutually_ref_dict, ]: with pytest.raises(HyWrapperError) as exc: as_model(structure) assert "Self-referential" in str(exc) hy-1.0.0/tests/test_positions.py000066400000000000000000000014271467401276000167450ustar00rootroot00000000000000'''Check that positioning attributes for AST nodes (which Python ultimately uses for tracebacks) are set correctly.''' import ast from hy import read_many from hy.compiler import hy_compile def cpl(string): '''Compile the Hy `string` and get its final body element. A newline is prepended so that line 1 is guaranteed to be the wrong position for generated nodes.''' return hy_compile(read_many('\n' + string), __name__).body[-1] def test_do_mac(): # https://github.com/hylang/hy/issues/2424 x = cpl("(do-mac '9)") assert isinstance(x, ast.Expr) assert x.lineno == 2 def test_defmacro_raise(): # https://github.com/hylang/hy/issues/2424 x = cpl("(defmacro m [] '(do (raise)))\n(m)") assert isinstance(x, ast.Raise) assert x.lineno == 3 hy-1.0.0/tests/test_reader.py000066400000000000000000000503231467401276000161570ustar00rootroot00000000000000import sys import traceback from math import isnan import pytest from hy import PrematureEndOfInput from hy.errors import hy_exc_handler from hy.models import ( Bytes, Complex, Dict, Expression, Float, FString, Integer, Keyword, List, Set, String, Symbol, ) from hy.reader import read_many from hy.reader.exceptions import LexException def tokenize(*args, **kwargs): return list(read_many(*args, **kwargs)) def peoi(): return pytest.raises(PrematureEndOfInput) def lexe(): return pytest.raises(LexException) def check_ex(einfo, expected): assert [ x.rstrip() for x in traceback.format_exception_only(einfo.type, einfo.value) ] == expected def check_trace_output(capsys, execinfo, expected): sys.__excepthook__(execinfo.type, execinfo.value, execinfo.tb) captured_wo_filtering = capsys.readouterr()[-1].strip("\n") hy_exc_handler(execinfo.type, execinfo.value, execinfo.tb) captured_w_filtering = capsys.readouterr()[-1].strip("\n") output = [x.rstrip() for x in captured_w_filtering.split("\n") if not (set(x) <= {" ", "^", "~"})] # Make sure the filtered frames aren't the same as the unfiltered ones. assert output != captured_wo_filtering.split("\n") # Remove the origin frame lines. assert output[5:] == expected def test_lex_exception(): """Ensure tokenize throws a fit on a partial input""" with peoi(): tokenize("(foo") with peoi(): tokenize("{foo bar") with peoi(): tokenize("(defn foo [bar]") with peoi(): tokenize('(foo "bar') def test_unbalanced_exception(): """Ensure the tokenization fails on unbalanced expressions""" with lexe(): tokenize("(bar))") with lexe(): tokenize("(baz [quux]])") def test_lex_single_quote_err(): # https://github.com/hylang/hy/issues/1252 with lexe() as execinfo: tokenize("' ") assert type(execinfo.value) is PrematureEndOfInput assert execinfo.value.msg == "Premature end of input while attempting to parse one form" def test_lex_expression_symbols(): """Make sure that expressions produce symbols""" objs = tokenize("(foo bar)") assert objs == [Expression([Symbol("foo"), Symbol("bar")])] def test_symbol_and_sugar(): # https://github.com/hylang/hy/issues/1798 s = Symbol def e(*x): return Expression(x) for char, head in ( ("'", "quote"), ("`", "quasiquote"), ("~", "unquote"), ("~@", "unquote-splice"), ): for string in (f"a{s1}{char}{s2}b" for s1 in ("", " ") for s2 in ("", " ")): assert tokenize(string) == [s("a"), e(s(head), s("b"))] assert tokenize("a~ @b") == tokenize("a ~ @b") == [s("a"), e(s("unquote"), s("@b"))] def test_lex_expression_strings(): """Test that expressions can produce strings""" objs = tokenize('(foo "bar")') assert objs == [Expression([Symbol("foo"), String("bar")])] def test_lex_expression_integer(): """Make sure expressions can produce integers""" objs = tokenize("(foo 2)") assert objs == [Expression([Symbol("foo"), Integer(2)])] def test_lex_symbols(): """Make sure that symbols are valid expressions""" objs = tokenize("foo ") assert objs == [Symbol("foo")] def test_lex_strings(): """Make sure that strings are valid expressions""" objs = tokenize('"foo"') assert objs == [String("foo")] # Make sure backslash-escaped newlines work (see issue #831) objs = tokenize( r""" "a\ bc" """ ) assert objs == [String("abc")] def test_lex_strings_exception(): """Make sure tokenize throws when codec can't decode some bytes""" with lexe() as execinfo: tokenize('"\\x8"') check_ex( execinfo, [ ' File "", line 1', ' "\\x8"', " ^", "hy.reader.exceptions.LexException:" " 'unicodeescape' codec can't decode bytes in position 0-2:" " truncated \\xXX escape", ], ) def test_lex_bracket_strings(): objs = tokenize("#[my delim[hello world]my delim]") assert objs == [String("hello world")] assert objs[0].brackets == "my delim" objs = tokenize("#[[squid]]") assert objs == [String("squid")] assert objs[0].brackets == "" def test_lex_integers(): assert tokenize("42") == [Integer(42)] assert tokenize("0x80") == [Integer(128)] assert tokenize("0o1232") == [Integer(666)] assert tokenize("0b1011101") == [Integer(93)] def test_lex_expression_float(): """Make sure expressions can produce floats""" objs = tokenize("(foo 2.)") assert objs == [Expression([Symbol("foo"), Float(2.0)])] objs = tokenize("(foo -0.5)") assert objs == [Expression([Symbol("foo"), Float(-0.5)])] objs = tokenize("(foo 1.e7)") assert objs == [Expression([Symbol("foo"), Float(1.0e7)])] def test_lex_big_float(): # https://github.com/hylang/hy/issues/1448 assert tokenize("1e900") == [Float(1e900)] assert tokenize("1e900-1e900j") == [Complex(1e900, -1e900)] def test_lex_nan_and_inf(): assert isnan(tokenize("NaN")[0]) assert tokenize("Nan") == [Symbol("Nan")] assert tokenize("nan") == [Symbol("nan")] assert tokenize("NAN") == [Symbol("NAN")] assert tokenize("Inf") == [Float(float("inf"))] assert tokenize("inf") == [Symbol("inf")] assert tokenize("INF") == [Symbol("INF")] assert tokenize("-Inf") == [Float(float("-inf"))] assert tokenize("-inf") == [Symbol("-inf")] assert tokenize("-INF") == [Symbol("-INF")] def test_lex_expression_complex(): """Make sure expressions can produce complex""" def t(x): return tokenize("(foo {})".format(x)) def f(x): return [Expression([Symbol("foo"), x])] assert t("2j") == f(Complex(2.0j)) assert t("2J") == f(Complex(2.0j)) assert t("2.j") == f(Complex(2.0j)) assert t("2.J") == f(Complex(2.0j)) assert t("-0.5j") == f(Complex(-0.5j)) assert t("1.e7j") == f(Complex(1e7j)) assert t("1.e7J") == f(Complex(1e7j)) assert t("j") == f(Symbol("j")) assert t("J") == f(Symbol("J")) assert isnan(t("NaNj")[0][1].imag) assert t("nanj") == f(Symbol("nanj")) assert t("Inf+Infj") == f(Complex(complex(float("inf"), float("inf")))) assert t("Inf-Infj") == f(Complex(complex(float("inf"), float("-inf")))) assert t("Inf-INFj") == f(Symbol("Inf-INFj")) # https://github.com/hylang/hy/issues/2521 assert isnan(t("NaNJ")[0][1].imag) assert t("nanJ") == f(Symbol("nanJ")) assert t("InfJ") == f(Complex(complex(0, float("inf")))) assert t("iNfJ") == f(Symbol("iNfJ")) assert t("Inf-INFJ") == f(Symbol("Inf-INFJ")) def test_lex_digit_separators(): assert tokenize("1_000_000") == [Integer(1000000)] assert tokenize("1,000,000") == [Integer(1000000)] assert tokenize("1,000_000") == [Integer(1000000)] assert tokenize("1_000,000") == [Integer(1000000)] # https://github.com/hylang/hy/issues/1340 assert tokenize("_42") == [Symbol("_42")] assert tokenize("0x_af") == [Integer(0xAF)] assert tokenize("0x,af") == [Integer(0xAF)] assert tokenize("0_xaf") == [Integer(0xAF)] assert tokenize("0b_010") == [Integer(0b010)] assert tokenize("0b,010") == [Integer(0b010)] assert tokenize("0o_373") == [Integer(0o373)] assert tokenize("0o,373") == [Integer(0o373)] assert tokenize("1_2._3,4") == [Float(12.34)] assert tokenize("1_2e_3,4") == [Float(12e34)] assert tokenize("1,0_00j,") == [Complex(1000j)] assert tokenize("1,,,,___,____,,__,,2__,,,__") == [Integer(12)] assert tokenize("_1,,,,___,____,,__,,2__,,,__") == [ Symbol("_1,,,,___,____,,__,,2__,,,__") ] assert tokenize("1,,,,___,____,,__,,2__,q,__") == [ Symbol("1,,,,___,____,,__,,2__,q,__") ] def test_leading_zero(): assert tokenize("0") == [Integer(0)] assert tokenize("0000") == [Integer(0)] assert tokenize("010") == [Integer(10)] assert tokenize("000010") == [Integer(10)] assert tokenize("000010.00") == [Float(10)] assert tokenize("010+000010j") == [Complex(10 + 10j)] def test_dotted_identifiers(): t = tokenize assert t("foo.bar") == t("(. foo bar)") assert t("foo.bar.baz") == t("(. foo bar baz)") assert t(".foo") == t("(. None foo)") assert t(".foo.bar.baz") == t("(. None foo bar baz)") assert t("..foo") == t("(.. None foo)") assert t("..foo.bar.baz") == t("(.. None foo bar baz)") def test_lex_bad_attrs(): with lexe() as execinfo: tokenize("1.foo") check_ex( execinfo, [ ' File "", line 1', " 1.foo", " ^", "hy.reader.exceptions.LexException: The parts of a dotted identifier must be symbols", ], ) with lexe(): tokenize("0.foo") with lexe(): tokenize("1.5.foo") with lexe(): tokenize("1e3.foo") with lexe(): tokenize("5j.foo") with lexe(): tokenize("3+5j.foo") with lexe(): tokenize("3.1+5.1j.foo") assert tokenize("j.foo") with lexe(): tokenize(":hello.foo") def test_lists(): assert tokenize("[1 2 3 4]") == [List(map(Integer, (1, 2, 3, 4)))] def test_dicts(): assert tokenize("{1 2 3 4}") == [Dict(map(Integer, (1, 2, 3, 4)))] assert tokenize("{1 (+ 1 1) 3 (+ 2 2)}") == [ Dict( ( Integer(1), Expression((Symbol("+"), Integer(1), Integer(1))), Integer(3), Expression((Symbol("+"), Integer(2), Integer(2))), ) ) ] def test_lex_column_counting(): entry = tokenize("(foo (one two))")[0] assert entry.start_line == 1 assert entry.start_column == 1 assert entry.end_line == 1 assert entry.end_column == 15 symbol = entry[0] assert symbol.start_line == 1 assert symbol.start_column == 2 assert symbol.end_line == 1 assert symbol.end_column == 4 inner_expr = entry[1] assert inner_expr.start_line == 1 assert inner_expr.start_column == 6 assert inner_expr.end_line == 1 assert inner_expr.end_column == 14 def test_lex_column_counting_with_literal_newline(): string, symbol = tokenize('"apple\nblueberry" abc') assert string.start_line == 1 assert string.start_column == 1 assert string.end_line == 2 assert string.end_column == 10 assert symbol.start_line == 2 assert symbol.start_column == 12 assert symbol.end_line == 2 assert symbol.end_column == 14 def test_lex_line_counting_multi(): """Make sure we can do multi-line tokenization""" entries = tokenize( """ (foo (one two)) (foo bar) """ ) entry = entries[0] assert entry.start_line == 2 assert entry.start_column == 1 assert entry.end_line == 2 assert entry.end_column == 15 entry = entries[1] assert entry.start_line == 3 assert entry.start_column == 1 assert entry.end_line == 3 assert entry.end_column == 9 def test_lex_line_counting_multi_inner(): """Make sure we can do multi-line tokenization (inner)""" entry = tokenize( """(foo bar)""" )[0] inner = entry[0] assert inner.start_line == 1 assert inner.start_column == 2 inner = entry[1] assert inner.start_line == 2 assert inner.start_column == 5 def test_line_counting_dotted(): # https://github.com/hylang/hy/issues/2422 x, = tokenize(";;;;;\na.b") for e in (x, *x): assert e.start_line == 2 def test_dicts(): """Ensure that we can tokenize a dict.""" objs = tokenize("{foo bar bar baz}") assert objs == [Dict([Symbol("foo"), Symbol("bar"), Symbol("bar"), Symbol("baz")])] objs = tokenize("(bar {foo bar bar baz})") assert objs == [ Expression( [ Symbol("bar"), Dict([Symbol("foo"), Symbol("bar"), Symbol("bar"), Symbol("baz")]), ] ) ] objs = tokenize("{(foo bar) (baz quux)}") assert objs == [ Dict( [ Expression([Symbol("foo"), Symbol("bar")]), Expression([Symbol("baz"), Symbol("quux")]), ] ) ] def test_sets(): """Ensure that we can tokenize a set.""" objs = tokenize("#{1 2}") assert objs == [Set([Integer(1), Integer(2)])] objs = tokenize("(bar #{foo bar baz})") assert objs == [ Expression([Symbol("bar"), Set([Symbol("foo"), Symbol("bar"), Symbol("baz")])]) ] objs = tokenize("#{(foo bar) (baz quux)}") assert objs == [ Set( [ Expression([Symbol("foo"), Symbol("bar")]), Expression([Symbol("baz"), Symbol("quux")]), ] ) ] # Duplicate items in a literal set should be okay (and should # be preserved). objs = tokenize("#{1 2 1 1 2 1}") assert objs == [Set([Integer(n) for n in [1, 2, 1, 1, 2, 1]])] assert len(objs[0]) == 6 # https://github.com/hylang/hy/issues/1120 objs = tokenize("#{a 1}") assert objs == [Set([Symbol("a"), Integer(1)])] def test_nospace(): """Ensure we can tokenize without spaces if we have to""" entry = tokenize("(foo(one two))")[0] assert entry.start_line == 1 assert entry.start_column == 1 assert entry.end_line == 1 assert entry.end_column == 14 entry = entry[1] assert entry.start_line == 1 assert entry.start_column == 5 assert entry.end_line == 1 assert entry.end_column == 13 def test_string_prefixes(): s = lambda x: tokenize(x)[0] assert s(r'b"hello"') == Bytes(b"hello") assert s(r'rb"hello"') == Bytes(b"hello") assert s(r'fr"hello"') == FString([String("hello")]) for bad in list("zRBFu") + ["bf", "rr", "rbr"]: with lexe(): s(bad + '"hello"') def test_escapes(): s = lambda x: tokenize(x)[0] # A valid escape sequence assert s(r'"foo\x5a"') == String("fooZ") assert s(r'b"foo\x5a"') == Bytes(b"fooZ") # In a raw string assert s(r'r"foo\x5a"') == String("foo\\x5a") assert s(r'rb"foo\x5a"') == Bytes(b"foo\\x5a") # An invalid escape sequence with lexe(): s(r'"foo\s"') with lexe(): s(r'b"foo\s"') # In a raw string assert s(r'r"foo\s"') == String("foo\\s") assert s(r'rb"foo\s"') == Bytes(b"foo\\s") # An escape sequence that's valid in strings, but not bytes. assert s(r'"foo\u005a"') == String("fooZ") with lexe(): s(r'b"foo\u005a"') def test_unicode_escapes(): """Ensure unicode escapes are handled correctly""" s = r'"a\xac\u1234\u20ac\U00008000"' assert len(s) == 29 entry = tokenize(s)[0] assert len(entry) == 5 assert [ord(x) for x in entry] == [97, 172, 4660, 8364, 32768] def test_complex(): """Ensure we tokenize complex numbers properly""" # This is a regression test for #143 entry = tokenize("(1j)")[0][0] assert entry == Complex("1.0j") entry = tokenize("(1J)")[0][0] assert entry == Complex("1.0j") entry = tokenize("(j)")[0][0] assert entry == Symbol("j") entry = tokenize("(J)")[0][0] assert entry == Symbol("J") 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 == [Symbol("foo")] def test_discard(): """Check that discarded terms are removed properly.""" # empty assert tokenize("") == [] # single assert tokenize("#_ 1") == [] # multiple assert tokenize("#_ 1 #_ 2") == [] assert tokenize("#_ 1 #_ 2 #_ 3") == [] # nested discard assert tokenize("#_ #_ 1 2") == [] assert tokenize("#_ #_ #_ 1 2 3") == [] # trailing assert tokenize("0") == [Integer(0)] assert tokenize("0 #_ 1") == [Integer(0)] assert tokenize("0 #_ 1 #_ 2") == [Integer(0)] # leading assert tokenize("2") == [Integer(2)] assert tokenize("#_ 1 2") == [Integer(2)] assert tokenize("#_ 0 #_ 1 2") == [Integer(2)] assert tokenize("#_ #_ 0 1 2") == [Integer(2)] # both assert tokenize("#_ 1 2 #_ 3") == [Integer(2)] assert tokenize("#_ 0 #_ 1 2 #_ #_ 3 4") == [Integer(2)] # inside assert tokenize("0 #_ 1 2") == [Integer(0), Integer(2)] assert tokenize("0 #_ 1 #_ 2 3") == [Integer(0), Integer(3)] assert tokenize("0 #_ #_ 1 2 3") == [Integer(0), Integer(3)] # in List assert tokenize("[]") == [List([])] assert tokenize("[#_ 1]") == [List([])] assert tokenize("[#_ 1 #_ 2]") == [List([])] assert tokenize("[#_ #_ 1 2]") == [List([])] assert tokenize("[0]") == [List([Integer(0)])] assert tokenize("[0 #_ 1]") == [List([Integer(0)])] assert tokenize("[0 #_ 1 #_ 2]") == [List([Integer(0)])] assert tokenize("[2]") == [List([Integer(2)])] assert tokenize("[#_ 1 2]") == [List([Integer(2)])] assert tokenize("[#_ 0 #_ 1 2]") == [List([Integer(2)])] assert tokenize("[#_ #_ 0 1 2]") == [List([Integer(2)])] # in Set assert tokenize("#{}") == [Set()] assert tokenize("#{#_ 1}") == [Set()] assert tokenize("#{0 #_ 1}") == [Set([Integer(0)])] assert tokenize("#{#_ 1 0}") == [Set([Integer(0)])] # in Dict assert tokenize("{}") == [Dict()] assert tokenize("{#_ 1}") == [Dict()] assert tokenize("{#_ 0 1 2}") == [Dict([Integer(1), Integer(2)])] assert tokenize("{1 #_ 0 2}") == [Dict([Integer(1), Integer(2)])] assert tokenize("{1 2 #_ 0}") == [Dict([Integer(1), Integer(2)])] # in Expression assert tokenize("()") == [Expression()] assert tokenize("(#_ foo)") == [Expression()] assert tokenize("(#_ foo bar)") == [Expression([Symbol("bar")])] assert tokenize("(foo #_ bar)") == [Expression([Symbol("foo")])] assert tokenize("(foo :bar 1)") == [ Expression([Symbol("foo"), Keyword("bar"), Integer(1)]) ] assert tokenize("(foo #_ :bar 1)") == [Expression([Symbol("foo"), Integer(1)])] assert tokenize("(foo :bar #_ 1)") == [Expression([Symbol("foo"), Keyword("bar")])] # discard term with nesting assert tokenize("[1 2 #_ [a b c [d e [f g] h]] 3 4]") == [ List([Integer(1), Integer(2), Integer(3), Integer(4)]) ] # discard with other prefix syntax assert tokenize("a #_ 'b c") == [Symbol("a"), Symbol("c")] assert tokenize("a '#_ b c") == [ Symbol("a"), Expression([Symbol("quote"), Symbol("c")]), ] assert tokenize("a '#_ b #_ c d") == [ Symbol("a"), Expression([Symbol("quote"), Symbol("d")]), ] assert tokenize("a '#_ #_ b c d") == [ Symbol("a"), Expression([Symbol("quote"), Symbol("d")]), ] def test_lex_exception_filtering(capsys): """Confirm that the exception filtering works for lexer errors.""" # First, test for PrematureEndOfInput with peoi() as execinfo: tokenize(" \n (foo\n \n") check_trace_output( capsys, execinfo, [ ' File "", line 2', " (foo", "hy.PrematureEndOfInput: Premature end of input while attempting to parse one form", ], ) # Now, for a generic LexException with lexe() as execinfo: tokenize(" \n\n 1.foo ") check_trace_output( capsys, execinfo, [ ' File "", line 3', " 1.foo", "hy.reader.exceptions.LexException: The parts of a dotted identifier must be symbols", ], ) def test_read_error(): """Compilation errors from code that was parsed with `hy.read` should have an arrow pointing to the source position where the error arose.""" import traceback import hy from hy.errors import HySyntaxError, hy_exc_handler with pytest.raises(HySyntaxError) as e: hy.eval(hy.read("(do (defn))")) assert "".join(traceback.format_exception_only(e.type, e.value)).startswith( ' File "", line 1\n (do (defn))\n ^' ) def test_shebang(): from hy.errors import HySyntaxError with pytest.raises(HySyntaxError): # By default, `read_many` doesn't allow a shebang. assert tokenize('#!/usr/bin/env hy\n5') assert (tokenize('#!/usr/bin/env hy\n5', skip_shebang = True) == [Integer(5)]) def test_reader_class_reprs(): "Don't show internal modules in which these classes are defined." assert repr(hy.Reader) == "" assert repr(hy.HyReader) == ""