Merge branch 'master' into letmacro
This commit is contained in:
commit
e0e664c030
1
AUTHORS
1
AUTHORS
@ -84,3 +84,4 @@
|
||||
* Andrew Silva <asilva@law.harvard.edu>
|
||||
* Zaheer Soebhan <z.soebhan@gmail.com>
|
||||
* Rob Day <rkd@rkd.me.uk>
|
||||
* Eric Kaschalk <ekaschalk@gmail.com>
|
||||
|
6
NEWS
6
NEWS
@ -22,6 +22,9 @@ Changes from 0.13.0
|
||||
* support EDN `#_` syntax to discard the next term
|
||||
* `return` has been implemented as a special form
|
||||
* `while` loops may now contain an `else` clause, like `for` loops
|
||||
* `xi` from `hy.extra.anaphoric` is now the `#%` tag macro
|
||||
* `#%` works on any expression and has a new `&kwargs` parameter `%**`
|
||||
* new `doc` macro and `#doc` tag macro
|
||||
|
||||
[ Bug Fixes ]
|
||||
* Numeric literals are no longer parsed as symbols when followed by a dot
|
||||
@ -39,11 +42,14 @@ Changes from 0.13.0
|
||||
* 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
|
||||
* `break` and `continue` now raise an error when given arguments
|
||||
instead of silently ignoring them
|
||||
* Multiple expressions are now allowed in the else clause of
|
||||
a for loop
|
||||
* `else` clauses in `for` and `while` are recognized more reliably
|
||||
* Argument destructuring no longer interferes with function docstrings.
|
||||
|
||||
[ Misc. Improvements ]
|
||||
|
@ -229,21 +229,37 @@ Returns a function which applies several forms in series from left to right. The
|
||||
=> (op 2)
|
||||
9
|
||||
|
||||
.. _xi
|
||||
.. _#%
|
||||
|
||||
xi
|
||||
#%
|
||||
==
|
||||
|
||||
Usage ``(xi body ...)``
|
||||
Usage ``#% expr``
|
||||
|
||||
Returns a function with parameters implicitly determined by the presence in the body of xi parameters. An xi symbol designates the ith parameter (1-based, e.g. x1, x2, x3, etc.), or all remaining parameters for xi itself. This is not a replacement for fn. The xi forms cannot be nested.
|
||||
Makes an expression into a function with an implicit ``%`` parameter list.
|
||||
|
||||
This is similar to Clojure's anonymous function literals (``#()``).
|
||||
A ``%i`` symbol designates the (1-based) *i* th parameter (such as ``%3``).
|
||||
Only the maximum ``%i`` determines the number of ``%i`` parameters--the
|
||||
others need not appear in the expression.
|
||||
``%*`` and ``%**`` name the ``&rest`` and ``&kwargs`` parameters, respectively.
|
||||
|
||||
.. code-block:: hy
|
||||
|
||||
=> ((xi identity [x1 x5 [x2 x3] xi x4]) 1 2 3 4 5 6 7 8)
|
||||
[1, 5, [2, 3,] (6, 7, 8), 4]
|
||||
=> (def add-10 (xi + 10 x1))
|
||||
=> (#%[%1 %6 42 [%2 %3] %* %4] 1 2 3 4 555 6 7 8)
|
||||
[1, 6, 42, [2, 3], (7, 8), 4]
|
||||
=> (#% %** :foo 2)
|
||||
{"foo": 2}
|
||||
|
||||
When used on an s-expression,
|
||||
``#%`` is similar to Clojure's anonymous function literals--``#()``.
|
||||
|
||||
.. code-block:: hy
|
||||
|
||||
=> (setv add-10 #%(+ 10 %1))
|
||||
=> (add-10 6)
|
||||
16
|
||||
|
||||
``#%`` determines the parameter list by the presence of a ``%*`` or ``%**``
|
||||
symbol and by the maximum ``%i`` symbol found *anywhere* in the expression,
|
||||
so nesting of ``#%`` forms is not recommended.
|
||||
|
||||
|
@ -501,6 +501,41 @@ Some example usage:
|
||||
``do`` can accept any number of arguments, from 1 to n.
|
||||
|
||||
|
||||
doc / #doc
|
||||
----------
|
||||
|
||||
Documentation macro and tag macro.
|
||||
Gets help for macros or tag macros, respectively.
|
||||
|
||||
.. code-block:: clj
|
||||
|
||||
=> (doc doc)
|
||||
Help on function (doc) in module hy.core.macros:
|
||||
|
||||
(doc)(symbol)
|
||||
macro documentation
|
||||
|
||||
Gets help for a macro function available in this module.
|
||||
Use ``require`` to make other macros available.
|
||||
|
||||
Use ``#doc foo`` instead for help with tag macro ``#foo``.
|
||||
Use ``(help foo)`` instead for help with runtime objects.
|
||||
|
||||
=> (doc comment)
|
||||
Help on function (comment) in module hy.core.macros:
|
||||
|
||||
(comment)(*body)
|
||||
Ignores body and always expands to None
|
||||
|
||||
=> #doc doc
|
||||
Help on function #doc in module hy.core.macros:
|
||||
|
||||
#doc(symbol)
|
||||
tag macro documentation
|
||||
|
||||
Gets help for a tag macro function available in this module.
|
||||
|
||||
|
||||
def / setv
|
||||
----------
|
||||
|
||||
|
@ -11,7 +11,7 @@ import sys
|
||||
import os
|
||||
import importlib
|
||||
|
||||
import astor.codegen
|
||||
import astor.code_gen
|
||||
|
||||
import hy
|
||||
|
||||
@ -413,17 +413,17 @@ def hy2py_main():
|
||||
else pretty_error(import_buffer_to_ast, stdin_text, module_name))
|
||||
if options.with_ast:
|
||||
if PY3 and platform.system() == "Windows":
|
||||
_print_for_windows(astor.dump(_ast))
|
||||
_print_for_windows(astor.dump_tree(_ast))
|
||||
else:
|
||||
print(astor.dump(_ast))
|
||||
print(astor.dump_tree(_ast))
|
||||
print()
|
||||
print()
|
||||
|
||||
if not options.without_python:
|
||||
if PY3 and platform.system() == "Windows":
|
||||
_print_for_windows(astor.codegen.to_source(_ast))
|
||||
_print_for_windows(astor.code_gen.to_source(_ast))
|
||||
else:
|
||||
print(astor.codegen.to_source(_ast))
|
||||
print(astor.code_gen.to_source(_ast))
|
||||
|
||||
parser.exit(0)
|
||||
|
||||
|
@ -27,6 +27,7 @@ import copy
|
||||
import inspect
|
||||
|
||||
from collections import defaultdict
|
||||
from cmath import isnan
|
||||
|
||||
if PY3:
|
||||
import builtins
|
||||
@ -376,6 +377,14 @@ def is_unpack(kind, x):
|
||||
and x[0] == "unpack_" + kind)
|
||||
|
||||
|
||||
def ends_with_else(expr):
|
||||
return (expr and
|
||||
isinstance(expr[-1], HyExpression) and
|
||||
expr[-1] and
|
||||
isinstance(expr[-1][0], HySymbol) and
|
||||
expr[-1][0] == HySymbol("else"))
|
||||
|
||||
|
||||
class HyASTCompiler(object):
|
||||
|
||||
def __init__(self, module_name):
|
||||
@ -1827,7 +1836,7 @@ class HyASTCompiler(object):
|
||||
|
||||
orel = Result()
|
||||
# (for* [] body (else …))
|
||||
if expression and expression[-1][0] == HySymbol("else"):
|
||||
if ends_with_else(expression):
|
||||
else_expr = expression.pop()
|
||||
for else_body in else_expr[1:]:
|
||||
orel += self.compile(else_body)
|
||||
@ -1856,7 +1865,7 @@ class HyASTCompiler(object):
|
||||
|
||||
orel = Result()
|
||||
# (while cond body (else …))
|
||||
if expr and expr[-1][0] == HySymbol("else"):
|
||||
if ends_with_else(expr):
|
||||
else_expr = expr.pop()
|
||||
for else_body in else_expr[1:]:
|
||||
orel += self.compile(else_body)
|
||||
@ -2094,11 +2103,27 @@ class HyASTCompiler(object):
|
||||
raise HyTypeError(cons, "Can't compile a top-level cons cell")
|
||||
|
||||
@builds(HyInteger, HyFloat, HyComplex)
|
||||
def compile_numeric_literal(self, number, building):
|
||||
def compile_numeric_literal(self, x, building):
|
||||
f = {HyInteger: long_type,
|
||||
HyFloat: float,
|
||||
HyComplex: complex}[building]
|
||||
return asty.Num(number, n=f(number))
|
||||
# Work around https://github.com/berkerpeksag/astor/issues/85 :
|
||||
# astor can't generate Num nodes with NaN, so we have
|
||||
# to build an expression that evaluates to NaN.
|
||||
def nn(number):
|
||||
return asty.Num(x, n=number)
|
||||
if isnan(x):
|
||||
def nan(): return asty.BinOp(
|
||||
x, left=nn(1e900), op=ast.Sub(), right=nn(1e900))
|
||||
if f is complex:
|
||||
return asty.Call(
|
||||
x,
|
||||
func=asty.Name(x, id="complex", ctx=ast.Load()),
|
||||
keywords=[],
|
||||
args=[nan() if isnan(x.real) else nn(x.real),
|
||||
nan() if isnan(x.imag) else nn(x.imag)])
|
||||
return nan()
|
||||
return nn(f(x))
|
||||
|
||||
@builds(HySymbol)
|
||||
def compile_symbol(self, symbol):
|
||||
|
@ -31,7 +31,7 @@
|
||||
~@body))))))
|
||||
|
||||
(defmacro if [&rest args]
|
||||
"if with elif"
|
||||
"Conditionally evaluate alternating test and then expressions."
|
||||
(setv n (len args))
|
||||
(if* n
|
||||
(if* (= n 1)
|
||||
@ -58,11 +58,11 @@
|
||||
(fn ~lambda-list ~@body))))
|
||||
|
||||
(defmacro macro-error [location reason]
|
||||
"error out properly within a macro"
|
||||
"Error out properly within a macro at `location` giving `reason`."
|
||||
`(raise (hy.errors.HyMacroExpansionError ~location ~reason)))
|
||||
|
||||
(defmacro defn [name lambda-list &rest body]
|
||||
"define a function `name` with signature `lambda-list` and body `body`"
|
||||
"Define `name` as a function with `lambda-list` signature and body `body`."
|
||||
(import hy)
|
||||
(if (not (= (type name) hy.HySymbol))
|
||||
(macro-error name "defn takes a name as first argument"))
|
||||
|
@ -22,15 +22,15 @@
|
||||
(import [hy.importer [hy-eval :as eval]])
|
||||
|
||||
(defn butlast [coll]
|
||||
"Returns coll except of last element."
|
||||
"Return an iterator of all but the last item in `coll`."
|
||||
(drop-last 1 coll))
|
||||
|
||||
(defn coll? [coll]
|
||||
"Checks whether item is a collection"
|
||||
"Check if `coll` is iterable and not a string."
|
||||
(and (iterable? coll) (not (string? coll))))
|
||||
|
||||
(defn comp [&rest fs]
|
||||
"Function composition"
|
||||
"Return the function from composing the given functions `fs`."
|
||||
(if (not fs) identity
|
||||
(= 1 (len fs)) (first fs)
|
||||
(do (setv rfs (reversed fs)
|
||||
@ -43,48 +43,48 @@
|
||||
res))))
|
||||
|
||||
(defn complement [f]
|
||||
"Create a function that reverses truth value of another function"
|
||||
"Returns a new function that returns the logically inverted result of `f`."
|
||||
(fn [&rest args &kwargs kwargs]
|
||||
(not (f #* args #** kwargs))))
|
||||
|
||||
(defn cons [a b]
|
||||
"Return a fresh cons cell with car = a and cdr = b"
|
||||
"Return a fresh cons cell with car = `a` and cdr = `b`."
|
||||
(HyCons a b))
|
||||
|
||||
(defn cons? [c]
|
||||
"Check whether c can be used as a cons object"
|
||||
"Check whether `c` is a cons cell."
|
||||
(instance? HyCons c))
|
||||
|
||||
(defn constantly [value]
|
||||
"Create a function that always returns the same value"
|
||||
"Create a new function that always returns `value` regardless of its input."
|
||||
(fn [&rest args &kwargs kwargs]
|
||||
value))
|
||||
|
||||
(defn keyword? [k]
|
||||
"Check whether k is a keyword"
|
||||
"Check whether `k` is a keyword."
|
||||
(and (instance? (type :foo) k)
|
||||
(.startswith k (get :foo 0))))
|
||||
|
||||
(defn dec [n]
|
||||
"Decrement n by 1"
|
||||
"Decrement `n` by 1."
|
||||
(- n 1))
|
||||
|
||||
(defn disassemble [tree &optional [codegen False]]
|
||||
"Return the python AST for a quoted Hy tree as a string.
|
||||
If the second argument is true, generate python code instead."
|
||||
"Return the python AST for a quoted Hy `tree` as a string.
|
||||
|
||||
If the second argument `codegen` is true, generate python code instead."
|
||||
(import astor)
|
||||
(import hy.compiler)
|
||||
|
||||
(spoof-positions tree)
|
||||
(setv compiled (hy.compiler.hy-compile tree (calling-module-name)))
|
||||
((if codegen
|
||||
astor.codegen.to_source
|
||||
astor.dump)
|
||||
astor.code-gen.to-source
|
||||
astor.dump-tree)
|
||||
compiled))
|
||||
|
||||
(defn distinct [coll]
|
||||
"Return a generator from the original collection with duplicates
|
||||
removed"
|
||||
"Return a generator from the original collection `coll` with no duplicates."
|
||||
(setv seen (set) citer (iter coll))
|
||||
(for* [val citer]
|
||||
(if (not_in val seen)
|
||||
@ -121,9 +121,8 @@
|
||||
(defn exec [$code &optional $globals $locals]
|
||||
"Execute Python code.
|
||||
|
||||
The parameter names contain weird characters to discourage calling this
|
||||
function with keyword arguments, which isn't supported by Python 3's
|
||||
`exec`."
|
||||
The parameter names contain weird characters to discourage calling this
|
||||
function with keyword arguments, which isn't supported by Python 3's `exec`."
|
||||
(if
|
||||
(none? $globals) (do
|
||||
(setv frame (._getframe sys (int 1)))
|
||||
@ -161,9 +160,9 @@
|
||||
|
||||
;; also from itertools, but not in Python2, and without func option until 3.3
|
||||
(defn accumulate [iterable &optional [func operator.add]]
|
||||
"accumulate(iterable[, func]) --> accumulate object
|
||||
"Accumulate `func` on `iterable`.
|
||||
|
||||
Return series of accumulated sums (or other binary function results)."
|
||||
Return series of accumulated sums (or other binary function results)."
|
||||
(setv it (iter iterable)
|
||||
total (next it))
|
||||
(yield total)
|
||||
@ -172,29 +171,29 @@
|
||||
(yield total)))
|
||||
|
||||
(defn drop [count coll]
|
||||
"Drop `count` elements from `coll` and yield back the rest"
|
||||
"Drop `count` elements from `coll` and yield back the rest."
|
||||
(islice coll count None))
|
||||
|
||||
(defn drop-last [n coll]
|
||||
"Return a sequence of all but the last n elements in coll."
|
||||
"Return a sequence of all but the last `n` elements in `coll`."
|
||||
(setv iters (tee coll))
|
||||
(map first (zip #* [(get iters 0)
|
||||
(drop n (get iters 1))])))
|
||||
|
||||
(defn empty? [coll]
|
||||
"Return True if `coll` is empty"
|
||||
"Check if `coll` is empty."
|
||||
(= 0 (len coll)))
|
||||
|
||||
(defn even? [n]
|
||||
"Return true if n is an even number"
|
||||
"Check if `n` is an even number."
|
||||
(= (% n 2) 0))
|
||||
|
||||
(defn every? [pred coll]
|
||||
"Return true if (pred x) is logical true for every x in coll, else false"
|
||||
"Check if `pred` is true applied to every x in `coll`."
|
||||
(all (map pred coll)))
|
||||
|
||||
(defn flatten [coll]
|
||||
"Return a single flat list expanding all members of coll"
|
||||
"Return a single flat list expanding all members of `coll`."
|
||||
(if (coll? coll)
|
||||
(_flatten coll [])
|
||||
(raise (TypeError (.format "{0!r} is not a collection" coll)))))
|
||||
@ -207,11 +206,11 @@
|
||||
result)
|
||||
|
||||
(defn float? [x]
|
||||
"Return True if x is float"
|
||||
"Check if x is float."
|
||||
(isinstance x float))
|
||||
|
||||
(defn symbol? [s]
|
||||
"Check whether s is a symbol"
|
||||
"Check if `s` is a symbol."
|
||||
(instance? HySymbol s))
|
||||
|
||||
(import [threading [Lock]])
|
||||
@ -219,6 +218,7 @@
|
||||
(setv _gensym_lock (Lock))
|
||||
|
||||
(defn gensym [&optional [g "G"]]
|
||||
"Generate a unique symbol for use in macros without accidental name clashes."
|
||||
(setv new_symbol None)
|
||||
(global _gensym_counter)
|
||||
(global _gensym_lock)
|
||||
@ -237,93 +237,95 @@
|
||||
(get f.f_globals "__name__"))
|
||||
|
||||
(defn first [coll]
|
||||
"Return first item from `coll`"
|
||||
"Return first item from `coll`."
|
||||
(next (iter coll) None))
|
||||
|
||||
(defn identity [x]
|
||||
"Returns the argument unchanged"
|
||||
"Return `x`."
|
||||
x)
|
||||
|
||||
(defn inc [n]
|
||||
"Increment n by 1"
|
||||
"Increment `n` by 1."
|
||||
(+ n 1))
|
||||
|
||||
(defn instance? [klass x]
|
||||
"Perform `isinstance` with reversed arguments."
|
||||
(isinstance x klass))
|
||||
|
||||
(defn integer [x]
|
||||
"Return Hy kind of integer"
|
||||
"Return Hy kind of integer for `x`."
|
||||
(long-type x))
|
||||
|
||||
(defn integer? [x]
|
||||
"Return True if x is an integer"
|
||||
"Check if `x` is an integer."
|
||||
(isinstance x (, int long-type)))
|
||||
|
||||
(defn integer-char? [x]
|
||||
"Return True if char `x` parses as an integer"
|
||||
"Check if char `x` parses as an integer."
|
||||
(try
|
||||
(integer? (int x))
|
||||
(except [ValueError] False)
|
||||
(except [TypeError] False)))
|
||||
|
||||
(defn interleave [&rest seqs]
|
||||
"Return an iterable of the first item in each of seqs, then the second etc."
|
||||
"Return an iterable of the first item in each of `seqs`, then the second etc."
|
||||
(chain.from-iterable (zip #* seqs)))
|
||||
|
||||
(defn interpose [item seq]
|
||||
"Return an iterable of the elements of seq separated by item"
|
||||
"Return an iterable of the elements of `seq` separated by `item`."
|
||||
(drop 1 (interleave (repeat item) seq)))
|
||||
|
||||
(defn iterable? [x]
|
||||
"Return true if x is iterable"
|
||||
"Check if `x` is an iterable."
|
||||
(isinstance x collections.Iterable))
|
||||
|
||||
(defn iterate [f x]
|
||||
"Returns an iterator repeatedly applying `f` to seed `x`.. x, f(x), f(f(x))..."
|
||||
(setv val x)
|
||||
(while True
|
||||
(yield val)
|
||||
(setv val (f val))))
|
||||
|
||||
(defn iterator? [x]
|
||||
"Return true if x is an iterator"
|
||||
"Check if `x` is an iterator."
|
||||
(isinstance x collections.Iterator))
|
||||
|
||||
(defn juxt [f &rest fs]
|
||||
"Return a function that applies each of the supplied functions to a single
|
||||
set of arguments and collects the results into a list."
|
||||
"Return a function applying each `fs` to args, collecting results in a list."
|
||||
(setv fs (cons f fs))
|
||||
(fn [&rest args &kwargs kwargs]
|
||||
(list-comp (f #* args #** kwargs) [f fs])))
|
||||
|
||||
(defn last [coll]
|
||||
"Return last item from `coll`"
|
||||
"Return last item from `coll`."
|
||||
(get (tuple coll) -1))
|
||||
|
||||
(defn list* [hd &rest tl]
|
||||
"Return a dotted list construed from the elements of the argument"
|
||||
"Return a chain of nested cons cells (dotted list) containing `hd` and `tl`."
|
||||
(if (not tl)
|
||||
hd
|
||||
(cons hd (list* #* tl))))
|
||||
|
||||
(defn macroexpand [form]
|
||||
"Return the full macro expansion of form"
|
||||
"Return the full macro expansion of `form`."
|
||||
(import hy.macros)
|
||||
|
||||
(setv name (calling-module-name))
|
||||
(hy.macros.macroexpand form (HyASTCompiler name)))
|
||||
|
||||
(defn macroexpand-1 [form]
|
||||
"Return the single step macro expansion of form"
|
||||
"Return the single step macro expansion of `form`."
|
||||
(import hy.macros)
|
||||
|
||||
(setv name (calling-module-name))
|
||||
(hy.macros.macroexpand-1 form (HyASTCompiler name)))
|
||||
|
||||
(defn merge-with [f &rest maps]
|
||||
"Returns a map that consists of the rest of the maps joined onto
|
||||
the first. If a key occurs in more than one map, the mapping(s)
|
||||
from the latter (left-to-right) will be combined with the mapping in
|
||||
the result by calling (f val-in-result val-in-latter)."
|
||||
"Return the map of `maps` joined onto the first via the function `f`.
|
||||
|
||||
If a key occurs in more than one map, the mapping(s) from the latter
|
||||
(left-to-right) will be combined with the mapping in the result by calling
|
||||
(f val-in-result val-in-latter)."
|
||||
(if (any maps)
|
||||
(do
|
||||
(defn merge-entry [m e]
|
||||
@ -337,31 +339,33 @@
|
||||
(reduce merge2 maps))))
|
||||
|
||||
(defn neg? [n]
|
||||
"Return true if n is < 0"
|
||||
"Check if `n` is < 0."
|
||||
(< n 0))
|
||||
|
||||
(defn none? [x]
|
||||
"Return true if x is None"
|
||||
"Check if `x` is None"
|
||||
(is x None))
|
||||
|
||||
(defn numeric? [x]
|
||||
"Check if `x` is an instance of numbers.Number."
|
||||
(import numbers)
|
||||
(instance? numbers.Number x))
|
||||
|
||||
(defn nth [coll n &optional [default None]]
|
||||
"Return nth item in collection or sequence, counting from 0.
|
||||
Return None if out of bounds unless specified otherwise."
|
||||
"Return `n`th item in `coll` or None (specify `default`) if out of bounds."
|
||||
(next (drop n coll) default))
|
||||
|
||||
(defn odd? [n]
|
||||
"Return true if n is an odd number"
|
||||
"Check if `n` is an odd number."
|
||||
(= (% n 2) 1))
|
||||
|
||||
(def -sentinel (object))
|
||||
(defn partition [coll &optional [n 2] step [fillvalue -sentinel]]
|
||||
"Chunks coll into n-tuples (pairs by default). The remainder, if any, is not
|
||||
included unless a fillvalue is specified. The step defaults to n, but can be
|
||||
more to skip elements, or less for a sliding window with overlap."
|
||||
"Chunk `coll` into `n`-tuples (pairs by default).
|
||||
|
||||
The remainder, if any, is not included unless `fillvalue` is specified. The step
|
||||
defaults to `n`, but can be more to skip elements, or less for a sliding window
|
||||
with overlap."
|
||||
(setv
|
||||
step (or step n)
|
||||
coll-clones (tee coll n)
|
||||
@ -372,46 +376,46 @@
|
||||
(zip-longest #* slices :fillvalue fillvalue)))
|
||||
|
||||
(defn pos? [n]
|
||||
"Return true if n is > 0"
|
||||
"Check if `n` is > 0."
|
||||
(> n 0))
|
||||
|
||||
(defn rest [coll]
|
||||
"Get all the elements of a coll, except the first."
|
||||
"Get all the elements of `coll`, except the first."
|
||||
(drop 1 coll))
|
||||
|
||||
(defn repeatedly [func]
|
||||
"Yield result of running func repeatedly"
|
||||
"Yield result of running `func` repeatedly."
|
||||
(while True
|
||||
(yield (func))))
|
||||
|
||||
(defn second [coll]
|
||||
"Return second item from `coll`"
|
||||
"Return second item from `coll`."
|
||||
(nth coll 1))
|
||||
|
||||
(defn some [pred coll]
|
||||
"Return the first logical true value of (pred x) for any x in coll, else None"
|
||||
"Return the first logical true value of applying `pred` in `coll`, else None."
|
||||
(first (filter None (map pred coll))))
|
||||
|
||||
(defn string [x]
|
||||
"Cast x as current string implementation"
|
||||
"Cast `x` as the current python verion's string implementation."
|
||||
(if-python2
|
||||
(unicode x)
|
||||
(str x)))
|
||||
|
||||
(defn string? [x]
|
||||
"Return True if x is a string"
|
||||
"Check if `x` is a string."
|
||||
(if-python2
|
||||
(isinstance x (, str unicode))
|
||||
(isinstance x str)))
|
||||
|
||||
(defn take [count coll]
|
||||
"Take `count` elements from `coll`, or the whole set if the total
|
||||
number of entries in `coll` is less than `count`."
|
||||
"Take `count` elements from `coll`."
|
||||
(islice coll None count))
|
||||
|
||||
(defn take-nth [n coll]
|
||||
"Return every nth member of coll
|
||||
raises ValueError for (not (pos? n))"
|
||||
"Return every `n`th member of `coll`.
|
||||
|
||||
Raises ValueError for (not (pos? n))."
|
||||
(if (not (pos? n))
|
||||
(raise (ValueError "n must be positive")))
|
||||
(setv citer (iter coll) skip (dec n))
|
||||
@ -421,13 +425,15 @@
|
||||
(next citer))))
|
||||
|
||||
(defn zero? [n]
|
||||
"Return true if n is 0"
|
||||
"Check if `n` equals 0."
|
||||
(= n 0))
|
||||
|
||||
(defn read [&optional [from-file sys.stdin]
|
||||
[eof ""]]
|
||||
"Read from input and returns a tokenized string. Can take a given input buffer
|
||||
to read from, and a single byte as EOF (defaults to an empty string)"
|
||||
"Read from input and returns a tokenized string.
|
||||
|
||||
Can take a given input buffer to read from, and a single byte
|
||||
as EOF (defaults to an empty string)."
|
||||
(setv buff "")
|
||||
(while True
|
||||
(setv inn (string (.readline from-file)))
|
||||
@ -441,16 +447,17 @@
|
||||
parsed)
|
||||
|
||||
(defn read-str [input]
|
||||
"Reads and tokenizes first line of input"
|
||||
"Reads and tokenizes first line of `input`."
|
||||
(read :from-file (StringIO input)))
|
||||
|
||||
(defn hyify [text]
|
||||
"Convert text to match hy identifier"
|
||||
"Convert `text` to match hy identifier."
|
||||
(.replace (string text) "_" "-"))
|
||||
|
||||
(defn keyword [value]
|
||||
"Create a keyword from the given value. Strings numbers and even objects
|
||||
with the __name__ magic will work"
|
||||
"Create a keyword from `value`.
|
||||
|
||||
Strings numbers and even objects with the __name__ magic will work."
|
||||
(if (and (string? value) (value.startswith HyKeyword.PREFIX))
|
||||
(hyify value)
|
||||
(if (string? value)
|
||||
@ -460,8 +467,10 @@
|
||||
(except [] (HyKeyword (+ ":" (string value))))))))
|
||||
|
||||
(defn name [value]
|
||||
"Convert the given value to a string. Keyword special character will be stripped.
|
||||
String will be used as is. Even objects with the __name__ magic will work"
|
||||
"Convert `value` to a string.
|
||||
|
||||
Keyword special character will be stripped. String will be used as is.
|
||||
Even objects with the __name__ magic will work."
|
||||
(if (and (string? value) (value.startswith HyKeyword.PREFIX))
|
||||
(hyify (cut value 2))
|
||||
(if (string? value)
|
||||
@ -471,7 +480,7 @@
|
||||
(except [] (string value))))))
|
||||
|
||||
(defn xor [a b]
|
||||
"Perform exclusive or between two parameters"
|
||||
"Perform exclusive or between `a` and `b`."
|
||||
(if (and a b)
|
||||
False
|
||||
(or a b)))
|
||||
|
@ -10,11 +10,14 @@
|
||||
(import [hy.models [HyList HySymbol]])
|
||||
|
||||
(defmacro as-> [head name &rest rest]
|
||||
"Expands to sequence of assignments to the provided name, starting with head.
|
||||
The previous result is thus available in the subsequent form. Returns the
|
||||
final result, and leaves the name bound to it in the local scope. This behaves
|
||||
much like the other threading macros, but requires you to specify the threading
|
||||
point per form via the name instead of always the first or last argument."
|
||||
"Beginning with `head`, expand a sequence of assignments `rest` to `name`.
|
||||
|
||||
Each assignment is passed to the subsequent form. Returns the final assignment,
|
||||
leaving the name bound to it in the local scope.
|
||||
|
||||
This behaves similarly to other threading macros, but requires specifying
|
||||
the threading point per-form via the name, rather than fixing to the first
|
||||
or last argument."
|
||||
`(do (setv
|
||||
~name ~head
|
||||
~@(interleave (repeat name) rest))
|
||||
@ -22,6 +25,10 @@
|
||||
|
||||
|
||||
(defmacro assoc [coll k1 v1 &rest other-kvs]
|
||||
"Associate key/index value pair(s) to a collection `coll` like a dict or list.
|
||||
|
||||
If more than three parameters are given, the remaining args are k/v pairs to
|
||||
be associated in pairs."
|
||||
(if (odd? (len other-kvs))
|
||||
(macro-error (last other-kvs)
|
||||
"`assoc` takes an odd number of arguments"))
|
||||
@ -37,12 +44,13 @@
|
||||
|
||||
|
||||
(defmacro with [args &rest body]
|
||||
"shorthand for nested with* loops:
|
||||
"Wrap execution of `body` within a context manager given as bracket `args`.
|
||||
|
||||
Shorthand for nested with* loops:
|
||||
(with [x foo y bar] baz) ->
|
||||
(with* [x foo]
|
||||
(with* [y bar]
|
||||
baz))"
|
||||
|
||||
baz))."
|
||||
(if (not (empty? args))
|
||||
(do
|
||||
(if (>= (len args) 2)
|
||||
@ -56,12 +64,10 @@
|
||||
|
||||
|
||||
(defmacro cond [&rest branches]
|
||||
"shorthand for nested ifs:
|
||||
(cond [foo bar] [baz quux]) ->
|
||||
(if foo
|
||||
bar
|
||||
(if baz
|
||||
quux))"
|
||||
"Build a nested if clause with each `branch` a [cond result] bracket pair.
|
||||
|
||||
The result in the bracket may be omitted, in which case the condition is also
|
||||
used as the result."
|
||||
(if (empty? branches)
|
||||
None
|
||||
(do
|
||||
@ -88,13 +94,10 @@
|
||||
|
||||
|
||||
(defmacro for [args &rest body]
|
||||
"shorthand for nested for loops:
|
||||
(for [x foo
|
||||
y bar]
|
||||
baz) ->
|
||||
(for* [x foo]
|
||||
(for* [y bar]
|
||||
baz))"
|
||||
"Build a for-loop with `args` as a [element coll] bracket pair and run `body`.
|
||||
|
||||
Args may contain multiple pairs, in which case it executes a nested for-loop
|
||||
in order of the given pairs."
|
||||
(setv body (list body))
|
||||
(if (empty? body)
|
||||
(macro-error None "`for' requires a body to evaluate"))
|
||||
@ -113,10 +116,10 @@
|
||||
|
||||
|
||||
(defmacro -> [head &rest rest]
|
||||
"Threads the head through the rest of the forms. Inserts
|
||||
head as the second item in the first form of rest. If
|
||||
there are more forms, inserts the first form as the
|
||||
second item in the second form of rest, etc."
|
||||
"Thread `head` first through the `rest` of the forms.
|
||||
|
||||
The result of the first threaded form is inserted into the first position of
|
||||
the second form, the second result is inserted into the third form, and so on."
|
||||
(setv ret head)
|
||||
(for* [node rest]
|
||||
(if (not (isinstance node HyExpression))
|
||||
@ -127,8 +130,7 @@
|
||||
|
||||
|
||||
(defmacro doto [form &rest expressions]
|
||||
"Performs a sequence of potentially mutating actions
|
||||
on an initial object, returning the resulting object"
|
||||
"Perform possibly mutating `expressions` on `form`, returning resulting obj."
|
||||
(setv f (gensym))
|
||||
(defn build-form [expression]
|
||||
(if (isinstance expression HyExpression)
|
||||
@ -140,10 +142,10 @@
|
||||
~f))
|
||||
|
||||
(defmacro ->> [head &rest rest]
|
||||
"Threads the head through the rest of the forms. Inserts
|
||||
head as the last item in the first form of rest. If there
|
||||
are more forms, inserts the first form as the last item
|
||||
in the second form of rest, etc."
|
||||
"Thread `head` last through the `rest` of the forms.
|
||||
|
||||
The result of the first threaded form is inserted into the last position of
|
||||
the second form, the second result is inserted into the third form, and so on."
|
||||
(setv ret head)
|
||||
(for* [node rest]
|
||||
(if (not (isinstance node HyExpression))
|
||||
@ -185,6 +187,7 @@
|
||||
|
||||
|
||||
(defmacro with-gensyms [args &rest body]
|
||||
"Execute `body` with `args` as bracket of names to gensym for use in macros."
|
||||
(setv syms [])
|
||||
(for* [arg args]
|
||||
(.extend syms [arg `(gensym '~arg)]))
|
||||
@ -193,6 +196,7 @@
|
||||
~@body))
|
||||
|
||||
(defmacro defmacro/g! [name args &rest body]
|
||||
"Like `defmacro`, but symbols prefixed with 'g!' are gensymed."
|
||||
(setv syms (list
|
||||
(distinct
|
||||
(filter (fn [x]
|
||||
@ -207,8 +211,9 @@
|
||||
~@body))
|
||||
|
||||
(defmacro defmacro! [name args &rest body]
|
||||
"Like defmacro/g! plus automatic once-only evaluation for o!
|
||||
parameters, which are available as the equivalent g! symbol."
|
||||
"Like `defmacro/g!`, with automatic once-only evaluation for 'o!' params.
|
||||
|
||||
Such 'o!' params are availible within `body` as the equivalent 'g!' symbol."
|
||||
(setv os (list-comp s [s args] (.startswith s "o!"))
|
||||
gs (list-comp (HySymbol (+ "g!" (cut s 2))) [s os]))
|
||||
`(defmacro/g! ~name ~args
|
||||
@ -217,7 +222,7 @@
|
||||
|
||||
|
||||
(defmacro defmain [args &rest body]
|
||||
"Write a function named \"main\" and do the if __main__ dance"
|
||||
"Write a function named \"main\" and do the 'if __main__' dance"
|
||||
(setv retval (gensym))
|
||||
`(when (= --name-- "__main__")
|
||||
(import sys)
|
||||
@ -227,6 +232,7 @@
|
||||
|
||||
|
||||
(deftag @ [expr]
|
||||
"with-decorator tag macro"
|
||||
(setv decorators (cut expr None -1)
|
||||
fndef (get expr -1))
|
||||
`(with-decorator ~@decorators ~fndef))
|
||||
@ -234,3 +240,41 @@
|
||||
(defmacro comment [&rest body]
|
||||
"Ignores body and always expands to None"
|
||||
None)
|
||||
|
||||
(defmacro doc [symbol]
|
||||
"macro documentation
|
||||
|
||||
Gets help for a macro function available in this module.
|
||||
Use ``require`` to make other macros available.
|
||||
|
||||
Use ``#doc foo`` instead for help with tag macro ``#foo``.
|
||||
Use ``(help foo)`` instead for help with runtime objects."
|
||||
`(try
|
||||
(help (. (__import__ "hy")
|
||||
macros
|
||||
_hy_macros
|
||||
[__name__]
|
||||
['~symbol]))
|
||||
(except [KeyError]
|
||||
(help (. (__import__ "hy")
|
||||
macros
|
||||
_hy_macros
|
||||
[None]
|
||||
['~symbol])))))
|
||||
|
||||
(deftag doc [symbol]
|
||||
"tag macro documentation
|
||||
|
||||
Gets help for a tag macro function available in this module."
|
||||
`(try
|
||||
(help (. (__import__ "hy")
|
||||
macros
|
||||
_hy_tag
|
||||
[__name__]
|
||||
['~symbol]))
|
||||
(except [KeyError]
|
||||
(help (. (__import__ "hy")
|
||||
macros
|
||||
_hy_tag
|
||||
[None]
|
||||
['~symbol])))))
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
|
||||
(defn + [&rest args]
|
||||
"Shadow + operator for when we need to import / map it against something"
|
||||
"Shadowed `+` operator adds `args`."
|
||||
(if
|
||||
(= (len args) 0)
|
||||
0
|
||||
@ -19,13 +19,13 @@
|
||||
(reduce operator.add args)))
|
||||
|
||||
(defn - [a1 &rest a-rest]
|
||||
"Shadow - operator for when we need to import / map it against something"
|
||||
"Shadowed `-` operator subtracts each `a-rest` from `a1`."
|
||||
(if a-rest
|
||||
(reduce operator.sub a-rest a1)
|
||||
(- a1)))
|
||||
|
||||
(defn * [&rest args]
|
||||
"Shadow * operator for when we need to import / map it against something"
|
||||
"Shadowed `*` operator multiplies `args`."
|
||||
(if
|
||||
(= (len args) 0)
|
||||
1
|
||||
@ -35,6 +35,7 @@
|
||||
(reduce operator.mul args)))
|
||||
|
||||
(defn ** [a1 a2 &rest a-rest]
|
||||
"Shadowed `**` operator takes `a1` to the power of `a2`, ..., `a-rest`."
|
||||
; We use `-foldr` instead of `reduce` because exponentiation
|
||||
; is right-associative.
|
||||
(-foldr operator.pow (+ (, a1 a2) a-rest)))
|
||||
@ -42,32 +43,40 @@
|
||||
(reduce (fn [x y] (f y x)) (cut xs None None -1)))
|
||||
|
||||
(defn / [a1 &rest a-rest]
|
||||
"Shadow / operator for when we need to import / map it against something"
|
||||
"Shadowed `/` operator divides `a1` by each `a-rest`."
|
||||
(if a-rest
|
||||
(reduce operator.truediv a-rest a1)
|
||||
(/ 1 a1)))
|
||||
|
||||
(defn // [a1 a2 &rest a-rest]
|
||||
"Shadowed `//` operator floor divides `a1` by `a2`, ..., `a-rest`."
|
||||
(reduce operator.floordiv (+ (, a2) a-rest) a1))
|
||||
|
||||
(defn % [x y]
|
||||
"Shadowed `%` operator takes `x` modulo `y`."
|
||||
(% x y))
|
||||
|
||||
(if PY35 (defn @ [a1 &rest a-rest]
|
||||
(if PY35
|
||||
(defn @ [a1 &rest a-rest]
|
||||
"Shadowed `@` operator matrix multiples `a1` by each `a-rest`."
|
||||
(reduce operator.matmul a-rest a1)))
|
||||
|
||||
(defn << [a1 a2 &rest a-rest]
|
||||
"Shadowed `<<` operator performs left-shift on `a1` by `a2`, ..., `a-rest`."
|
||||
(reduce operator.lshift (+ (, a2) a-rest) a1))
|
||||
|
||||
(defn >> [a1 a2 &rest a-rest]
|
||||
"Shadowed `>>` operator performs right-shift on `a1` by `a2`, ..., `a-rest`."
|
||||
(reduce operator.rshift (+ (, a2) a-rest) a1))
|
||||
|
||||
(defn & [a1 &rest a-rest]
|
||||
"Shadowed `&` operator performs bitwise-and on `a1` by each `a-rest`."
|
||||
(if a-rest
|
||||
(reduce operator.and_ a-rest a1)
|
||||
a1))
|
||||
|
||||
(defn | [&rest args]
|
||||
"Shadowed `|` operator performs bitwise-or on `a1` by each `a-rest`."
|
||||
(if
|
||||
(= (len args) 0)
|
||||
0
|
||||
@ -77,9 +86,11 @@
|
||||
(reduce operator.or_ args)))
|
||||
|
||||
(defn ^ [x y]
|
||||
"Shadowed `^` operator performs bitwise-xor on `x` and `y`."
|
||||
(^ x y))
|
||||
|
||||
(defn ~ [x]
|
||||
"Shadowed `~` operator performs bitwise-negation on `x`."
|
||||
(~ x))
|
||||
|
||||
(defn comp-op [op a1 a-rest]
|
||||
@ -89,29 +100,32 @@
|
||||
(list-comp (op x y) [(, x y) (zip (+ (, a1) a-rest) a-rest)]))
|
||||
True))
|
||||
(defn < [a1 &rest a-rest]
|
||||
"Shadow < operator for when we need to import / map it against something"
|
||||
"Shadowed `<` operator perform lt comparison on `a1` by each `a-rest`."
|
||||
(comp-op operator.lt a1 a-rest))
|
||||
(defn <= [a1 &rest a-rest]
|
||||
"Shadow <= operator for when we need to import / map it against something"
|
||||
"Shadowed `<=` operator perform le comparison on `a1` by each `a-rest`."
|
||||
(comp-op operator.le a1 a-rest))
|
||||
(defn = [a1 &rest a-rest]
|
||||
"Shadow = operator for when we need to import / map it against something"
|
||||
"Shadowed `=` operator perform eq comparison on `a1` by each `a-rest`."
|
||||
(comp-op operator.eq a1 a-rest))
|
||||
(defn is [a1 &rest a-rest]
|
||||
"Shadowed `is` keyword perform is on `a1` by each `a-rest`."
|
||||
(comp-op operator.is_ a1 a-rest))
|
||||
(defn != [a1 a2 &rest a-rest]
|
||||
"Shadow != operator for when we need to import / map it against something"
|
||||
"Shadowed `!=` operator perform neq comparison on `a1` by `a2`, ..., `a-rest`."
|
||||
(comp-op operator.ne a1 (+ (, a2) a-rest)))
|
||||
(defn is-not [a1 a2 &rest a-rest]
|
||||
"Shadowed `is-not` keyword perform is-not on `a1` by `a2`, ..., `a-rest`."
|
||||
(comp-op operator.is-not a1 (+ (, a2) a-rest)))
|
||||
(defn >= [a1 &rest a-rest]
|
||||
"Shadow >= operator for when we need to import / map it against something"
|
||||
"Shadowed `>=` operator perform ge comparison on `a1` by each `a-rest`."
|
||||
(comp-op operator.ge a1 a-rest))
|
||||
(defn > [a1 &rest a-rest]
|
||||
"Shadow > operator for when we need to import / map it against something"
|
||||
"Shadowed `>` operator perform gt comparison on `a1` by each `a-rest`."
|
||||
(comp-op operator.gt a1 a-rest))
|
||||
|
||||
(defn and [&rest args]
|
||||
"Shadowed `and` keyword perform and on `args`."
|
||||
(if
|
||||
(= (len args) 0)
|
||||
True
|
||||
@ -121,6 +135,7 @@
|
||||
(reduce (fn [x y] (and x y)) args)))
|
||||
|
||||
(defn or [&rest args]
|
||||
"Shadowed `or` keyword perform or on `args`."
|
||||
(if
|
||||
(= (len args) 0)
|
||||
None
|
||||
@ -130,15 +145,19 @@
|
||||
(reduce (fn [x y] (or x y)) args)))
|
||||
|
||||
(defn not [x]
|
||||
"Shadowed `not` keyword perform not on `x`."
|
||||
(not x))
|
||||
|
||||
(defn in [x y]
|
||||
"Shadowed `in` keyword perform `x` in `y`."
|
||||
(in x y))
|
||||
|
||||
(defn not-in [x y]
|
||||
"Shadowed `not in` keyword perform `x` not in `y`."
|
||||
(not-in x y))
|
||||
|
||||
(defn get [coll key1 &rest keys]
|
||||
"Access item in `coll` indexed by `key1`, with optional `keys` nested-access."
|
||||
(setv coll (get coll key1))
|
||||
(for* [k keys]
|
||||
(setv coll (get coll k)))
|
||||
|
10
hy/errors.py
10
hy/errors.py
@ -43,6 +43,11 @@ class HyTypeError(TypeError):
|
||||
|
||||
def __str__(self):
|
||||
|
||||
result = ""
|
||||
|
||||
if all(getattr(self.expression, x, None) is not None
|
||||
for x in ("start_line", "start_column", "end_column")):
|
||||
|
||||
line = self.expression.start_line
|
||||
start = self.expression.start_column
|
||||
end = self.expression.end_column
|
||||
@ -56,8 +61,6 @@ class HyTypeError(TypeError):
|
||||
else:
|
||||
length = len(source[0]) - start
|
||||
|
||||
result = ""
|
||||
|
||||
result += ' File "%s", line %d, column %d\n\n' % (self.filename,
|
||||
line,
|
||||
start)
|
||||
@ -79,6 +82,9 @@ class HyTypeError(TypeError):
|
||||
result += ' %s\n' % colored.red("".join(source[-1]))
|
||||
result += ' %s\n' % colored.green('-'*(end-1) + '^')
|
||||
|
||||
else:
|
||||
result += ' File "%s", unknown location\n' % self.filename
|
||||
|
||||
result += colored.yellow("%s: %s\n\n" %
|
||||
(self.__class__.__name__,
|
||||
self.message))
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
;;; These macros make writing functional programs more concise
|
||||
|
||||
|
||||
(defmacro ap-if [test-form then-form &optional else-form]
|
||||
`(do
|
||||
(setv it ~test-form)
|
||||
@ -112,26 +111,32 @@
|
||||
"Returns a function which is the composition of several forms."
|
||||
`(fn [var] (ap-pipe var ~@forms)))
|
||||
|
||||
(defmacro xi [&rest body]
|
||||
"Returns a function with parameters implicitly determined by the presence in
|
||||
the body of xi parameters. An xi symbol designates the ith parameter
|
||||
(1-based, e.g. x1, x2, x3, etc.), or all remaining parameters for xi itself.
|
||||
This is not a replacement for fn. The xi forms cannot be nested. "
|
||||
(setv flatbody (flatten body))
|
||||
`(fn [;; generate all xi symbols up to the maximum found in body
|
||||
~@(genexpr (HySymbol (+ "x"
|
||||
(str i)))
|
||||
[i (range 1
|
||||
;; find the maximum xi
|
||||
(inc (max (+ (list-comp (int (cut a 1))
|
||||
[a flatbody]
|
||||
(and (symbol? a)
|
||||
(.startswith a 'x)
|
||||
(.isdigit (cut a 1))))
|
||||
[0]))))])
|
||||
;; generate the &rest parameter only if 'xi is present in body
|
||||
~@(if (in 'xi flatbody)
|
||||
'(&rest xi)
|
||||
'())]
|
||||
(~@body)))
|
||||
(deftag % [expr]
|
||||
"Makes an expression into a function with an implicit `%` parameter list.
|
||||
|
||||
A `%i` symbol designates the (1-based) ith parameter (such as `%3`).
|
||||
Only the maximum `%i` determines the number of `%i` parameters--the
|
||||
others need not appear in the expression.
|
||||
`%*` and `%**` name the `&rest` and `&kwargs` parameters, respectively.
|
||||
|
||||
Nesting of `#%` forms is not recommended."
|
||||
(setv %symbols (set-comp a
|
||||
[a (flatten [expr])]
|
||||
(and (symbol? a)
|
||||
(.startswith a '%))))
|
||||
`(fn [;; generate all %i symbols up to the maximum found in expr
|
||||
~@(genexpr (HySymbol (+ "%" (str i)))
|
||||
[i (range 1 (-> (list-comp (int (cut a 1))
|
||||
[a %symbols]
|
||||
(.isdigit (cut a 1)))
|
||||
(or (, 0))
|
||||
max
|
||||
inc))])
|
||||
;; generate the &rest parameter only if '%* is present in expr
|
||||
~@(if (in '%* %symbols)
|
||||
'(&rest %*))
|
||||
;; similarly for &kwargs and %**
|
||||
~@(if (in '%** %symbols)
|
||||
'(&kwargs %**))]
|
||||
~expr))
|
||||
|
||||
|
2
setup.py
2
setup.py
@ -30,7 +30,7 @@ class Install(install):
|
||||
"." + filename[:-len(".hy")])
|
||||
install.run(self)
|
||||
|
||||
install_requires = ['rply>=0.7.5', 'astor>=0.5', 'clint>=0.4']
|
||||
install_requires = ['rply>=0.7.5', 'astor>=0.6', 'clint>=0.4']
|
||||
if os.name == 'nt':
|
||||
install_requires.append('pyreadline>=2.1')
|
||||
|
||||
|
@ -99,28 +99,46 @@
|
||||
(assert-equal ((ap-compose (+ it 1) (* it 3)) 2) 9)
|
||||
(assert-equal ((ap-compose (list (rest it)) (len it)) [4 5 6 7]) 3))
|
||||
|
||||
(defn test-xi []
|
||||
"NATIVE: testing xi forms"
|
||||
(defn test-tag-fn []
|
||||
"NATIVE: testing #%() forms"
|
||||
;; test ordering
|
||||
(assert-equal ((xi / x1 x2) 2 4) 0.5)
|
||||
(assert-equal ((xi / x2 x1) 2 4) 2)
|
||||
(assert-equal ((xi identity (, x5 x4 x3 x2 x1)) 1 2 3 4 5) (, 5 4 3 2 1))
|
||||
(assert-equal ((xi identity (, x1 x2 x3 x4 x5)) 1 2 3 4 5) (, 1 2 3 4 5))
|
||||
(assert-equal ((xi identity (, x1 x5 x2 x3 x4)) 1 2 3 4 5) (, 1 5 2 3 4))
|
||||
(assert-equal (#%(/ %1 %2) 2 4) 0.5)
|
||||
(assert-equal (#%(/ %2 %1) 2 4) 2)
|
||||
(assert-equal (#%(identity (, %5 %4 %3 %2 %1)) 1 2 3 4 5) (, 5 4 3 2 1))
|
||||
(assert-equal (#%(identity (, %1 %2 %3 %4 %5)) 1 2 3 4 5) (, 1 2 3 4 5))
|
||||
(assert-equal (#%(identity (, %1 %5 %2 %3 %4)) 1 2 3 4 5) (, 1 5 2 3 4))
|
||||
;; test &rest
|
||||
(assert-equal ((xi sum xi) 1 2 3) 6)
|
||||
(assert-equal ((xi identity (, x1 xi)) 10 1 2 3) (, 10 (, 1 2 3)))
|
||||
(assert-equal (#%(sum %*) 1 2 3) 6)
|
||||
(assert-equal (#%(identity (, %1 %*)) 10 1 2 3) (, 10 (, 1 2 3)))
|
||||
;; no parameters
|
||||
(assert-equal ((xi list)) [])
|
||||
(assert-equal ((xi identity "Hy!")) "Hy!")
|
||||
(assert-equal ((xi identity "xi")) "xi")
|
||||
(assert-equal ((xi + "Hy " "world!")) "Hy world!")
|
||||
(assert-equal (#%(list)) [])
|
||||
(assert-equal (#%(identity "Hy!")) "Hy!")
|
||||
(assert-equal (#%(identity "%*")) "%*")
|
||||
(assert-equal (#%(+ "Hy " "world!")) "Hy world!")
|
||||
;; test skipped parameters
|
||||
(assert-equal ((xi identity [x3 x1]) 1 2 3) [3 1])
|
||||
(assert-equal (#%(identity [%3 %1]) 1 2 3) [3 1])
|
||||
;; test nesting
|
||||
(assert-equal ((xi identity [x1 (, x2 [x3] "Hy" [xi])]) 1 2 3 4 5)
|
||||
(assert-equal (#%(identity [%1 (, %2 [%3] "Hy" [%*])]) 1 2 3 4 5)
|
||||
[1 (, 2 [3] "Hy" [(, 4 5)])])
|
||||
;; test arg as function
|
||||
(assert-equal ((xi x1 2 4) +) 6)
|
||||
(assert-equal ((xi x1 2 4) -) -2)
|
||||
(assert-equal ((xi x1 2 4) /) 0.5))
|
||||
(assert-equal (#%(%1 2 4) +) 6)
|
||||
(assert-equal (#%(%1 2 4) -) -2)
|
||||
(assert-equal (#%(%1 2 4) /) 0.5)
|
||||
;; test &rest &kwargs
|
||||
(assert-equal (#%(, %* %**) 1 2 :a 'b)
|
||||
(, (, 1 2)
|
||||
(dict :a 'b)))
|
||||
;; test other expression types
|
||||
(assert-equal (#% %* 1 2 3)
|
||||
(, 1 2 3))
|
||||
(assert-equal (#% %** :foo 2)
|
||||
(dict :foo 2))
|
||||
(assert-equal (#%[%3 %2 %1] 1 2 3)
|
||||
[3 2 1])
|
||||
(assert-equal (#%{%1 %2} 10 100)
|
||||
{10 100})
|
||||
(assert-equal (#% #{%3 %2 %1} 1 3 2)
|
||||
#{3 1 2}) ; sets are not ordered.
|
||||
(assert-equal (#% "%1")
|
||||
"%1"))
|
||||
|
||||
|
@ -219,8 +219,24 @@
|
||||
(else
|
||||
(+= count 1)
|
||||
(+= count 10)))
|
||||
|
||||
(assert (= count 161))
|
||||
|
||||
; don't be fooled by constructs that look like else
|
||||
(setv s "")
|
||||
(setv (get (globals) "else") True)
|
||||
(for [x "abcde"]
|
||||
(+= s x)
|
||||
[else (+= s "_")])
|
||||
(assert (= s "a_b_c_d_e_"))
|
||||
|
||||
(setv s "")
|
||||
(setv (get (globals) "else") True)
|
||||
(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)))))
|
||||
(list-comp y [x [[1] [2 3]] y x])))
|
||||
(assert (= (list ((fn [] (for [x [[1] [2 3]] y x z (range 5)] (yield z)))))
|
||||
@ -308,7 +324,26 @@
|
||||
(while True
|
||||
(break)
|
||||
(else (setv myvariable 53)))
|
||||
(assert (= myvariable 26)))
|
||||
(assert (= myvariable 26))
|
||||
|
||||
; don't be fooled by constructs that look like else clauses
|
||||
(setv x 2)
|
||||
(setv a [])
|
||||
(setv (get (globals) "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-branching []
|
||||
@ -1515,13 +1550,23 @@
|
||||
"NATIVE: Test the disassemble function"
|
||||
(if PY35
|
||||
(assert (= (disassemble '(do (leaky) (leaky) (macros)))
|
||||
"Module(\n body=[Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),\n Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),\n Expr(value=Call(func=Name(id='macros'), args=[], keywords=[]))])"))
|
||||
"Module(
|
||||
body=[Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
|
||||
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
|
||||
Expr(value=Call(func=Name(id='macros'), args=[], keywords=[]))])"))
|
||||
(assert (= (disassemble '(do (leaky) (leaky) (macros)))
|
||||
"Module(\n body=[\n Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),\n Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),\n Expr(value=Call(func=Name(id='macros'), args=[], keywords=[], starargs=None, kwargs=None))])")))
|
||||
"Module(
|
||||
body=[
|
||||
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),
|
||||
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),
|
||||
Expr(value=Call(func=Name(id='macros'), args=[], keywords=[], starargs=None, kwargs=None))])")))
|
||||
(assert (= (disassemble '(do (leaky) (leaky) (macros)) True)
|
||||
"leaky()\nleaky()\nmacros()"))
|
||||
"leaky()
|
||||
leaky()
|
||||
macros()
|
||||
"))
|
||||
(assert (= (re.sub r"[L() ]" "" (disassemble `(+ ~(+ 1 1) 40) True))
|
||||
"2+40")))
|
||||
"2+40\n")))
|
||||
|
||||
|
||||
(defn test-attribute-access []
|
||||
|
@ -144,7 +144,7 @@
|
||||
|
||||
(defn test-gensym-in-macros []
|
||||
(import ast)
|
||||
(import [astor.codegen [to_source]])
|
||||
(import [astor.code-gen [to-source]])
|
||||
(import [hy.importer [import_buffer_to_ast]])
|
||||
(setv macro1 "(defmacro nif [expr pos zero neg]
|
||||
(setv g (gensym))
|
||||
@ -170,7 +170,7 @@
|
||||
|
||||
(defn test-with-gensym []
|
||||
(import ast)
|
||||
(import [astor.codegen [to_source]])
|
||||
(import [astor.code-gen [to-source]])
|
||||
(import [hy.importer [import_buffer_to_ast]])
|
||||
(setv macro1 "(defmacro nif [expr pos zero neg]
|
||||
(with-gensyms [a]
|
||||
@ -194,7 +194,7 @@
|
||||
|
||||
(defn test-defmacro-g! []
|
||||
(import ast)
|
||||
(import [astor.codegen [to_source]])
|
||||
(import [astor.code-gen [to-source]])
|
||||
(import [hy.importer [import_buffer_to_ast]])
|
||||
(setv macro1 "(defmacro/g! nif [expr pos zero neg]
|
||||
`(do
|
||||
@ -223,7 +223,7 @@
|
||||
(defn test-defmacro! []
|
||||
;; defmacro! must do everything defmacro/g! can
|
||||
(import ast)
|
||||
(import [astor.codegen [to_source]])
|
||||
(import [astor.code-gen [to-source]])
|
||||
(import [hy.importer [import_buffer_to_ast]])
|
||||
(setv macro1 "(defmacro! nif [expr pos zero neg]
|
||||
`(do
|
||||
|
@ -72,7 +72,7 @@ def test_bin_hy_stdin():
|
||||
|
||||
output, _ = run_cmd("hy --spy", '(koan)')
|
||||
assert "monk" in output
|
||||
assert "\\n Ummon" in output
|
||||
assert "\n Ummon" in output
|
||||
|
||||
# --spy should work even when an exception is thrown
|
||||
output, _ = run_cmd("hy --spy", '(foof)')
|
||||
@ -138,6 +138,16 @@ def test_bin_hy_stdin_except_do():
|
||||
assert "zzz" in output
|
||||
|
||||
|
||||
def test_bin_hy_stdin_unlocatable_hytypeerror():
|
||||
# https://github.com/hylang/hy/issues/1412
|
||||
# The chief test of interest here is the returncode assertion
|
||||
# inside run_cmd.
|
||||
_, err = run_cmd("hy", """
|
||||
(import hy.errors)
|
||||
(raise (hy.errors.HyTypeError '[] (+ "A" "Z")))""")
|
||||
assert "AZ" in err
|
||||
|
||||
|
||||
def test_bin_hy_stdin_bad_repr():
|
||||
# https://github.com/hylang/hy/issues/1389
|
||||
output, err = run_cmd("hy", """
|
||||
@ -185,7 +195,7 @@ def test_bin_hy_icmd_file():
|
||||
|
||||
def test_bin_hy_icmd_and_spy():
|
||||
output, _ = run_cmd("hy -i \"(+ [] [])\" --spy", "(+ 1 1)")
|
||||
assert "([] + [])" in output
|
||||
assert "[] + []" in output
|
||||
|
||||
|
||||
def test_bin_hy_missing_file():
|
||||
|
Loading…
Reference in New Issue
Block a user