Merge branch 'master' into pr/300

This commit is contained in:
Paul Tagliamonte 2013-10-10 17:42:51 -04:00
commit 187268c960
22 changed files with 397 additions and 312 deletions

View File

@ -17,3 +17,4 @@
* Bob Tolbert <bob@tolbert.org>
* Ralph Möritz <ralph.moeritz@outlook.com>
* Josh McLaughlin <josh@phear.cc>
* Berker Peksag <berker.peksag@gmail.com>

View File

@ -2,6 +2,8 @@
Hacking on hy
===============
.. highlight:: bash
Join our hyve!
==============
@ -21,24 +23,39 @@ Hack!
Do this:
1. create a `Python virtual environment
<https://pypi.python.org/pypi/virtualenv>`_
2. (optional) go to https://github.com/paultag/hy and fork it
3. get the source code::
1. create a `virtual environment
<https://pypi.python.org/pypi/virtualenv>`_::
$ git clone git://github.com/paultag/hy.git
$ virtualenv venv
(or use your fork)
4. install for hacking::
and activate it::
$ python setup.py develop
$ . venv/bin/activate
5. install other develop-y requirements::
or use `virtualenvwrapper <http://virtualenvwrapper.readthedocs.org/en/latest/#introduction>`_
to create and manage your virtual environment::
$ mkvirtualenv hy
$ workon hy
2. get the source code::
$ git clone https://github.com/hylang/hy.git
or use your fork::
$ git clone git@github.com:<YOUR_USERNAME>/hy.git
3. install for hacking::
$ cd hy/
$ pip install -e .
4. install other develop-y requirements::
$ pip install -r requirements-dev.txt
6. do awesome things; make someone shriek in delight/disgust at what
you have wrought
5. do awesome things; make someone shriek in delight/disgust at what
you have wrought.
Test!
@ -60,7 +77,7 @@ Document!
Documentation is located in ``docs/``. We use `Sphinx
<http://sphinx-doc.org/>`_.
To build the docs in html::
To build the docs in HTML::
$ cd docs
$ make html

View File

@ -24,8 +24,8 @@ languages.
* UTF-8 entities will be encoded using
`punycode <http://en.wikipedia.org/wiki/Punycode>`_ and prefixed with
`hy_`. For instance, `⚘` will become `hy_w7h`, and `♥` will become
`hy_g6h`.
`hy_`. For instance, `⚘` will become `hy_w7h`, `♥` will become `hy_g6h`,
and `i♥u` will become `hy_iu_t0x`.
* Symbols that contain dashes will have them replaced with underscores. For
example, `render-template` will become `render_template`.

View File

@ -19,10 +19,20 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
try:
import __builtin__ as builtins
except ImportError:
import builtins # NOQA
import sys
PY3 = sys.version_info[0] >= 3
if sys.version_info[0] >= 3:
if PY3:
str_type = str
else:
str_type = unicode # NOQA
if PY3:
long_type = int
else:
long_type = long # NOQA

View File

@ -41,11 +41,7 @@ from hy.models.expression import HyExpression
from hy.models.string import HyString
from hy.models.symbol import HySymbol
try:
import __builtin__ as builtins
except ImportError:
import builtins
from hy._compat import builtins
class HyQuitter(object):

View File

@ -36,8 +36,8 @@ from hy.models.float import HyFloat
from hy.models.list import HyList
from hy.models.dict import HyDict
from hy.macros import require, process
from hy.util import str_type
from hy.macros import require, macroexpand
from hy._compat import str_type
import hy.importer
import traceback
@ -419,7 +419,7 @@ class HyASTCompiler(object):
def compile(self, tree):
try:
tree = process(tree, self.module_name)
tree = macroexpand(tree, self.module_name)
_type = type(tree)
ret = self.compile_atom(_type, tree)
if ret:
@ -610,7 +610,7 @@ class HyASTCompiler(object):
level)
imports.update(f_imports)
if splice:
to_add = f_contents
to_add = HyExpression([HySymbol("list"), f_contents])
else:
to_add = HyList([f_contents])

