hylang / hy

A dialect of Lisp that's embedded in Python
http://hylang.org
Other
5.13k stars 373 forks source link

Macros can't see user defined functions #1917

Closed atgmello closed 4 years ago

atgmello commented 4 years ago

Dealing with https://github.com/Calysto/calysto_hy/issues/15 was straightforward. Changes made to Hy broke some calls from Calysto, but could be easily fixed.

However, the issue mentioned in this commend still persists.

The following code (MWE) is enough to get the kernel running as expected...

import ast
from hy.lex import tokenize
from hy.compiler import hy_compile

code = "(+ 1 1)"
tree = tokenize(code)
_ast = hy_compile(tree, 'hy', root=ast.Interactive)
code = compile(_ast, "exec", mode="single")
eval(code)

...except for macros that call user defined functions, such the one suggested in the comments:

(import [numpy :as np])

(defn parse-colon [sym]
            (list (map (fn [x]
                       (if (empty? x)
                           None
                           (int x)))
                     (.split (str sym) ":"))))

(defn parse-indexing [sym]
          (cond
            [(in ":" (str sym)) `(slice ~@(parse-colon sym))]
            [(in "..." (str sym)) 'Ellipsis]
            [True sym]))

(defmacro nget [ar &rest keys]
            `(get ~ar (, ~@(map parse-indexing keys))))

(setv a (np.array [[0 1 2 3 4 5] [10 11 12 13 14 15] [20 21 22 23 24 25]
                      [30 31 32 33 34 35] [40 41 42 43 44 45] [50 51 52 53 54 55]]))

(nget a (, 1 2 3 4 5) (, 0 1 2 3 4))

Since the user mentioned that the above code ran in the REPL as expected, I gave it a try and confirmed that it indeed does. I also tried calling the REPL from Python with the run_repl() function and it also worked. Since I'm not an expert on Hy internals, I suspected that the HyREPL was keeping some kind of state that the low level manipulation from the first snippet simply couldn't. So I gave this a try:

from hy.cmdline import HyREPL

repl = HyREPL()

code = """
(import [numpy :as np])

(defn parse-colon [sym]
            (list (map (fn [x]
                       (if (empty? x)
                           None
                           (int x)))
                     (.split (str sym) ":"))))

(defn parse-indexing [sym]
          (cond
            [(in ":" (str sym)) `(slice ~@(parse-colon sym))]
            [(in "..." (str sym)) 'Ellipsis]
            [True sym]))

(defmacro nget [ar &rest keys]
            `(get ~ar (, ~@(map parse-indexing keys))))

(setv a (np.array [[0 1 2 3 4 5] [10 11 12 13 14 15] [20 21 22 23 24 25]
                      [30 31 32 33 34 35] [40 41 42 43 44 45] [50 51 52 53 54 55]]))

(nget a (, 1 2 3 4 5) (, 0 1 2 3 4))
"""
compiled = repl.compile(code)
repl.runcode(compiled)

But it also wouldn't run.

In summary, at first I thought that this could be an issue with https://github.com/Calysto/calysto_hy itself. But then, after these tests, I believe that this might be something that could be fixed on Hy instead. That, or I need a better understanding of the internals to properly compile and run Hy macros from Python.

Either way, in all attempts the error message is the following:

Error message

```python File "/home/andrew/git/hy/hy/macros.py", line 246, in macro_exceptions yield File "/home/andrew/git/hy/hy/macros.py", line 337, in macroexpand obj = m(module.__name__, *tree[1:], **opts) File "", line 18, in nget NameError: name 'parse_indexing' is not defined During handling of the above exception, another exception occurred: Traceback (most recent call last): File "input-5531d6b0f7f20f2b930d044bef14ffa113b32256", line 1, in File "/home/andrew/git/hy/hy/_compat.py", line 14, in reraise raise value.with_traceback(traceback) File "/home/andrew/git/hy/hy/compiler.py", line 2195, in hy_compile result = compiler.compile(tree) File "/home/andrew/git/hy/hy/compiler.py", line 448, in compile reraise(type(e), e, sys.exc_info()[2]) File "/home/andrew/git/hy/hy/_compat.py", line 14, in reraise raise value.with_traceback(traceback) File "/home/andrew/git/hy/hy/compiler.py", line 438, in compile ret = self.compile_atom(tree) File "/home/andrew/git/hy/hy/compiler.py", line 432, in compile_atom return Result() + _model_compilers[type(atom)](self, atom) File "/home/andrew/git/hy/hy/compiler.py", line 1783, in compile_expression self, expr, unmangle(sroot), *parse_tree) File "/home/andrew/git/hy/hy/compiler.py", line 643, in compile_do return self._compile_branch(body) File "/home/andrew/git/hy/hy/compiler.py", line 519, in _compile_branch ret += self.compile(exprs[-1]) File "/home/andrew/git/hy/hy/compiler.py", line 448, in compile reraise(type(e), e, sys.exc_info()[2]) File "/home/andrew/git/hy/hy/_compat.py", line 14, in reraise raise value.with_traceback(traceback) File "/home/andrew/git/hy/hy/compiler.py", line 438, in compile ret = self.compile_atom(tree) File "/home/andrew/git/hy/hy/compiler.py", line 432, in compile_atom return Result() + _model_compilers[type(atom)](self, atom) File "/home/andrew/git/hy/hy/compiler.py", line 1742, in compile_expression expr = macroexpand(expr, self.module, self) File "/home/andrew/git/hy/hy/macros.py", line 342, in macroexpand tree = replace_hy_obj(obj, tree) File "/home/andrew/anaconda3/envs/hylang/lib/python3.7/contextlib.py", line 130, in __exit__ self.gen.throw(type, value, traceback) File "/home/andrew/git/hy/hy/macros.py", line 269, in macro_exceptions sys.exc_info()[2]) File "/home/andrew/git/hy/hy/_compat.py", line 14, in reraise raise value.with_traceback(traceback) File "/home/andrew/git/hy/hy/macros.py", line 246, in macro_exceptions yield File "/home/andrew/git/hy/hy/macros.py", line 337, in macroexpand obj = m(module.__name__, *tree[1:], **opts) File "", line 18, in nget hy.errors.HyMacroExpansionError: File "", line 22 (nget a (, 1 2 3 4 5) (, 0 1 2 3 4)) ^----------------------------------^ expanding macro nget NameError: name 'parse_indexing' is not defined ```

The tests were conducted using the latest Hy (hy 0.19.0+3.g4722844 using CPython(default) 3.7.7 on Linux).

Kodiologist commented 4 years ago

This isn't a bug. See the documentation for eval-and-compile.

atgmello commented 4 years ago

Thanks @Kodiologist, your comment led me to the right path. :) After some digging into Hy I believe I've found what I needed to get things working. And thank you for your efforts on maintaining Hy!