Generando código Python con macros Hy

Srv19

Estoy tratando de generar un código Python desde Hy. ¿Cómo se hace mejor?

He probado varios enfoques. Uno es con una macro:

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

Luego, después de capturar la macroexpansión, imprimo el desmontaje de Python de un código.

Sin embargo, parece que con las macros no puedo pasar variables, por lo que:

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

define varv, varn, varay así sucesivamente, en lugar de var1, var2, var3. Parece que la invocación correcta se podría hacer con:

(macroexpand `(make-vars ~vnames))

pero eso parece excesivamente complejo.

Otro problema con el que me he encontrado es la necesidad HySymbol, lo que fue una gran sorpresa. Pero eso me ha dolido mucho, cuando probé el segundo enfoque, donde hice una función que devuelve formas entre comillas:

(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)))

Esa función toma metadatos que se ven así en Python:

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

Y toma una lista de clases que tienen forma de:

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

Requería un uso extensivo de clases internas de hy, y no logré representar correctamente Verdadero y Falso, recurriendo a HyInteger(1)y en su HyInteger(0)lugar.

Para obtener el código Python de esta función, ejecuto su resultado disassemble.

Resumir:

  1. ¿Cuál sería la mejor manera de generar código Python desde Hy?
  2. ¿Qué es la representación interna de Verdadero y Falso?
  3. ¿Se puede llamar a una función que procesa sus parámetros y devuelve una forma Hy entre comillas desde una macro y cómo?
mierda

En Hy generalmente no es necesario generar código Python, ya que Hy es mucho mejor generando código Hy y es igual de ejecutable. Esto se hace todo el tiempo en las macros Hy.

En el caso inusual de que necesite generar Python real y no solo Hy, la mejor manera es con cadenas, de la misma manera que lo haría en Python. Hy compila en AST de Python, no en Python en sí. El desensamblador es realmente solo para fines de depuración. No siempre genera Python válido:

=> (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

El nombre de la variable +!@$es tan legal como spamen el AST, pero Python se execahoga porque no es un identificador válido de Python.

Si comprende y está de acuerdo con esta limitación, puede usarla disassemble, pero sin macros. Las funciones ordinarias en tiempo de ejecución pueden tomar y generar (como demostró) expresiones Hy. Las macros son realmente funciones como esta que se ejecutan en tiempo de compilación. No es inusual en Hy que una macro delegue parte de su trabajo a una función ordinaria que toma una expresión Hy como uno de sus argumentos y devuelve una expresión Hy.

La forma más sencilla de crear una expresión Hy como datos es citarla '. La sintaxis de comillas invertidas para interpolar valores también es válida incluso fuera del cuerpo de una macro. También puede usar esto en funciones normales de tiempo de ejecución. Pero entienda, debe insertar formas entre comillas en la interpolación si desea desensamblarla, porque eso es lo que una macro recibiría como argumentos: el código en sí , no sus valores evaluados. Es por eso que estás usando HySymboly amigos.

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

Puede preguntar al REPL qué tipos usa para los formularios cotizados.

=> (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'>

Como puede ver, Truees solo un símbolo internamente. Tenga en cuenta que pude generar un HySymbolsolo ', sin usar la HySymbolllamada. Si su archivo de metadatos se escribió en Hy y se hizo con formularios Hy entre comillas en primer lugar, no tendría que convertirlos. Pero no hay razón para que deba hacerse en el último minuto dentro del formulario de comillas invertidas. Eso podría hacerse de antemano mediante una función de ayuda si eso es lo que prefiere.


Seguimiento

¿Se puede llamar a una función que procesa sus parámetros y devuelve una forma Hy entre comillas desde una macro y cómo?

Mi punto original era que una macro es la herramienta incorrecta para lo que está tratando de hacer. Pero para aclarar, puede llamar a una macro en tiempo de ejecución, usando macroexpand, como ya demostró. Por supuesto, puede poner la macroexpandllamada dentro de otra función, pero macroexpanddebe tener una forma entre comillas como argumento.

Además, la misma pregunta sobre los diccionarios generados dinámicamente. La construcción que he usado se ve horrible.

La parte del diccionario podría simplificarse a algo más como

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

Mientras que Python dictestá respaldado por una tabla hash, el HyDictmodelo de Hy es en realidad solo una lista. Esto se debe a que no representa la tabla hash en sí, sino el código que produce el dict. Es por eso que puede empalmarlo como una lista.

Sin embargo, si es posible, ¿podría agregar un ejemplo de cómo pasar correctamente cadenas generadas dinámicamente en la expresión final entre comillas? Por lo que tengo entendido, se puede hacer agregando una tarea más (que agregaría una cita), pero ¿hay una forma más elegante?

Los modelos de Hy se consideran parte de la API pública, simplemente no se usan mucho fuera de las macros. Está bien usarlos cuando sea necesario. Otros Lisps no hacen el mismo tipo de distinción entre los objetos del modelo de código y los datos que producen. Hy lo hace de esta manera para una mejor interoperabilidad de Python. Se podría argumentar que la ~sintaxis debería realizar esta conversión automáticamente para ciertos tipos de datos, pero en la actualidad no es así. [ Actualización : en la rama maestra actual, el compilador de Hy ajustará automáticamente los valores compatibles en un modelo de Hy cuando sea posible, por lo que normalmente ya no tendrá que hacerlo usted mismo].

HySymboles apropiado para generar símbolos dinámicamente a partir de cadenas, como está tratando de hacer. No es la única forma, pero es lo que quieres en este caso. La otra forma, gensymse usa más a menudo en macros, pero no pueden ser tan bonitas. Puede llamar gensymcon una cadena para darle un nombre más significativo con fines de depuración, pero aún tiene un sufijo numérico para que sea único. Por supuesto, podría asignar HySymbolun alias más corto o delegar esa parte a una función auxiliar.

También puede convertirlo de antemano, por ejemplo, el fragmento

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

En cambio podría ser

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

Entonces no tienes que hacerlo dos veces.

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

Eso probablemente hace que la plantilla sea más fácil de leer.


Actualizar

Hy master ahora transforma los símbolos en identificadores de Python válidos en la compilación, por lo que la hy2pyherramienta y el desmontaje de astor deberían generar un código Python válido de manera más confiable, incluso si hay caracteres especiales en los símbolos.

Este artículo se recopila de Internet, indique la fuente cuando se vuelva a imprimir.

En caso de infracción, por favor [email protected] Eliminar

Editado en
0

Déjame decir algunas palabras

0Comentarios
Iniciar sesiónRevisión de participación posterior

Artículos relacionados

TOP Lista

CalienteEtiquetas

Archivo