Clause
← Toda la documentación

El original en inglés ha cambiado desde que se hizo esta traducción, por lo que partes de ella pueden estar desactualizadas. Leer en inglés

Traducido del original en inglés · 1e3a39c8-dirty · 2026-10-26

Parse transforms

Uso de parse transforms

clau --erl "C:/Program Files/Erlang OTP/bin/erl.exe" --transform-path ebin -o app app.erl

Clause aplica {parse_transform, Module} como lo hace erlc. Exporta el módulo analizado como formas del formato abstracto de OTP, ejecuta el módulo de transformación en un Erlang/OTP 29 instalado en el anfitrión, importa las formas devueltas como la nueva sintaxis del módulo y compila eso (compiler/src/transforms/). OTP solo se necesita mientras se compila un módulo que usa una transformación; los programas generados nunca lo necesitan.

Comportamiento de OTP

A partir de lib/compiler/src/compile.erl fijado (transform_module, foldl_transform, maybe_strip_columns) y observado con OTP 29.1.1:

Pipeline de Clause

  1. Preprocesar y analizar el módulo. Los errores del preprocesador o del análisis sintáctico se detienen aquí; las transformaciones no se ejecutan en un módulo con errores (difiere de OTP, que transmite los errores como formas).
  2. Reunir las transformaciones en el orden de OTP: las opciones --parse-transform y los parse_transforms del proyecto, y después los atributos -compile. Un módulo sin ninguna se salta el resto y nunca inicia OTP.
  3. Eliminar esas opciones de las formas y exportarlas: anotaciones {Line, Column} tal como las asigna erl_parse, los atributos file de epp y eof. Las funciones predefinidas todavía no forman parte de las formas.
  4. Ejecutar el cargador una vez para el módulo.
  5. Importar las formas devueltas convirtiéndolas en tokens ubicados en sus anotaciones y analizándolas con el parser de Clause; un atributo file cambia el archivo de las formas siguientes como en erl_lint.
  6. Añadir las funciones predefinidas y después analizar con lint, analizar y compilar como de costumbre.

Las acciones que ejecutan transformaciones se enumeran en uso de parse transforms.

Intercambio

Importación de formas

Las formas devueltas, y las entradas .abstr (posicionales o sources del proyecto), se convierten en tokens ubicados en sus anotaciones y pasan por el parser de Clause, por lo que reciben las mismas comprobaciones sintácticas que el código fuente; la precedencia de operadores decide dónde se añaden paréntesis. Se aceptan {Line, Column}, un Line simple (mostrado como file:line:) y listas de propiedades con location y file; generated se ignora. La línea 0, que la transformación qlc de OTP asigna al código generado, toma la ubicación del nodo importado antes de ella. Un atributo file cambia el archivo de las formas siguientes; cuando ese archivo existe, el código importado conserva sus líneas de depuración y los comentarios del fuente. Las formas mal construidas son errores en su anotación (unknown expression bogus, expected {Name, Arity}, invalid annotation) y el módulo falla; las formas {error, _}/{warning, _} de OTP se informan con su descriptor, que necesita OTP para formatearse. Importar un módulo exportado y exportarlo de nuevo produce las mismas formas.

OTP del anfitrión

Cargador

compiler/src/transforms/loader/clause_transform_loader.erl es propiedad del proyecto y está incrustado en clau. Cada ejecución lo escribe junto con la solicitud en un directorio temporal nuevo, clause-transform-* bajo <build_dir>/tmp (directorio de compilación) o, para entradas posicionales sin --build-dir, el directorio temporal del sistema, que se elimina tras la ejecución; erl compila y carga el cargador en memoria. La solicitud contiene las formas, las opciones, la lista de transformaciones, el nombre de archivo del módulo y los fuentes de transformaciones del proyecto que hay que compilar. Con un directorio de compilación, la respuesta y la salida del cargador se guardan en la caché de compilación y se reutilizan mientras la solicitud y todo aquello de lo que depende no cambien; la lista de módulos sticky del anfitrión se conserva de la misma manera. El cargador:

  1. comprueba la versión de OTP;
  2. compila en memoria los fuentes de transformaciones del proyecto solicitados y los carga;
  3. para cada transformación: la carga, elimina las columnas según lo pida parse_transform_info/0, llama a parse_transform/2 y formatea las advertencias y los errores como {File, Location, Text} con Module:format_error/1;
  4. escribe {ok, Forms, Warnings} o {error, Errors, Warnings} y se detiene.

Módulos de transformación

