Clause
← Toda a documentação

O original em inglês mudou desde esta tradução, pelo que partes dela podem estar desatualizadas. Ler em inglês

Traduzido do original em inglês · 1e3a39c8-dirty · 2026-10-26

Parse transforms

Utilizar parse transforms

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

O Clause aplica {parse_transform, Module} tal como o erlc. Exporta o módulo analisado como formas do formato abstrato do OTP, executa o módulo de transform num Erlang/OTP 29 instalado no anfitrião, importa as formas devolvidas como a nova sintaxe do módulo e compila-a (compiler/src/transforms/). O OTP só é necessário enquanto se compila um módulo que usa um transform; os programas gerados nunca precisam dele.

Comportamento do OTP

A partir de lib/compiler/src/compile.erl fixado (transform_module, foldl_transform, maybe_strip_columns) e observado com o OTP 29.1.1:

Pipeline do Clause

  1. Pré-processar e analisar o módulo. Os erros do pré-processador ou da análise sintática param aqui; os transforms não correm num módulo com erros (difere do OTP, que passa os erros adiante como formas).
  2. Recolher os transforms pela ordem do OTP: as opções --parse-transform e os parse_transforms do projeto, depois os atributos -compile. Um módulo sem nenhum salta o resto e nunca inicia o OTP.
  3. Remover essas opções das formas e exportá-las: anotações {Line, Column} tal como o erl_parse as atribui, os atributos file do epp e eof. As funções predefinidas ainda não fazem parte das formas.
  4. Executar o carregador uma vez para o módulo.
  5. Importar as formas devolvidas transformando-as em tokens localizados nas suas anotações e analisando-as com o parser do Clause; um atributo file muda o ficheiro das formas seguintes, como no erl_lint.
  6. Acrescentar as funções predefinidas e depois fazer o lint, analisar e compilar como habitualmente.

As ações que executam transforms estão listadas em utilizar parse transforms.

Intercâmbio

Importar formas

As formas devolvidas, e as entradas .abstr (posicionais ou nos sources do projeto), tornam-se tokens localizados nas suas anotações e passam pelo parser do Clause, pelo que recebem as mesmas verificações de sintaxe que o código-fonte; a precedência dos operadores decide onde são acrescentados parênteses. São aceites {Line, Column}, um Line simples (mostrado como file:line:) e listas de propriedades com location e file; generated é ignorado. A linha 0, que o transform qlc do OTP atribui ao código gerado, assume a localização do nó importado antes dela. Um atributo file muda o ficheiro das formas seguintes; quando esse ficheiro existe, o código importado mantém as suas linhas de depuração e os comentários de código-fonte. As formas mal formadas são erros na sua anotação (unknown expression bogus, expected {Name, Arity}, invalid annotation) e o módulo falha; as formas {error, _}/{warning, _} do OTP são comunicadas com o seu descritor, cuja formatação precisa do OTP. Importar um módulo exportado e exportá-lo de novo dá as mesmas formas.

OTP do anfitrião

Carregador

compiler/src/transforms/loader/clause_transform_loader.erl pertence ao projeto e está embutido no clau. Cada execução escreve-o, juntamente com o pedido, num diretório temporário novo, clause-transform-* abaixo de <build_dir>/tmp (diretório de compilação) ou, para entradas posicionais sem --build-dir, no diretório temporário do sistema, removido após a execução; o erl compila e carrega o carregador em memória. O pedido contém as formas, as opções, a lista de transforms, o nome do ficheiro do módulo e os códigos-fonte de transforms do projeto a compilar. Com um diretório de compilação, a resposta e a saída do carregador são guardadas na cache de compilação e reutilizadas enquanto o pedido e tudo aquilo de que depende se mantiverem inalterados; a lista de módulos sticky do anfitrião é guardada da mesma forma. O carregador:

  1. verifica a release do OTP;
  2. compila em memória os códigos-fonte de transforms do projeto pedidos e carrega-os;
  3. para cada transform: carrega-o, retira as colunas conforme parse_transform_info/0 pedir, chama parse_transform/2 e formata os avisos e os erros como {File, Location, Text} com Module:format_error/1;
  4. escreve {ok, Forms, Warnings} ou {error, Errors, Warnings} e termina.

Módulos de transform

