Langues: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
PluginPrep.h expose le préprocesseur de deux manières. Un abonnement à 39
genres d'événements vous donne une trace en lecture seule de tout ce que fait le
préprocesseur : entrée dans un fichier, définition et expansion de macro,
évaluation de conditions, pragmas. Six phases vont plus loin et vous
laissent réécrire le résultat : rediriger un #include, remplacer les jetons
d'expansion d'une macro, traiter un pragma vous-même, ou répondre autrement à
__has_feature.
#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);Les 230 genres de jetons (NEVERC_TOKEN_KIND_COUNT) et les genres de mots-clés
du préprocesseur proviennent de Schema/PluginPrepSchema.inc, que l'en-tête
inclut et dont le majeur de capacité doit être égal à NEVERC_PREP_API_MAJOR —
un désaccord est une erreur de compilation, non une surprise à l'exécution.
Chaque genre porte aussi une catégorie : NEVERC_TOKEN_CATEGORY_SPECIAL,
COMMENT, IDENTIFIER, LITERAL, PUNCTUATOR, KEYWORD ou ANNOTATION.
| Phase | Politique | Entrée → sortie |
|---|---|---|
neverc.prep.token |
OBSERVABLE, INTERCEPTABLE, REPLACEABLE | un jeton → liste de jetons |
neverc.prep.build_token_stream |
idem | plage → flux de jetons |
neverc.prep.include.intercept |
idem | requête d'inclusion → décision d'inclusion |
neverc.prep.macro.intercept |
idem | opération de macro → action + jetons |
neverc.prep.pragma.intercept |
idem | pragma → action + jetons |
neverc.prep.feature_query.intercept |
idem | requête __has_* → valeur |
Chacune possède sur NevercPrepAPI un couple Get<Kind>PhaseInput et
Create<Kind>PhaseOutput ; la moitié Create prend la
NevercPhaseContinuation de l'intercepteur, de sorte qu'une sortie ne peut être
produite que depuis l'intérieur de la phase qui la possède.
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 vaut NEVERC_TOKEN_ORIGIN_FILE, MACRO_REPLACEMENT,
MACRO_ARGUMENT ou SYNTHESIZED : c'est ainsi que l'on distingue un jeton
saisi par l'utilisateur d'un jeton produit par une macro.
Les drapeaux constituent la comptabilité interne du préprocesseur et comptent lorsque vous synthétisez des jetons :
| Drapeau | Signification |
|---|---|
NEVERC_TOKEN_FLAG_START_OF_LINE |
Premier jeton de sa ligne |
_LEADING_SPACE |
Une espace le précède |
_DISABLE_EXPANSION |
Ne pas développer ce jeton par macro |
_NEEDS_CLEANING |
L'écriture contient des sauts de ligne échappés ou des trigraphes |
_LEADING_EMPTY_MACRO |
Une macro vide s'est développée juste avant |
_HAS_UCN |
Contient un nom de caractère universel |
_IGNORED_COMMA, _COMMA_AFTER_ELIDED |
Comptabilité de l'élision de virgule variadique |
_STRINGIFIED_IN_MACRO |
Produit par # |
_REINJECTED |
Réinjecté dans le flux de jetons |
NEVERC_TOKEN_FLAG_ALL est le masque de tous les bits définis. Les lectures par
lot passent par GetTokenInfoBatch ; un flux entier se lit soit comme une vue
légère d'enregistrements NevercTokenView via GetTokenStreamView, soit un
descripteur à la fois avec GetTokenStreamToken. Un flux contient au plus
NEVERC_PREP_TOKEN_STREAM_MAX_TOKENS (16 777 216) jetons.
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 rapporte le nom, la directive de définition, les
emplacements de définition, de fin et d'annulation, le nombre de paramètres et
de jetons de remplacement, ainsi que des drapeaux :
NEVERC_MACRO_FUNCTION_LIKE, VARIADIC, C99_VARIADIC, GNU_VARIADIC,
HAS_VA_OPT, BUILTIN et COMMA_PASTING. Les paramètres et jetons de
remplacement individuels viennent de GetMacroParameter et
GetMacroReplacementToken.
NevercIdentifierInfo y ajoute le genre de jeton, le genre de mot-clé du
préprocesseur, l'identifiant de fonction intrinsèque et des drapeaux tels que
NEVERC_IDENTIFIER_KEYWORD, _HAS_MACRO, _POISONED et _RESERVED.
Sur un site d'expansion, GetMacroArgumentInfo indique le nombre d'arguments et
si les arguments variadiques ont été élidés, et
GetMacroArgumentTokenStream fournit les jetons de chaque argument.
Un seul rappel reçoit tous les événements souscrits. Le masque se construit à partir des genres qui vous intéressent :
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 vaut NOT_EVALUATED, FALSE ou 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 abonne à tout. Les 39 genres, regroupés selon le
membre d'union de charge utile qu'ils emploient :
| Charge utile | Événements |
|---|---|
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 distingue NEVERC_PREP_FILE_ENTER, EXIT,
SYSTEM_HEADER_PRAGMA et RENAME. Les événements sont en lecture seule :
l'enregistrement et chaque vue qu'il contient sont empruntés le temps du rappel,
tandis que les descripteurs publiés dans un événement sont promus à la portée de
la tâche englobante.
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);Les actions sont NEVERC_PREP_INCLUDE_CONTINUE, _SKIP et _REDIRECT.
L'entrée signale aussi IsImport et IsIncludeNext, si bien que #import et
#include_next restent distinguables.
L'entrée de la phase macro porte l'opération en cours —
NEVERC_PREP_MACRO_DEFINE, _UNDEFINE, _EXPAND ou _EXPAND_BUILTIN — avec
le jeton du nom, la définition, les arguments et les jetons de remplacement que
le préprocesseur allait utiliser.
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 conserve le comportement natif et _SUPPRESS se
développe en rien du tout.
Les jetons synthétisés proviennent d'un constructeur, qui valide la combinaison de genre, d'écriture et d'identifiant avant de valider :
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);Utilisez TokenBuilderSetKind pour les signes de ponctuation et les mots-clés,
et TokenBuilderSetIdentifier pour les identifiants. Les constantes de genre de
jeton viennent de PluginPrepSchema.inc.
Pour un flux entier — la phase neverc.prep.build_token_stream — accumulez dans
un constructeur de flux et validez en une fois :
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);L'entrée de phase, NevercPrepTokenStreamPhaseInput, donne les emplacements de
début et de fin ainsi qu'un MaximumTokenCount que la sortie doit respecter.
L'entrée d'une phase pragma indique l'introducteur (NEVERC_PREP_PRAGMA_HASH,
_OPERATOR pour _Pragma, ou _MS pour __pragma), l'espace de noms et le
nom, ainsi que les jetons d'arguments. L'action de sortie est
NEVERC_PREP_PRAGMA_CONTINUE, _HANDLED ou _REPLACE_TOKENS.
Une requête de fonctionnalité couvre __has_feature, __has_extension,
__has_builtin, __has_include et __has_include_next via
NEVERC_PREP_QUERY_HAS_FEATURE et ses semblables. L'entrée porte le nom et la
BuiltinValue calculée par le compilateur ; la sortie continue ou remplace :
NevercPrepFeatureQueryPhaseOutput Out = {0};
Out.Header = /* … */;
Out.Action = NEVERC_PREP_QUERY_REPLACE;
Out.Value = NEVERC_TRUE;
Prep->CreateFeatureQueryPhaseOutput(Prep->Context, Frame, Continuation, &Out,
&Output);- Les enregistrements d'événement, les vues de chaîne et les tableaux d'entiers sont empruntés le temps du rappel. Les descripteurs publiés dans un événement vivent jusqu'à la fin de la tâche.
- Chaque constructeur exige son appel
Destroy*correspondant, y compris sur le chemin d'erreur. - Un appel à
Create<Kind>PhaseOutputrequiert la continuation de la phase à laquelle il appartient ; utiliser celle d'une autre phase renvoieNEVERC_STATUS_WRONG_SCOPE. - N'abonnez-vous qu'aux événements que vous traitez. Le masque est le
régulateur : un plugin qui prend
NEVERC_PREP_EVENT_MASK_ALLpuis filtre en C paie chaque rappel. - Les rappels du préprocesseur s'exécutent sur le fil de la tâche alors que le préprocesseur est en plein travail. N'y rentrez pas de nouveau depuis un rappel.
- Renvoyez
NEVERC_STATUS_INVALID_ARGUMENTpour un pointeur obligatoire manquant, et ne laissez jamais une exception franchir la frontière.
Voir PluginPrep.h et Schema/PluginPrepSchema.inc pour les déclarations
normatives, Schema/PrepSchema.json pour le schéma des genres de jetons, et
Schema/PhaseSchema.json pour les six phases du préprocesseur et leurs
politiques.