Clause
← Gesamte Dokumentation

Das englische Original wurde seit dieser Übersetzung geändert, daher können Teile davon veraltet sein. Auf Englisch lesen

Übersetzt aus dem englischen Original · 1e3a39c8-dirty · 2026-10-26

Parse-Transforms

Parse-Transforms verwenden

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

Clause wendet {parse_transform, Module} so an, wie es erlc tut. Es exportiert das geparste Modul als Formen im Abstract Format von OTP, führt das Transform-Modul auf einem auf dem Host installierten Erlang/OTP 29 aus, importiert die zurückgegebenen Formen als neue Syntax des Moduls und kompiliert diese (compiler/src/transforms/). OTP wird nur gebraucht, während ein Modul kompiliert wird, das einen Transform verwendet; erzeugte Programme brauchen es nie.

Verhalten von OTP

Aus dem festgelegten lib/compiler/src/compile.erl (transform_module, foldl_transform, maybe_strip_columns) und beobachtet unter OTP 29.1.1:

Pipeline von Clause

  1. Das Modul präprozessieren und parsen. Präprozessor- oder Parse-Fehler beenden hier; Transforms laufen nicht auf einem Modul mit Fehlern (abweichend von OTP, das die Fehler als Formen weitergibt).
  2. Die Transforms in der Reihenfolge von OTP sammeln: Optionen --parse-transform und die parse_transforms des Projekts, dann -compile-Attribute. Ein Modul ohne Transforms überspringt den Rest und startet OTP nie.
  3. Diese Optionen aus den Formen entfernen und sie exportieren: Annotationen {Line, Column}, wie erl_parse sie vergibt, die file-Attribute von epp und eof. Die vordefinierten Funktionen sind noch nicht Teil der Formen.
  4. Den Loader einmal für das Modul ausführen.
  5. Die zurückgegebenen Formen importieren, indem sie in Tokens umgewandelt werden, die an ihren Annotationen verortet sind, und mit dem Parser von Clause geparst werden; ein file-Attribut wechselt die Datei der folgenden Formen wie in erl_lint.
  6. Die vordefinierten Funktionen hinzufügen, dann wie üblich linten, analysieren und kompilieren.

Die Aktionen, die Transforms ausführen, stehen unter Parse-Transforms verwenden.

Austausch

Formen importieren

Zurückgegebene Formen und .abstr-Eingaben (positional oder Projekt-sources) werden zu Tokens, die an ihren Annotationen verortet sind, und durchlaufen den Parser von Clause, sodass sie dieselben Syntaxprüfungen wie Quelltext erhalten; die Operatorrangfolge entscheidet, wo Klammern ergänzt werden. {Line, Column}, ein bloßes Line (angezeigt als file:line:) und Property-Listen mit location und file werden akzeptiert; generated wird ignoriert. Zeile 0, mit der der qlc-Transform von OTP erzeugten Code versieht, übernimmt die Position des zuvor importierten Knotens. Ein file-Attribut wechselt die Datei der folgenden Formen; existiert diese Datei, behält importierter Code seine Debug-Zeilen und Quelltextkommentare. Fehlerhafte Formen sind Fehler an ihrer Annotation (unknown expression bogus, expected {Name, Arity}, invalid annotation), und das Modul scheitert; die Formen {error, _}/{warning, _} von OTP werden mit ihrem Deskriptor gemeldet, dessen Formatierung OTP braucht. Ein exportiertes Modul zu importieren und erneut zu exportieren ergibt dieselben Formen.

OTP des Hosts

Loader

compiler/src/transforms/loader/clause_transform_loader.erl gehört dem Projekt und ist in clau eingebettet. Jeder Lauf schreibt ihn und die Anfrage in ein frisches temporäres Verzeichnis, clause-transform-* unter <build_dir>/tmp (Build-Verzeichnis) oder, für positionale Eingaben ohne --build-dir, im temporären Verzeichnis des Systems, das nach dem Lauf entfernt wird; erl kompiliert und lädt den Loader im Speicher. Die Anfrage enthält die Formen, die Optionen, die Transform-Liste, den Dateinamen des Moduls und die zu bauenden Transform-Quellen des Projekts. Mit einem Build-Verzeichnis werden die Antwort und die Ausgabe des Loaders im Build-Cache behalten und wiederverwendet, solange die Anfrage und alles, wovon sie abhängt, unverändert sind; die Liste der Sticky-Module des Hosts wird auf dieselbe Weise behalten. Der Loader:

  1. prüft das OTP-Release;
  2. kompiliert die angefragten Transform-Quellen des Projekts im Speicher und lädt sie;
  3. für jeden Transform: lädt ihn, entfernt Spalten, wie es parse_transform_info/0 verlangt, ruft parse_transform/2 auf und formatiert Warnungen und Fehler mit Module:format_error/1 zu {File, Location, Text};
  4. schreibt {ok, Forms, Warnings} oder {error, Errors, Warnings} und hält an.

Transform-Module