View File

@ -43,15 +43,11 @@ except ImportError:
import hy.macros
import hy.compiler
try:
import __builtin__
except ImportError:
import builtins as __builtin__ # NOQA
from hy._compat import builtins
PATH = [hy.compiler._compile_table,
hy.macros._hy_macros,
__builtin__.__dict__]
builtins.__dict__]
class Completer(object):

54
hy/contrib/meth.hy Normal file
View File

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

View File

@ -1,58 +0,0 @@
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
from hy.models.expression import HyExpression
from hy.models.symbol import HySymbol
from hy.models.string import HyString
from hy.models.list import HyList
from hy.models.dict import HyDict
from hy.macros import macro
def router(tree, rkwargs=None):
tree = HyExpression(tree)
name = tree.pop(0)
path = tree.pop(0)
tree.insert(0, HySymbol("fn"))
tree = HyExpression([HySymbol("def"), name, tree])
route = HyExpression([HySymbol(".route"), HySymbol("app"), path])
if rkwargs:
route = HyExpression([HySymbol("kwapply"), route,
HyDict({HyString("methods"): rkwargs})])
return HyExpression([HySymbol("with_decorator"), route, tree])
@macro("route")
def route_macro(*tree):
return router(tree)
@macro("post_route")
def post_route_macro(*tree):
return router(tree, rkwargs=HyList([HyString("POST")]))
@macro("get_route")
def get_route_macro(*tree):
return router(tree, rkwargs=HyList([HyString("GET")]))

74
hy/core/bootstrap.hy Normal file
View File

