diff --git a/hy/core/bootstrap.hy b/hy/core/bootstrap.hy new file mode 100644 index 0000000..69fbfbb --- /dev/null +++ b/hy/core/bootstrap.hy @@ -0,0 +1,74 @@ +;;; Hy bootstrap macros +;; +;; Copyright (c) 2013 Nicolas Dandrimont +;; Copyright (c) 2013 Paul Tagliamonte +;; Copyright (c) 2013 Konrad Hinsen +;; +;; 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)) diff --git a/hy/core/bootstrap.py b/hy/core/bootstrap.py deleted file mode 100644 index eaedb28..0000000 --- a/hy/core/bootstrap.py +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright (c) 2013 Paul Tagliamonte -# -# 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)), - ]) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index d25dd25..17792f9 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -1,12 +1,118 @@ -;;; hy core macros +;;; Hy core macros +;; +;; Copyright (c) 2013 Nicolas Dandrimont +;; Copyright (c) 2013 Paul Tagliamonte +;; Copyright (c) 2013 Konrad Hinsen +;; +;; 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))) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 6dd2986..d19ca17 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -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 [] diff --git a/tests/resources/argparse_ex.hy b/tests/resources/argparse_ex.hy index 9b2500f..82df479 100755 --- a/tests/resources/argparse_ex.hy +++ b/tests/resources/argparse_ex.hy @@ -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"))])