Ein Transform-Modul wird in dieser Reihenfolge gesucht:

  1. Vorkompilierte .beam-Dateien in den Verzeichnissen von --transform-path DIR (wiederholbar) und der Projektoption transform_paths, die vor denen von OTP in den Codepfad des Loaders kommen. Das ist der einzige Weg, einen Transform zu verwenden, der aus einer abhängigen Bibliothek stammt: Diese Bibliothek zuerst mit dem OTP des Hosts bauen (zum Beispiel erlc -o deps/lib/ebin deps/lib/src/*.erl); Clause baut nie Abhängigkeiten. Die .beam muss aus dem OTP-Hauptrelease des Hosts stammen.
  2. Die Quellen des aktuellen Projekts: eine Module.erl im Batch oder in seinen source_search_paths, bei jedem Lauf vom Loader im Speicher mit den Include-Verzeichnissen und Defines des Ziels kompiliert, zusammen mit den Projektmodulen, die er per Namen aufruft (auf dieselbe Weise gefunden). Die Suche übergeht Quellen, die ignore_dirs oder ignore_modules auslassen, wie es die Modulsuche tut. Clause sucht diese Quellen einmal pro Batch und behält sie im Speicher; der Loader kompiliert sie dennoch für jedes Modul, auf dem der Transform läuft, daher lohnt es sich, einen großen Transform vorzukompilieren, um diese Zeit zu sparen. Module, die der Host aus seinen Sticky-Verzeichnissen lädt (kernel, stdlib, compiler, etwa ms_transform oder qlc_pt, wenn die Quellen von OTP auf einem Suchpfad liegen), lassen sich nicht neu laden, daher werden die eigenen Module des Hosts verwendet: Ihre Quellen und die Module, die nur sie aufrufen, werden weder gesucht noch kompiliert. Clause fragt das erl des Hosts einmal pro Aufruf nach diesen Modulen.
  3. Die eigenen Module von OTP (ms_transform, qlc) aus dem Codepfad des Hosts. OTP liefert erl_id_trans nur als Beispielquelle, nicht als Modul. Ihre Header werden wie jedes include_lib aufgelöst (Präprozessor): die Anwendung zuordnen, wie in --app-dir "stdlib=C:/Program Files/Erlang OTP/lib/stdlib-8.1" für include_lib("stdlib/include/ms_transform.hrl"), oder den Transform direkt mit -compile({parse_transform, ms_transform}) nennen.

Das Kompilieren eines Projekt-Transforms kann scheitern, wo sein echter Build gelingt: Header von Abhängigkeiten, ein Transform des Transforms oder Makros, die nur ein anderes Build-Werkzeug definiert. Clause meldet dann die Meldungen des Compilers zur Quelle des Transforms mit dem Hinweis, ihn mit dem OTP des Hosts zu einer .beam vorzukompilieren und ihr Verzeichnis mit --transform-path zu übergeben. Ein Projekt-Transform wird von Clause auch als gewöhnliches Modul kompiliert, wenn er Teil des Batches ist; seine eigenen Aufrufe (erl_syntax, erl_parse, ...) können dann Bibliotheksmodule brauchen, die Clause nicht mitliefert, daher Transforms aus den sources eines Ziels heraushalten, wenn nur der Transform sie braucht.

Diagnosen

SituationMeldung
Kein OTP auf dem HostFile: parse transform 'M' needs Erlang/OTP 29 on the host; pass --erl
--erl nennt keine ausführbare DateiFile: erl not found: PATH
Falsches OTP-Releaseparse transforms need Erlang/OTP 29 on the host, found N
Transform nicht gefundenFile: undefined parse transform 'M' (Text von OTP)
Transform hat ausgelöstFile: error in parse transform 'M': ... (Text von OTP)
Warnung oder Fehler des TransformsFile:Line:Column: Text wie zurückgegeben
Transform-Quelle kompiliert nichtihre Meldungen, dann der Hinweis zum Vorkompilieren
Ein Transform-Pfad existiert nichtFile: parse transform path is not a directory: DIR
Eine .beam auf einem Transform-Pfad lädt nichtFile: cannot load parse transform 'M' from PATH: Reason
Loader gescheitert, Zeit überschritten oder keine Antwort geschriebenparse transform loader failed: ...
Ungültige zurückgegebene Formendie Diagnose des Parsers am Ort der Form

Optionen

CLIProjekt ([targets.options])Bedeutung
--parse-transform Mparse_transforms = ["m"]M auf jedes Modul anwenden, vor dessen eigenen -compile-Transforms (die der CLI zuerst)
--transform-path DIRtransform_paths = ["deps/*/ebin"]Codepfad für vorkompilierte Transform-.beam-Dateien; Muster wie in source_search_paths
--erl PATH oder none—erl-Datei des Hosts; none führt nie Host-Code aus
--print-abstr—Die Formen nach den Transforms ausgeben, statt zu kompilieren

Ein Projekt, das einen eigenen Transform und einen vorkompilierten aus einer Abhängigkeit verwendet:

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