编程语言
首页 > 编程语言> > 使用Hy宏生成Python代码

使用Hy宏生成Python代码

作者:互联网

我正在尝试从Hy生成一些python代码.怎么做得更好?

我尝试了几种方法.一个是与宏:

(defmacro make-vars [data]
  (setv res '())
  (for [element data]
    (setv varname (HySymbol (+ "var" (str element))))
    (setv res (cons `(setv ~varname 0) res)))
  `(do ~@res))

然后,在捕获宏扩展之后,我将打印python反汇编代码.

但是,似乎我无法使用宏来传递变量,因此:

(setv vnames [1 2 3])
(make-vars vnames)

定义varv,varn,vara等,而不是var1,var2,var3.似乎可以通过以下方式进行正确的调用:

(macroexpand `(make-vars ~vnames))

但这似乎太复杂了.

我遇到的另一个问题是HySymbol的必要性,这令人惊讶.但是,当我尝试第二种方法时,我确实受到了伤害,在该方法中,我制作了一个返回引用形式的函数:

(defn make-faction-detaches [faction metadata unit-types]
  (let [meta-base (get metadata "Base")
        meta-pattern (get metadata "Sections")
        class-cand []
        class-def '()
        class-grouping (dict)]
    (for [(, sec-name sec-flag) (.iteritems meta-pattern)]
      ;; if section flag is set but no unit types with the section are found, break and return nothing
      (print "checking" sec-name)
      (if-not (or (not sec-flag) (any (genexpr (in sec-name (. ut roles)) [ut unit-types])))
              (break)
              ;; save unit types for section 
              (do
               (print "match for section" sec-name)
               (setv sec-grouping (list-comp ut [ut unit-types]
                                             (in sec-name (. ut roles))))
               (print (len sec-grouping) "types found for section" sec-name)
               (when sec-grouping
                 (assoc class-grouping sec-name sec-grouping))))
      ;; in case we finished the cycle
      (else
       (do
        (def
          class-name (.format "{}_{}" (. meta-base __name__) (fix-faction-string faction))
          army-id (.format "{}_{}" (. meta-base army_id) (fix-faction-string faction))
          army-name (.format "{} ({})" (fix-faction-name faction) (. meta-base army_name)))
         (print "Class name is" class-name)
         (print "Army id is" army-id)
         (print "Army name is" army-name)
         (setv class-cand [(HySymbol class-name)])
         (setv class-def [`(defclass ~(HySymbol class-name) [~(HySymbol (. meta-base __name__))]
                            [army_name ~(HyString army-name)
                             faction ~(HyString faction)
                             army_id ~(HyString army-id)]
                             (defn --init-- [self]
                               (.--init-- (super) ~(HyDict (interleave (genexpr (HyString k) [k class-grouping])
                                                                       (cycle [(HyInteger 1)]))))
                               ~@(map (fn [key]
                                        `(.add-classes (. self ~(HySymbol key))
                                                       ~(HyList (genexpr (HySymbol (. ut __name__))
                                                                         [ut (get class-grouping key)]))))
                                      class-grouping)))]))))
    (, class-def class-cand)))

该函数采用的元数据在python中看起来像这样:

metadata = [
    {'Base': DetachPatrol,
     'Sections': {'hq': True, 'elite': False,
                  'troops': True, 'fast': False,
                  'heavy': False, 'fliers': False,
                  'transports': False}}]

并采用以下形式的类列表:

class SomeSection(object):
    roles = ['hq']

它需要广泛使用hy的内部类,而我无法正确地表示True和False,而是使用HyInteger(1)和HyInteger(0).

为了从该函数获取python代码,我通过反汇编来运行其结果.

总结一下:

>从Hy生成python代码的最佳方法是什么?
>对与错的内部表示是什么?
>可以调用一个函数来处理其参数并从宏返回带引号的Hy形式吗?

解决方法:

在Hy中,您通常不需要生成Python代码,因为Hy在生成Hy代码方面要好得多,并且与可执行文件一样.这总是在Hy宏中完成.

在不寻常的情况下,您需要生成真正的Python而不是Hy,最好的方法是使用字符串,就像在Python中一样. Hy编译为Python的AST,而不是Python本身.反汇编程序实际上仅用于调试目的.它并不总是生成有效的Python:

=> (setv +!@$42)
=> +!@$
42
=> (disassemble '(setv +!@$42) True)
'+!@$= 42'
=> (exec (disassemble '(setv +!@$42) True))
Traceback (most recent call last):
  File "/home/gilch/repos/hy/hy/importer.py", line 193, in hy_eval
    return eval(ast_compile(expr, "<eval>", "eval"), namespace)
  File "<eval>", line 1, in <module>
  File "<string>", line 1
    +!@$= 42
     ^
SyntaxError: invalid syntax
=> (exec "spam = 42; print(spam)")
42

变量名!@ $与AST中的垃圾邮件一样合法,但是Python的exec对此感到窒息,因为它不是有效的Python标识符.

如果您了解并可以使用此限制,则可以使用反汇编,但不使用宏.允许普通的运行时函数采用和生成(如您所演示的)Hy表达式.宏实际上就是这样的功能,而不是在编译时运行.在Hy中,宏将工作的一部分委派给一个普通函数的情况并不罕见,该函数将Hy表达式作为其参数之一并返回Hy表达式.

创建Hy表达式作为数据的最简单方法是用’引用.插值的反引号语法即使在宏的主体之外也有效.您也可以在正常的运行时函数中使用它.但是请理解,如果要反汇编,则必须将带引号的形式插入到插值中,因为这就是宏将作为参数接收的内容-代码本身,而不是其求值.这就是为什么您使用HySymbol和朋友.

=> (setv class-name 'Foo)  ; N.B. 'Foo is quoted
=> (print (disassemble `(defclass ~class-name) True))
class Foo:
    pass

您可以询问REPL引用形式使用什么类型.

=> (type 1)
<class 'int'>
=> (type '1)
<class 'hy.models.HyInteger'>
=> (type "foo!")
<class 'str'>
=> (type '"foo!")
<class 'hy.models.HyString'>
=> (type True)
<class 'bool'>
=> (type 'True)
<class 'hy.models.HySymbol'>

如您所见,True在内部只是一个符号.请注意,我能够仅使用’生成HySymbol,而无需使用HySymbol调用.如果您的元数据文件是用Hy编写的,并且首先是用带引号的Hy格式制作的,则无需进行转换.但是没有理由必须在反引号表单内的最后一刻完成它.如果您愿意的话,可以通过辅助功能提前完成.

跟进

Can one call a function that processes its parameters and returns a quoted Hy form from a macro and how?

我最初的观点是,宏是您要尝试执行的操作的错误工具.但是要澄清一下,您已经可以通过使用macroexpand在运行时调用宏.当然,您可以将macroexpand调用放在另一个函数中,但是macroexpand必须具有带引号的形式作为其参数.

Also, the same question about dynamically generated dictionaries. Construction I have used looks horrible.

字典部分可以简化为更多类似的内容

{~@(interleave (map HyString class-grouping) (repeat '1))}

虽然Python的字典由哈希表支持,但Hy的HyDict模型实际上只是一个列表.这是因为它不代表哈希表本身,而是代表字典的代码.因此,您可以像列表一样将其拼接.

However if possible, could you add an example of properly passing dynamically generated strings into the final quoted expression? As far as I understand, it can be done with adding one more assignment (that would add quotation), but is there a more elegant way?

Hy’s models被认为是公共API的一部分,只是在宏之外并没有太多使用.必要时可以使用它们.其他Lisps在代码模型对象和它们产生的数据之间没有做出相同的区分. Hy这样做是为了获得更好的Python互操作性.有人可能会说〜语法应针对某些数据类型自动执行此转换,但目前还不行.
[更新:在当前的master分支上,Hy的编译器将在可能的情况下自动将兼容的值包装在Hy模型中,因此您通常不必自己再执行此操作.

HySymbol适用于从字符串中动态生成符号,就像您要尝试的那样.这不是唯一的方法,但在这种情况下,这就是您想要的.另一种方法gensym在宏中使用较多,但它们却不能那么漂亮.您可以使用字符串调用gensym以为其提供更有意义的名称以进行调试,但是它仍然具有数字后缀以使其唯一.您当然可以为HySymbol分配一个较短的别名,或将该部分委托给一个辅助函数.

您也可以预先将其转换,例如片段

(def class-name (.format "{}_{}" (. meta-base __name__) ...

可能是

(def class-name (HySymbol (.format "{}_{}" (. meta-base __name__) ...

然后,您不必重复两次.

(setv class-cand [class-name])
(setv class-def [`(defclass ~class-name ...

这可能使模板更易于阅读.

更新资料

Hy master现在可以在编译时将符号转换为有效的Python标识符,因此即使符号中包含特殊字符,hy2py工具和astor反汇编也应更可靠地生成有效的Python代码.

标签:macros,lisp,python,hy
来源: https://codeday.me/bug/20191111/2018947.html