@ -0,0 +1,74 @@
;;; Hy bootstrap macros
;;
;; Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
;; Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
;; Copyright (c) 2013 Konrad Hinsen <konrad.hinsen@fastmail.net>
;;
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.
;;
;;; These macros are the essential hy macros.
;;; They are automatically required everywhere, even inside hy.core modules.
(import [hy.compiler [HyTypeError]])
(defmacro macro-error [location reason]
"error out properly within a macro"
`(raise (HyTypeError ~location ~reason)))
(defmacro defmacro-alias [names lambda-list &rest body]
"define one macro with several names"
(setv ret `(do))
(foreach [name names]
(.append ret
`(defmacro ~name ~lambda-list ~@body)))
ret)
(defmacro-alias [defn defun] [name lambda-list &rest body]
"define a function `name` with signature `lambda-list` and body `body`"
(if (not (= (type name) HySymbol))
(macro-error name "defn/defun takes a name as first argument"))
`(setv ~name (fn ~lambda-list ~@body)))
(defmacro let [variables &rest body]
"Execute `body` in the lexical context of `variables`"
(setv macroed_variables [])
(if (not (isinstance variables HyList))
(macro-error variables "let lexical context must be a list"))
(foreach [variable variables]
(if (isinstance variable HyList)
(do
(if (!= (len variable) 2)
(macro-error variable "let variable assignments must contain two items"))
(.append macroed-variables `(setv ~(get variable 0) ~(get variable 1))))
(.append macroed-variables `(setv ~variable None))))
`((fn []
~@macroed-variables
~@body)))
(defmacro if-python2 [python2-form python3-form]
"If running on python2, execute python2-form, else, execute python3-form"
(import sys)
(if (< (get sys.version_info 0) 3)
python2-form
python3-form))

View File

@ -1,156 +0,0 @@
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
from hy.macros import macro
from hy.models.expression import HyExpression
from hy.models.integer import HyInteger
from hy.models.symbol import HySymbol
from hy.models.list import HyList
@macro("defn")
@macro("defun")
def defn_macro(name, *body):
return HyExpression([HySymbol("def"),
name, HyExpression([HySymbol("fn")] + list(body))])
@macro("cond")
def cond_macro(*tree):
it = iter(tree)
test, branch = next(it)
root = HyExpression([HySymbol("if"), test, branch])
ret = root
for (test, branch) in it:
n = HyExpression([HySymbol("if"), test, branch])
ret.append(n)
ret = n
return root
@macro("for")
def for_macro(*tree):
ret = None
# for [x iter y iter] ...
# ->
# foreach x iter
# foreach y iter
# ...
tree = HyExpression(tree).replace(tree[0])
it = iter(tree.pop(0))
blocks = list(zip(it, it)) # List for Python 3.x degenerating.
key, val = blocks.pop(0)
ret = HyExpression([HySymbol("foreach"),
HyList([key, val])])
root = ret
ret.replace(tree)
for key, val in blocks:
# x, [1, 2, 3, 4]
nret = HyExpression([HySymbol("foreach"),
HyList([key, val])])
nret.replace(key)
ret.append(nret)
ret = nret
[ret.append(x) for x in tree] # we really need ~@
return root
@macro("_>")
def threading_macro(head, *rest):
ret = head
for node in rest:
if not isinstance(node, HyExpression):
nnode = HyExpression([node])
nnode.replace(node)
node = nnode
node.insert(1, ret)
ret = node
return ret
@macro("_>>")
def threading_tail_macro(head, *rest):
ret = head
for node in rest:
if not isinstance(node, HyExpression):
nnode = HyExpression([node])
nnode.replace(node)
node = nnode
node.append(ret)
ret = node
return ret
@macro("car")
@macro("first")
def first_macro(lst):
return HyExpression([HySymbol('get'),
lst,
HyInteger(0)])
@macro("cdr")
@macro("rest")
def rest_macro(lst):
return HyExpression([HySymbol('slice'),
lst,
HyInteger(1)])
@macro("let")
def let_macro(variables, *body):
expr = HyExpression([HySymbol("fn"), HyList([])])
for var in variables:
if isinstance(var, list):
expr.append(HyExpression([HySymbol("setv"),
var[0], var[1]]))
else:
expr.append(HyExpression([HySymbol("setv"),
var, HySymbol("None")]))
return HyExpression([expr + list(body)])
@macro("when")
def when_macro(test, *body):
return HyExpression([
HySymbol('if'),
test,
HyExpression([HySymbol("do")] + list(body)),
])
@macro("unless")
def unless_macro(test, *body):
return HyExpression([
HySymbol('if'),
test,
HySymbol('None'),
HyExpression([HySymbol("do")] + list(body)),
])

View File

@ -23,8 +23,6 @@
;;;; to make functional programming slightly easier.
;;;;
(require hy.core.macros)
(defn _numeric-check [x]
(if (not (numeric? x))
(raise (TypeError (.format "{0!r} is not a number" x)))))
@ -32,11 +30,11 @@
(defn cycle [coll]
"Yield an infinite repetition of the items in coll"
(setv seen [])
(for [x coll]
(foreach [x coll]
(yield x)
(.append seen x))
(while seen
(for [x seen]
(foreach [x seen]
(yield x))))
(defn dec [n]
@ -48,7 +46,7 @@
"Return a generator from the original collection with duplicates
removed"
(let [[seen []] [citer (iter coll)]]
(for [val citer]
(foreach [val citer]
(if (not_in val seen)
(do
(yield val)
@ -57,7 +55,7 @@
(defn drop [count coll]
"Drop `count` elements from `coll` and yield back the rest"
(let [[citer (iter coll)]]
(try (for [i (range count)]
(try (foreach [i (range count)]
(next citer))
(catch [StopIteration]))
citer))
@ -65,10 +63,10 @@
(defn drop-while [pred coll]
"Drop all elements of `coll` until `pred` is False"
(let [[citer (iter coll)]]
(for [val citer]
(foreach [val citer]
(if (not (pred val))
(do (yield val) (break))))
(for [val citer]
(foreach [val citer]
(yield val))))
(defn empty? [coll]
@ -83,7 +81,7 @@
(defn filter [pred coll]
"Return all elements from `coll` that pass `pred`"
(let [[citer (iter coll)]]
(for [val citer]
(foreach [val citer]
(if (pred val)
(yield val)))))
@ -138,7 +136,7 @@
"Return nth item in collection or sequence, counting from 0"
(if (not (neg? index))
(if (iterable? coll)
(try (first (list (take 1 (drop index coll))))
(try (get (list (take 1 (drop index coll))) 0)
(catch [IndexError] None))
(try (get coll index)
(catch [IndexError] None)))
@ -157,7 +155,7 @@
(defn remove [pred coll]
"Return coll with elements removed that pass `pred`"
(let [[citer (iter coll)]]
(for [val citer]
(foreach [val citer]
(if (not (pred val))
(yield val)))))
@ -165,7 +163,7 @@
"Yield x forever or optionally n times"
(if (none? n)
(setv dispatch (fn [] (while true (yield x))))
(setv dispatch (fn [] (for [_ (range n)] (yield x)))))
(setv dispatch (fn [] (foreach [_ (range n)] (yield x)))))
(dispatch))
(defn repeatedly [func]
@ -187,7 +185,7 @@
"Take `count` elements from `coll`, or the whole set if the total
number of entries in `coll` is less than `count`."
(let [[citer (iter coll)]]
(for [_ (range count)]
(foreach [_ (range count)]
(yield (next citer)))))
(defn take-nth [n coll]
@ -195,16 +193,16 @@
raises ValueError for (not (pos? n))"
(if (pos? n)
(let [[citer (iter coll)] [skip (dec n)]]
(for [val citer]
(foreach [val citer]
(yield val)
(for [_ (range skip)]
(foreach [_ (range skip)]
(next citer))))
(raise (ValueError "n must be positive"))))
(defn take-while [pred coll]
"Take all elements while `pred` is true"
(let [[citer (iter coll)]]
(for [val citer]
(foreach [val citer]
(if (pred val)
(yield val)
(break)))))

View File

@ -1,12 +1,118 @@
;;; hy core macros
;;; Hy core macros
;;
;; Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
;; Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
;; Copyright (c) 2013 Konrad Hinsen <konrad.hinsen@fastmail.net>
;;
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.
;;
;;; These macros form the hy language
;;; They are automatically required in every module, except inside hy.core
(defmacro if-python2 [python2-form python3-form]
(import sys)
(if (< (get sys.version_info 0) 3)
python2-form
python3-form))
(defmacro yield-from [_hy_yield_from_els]
(quasiquote
(for [_hy_yield_from_x (unquote _hy_yield_from_els)]
(yield _hy_yield_from_x))))
(defmacro for [args &rest body]
"shorthand for nested foreach loops:
(for [x foo y bar] baz) ->
(foreach [x foo]
(foreach [y bar]
baz))"
;; TODO: that signature sucks.
;; (for [[x foo] [y bar]] baz) would be more consistent
(if (% (len args) 2)
(macro-error args "for needs an even number of elements in its first argument"))
(if args
`(foreach [~(.pop args 0) ~(.pop args 0)] (for ~args ~@body))
`(do ~@body)))
(defmacro-alias [car first] [thing]
"Get the first element of a list/cons"
`(get ~thing 0))
(defmacro-alias [cdr rest] [thing]
"Get all the elements of a thing, except the first"
`(slice ~thing 1))
(defmacro cond [&rest branches]
"shorthand for nested ifs:
(cond [foo bar] [baz quux]) ->
(if foo
bar
(if baz
quux))"
(setv branches (iter branches))
(setv branch (next branches))
(defn check-branch [branch]
"check `cond` branch for validity, return the corresponding `if` expr"
(if (not (= (type branch) HyList))
(macro-error branch "cond branches need to be a list"))
(if (!= (len branch) 2)
(macro-error branch "cond branches need two items: a test and a code branch"))
(setv (, test thebranch) branch)
`(if ~test ~thebranch))
(setv root (check-branch branch))
(setv latest-branch root)
(foreach [branch branches]
(setv cur-branch (check-branch branch))
(.append latest-branch cur-branch)
(setv latest-branch cur-branch))
root)
(defmacro -> [head &rest rest]
;; TODO: fix the docstring by someone who understands this
(setv ret head)
(foreach [node rest]
(if (not (isinstance node HyExpression))
(setv node `(~node)))
(.insert node 1 ret)
(setv ret node))
ret)
(defmacro ->> [head &rest rest]
;; TODO: fix the docstring by someone who understands this
(setv ret head)
(foreach [node rest]
(if (not (isinstance node HyExpression))
(setv node `(~node)))
(.append node ret)
(setv ret node))
ret)
(defmacro when [test &rest body]
"Execute `body` when `test` is true"
`(if ~test (do ~@body)))
(defmacro unless [test &rest body]
"Execute `body` when `test` is false"
`(if ~test None (do ~@body)))
(defmacro yield-from [iterable]
"Yield all the items from iterable"
;; TODO: this needs some gensym love
`(foreach [_hy_yield_from_x ~iterable]
(yield _hy_yield_from_x)))

View File

@ -32,11 +32,7 @@ import ast
import os
import __future__
if sys.version_info[0] >= 3:
long_type = int
else:
import __builtin__
long_type = long # NOQA
from hy._compat import builtins, long_type
def ast_compile(ast, filename, mode):
@ -129,10 +125,7 @@ def write_hy_as_pyc(fname):
code = ast_compile(_ast, fname, "exec")
cfile = "%s.pyc" % fname[:-len(".hy")]
if sys.version_info[0] >= 3:
open_ = open
else:
open_ = __builtin__.open
open_ = builtins.open
with open_(cfile, 'wb') as fc:
if sys.version_info[0] >= 3:

View File

@ -26,7 +26,7 @@ from hy.models.integer import HyInteger
from hy.models.float import HyFloat
from hy.models.complex import HyComplex
from hy.models.dict import HyDict
from hy.util import str_type
from hy._compat import str_type
from collections import defaultdict
@ -43,6 +43,17 @@ _hy_macros = defaultdict(dict)
def macro(name):
"""Decorator to define a macro called `name`.
This stores the macro `name` in the namespace for the module where it is
defined.
If the module where it is defined is in `hy.core`, then the macro is stored
in the default `None` namespace.
This function is called from the `defmacro` special form in the compiler.
"""
def _(fn):
module_name = fn.__module__
if module_name.startswith("hy.core"):
@ -52,20 +63,20 @@ def macro(name):
return _
def require(source_module_name, target_module_name):
macros = _hy_macros[source_module_name]
refs = _hy_macros[target_module_name]
def require(source_module, target_module):
"""Load the macros from `source_module` in the namespace of
`target_module`.
This function is called from the `require` special form in the compiler.
"""
macros = _hy_macros[source_module]
refs = _hy_macros[target_module]
for name, macro in macros.items():
refs[name] = macro
def _wrap_value(x):
wrapper = _wrappers.get(type(x))
if wrapper is None:
return x
else:
return wrapper(x)
# type -> wrapping function mapping for _wrap_value
_wrappers = {
int: HyInteger,
bool: lambda x: HySymbol("True") if x else HySymbol("False"),
@ -77,27 +88,60 @@ _wrappers = {
}
def process(tree, module_name):
load_macros(module_name)
old = None
while old != tree:
old = tree
tree = macroexpand(tree, module_name)
return tree
def _wrap_value(x):
"""Wrap `x` into the corresponding Hy type.
This allows a macro to return an unquoted expression transparently.
"""
wrapper = _wrappers.get(type(x))
if wrapper is None:
return x
else:
return wrapper(x)
def load_macros(module_name):
"""Load the hy builtin macros for module `module_name`.
Modules from `hy.core` can only use the macros from CORE_MACROS.
Other modules get the macros from CORE_MACROS and EXTRA_MACROS.
"""
def _import(module, module_name=module_name):
"__import__ a module, avoiding recursions"
if module != module_name:
__import__(module)
for module in CORE_MACROS:
__import__(module)
_import(module)
if module_name.startswith("hy.core"):
return
for module in EXTRA_MACROS:
__import__(module)
_import(module)
def macroexpand(tree, module_name):
"""Expand the toplevel macros for the `tree`.
Load the macros from the given `module_name`, then expand the (top-level)
macros in `tree` until it stops changing.
"""
load_macros(module_name)
old = None
while old != tree:
old = tree
tree = macroexpand_1(tree, module_name)
return tree
def macroexpand_1(tree, module_name):
"""Expand the toplevel macro from `tree` once, in the context of
`module_name`."""
if isinstance(tree, HyExpression):
if tree == []:
return tree

View File

@ -24,7 +24,6 @@ class HyObject(object):
Generic Hy Object model. This is helpful to inject things into all the
Hy lexing Objects at once.
"""
pass
def replace(self, other):
if isinstance(other, HyObject):

View File

@ -20,7 +20,7 @@
from __future__ import unicode_literals
from hy.models import HyObject
from hy.util import str_type
from hy._compat import str_type
class HyKeyword(HyObject, str_type):

View File

@ -19,7 +19,7 @@
# DEALINGS IN THE SOFTWARE.
from hy.models import HyObject
from hy.util import str_type
from hy._compat import str_type
class HyString(HyObject, str_type):

View File

@ -1,5 +1,5 @@
from hy.macros import macro, process
from hy.macros import macro, macroexpand
from hy.lex import tokenize
from hy.models.string import HyString
@ -16,14 +16,14 @@ def tmac(*tree):
def test_preprocessor_simple():
""" Test basic macro expansion """
obj = process(tokenize('(test "one" "two")')[0], __name__)
obj = macroexpand(tokenize('(test "one" "two")')[0], __name__)
assert obj == HyList(["one", "two"])
assert type(obj) == HyList
def test_preprocessor_expression():
""" Test that macro expansion doesn't recurse"""
obj = process(tokenize('(test (test "one" "two"))')[0], __name__)
obj = macroexpand(tokenize('(test (test "one" "two"))')[0], __name__)
assert type(obj) == HyList
assert type(obj[0]) == HyExpression
@ -34,4 +34,4 @@ def test_preprocessor_expression():
obj = HyList([HyString("one"), HyString("two")])
obj = tokenize('(shill ["one" "two"])')[0][1]
assert obj == process(obj, '')
assert obj == macroexpand(obj, '')

View File

@ -122,8 +122,8 @@
(defn test-cond []
"NATIVE: test if cond sorta works."
(cond
((= 1 2) (assert (is true false)))
((is null null) (assert (is true true)))))
[(= 1 2) (assert (is true false))]
[(is null null) (assert (is true true))]))
(defn test-index []

View File

@ -82,3 +82,14 @@
(setv opt (quote &optional))
(assert (isinstance opt hy.HyLambdaListKeyword))
(assert (= (str opt) "&optional")))
(defmacro doodle [&rest body]
`(do ~@body))
(defn test-unquote-splice []
"NATIVE: test unquote-splice does what's intended"
(assert (=
(doodle
[1 2 3]
[4 5 6])
[4 5 6])))

View File

@ -12,7 +12,7 @@
;; using (cond) allows -i to take precedence over -c
(cond (args.i
(print (str args.i)))
(args.c
(print (str "got c"))))
(cond [args.i
(print (str args.i))]
[args.c
(print (str "got c"))])