summaryrefslogtreecommitdiffstats
path: root/source/slang/preprocessor.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-05-31 17:20:37 -0400
committerGitHub <noreply@github.com>2019-05-31 17:20:37 -0400
commit6cbc3929a54d37bd23cb5efa8e3320ba02f78b2f (patch)
tree5a23cb47782e9e2a77762c90dd35da1005eba8d0 /source/slang/preprocessor.cpp
parentb81ff3ef968d1cc4e954b31a1812b3c391d17b02 (diff)
Use slang- prefix on slang compiler and core source (#973)
* Prefixing source files in source/slang with slang- * Prefix source in source/slang with slang- prefix. * Rename core source files with slang- prefix. * Update project files. * Fix problems from automatic merge.
Diffstat (limited to 'source/slang/preprocessor.cpp')
-rw-r--r--source/slang/preprocessor.cpp2302
1 files changed, 0 insertions, 2302 deletions
diff --git a/source/slang/preprocessor.cpp b/source/slang/preprocessor.cpp
deleted file mode 100644
index bf6f7b7ca..000000000
--- a/source/slang/preprocessor.cpp
+++ /dev/null
@@ -1,2302 +0,0 @@
-// preprocessor.cpp
-#include "preprocessor.h"
-
-#include "compiler.h"
-#include "diagnostics.h"
-#include "lexer.h"
-// Needed so that we can construct modifier syntax to represent GLSL directives
-#include "syntax.h"
-
-#include <assert.h>
-
-// This file provides an implementation of a simple C-style preprocessor.
-// It does not aim for 100% compatibility with any particular preprocessor
-// specification, but the goal is to have it accept the most common
-// idioms for using the preprocessor, found in shader code in the wild.
-
-
-namespace Slang {
-
-// State of a preprocessor conditional, which can change when
-// we encounter directives like `#elif` or `#endif`
-enum class PreprocessorConditionalState
-{
- Before, // We have not yet seen a branch with a `true` condition.
- During, // We are inside the branch with a `true` condition.
- After, // We have already seen the branch with a `true` condition.
-};
-
-// Represents a preprocessor conditional that we are currently
-// nested inside.
-struct PreprocessorConditional
-{
- // The next outer conditional in the current file/stream, or NULL.
- PreprocessorConditional* parent;
-
- // The directive token that started the conditional (an `#if` or `#ifdef`)
- Token ifToken;
-
- // The `#else` directive token, if one has been seen (otherwise `TokenType::Unknown`)
- Token elseToken;
-
- // The state of the conditional
- PreprocessorConditionalState state;
-};
-
-struct PreprocessorMacro;
-
-struct PreprocessorEnvironment
-{
- // The "outer" environment, to be used if lookup in this env fails
- PreprocessorEnvironment* parent = NULL;
-
- // Macros defined in this environment
- Dictionary<Name*, PreprocessorMacro*> macros;
-
- ~PreprocessorEnvironment();
-};
-
-// Input tokens can either come from source text, or from macro expansion.
-// In general, input streams can be nested, so we have to keep a conceptual
-// stack of input.
-
-struct PrimaryInputStream;
-
-// A stream of input tokens to be consumed
-struct PreprocessorInputStream
-{
- // The primary input stream that is the parent to this one,
- // or NULL if this stream is itself a primary stream.
- PrimaryInputStream* primaryStream;
-
- // The next input stream up the stack, if any.
- PreprocessorInputStream* parent;
-
- // Environment to use when looking up macros
- PreprocessorEnvironment* environment;
-
- // Destructor is virtual so that we can clean up
- // after concrete subtypes.
- virtual ~PreprocessorInputStream() = default;
-};
-
-// A "primary" input stream represents the top-level context of a file
-// being parsed, and tracks things like preprocessor conditional state
-struct PrimaryInputStream : PreprocessorInputStream
-{
- // The next *primary* input stream up the stack
- PrimaryInputStream* parentPrimaryInputStream;
-
- // The deepest preprocessor conditional active for this stream.
- PreprocessorConditional* conditional;
-
- // The lexer state that will provide input
- Lexer lexer;
-
- // One token of lookahead
- Token token;
-};
-
-// A "secondary" input stream represents code that is being expanded
-// into the current scope, but which had already been tokenized before.
-//
-struct PretokenizedInputStream : PreprocessorInputStream
-{
- // Reader for pre-tokenized input
- TokenReader tokenReader;
-};
-
-// A pre-tokenized input stream that will only be used once, and which
-// therefore owns the memory for its tokens.
-struct SimpleTokenInputStream : PretokenizedInputStream
-{
- // A list of raw tokens that will provide input
- TokenList lexedTokens;
-};
-
-struct MacroExpansion : PretokenizedInputStream
-{
- // The macro we will expand
- PreprocessorMacro* macro;
-};
-
-struct ObjectLikeMacroExpansion : MacroExpansion
-{
-};
-
-struct FunctionLikeMacroExpansion : MacroExpansion
-{
- // Environment for macro arguments
- PreprocessorEnvironment argumentEnvironment;
-};
-
-// An enumeration for the diferent types of macros
-enum class PreprocessorMacroFlavor
-{
- ObjectLike,
- FunctionArg,
- FunctionLike,
-};
-
-// In the current design (which we may want to re-consider),
-// a macro is a specialized flavor of input stream, that
-// captures the token list in its expansion, and then
-// can be "played back."
-struct PreprocessorMacro
-{
- // The name under which the macro was `#define`d
- NameLoc nameAndLoc;
-
- // Parameters of the macro, in case of a function-like macro
- List<NameLoc> params;
-
- // The tokens that make up the macro body
- TokenList tokens;
-
- // The flavor of macro
- PreprocessorMacroFlavor flavor;
-
- // The environment in which this macro needs to be expanded.
- // For ordinary macros this will be the global environment,
- // while for function-like macro arguments, it will be
- // the environment of the macro invocation.
- PreprocessorEnvironment* environment;
-
- //
- Name* getName()
- {
- return nameAndLoc.name;
- }
-
- SourceLoc getLoc()
- {
- return nameAndLoc.loc;
- }
-};
-
-// State of the preprocessor
-struct Preprocessor
-{
- // diagnostics sink to use when writing messages
- DiagnosticSink* sink;
-
- // An external callback interface to use when looking
- // for files in a `#include` directive
- IncludeHandler* includeHandler;
-
- // Current input stream (top of the stack of input)
- PreprocessorInputStream* inputStream;
-
- // Currently-defined macros
- PreprocessorEnvironment globalEnv;
-
- // A pre-allocated token that can be returned to
- // represent end-of-input situations.
- Token endOfFileToken;
-
- /// The linkage the provides the context for preprocessing
- Linkage* linkage = nullptr;
-
- /// The module, if any, that the preprocessed result will belong to
- Module* parentModule = nullptr;
-
- // The unique identities of any paths that have issued `#pragma once` directives to
- // stop them from being included again.
- HashSet<String> pragmaOnceUniqueIdentities;
-
- NamePool* getNamePool() { return linkage->getNamePool(); }
- SourceManager* getSourceManager() { return linkage->getSourceManager(); }
-};
-
-// Convenience routine to access the diagnostic sink
-static DiagnosticSink* GetSink(Preprocessor* preprocessor)
-{
- return preprocessor->sink;
-}
-
-//
-// Forward declarations
-//
-
-static void DestroyConditional(PreprocessorConditional* conditional);
-static void DestroyMacro(Preprocessor* preprocessor, PreprocessorMacro* macro);
-static bool IsSkipping(Preprocessor* preprocessor);
-
-//
-// Basic Input Handling
-//
-
-// Create a fresh input stream
-static void initializeInputStream(Preprocessor* preprocessor, PreprocessorInputStream* inputStream)
-{
- inputStream->parent = NULL;
- inputStream->environment = &preprocessor->globalEnv;
-}
-
-static void initializePrimaryInputStream(Preprocessor* preprocessor, PrimaryInputStream* inputStream)
-{
- initializeInputStream(preprocessor, inputStream);
- inputStream->primaryStream = inputStream;
- inputStream->conditional = NULL;
-}
-
-// Destroy an input stream
-static void destroyInputStream(Preprocessor* /*preprocessor*/, PreprocessorInputStream* inputStream)
-{
- delete inputStream;
-}
-
-// Create an input stream to represent a pre-tokenized input file.
-// TODO(tfoley): pre-tokenizing files isn't going to work in the long run.
-static PreprocessorInputStream* CreateInputStreamForSource(
- Preprocessor* preprocessor,
- SourceView* sourceView)
-{
- MemoryArena* memoryArena = sourceView->getSourceManager()->getMemoryArena();
-
- PrimaryInputStream* inputStream = new PrimaryInputStream();
- initializePrimaryInputStream(preprocessor, inputStream);
-
- // initialize the embedded lexer so that it can generate a token stream
- inputStream->lexer.initialize(sourceView, GetSink(preprocessor), preprocessor->getNamePool(), memoryArena);
- inputStream->token = inputStream->lexer.lexToken();
-
- return inputStream;
-}
-
-static PrimaryInputStream* asPrimaryInputStream(PreprocessorInputStream* inputStream)
-{
- auto primaryStream = inputStream->primaryStream;
- if(primaryStream == inputStream)
- return primaryStream;
- return nullptr;
-}
-
-
-static void PushInputStream(Preprocessor* preprocessor, PreprocessorInputStream* inputStream)
-{
- inputStream->parent = preprocessor->inputStream;
- if(!asPrimaryInputStream(inputStream))
- inputStream->primaryStream = preprocessor->inputStream->primaryStream;
- preprocessor->inputStream = inputStream;
-}
-
-// Called when we reach the end of an input stream.
-// Performs some validation and then destroys the input stream if required.
-static void EndInputStream(Preprocessor* preprocessor, PreprocessorInputStream* inputStream)
-{
- if(auto primaryStream = asPrimaryInputStream(inputStream))
- {
- // If there are any conditionals that weren't completed, then it is an error
- if (primaryStream->conditional)
- {
- PreprocessorConditional* conditional = primaryStream->conditional;
-
- GetSink(preprocessor)->diagnose(conditional->ifToken.loc, Diagnostics::endOfFileInPreprocessorConditional);
-
- while (conditional)
- {
- PreprocessorConditional* parent = conditional->parent;
- DestroyConditional(conditional);
- conditional = parent;
- }
- }
- }
-
- destroyInputStream(preprocessor, inputStream);
-}
-
-// Consume one token from an input stream
-static Token AdvanceRawToken(PreprocessorInputStream* inputStream, LexerFlags lexerFlags = 0)
-{
- if( auto primaryStream = asPrimaryInputStream(inputStream) )
- {
- auto result = primaryStream->token;
- primaryStream->token = primaryStream->lexer.lexToken(lexerFlags);
- return result;
- }
- else
- {
- PretokenizedInputStream* pretokenized = (PretokenizedInputStream*) inputStream;
- return pretokenized->tokenReader.AdvanceToken();
- }
-}
-
-// Peek one token from an input stream
-static Token PeekRawToken(PreprocessorInputStream* inputStream)
-{
- if( auto primaryStream = asPrimaryInputStream(inputStream) )
- {
- return primaryStream->token;
- }
- else
- {
- PretokenizedInputStream* pretokenized = (PretokenizedInputStream*) inputStream;
- return pretokenized->tokenReader.PeekToken();
- }
-}
-
-// Peek one token type from an input stream
-static TokenType PeekRawTokenType(PreprocessorInputStream* inputStream)
-{
- if( auto primaryStream = asPrimaryInputStream(inputStream) )
- {
- return primaryStream->token.type;
- }
- else
- {
- PretokenizedInputStream* pretokenized = (PretokenizedInputStream*) inputStream;
- return pretokenized->tokenReader.PeekTokenType();
- }
-}
-
-
-// Read one token in "raw" mode (meaning don't expand macros)
-static Token AdvanceRawToken(Preprocessor* preprocessor, LexerFlags lexerFlags = 0)
-{
- for(;;)
- {
- // Look at the input stream on top of the stack
- PreprocessorInputStream* inputStream = preprocessor->inputStream;
-
- // If there isn't one, then there is no more input left to read.
- if(!inputStream)
- {
- return preprocessor->endOfFileToken;
- }
-
- // The top-most input stream may be at its end
- if(PeekRawTokenType(inputStream) == TokenType::EndOfFile)
- {
- // If there is another stream remaining, switch to it
- if(inputStream->parent)
- {
- preprocessor->inputStream = inputStream->parent;
- EndInputStream(preprocessor, inputStream);
- continue;
- }
- }
-
- // Everything worked, so read a token from the top-most stream
- return AdvanceRawToken(
- inputStream,
- lexerFlags | (IsSkipping(preprocessor) ? kLexerFlag_IgnoreInvalid : 0));
- }
-}
-
-// Return the next token in "raw" mode, but don't advance the
-// current token state.
-static Token PeekRawToken(Preprocessor* preprocessor)
-{
- // We need to find the stream that `advanceRawToken` would read from.
- PreprocessorInputStream* inputStream = preprocessor->inputStream;
- for (;;)
- {
- if (!inputStream)
- {
- // No more input streams left to read
- return preprocessor->endOfFileToken;
- }
-
- // The top-most input stream may be at its end, so
- // look one entry up the stack (don't actually pop
- // here, since we are just peeking)
- if (PeekRawTokenType(inputStream) == TokenType::EndOfFile)
- {
- if (inputStream->parent)
- {
- inputStream = inputStream->parent;
- continue;
- }
- }
-
- // Everything worked, so the token we just peeked is fine.
- return PeekRawToken(inputStream);
- }
-}
-
-// Get the location of the current (raw) token
-static SourceLoc PeekLoc(Preprocessor* preprocessor)
-{
- return PeekRawToken(preprocessor).loc;
-}
-
-// Get the `TokenType` of the current (raw) token
-static TokenType PeekRawTokenType(Preprocessor* preprocessor)
-{
- return PeekRawToken(preprocessor).type;
-}
-
-//
-// Macros
-//
-
-// Create a macro
-static PreprocessorMacro* CreateMacro(Preprocessor* preprocessor)
-{
- // TODO(tfoley): Allocate these more intelligently.
- // For example, consider pooling them on the preprocessor.
-
- PreprocessorMacro* macro = new PreprocessorMacro();
- macro->flavor = PreprocessorMacroFlavor::ObjectLike;
- macro->environment = &preprocessor->globalEnv;
- return macro;
-}
-
-// Destroy a macro
-static void DestroyMacro(Preprocessor* /*preprocessor*/, PreprocessorMacro* macro)
-{
- delete macro;
-}
-
-
-// Find the currently-defined macro of the given name, or return NULL
-static PreprocessorMacro* LookupMacro(PreprocessorEnvironment* environment, Name* name)
-{
- for(PreprocessorEnvironment* e = environment; e; e = e->parent)
- {
- PreprocessorMacro* macro = NULL;
- if (e->macros.TryGetValue(name, macro))
- return macro;
- }
-
- return NULL;
-}
-
-static PreprocessorEnvironment* GetCurrentEnvironment(Preprocessor* preprocessor)
-{
- // The environment we will use for looking up a macro is associated
- // with the current input stream (because it may include entries
- // for macro arguments).
- //
- // We need to be careful, though, when we are at the end of an
- // input stream (e.g., representing one argument), so that we
- // don't use its environment.
-
- PreprocessorInputStream* inputStream = preprocessor->inputStream;
-
- for(;;)
- {
- // If there is no input stream that isn't at its end,
- // then fall back to the global environment.
- if (!inputStream)
- return &preprocessor->globalEnv;
-
- // If the current input stream is at its end, then
- // fall back to its parent stream.
- if (PeekRawTokenType(inputStream) == TokenType::EndOfFile)
- {
- inputStream = inputStream->parent;
- continue;
- }
-
- // If we've found an active stream that isn't at its end,
- // then use that for lookup.
- return inputStream->environment;
- }
-}
-
-static PreprocessorMacro* LookupMacro(Preprocessor* preprocessor, Name* name)
-{
- return LookupMacro(GetCurrentEnvironment(preprocessor), name);
-}
-
-// A macro is "busy" if it is currently being used for expansion.
-// A macro cannot be expanded again while busy, to avoid infinite recursion.
-static bool IsMacroBusy(PreprocessorMacro* /*macro*/)
-{
- // TODO: need to implement this correctly
- //
- // The challenge here is that we are implementing expansion
- // for argumenst to function-like macros in a "lazy" fashion.
- //
- // The letter of the spec is that we should macro expand
- // each argument *before* substitution, and then go and
- // macro-expand the substituted body. This means that we
- // can invoke a macro as part of an argument to an
- // invocation of the same macro:
- //
- // FOO( 1, FOO(22), 333 );
- //
- // In our implementation, the "inner" invocation of `FOO`
- // gets expanded at the point where it gets referenced
- // in the body of the "outer" invocation of `FOO`.
- // Doing things this way leads to greatly simplified
- // code for handling expansion.
- //
- // A proper implementation of `IsMacroBusy` needs to
- // take context into account, so that it bans recursive
- // use of a macro when it occurs (indirectly) through
- // the *body* of the expansion, but not when it occcurs
- // only through an *argument*.
- return false;
-}
-
-//
-// Reading Tokens With Expansion
-//
-
-static void InitializeMacroExpansion(
- Preprocessor* preprocessor,
- MacroExpansion* expansion,
- PreprocessorMacro* macro)
-{
- initializeInputStream(preprocessor, expansion);
-
- expansion->parent = preprocessor->inputStream;
- expansion->primaryStream = preprocessor->inputStream->primaryStream;
-
- expansion->environment = macro->environment;
- expansion->macro = macro;
- expansion->tokenReader = TokenReader(macro->tokens);
-}
-
-static void PushMacroExpansion(
- Preprocessor* preprocessor,
- MacroExpansion* expansion)
-{
- PushInputStream(preprocessor, expansion);
-}
-
-static void AddEndOfStreamToken(
- Preprocessor* preprocessor,
- PreprocessorMacro* macro)
-{
- Token token = PeekRawToken(preprocessor);
- token.type = TokenType::EndOfFile;
- macro->tokens.mTokens.add(token);
-}
-
-static SimpleTokenInputStream* createSimpleInputStream(
- Preprocessor* preprocessor,
- Token const& token)
-{
- SimpleTokenInputStream* inputStream = new SimpleTokenInputStream();
- initializeInputStream(preprocessor, inputStream);
-
- inputStream->lexedTokens.mTokens.add(token);
-
- Token eofToken;
- eofToken.type = TokenType::EndOfFile;
- eofToken.loc = token.loc;
- eofToken.flags = TokenFlag::AfterWhitespace | TokenFlag::AtStartOfLine;
- inputStream->lexedTokens.mTokens.add(eofToken);
-
- inputStream->tokenReader = TokenReader(inputStream->lexedTokens);
-
- return inputStream;
-}
-
-// Check whether the current token on the given input stream should be
-// treated as a macro invocation, and if so set up state for expanding
-// that macro.
-static void MaybeBeginMacroExpansion(
- Preprocessor* preprocessor )
-{
- // We iterate because the first token in the expansion of one
- // macro may be another macro invocation.
- for (;;)
- {
- // Look at the next token ahead of us
- Token token = PeekRawToken(preprocessor);
-
- // Not an identifier? Can't be a macro.
- if (token.type != TokenType::Identifier)
- return;
-
- // Look for a macro with the given name.
- Name* name = token.getName();
- PreprocessorMacro* macro = LookupMacro(preprocessor, name);
-
- // Not a macro? Can't be an invocation.
- if (!macro)
- return;
-
- // If the macro is busy (already being expanded),
- // don't try to trigger recursive expansion
- if (IsMacroBusy(macro))
- return;
-
- // We might already have looked at this token,
- // and need to suppress expansion
- if (token.flags & TokenFlag::SuppressMacroExpansion)
- return;
-
- // A function-style macro invocation should only match
- // if the token *after* the identifier is `(`. This
- // requires more lookahead than we usually have/need
- if (macro->flavor == PreprocessorMacroFlavor::FunctionLike)
- {
- // Consume the token that (possibly) triggered macro expansion
- AdvanceRawToken(preprocessor);
-
- // Look at the next token, and see if it is an opening `(`
- // that indicates we should actually expand a macro.
- if(PeekRawTokenType(preprocessor) != TokenType::LParent)
- {
- // In this case, we are in a bit of a mess, because we have
- // consumed the token that named the macro, but we need to
- // make sure that token (and not whatever came after it)
- // gets returned to the user.
- //
- // To work around this we will construct a short-lived input
- // stream just to handle that one token, and also set
- // a flag on the token to keep us from doing this logic again.
-
- token.flags |= TokenFlag::SuppressMacroExpansion;
-
- SimpleTokenInputStream* simpleStream = createSimpleInputStream(preprocessor, token);
- PushInputStream(preprocessor, simpleStream);
- return;
- }
-
- // Consume the opening `(`
- Token leftParen = AdvanceRawToken(preprocessor);
-
- FunctionLikeMacroExpansion* expansion = new FunctionLikeMacroExpansion();
- InitializeMacroExpansion(preprocessor, expansion, macro);
- expansion->argumentEnvironment.parent = &preprocessor->globalEnv;
- expansion->environment = &expansion->argumentEnvironment;
-
- // Try to read any arguments present.
- UInt paramCount = macro->params.getCount();
- UInt argIndex = 0;
-
- switch (PeekRawTokenType(preprocessor))
- {
- case TokenType::EndOfFile:
- case TokenType::RParent:
- // No arguments.
- break;
-
- default:
- // At least one argument
- while(argIndex < paramCount)
- {
- // Read an argument
-
- // Create the argument, represented as a special flavor of macro
- PreprocessorMacro* arg = CreateMacro(preprocessor);
- arg->flavor = PreprocessorMacroFlavor::FunctionArg;
- arg->environment = GetCurrentEnvironment(preprocessor);
-
- // Associate the new macro with its parameter name
- NameLoc paramNameAndLoc = macro->params[argIndex];
- Name* paramName = paramNameAndLoc.name;
- arg->nameAndLoc = paramNameAndLoc;
- expansion->argumentEnvironment.macros[paramName] = arg;
- argIndex++;
-
- // Read tokens for the argument
-
- // We track the nesting depth, since we don't break
- // arguments on a `,` nested in balanced parentheses
- //
- int nesting = 0;
- for (;;)
- {
- switch (PeekRawTokenType(preprocessor))
- {
- case TokenType::EndOfFile:
- // if we reach the end of the file,
- // then we have an error, and need to
- // bail out
- AddEndOfStreamToken(preprocessor, arg);
- goto doneWithAllArguments;
-
- case TokenType::RParent:
- // If we see a right paren when we aren't nested
- // then we are at the end of an argument
- if (nesting == 0)
- {
- AddEndOfStreamToken(preprocessor, arg);
- goto doneWithAllArguments;
- }
- // Otherwise we decrease our nesting depth, add
- // the token, and keep going
- nesting--;
- break;
-
- case TokenType::Comma:
- // If we see a comma when we aren't nested
- // then we are at the end of an argument
- if (nesting == 0)
- {
- AddEndOfStreamToken(preprocessor, arg);
- AdvanceRawToken(preprocessor);
- goto doneWithArgument;
- }
- // Otherwise we add it as a normal token
- break;
-
- case TokenType::LParent:
- // If we see a left paren then we need to
- // increase our tracking of nesting
- nesting++;
- break;
-
- default:
- break;
- }
-
- // Add the token and continue parsing.
- arg->tokens.mTokens.add(AdvanceRawToken(preprocessor));
- }
- doneWithArgument: {}
- // We've parsed an argument and should move onto
- // the next one.
- }
- break;
- }
- doneWithAllArguments:
- // TODO: handle possible varargs
-
- // Expect closing right paren
- if (PeekRawTokenType(preprocessor) == TokenType::RParent)
- {
- AdvanceRawToken(preprocessor);
- }
- else
- {
- GetSink(preprocessor)->diagnose(PeekLoc(preprocessor), Diagnostics::expectedTokenInMacroArguments, TokenType::RParent, PeekRawTokenType(preprocessor));
- }
-
- UInt argCount = argIndex;
- if (argCount != paramCount)
- {
- GetSink(preprocessor)->diagnose(PeekLoc(preprocessor), Diagnostics::wrongNumberOfArgumentsToMacro, paramCount, argCount);
- }
-
- // We are ready to expand.
- PushMacroExpansion(preprocessor, expansion);
- }
- else
- {
- // Consume the token that triggered macro expansion
- AdvanceRawToken(preprocessor);
-
- // Object-like macros are the easy case.
- ObjectLikeMacroExpansion* expansion = new ObjectLikeMacroExpansion();
- InitializeMacroExpansion(preprocessor, expansion, macro);
- PushMacroExpansion(preprocessor, expansion);
- }
- }
-}
-
-// Read one token with macro-expansion enabled.
-static Token AdvanceToken(Preprocessor* preprocessor)
-{
-top:
- // Check whether we need to macro expand at the cursor.
- MaybeBeginMacroExpansion(preprocessor);
-
- // Read a raw token (now that expansion has been triggered)
- Token token = AdvanceRawToken(preprocessor);
-
- // Check if we need to perform token pasting
- if (PeekRawTokenType(preprocessor) != TokenType::PoundPound)
- {
- // If we aren't token pasting, then we are done
- return token;
- }
- else
- {
- // We are pasting tokens, which could get messy
-
- StringBuilder sb;
- sb << token.Content;
-
- while (PeekRawTokenType(preprocessor) == TokenType::PoundPound)
- {
- // Consume the `##`
- AdvanceRawToken(preprocessor);
-
- // Possibly macro-expand the next token
- MaybeBeginMacroExpansion(preprocessor);
-
- // Read the next raw token (now that expansion has been triggered)
- Token nextToken = AdvanceRawToken(preprocessor);
-
- sb << nextToken.Content;
- }
-
- // Now re-lex the input
-
- SourceManager* sourceManager = preprocessor->getSourceManager();
-
- // We create a dummy file to represent the token-paste operation
- PathInfo pathInfo = PathInfo::makeTokenPaste();
-
- SourceFile* sourceFile = sourceManager->createSourceFileWithString(pathInfo, sb.ProduceString());
- SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr);
-
- Lexer lexer;
- lexer.initialize(sourceView, GetSink(preprocessor), preprocessor->getNamePool(), sourceManager->getMemoryArena());
-
- SimpleTokenInputStream* inputStream = new SimpleTokenInputStream();
- initializeInputStream(preprocessor, inputStream);
-
- inputStream->lexedTokens = lexer.lexAllTokens();
- inputStream->tokenReader = TokenReader(inputStream->lexedTokens);
-
- // We expect the reuslt of lexing to be two tokens: one for the actual value,
- // and one for the end-of-input marker.
- if (inputStream->tokenReader.GetCount() != 2)
- {
- // We expect a token paste to produce a single token
- // TODO(tfoley): emit a diagnostic here
- }
-
- PushInputStream(preprocessor, inputStream);
- goto top;
- }
-}
-
-// Read one token with macro-expansion enabled.
-//
-// Note that because triggering macro expansion may
-// involve changing the input-stream state, this
-// operation *can* have side effects.
-static Token PeekToken(Preprocessor* preprocessor)
-{
- // Check whether we need to macro expand at the cursor.
- MaybeBeginMacroExpansion(preprocessor);
-
- // Peek a raw token (now that expansion has been triggered)
- return PeekRawToken(preprocessor);
-
- // TODO: need a plan for how to handle token pasting
- // here without it being onerous. Would be nice if we
- // didn't have to re-do pasting on a "peek"...
-}
-
-// Peek the type of the next token, including macro expansion.
-static TokenType PeekTokenType(Preprocessor* preprocessor)
-{
- return PeekToken(preprocessor).type;
-}
-
-//
-// Preprocessor Directives
-//
-
-// When reading a preprocessor directive, we use a context
-// to wrap the direct preprocessor routines defines so far.
-//
-// One of the most important things the directive context
-// does is give us a convenient way to read tokens with
-// a guarantee that we won't read past the end of a line.
-struct PreprocessorDirectiveContext
-{
- // The preprocessor that is parsing the directive.
- Preprocessor* preprocessor;
-
- // The directive token (e.g., the `if` in `#if`).
- // Useful for reference in diagnostic messages.
- Token directiveToken;
-
- // Has any kind of parse error been encountered in
- // the directive so far?
- bool parseError;
-
- // Have we done the necessary checks at the end
- // of the directive already?
- bool haveDoneEndOfDirectiveChecks;
-};
-
-// Get the token for the preprocessor directive being parsed.
-inline Token const& GetDirective(PreprocessorDirectiveContext* context)
-{
- return context->directiveToken;
-}
-
-// Get the name of the directive being parsed.
-inline UnownedStringSlice const& GetDirectiveName(PreprocessorDirectiveContext* context)
-{
- return context->directiveToken.Content;
-}
-
-// Get the location of the directive being parsed.
-inline SourceLoc const& GetDirectiveLoc(PreprocessorDirectiveContext* context)
-{
- return context->directiveToken.loc;
-}
-
-// Wrapper to get the diagnostic sink in the context of a directive.
-static inline DiagnosticSink* GetSink(PreprocessorDirectiveContext* context)
-{
- return GetSink(context->preprocessor);
-}
-
-// Wrapper to get a "current" location when parsing a directive
-static SourceLoc PeekLoc(PreprocessorDirectiveContext* context)
-{
- return PeekLoc(context->preprocessor);
-}
-
-// Wrapper to look up a macro in the context of a directive.
-static PreprocessorMacro* LookupMacro(PreprocessorDirectiveContext* context, Name* name)
-{
- return LookupMacro(context->preprocessor, name);
-}
-
-// Determine if we have read everything on the directive's line.
-static bool IsEndOfLine(PreprocessorDirectiveContext* context)
-{
- return PeekRawToken(context->preprocessor).type == TokenType::EndOfDirective;
-}
-
-// Peek one raw token in a directive, without going past the end of the line.
-static Token PeekRawToken(PreprocessorDirectiveContext* context)
-{
- return PeekRawToken(context->preprocessor);
-}
-
-// Read one raw token in a directive, without going past the end of the line.
-static Token AdvanceRawToken(PreprocessorDirectiveContext* context, LexerFlags lexerFlags = 0)
-{
- if (IsEndOfLine(context))
- return PeekRawToken(context);
- return AdvanceRawToken(context->preprocessor, lexerFlags);
-}
-
-// Peek next raw token type, without going past the end of the line.
-static TokenType PeekRawTokenType(PreprocessorDirectiveContext* context)
-{
- return PeekRawTokenType(context->preprocessor);
-}
-
-// Read one token, with macro-expansion, without going past the end of the line.
-static Token AdvanceToken(PreprocessorDirectiveContext* context)
-{
- if (IsEndOfLine(context))
- return PeekRawToken(context);
- return AdvanceToken(context->preprocessor);
-}
-
-// Peek one token, with macro-expansion, without going past the end of the line.
-static Token PeekToken(PreprocessorDirectiveContext* context)
-{
- if (IsEndOfLine(context))
- return context->preprocessor->endOfFileToken;
- return PeekToken(context->preprocessor);
-}
-
-// Peek next token type, with macro-expansion, without going past the end of the line.
-static TokenType PeekTokenType(PreprocessorDirectiveContext* context)
-{
- if (IsEndOfLine(context))
- return TokenType::EndOfDirective;
- return PeekTokenType(context->preprocessor);
-}
-
-// Skip to the end of the line (useful for recovering from errors in a directive)
-static void SkipToEndOfLine(PreprocessorDirectiveContext* context)
-{
- while(!IsEndOfLine(context))
- {
- AdvanceRawToken(context);
- }
-}
-
-static bool ExpectRaw(PreprocessorDirectiveContext* context, TokenType tokenType, DiagnosticInfo const& diagnostic, Token* outToken = NULL)
-{
- if (PeekRawTokenType(context) != tokenType)
- {
- // Only report the first parse error within a directive
- if (!context->parseError)
- {
- GetSink(context)->diagnose(PeekLoc(context), diagnostic, tokenType, GetDirectiveName(context));
- }
- context->parseError = true;
- return false;
- }
- Token const& token = AdvanceRawToken(context);
- if (outToken)
- *outToken = token;
- return true;
-}
-
-static bool Expect(PreprocessorDirectiveContext* context, TokenType tokenType, DiagnosticInfo const& diagnostic, Token* outToken = NULL)
-{
- if (PeekTokenType(context) != tokenType)
- {
- // Only report the first parse error within a directive
- if (!context->parseError)
- {
- GetSink(context)->diagnose(PeekLoc(context), diagnostic, tokenType, GetDirectiveName(context));
- context->parseError = true;
- }
- return false;
- }
- Token const& token = AdvanceToken(context);
- if (outToken)
- *outToken = token;
- return true;
-}
-
-
-
-//
-// Preprocessor Conditionals
-//
-
-// Determine whether the current preprocessor state means we
-// should be skipping tokens.
-static bool IsSkipping(Preprocessor* preprocessor)
-{
- PreprocessorInputStream* inputStream = preprocessor->inputStream;
- if (!inputStream) return false;
-
- PrimaryInputStream* primaryStream = inputStream->primaryStream;
- if(!primaryStream) return false;
-
- // If we are not inside a preprocessor conditional, then don't skip
- PreprocessorConditional* conditional = primaryStream->conditional;
- if (!conditional) return false;
-
- // skip tokens unless the conditional is inside its `true` case
- return conditional->state != PreprocessorConditionalState::During;
-}
-
-// Wrapper for use inside directives
-static inline bool IsSkipping(PreprocessorDirectiveContext* context)
-{
- return IsSkipping(context->preprocessor);
-}
-
-// Create a preprocessor conditional
-static PreprocessorConditional* CreateConditional(Preprocessor* /*preprocessor*/)
-{
- // TODO(tfoley): allocate these more intelligently (for example,
- // pool them on the `Preprocessor`.
- return new PreprocessorConditional();
-}
-
-// Destroy a preprocessor conditional.
-static void DestroyConditional(PreprocessorConditional* conditional)
-{
- delete conditional;
-}
-
-// Start a preprocessor conditional, with an initial enable/disable state.
-static void beginConditional(
- PreprocessorDirectiveContext* context,
- PreprocessorInputStream* inputStream,
- bool enable)
-{
- Preprocessor* preprocessor = context->preprocessor;
- SLANG_ASSERT(inputStream);
-
- PreprocessorConditional* conditional = CreateConditional(preprocessor);
-
- conditional->ifToken = context->directiveToken;
-
- // Set state of this condition appropriately.
- //
- // Default to the "haven't yet seen a `true` branch" state.
- PreprocessorConditionalState state = PreprocessorConditionalState::Before;
- //
- // If we are nested inside a `false` branch of another condition, then
- // we never want to enable, so we act as if we already *saw* the `true` branch.
- //
- if (IsSkipping(preprocessor)) state = PreprocessorConditionalState::After;
- //
- // Similarly, if we ran into any parse errors when dealing with the
- // opening directive, then things are probably screwy and we should just
- // skip all the branches.
- if (IsSkipping(preprocessor)) state = PreprocessorConditionalState::After;
- //
- // Otherwise, if our condition was true, then set us to be inside the `true` branch
- else if (enable) state = PreprocessorConditionalState::During;
-
- conditional->state = state;
-
- // Push conditional onto the stack
- auto primaryStream = inputStream->primaryStream;
- conditional->parent = primaryStream->conditional;
- primaryStream->conditional = conditional;
-}
-
-// Start a preprocessor conditional, with an initial enable/disable state.
-static void beginConditional(
- PreprocessorDirectiveContext* context,
- bool enable)
-{
- beginConditional(context, context->preprocessor->inputStream, enable);
-}
-
-//
-// Preprocessor Conditional Expressions
-//
-
-// Conditional expressions are always of type `int`
-typedef int PreprocessorExpressionValue;
-
-// Forward-declaretion
-static PreprocessorExpressionValue ParseAndEvaluateExpression(PreprocessorDirectiveContext* context);
-
-// Parse a unary (prefix) expression inside of a preprocessor directive.
-static PreprocessorExpressionValue ParseAndEvaluateUnaryExpression(PreprocessorDirectiveContext* context)
-{
- switch (PeekTokenType(context))
- {
- // handle prefix unary ops
- case TokenType::OpSub:
- AdvanceToken(context);
- return -ParseAndEvaluateUnaryExpression(context);
- case TokenType::OpNot:
- AdvanceToken(context);
- return !ParseAndEvaluateUnaryExpression(context);
- case TokenType::OpBitNot:
- AdvanceToken(context);
- return ~ParseAndEvaluateUnaryExpression(context);
-
- // handle parenthized sub-expression
- case TokenType::LParent:
- {
- Token leftParen = AdvanceToken(context);
- PreprocessorExpressionValue value = ParseAndEvaluateExpression(context);
- if (!Expect(context, TokenType::RParent, Diagnostics::expectedTokenInPreprocessorExpression))
- {
- GetSink(context)->diagnose(leftParen.loc, Diagnostics::seeOpeningToken, leftParen);
- }
- return value;
- }
-
- case TokenType::IntegerLiteral:
- return StringToInt(AdvanceToken(context).Content);
-
- case TokenType::Identifier:
- {
- Token token = AdvanceToken(context);
- if (token.Content == "defined")
- {
- // handle `defined(someName)`
-
- // Possibly parse a `(`
- Token leftParen;
- if (PeekRawTokenType(context) == TokenType::LParent)
- {
- leftParen = AdvanceRawToken(context);
- }
-
- // Expect an identifier
- Token nameToken;
- if (!ExpectRaw(context, TokenType::Identifier, Diagnostics::expectedTokenInDefinedExpression, &nameToken))
- {
- return 0;
- }
- Name* name = nameToken.getName();
-
- // If we saw an opening `(`, then expect one to close
- if (leftParen.type != TokenType::Unknown)
- {
- if(!ExpectRaw(context, TokenType::RParent, Diagnostics::expectedTokenInDefinedExpression))
- {
- GetSink(context)->diagnose(leftParen.loc, Diagnostics::seeOpeningToken, leftParen);
- return 0;
- }
- }
-
- return LookupMacro(context, name) != NULL;
- }
-
- // An identifier here means it was not defined as a macro (or
- // it is defined, but as a function-like macro. These should
- // just evaluate to zero (possibly with a warning)
- GetSink(context)->diagnose(token.loc, Diagnostics::undefinedIdentifierInPreprocessorExpression, token.getName());
- return 0;
- }
-
- default:
- GetSink(context)->diagnose(PeekLoc(context), Diagnostics::syntaxErrorInPreprocessorExpression);
- return 0;
- }
-}
-
-// Determine the precedence level of an infix operator
-// for use in parsing preprocessor conditionals.
-static int GetInfixOpPrecedence(Token const& opToken)
-{
- // If token is on another line, it is not part of the
- // expression
- if (opToken.flags & TokenFlag::AtStartOfLine)
- return -1;
-
- // otherwise we look at the token type to figure
- // out what precedence it should be parse with
- switch (opToken.type)
- {
- default:
- // tokens that aren't infix operators should
- // cause us to stop parsing an expression
- return -1;
-
- case TokenType::OpMul: return 10;
- case TokenType::OpDiv: return 10;
- case TokenType::OpMod: return 10;
-
- case TokenType::OpAdd: return 9;
- case TokenType::OpSub: return 9;
-
- case TokenType::OpLsh: return 8;
- case TokenType::OpRsh: return 8;
-
- case TokenType::OpLess: return 7;
- case TokenType::OpGreater: return 7;
- case TokenType::OpLeq: return 7;
- case TokenType::OpGeq: return 7;
-
- case TokenType::OpEql: return 6;
- case TokenType::OpNeq: return 6;
-
- case TokenType::OpBitAnd: return 5;
- case TokenType::OpBitOr: return 4;
- case TokenType::OpBitXor: return 3;
- case TokenType::OpAnd: return 2;
- case TokenType::OpOr: return 1;
- }
-};
-
-// Evaluate one infix operation in a preprocessor
-// conditional expression
-static PreprocessorExpressionValue EvaluateInfixOp(
- PreprocessorDirectiveContext* context,
- Token const& opToken,
- PreprocessorExpressionValue left,
- PreprocessorExpressionValue right)
-{
- switch (opToken.type)
- {
- default:
-// SLANG_INTERNAL_ERROR(getSink(preprocessor), opToken);
- return 0;
- break;
-
- case TokenType::OpMul: return left * right;
- case TokenType::OpDiv:
- {
- if (right == 0)
- {
- if (!context->parseError)
- {
- GetSink(context)->diagnose(opToken.loc, Diagnostics::divideByZeroInPreprocessorExpression);
- }
- return 0;
- }
- return left / right;
- }
- case TokenType::OpMod:
- {
- if (right == 0)
- {
- if (!context->parseError)
- {
- GetSink(context)->diagnose(opToken.loc, Diagnostics::divideByZeroInPreprocessorExpression);
- }
- return 0;
- }
- return left % right;
- }
- case TokenType::OpAdd: return left + right;
- case TokenType::OpSub: return left - right;
- case TokenType::OpLsh: return left << right;
- case TokenType::OpRsh: return left >> right;
- case TokenType::OpLess: return left < right ? 1 : 0;
- case TokenType::OpGreater: return left > right ? 1 : 0;
- case TokenType::OpLeq: return left <= right ? 1 : 0;
- case TokenType::OpGeq: return left >= right ? 1 : 0;
- case TokenType::OpEql: return left == right ? 1 : 0;
- case TokenType::OpNeq: return left != right ? 1 : 0;
- case TokenType::OpBitAnd: return left & right;
- case TokenType::OpBitOr: return left | right;
- case TokenType::OpBitXor: return left ^ right;
- case TokenType::OpAnd: return left && right;
- case TokenType::OpOr: return left || right;
- }
-}
-
-// Parse the rest of an infix preprocessor expression with
-// precedence greater than or equal to the given `precedence` argument.
-// The value of the left-hand-side expression is provided as
-// an argument.
-// This is used to form a simple recursive-descent expression parser.
-static PreprocessorExpressionValue ParseAndEvaluateInfixExpressionWithPrecedence(
- PreprocessorDirectiveContext* context,
- PreprocessorExpressionValue left,
- int precedence)
-{
- for (;;)
- {
- // Look at the next token, and see if it is an operator of
- // high enough precedence to be included in our expression
- Token opToken = PeekToken(context);
- int opPrecedence = GetInfixOpPrecedence(opToken);
-
- // If it isn't an operator of high enough precedence, we are done.
- if(opPrecedence < precedence)
- break;
-
- // Otherwise we need to consume the operator token.
- AdvanceToken(context);
-
- // Next we parse a right-hand-side expression by starting with
- // a unary expression and absorbing and many infix operators
- // as possible with strictly higher precedence than the operator
- // we found above.
- PreprocessorExpressionValue right = ParseAndEvaluateUnaryExpression(context);
- for (;;)
- {
- // Look for an operator token
- Token rightOpToken = PeekToken(context);
- int rightOpPrecedence = GetInfixOpPrecedence(rightOpToken);
-
- // If no operator was found, or the operator wasn't high
- // enough precedence to fold into the right-hand-side,
- // exit this loop.
- if (rightOpPrecedence <= opPrecedence)
- break;
-
- // Now invoke the parser recursively, passing in our
- // existing right-hand side to form an even larger one.
- right = ParseAndEvaluateInfixExpressionWithPrecedence(
- context,
- right,
- rightOpPrecedence);
- }
-
- // Now combine the left- and right-hand sides using
- // the operator we found above.
- left = EvaluateInfixOp(context, opToken, left, right);
- }
- return left;
-}
-
-// Parse a complete (infix) preprocessor expression, and return its value
-static PreprocessorExpressionValue ParseAndEvaluateExpression(PreprocessorDirectiveContext* context)
-{
- // First read in the left-hand side (or the whole expression in the unary case)
- PreprocessorExpressionValue value = ParseAndEvaluateUnaryExpression(context);
-
- // Try to read in trailing infix operators with correct precedence
- return ParseAndEvaluateInfixExpressionWithPrecedence(context, value, 0);
-}
-
-// Handle a `#if` directive
-static void HandleIfDirective(PreprocessorDirectiveContext* context)
-{
- // Record current input stream in case preprocessor expression
- // changes the input stream to a macro expansion while we
- // are parsing.
- auto inputStream = context->preprocessor->inputStream;
-
- // If we are skipping, we can just consume the expression, and assume true
- if (IsSkipping(context->preprocessor))
- {
- // Consume everything until the end of the line
- SkipToEndOfLine(context);
- // Begin a preprocessor block, assume true based on the expression
- // (contents will all be ignored because skipping).
- beginConditional(context, inputStream, true);
- }
- else
- {
- // Parse a preprocessor expression.
- PreprocessorExpressionValue value = ParseAndEvaluateExpression(context);
-
- // Begin a preprocessor block, enabled based on the expression.
- beginConditional(context, inputStream, value != 0);
- }
-}
-
-// Handle a `#ifdef` directive
-static void HandleIfDefDirective(PreprocessorDirectiveContext* context)
-{
- // Expect a raw identifier, so we can check if it is defined
- Token nameToken;
- if(!ExpectRaw(context, TokenType::Identifier, Diagnostics::expectedTokenInPreprocessorDirective, &nameToken))
- return;
- Name* name = nameToken.getName();
-
- // Check if the name is defined.
- beginConditional(context, LookupMacro(context, name) != NULL);
-}
-
-// Handle a `#ifndef` directive
-static void HandleIfNDefDirective(PreprocessorDirectiveContext* context)
-{
- // Expect a raw identifier, so we can check if it is defined
- Token nameToken;
- if(!ExpectRaw(context, TokenType::Identifier, Diagnostics::expectedTokenInPreprocessorDirective, &nameToken))
- return;
- Name* name = nameToken.getName();
-
- // Check if the name is defined.
- beginConditional(context, LookupMacro(context, name) == NULL);
-}
-
-// Handle a `#else` directive
-static void HandleElseDirective(PreprocessorDirectiveContext* context)
-{
- PreprocessorInputStream* inputStream = context->preprocessor->inputStream;
- SLANG_ASSERT(inputStream);
-
- // if we aren't inside a conditional, then error
- PreprocessorConditional* conditional = inputStream->primaryStream->conditional;
- if (!conditional)
- {
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveWithoutIf, GetDirectiveName(context));
- return;
- }
-
- // if we've already seen a `#else`, then it is an error
- if (conditional->elseToken.type != TokenType::Unknown)
- {
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveAfterElse, GetDirectiveName(context));
- GetSink(context)->diagnose(conditional->elseToken.loc, Diagnostics::seeDirective);
- return;
- }
- conditional->elseToken = context->directiveToken;
-
- switch (conditional->state)
- {
- case PreprocessorConditionalState::Before:
- conditional->state = PreprocessorConditionalState::During;
- break;
-
- case PreprocessorConditionalState::During:
- conditional->state = PreprocessorConditionalState::After;
- break;
-
- default:
- break;
- }
-}
-
-// Handle a `#elif` directive
-static void HandleElifDirective(PreprocessorDirectiveContext* context)
-{
- // Need to grab current input stream *before* we try to parse
- // the conditional expression.
- PreprocessorInputStream* inputStream = context->preprocessor->inputStream;
- SLANG_ASSERT(inputStream);
-
- // HACK(tfoley): handle an empty `elif` like an `else` directive
- //
- // This is the behavior expected by at least one input program.
- // We will eventually want to be pedantic about this.
- // even if t
- if (PeekRawTokenType(context) == TokenType::EndOfDirective)
- {
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveExpectsExpression, GetDirectiveName(context));
- HandleElseDirective(context);
- return;
- }
-
- PreprocessorExpressionValue value = ParseAndEvaluateExpression(context);
-
- // if we aren't inside a conditional, then error
- PreprocessorConditional* conditional = inputStream->primaryStream->conditional;
- if (!conditional)
- {
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveWithoutIf, GetDirectiveName(context));
- return;
- }
-
- // if we've already seen a `#else`, then it is an error
- if (conditional->elseToken.type != TokenType::Unknown)
- {
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveAfterElse, GetDirectiveName(context));
- GetSink(context)->diagnose(conditional->elseToken.loc, Diagnostics::seeDirective);
- return;
- }
-
- switch (conditional->state)
- {
- case PreprocessorConditionalState::Before:
- if(value)
- conditional->state = PreprocessorConditionalState::During;
- break;
-
- case PreprocessorConditionalState::During:
- conditional->state = PreprocessorConditionalState::After;
- break;
-
- default:
- break;
- }
-}
-
-// Handle a `#endif` directive
-static void HandleEndIfDirective(PreprocessorDirectiveContext* context)
-{
- PreprocessorInputStream* inputStream = context->preprocessor->inputStream;
- SLANG_ASSERT(inputStream);
-
- // if we aren't inside a conditional, then error
- PreprocessorConditional* conditional = inputStream->primaryStream->conditional;
- if (!conditional)
- {
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveWithoutIf, GetDirectiveName(context));
- return;
- }
-
- inputStream->primaryStream->conditional = conditional->parent;
- DestroyConditional(conditional);
-}
-
-// Helper routine to check that we find the end of a directive where
-// we expect it.
-//
-// Most directives do not need to call this directly, since we have
-// a catch-all case in the main `HandleDirective()` function.
-// The `#include` case will call it directly to avoid complications
-// when it switches the input stream.
-static void expectEndOfDirective(PreprocessorDirectiveContext* context)
-{
- if(context->haveDoneEndOfDirectiveChecks)
- return;
-
- context->haveDoneEndOfDirectiveChecks = true;
-
- if (!IsEndOfLine(context))
- {
- // If we already saw a previous parse error, then don't
- // emit another one for the same directive.
- if (!context->parseError)
- {
- GetSink(context)->diagnose(PeekLoc(context), Diagnostics::unexpectedTokensAfterDirective, GetDirectiveName(context));
- }
- SkipToEndOfLine(context);
- }
-
- // Clear out the end-of-directive token
- AdvanceRawToken(context->preprocessor);
-}
-
- /// Read a file in the context of handling a preprocessor directive
-static SlangResult readFile(
- PreprocessorDirectiveContext* context,
- String const& path,
- ISlangBlob** outBlob)
-{
- // The actual file loading will be handled by the file system
- // associated with the parent linkage.
- //
- auto linkage = context->preprocessor->linkage;
- auto fileSystemExt = linkage->getFileSystemExt();
- SLANG_RETURN_ON_FAIL(fileSystemExt->loadFile(path.getBuffer(), outBlob));
-
- // If we are running the preprocessor as part of compiling a
- // specific module, then we must keep track of the file we've
- // read as yet another file that the module will depend on.
- //
- if(auto module = context->preprocessor->parentModule)
- {
- module->addFilePathDependency(path);
- }
-
- return SLANG_OK;
-}
-
-// Handle a `#include` directive
-static void HandleIncludeDirective(PreprocessorDirectiveContext* context)
-{
- // Consume the directive, and inform the lexer to process the remainder of the line as a file path.
- AdvanceRawToken(context, kLexerFlag_ExpectFileName);
-
- Token pathToken;
- if(!Expect(context, TokenType::StringLiteral, Diagnostics::expectedTokenInPreprocessorDirective, &pathToken))
- return;
-
- String path = getFileNameTokenValue(pathToken);
-
- auto directiveLoc = GetDirectiveLoc(context);
-
- PathInfo includedFromPathInfo = context->preprocessor->getSourceManager()->getPathInfo(directiveLoc, SourceLocType::Actual);
-
- IncludeHandler* includeHandler = context->preprocessor->includeHandler;
- if (!includeHandler)
- {
- GetSink(context)->diagnose(pathToken.loc, Diagnostics::includeFailed, path);
- GetSink(context)->diagnose(pathToken.loc, Diagnostics::noIncludeHandlerSpecified);
- return;
- }
-
- /* Find the path relative to the foundPath */
- PathInfo filePathInfo;
- if (SLANG_FAILED(includeHandler->findFile(path, includedFromPathInfo.foundPath, filePathInfo)))
- {
- GetSink(context)->diagnose(pathToken.loc, Diagnostics::includeFailed, path);
- return;
- }
-
- // We must have a uniqueIdentity to be compare
- if (!filePathInfo.hasUniqueIdentity())
- {
- GetSink(context)->diagnose(pathToken.loc, Diagnostics::noUniqueIdentity, path);
- return;
- }
-
- // Do all checking related to the end of this directive before we push a new stream,
- // just to avoid complications where that check would need to deal with
- // a switch of input stream
- expectEndOfDirective(context);
-
- // Check whether we've previously included this file and seen a `#pragma once` directive
- if(context->preprocessor->pragmaOnceUniqueIdentities.Contains(filePathInfo.uniqueIdentity))
- {
- return;
- }
-
- // Simplify the path
- filePathInfo.foundPath = includeHandler->simplifyPath(filePathInfo.foundPath);
-
- // Push the new file onto our stack of input streams
- // TODO(tfoley): check if we have made our include stack too deep
- auto sourceManager = context->preprocessor->getSourceManager();
-
- // See if this an already loaded source file
- SourceFile* sourceFile = sourceManager->findSourceFileRecursively(filePathInfo.uniqueIdentity);
- // If not create a new one, and add to the list of known source files
- if (!sourceFile)
- {
- ComPtr<ISlangBlob> foundSourceBlob;
- if (SLANG_FAILED(readFile(context, filePathInfo.foundPath, foundSourceBlob.writeRef())))
- {
- GetSink(context)->diagnose(pathToken.loc, Diagnostics::includeFailed, path);
- return;
- }
-
-
- sourceFile = sourceManager->createSourceFileWithBlob(filePathInfo, foundSourceBlob);
- sourceManager->addSourceFile(filePathInfo.uniqueIdentity, sourceFile);
- }
-
- // This is a new parse (even if it's a pre-existing source file), so create a new SourceUnit
- SourceView* sourceView = sourceManager->createSourceView(sourceFile, &filePathInfo);
-
- PreprocessorInputStream* inputStream = CreateInputStreamForSource(context->preprocessor, sourceView);
- inputStream->parent = context->preprocessor->inputStream;
- context->preprocessor->inputStream = inputStream;
-}
-
-// Handle a `#define` directive
-static void HandleDefineDirective(PreprocessorDirectiveContext* context)
-{
- Token nameToken;
- if (!ExpectRaw(context, TokenType::Identifier, Diagnostics::expectedTokenInPreprocessorDirective, &nameToken))
- return;
- Name* name = nameToken.getName();
-
- PreprocessorMacro* macro = CreateMacro(context->preprocessor);
- macro->nameAndLoc = NameLoc(nameToken);
-
- PreprocessorMacro* oldMacro = LookupMacro(&context->preprocessor->globalEnv, name);
- if (oldMacro)
- {
- GetSink(context)->diagnose(nameToken.loc, Diagnostics::macroRedefinition, name);
- GetSink(context)->diagnose(oldMacro->getLoc(), Diagnostics::seePreviousDefinitionOf, name);
-
- DestroyMacro(context->preprocessor, oldMacro);
- }
- context->preprocessor->globalEnv.macros[name] = macro;
-
- // If macro name is immediately followed (with no space) by `(`,
- // then we have a function-like macro
- if (PeekRawTokenType(context) == TokenType::LParent)
- {
- if (!(PeekRawToken(context).flags & TokenFlag::AfterWhitespace))
- {
- // This is a function-like macro, so we need to remember that
- // and start capturing parameters
- macro->flavor = PreprocessorMacroFlavor::FunctionLike;
-
- AdvanceRawToken(context);
-
- // If there are any parameters, parse them
- if (PeekRawTokenType(context) != TokenType::RParent)
- {
- for (;;)
- {
- // TODO: handle elipsis (`...`) for varags
-
- // A macro parameter name should be a raw identifier
- Token paramToken;
- if (!ExpectRaw(context, TokenType::Identifier, Diagnostics::expectedTokenInMacroParameters, &paramToken))
- break;
-
- // TODO(tfoley): some validation on parameter name.
- // Certain names (e.g., `defined` and `__VA_ARGS__`
- // are not allowed to be used as macros or parameters).
-
- // Add the parameter to the macro being deifned
- macro->params.add(paramToken);
-
- // If we see `)` then we are done with arguments
- if (PeekRawTokenType(context) == TokenType::RParent)
- break;
-
- ExpectRaw(context, TokenType::Comma, Diagnostics::expectedTokenInMacroParameters);
- }
- }
-
- ExpectRaw(context, TokenType::RParent, Diagnostics::expectedTokenInMacroParameters);
- }
- }
-
- // consume tokens until end-of-line
- for(;;)
- {
- Token token = AdvanceRawToken(context);
- if( token.type == TokenType::EndOfDirective )
- {
- // Last token on line will be turned into a conceptual end-of-file
- // token for the sub-stream that the macro expands into.
- token.type = TokenType::EndOfFile;
- macro->tokens.mTokens.add(token);
- break;
- }
-
- // In the ordinary case, we just add the token to the definition
- macro->tokens.mTokens.add(token);
- }
-}
-
-// Handle a `#undef` directive
-static void HandleUndefDirective(PreprocessorDirectiveContext* context)
-{
- Token nameToken;
- if (!ExpectRaw(context, TokenType::Identifier, Diagnostics::expectedTokenInPreprocessorDirective, &nameToken))
- return;
- Name* name = nameToken.getName();
-
- PreprocessorEnvironment* env = &context->preprocessor->globalEnv;
- PreprocessorMacro* macro = LookupMacro(env, name);
- if (macro != NULL)
- {
- // name was defined, so remove it
- env->macros.Remove(name);
-
- DestroyMacro(context->preprocessor, macro);
- }
- else
- {
- // name wasn't defined
- GetSink(context)->diagnose(nameToken.loc, Diagnostics::macroNotDefined, name);
- }
-}
-
-// Handle a `#warning` directive
-static void HandleWarningDirective(PreprocessorDirectiveContext* context)
-{
- // Consume the directive, and inform the lexer to process the remainder of the line as a custom message.
- AdvanceRawToken(context, kLexerFlag_ExpectDirectiveMessage);
-
- // Read the message token.
- Token messageToken;
- Expect(context, TokenType::DirectiveMessage, Diagnostics::expectedTokenInPreprocessorDirective, &messageToken);
-
- // Report the custom error.
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::userDefinedWarning, messageToken.Content);
-}
-
-// Handle a `#error` directive
-static void HandleErrorDirective(PreprocessorDirectiveContext* context)
-{
- // Consume the directive, and inform the lexer to process the remainder of the line as a custom message.
- AdvanceRawToken(context, kLexerFlag_ExpectDirectiveMessage);
-
- // Read the message token.
- Token messageToken;
- Expect(context, TokenType::DirectiveMessage, Diagnostics::expectedTokenInPreprocessorDirective, &messageToken);
-
- // Report the custom error.
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::userDefinedError, messageToken.Content);
-}
-
-// Handle a `#line` directive
-static void HandleLineDirective(PreprocessorDirectiveContext* context)
-{
- auto inputStream = context->preprocessor->inputStream;
-
- int line = 0;
-
- SourceLoc directiveLoc = GetDirectiveLoc(context);
-
- // `#line <integer-literal> ...`
- if (PeekTokenType(context) == TokenType::IntegerLiteral)
- {
- line = StringToInt(AdvanceToken(context).Content);
- }
- // `#line`
- // `#line default`
- else if (
- PeekTokenType(context) == TokenType::EndOfDirective
- || (PeekTokenType(context) == TokenType::Identifier
- && PeekToken(context).Content == "default"))
- {
- AdvanceToken(context);
-
- // Stop overriding source locations.
- auto sourceView = inputStream->primaryStream->lexer.sourceView;
- sourceView->addDefaultLineDirective(directiveLoc);
- return;
- }
- else
- {
- GetSink(context)->diagnose(PeekLoc(context), Diagnostics::expected2TokensInPreprocessorDirective,
- TokenType::IntegerLiteral,
- "default",
- GetDirectiveName(context));
- context->parseError = true;
- return;
- }
-
- auto sourceManager = context->preprocessor->getSourceManager();
-
- String file;
- if (PeekTokenType(context) == TokenType::EndOfDirective)
- {
- file = sourceManager->getPathInfo(directiveLoc).foundPath;
- }
- else if (PeekTokenType(context) == TokenType::StringLiteral)
- {
- file = getStringLiteralTokenValue(AdvanceToken(context));
- }
- else if (PeekTokenType(context) == TokenType::IntegerLiteral)
- {
- // Note(tfoley): GLSL allows the "source string" to be indicated by an integer
- // TODO(tfoley): Figure out a better way to handle this, if it matters
- file = AdvanceToken(context).Content;
- }
- else
- {
- Expect(context, TokenType::StringLiteral, Diagnostics::expectedTokenInPreprocessorDirective);
- return;
- }
-
- auto sourceView = inputStream->primaryStream->lexer.sourceView;
- sourceView->addLineDirective(directiveLoc, file, line);
-}
-
-#define SLANG_PRAGMA_DIRECTIVE_CALLBACK(NAME) \
- void NAME(PreprocessorDirectiveContext* context, Token subDirectiveToken)
-
-// Callback interface used by `#pragma` directives
-typedef SLANG_PRAGMA_DIRECTIVE_CALLBACK((*PragmaDirectiveCallback));
-
-SLANG_PRAGMA_DIRECTIVE_CALLBACK(handleUnknownPragmaDirective)
-{
- GetSink(context)->diagnose(subDirectiveToken, Diagnostics::unknownPragmaDirectiveIgnored, subDirectiveToken.getName());
- SkipToEndOfLine(context);
- return;
-}
-
-SLANG_PRAGMA_DIRECTIVE_CALLBACK(handlePragmaOnceDirective)
-{
- // We need to identify the path of the file we are preprocessing,
- // so that we can avoid including it again.
- //
- // We are using the 'uniqueIdentity' as determined by the ISlangFileSystemEx interface to determine file identities.
-
- auto directiveLoc = GetDirectiveLoc(context);
- auto issuedFromPathInfo = context->preprocessor->getSourceManager()->getPathInfo(directiveLoc, SourceLocType::Actual);
-
- // Must have uniqueIdentity for a #pragma once to work
- if (!issuedFromPathInfo.hasUniqueIdentity())
- {
- GetSink(context)->diagnose(subDirectiveToken, Diagnostics::pragmaOnceIgnored);
- return;
- }
-
- context->preprocessor->pragmaOnceUniqueIdentities.Add(issuedFromPathInfo.uniqueIdentity);
-}
-
-// Information about a specific `#pragma` directive
-struct PragmaDirective
-{
- // name of the directive
- char const* name;
-
- // Callback to handle the directive
- PragmaDirectiveCallback callback;
-};
-
-// A simple array of all the `#pragma` directives we know how to handle.
-static const PragmaDirective kPragmaDirectives[] =
-{
- { "once", &handlePragmaOnceDirective },
-
- { NULL, NULL },
-};
-
-static const PragmaDirective kUnknownPragmaDirective = {
- NULL, &handleUnknownPragmaDirective,
-};
-
-// Look up the `#pragma` directive with the given name.
-static PragmaDirective const* findPragmaDirective(String const& name)
-{
- char const* nameStr = name.getBuffer();
- for (int ii = 0; kPragmaDirectives[ii].name; ++ii)
- {
- if (strcmp(kPragmaDirectives[ii].name, nameStr) != 0)
- continue;
-
- return &kPragmaDirectives[ii];
- }
-
- return &kUnknownPragmaDirective;
-}
-
-// Handle a `#pragma` directive
-static void HandlePragmaDirective(PreprocessorDirectiveContext* context)
-{
- // Try to read the sub-directive name.
- Token subDirectiveToken = PeekRawToken(context);
-
- // The sub-directive had better be an identifier
- if (subDirectiveToken.type != TokenType::Identifier)
- {
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::expectedPragmaDirectiveName);
- SkipToEndOfLine(context);
- return;
- }
- AdvanceRawToken(context);
-
- // Look up the handler for the sub-directive.
- PragmaDirective const* subDirective = findPragmaDirective(subDirectiveToken.getName()->text);
-
- // Apply the sub-directive-specific callback
- (subDirective->callback)(context, subDirectiveToken);
-}
-
-// Handle an invalid directive
-static void HandleInvalidDirective(PreprocessorDirectiveContext* context)
-{
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::unknownPreprocessorDirective, GetDirectiveName(context));
- SkipToEndOfLine(context);
-}
-
-// Callback interface used by preprocessor directives
-typedef void (*PreprocessorDirectiveCallback)(PreprocessorDirectiveContext* context);
-
-enum PreprocessorDirectiveFlag : unsigned int
-{
- // Should this directive be handled even when skipping disbaled code?
- ProcessWhenSkipping = 1 << 0,
-
- /// Allow the handler for this directive to advance past the
- /// directive token itself, so that it can control lexer behavior
- /// more closely.
- DontConsumeDirectiveAutomatically = 1 << 1,
-};
-
-// Information about a specific directive
-struct PreprocessorDirective
-{
- // name of the directive
- char const* name;
-
- // Callback to handle the directive
- PreprocessorDirectiveCallback callback;
-
- unsigned int flags;
-};
-
-// A simple array of all the directives we know how to handle.
-// TODO(tfoley): considering making this into a real hash map,
-// and then make it easy-ish for users of the codebase to add
-// their own directives as desired.
-static const PreprocessorDirective kDirectives[] =
-{
- { "if", &HandleIfDirective, ProcessWhenSkipping },
- { "ifdef", &HandleIfDefDirective, ProcessWhenSkipping },
- { "ifndef", &HandleIfNDefDirective, ProcessWhenSkipping },
- { "else", &HandleElseDirective, ProcessWhenSkipping },
- { "elif", &HandleElifDirective, ProcessWhenSkipping },
- { "endif", &HandleEndIfDirective, ProcessWhenSkipping },
-
- { "include", &HandleIncludeDirective, DontConsumeDirectiveAutomatically },
- { "define", &HandleDefineDirective, 0 },
- { "undef", &HandleUndefDirective, 0 },
- { "warning", &HandleWarningDirective, DontConsumeDirectiveAutomatically },
- { "error", &HandleErrorDirective, DontConsumeDirectiveAutomatically },
- { "line", &HandleLineDirective, 0 },
- { "pragma", &HandlePragmaDirective, 0 },
-
- { nullptr, nullptr, 0 },
-};
-
-static const PreprocessorDirective kInvalidDirective = {
- nullptr, &HandleInvalidDirective, 0,
-};
-
-// Look up the directive with the given name.
-static PreprocessorDirective const* FindDirective(String const& name)
-{
- char const* nameStr = name.getBuffer();
- for (int ii = 0; kDirectives[ii].name; ++ii)
- {
- if (strcmp(kDirectives[ii].name, nameStr) != 0)
- continue;
-
- return &kDirectives[ii];
- }
-
- return &kInvalidDirective;
-}
-
-// Process a directive, where the preprocessor has already consumed the
-// `#` token that started the directive line.
-static void HandleDirective(PreprocessorDirectiveContext* context)
-{
- // Try to read the directive name.
- context->directiveToken = PeekRawToken(context);
-
- TokenType directiveTokenType = GetDirective(context).type;
-
- // An empty directive is allowed, and ignored.
- if (directiveTokenType == TokenType::EndOfDirective)
- {
- return;
- }
- // Otherwise the directive name had better be an identifier
- else if (directiveTokenType != TokenType::Identifier)
- {
- GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::expectedPreprocessorDirectiveName);
- SkipToEndOfLine(context);
- return;
- }
-
- // Look up the handler for the directive.
- PreprocessorDirective const* directive = FindDirective(GetDirectiveName(context));
-
- // If we are skipping disabled code, and the directive is not one
- // of the small number that need to run even in that case, skip it.
- if (IsSkipping(context) && !(directive->flags & PreprocessorDirectiveFlag::ProcessWhenSkipping))
- {
- SkipToEndOfLine(context);
- return;
- }
-
- if(!(directive->flags & PreprocessorDirectiveFlag::DontConsumeDirectiveAutomatically))
- {
- // Consume the directive name token.
- AdvanceRawToken(context);
- }
-
- // Apply the directive-specific callback
- (directive->callback)(context);
-
- // We expect the directive callback to consume the entire line, so if
- // it hasn't that is a parse error.
- expectEndOfDirective(context);
-}
-
-// Read one token using the full preprocessor, with all its behaviors.
-static Token ReadToken(Preprocessor* preprocessor)
-{
- for (;;)
- {
- // Depending on what the lookahead token is, we
- // might need to start expanding it.
- //
- // Note: doing this at the start of this loop
- // is important, in case a macro has an empty
- // expansion, and we end up looking at a different
- // token after applying the expansion.
- if(!IsSkipping(preprocessor))
- {
- MaybeBeginMacroExpansion(preprocessor);
- }
-
- // Look at the next raw token in the input.
- Token const& token = PeekRawToken(preprocessor);
- if (token.type == TokenType::EndOfFile)
- return token;
-
- // If we have a directive (`#` at start of line) then handle it
- if ((token.type == TokenType::Pound) && (token.flags & TokenFlag::AtStartOfLine))
- {
- // Skip the `#`
- AdvanceRawToken(preprocessor);
-
- // Create a context for parsing the directive
- PreprocessorDirectiveContext directiveContext;
- directiveContext.preprocessor = preprocessor;
- directiveContext.parseError = false;
- directiveContext.haveDoneEndOfDirectiveChecks = false;
-
- // Parse and handle the directive
- HandleDirective(&directiveContext);
- continue;
- }
-
- // otherwise, if we are currently in a skipping mode, then skip tokens
- if (IsSkipping(preprocessor))
- {
- AdvanceRawToken(preprocessor);
- continue;
- }
-
- // otherwise read a token, which may involve macro expansion
- return AdvanceToken(preprocessor);
- }
-}
-
-// intialize a preprocessor context, using the given sink for errros
-static void InitializePreprocessor(
- Preprocessor* preprocessor,
- DiagnosticSink* sink)
-{
- preprocessor->sink = sink;
- preprocessor->includeHandler = NULL;
- preprocessor->endOfFileToken.type = TokenType::EndOfFile;
- preprocessor->endOfFileToken.flags = TokenFlag::AtStartOfLine;
-}
-
-// clean up after an environment
-PreprocessorEnvironment::~PreprocessorEnvironment()
-{
- for (auto pair : this->macros)
- {
- DestroyMacro(NULL, pair.Value);
- }
-}
-
-// finalize a preprocessor and free any memory still in use
-static void FinalizePreprocessor(
- Preprocessor* preprocessor)
-{
- // Clear out any waiting input streams
- PreprocessorInputStream* input = preprocessor->inputStream;
- while (input)
- {
- PreprocessorInputStream* parent = input->parent;
- EndInputStream(preprocessor, input);
- input = parent;
- }
-
-#if 0
- // clean up any macros that were allocated
- for (auto pair : preprocessor->globalEnv.macros)
- {
- DestroyMacro(preprocessor, pair.Value);
- }
-#endif
-}
-
-// Add a simple macro definition from a string (e.g., for a
-// `-D` option passed on the command line
-static void DefineMacro(
- Preprocessor* preprocessor,
- String const& key,
- String const& value)
-{
- PathInfo pathInfo = PathInfo::makeCommandLine();
-
- PreprocessorMacro* macro = CreateMacro(preprocessor);
-
- auto sourceManager = preprocessor->getSourceManager();
-
- SourceFile* keyFile = sourceManager->createSourceFileWithString(pathInfo, key);
- SourceFile* valueFile = sourceManager->createSourceFileWithString(pathInfo, value);
-
- SourceView* keyView = sourceManager->createSourceView(keyFile, nullptr);
- SourceView* valueView = sourceManager->createSourceView(valueFile, nullptr);
-
- // Use existing `Lexer` to generate a token stream.
- Lexer lexer;
- lexer.initialize(valueView, GetSink(preprocessor), preprocessor->getNamePool(), sourceManager->getMemoryArena());
- macro->tokens = lexer.lexAllTokens();
-
- Name* keyName = preprocessor->getNamePool()->getName(key);
-
- macro->nameAndLoc.name = keyName;
- macro->nameAndLoc.loc = keyView->getRange().begin;
-
- PreprocessorMacro* oldMacro = NULL;
- if (preprocessor->globalEnv.macros.TryGetValue(keyName, oldMacro))
- {
- DestroyMacro(preprocessor, oldMacro);
- }
-
- preprocessor->globalEnv.macros[keyName] = macro;
-}
-
-// read the entire input into tokens
-static TokenList ReadAllTokens(
- Preprocessor* preprocessor)
-{
- TokenList tokens;
- for (;;)
- {
- Token token = ReadToken(preprocessor);
-
- tokens.mTokens.add(token);
-
- // Note: we include the EOF token in the list,
- // since that is expected by the `TokenList` type.
- if (token.type == TokenType::EndOfFile)
- break;
- }
- return tokens;
-}
-
-TokenList preprocessSource(
- SourceFile* file,
- DiagnosticSink* sink,
- IncludeHandler* includeHandler,
- Dictionary<String, String> defines,
- Linkage* linkage,
- Module* parentModule)
-{
- Preprocessor preprocessor;
- InitializePreprocessor(&preprocessor, sink);
- preprocessor.linkage = linkage;
- preprocessor.parentModule = parentModule;
-
- preprocessor.includeHandler = includeHandler;
- for (auto p : defines)
- {
- DefineMacro(&preprocessor, p.Key, p.Value);
- }
-
- SourceManager* sourceManager = linkage->getSourceManager();
-
- SourceView* sourceView = sourceManager->createSourceView(file, nullptr);
-
- // create an initial input stream based on the provided buffer
- preprocessor.inputStream = CreateInputStreamForSource(&preprocessor, sourceView);
-
- TokenList tokens = ReadAllTokens(&preprocessor);
-
- FinalizePreprocessor(&preprocessor);
-
- // debugging: build the pre-processed source back together
-#if 0
- StringBuilder sb;
- for (auto t : tokens)
- {
- if (t.flags & TokenFlag::AtStartOfLine)
- {
- sb << "\n";
- }
- else if (t.flags & TokenFlag::AfterWhitespace)
- {
- sb << " ";
- }
-
- sb << t.Content;
- }
-
- String s = sb.ProduceString();
-#endif
-
- return tokens;
-}
-
-}