Clause
← All dokumentation

Det engelska originalet har ändrats sedan översättningen gjordes, så delar av den kan vara inaktuella. Läs på engelska

Översatt från det engelska originalet · 1e3a39c8-dirty · 2026-10-26

Parse transforms

Använda parse transforms

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

Clause tillämpar {parse_transform, Module} på samma sätt som erlc. Den exporterar den tolkade modulen som former i OTP:s abstrakta format, kör transformmodulen på ett Erlang/OTP 29 som är installerat på värden, importerar de returnerade formerna som modulens nya syntax och kompilerar den (compiler/src/transforms/). OTP behövs endast medan en modul som använder en transform kompileras; genererade program behöver det aldrig.

OTP:s beteende

Från den fastlåsta lib/compiler/src/compile.erl (transform_module, foldl_transform, maybe_strip_columns) och observerat under OTP 29.1.1:

Clauses kedja

  1. Förbehandla och tolka modulen. Fel från preprocessorn eller tolkningen stoppar här; transformer körs inte på en modul med fel (skiljer sig från OTP, som skickar vidare felen som former).
  2. Samla transformerna i OTP:s ordning: --parse-transform-alternativ och projektets parse_transforms, sedan -compile-attribut. En modul utan några hoppar över resten och startar aldrig OTP.
  3. Ta bort dessa alternativ från formerna och exportera dem: annoteringar {Line, Column} så som erl_parse tilldelar dem, epp:s file-attribut och eof. De fördefinierade funktionerna är ännu inte en del av formerna.
  4. Kör laddaren en gång för modulen.
  5. Importera de returnerade formerna genom att göra dem till token placerade vid sina annoteringar och tolka dem med Clauses parser; ett file-attribut byter fil för de följande formerna som i erl_lint.
  6. Lägg till de fördefinierade funktionerna, och kör sedan lint, analys och kompilering som vanligt.

De åtgärder som kör transformer listas under använda parse transforms.

Utbyte

Importera former

Returnerade former, och .abstr-indata (positionella eller projektets sources), blir token placerade vid sina annoteringar och går genom Clauses parser, så att de får samma syntaxkontroller som källkod; operatorprioriteten avgör var parenteser läggs till. {Line, Column}, en ensam Line (visas som file:line:) och egenskapslistor med location och file godtas; generated ignoreras. Rad 0, som OTP:s qlc-transform ger sin genererade kod, tar platsen för den nod som importerades före den. Ett filattribut byter fil för de följande formerna; när den filen finns behåller importerad kod sina felsökningsrader och källkommentarer. Felformade former är fel vid sin annotering (unknown expression bogus, expected {Name, Arity}, invalid annotation) och modulen misslyckas; OTP:s {error, _}/{warning, _}-former rapporteras med sin deskriptor, som kräver OTP för att formateras. Att importera en exporterad modul och exportera den igen ger samma former.

Värdens OTP

Laddare

compiler/src/transforms/loader/clause_transform_loader.erl ägs av projektet och är inbäddad i clau. Varje körning skriver den och begäran i en ny temporär katalog, clause-transform-* under <build_dir>/tmp (byggkatalog) eller, för positionella indata utan --build-dir, systemets temporära katalog, som tas bort efter körningen; erl kompilerar och laddar laddaren i minnet. Begäran innehåller formerna, alternativen, transformlistan, modulens filnamn och de projekttransformer i källform som ska byggas. Med en byggkatalog behålls svaret och laddarens utdata i byggcachen och återanvänds så länge begäran och allt den beror på är oförändrat; värdens lista över klibbiga (sticky) moduler behålls på samma sätt. Laddaren:

  1. kontrollerar OTP-versionen;
  2. kompilerar de begärda projekttransformerna i källform i minnet och laddar dem;
  3. för varje transform: laddar den, tar bort kolumner så som parse_transform_info/0 begär, anropar parse_transform/2 och formaterar varningar och fel till {File, Location, Text} med Module:format_error/1;
  4. skriver {ok, Forms, Warnings} eller {error, Errors, Warnings} och stannar.

Transformmoduler