Um módulo de transform é procurado por esta ordem:

  1. Ficheiros .beam pré-compilados nos diretórios de --transform-path DIR (repetível) e da opção de projeto transform_paths, que entram no caminho de código do carregador antes dos do OTP. É a única forma de usar um transform que vem de uma biblioteca de dependência: compile primeiro essa biblioteca com o OTP do anfitrião (por exemplo erlc -o deps/lib/ebin deps/lib/src/*.erl); o Clause nunca compila dependências. O .beam tem de vir da release principal do OTP do anfitrião.
  2. Os códigos-fonte do projeto atual: um Module.erl no lote ou nos seus source_search_paths, compilado pelo carregador em memória com os diretórios de include e as definições do alvo em cada execução, juntamente com os módulos do projeto que chama pelo nome (encontrados da mesma forma). A pesquisa ignora os códigos-fonte que ignore_dirs ou ignore_modules deixam de fora, tal como faz a pesquisa de módulos. O Clause procura estes códigos-fonte uma vez por lote e mantém-nos em memória; o carregador continua a compilá-los para cada módulo em que o transform corre, pelo que convém pré-compilar um transform grande para poupar esse tempo. Os módulos que o anfitrião carrega dos seus diretórios sticky (kernel, stdlib, compiler, como ms_transform ou qlc_pt quando os códigos-fonte do OTP estão num caminho de pesquisa) não podem ser recarregados, pelo que são usados os próprios módulos do anfitrião: os seus códigos-fonte, e os módulos que só eles chamam, não são procurados nem compilados. O Clause pergunta ao erl do anfitrião por estes módulos uma vez por invocação.
  3. Os próprios módulos do OTP (ms_transform, qlc) a partir do caminho de código do anfitrião. O OTP inclui erl_id_trans apenas como código-fonte de exemplo, não como módulo. Os seus cabeçalhos resolvem-se como qualquer include_lib (pré-processador): mapeie a aplicação, como em --app-dir "stdlib=C:/Program Files/Erlang OTP/lib/stdlib-8.1" para include_lib("stdlib/include/ms_transform.hrl"), ou indique o transform diretamente com -compile({parse_transform, ms_transform}).

Compilar um transform do projeto pode falhar onde a sua compilação real tem sucesso: cabeçalhos de dependências, um transform do transform, ou macros que só outra ferramenta de compilação define. O Clause comunica então as mensagens do compilador em relação ao código-fonte do transform, com a sugestão de o pré-compilar num .beam com o OTP do anfitrião e de passar o seu diretório com --transform-path. Um transform do projeto também é compilado pelo Clause como um módulo comum quando faz parte do lote; as suas próprias chamadas (erl_syntax, erl_parse, ...) podem então precisar de módulos de biblioteca que o Clause não inclui, pelo que convém manter os transforms fora dos sources de um alvo quando só o transform precisa deles.

Diagnósticos

SituaçãoMensagem
Sem OTP no anfitriãoFile: parse transform 'M' needs Erlang/OTP 29 on the host; pass --erl
--erl não indica nenhum executávelFile: erl not found: PATH
Release do OTP erradaparse transforms need Erlang/OTP 29 on the host, found N
Transform não encontradoFile: undefined parse transform 'M' (texto do OTP)
O transform lançou uma exceçãoFile: error in parse transform 'M': ... (texto do OTP)
Aviso ou erro do transformFile:Line:Column: Text tal como devolvido
O código-fonte do transform não compilaas suas mensagens e depois a sugestão de pré-compilação
Um caminho de transforms não existeFile: parse transform path is not a directory: DIR
Um .beam num caminho de transforms não carregaFile: cannot load parse transform 'M' from PATH: Reason
O carregador falhou, esgotou o tempo ou não escreveu respostaparse transform loader failed: ...
Formas devolvidas inválidaso diagnóstico do parser na localização da forma

Opções

CLIProjeto ([targets.options])Significado
--parse-transform Mparse_transforms = ["m"]Aplica M a todos os módulos, antes dos seus próprios transforms -compile (os da CLI primeiro)
--transform-path DIRtransform_paths = ["deps/*/ebin"]Caminho de código para ficheiros .beam de transforms pré-compilados; padrões como em source_search_paths
--erl PATH ou none—Executável erl do anfitrião; none nunca executa código do anfitrião
--print-abstr—Imprime as formas depois dos transforms em vez de compilar

Um projeto que usa o seu próprio transform e um pré-compilado de uma dependência:

[[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