Plasma Engine  2.0
Loading...
Searching...
No Matches
Preprocessor.h
1#pragma once
2
3#include <Foundation/Basics.h>
4#include <Foundation/CodeUtils/TokenParseUtils.h>
5#include <Foundation/CodeUtils/Tokenizer.h>
6#include <Foundation/Containers/Map.h>
7#include <Foundation/Containers/Set.h>
8#include <Foundation/IO/Stream.h>
9#include <Foundation/Logging/Log.h>
10#include <Foundation/Memory/CommonAllocators.h>
11#include <Foundation/Time/Timestamp.h>
12
15class PL_FOUNDATION_DLL plTokenizedFileCache
16{
17public:
18 struct FileData
19 {
20 plTokenizer m_Tokens;
21 plTimestamp m_Timestamp;
22 };
23
25 plMap<plString, FileData>::ConstIterator Lookup(const plString& sFileName) const;
26
28 void Remove(const plString& sFileName);
29
31 void Clear();
32
37 const plTokenizer* Tokenize(const plString& sFileName, plArrayPtr<const plUInt8> fileContent, const plTimestamp& fileTimeStamp, plLogInterface* pLog);
38
39private:
40 void SkipWhitespace(plDeque<plToken>& Tokens, plUInt32& uiCurToken);
41
42 mutable plMutex m_Mutex;
44};
45
62class PL_FOUNDATION_DLL plPreprocessor
63{
64public:
67 {
70 GlobalInclude
71 };
72
76
83
90
92
98 {
113
114 EventType m_Type = EventType::Error;
115
116 const plToken* m_pToken = nullptr;
117 plStringView m_sInfo;
118 };
119
123
125
133 void SetLogInterface(plLogInterface* pLog);
134
139 void SetCustomFileCache(plTokenizedFileCache* pFileCache = nullptr);
140
142 void SetPassThroughPragma(bool bPassThrough) { m_bPassThroughPragma = bPassThrough; }
143
145 void SetPassThroughLine(bool bPassThrough) { m_bPassThroughLine = bPassThrough; }
146
148 void SetPassThroughUnknownCmdsCB(PassThroughUnknownCmdCB callback) { m_PassThroughUnknownCmdCB = callback; }
149
153 void SetFileOpenFunction(FileOpenCB openAbsFileCB);
154
159 void SetFileLocatorFunction(FileLocatorCB locateAbsFileCB);
160
169 plResult AddCustomDefine(plStringView sDefinition);
170
174 plResult Process(plStringView sMainFile, plTokenParseUtils::TokenStream& ref_tokenOutput);
175
180 plResult Process(plStringView sMainFile, plStringBuilder& ref_sOutput, bool bKeepComments = true, bool bRemoveRedundantWhitespace = false, bool bInsertLine = false);
181
182
183private:
184 struct FileData
185 {
186 FileData()
187 {
188 m_iCurrentLine = 1;
189 m_iExpandDepth = 0;
190 }
191
192 plHashedString m_sVirtualFileName;
193 plHashedString m_sFileName;
194 plInt32 m_iCurrentLine;
195 plInt32 m_iExpandDepth;
196 };
197
198 enum IfDefActivity
199 {
200 IsActive,
201 IsInactive,
202 WasActive,
203 };
204
205 struct CustomDefine
206 {
208 plTokenizer m_Tokenized;
209 };
210
211 // This class-local allocator is used to get rid of some of the memory allocation
212 // tracking that would otherwise occur for allocations made by the preprocessor.
213 // If changing its position in the class, make sure it always comes before all
214 // other members that depend on it to ensure deallocations in those members
215 // happen before the allocator get destroyed.
217
218 bool m_bPassThroughPragma;
219 bool m_bPassThroughLine;
220 PassThroughUnknownCmdCB m_PassThroughUnknownCmdCB;
221
222 // this file cache is used as long as the user does not provide his own
223 plTokenizedFileCache m_InternalFileCache;
224
225 // pointer to the file cache that is in use
226 plTokenizedFileCache* m_pUsedFileCache;
227
228 plDeque<FileData> m_CurrentFileStack;
229
230 plLogInterface* m_pLog;
231
232 plDeque<CustomDefine> m_CustomDefines;
233
234 struct IfDefState
235 {
236 IfDefState(IfDefActivity activeState = IfDefActivity::IsActive)
237 : m_ActiveState(activeState)
238
239 {
240 }
241
242 IfDefActivity m_ActiveState;
243 bool m_bIsInElseClause = false;
244 };
245
246 plDeque<IfDefState> m_IfdefActiveStack;
247
248 plResult ProcessFile(plStringView sFile, plTokenParseUtils::TokenStream& TokenOutput);
249 plResult ProcessCmd(const plTokenParseUtils::TokenStream& Tokens, plTokenParseUtils::TokenStream& TokenOutput);
250
251public:
252 static plResult DefaultFileLocator(plStringView sCurAbsoluteFile, plStringView sIncludeFile, plPreprocessor::IncludeType incType, plStringBuilder& out_sAbsoluteFilePath);
253 static plResult DefaultFileOpen(plStringView sAbsoluteFile, plDynamicArray<plUInt8>& ref_fileContent, plTimestamp& out_fileModification);
254
255private: // *** File Handling ***
256 plResult OpenFile(plStringView sFile, const plTokenizer** pTokenizer);
257
258 FileOpenCB m_FileOpenCallback;
259 FileLocatorCB m_FileLocatorCallback;
260 plSet<plTempHashedString> m_PragmaOnce;
261
262private: // *** Macro Definition ***
263 bool RemoveDefine(plStringView sName);
264 plResult HandleDefine(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken);
265
266 struct MacroDefinition
267 {
268 MacroDefinition();
269
270 const plToken* m_MacroIdentifier;
271 bool m_bIsFunction;
272 bool m_bCurrentlyExpanding;
273 bool m_bHasVarArgs;
274 plInt32 m_iNumParameters;
275 plTokenParseUtils::TokenStream m_Replacement;
276 };
277
278 plResult StoreDefine(const plToken* pMacroNameToken, const plTokenParseUtils::TokenStream* pReplacementTokens, plUInt32 uiFirstReplacementToken, plInt32 iNumParameters, bool bUsesVarArgs);
279 plResult ExtractParameterName(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plString& sIdentifierName);
280
282
283 static constexpr plInt32 s_iMacroParameter0 = plTokenType::ENUM_COUNT + 2;
284 static plString s_ParamNames[32];
285 plToken m_ParameterTokens[32];
286
287private: // *** #if condition parsing ***
288 plResult EvaluateCondition(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
289 plResult ParseCondition(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
290 plResult ParseFactor(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
291 plResult ParseExpressionMul(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
292 plResult ParseExpressionOr(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
293 plResult ParseExpressionAnd(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
294 plResult ParseExpressionPlus(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
295 plResult ParseExpressionShift(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
296 plResult ParseExpressionBitOr(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
297 plResult ParseExpressionBitAnd(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
298 plResult ParseExpressionBitXor(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plInt64& iResult);
299
300
301private: // *** Parsing ***
302 plResult CopyTokensAndEvaluateDefined(const plTokenParseUtils::TokenStream& Source, plUInt32 uiFirstSourceToken, plTokenParseUtils::TokenStream& Destination);
303 void CopyTokensReplaceParams(const plTokenParseUtils::TokenStream& Source, plUInt32 uiFirstSourceToken, plTokenParseUtils::TokenStream& Destination, const plHybridArray<plString, 16>& parameters);
304
305 plResult Expect(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plStringView sToken, plUInt32* pAccepted = nullptr);
306 plResult Expect(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plTokenType::Enum Type, plUInt32* pAccepted = nullptr);
307 plResult Expect(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plStringView sToken1, plStringView sToken2, plUInt32* pAccepted = nullptr);
308 plResult ExpectEndOfLine(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken);
309
310private: // *** Macro Expansion ***
313 plResult ExpandObjectMacro(MacroDefinition& Macro, plTokenParseUtils::TokenStream& Output, const plToken* pMacroToken);
314 plResult ExpandFunctionMacro(MacroDefinition& Macro, const MacroParameters& Parameters, plTokenParseUtils::TokenStream& Output, const plToken* pMacroToken);
315 plResult ExpandMacroParam(const plToken& MacroToken, plUInt32 uiParam, plTokenParseUtils::TokenStream& Output, const MacroDefinition& Macro);
316 void PassThroughFunctionMacro(MacroDefinition& Macro, const MacroParameters& Parameters, plTokenParseUtils::TokenStream& Output);
317 plToken* AddCustomToken(const plToken* pPrevious, const plStringView& sNewText);
318 void OutputNotExpandableMacro(MacroDefinition& Macro, plTokenParseUtils::TokenStream& Output);
319 plResult ExtractAllMacroParameters(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plDeque<plTokenParseUtils::TokenStream>& AllParameters);
320 plResult ExtractParameterValue(const plTokenParseUtils::TokenStream& Tokens, plUInt32& uiCurToken, plTokenParseUtils::TokenStream& ParamTokens);
321
322 plResult InsertParameters(const plTokenParseUtils::TokenStream& Tokens, plTokenParseUtils::TokenStream& Output, const MacroDefinition& Macro);
323
324 plResult InsertStringifiedParameters(const plTokenParseUtils::TokenStream& Tokens, plTokenParseUtils::TokenStream& Output, const MacroDefinition& Macro);
325 plResult ConcatenateParameters(const plTokenParseUtils::TokenStream& Tokens, plTokenParseUtils::TokenStream& Output, const MacroDefinition& Macro);
326 void MergeTokens(const plToken* pFirst, const plToken* pSecond, plTokenParseUtils::TokenStream& Output, const MacroDefinition& Macro);
327
328 struct CustomToken
329 {
330 plToken m_Token;
331 plString m_sIdentifierString;
332 };
333
334 enum TokenFlags : plUInt32
335 {
336 NoFurtherExpansion = PL_BIT(0),
337 };
338
339 plToken m_TokenFile;
340 plToken m_TokenLine;
341 const plToken* m_pTokenOpenParenthesis;
342 const plToken* m_pTokenClosedParenthesis;
343 const plToken* m_pTokenComma;
344
345 plDeque<const MacroParameters*> m_MacroParamStack;
346 plDeque<const MacroParameters*> m_MacroParamStackExpanded;
347 plDeque<CustomToken> m_CustomTokens;
348
349private: // *** Other ***
350 static void StringifyTokens(const plTokenParseUtils::TokenStream& Tokens, plStringBuilder& sResult, bool bSurroundWithQuotes);
351 plToken* CreateStringifiedParameter(plUInt32 uiParam, const plToken* pParamToken, const MacroDefinition& Macro);
352
353 plResult HandleErrorDirective(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken, plUInt32 uiDirectiveToken);
354 plResult HandleWarningDirective(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken, plUInt32 uiDirectiveToken);
355 plResult HandleUndef(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken, plUInt32 uiDirectiveToken);
356
357 plResult HandleEndif(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken, plUInt32 uiDirectiveToken);
358 plResult HandleElif(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken, plUInt32 uiDirectiveToken);
359 plResult HandleIf(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken, plUInt32 uiDirectiveToken);
360 plResult HandleElse(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken, plUInt32 uiDirectiveToken);
361 plResult HandleIfdef(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken, plUInt32 uiDirectiveToken, bool bIsIfdef);
362 plResult HandleInclude(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken, plUInt32 uiDirectiveToken, plTokenParseUtils::TokenStream& TokenOutput);
363 plResult HandleLine(const plTokenParseUtils::TokenStream& Tokens, plUInt32 uiCurToken, plUInt32 uiDirectiveToken, plTokenParseUtils::TokenStream& TokenOutput);
364};
365
366#define PP_LOG0(Type, FormatStr, ErrorToken) \
367 { \
368 ProcessingEvent pe; \
369 pe.m_Type = ProcessingEvent::Type; \
370 pe.m_pToken = ErrorToken; \
371 pe.m_sInfo = FormatStr; \
372 if (pe.m_pToken->m_uiLine == 0 && pe.m_pToken->m_uiColumn == 0) \
373 { \
374 const_cast<plToken*>(pe.m_pToken)->m_uiLine = m_CurrentFileStack.PeekBack().m_iCurrentLine; \
375 const_cast<plToken*>(pe.m_pToken)->m_File = m_CurrentFileStack.PeekBack().m_sVirtualFileName; \
376 } \
377 m_ProcessingEvents.Broadcast(pe); \
378 plLog::Type(m_pLog, "File '{0}', Line {1} ({2}): " FormatStr, pe.m_pToken->m_File.GetString(), pe.m_pToken->m_uiLine, pe.m_pToken->m_uiColumn); \
379 }
380
381#define PP_LOG(Type, FormatStr, ErrorToken, ...) \
382 { \
383 ProcessingEvent _pe; \
384 _pe.m_Type = ProcessingEvent::Type; \
385 _pe.m_pToken = ErrorToken; \
386 if (_pe.m_pToken->m_uiLine == 0 && _pe.m_pToken->m_uiColumn == 0) \
387 { \
388 const_cast<plToken*>(_pe.m_pToken)->m_uiLine = m_CurrentFileStack.PeekBack().m_iCurrentLine; \
389 const_cast<plToken*>(_pe.m_pToken)->m_File = m_CurrentFileStack.PeekBack().m_sVirtualFileName; \
390 } \
391 plStringBuilder sInfo; \
392 sInfo.SetFormat(FormatStr, ##__VA_ARGS__); \
393 _pe.m_sInfo = sInfo; \
394 m_ProcessingEvents.Broadcast(_pe); \
395 plLog::Type(m_pLog, "File '{0}', Line {1} ({2}): {3}", _pe.m_pToken->m_File.GetString(), _pe.m_pToken->m_uiLine, _pe.m_pToken->m_uiColumn, sInfo); \
396 }
Policy based allocator implementation of the plAllocator interface.
Definition AllocatorWithPolicy.h:19
This class encapsulates an array and it's size. It is recommended to use this class instead of plain ...
Definition ArrayPtr.h:37
Definition Deque.h:270
Definition DynamicArray.h:81
Definition Event.h:177
This class is optimized to take nearly no memory (sizeof(void*)) and to allow very fast checks whethe...
Definition HashedString.h:25
Base class for all logging classes.
Definition Log.h:77
Definition Map.h:408
Provides a simple mechanism for mutual exclusion to prevent multiple threads from accessing a shared ...
Definition Mutex.h:13
plPreprocessor implements a standard C preprocessor. It can be used to pre-process files to get the o...
Definition Preprocessor.h:63
void SetPassThroughPragma(bool bPassThrough)
If set to true, all #pragma commands are passed through to the output, otherwise they are removed.
Definition Preprocessor.h:142
IncludeType
Describes the type of #include that was encountered during preprocessing.
Definition Preprocessor.h:67
@ RelativeInclude
An #include "file" has been encountered.
Definition Preprocessor.h:69
@ MainFile
This is used for the very first access to the main source file.
Definition Preprocessor.h:68
void SetPassThroughUnknownCmdsCB(PassThroughUnknownCmdCB callback)
Sets the callback that is used to determine whether an unknown command is passed through or triggers ...
Definition Preprocessor.h:148
plEvent< const ProcessingEvent & > m_ProcessingEvents
Broadcasts events during the processing. This can be used to create detailed callstacks when an error...
Definition Preprocessor.h:122
void SetPassThroughLine(bool bPassThrough)
If set to true, all #line commands are passed through to the output, otherwise they are removed.
Definition Preprocessor.h:145
Definition Set.h:238
plStringBuilder is a class that is meant for creating and modifying strings.
Definition StringBuilder.h:35
plStringView represent a read-only sub-string of a larger string, as it can store a dedicated string ...
Definition StringView.h:34
The timestamp class encapsulates a date in time as microseconds since Unix epoch.
Definition Timestamp.h:23
This object caches files in a tokenized state. It can be shared among plPreprocessor instances to imp...
Definition Preprocessor.h:16
Takes text and splits it up into plToken objects. The result can be used for easier parsing.
Definition Tokenizer.h:79
Base class for all iterators.
Definition Map.h:11
The event data that the processor broadcasts.
Definition Preprocessor.h:98
EventType
The event types that the processor broadcasts.
Definition Preprocessor.h:101
@ CheckDefined
A 'defined(X)' is being evaluated.
Definition Preprocessor.h:106
@ Define
A #define X has been stored.
Definition Preprocessor.h:110
@ Redefine
A #define for an already existing macro name (also logged as a warning)
Definition Preprocessor.h:111
@ EvaluateUnknown
Inside an if an unknown identifier has been encountered, it will be evaluated as zero.
Definition Preprocessor.h:109
@ Warning
A warning has been output.
Definition Preprocessor.h:105
@ CheckIfdef
A '#ifdef X' is being evaluated.
Definition Preprocessor.h:107
@ BeginExpansion
A macro is now going to be expanded.
Definition Preprocessor.h:102
@ Error
An error was encountered.
Definition Preprocessor.h:104
@ CheckIfndef
A '#ifndef X' is being evaluated.
Definition Preprocessor.h:108
@ EndExpansion
A macro is finished being expanded.
Definition Preprocessor.h:103
Default enum for returning failure or success, instead of using a bool.
Definition Types.h:54
Represents one piece of tokenized text in a document.
Definition Tokenizer.h:37
Enum
Definition Tokenizer.h:13
Definition Preprocessor.h:19