Sprachen: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
PluginPrep.h legt den Präprozessor auf zwei Weisen offen. Ein Abonnement
von 39 Ereignisarten liefert eine reine Lesespur von allem, was der Präprozessor
tut: Dateieintritt, Makrodefinition und -expansion, Auswertung von Bedingungen,
Pragmas. Sechs Phasen gehen weiter und lassen Sie das Ergebnis umschreiben:
ein #include umleiten, die Expansionstoken eines Makros ersetzen, ein Pragma
selbst behandeln oder __has_feature anders beantworten.
#include "neverc/Plugin/PluginPrep.h"
Bootstrap->QueryInterface(
Bootstrap->Context,
(NevercInterfaceID){NEVERC_INTERFACE_PREP_HIGH, NEVERC_INTERFACE_PREP_LOW},
NEVERC_PREP_API_MAJOR, NEVERC_PREP_API_MINOR, &Table, &Minor, &TableSize);Die 230 Tokenarten (NEVERC_TOKEN_KIND_COUNT) und die Präprozessor-
Schlüsselwortarten stammen aus Schema/PluginPrepSchema.inc, das der Header
einbindet und dessen Capability-Major gleich NEVERC_PREP_API_MAJOR sein muss —
eine Abweichung ist ein Übersetzungsfehler, keine Überraschung zur Laufzeit.
Jede Art trägt außerdem eine Kategorie: NEVERC_TOKEN_CATEGORY_SPECIAL,
COMMENT, IDENTIFIER, LITERAL, PUNCTUATOR, KEYWORD oder ANNOTATION.
| Phase | Richtlinie | Eingabe → Ausgabe |
|---|---|---|
neverc.prep.token |
OBSERVABLE, INTERCEPTABLE, REPLACEABLE | ein Token → Tokenliste |
neverc.prep.build_token_stream |
ebenso | Bereich → Tokenstrom |
neverc.prep.include.intercept |
ebenso | Include-Anfrage → Include-Entscheidung |
neverc.prep.macro.intercept |
ebenso | Makrooperation → Aktion + Token |
neverc.prep.pragma.intercept |
ebenso | Pragma → Aktion + Token |
neverc.prep.feature_query.intercept |
ebenso | __has_*-Anfrage → Wert |
Jede besitzt auf NevercPrepAPI ein Paar Get<Kind>PhaseInput und
Create<Kind>PhaseOutput; die Create-Hälfte nimmt die
NevercPhaseContinuation des Interzeptors entgegen, sodass eine Ausgabe nur von
innerhalb der Phase erzeugt werden kann, der sie gehört.
typedef struct NevercTokenInfo {
NevercABITableHeader Header;
NevercTokenKind Kind;
NevercTokenFlags Flags;
NevercTokenOriginKind Origin;
uint32_t Reserved;
NevercStringView Spelling;
NevercSourceLocation Location;
NevercSourceRange Range;
NevercIdentifierHandle Identifier;
NevercMacroDefinitionHandle MacroDefinition;
} NevercTokenInfo;Origin ist NEVERC_TOKEN_ORIGIN_FILE, MACRO_REPLACEMENT, MACRO_ARGUMENT
oder SYNTHESIZED — so unterscheidet man ein vom Benutzer getipptes Token von
einem, das ein Makro erzeugt hat.
Die Flags sind die Buchführung des Präprozessors selbst und werden wichtig, sobald Sie Token synthetisieren:
| Flag | Bedeutung |
|---|---|
NEVERC_TOKEN_FLAG_START_OF_LINE |
Erstes Token seiner Zeile |
_LEADING_SPACE |
Davor steht Leerraum |
_DISABLE_EXPANSION |
Dieses Token nicht makroexpandieren |
_NEEDS_CLEANING |
Die Schreibweise enthält maskierte Zeilenumbrüche oder Trigraphen |
_LEADING_EMPTY_MACRO |
Unmittelbar davor expandierte ein leeres Makro |
_HAS_UCN |
Enthält einen universellen Zeichennamen |
_IGNORED_COMMA, _COMMA_AFTER_ELIDED |
Buchführung zur variadischen Kommaauslassung |
_STRINGIFIED_IN_MACRO |
Von # erzeugt |
_REINJECTED |
Zurück in den Tokenstrom eingespeist |
NEVERC_TOKEN_FLAG_ALL ist die Maske aller definierten Bits. Stapellesevorgänge
laufen über GetTokenInfoBatch; ein ganzer Strom wird entweder als leichte
Sicht auf NevercTokenView-Datensätze über GetTokenStreamView gelesen oder
Handle für Handle mit GetTokenStreamToken. Ein Strom fasst höchstens
NEVERC_PREP_TOKEN_STREAM_MAX_TOKENS (16.777.216) Token.
NevercIdentifierHandle Identifier;
Prep->GetOrCreateIdentifier(Prep->Context, Task, SV("MY_MACRO"), &Identifier);
NevercMacroDefinitionHandle Definition;
Prep->GetMacroDefinitionForIdentifier(Prep->Context, Task, Identifier,
&Definition);
NevercMacroDefinitionInfo Info = {0};
Info.Header = (NevercABITableHeader){sizeof(Info), NEVERC_PREP_API_MAJOR,
NEVERC_PREP_API_MINOR, 0};
Prep->GetMacroDefinitionInfo(Prep->Context, Task, Definition, &Info);NevercMacroDefinitionInfo meldet den Namen, die definierende Direktive, die
Positionen von Definition, Ende und Aufhebung, die Anzahl der Parameter und
Ersatztoken sowie Flags: NEVERC_MACRO_FUNCTION_LIKE, VARIADIC,
C99_VARIADIC, GNU_VARIADIC, HAS_VA_OPT, BUILTIN und COMMA_PASTING.
Einzelne Parameter und Ersatztoken kommen von GetMacroParameter und
GetMacroReplacementToken.
NevercIdentifierInfo ergänzt Tokenart, Präprozessor-Schlüsselwortart,
Builtin-ID und Flags wie NEVERC_IDENTIFIER_KEYWORD, _HAS_MACRO,
_POISONED und _RESERVED.
An einer Expansionsstelle meldet GetMacroArgumentInfo die Argumentanzahl und
ob variadische Argumente ausgelassen wurden, und
GetMacroArgumentTokenStream liefert die Token jedes Arguments.
Ein einziger Rückruf empfängt alle abonnierten Ereignisse. Die Maske wird aus den Ereignisarten gebaut, die Sie interessieren:
static NevercStatus NEVERC_CALL
on_event(NevercTaskHandle Task, const NevercPrepEvent *Event, void *UserData) {
switch (Event->Kind) {
case NEVERC_PREP_EVENT_INCLUSION_DIRECTIVE:
/* Event->Payload.Include.Filename, .IsAngled, .File, .FilenameRange */
break;
case NEVERC_PREP_EVENT_MACRO_EXPANDS:
/* Event->Payload.Macro.NameToken, .Definition, .Arguments, .Range */
break;
case NEVERC_PREP_EVENT_IFDEF:
/* Event->Payload.Condition.Value ist NOT_EVALUATED, FALSE oder TRUE */
break;
default:
break;
}
return neverc_status_ok();
}
NevercPrepObserverDescriptor Observer = {0};
Observer.Header = (NevercABITableHeader){sizeof(Observer),
NEVERC_PREP_API_MAJOR,
NEVERC_PREP_API_MINOR, 0};
Observer.Events = NEVERC_PREP_EVENT_MASK(NEVERC_PREP_EVENT_INCLUSION_DIRECTIVE) |
NEVERC_PREP_EVENT_MASK(NEVERC_PREP_EVENT_MACRO_EXPANDS) |
NEVERC_PREP_EVENT_MASK(NEVERC_PREP_EVENT_IFDEF);
Observer.Callback = on_event;
Observer.UserData = State;
Prep->RegisterEventObserver(Prep->Context, Task, &Observer);NEVERC_PREP_EVENT_MASK_ALL abonniert alles. Die 39 Arten, gruppiert nach dem
Union-Glied der Nutzlast, das sie verwenden:
| Nutzlast | Ereignisse |
|---|---|
File |
FILE_CHANGED, LEXED_FILE_CHANGED, FILE_SKIPPED, FILE_NOT_FOUND, END_OF_MAIN_FILE |
Include |
INCLUSION_DIRECTIVE |
Text |
IDENT, PRAGMA_DIRECTIVE, PRAGMA_COMMENT, PRAGMA_MARK, PRAGMA_DETECT_MISMATCH, PRAGMA_DEBUG, PRAGMA_MESSAGE, PRAGMA_DIAGNOSTIC_PUSH, PRAGMA_DIAGNOSTIC_POP, PRAGMA_DIAGNOSTIC, PRAGMA_WARNING, PRAGMA_WARNING_PUSH, PRAGMA_WARNING_POP, PRAGMA_EXEC_CHARSET_PUSH, PRAGMA_EXEC_CHARSET_POP, PRAGMA_ASSUME_NONNULL_BEGIN, PRAGMA_ASSUME_NONNULL_END, SOURCE_RANGE_SKIPPED |
Macro |
MACRO_EXPANDS, MACRO_DEFINED, MACRO_UNDEFINED, DEFINED, HAS_INCLUDE |
Condition |
IF, ELIF, IFDEF, ELIFDEF, ELIFDEF_SKIPPED, IFNDEF, ELIFNDEF, ELIFNDEF_SKIPPED, ELSE, ENDIF |
NevercPrepFileEvent.Reason unterscheidet NEVERC_PREP_FILE_ENTER, EXIT,
SYSTEM_HEADER_PRAGMA und RENAME. Ereignisse sind schreibgeschützt: der
Datensatz und jede Sicht darin sind für die Dauer des Rückrufs geliehen, während
in einem Ereignis veröffentlichte Handles in den umgebenden Aufgabenbereich
hochgestuft werden.
NevercPrepIncludePhaseInput In = {0};
In.Header = (NevercABITableHeader){sizeof(In), NEVERC_PREP_API_MAJOR,
NEVERC_PREP_API_MINOR, 0};
Prep->GetIncludePhaseInput(Prep->Context, Frame, Frame->Input, &In);
NevercPrepIncludePhaseOutput Out = {0};
Out.Header = In.Header;
if (view_equals(In.Filename, "legacy.h")) {
Out.Action = NEVERC_PREP_INCLUDE_REDIRECT;
Out.Filename = SV("modern.h");
Out.IsAngled = NEVERC_FALSE;
} else {
Out.Action = NEVERC_PREP_INCLUDE_CONTINUE;
}
NevercArtifactHandle Output;
Prep->CreateIncludePhaseOutput(Prep->Context, Frame, Continuation, &Out,
&Output);Die Aktionen sind NEVERC_PREP_INCLUDE_CONTINUE, _SKIP und _REDIRECT. Die
Eingabe meldet zusätzlich IsImport und IsIncludeNext, sodass sich #import
und #include_next unterscheiden lassen.
Die Eingabe der Makrophase trägt die gerade ausgeführte Operation —
NEVERC_PREP_MACRO_DEFINE, _UNDEFINE, _EXPAND oder _EXPAND_BUILTIN —
zusammen mit dem Namenstoken, der Definition, den Argumenten und den
Ersatztoken, die der Präprozessor verwenden wollte.
NevercPrepMacroPhaseOutput Out = {0};
Out.Header = /* … */;
Out.Action = NEVERC_PREP_MACRO_REPLACE_TOKENS;
Out.Tokens = MyTokens; /* const NevercTokenHandle * */
Out.TokenCount = MyTokenCount;
Prep->CreateMacroPhaseOutput(Prep->Context, Frame, Continuation, &Out, &Output);NEVERC_PREP_MACRO_CONTINUE behält das eingebaute Verhalten bei, und
_SUPPRESS expandiert zu nichts.
Synthetisierte Token stammen aus einem Erbauer, der die Kombination aus Art, Schreibweise und Bezeichner vor dem Festschreiben prüft:
NevercTokenBuilderHandle Builder;
Prep->CreateTokenBuilder(Prep->Context, Task, &Builder);
Prep->TokenBuilderSetLiteral(Prep->Context, Task, Builder,
NEVERC_TOKEN_NUMERIC_CONSTANT, SV("42"));
Prep->TokenBuilderSetLocation(Prep->Context, Task, Builder, Location);
Prep->TokenBuilderSetFlags(Prep->Context, Task, Builder,
NEVERC_TOKEN_FLAG_LEADING_SPACE);
NevercTokenHandle Token;
Prep->TokenBuilderCommit(Prep->Context, Task, Builder, &Token);
Prep->DestroyTokenBuilder(Prep->Context, Task, Builder);Verwenden Sie TokenBuilderSetKind für Satzzeichen und Schlüsselwörter und
TokenBuilderSetIdentifier für Bezeichner. Die Tokenart-Konstanten stammen aus
PluginPrepSchema.inc.
Für einen ganzen Strom — die Phase neverc.prep.build_token_stream — sammeln
Sie in einem Stromerbauer und schreiben einmal fest:
NevercTokenStreamBuilderHandle Stream;
Prep->CreateTokenStreamBuilder(Prep->Context, Task, &Stream);
Prep->TokenStreamBuilderAppend(Prep->Context, Task, Stream, Tokens, Count);
Prep->TokenStreamBuilderCommit(Prep->Context, Frame, Stream, &Output);
Prep->DestroyTokenStreamBuilder(Prep->Context, Task, Stream);Die Phaseneingabe NevercPrepTokenStreamPhaseInput liefert Anfangs- und
Endposition sowie ein MaximumTokenCount, das die Ausgabe einhalten muss.
Die Eingabe einer Pragmaphase meldet den Einleiter (NEVERC_PREP_PRAGMA_HASH,
_OPERATOR für _Pragma oder _MS für __pragma), Namensraum und Namen sowie
die Argumenttoken. Die Ausgabeaktion ist NEVERC_PREP_PRAGMA_CONTINUE,
_HANDLED oder _REPLACE_TOKENS.
Eine Feature-Abfrage deckt __has_feature, __has_extension, __has_builtin,
__has_include und __has_include_next über NEVERC_PREP_QUERY_HAS_FEATURE
und Verwandte ab. Die Eingabe trägt den Namen und den vom Compiler berechneten
BuiltinValue; die Ausgabe fährt entweder fort oder ersetzt:
NevercPrepFeatureQueryPhaseOutput Out = {0};
Out.Header = /* … */;
Out.Action = NEVERC_PREP_QUERY_REPLACE;
Out.Value = NEVERC_TRUE;
Prep->CreateFeatureQueryPhaseOutput(Prep->Context, Frame, Continuation, &Out,
&Output);- Ereignisdatensätze, Zeichenkettensichten und Ganzzahlfelder sind für die Dauer des Rückrufs geliehen. In einem Ereignis veröffentlichte Handles leben, bis die Aufgabe endet.
- Jeder Erbauer braucht seinen passenden
Destroy*-Aufruf, auch auf dem Fehlerpfad. - Ein Aufruf von
Create<Kind>PhaseOutputverlangt die Continuation der Phase, zu der er gehört; die Continuation einer anderen Phase zu verwenden liefertNEVERC_STATUS_WRONG_SCOPE. - Abonnieren Sie nur Ereignisse, die Sie auch behandeln. Die Maske ist die
Drossel — ein Plugin, das
NEVERC_PREP_EVENT_MASK_ALLnimmt und in C filtert, bezahlt jeden einzelnen Rückruf. - Präprozessor-Rückrufe laufen auf dem Aufgaben-Thread, während der Präprozessor mitten in der Arbeit ist. Treten Sie aus einem Rückruf nicht erneut in den Präprozessor ein.
- Geben Sie
NEVERC_STATUS_INVALID_ARGUMENTzurück, wenn ein erforderlicher Zeiger fehlt, und lassen Sie niemals eine Ausnahme die Grenze überschreiten.
Die normativen Deklarationen stehen in PluginPrep.h und
Schema/PluginPrepSchema.inc, das Schema der Tokenarten in
Schema/PrepSchema.json, die sechs Präprozessor-Phasen und ihre
Richtlinien in Schema/PhaseSchema.json.