Un módulo de transformación se busca en este orden:

  1. Archivos .beam precompilados en los directorios de --transform-path DIR (repetible) y de la opción de proyecto transform_paths, que se colocan en la ruta de código del cargador antes que los de OTP. Esta es la única forma de usar una transformación que procede de una biblioteca de dependencia: primero se compila esa biblioteca con el OTP del anfitrión (por ejemplo erlc -o deps/lib/ebin deps/lib/src/*.erl); Clause nunca compila dependencias. El .beam debe proceder de la versión mayor de OTP del anfitrión.
  2. Los fuentes del proyecto actual: un Module.erl del lote o de sus source_search_paths, compilado en memoria por el cargador con los directorios de include y las definiciones del destino en cada ejecución, junto con los módulos del proyecto a los que llama por nombre (encontrados de la misma manera). La búsqueda omite los fuentes que ignore_dirs o ignore_modules dejan fuera, como hace la búsqueda de módulos. Clause busca estos fuentes una vez por lote y los mantiene en memoria; el cargador sigue compilándolos para cada módulo sobre el que se ejecuta la transformación, así que conviene precompilar una transformación grande para ahorrar ese tiempo. Los módulos que el anfitrión carga desde sus directorios sticky (kernel, stdlib, compiler, como ms_transform o qlc_pt cuando los fuentes de OTP están en una ruta de búsqueda) no pueden recargarse, por lo que se usan los propios módulos del anfitrión: sus fuentes, y los módulos a los que solo ellos llaman, no se buscan ni se compilan. Clause pregunta al erl del anfitrión por estos módulos una vez por invocación.
  3. Los propios módulos de OTP (ms_transform, qlc) de la ruta de código del anfitrión. OTP incluye erl_id_trans solo como fuente de ejemplo, no como módulo. Sus cabeceras se resuelven como cualquier include_lib (preprocesador): se asigna la aplicación, como en --app-dir "stdlib=C:/Program Files/Erlang OTP/lib/stdlib-8.1" para include_lib("stdlib/include/ms_transform.hrl"), o se nombra la transformación directamente con -compile({parse_transform, ms_transform}).

Compilar una transformación del proyecto puede fallar donde su compilación real tiene éxito: cabeceras de dependencias, una transformación de la transformación o macros que solo define otra herramienta de compilación. Clause informa entonces de los mensajes del compilador sobre el fuente de la transformación con la indicación de precompilarla en un .beam con el OTP del anfitrión y pasar su directorio con --transform-path. Clause también compila una transformación del proyecto como un módulo ordinario cuando forma parte del lote; sus propias llamadas (erl_syntax, erl_parse, ...) pueden necesitar entonces módulos de biblioteca que Clause no incluye, por lo que conviene mantener las transformaciones fuera de los sources de un destino cuando solo la transformación los necesita.

Diagnósticos

SituaciónMensaje
Sin OTP en el anfitriónFile: parse transform 'M' needs Erlang/OTP 29 on the host; pass --erl
--erl no nombra ningún ejecutableFile: erl not found: PATH
Versión de OTP incorrectaparse transforms need Erlang/OTP 29 on the host, found N
Transformación no encontradaFile: undefined parse transform 'M' (texto de OTP)
La transformación lanzó una excepciónFile: error in parse transform 'M': ... (texto de OTP)
Advertencia o error de la transformaciónFile:Line:Column: Text tal como se devuelve
El fuente de la transformación no compilasus mensajes y después la indicación de precompilar
Una ruta de transformaciones no existeFile: parse transform path is not a directory: DIR
Un .beam en una ruta de transformaciones no se cargaFile: cannot load parse transform 'M' from PATH: Reason
El cargador falló, agotó el tiempo o no escribió respuestaparse transform loader failed: ...
Formas devueltas no válidasel diagnóstico del parser en la ubicación de la forma

Opciones

CLIProyecto ([targets.options])Significado
--parse-transform Mparse_transforms = ["m"]Aplica M a cada módulo, antes de sus propias transformaciones -compile (primero las de la CLI)
--transform-path DIRtransform_paths = ["deps/*/ebin"]Ruta de código para archivos .beam de transformaciones precompiladas; patrones como en source_search_paths
--erl PATH o none—Ejecutable erl del anfitrión; none nunca ejecuta código del anfitrión
--print-abstr—Imprime las formas después de las transformaciones en lugar de compilar

Un proyecto que usa su propia transformación y una precompilada de una dependencia:

[[targets]]
name = "app"
sources = ["src/*.erl"]

[targets.options]
source_search_paths = ["transforms"]  # transforms/pt_rewrite.erl, compiled for each build
parse_transforms = ["pt_dep"]         # applied to every module of the target
transform_paths = ["deps/*/ebin"]     # deps/ptdep/ebin/pt_dep.beam, built with erlc first