En transformmodul slås upp i denna ordning:

  1. Förkompilerade .beam-filer i katalogerna från --transform-path DIR (kan upprepas) och projektalternativet transform_paths, som läggs i laddarens kodsökväg före OTP:s. Detta är det enda sättet att använda en transform som kommer från ett beroendebibliotek: bygg det biblioteket med värdens OTP först (till exempel erlc -o deps/lib/ebin deps/lib/src/*.erl); Clause bygger aldrig beroenden. .beam-filen måste komma från värdens OTP-huvudversion.
  2. Det aktuella projektets källfiler: en Module.erl i batchen eller dess source_search_paths, kompilerad av laddaren i minnet med målets include-kataloger och definitioner vid varje körning, tillsammans med de projektmoduler som den anropar via namn (hittade på samma sätt). Sökningen hoppar över källfiler som ignore_dirs eller ignore_modules utelämnar, som modulsökningen gör. Clause letar efter dessa källfiler en gång per batch och behåller dem i minnet; laddaren kompilerar dem ändå för varje modul som transformen körs på, så förkompilera en stor transform för att spara den tiden. Moduler som värden laddar från sina klibbiga kataloger (kernel, stdlib, compiler, till exempel ms_transform eller qlc_pt när OTP:s källkod finns på en söksökväg) kan inte laddas om, så värdens egna moduler används: deras källfiler, och de moduler som endast de anropar, söks inte efter och kompileras inte. Clause frågar värdens erl efter dessa moduler en gång per anrop.
  3. OTP:s egna moduler (ms_transform, qlc) från värdens kodsökväg. OTP levererar erl_id_trans endast som en exempelkällfil, inte som en modul. Deras headers löses som varje include_lib (preprocessor): avbilda applikationen, som i --app-dir "stdlib=C:/Program Files/Erlang OTP/lib/stdlib-8.1" för include_lib("stdlib/include/ms_transform.hrl"), eller ange transformen direkt med -compile({parse_transform, ms_transform}).

Att kompilera en projekttransform kan misslyckas där dess verkliga bygge lyckas: headers från beroenden, en transform av transformen, eller makron som endast ett annat byggverktyg definierar. Clause rapporterar då kompilatorns meddelanden mot transformens källfil med tipset att förkompilera den till en .beam med värdens OTP och skicka dess katalog med --transform-path. En projekttransform kompileras också av Clause som en vanlig modul när den ingår i batchen; dess egna anrop (erl_syntax, erl_parse, ...) kan då behöva biblioteksmoduler som Clause inte levererar, så håll transformer utanför ett måls sources när endast transformen behöver dem.

Diagnostik

SituationMeddelande
Inget OTP på värdenFile: parse transform 'M' needs Erlang/OTP 29 on the host; pass --erl
--erl anger ingen körbar filFile: erl not found: PATH
Fel OTP-versionparse transforms need Erlang/OTP 29 on the host, found N
Transformen hittades inteFile: undefined parse transform 'M' (OTP:s text)
Transformen kastadeFile: error in parse transform 'M': ... (OTP:s text)
Varning eller fel från transformenFile:Line:Column: Text som returnerat
Transformens källfil kompileras intedess meddelanden, sedan tipset om förkompilering
En transformsökväg finns inteFile: parse transform path is not a directory: DIR
En .beam på en transformsökväg laddas inteFile: cannot load parse transform 'M' from PATH: Reason
Laddaren misslyckades, överskred tidsgränsen eller skrev inget svarparse transform loader failed: ...
Ogiltiga returnerade formerparserns diagnostik vid formens plats

Alternativ

CLIProjekt ([targets.options])Betydelse
--parse-transform Mparse_transforms = ["m"]Tillämpa M på varje modul, före dess egna -compile-transformer (CLI:ns först)
--transform-path DIRtransform_paths = ["deps/*/ebin"]Kodsökväg för förkompilerade .beam-filer för transformer; mönster som i source_search_paths
--erl PATH eller none—Värdens körbara erl; none kör aldrig kod på värden
--print-abstr—Skriv ut formerna efter transformer i stället för att kompilera

Ett projekt som använder sin egen transform och en förkompilerad från ett beroende:

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