summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--slang.h1
-rw-r--r--source/core/slang-string.cpp6
-rw-r--r--source/core/slang-string.h2
-rw-r--r--source/slang/compiler.cpp8
-rw-r--r--source/slang/compiler.h45
-rw-r--r--source/slang/diagnostics.cpp29
-rw-r--r--source/slang/diagnostics.h37
-rw-r--r--source/slang/emit.cpp125
-rw-r--r--source/slang/lexer.cpp81
-rw-r--r--source/slang/lexer.h30
-rw-r--r--source/slang/lower.cpp16
-rw-r--r--source/slang/options.cpp8
-rw-r--r--source/slang/parser.cpp22
-rw-r--r--source/slang/parser.h1
-rw-r--r--source/slang/preprocessor.cpp383
-rw-r--r--source/slang/preprocessor.h3
-rw-r--r--source/slang/slang.cpp55
-rw-r--r--source/slang/slang.vcxproj1
-rw-r--r--source/slang/slang.vcxproj.filters1
-rw-r--r--source/slang/source-loc.cpp332
-rw-r--r--source/slang/source-loc.h201
-rw-r--r--source/slang/syntax-base-defs.h2
-rw-r--r--source/slang/syntax-visitors.h2
-rw-r--r--source/slang/token.h17
-rw-r--r--tests/preprocessor/line.slang19
-rw-r--r--tests/preprocessor/line.slang.expected11
26 files changed, 1058 insertions, 380 deletions
diff --git a/slang.h b/slang.h
index 2485a5e57..1c34f3033 100644
--- a/slang.h
+++ b/slang.h
@@ -984,6 +984,7 @@ namespace slang
#include "source/slang/check.cpp"
#include "source/slang/compiler.cpp"
#include "source/slang/slang-stdlib.cpp"
+#include "source/slang/source-loc.cpp"
#include "source/slang/syntax.cpp"
#include "source/slang/token.cpp"
#include "source/slang/type-layout.cpp"
diff --git a/source/core/slang-string.cpp b/source/core/slang-string.cpp
index 459396f69..9bc9e3a54 100644
--- a/source/core/slang-string.cpp
+++ b/source/core/slang-string.cpp
@@ -51,6 +51,12 @@ namespace Slang
, endIndex(0)
{}
+ StringSlice::StringSlice(String const& str)
+ : representation(str.buffer)
+ , beginIndex(0)
+ , endIndex(str.Length())
+ {}
+
StringSlice::StringSlice(String const& str, UInt beginIndex, UInt endIndex)
: representation(str.buffer)
, beginIndex(beginIndex)
diff --git a/source/core/slang-string.h b/source/core/slang-string.h
index 552afe833..98fd51a07 100644
--- a/source/core/slang-string.h
+++ b/source/core/slang-string.h
@@ -155,6 +155,8 @@ namespace Slang
public:
StringSlice();
+ StringSlice(String const& str);
+
StringSlice(String const& str, UInt beginIndex, UInt endIndex);
UInt getLength() const
diff --git a/source/slang/compiler.cpp b/source/slang/compiler.cpp
index 60f111d83..55b79c00e 100644
--- a/source/slang/compiler.cpp
+++ b/source/slang/compiler.cpp
@@ -196,7 +196,7 @@ namespace Slang
HMODULE d3dCompiler = LoadLibraryA(libraryName);
if (!d3dCompiler)
{
- request->mSink.diagnose(CodePosition(), Diagnostics::failedToLoadDynamicLibrary, libraryName);
+ request->mSink.diagnose(SourceLoc(), Diagnostics::failedToLoadDynamicLibrary, libraryName);
}
return d3dCompiler;
}
@@ -373,7 +373,7 @@ namespace Slang
HMODULE glslCompiler = LoadLibraryA(libraryName);
if (!glslCompiler)
{
- request->mSink.diagnose(CodePosition(), Diagnostics::failedToLoadDynamicLibrary, libraryName);
+ request->mSink.diagnose(SourceLoc(), Diagnostics::failedToLoadDynamicLibrary, libraryName);
}
return glslCompiler;
}
@@ -611,7 +611,7 @@ namespace Slang
if (count != 1)
{
compileRequest->mSink.diagnose(
- CodePosition(),
+ SourceLoc(),
Diagnostics::cannotWriteOutputFile,
path);
}
@@ -630,7 +630,7 @@ namespace Slang
if (!file)
{
compileRequest->mSink.diagnose(
- CodePosition(),
+ SourceLoc(),
Diagnostics::cannotWriteOutputFile,
path);
return;
diff --git a/source/slang/compiler.h b/source/slang/compiler.h
index fd40a62f6..5a6cb5c19 100644
--- a/source/slang/compiler.h
+++ b/source/slang/compiler.h
@@ -122,17 +122,7 @@ namespace Slang
// GLSL, // pass through GLSL to `glslang` library
};
- // Represents a single source file (either an on-disk file, or a
- // "virtual" file passed in as a string)
- class SourceFile : public RefObject
- {
- public:
- // The file path for a real file, or the nominal path for a virtual file
- String path;
-
- // The actual contents of the file
- String content;
- };
+ class SourceFile;
// A single translation unit requested to be compiled.
//
@@ -228,6 +218,10 @@ namespace Slang
// Are we being driven by the command-line `slangc`, and should act accordingly?
bool isCommandLineCompile = false;
+ // Source manager to help track files loaded
+ SourceManager sourceManagerStorage;
+ SourceManager* sourceManager;
+
// Output stuff
DiagnosticSink mSink;
String mDiagnosticOutput;
@@ -250,12 +244,9 @@ namespace Slang
Dictionary<String, RefPtr<ModuleDecl>> mapNameToLoadedModules;
- CompileRequest(Session* session)
- : mSession(session)
- {}
+ CompileRequest(Session* session);
- ~CompileRequest()
- {}
+ ~CompileRequest();
void parseTranslationUnit(
TranslationUnitRequest* translationUnit);
@@ -267,6 +258,10 @@ namespace Slang
int addTranslationUnit(SourceLanguage language, String const& name);
+ void addTranslationUnitSourceFile(
+ int translationUnitIndex,
+ SourceFile* sourceFile);
+
void addTranslationUnitSourceString(
int translationUnitIndex,
String const& path,
@@ -285,7 +280,7 @@ namespace Slang
String const& name,
String const& path,
String const& source,
- CodePosition const& loc);
+ SourceLoc const& loc);
void handlePoundImport(
String const& path,
@@ -293,7 +288,18 @@ namespace Slang
RefPtr<ModuleDecl> findOrImportModule(
String const& name,
- CodePosition const& loc);
+ SourceLoc const& loc);
+
+ SourceManager* getSourceManager()
+ {
+ return sourceManager;
+ }
+
+ void setSourceManager(SourceManager* sm)
+ {
+ sourceManager = sm;
+ mSink.sourceManager = sm;
+ }
};
void generateOutput(
@@ -324,6 +330,9 @@ namespace Slang
List<RefPtr<ModuleDecl>> loadedModuleCode;
+ SourceManager builtinSourceManager;
+
+ SourceManager* getBuiltinSourceManager() { return &builtinSourceManager; }
//
diff --git a/source/slang/diagnostics.cpp b/source/slang/diagnostics.cpp
index e0b959a81..eb6a818d3 100644
--- a/source/slang/diagnostics.cpp
+++ b/source/slang/diagnostics.cpp
@@ -1,7 +1,8 @@
-// Diagnostics.cpp
-#include "Diagnostics.h"
+// diagnostics.cpp
+#include "diagnostics.h"
-#include "Syntax.h"
+#include "compiler.h"
+#include "syntax.h"
#include <assert.h>
@@ -67,17 +68,17 @@ void printDiagnosticArg(StringBuilder& sb, Token const& token)
sb << token.Content;
}
-CodePosition const& getDiagnosticPos(SyntaxNode const* syntax)
+SourceLoc const& getDiagnosticPos(SyntaxNode const* syntax)
{
return syntax->Position;
}
-CodePosition const& getDiagnosticPos(Token const& token)
+SourceLoc const& getDiagnosticPos(Token const& token)
{
return token.Position;
}
-CodePosition const& getDiagnosticPos(TypeExp const& typeExp)
+SourceLoc const& getDiagnosticPos(TypeExp const& typeExp)
{
return typeExp.exp->Position;
}
@@ -140,12 +141,18 @@ static void formatDiagnosticMessage(StringBuilder& sb, char const* format, int a
}
static void formatDiagnostic(
+ DiagnosticSink* sink,
StringBuilder& sb,
Diagnostic const& diagnostic)
{
- sb << diagnostic.Position.FileName;
+ auto sourceManager = sink->sourceManager;
+
+ auto expandedLoc = sourceManager->expandSourceLoc(diagnostic.Position);
+ auto humaneLoc = sourceManager->getHumaneLoc(expandedLoc);
+
+ sb << humaneLoc.getPath();
sb << "(";
- sb << diagnostic.Position.Line;
+ sb << humaneLoc.line;
sb << "): ";
sb << getSeverityName(diagnostic.severity);
sb << " ";
@@ -155,7 +162,7 @@ static void formatDiagnostic(
sb << "\n";
}
-void DiagnosticSink::diagnoseImpl(CodePosition const& pos, DiagnosticInfo const& info, int argCount, DiagnosticArg const* const* args)
+void DiagnosticSink::diagnoseImpl(SourceLoc const& pos, DiagnosticInfo const& info, int argCount, DiagnosticArg const* const* args)
{
StringBuilder sb;
formatDiagnosticMessage(sb, info.messageFormat, argCount, args);
@@ -176,7 +183,7 @@ void DiagnosticSink::diagnoseImpl(CodePosition const& pos, DiagnosticInfo const&
{
// If so, pass the error string along to them
StringBuilder messageBuilder;
- formatDiagnostic(messageBuilder, diagnostic);
+ formatDiagnostic(this, messageBuilder, diagnostic);
callback(messageBuilder.ProduceString().begin(), callbackUserData);
}
@@ -184,7 +191,7 @@ void DiagnosticSink::diagnoseImpl(CodePosition const& pos, DiagnosticInfo const&
{
// If the user doesn't have a callback, then just
// collect our diagnostic messages into a buffer
- formatDiagnostic(outputBuffer, diagnostic);
+ formatDiagnostic(this, outputBuffer, diagnostic);
}
if (diagnostic.severity >= Severity::Fatal)
diff --git a/source/slang/diagnostics.h b/source/slang/diagnostics.h
index 6ea677b38..e22320d3d 100644
--- a/source/slang/diagnostics.h
+++ b/source/slang/diagnostics.h
@@ -46,7 +46,7 @@ namespace Slang
{
public:
String Message;
- CodePosition Position;
+ SourceLoc Position;
int ErrorID;
Severity severity;
@@ -57,7 +57,7 @@ namespace Slang
Diagnostic(
const String & msg,
int id,
- const CodePosition & pos,
+ const SourceLoc & pos,
Severity severity)
: severity(severity)
{
@@ -93,16 +93,16 @@ namespace Slang
printDiagnosticArg(sb, ptr.Ptr());
}
- inline CodePosition const& getDiagnosticPos(CodePosition const& pos) { return pos; }
+ inline SourceLoc const& getDiagnosticPos(SourceLoc const& pos) { return pos; }
class SyntaxNode;
class ShaderClosure;
- CodePosition const& getDiagnosticPos(SyntaxNode const* syntax);
- CodePosition const& getDiagnosticPos(Token const& token);
- CodePosition const& getDiagnosticPos(TypeExp const& typeExp);
+ SourceLoc const& getDiagnosticPos(SyntaxNode const* syntax);
+ SourceLoc const& getDiagnosticPos(Token const& token);
+ SourceLoc const& getDiagnosticPos(TypeExp const& typeExp);
template<typename T>
- CodePosition getDiagnosticPos(RefPtr<T> const& ptr)
+ SourceLoc getDiagnosticPos(RefPtr<T> const& ptr)
{
return getDiagnosticPos(ptr.Ptr());
}
@@ -128,6 +128,9 @@ namespace Slang
class DiagnosticSink
{
public:
+ // The source manager to use when mapping source locations to file+line info
+ SourceManager* sourceManager;
+
StringBuilder outputBuffer;
// List<Diagnostic> diagnostics;
int errorCount = 0;
@@ -136,43 +139,43 @@ namespace Slang
void* callbackUserData = nullptr;
/*
- void Error(int id, const String & msg, const CodePosition & pos)
+ void Error(int id, const String & msg, const SourceLoc & pos)
{
diagnostics.Add(Diagnostic(msg, id, pos, Severity::Error));
errorCount++;
}
- void Warning(int id, const String & msg, const CodePosition & pos)
+ void Warning(int id, const String & msg, const SourceLoc & pos)
{
diagnostics.Add(Diagnostic(msg, id, pos, Severity::Warning));
}
*/
int GetErrorCount() { return errorCount; }
- void diagnoseDispatch(CodePosition const& pos, DiagnosticInfo const& info)
+ void diagnoseDispatch(SourceLoc const& pos, DiagnosticInfo const& info)
{
diagnoseImpl(pos, info, 0, NULL);
}
- void diagnoseDispatch(CodePosition const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0)
+ void diagnoseDispatch(SourceLoc const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0)
{
DiagnosticArg const* args[] = { &arg0 };
diagnoseImpl(pos, info, 1, args);
}
- void diagnoseDispatch(CodePosition const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0, DiagnosticArg const& arg1)
+ void diagnoseDispatch(SourceLoc const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0, DiagnosticArg const& arg1)
{
DiagnosticArg const* args[] = { &arg0, &arg1 };
diagnoseImpl(pos, info, 2, args);
}
- void diagnoseDispatch(CodePosition const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0, DiagnosticArg const& arg1, DiagnosticArg const& arg2)
+ void diagnoseDispatch(SourceLoc const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0, DiagnosticArg const& arg1, DiagnosticArg const& arg2)
{
DiagnosticArg const* args[] = { &arg0, &arg1, &arg2 };
diagnoseImpl(pos, info, 3, args);
}
- void diagnoseDispatch(CodePosition const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0, DiagnosticArg const& arg1, DiagnosticArg const& arg2, DiagnosticArg const& arg3)
+ void diagnoseDispatch(SourceLoc const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0, DiagnosticArg const& arg1, DiagnosticArg const& arg2, DiagnosticArg const& arg3)
{
DiagnosticArg const* args[] = { &arg0, &arg1, &arg2, &arg3 };
diagnoseImpl(pos, info, 4, args);
@@ -184,7 +187,7 @@ namespace Slang
diagnoseDispatch(getDiagnosticPos(pos), info, args...);
}
- void diagnoseImpl(CodePosition const& pos, DiagnosticInfo const& info, int argCount, DiagnosticArg const* const* args);
+ void diagnoseImpl(SourceLoc const& pos, DiagnosticInfo const& info, int argCount, DiagnosticArg const* const* args);
// Add a diagnostic with raw text
// (used when we get errors from a downstream compiler)
@@ -202,9 +205,9 @@ namespace Slang
#ifdef _DEBUG
#define SLANG_INTERNAL_ERROR(sink, pos) \
- (sink)->diagnose(Slang::CodePosition(__LINE__, 0, 0, __FILE__), Slang::Diagnostics::internalCompilerError)
+ (sink)->diagnose(Slang::SourceLoc(__LINE__, 0, 0, __FILE__), Slang::Diagnostics::internalCompilerError)
#define SLANG_UNIMPLEMENTED(sink, pos, what) \
- (sink)->diagnose(Slang::CodePosition(__LINE__, 0, 0, __FILE__), Slang::Diagnostics::unimplemented, what)
+ (sink)->diagnose(Slang::SourceLoc(__LINE__, 0, 0, __FILE__), Slang::Diagnostics::unimplemented, what)
#else
#define SLANG_INTERNAL_ERROR(sink, pos) \
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 72632260f..2a5f40787 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -58,8 +58,8 @@ struct SharedEmitContext
StringBuilder sb;
// Current source position for tracking purposes...
- CodePosition loc;
- CodePosition nextSourceLocation;
+ HumaneSourceLoc loc;
+ HumaneSourceLoc nextSourceLocation;
bool needToUpdateSourceLocation;
// For GLSL output, we can't emit traidtional `#line` directives
@@ -344,7 +344,7 @@ struct EDeclarator
// Used for `Flavor::Name`
String name;
- CodePosition loc;
+ SourceLoc loc;
// Used for `Flavor::Array`
IntVal* elementCount;
@@ -407,7 +407,7 @@ struct EmitVisitor
// Update our logical position
// TODO(tfoley): Need to make "corelib" not use `int` for pointer-sized things...
auto len = int(textEnd - textBegin);
- context->shared->loc.Col += len;
+ context->shared->loc.column += len;
}
void Emit(char const* textBegin, char const* textEnd)
@@ -431,8 +431,8 @@ struct EmitVisitor
// At the end of a line, we need to update our tracking
// information on code positions
emitTextSpan(spanBegin, spanEnd);
- context->shared->loc.Line++;
- context->shared->loc.Col = 1;
+ context->shared->loc.line++;
+ context->shared->loc.column = 1;
// Start a new span for emit purposes
spanBegin = spanEnd;
@@ -452,7 +452,7 @@ struct EmitVisitor
void emitName(
String const& inName,
- CodePosition const& loc)
+ SourceLoc const& loc)
{
String name = inName;
@@ -467,7 +467,7 @@ struct EmitVisitor
void emitName(String const& name)
{
- emitName(name, CodePosition());
+ emitName(name, SourceLoc());
}
void Emit(IntegerLiteralValue value)
@@ -504,12 +504,12 @@ struct EmitVisitor
// Emit a `#line` directive to the output.
// Doesn't udpate state of source-location tracking.
void emitLineDirective(
- CodePosition const& sourceLocation)
+ HumaneSourceLoc const& sourceLocation)
{
emitRawText("\n#line ");
char buffer[16];
- sprintf(buffer, "%d", sourceLocation.Line);
+ sprintf(buffer, "%d", sourceLocation.line);
emitRawText(buffer);
emitRawText(" ");
@@ -547,7 +547,7 @@ struct EmitVisitor
if(shouldUseGLSLStyleLineDirective)
{
- auto path = sourceLocation.FileName;
+ auto path = sourceLocation.getPath();
// GLSL doesn't support the traditional form of a `#line` directive without
// an extension. Rather than depend on that extension we will output
@@ -578,7 +578,7 @@ struct EmitVisitor
// in a module that tracks source files.
emitRawText("\"");
- for(auto c : sourceLocation.FileName)
+ for(auto c : sourceLocation.getPath())
{
char charBuffer[] = { c, 0 };
switch(c)
@@ -607,17 +607,18 @@ struct EmitVisitor
// ensure that source location tracking information
// is correct based on the directive we just output.
void emitLineDirectiveAndUpdateSourceLocation(
- CodePosition const& sourceLocation)
+ HumaneSourceLoc const& sourceLocation)
{
emitLineDirective(sourceLocation);
-
- context->shared->loc.FileName = sourceLocation.FileName;
- context->shared->loc.Line = sourceLocation.Line;
- context->shared->loc.Col = 1;
+
+ HumaneSourceLoc newLoc = sourceLocation;
+ newLoc.column = 1;
+
+ context->shared->loc = newLoc;
}
void emitLineDirectiveIfNeeded(
- CodePosition const& sourceLocation)
+ HumaneSourceLoc const& sourceLocation)
{
// Don't do any of this work if the user has requested that we
// not emit line directives.
@@ -626,31 +627,31 @@ struct EmitVisitor
return;
// Ignore invalid source locations
- if(sourceLocation.Line <= 0)
+ if(sourceLocation.line <= 0)
return;
// If we are currently emitting code at a source location with
// a differnet file or line, *or* if the source location is
// somehow later on the line than what we want to emit,
// then we need to emit a new `#line` directive.
- if(sourceLocation.FileName != context->shared->loc.FileName
- || sourceLocation.Line != context->shared->loc.Line
- || sourceLocation.Col < context->shared->loc.Col)
+ if(sourceLocation.path != context->shared->loc.path
+ || sourceLocation.line != context->shared->loc.line
+ || sourceLocation.column < context->shared->loc.column)
{
// Special case: if we are in the same file, and within a small number
// of lines of the target location, then go ahead and output newlines
// to get us caught up.
enum { kSmallLineCount = 3 };
- auto lineDiff = sourceLocation.Line - context->shared->loc.Line;
- if(sourceLocation.FileName == context->shared->loc.FileName
- && sourceLocation.Line > context->shared->loc.Line
+ auto lineDiff = sourceLocation.line - context->shared->loc.line;
+ if(sourceLocation.path == context->shared->loc.path
+ && sourceLocation.line > context->shared->loc.line
&& lineDiff <= kSmallLineCount)
{
for(int ii = 0; ii < lineDiff; ++ii )
{
Emit("\n");
}
- SLANG_RELEASE_ASSERT(sourceLocation.Line == context->shared->loc.Line);
+ SLANG_RELEASE_ASSERT(sourceLocation.line == context->shared->loc.line);
}
else
{
@@ -666,28 +667,39 @@ struct EmitVisitor
// came in as spaces or tabs, so there is necessarily going to be
// coupling between how the downstream compiler counts columns,
// and how we do.
- if(sourceLocation.Col > context->shared->loc.Col)
+ if(sourceLocation.column > context->shared->loc.column)
{
- int delta = sourceLocation.Col - context->shared->loc.Col;
+ int delta = sourceLocation.column - context->shared->loc.column;
for( int ii = 0; ii < delta; ++ii )
{
emitRawText(" ");
}
- context->shared->loc.Col = sourceLocation.Col;
+ context->shared->loc.column = sourceLocation.column;
}
}
void advanceToSourceLocation(
- CodePosition const& sourceLocation)
+ HumaneSourceLoc const& sourceLocation)
{
// Skip invalid locations
- if(sourceLocation.Line <= 0)
+ if(sourceLocation.line <= 0)
return;
context->shared->needToUpdateSourceLocation = true;
context->shared->nextSourceLocation = sourceLocation;
}
+ SourceManager* getSourceManager()
+ {
+ return context->shared->entryPoint->compileRequest->getSourceManager();
+ }
+
+ void advanceToSourceLocation(
+ SourceLoc const& sourceLocation)
+ {
+ advanceToSourceLocation(getSourceManager()->getHumaneLoc(sourceLocation));
+ }
+
void flushSourceLocationChange()
{
if(!context->shared->needToUpdateSourceLocation)
@@ -707,9 +719,8 @@ struct EmitVisitor
if (mode == LineDirectiveMode::None)
return;
-
if ((mode == LineDirectiveMode::None)
- || token.Position.FileName.Length() == 0)
+ || !token.Position.isValid())
{
// If we don't have the original position info, or we are in the
// mode where the user didn't want line directives, we need to play
@@ -755,7 +766,7 @@ struct EmitVisitor
}
else
{
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unknown type of integer constant value");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unknown type of integer constant value");
}
}
@@ -787,7 +798,7 @@ struct EmitVisitor
break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unknown declarator flavor");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unknown declarator flavor");
break;
}
}
@@ -808,7 +819,7 @@ struct EmitVisitor
case BaseType::Bool: Emit("b"); break;
case BaseType::Double: Emit("d"); break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled GLSL type prefix");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled GLSL type prefix");
break;
}
}
@@ -822,7 +833,7 @@ struct EmitVisitor
}
else
{
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled GLSL type prefix");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled GLSL type prefix");
}
}
@@ -851,7 +862,7 @@ struct EmitVisitor
break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled resource access mode");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled resource access mode");
break;
}
@@ -863,7 +874,7 @@ struct EmitVisitor
case TextureType::ShapeCube: Emit("TextureCube"); break;
case TextureType::ShapeBuffer: Emit("Buffer"); break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled resource shape");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled resource shape");
break;
}
@@ -895,7 +906,7 @@ struct EmitVisitor
case TextureType::ShapeCube: Emit("Cube"); break;
case TextureType::ShapeBuffer: Emit("Buffer"); break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled resource shape");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled resource shape");
break;
}
@@ -941,7 +952,7 @@ struct EmitVisitor
break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled code generation target");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled code generation target");
break;
}
}
@@ -956,7 +967,7 @@ struct EmitVisitor
break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "this target should see combined texture-sampler types");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "this target should see combined texture-sampler types");
break;
}
}
@@ -975,7 +986,7 @@ struct EmitVisitor
break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "this target should see GLSL image types");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "this target should see GLSL image types");
break;
}
}
@@ -1026,7 +1037,7 @@ struct EmitVisitor
case BaseType::Bool: Emit("bool"); break;
case BaseType::Double: Emit("double"); break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled scalar type");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled scalar type");
break;
}
@@ -1058,7 +1069,7 @@ struct EmitVisitor
break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled code generation target");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled code generation target");
break;
}
@@ -1096,7 +1107,7 @@ struct EmitVisitor
break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled code generation target");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled code generation target");
break;
}
@@ -1136,7 +1147,7 @@ struct EmitVisitor
case SamplerStateType::Flavor::SamplerState: Emit("SamplerState"); break;
case SamplerStateType::Flavor::SamplerComparisonState: Emit("SamplerComparisonState"); break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled sampler state flavor");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled sampler state flavor");
break;
}
break;
@@ -1147,7 +1158,7 @@ struct EmitVisitor
case SamplerStateType::Flavor::SamplerState: Emit("sampler"); break;
case SamplerStateType::Flavor::SamplerComparisonState: Emit("samplerShadow"); break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled sampler state flavor");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled sampler state flavor");
break;
}
break;
@@ -1187,9 +1198,9 @@ struct EmitVisitor
void EmitType(
RefPtr<Type> type,
- CodePosition const& typeLoc,
+ SourceLoc const& typeLoc,
String const& name,
- CodePosition const& nameLoc)
+ SourceLoc const& nameLoc)
{
advanceToSourceLocation(typeLoc);
@@ -1203,7 +1214,7 @@ struct EmitVisitor
void EmitType(RefPtr<Type> type, Token const& nameToken)
{
- EmitType(type, CodePosition(), nameToken.Content, nameToken.Position);
+ EmitType(type, SourceLoc(), nameToken.Content, nameToken.Position);
}
void EmitType(RefPtr<Type> type)
@@ -1232,13 +1243,13 @@ struct EmitVisitor
}
}
- void EmitType(TypeExp const& typeExp, String const& name, CodePosition const& nameLoc)
+ void EmitType(TypeExp const& typeExp, String const& name, SourceLoc const& nameLoc)
{
if (!typeExp.type || typeExp.type->As<ErrorType>())
{
if (!typeExp.exp)
{
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unresolved type expression should have expression part");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unresolved type expression should have expression part");
}
EDeclarator nameDeclarator;
@@ -1251,7 +1262,7 @@ struct EmitVisitor
else
{
EmitType(typeExp.type,
- typeExp.exp ? typeExp.exp->Position : CodePosition(),
+ typeExp.exp ? typeExp.exp->Position : SourceLoc(),
name, nameLoc);
}
}
@@ -1263,7 +1274,7 @@ struct EmitVisitor
void EmitType(TypeExp const& typeExp, String const& name)
{
- EmitType(typeExp, name, CodePosition());
+ EmitType(typeExp, name, SourceLoc());
}
void emitTypeExp(TypeExp const& typeExp)
@@ -1463,7 +1474,7 @@ struct EmitVisitor
switch(context->shared->target)
{
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled code generation target");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled code generation target");
return false;
case CodeGenTarget::GLSL: return targetName == "glsl";
@@ -3203,7 +3214,7 @@ struct EmitVisitor
Emit("s");
break;
default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), CodePosition(), "unhandled HLSL register type");
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled HLSL register type");
break;
}
Emit(info.index);
diff --git a/source/slang/lexer.cpp b/source/slang/lexer.cpp
index 84b34c9a9..fe01878d1 100644
--- a/source/slang/lexer.cpp
+++ b/source/slang/lexer.cpp
@@ -1,4 +1,12 @@
-#include "Lexer.h"
+// lexer.cpp
+#include "lexer.h"
+
+// This file implements the lexer/scanner, which is responsible for taking a raw stream of
+// input bytes and turning it into semantically useful tokens.
+//
+
+#include "compiler.h"
+#include "source-loc.h"
#include <assert.h>
@@ -6,7 +14,7 @@ namespace Slang
{
static Token GetEndOfFileToken()
{
- return Token(TokenType::EndOfFile, "", 0, 0, 0, "");
+ return Token(TokenType::EndOfFile, "", SourceLoc());
}
Token* TokenList::begin() const
@@ -52,10 +60,10 @@ namespace Slang
return mCursor->type;
}
- CodePosition TokenReader::PeekLoc() const
+ SourceLoc TokenReader::PeekLoc() const
{
if (!mCursor)
- return CodePosition();
+ return SourceLoc();
SLANG_ASSERT(mCursor);
return mCursor->Position;
}
@@ -75,18 +83,22 @@ namespace Slang
// Lexer
- Lexer::Lexer(
- String const& path,
- String const& content,
- DiagnosticSink* sink)
- : path(path)
- , content(content)
- , sink(sink)
+ void Lexer::initialize(
+ SourceFile* inSourceFile,
+ DiagnosticSink* inSink)
{
+ sourceFile = inSourceFile;
+ sink = inSink;
+
+ auto content = inSourceFile->content;
+
+ begin = content.begin();
cursor = content.begin();
end = content.end();
- loc = CodePosition(1, 1, 0, path);
+ spellingStartLoc = inSourceFile->sourceRange.begin;
+ presumedStartLoc = spellingStartLoc;
+
tokenFlags = TokenFlag::AtStartOfLine | TokenFlag::AfterWhitespace;
lexerFlags = 0;
}
@@ -142,9 +154,6 @@ namespace Slang
{
advanceRaw(lexer);
}
-
- lexer->loc.Line++;
- lexer->loc.Col = 1;
}
// Look ahead one code point, dealing with complications like
@@ -224,12 +233,7 @@ namespace Slang
// TODO: Need to handle non-ASCII code points.
- // Default case is to advance by one location
- // and return the raw byte we saw.
-
- lexer->loc.Col++;
- lexer->loc.Pos++;
-
+ // Default case is to return the raw byte we saw.
return c;
}
}
@@ -323,6 +327,27 @@ namespace Slang
}
}
+ static SourceLoc getSourceLoc(Lexer* lexer)
+ {
+ return lexer->presumedStartLoc + (lexer->cursor - lexer->begin);
+ }
+
+ // Begin overriding the reported locations of tokens,
+ // based on a `#line` directives
+ void Lexer::startOverridingSourceLocations(
+ SourceLoc loc)
+ {
+ if(loc.isValid())
+ {
+ presumedStartLoc = loc;
+ }
+ }
+
+ void Lexer::stopOverridingSourceLocations()
+ {
+ presumedStartLoc = spellingStartLoc;
+ }
+
static void lexDigits(Lexer* lexer, int base)
{
for(;;)
@@ -355,7 +380,7 @@ namespace Slang
if(digitVal >= base)
{
char buffer[] = { (char) c, 0 };
- lexer->sink->diagnose(lexer->loc, Diagnostics::invalidDigitForBase, buffer, base);
+ lexer->sink->diagnose(getSourceLoc(lexer), Diagnostics::invalidDigitForBase, buffer, base);
}
advance(lexer);
@@ -695,11 +720,11 @@ namespace Slang
switch(c)
{
case kEOF:
- lexer->sink->diagnose(lexer->loc, Diagnostics::endOfFileInLiteral);
+ lexer->sink->diagnose(getSourceLoc(lexer), Diagnostics::endOfFileInLiteral);
return;
case '\n': case '\r':
- lexer->sink->diagnose(lexer->loc, Diagnostics::newlineInLiteral);
+ lexer->sink->diagnose(getSourceLoc(lexer), Diagnostics::newlineInLiteral);
return;
case '\\':
@@ -952,7 +977,7 @@ namespace Slang
case '0':
{
- auto loc = lexer->loc;
+ auto loc = getSourceLoc(lexer);
advance(lexer);
switch(peek(lexer))
{
@@ -1170,7 +1195,7 @@ namespace Slang
// If none of the above cases matched, then we have an
// unexpected/invalid character.
- auto loc = lexer->loc;
+ auto loc = getSourceLoc(lexer);
auto sink = lexer->sink;
int c = advance(lexer);
if(c >= 0x20 && c <= 0x7E)
@@ -1194,7 +1219,7 @@ namespace Slang
for(;;)
{
Token token;
- token.Position = loc;
+ token.Position = getSourceLoc(this);
char const* textBegin = cursor;
@@ -1314,7 +1339,7 @@ namespace Slang
TokenList Lexer::Parse(const String & fileName, const String & str, DiagnosticSink * sink)
{
TokenList tokenList;
- tokenList.mTokens = TokenizeText(fileName, str, [&](TokenizeErrorType errType, CodePosition pos)
+ tokenList.mTokens = TokenizeText(fileName, str, [&](TokenizeErrorType errType, SourceLoc pos)
{
auto curChar = str[pos.Pos];
switch (errType)
diff --git a/source/slang/lexer.h b/source/slang/lexer.h
index 71b2e85b1..9bb4d34aa 100644
--- a/source/slang/lexer.h
+++ b/source/slang/lexer.h
@@ -47,7 +47,7 @@ namespace Slang
bool IsAtEnd() const { return mCursor == mEnd; }
Token PeekToken() const;
TokenType PeekTokenType() const;
- CodePosition PeekLoc() const;
+ SourceLoc PeekLoc() const;
Token AdvanceToken();
@@ -66,9 +66,8 @@ namespace Slang
struct Lexer
{
- Lexer(
- String const& path,
- String const& content,
+ void initialize(
+ SourceFile* sourceFile,
DiagnosticSink* sink);
~Lexer();
@@ -77,13 +76,30 @@ namespace Slang
TokenList lexAllTokens();
- String path;
- String content;
+ // Begin overriding the reported locations of tokens,
+ // based on a `#line` directives
+ void startOverridingSourceLocations(SourceLoc loc);
+
+ // Stop overriding source locations, and go back
+ // to reporting source locations in the original file
+ void stopOverridingSourceLocations();
+
+ SourceFile* sourceFile;
DiagnosticSink* sink;
char const* cursor;
+
+ char const* begin;
char const* end;
- CodePosition loc;
+
+ // The starting source location for the code as written,
+ // which cannot be overridden.
+ SourceLoc spellingStartLoc;
+
+ // The nominal starting location for the file, taking
+ // any active `#line` directive into account.
+ SourceLoc presumedStartLoc;
+
TokenFlags tokenFlags;
LexerFlags lexerFlags;
};
diff --git a/source/slang/lower.cpp b/source/slang/lower.cpp
index 611180415..e253abca6 100644
--- a/source/slang/lower.cpp
+++ b/source/slang/lower.cpp
@@ -318,7 +318,7 @@ class PseudoVarDecl : public RefObject
{
public:
Token Name;
- CodePosition Position;
+ SourceLoc Position;
TypeExp type;
};
@@ -339,7 +339,7 @@ public:
class PseudoExpr : public RefObject
{
public:
- CodePosition Position;
+ SourceLoc Position;
QualType type;
};
@@ -384,7 +384,7 @@ public:
List<Element> elements;
};
-static CodePosition getPosition(LoweredExpr const& expr)
+static SourceLoc getPosition(LoweredExpr const& expr)
{
switch (expr.getFlavor())
{
@@ -393,7 +393,7 @@ static CodePosition getPosition(LoweredExpr const& expr)
case LoweredExpr::Flavor::VaryingTuple: return expr.getVaryingTupleExpr()->Position;
default:
SLANG_UNREACHABLE("all cases handled");
- return CodePosition();
+ return SourceLoc();
}
}
@@ -804,7 +804,7 @@ struct LoweringVisitor
}
RefPtr<Expr> createSimpleVarRef(
- CodePosition const& loc,
+ SourceLoc const& loc,
VarDeclBase* decl)
{
RefPtr<VarExpr> result = new VarExpr();
@@ -816,7 +816,7 @@ struct LoweringVisitor
}
LoweredExpr createVarRef(
- CodePosition const& loc,
+ SourceLoc const& loc,
LoweredDecl const& decl)
{
switch (decl.getFlavor())
@@ -838,7 +838,7 @@ struct LoweringVisitor
LoweredExpr createTupleRef(
- CodePosition const& loc,
+ SourceLoc const& loc,
TupleVarDecl* decl)
{
RefPtr<TupleExpr> result = new TupleExpr();
@@ -868,7 +868,7 @@ struct LoweringVisitor
}
LoweredExpr createVaryingTupleRef(
- CodePosition const& /*loc*/,
+ SourceLoc const& /*loc*/,
VaryingTupleVarDecl* decl)
{
return decl->expr;
diff --git a/source/slang/options.cpp b/source/slang/options.cpp
index 49f33f58d..a360695f2 100644
--- a/source/slang/options.cpp
+++ b/source/slang/options.cpp
@@ -596,7 +596,7 @@ struct OptionsParser
else if (rawOutputPaths.Count() > rawEntryPoints.Count())
{
requestImpl->mSink.diagnose(
- CodePosition(),
+ SourceLoc(),
Diagnostics::tooManyOutputPathsSpecified,
rawOutputPaths.Count(),
rawEntryPoints.Count());
@@ -611,7 +611,7 @@ struct OptionsParser
if (entryPoint.outputPathIndex < 0)
{
requestImpl->mSink.diagnose(
- CodePosition(),
+ SourceLoc(),
Diagnostics::noOutputPathSpecifiedForEntryPoint,
entryPoint.name);
@@ -639,7 +639,7 @@ struct OptionsParser
// This file didn't imply a target, and that
// needs to be an error:
requestImpl->mSink.diagnose(
- CodePosition(),
+ SourceLoc(),
Diagnostics::cannotDeduceOutputFormatFromPath,
rawOutputPath.path);
@@ -669,7 +669,7 @@ struct OptionsParser
// This file didn't imply a target, and that
// needs to be an error:
requestImpl->mSink.diagnose(
- CodePosition(),
+ SourceLoc(),
Diagnostics::outputPathsImplyDifferentFormats,
rawOutputPaths[0].path,
rawOutputPath.path);
diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp
index 41a4411d2..cc6a0dd6f 100644
--- a/source/slang/parser.cpp
+++ b/source/slang/parser.cpp
@@ -41,7 +41,6 @@ namespace Slang
TokenReader tokenReader;
DiagnosticSink * sink;
- String fileName;
int genericDepth = 0;
// Have we seen any `import` declarations? If so, we need
@@ -73,11 +72,9 @@ namespace Slang
Parser(
TokenSpan const& _tokens,
DiagnosticSink * sink,
- String _fileName,
RefPtr<Scope> const& outerScope)
: tokenReader(_tokens)
, sink(sink)
- , fileName(_fileName)
, outerScope(outerScope)
{}
@@ -614,7 +611,7 @@ namespace Slang
RefPtr<Modifier>* modifierLink = &modifiers.first;
for (;;)
{
- CodePosition loc = parser->tokenReader.PeekLoc();
+ SourceLoc loc = parser->tokenReader.PeekLoc();
if (0) {}
@@ -1003,7 +1000,7 @@ namespace Slang
struct PointerDeclarator : Declarator
{
// location of the `*` token
- CodePosition starLoc;
+ SourceLoc starLoc;
RefPtr<Declarator> inner;
};
@@ -1014,7 +1011,7 @@ namespace Slang
RefPtr<Declarator> inner;
// location of the `[` token
- CodePosition openBracketLoc;
+ SourceLoc openBracketLoc;
// The expression that yields the element count, or NULL
RefPtr<Expr> elementCountExpr;
@@ -1377,7 +1374,7 @@ namespace Slang
// Either a single declaration, or a group of them
struct DeclGroupBuilder
{
- CodePosition startPosition;
+ SourceLoc startPosition;
RefPtr<Decl> decl;
RefPtr<DeclGroup> group;
@@ -1546,7 +1543,7 @@ namespace Slang
Parser* parser,
ContainerDecl* containerDecl)
{
- CodePosition startPosition = parser->tokenReader.PeekLoc();
+ SourceLoc startPosition = parser->tokenReader.PeekLoc();
auto typeSpec = parseTypeSpec(parser);
@@ -1780,7 +1777,7 @@ namespace Slang
// We first look at the declaration keywrod to determine
// the type of buffer to declare:
String bufferWrapperTypeName;
- CodePosition bufferWrapperTypeNamePos = parser->tokenReader.PeekLoc();
+ SourceLoc bufferWrapperTypeNamePos = parser->tokenReader.PeekLoc();
if (AdvanceIf(parser, "cbuffer"))
{
bufferWrapperTypeName = "ConstantBuffer";
@@ -1914,7 +1911,7 @@ namespace Slang
// will see through it to the members inside.
- CodePosition pos = parser->tokenReader.PeekLoc();
+ SourceLoc pos = parser->tokenReader.PeekLoc();
// The initial name before the `{` is only supposed
// to be made visible to reflection
@@ -2446,7 +2443,7 @@ namespace Slang
}
PushScope(program);
- program->Position = CodePosition(0, 0, 0, fileName);
+ program->Position = tokenReader.PeekLoc();
ParseDeclBody(this, program, TokenType::EndOfFile);
PopScope();
@@ -3853,10 +3850,9 @@ namespace Slang
TranslationUnitRequest* translationUnit,
TokenSpan const& tokens,
DiagnosticSink* sink,
- String const& fileName,
RefPtr<Scope> const& outerScope)
{
- Parser parser(tokens, sink, fileName, outerScope);
+ Parser parser(tokens, sink, outerScope);
parser.translationUnit = translationUnit;
diff --git a/source/slang/parser.h b/source/slang/parser.h
index 8dfef4c12..41a82602a 100644
--- a/source/slang/parser.h
+++ b/source/slang/parser.h
@@ -12,7 +12,6 @@ namespace Slang
TranslationUnitRequest* translationUnit,
TokenSpan const& tokens,
DiagnosticSink* sink,
- String const& fileName,
RefPtr<Scope> const& outerScope);
;
}
diff --git a/source/slang/preprocessor.cpp b/source/slang/preprocessor.cpp
index 53f0c4774..9c9a340ae 100644
--- a/source/slang/preprocessor.cpp
+++ b/source/slang/preprocessor.cpp
@@ -60,45 +60,61 @@ struct PreprocessorEnvironment
// 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;
- // The deepest preprocessor conditional active for this stream.
- PreprocessorConditional* conditional;
-
// Environment to use when looking up macros
PreprocessorEnvironment* environment;
- // Reader for pre-tokenized input
- TokenReader tokenReader;
+ // Destructor is virtual so that we can clean up
+ // after concrete subtypes.
+ virtual ~PreprocessorInputStream() = default;
+};
- // If we are clobbering source locations with `#line`, then
- // the state is tracked here:
+// 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;
- // Are we overriding source locations?
- bool isOverridingSourceLoc;
+ // The deepest preprocessor conditional active for this stream.
+ PreprocessorConditional* conditional;
- // What is the file name we are overriding to?
- String overrideFileName;
+ // The lexer state that will provide input
+ Lexer lexer;
- // What is the relative offset to apply to any line numbers?
- int overrideLineOffset;
+ // One token of lookahead
+ Token token;
+};
- // Destructor is virtual so that we can clean up
- // after concrete subtypes.
- virtual ~PreprocessorInputStream() = default;
+// 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;
};
-struct SourceTextInputStream : PreprocessorInputStream
+// A pre-tokenized input stream that will only be used once, and which
+// therefore owns the memory for its tokens.
+struct SimpleTokenInputStream : PretokenizedInputStream
{
- // The pre-tokenized input
- TokenList lexedTokens;
+ // A list of raw tokens that will provide input
+ TokenList lexedTokens;
};
-struct MacroExpansion : PreprocessorInputStream
+struct MacroExpansion : PretokenizedInputStream
{
// The macro we will expand
PreprocessorMacro* macro;
@@ -204,39 +220,55 @@ static void DestroyMacro(Preprocessor* preprocessor, PreprocessorMacro* macro);
//
// Create a fresh input stream
-static void InitializeInputStream(Preprocessor* preprocessor, PreprocessorInputStream* inputStream)
+static void initializeInputStream(Preprocessor* preprocessor, PreprocessorInputStream* inputStream)
{
inputStream->parent = NULL;
- inputStream->conditional = 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)
+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, String const& source, String const& fileName)
+static PreprocessorInputStream* CreateInputStreamForSource(
+ Preprocessor* preprocessor,
+ SourceFile* sourceFile)
{
- SourceTextInputStream* inputStream = new SourceTextInputStream();
- InitializeInputStream(preprocessor, inputStream);
+ PrimaryInputStream* inputStream = new PrimaryInputStream();
+ initializePrimaryInputStream(preprocessor, inputStream);
- // Use existing `Lexer` to generate a token stream.
- Lexer lexer(fileName, source, GetSink(preprocessor));
- inputStream->lexedTokens = lexer.lexAllTokens();
- inputStream->tokenReader = TokenReader(inputStream->lexedTokens);
+ // initialize the embedded lexer so that it can generate a token stream
+ inputStream->lexer.initialize(sourceFile, GetSink(preprocessor));
+ 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;
}
@@ -244,52 +276,69 @@ static void PushInputStream(Preprocessor* preprocessor, PreprocessorInputStream*
// Performs some validation and then destroys the input stream if required.
static void EndInputStream(Preprocessor* preprocessor, PreprocessorInputStream* inputStream)
{
- // If there are any conditionals that weren't completed, then it is an error
- if (inputStream->conditional)
+ if(auto primaryStream = asPrimaryInputStream(inputStream))
{
- PreprocessorConditional* conditional = inputStream->conditional;
+ // 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.Position, Diagnostics::endOfFileInPreprocessorConditional);
+ GetSink(preprocessor)->diagnose(conditional->ifToken.Position, Diagnostics::endOfFileInPreprocessorConditional);
- while (conditional)
- {
- PreprocessorConditional* parent = conditional->parent;
- DestroyConditional(conditional);
- conditional = parent;
+ while (conditional)
+ {
+ PreprocessorConditional* parent = conditional->parent;
+ DestroyConditional(conditional);
+ conditional = parent;
+ }
}
}
- DestroyInputStream(preprocessor, inputStream);
-}
-
-// Potentially clobber source location information based on `#line`
-static Token PossiblyOverrideSourceLoc(PreprocessorInputStream* inputStream, Token const& token)
-{
- Token result = token;
- if( inputStream->isOverridingSourceLoc )
- {
- result.Position.FileName = inputStream->overrideFileName;
- result.Position.Line += inputStream->overrideLineOffset;
- }
- return result;
+ destroyInputStream(preprocessor, inputStream);
}
// Consume one token from an input stream
static Token AdvanceRawToken(PreprocessorInputStream* inputStream)
{
- return PossiblyOverrideSourceLoc(inputStream, inputStream->tokenReader.AdvanceToken());
+ if( auto primaryStream = asPrimaryInputStream(inputStream) )
+ {
+ auto result = primaryStream->token;
+ primaryStream->token = primaryStream->lexer.lexToken();
+ return result;
+ }
+ else
+ {
+ PretokenizedInputStream* pretokenized = (PretokenizedInputStream*) inputStream;
+ return pretokenized->tokenReader.AdvanceToken();
+ }
}
// Peek one token from an input stream
static Token PeekRawToken(PreprocessorInputStream* inputStream)
{
- return PossiblyOverrideSourceLoc(inputStream, inputStream->tokenReader.PeekToken());
+ 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)
{
- return inputStream->tokenReader.PeekTokenType();
+ if( auto primaryStream = asPrimaryInputStream(inputStream) )
+ {
+ return primaryStream->token.type;
+ }
+ else
+ {
+ PretokenizedInputStream* pretokenized = (PretokenizedInputStream*) inputStream;
+ return pretokenized->tokenReader.PeekTokenType();
+ }
}
@@ -355,55 +404,8 @@ static Token PeekRawToken(Preprocessor* preprocessor)
}
}
-// Without advancing preprocessor state, look *two* raw tokens ahead
-// (This is only needed in order to determine when we are possibly
-// expanding a function-style macro)
-TokenType PeekSecondRawTokenType(Preprocessor* preprocessor)
-{
- // We need to find the strema that `advanceRawToken` would read from.
- PreprocessorInputStream* inputStream = preprocessor->inputStream;
- int count = 1;
- for (;;)
- {
- if (!inputStream)
- {
- // No more input streams left to read
- return TokenType::EndOfFile;
- }
-
- // 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)
-
- TokenReader reader = inputStream->tokenReader;
- if (reader.PeekTokenType() == TokenType::EndOfFile)
- {
- inputStream = inputStream->parent;
- continue;
- }
-
- if (count)
- {
- count--;
-
- // Note: we are advancing our temporary
- // copy of the token reader
- reader.AdvanceToken();
- if (reader.PeekTokenType() == TokenType::EndOfFile)
- {
- inputStream = inputStream->parent;
- continue;
- }
- }
-
- // Everything worked, so peek a token from the top-most stream
- return reader.PeekTokenType();
- }
-}
-
-
// Get the location of the current (raw) token
-static CodePosition PeekLoc(Preprocessor* preprocessor)
+static SourceLoc PeekLoc(Preprocessor* preprocessor)
{
return PeekRawToken(preprocessor).Position;
}
@@ -471,7 +473,7 @@ static PreprocessorEnvironment* GetCurrentEnvironment(Preprocessor* preprocessor
// If the current input stream is at its end, then
// fall back to its parent stream.
- if (inputStream->tokenReader.PeekTokenType() == TokenType::EndOfFile)
+ if (PeekRawTokenType(inputStream) == TokenType::EndOfFile)
{
inputStream = inputStream->parent;
continue;
@@ -528,7 +530,11 @@ static void InitializeMacroExpansion(
MacroExpansion* expansion,
PreprocessorMacro* macro)
{
- InitializeInputStream(preprocessor, expansion);
+ initializeInputStream(preprocessor, expansion);
+
+ expansion->parent = preprocessor->inputStream;
+ expansion->primaryStream = preprocessor->inputStream->primaryStream;
+
expansion->environment = macro->environment;
expansion->macro = macro;
expansion->tokenReader = TokenReader(macro->tokens);
@@ -550,6 +556,26 @@ static void AddEndOfStreamToken(
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.Position = token.Position;
+ 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.
@@ -561,7 +587,7 @@ static void MaybeBeginMacroExpansion(
for (;;)
{
// Look at the next token ahead of us
- Token const& token = PeekRawToken(preprocessor);
+ Token token = PeekRawToken(preprocessor);
// Not an identifier? Can't be a macro.
if (token.type != TokenType::Identifier)
@@ -585,12 +611,29 @@ static void MaybeBeginMacroExpansion(
// requires more lookahead than we usually have/need
if (macro->flavor == PreprocessorMacroFlavor::FunctionLike)
{
- if(PeekSecondRawTokenType(preprocessor) != TokenType::LParent)
- return;
-
- // Consume the token that triggered macro expansion
+ // 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);
@@ -762,8 +805,22 @@ top:
}
// Now re-lex the input
- PreprocessorInputStream* inputStream = CreateInputStreamForSource(preprocessor, sb.ProduceString(), "token paste");
- if (inputStream->tokenReader.GetCount() != 1)
+
+ // We create a dummy file to represent the token-paste operation
+ SourceFile* sourceFile = preprocessor->getCompileRequest()->getSourceManager()->allocateSourceFile("token paste", sb.ProduceString());
+
+ Lexer lexer;
+ lexer.initialize(sourceFile, GetSink(preprocessor));
+
+ 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
@@ -839,7 +896,7 @@ inline String const& GetDirectiveName(PreprocessorDirectiveContext* context)
}
// Get the location of the directive being parsed.
-inline CodePosition const& GetDirectiveLoc(PreprocessorDirectiveContext* context)
+inline SourceLoc const& GetDirectiveLoc(PreprocessorDirectiveContext* context)
{
return context->directiveToken.Position;
}
@@ -851,7 +908,7 @@ static inline DiagnosticSink* GetSink(PreprocessorDirectiveContext* context)
}
// Wrapper to get a "current" location when parsing a directive
-static CodePosition PeekLoc(PreprocessorDirectiveContext* context)
+static SourceLoc PeekLoc(PreprocessorDirectiveContext* context)
{
return PeekLoc(context->preprocessor);
}
@@ -970,8 +1027,11 @@ 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 = inputStream->conditional;
+ PreprocessorConditional* conditional = primaryStream->conditional;
if (!conditional) return false;
// skip tokens unless the conditional is inside its `true` case
@@ -1032,8 +1092,9 @@ static void beginConditional(
conditional->state = state;
// Push conditional onto the stack
- conditional->parent = inputStream->conditional;
- inputStream->conditional = conditional;
+ auto primaryStream = inputStream->primaryStream;
+ conditional->parent = primaryStream->conditional;
+ primaryStream->conditional = conditional;
}
// Start a preprocessor conditional, with an initial enable/disable state.
@@ -1348,7 +1409,7 @@ static void HandleElseDirective(PreprocessorDirectiveContext* context)
SLANG_ASSERT(inputStream);
// if we aren't inside a conditional, then error
- PreprocessorConditional* conditional = inputStream->conditional;
+ PreprocessorConditional* conditional = inputStream->primaryStream->conditional;
if (!conditional)
{
GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveWithoutIf, GetDirectiveName(context));
@@ -1402,7 +1463,7 @@ static void HandleElifDirective(PreprocessorDirectiveContext* context)
PreprocessorExpressionValue value = ParseAndEvaluateExpression(context);
// if we aren't inside a conditional, then error
- PreprocessorConditional* conditional = inputStream->conditional;
+ PreprocessorConditional* conditional = inputStream->primaryStream->conditional;
if (!conditional)
{
GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveWithoutIf, GetDirectiveName(context));
@@ -1440,14 +1501,14 @@ static void HandleEndIfDirective(PreprocessorDirectiveContext* context)
SLANG_ASSERT(inputStream);
// if we aren't inside a conditional, then error
- PreprocessorConditional* conditional = inputStream->conditional;
+ PreprocessorConditional* conditional = inputStream->primaryStream->conditional;
if (!conditional)
{
GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveWithoutIf, GetDirectiveName(context));
return;
}
- inputStream->conditional = conditional->parent;
+ inputStream->primaryStream->conditional = conditional->parent;
DestroyConditional(conditional);
}
@@ -1492,8 +1553,9 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context)
String path = getFileNameTokenValue(pathToken);
- // TODO(tfoley): make this robust in presence of `#line`
- String pathIncludedFrom = GetDirectiveLoc(context).FileName;
+ auto directiveLoc = GetDirectiveLoc(context);
+ auto expandedDirectiveLoc = context->preprocessor->translationUnit->compileRequest->getSourceManager()->expandSourceLoc(directiveLoc);
+ String pathIncludedFrom = expandedDirectiveLoc.getSpellingPath();
String foundPath;
String foundSource;
@@ -1524,7 +1586,10 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context)
// Push the new file onto our stack of input streams
// TODO(tfoley): check if we have made our include stack too deep
- PreprocessorInputStream* inputStream = CreateInputStreamForSource(context->preprocessor, foundSource, foundPath);
+
+ SourceFile* sourceFile = context->preprocessor->getCompileRequest()->getSourceManager()->allocateSourceFile(foundPath, foundSource);
+
+ PreprocessorInputStream* inputStream = CreateInputStreamForSource(context->preprocessor, sourceFile);
inputStream->parent = context->preprocessor->inputStream;
context->preprocessor->inputStream = inputStream;
}
@@ -1654,18 +1719,26 @@ static void HandleErrorDirective(PreprocessorDirectiveContext* context)
// Handle a `#line` directive
static void HandleLineDirective(PreprocessorDirectiveContext* context)
{
+ auto inputStream = context->preprocessor->inputStream;
+
int line = 0;
+
+ // `#line <integer-literal> ...`
if (PeekTokenType(context) == TokenType::IntegerLiteral)
{
line = StringToInt(AdvanceToken(context).Content);
}
- else if (PeekTokenType(context) == TokenType::Identifier
- && PeekToken(context).Content == "default")
+ // `#line`
+ // `#line default`
+ else if (
+ PeekTokenType(context) == TokenType::EndOfDirective
+ || (PeekTokenType(context) == TokenType::Identifier
+ && PeekToken(context).Content == "default"))
{
AdvanceToken(context);
// Stop overiding soure locations.
- context->preprocessor->inputStream->isOverridingSourceLoc = false;
+ inputStream->primaryStream->lexer.stopOverridingSourceLocations();
return;
}
else
@@ -1678,16 +1751,19 @@ static void HandleLineDirective(PreprocessorDirectiveContext* context)
return;
}
- CodePosition directiveLoc = GetDirectiveLoc(context);
+ SourceLoc directiveLoc = GetDirectiveLoc(context);
+
+ auto sourceManager = context->preprocessor->translationUnit->compileRequest->getSourceManager();
+ auto expandedDirectiveLoc = sourceManager->expandSourceLoc(directiveLoc);
String file;
if (PeekTokenType(context) == TokenType::EndOfDirective)
{
- file = directiveLoc.FileName;
+ file = expandedDirectiveLoc.getPath();
}
else if (PeekTokenType(context) == TokenType::StringLiteral)
{
- file = AdvanceToken(context).Content;
+ file = getStringLiteralTokenValue(AdvanceToken(context));
}
else if (PeekTokenType(context) == TokenType::IntegerLiteral)
{
@@ -1701,11 +1777,9 @@ static void HandleLineDirective(PreprocessorDirectiveContext* context)
return;
}
- PreprocessorInputStream* inputStream = context->preprocessor->inputStream;
+ SourceLoc newLoc = sourceManager->allocateSourceFileForLineDirective(expandedDirectiveLoc, file, line);
- inputStream->isOverridingSourceLoc = true;
- inputStream->overrideFileName = file;
- inputStream->overrideLineOffset = line - (directiveLoc.Line + 1);
+ inputStream->primaryStream->lexer.startOverridingSourceLocations(newLoc);
}
// Handle a `#pragma` directive
@@ -2005,10 +2079,14 @@ static void DefineMacro(
String fileName = "command line";
PreprocessorMacro* macro = CreateMacro(preprocessor);
+ SourceFile* keyFile = preprocessor->translationUnit->compileRequest->getSourceManager()->allocateSourceFile(fileName, key);
+ SourceFile* valueFile = preprocessor->translationUnit->compileRequest->getSourceManager()->allocateSourceFile(fileName, value);
+
// Use existing `Lexer` to generate a token stream.
- Lexer lexer(fileName, value, GetSink(preprocessor));
+ Lexer lexer;
+ lexer.initialize(valueFile, GetSink(preprocessor));
macro->tokens = lexer.lexAllTokens();
- macro->nameToken = Token(TokenType::Identifier, key, 0, 0, 0, fileName);
+ macro->nameToken = Token(TokenType::Identifier, key, keyFile->sourceRange.begin);
PreprocessorMacro* oldMacro = NULL;
if (preprocessor->globalEnv.macros.TryGetValue(key, oldMacro))
@@ -2039,8 +2117,7 @@ static TokenList ReadAllTokens(
}
TokenList preprocessSource(
- String const& source,
- String const& fileName,
+ SourceFile* file,
DiagnosticSink* sink,
IncludeHandler* includeHandler,
Dictionary<String, String> defines,
@@ -2057,7 +2134,7 @@ TokenList preprocessSource(
}
// create an initial input stream based on the provided buffer
- preprocessor.inputStream = CreateInputStreamForSource(&preprocessor, source, fileName);
+ preprocessor.inputStream = CreateInputStreamForSource(&preprocessor, file);
TokenList tokens = ReadAllTokens(&preprocessor);
@@ -2097,8 +2174,9 @@ static void HandleImportDirective(PreprocessorDirectiveContext* context)
String path = getFileNameTokenValue(pathToken);
- // TODO(tfoley): make this robust in presence of `#line`
- String pathIncludedFrom = GetDirectiveLoc(context).FileName;
+ auto directiveLoc = GetDirectiveLoc(context);
+ auto expandedDirectiveLoc = context->preprocessor->translationUnit->compileRequest->getSourceManager()->expandSourceLoc(directiveLoc);
+ String pathIncludedFrom = expandedDirectiveLoc.getSpellingPath();
String foundPath;
String foundSource;
@@ -2162,7 +2240,8 @@ static void HandleImportDirective(PreprocessorDirectiveContext* context)
PreprocessorInputStream* savedStream = preprocessor->inputStream;
// Create an input stream for reading from the imported file
- PreprocessorInputStream* subInputStream = CreateInputStreamForSource(preprocessor, foundSource, foundPath);
+ SourceFile* sourceFile = context->preprocessor->getCompileRequest()->getSourceManager()->allocateSourceFile(foundPath, foundSource);
+ PreprocessorInputStream* subInputStream = CreateInputStreamForSource(preprocessor, sourceFile);
// Now preprocess that stream
preprocessor->inputStream = subInputStream;
@@ -2180,24 +2259,18 @@ static void HandleImportDirective(PreprocessorDirectiveContext* context)
// Now create a dummy token stream to represent the import request,
// so that it can be manifest in the user's program
- SourceTextInputStream* inputStream = new SourceTextInputStream();
-
+
Token token;
token.type = TokenType::PoundImport;
token.Position = GetDirectiveLoc(context);
token.flags = 0;
token.Content = foundPath;
-
- inputStream->lexedTokens.mTokens.Add(token);
-
- token.type = TokenType::EndOfFile;
- token.flags = TokenFlag::AfterWhitespace | TokenFlag::AtStartOfLine;
- inputStream->lexedTokens.mTokens.Add(token);
-
- inputStream->tokenReader = TokenReader(inputStream->lexedTokens);
-
- inputStream->parent = context->preprocessor->inputStream;
- context->preprocessor->inputStream = inputStream;
+
+ SimpleTokenInputStream* inputStream = createSimpleInputStream(
+ context->preprocessor,
+ token);
+
+ PushInputStream(context->preprocessor, inputStream);
}
diff --git a/source/slang/preprocessor.h b/source/slang/preprocessor.h
index 008707ffb..64591ef03 100644
--- a/source/slang/preprocessor.h
+++ b/source/slang/preprocessor.h
@@ -31,8 +31,7 @@ struct IncludeHandler
// Take a string of source code and preprocess it into a list of tokens.
TokenList preprocessSource(
- String const& source,
- String const& fileName,
+ SourceFile* file,
DiagnosticSink* sink,
IncludeHandler* includeHandler,
Dictionary<String, String> defines,
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 0dcbf44ea..e66884923 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -20,6 +20,9 @@ namespace Slang {
Session::Session()
{
+ // Make sure our source manager is initialized
+ builtinSourceManager.initialize(nullptr);
+
// Initialize representations of some very basic types:
initializeTypes();
@@ -82,6 +85,16 @@ struct IncludeHandlerImpl : IncludeHandler
}
};
+CompileRequest::CompileRequest(Session* session)
+ : mSession(session)
+{
+ setSourceManager(&sourceManagerStorage);
+
+ sourceManager->initialize(session->getBuiltinSourceManager());
+}
+
+CompileRequest::~CompileRequest()
+{}
void CompileRequest::parseTranslationUnit(
TranslationUnitRequest* translationUnit)
@@ -117,12 +130,8 @@ void CompileRequest::parseTranslationUnit(
for (auto sourceFile : translationUnit->sourceFiles)
{
- auto sourceFilePath = sourceFile->path;
- String source = sourceFile->content;
-
auto tokens = preprocessSource(
- source,
- sourceFilePath,
+ sourceFile,
&mSink,
&includeHandler,
combinedPreprocessorDefinitions,
@@ -132,7 +141,6 @@ void CompileRequest::parseTranslationUnit(
translationUnit,
tokens,
&mSink,
- sourceFilePath,
languageScope);
}
}
@@ -297,16 +305,21 @@ int CompileRequest::addTranslationUnit(SourceLanguage language, String const&)
return (int) result;
}
+void CompileRequest::addTranslationUnitSourceFile(
+ int translationUnitIndex,
+ SourceFile* sourceFile)
+{
+ translationUnits[translationUnitIndex]->sourceFiles.Add(sourceFile);
+}
+
void CompileRequest::addTranslationUnitSourceString(
int translationUnitIndex,
String const& path,
String const& source)
{
- RefPtr<SourceFile> sourceFile = new SourceFile();
- sourceFile->path = path;
- sourceFile->content = source;
+ RefPtr<SourceFile> sourceFile = getSourceManager()->allocateSourceFile(path, source);
- translationUnits[translationUnitIndex]->sourceFiles.Add(sourceFile);
+ addTranslationUnitSourceFile(translationUnitIndex, sourceFile);
}
void CompileRequest::addTranslationUnitSourceFile(
@@ -322,7 +335,7 @@ void CompileRequest::addTranslationUnitSourceFile(
{
// Emit a diagnostic!
mSink.diagnose(
- CodePosition(0, 0, 0, path),
+ SourceLoc(),
Diagnostics::cannotOpenFile,
path);
return;
@@ -359,7 +372,7 @@ RefPtr<ModuleDecl> CompileRequest::loadModule(
String const& name,
String const& path,
String const& source,
- CodePosition const&)
+ SourceLoc const&)
{
RefPtr<TranslationUnitRequest> translationUnit = new TranslationUnitRequest();
translationUnit->compileRequest = this;
@@ -370,9 +383,7 @@ RefPtr<ModuleDecl> CompileRequest::loadModule(
//
// TODO: decide which options, if any, should be inherited.
- RefPtr<SourceFile> sourceFile = new SourceFile();
- sourceFile->path = path;
- sourceFile->content = source;
+ RefPtr<SourceFile> sourceFile = getSourceManager()->allocateSourceFile(path, source);
translationUnit->sourceFiles.Add(sourceFile);
@@ -413,7 +424,6 @@ void CompileRequest::handlePoundImport(
translationUnit.Ptr(),
tokens,
&mSink,
- path,
languageScope);
// TODO: handle errors
@@ -438,7 +448,7 @@ void CompileRequest::handlePoundImport(
RefPtr<ModuleDecl> CompileRequest::findOrImportModule(
String const& name,
- CodePosition const& loc)
+ SourceLoc const& loc)
{
// Have we already loaded a module matching this name?
// If so, return it.
@@ -470,7 +480,9 @@ RefPtr<ModuleDecl> CompileRequest::findOrImportModule(
IncludeHandlerImpl includeHandler;
includeHandler.request = this;
- String pathIncludedFrom = loc.FileName;
+ auto expandedLoc = getSourceManager()->expandSourceLoc(loc);
+
+ String pathIncludedFrom = expandedLoc.getSpellingPath();
String foundPath;
String foundSource;
@@ -508,7 +520,7 @@ RefPtr<ModuleDecl> CompileRequest::findOrImportModule(
RefPtr<ModuleDecl> findOrImportModule(
CompileRequest* request,
String const& name,
- CodePosition const& loc)
+ SourceLoc const& loc)
{
return request->findOrImportModule(name, loc);
}
@@ -519,9 +531,12 @@ void Session::addBuiltinSource(
String const& source)
{
RefPtr<CompileRequest> compileRequest = new CompileRequest(this);
+ compileRequest->setSourceManager(getBuiltinSourceManager());
auto translationUnitIndex = compileRequest->addTranslationUnit(SourceLanguage::Slang, path);
+ RefPtr<SourceFile> sourceFile = builtinSourceManager.allocateSourceFile(path, source);
+
compileRequest->addTranslationUnitSourceString(
translationUnitIndex,
path,
@@ -818,7 +833,7 @@ SLANG_API int spCompile(
}
catch (...)
{
- req->mSink.diagnose(Slang::CodePosition(), Slang::Diagnostics::compilationAborted);
+ req->mSink.diagnose(Slang::SourceLoc(), Slang::Diagnostics::compilationAborted);
return 1;
}
}
diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj
index 4a1e75bc3..7da5c39d6 100644
--- a/source/slang/slang.vcxproj
+++ b/source/slang/slang.vcxproj
@@ -213,6 +213,7 @@
<ClCompile Include="reflection.cpp" />
<ClCompile Include="slang-stdlib.cpp" />
<ClCompile Include="slang.cpp" />
+ <ClCompile Include="source-loc.cpp" />
<ClCompile Include="syntax.cpp" />
<ClCompile Include="token.cpp" />
<ClCompile Include="type-layout.cpp" />
diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters
index c43cd9dbb..2241dc67c 100644
--- a/source/slang/slang.vcxproj.filters
+++ b/source/slang/slang.vcxproj.filters
@@ -56,5 +56,6 @@
<ClCompile Include="type-layout.cpp" />
<ClCompile Include="options.cpp" />
<ClCompile Include="lower.cpp" />
+ <ClCompile Include="source-loc.cpp" />
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/source/slang/source-loc.cpp b/source/slang/source-loc.cpp
new file mode 100644
index 000000000..cea8441e6
--- /dev/null
+++ b/source/slang/source-loc.cpp
@@ -0,0 +1,332 @@
+// source-loc.cpp
+#include "source-loc.h"
+
+namespace Slang {
+
+String ExpandedSourceLoc::getPath() const
+{
+ if(!sourceManager)
+ return String();
+
+ return sourceManager->sourceFiles[entryIndex].path;
+}
+
+String ExpandedSourceLoc::getSpellingPath() const
+{
+ if(!sourceManager)
+ return String();
+
+ return sourceManager->sourceFiles[entryIndex].sourceFile->path;
+}
+
+SourceFile* ExpandedSourceLoc::getSourceFile() const
+{
+ if(!sourceManager)
+ return nullptr;
+
+ return sourceManager->sourceFiles[entryIndex].sourceFile;
+}
+
+void SourceManager::initialize(
+ SourceManager* p)
+{
+ parent = p;
+
+ if( p )
+ {
+ // If we have a parent source manager, then we assume that all code at that level
+ // has already been loaded, and it is safe to start our own source locations
+ // right after those from the parent.
+ //
+ // TODO: more clever allocation in cases where that might not be reasonable
+ startLoc = p->nextLoc;
+ }
+ else
+ {
+ // Location zero is reserved for an invalid location,
+ // so we need to start reserving locations starting at 1.
+ startLoc = SourceLoc::fromRaw(1);
+ }
+
+ nextLoc = startLoc;
+}
+
+SourceRange SourceManager::allocateSourceRange(UInt size)
+{
+ // TODO: consider using atomics here
+
+
+ SourceLoc beginLoc = nextLoc;
+ SourceLoc endLoc = beginLoc + size;
+
+ // We need to be able to represent the location that is *at* the end of
+ // the input source, so the next available location for a new file
+ // must be placed one after the end of this one.
+
+ nextLoc = endLoc + 1;
+
+ return SourceRange(beginLoc, endLoc);
+}
+
+SourceFile* SourceManager::allocateSourceFile(
+ String const& path,
+ String const& content)
+{
+ UInt size = content.Length();
+
+ SourceRange sourceRange = allocateSourceRange(size);
+
+ SourceFile* sourceFile = new SourceFile();
+ sourceFile->path = path;
+ sourceFile->content = content;
+ sourceFile->sourceRange = sourceRange;
+
+ Entry entry;
+ entry.sourceFile = sourceFile;
+ entry.startLoc = sourceRange.begin;
+ entry.path = path;
+
+ sourceFiles.Add(entry);
+
+ return sourceFile;
+}
+
+SourceLoc SourceManager::allocateSourceFileForLineDirective(
+ SourceLoc const& directiveLoc,
+ String const& path,
+ UInt line)
+{
+ // First, we need to find out what file we are being asked to remap
+ ExpandedSourceLoc expandedDirectiveLoc = expandSourceLoc(getSpellingLoc(directiveLoc));
+ HumaneSourceLoc humaneDirectiveLoc = getHumaneLoc(expandedDirectiveLoc);
+
+ SourceFile* sourceFile = expandedDirectiveLoc.getSourceFile();
+ if(!sourceFile)
+ return SourceLoc();
+
+ // We are going to be wasteful here and allocate a range of source locations
+ // that can cover the entire input file. This will lead to a problem with
+ // memory usage if we ever had a large input file that used many `#line` directives,
+ // since our usage of ranges would be quadratic!
+
+ // Count how many locations we'd need to reserve for a complete clone of the input
+ UInt size = sourceFile->sourceRange.end.getRaw() - sourceFile->sourceRange.begin.getRaw();
+
+ // Allocate a fresh range for our logically remapped file
+ SourceRange sourceRange = allocateSourceRange(size);
+
+ // Now fill in an entry that will point at the original source file,
+ // but use our new range.
+ Entry entry;
+ entry.sourceFile = sourceFile;
+ entry.startLoc = sourceRange.begin;
+ entry.path = path;
+
+ // We also need to make sure that any lookups for line numbers will
+ // get corrected based on this files location.
+ entry.lineAdjust = Int(line) - Int(humaneDirectiveLoc.line + 1);
+
+ sourceFiles.Add(entry);
+
+ return entry.startLoc;
+}
+
+static ExpandedSourceLoc expandSourceLoc(
+ SourceManager* inSourceManager,
+ SourceLoc const& loc)
+{
+ SourceManager* sourceManager = inSourceManager;
+
+ ExpandedSourceLoc expanded;
+
+ SourceLoc::RawValue rawValue = loc.getRaw();
+
+ // Invalid location? -> invalid expanded location
+ if(rawValue == 0)
+ return expanded;
+
+ // Past the end of what we can handle? -> invalid
+ if(rawValue >= sourceManager->nextLoc.getRaw())
+ return expanded;
+
+ // Maybe the location came from a parent source manager
+ while( rawValue < sourceManager->startLoc.getRaw()
+ && sourceManager->parent)
+ {
+ sourceManager = sourceManager->parent;
+ }
+
+ SLANG_ASSERT(sourceManager->sourceFiles.Count() > 0);
+
+ UInt lo = 0;
+ UInt hi = sourceManager->sourceFiles.Count();
+
+ while( lo+1 < hi )
+ {
+ UInt mid = lo + (hi - lo) / 2;
+
+ SourceManager::Entry const& midEntry = sourceManager->sourceFiles[mid];
+ SourceLoc::RawValue midValue = midEntry.startLoc.getRaw();
+
+ if( midValue <= rawValue )
+ {
+ // The location we seek is at or after this entry
+ lo = mid;
+ }
+ else
+ {
+ // The location we seek is before this entry
+ hi = mid;
+ }
+ }
+
+ // `lo` should now point at the entry we want
+ UInt entryIndex = lo;
+
+ expanded.setRaw(loc.getRaw());
+ expanded.sourceManager = sourceManager;
+ expanded.entryIndex = entryIndex;
+
+ return expanded;
+
+
+}
+
+ExpandedSourceLoc SourceManager::expandSourceLoc(SourceLoc const& loc)
+{
+ return Slang::expandSourceLoc(this, loc);
+}
+
+HumaneSourceLoc SourceManager::getHumaneLoc(ExpandedSourceLoc const& loc)
+{
+ // First check if this location maps to an actual file.
+ SourceFile* sourceFile = loc.getSourceFile();
+ if(!sourceFile)
+ return HumaneSourceLoc();
+
+ auto& entry = sourceFiles[loc.entryIndex];
+ UInt offset = loc.getRaw() - entry.startLoc.getRaw();
+
+ // We now have a raw input file that we can search for line breaks.
+ // We obviously don't want to do a linear scan over and over, so we will
+ // cache an array of line break locations in the file.
+ auto& lineBreakOffsets = sourceFile->lineBreakOffsets;
+ if( lineBreakOffsets.Count() == 0 )
+ {
+ char const* begin = sourceFile->content.begin();
+ char const* end = sourceFile->content.end();
+
+ char const* cursor = begin;
+
+ // Treat the beginning of the file as a line break
+ lineBreakOffsets.Add(0);
+
+ while( cursor != end )
+ {
+ int c = *cursor++;
+ switch( c )
+ {
+ case '\r': case '\n':
+ {
+ // When we see a line-break character we need
+ // to record the line break, but we also need
+ // to deal with the annoying issue of encodings,
+ // where a multi-byte sequence might encode
+ // the line break.
+
+ int d = *cursor;
+ if( (c^d) == ('\r' ^ '\n'))
+ cursor++;
+
+ lineBreakOffsets.Add(cursor - begin);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Note taht we do *not* treat the end of the file as a line
+ // break, because otherwise we would report errors like
+ // "end of file inside string literal" with a line number
+ // that points at a line that doesn't exist.
+ }
+
+ // At this point we can assume the `lineBreakOffsets` array has been filled in.
+ // We will use a binary search to find the line index that contains our
+ // chosen offset.
+ UInt lo = 0;
+ UInt hi = lineBreakOffsets.Count();
+
+ while( lo+1 < hi )
+ {
+ UInt mid = lo + (hi - lo)/2;
+
+ UInt midOffset = lineBreakOffsets[mid];
+ if( midOffset <= offset )
+ {
+ lo = mid;
+ }
+ else
+ {
+ hi = mid;
+ }
+ }
+
+ UInt lineIndex = lo;
+ UInt byteIndexInLine = offset - lineBreakOffsets[lineIndex];
+
+ // Apply adjustment to the line number
+ lineIndex = lineIndex + entry.lineAdjust;
+
+ // TODO: we should really translate the byte index in the line
+ // to deal with:
+ //
+ // - Non-ASCII characters, while might consume multiple bytes
+ //
+ // - Tab characters, which should really adjust how we report
+ // columns (although how are we supposed to know the setting
+ // that an IDE expects us to use when reporting locations?)
+
+ HumaneSourceLoc humaneLoc;
+ humaneLoc.path = entry.path;
+ humaneLoc.line = lineIndex + 1;
+ humaneLoc.column = byteIndexInLine + 1;
+
+ return humaneLoc;
+}
+
+HumaneSourceLoc SourceManager::getHumaneLoc(SourceLoc const& loc)
+{
+ return getHumaneLoc(expandSourceLoc(loc));
+
+}
+
+SourceLoc SourceManager::getSpellingLoc(ExpandedSourceLoc const& loc)
+{
+ // First check if this location maps to some raw source file,
+ // so that a "spelling" is even possible
+ SourceFile* sourceFile = loc.getSourceFile();
+ if(!sourceFile)
+ return loc;
+
+ // If we mapped to a source file, then the location must represent
+ // some offset from an entry in our array.
+ auto& entry = sourceFiles[loc.entryIndex];
+
+ // We extract the offset of the location from the start of the entry
+ SourceLoc::RawValue offsetFromStart = loc.getRaw() - entry.startLoc.getRaw();
+
+ // And instead apply that offset to the spelling location of the file start
+ SourceLoc result = sourceFile->sourceRange.begin + offsetFromStart;
+
+ return result;
+}
+
+SourceLoc SourceManager::getSpellingLoc(SourceLoc const& loc)
+{
+ return getSpellingLoc(expandSourceLoc(loc));
+}
+
+} // namespace Slang
diff --git a/source/slang/source-loc.h b/source/slang/source-loc.h
index 59f2f10ea..8bb89c645 100644
--- a/source/slang/source-loc.h
+++ b/source/slang/source-loc.h
@@ -6,36 +6,181 @@
namespace Slang {
-class CodePosition
+class SourceLoc
{
public:
- int Line = -1, Col = -1, Pos = -1;
- String FileName;
- String ToString()
- {
- StringBuilder sb(100);
- sb << FileName;
- if (Line != -1)
- sb << "(" << Line << ")";
- return sb.ProduceString();
- }
- CodePosition() = default;
- CodePosition(int line, int col, int pos, String fileName)
- {
- Line = line;
- Col = col;
- Pos = pos;
- this->FileName = fileName;
- }
- bool operator < (const CodePosition & pos) const
- {
- return FileName < pos.FileName || (FileName == pos.FileName && Line < pos.Line) ||
- (FileName == pos.FileName && Line == pos.Line && Col < pos.Col);
- }
- bool operator == (const CodePosition & pos) const
- {
- return FileName == pos.FileName && Line == pos.Line && Col == pos.Col;
- }
+ typedef UInt RawValue;
+
+private:
+ RawValue raw;
+
+public:
+ SourceLoc()
+ : raw(0)
+ {}
+
+ SourceLoc(
+ SourceLoc const& loc)
+ : raw(loc.raw)
+ {}
+
+ RawValue getRaw() const { return raw; }
+ void setRaw(RawValue value) { raw = value; }
+
+ static SourceLoc fromRaw(RawValue value)
+ {
+ SourceLoc result;
+ result.setRaw(value);
+ return result;
+ }
+
+ bool isValid() const
+ {
+ return raw != 0;
+ }
+};
+
+inline SourceLoc operator+(SourceLoc loc, Int offset)
+{
+ return SourceLoc::fromRaw(loc.getRaw() + UInt(offset));
+}
+
+// A range of locations in the input source
+struct SourceRange
+{
+ SourceRange()
+ {}
+
+ SourceRange(SourceLoc loc)
+ : begin(loc)
+ , end(loc)
+ {}
+
+ SourceRange(SourceLoc begin, SourceLoc end)
+ : begin(begin)
+ , end(end)
+ {}
+
+ SourceLoc begin;
+ SourceLoc end;
+};
+
+// A logical or phyiscal storage object for a range of input code
+// that has logically contiguous source locations.
+class SourceFile : public RefObject
+{
+public:
+ // The logical file path to report for locations inside this span.
+ String path;
+
+ // The actual contents of the file.
+ String content;
+
+ // The range of source locations that the span covers
+ SourceRange sourceRange;
+
+ // In order to speed up lookup of line number information,
+ // we will cache the starting offset of each line break in
+ // the input file:
+ List<UInt> lineBreakOffsets;
+};
+
+struct SourceManager;
+
+// A source location in a format a human might like to see
+struct HumaneSourceLoc
+{
+ String path;
+ Int line = 0;
+ Int column = 0;
+
+ String const& getPath() const { return path; }
+ Int getLine() const { return line; }
+ Int getColumn() const { return column; }
+};
+
+// A source location that has been expanded with the info
+// needed to reconstruct a "humane" location if needed.
+struct ExpandedSourceLoc : public SourceLoc
+{
+ // The source manager that owns this location
+ SourceManager* sourceManager = nullptr;
+
+ // The entry index that is used to understand the location
+ UInt entryIndex = 0;
+
+ // Get the nominal path for this location
+ String getPath() const;
+
+ // Get the actual file path where this location appears
+ String getSpellingPath() const;
+
+ // Get the original source file that holds this location
+ SourceFile* getSourceFile() const;
+};
+
+
+
+struct SourceManager
+{
+ // Initialize a source manager, with an optional parent
+ void initialize(
+ SourceManager* parent);
+
+ SourceRange allocateSourceRange(UInt size);
+
+ SourceFile* allocateSourceFile(
+ String const& path,
+ String const& content);
+
+ SourceLoc allocateSourceFileForLineDirective(
+ SourceLoc const& directiveLoc,
+ String const& path,
+ UInt line);
+
+ // Expand a source location to include more explicit info
+ ExpandedSourceLoc expandSourceLoc(SourceLoc const& loc);
+
+ // Get a "humane" version of a source location
+ HumaneSourceLoc getHumaneLoc(ExpandedSourceLoc const& loc);
+ HumaneSourceLoc getHumaneLoc(SourceLoc const& loc);
+
+
+ // Get the source location that represents the spelling location corresponding to a location.
+ SourceLoc getSpellingLoc(ExpandedSourceLoc const& loc);
+ SourceLoc getSpellingLoc(SourceLoc const& loc);
+
+ // The first location available to this source manager
+ // (may not be the first location of all, because we might
+ // have a parent source manager)
+ SourceLoc startLoc;
+
+ // The "parent" source manager that owns locations ahead of `startLoc`
+ SourceManager* parent = nullptr;
+
+ // The location to be used by the next source file to be loaded
+ SourceLoc nextLoc;
+
+ // Each entry represents some contiguous span of locations that
+ // all map to the same logical file.
+ struct Entry
+ {
+ // Where does this entry begin?
+ SourceLoc startLoc;
+
+ // The soure file that represents the actual data
+ RefPtr<SourceFile> sourceFile;
+
+ // What is the presumed path for this entry
+ String path;
+
+ // Adjustment to apply to source line numbers when printing presumed locations
+ Int lineAdjust = 0;
+ };
+
+ // An array of soure files we have loaded, ordered by
+ // increasing starting location
+ List<Entry> sourceFiles;
};
diff --git a/source/slang/syntax-base-defs.h b/source/slang/syntax-base-defs.h
index d76607106..711c4425b 100644
--- a/source/slang/syntax-base-defs.h
+++ b/source/slang/syntax-base-defs.h
@@ -9,7 +9,7 @@
ABSTRACT_SYNTAX_CLASS(SyntaxNodeBase, RefObject)
// The primary source location associated with this AST node
- FIELD(CodePosition, Position)
+ FIELD(SourceLoc, Position)
RAW(
// Allow dynamic casting with a convenient syntax
diff --git a/source/slang/syntax-visitors.h b/source/slang/syntax-visitors.h
index f2c080a57..0780bab2a 100644
--- a/source/slang/syntax-visitors.h
+++ b/source/slang/syntax-visitors.h
@@ -26,7 +26,7 @@ namespace Slang
RefPtr<ModuleDecl> findOrImportModule(
CompileRequest* request,
String const& name,
- CodePosition const& loc);
+ SourceLoc const& loc);
}
#endif \ No newline at end of file
diff --git a/source/slang/token.h b/source/slang/token.h
index f29f2b4c6..306d4a2f2 100644
--- a/source/slang/token.h
+++ b/source/slang/token.h
@@ -18,8 +18,9 @@ char const* TokenTypeToString(TokenType type);
enum TokenFlag : unsigned int
{
- AtStartOfLine = 1 << 0,
- AfterWhitespace = 1 << 1,
+ AtStartOfLine = 1 << 0,
+ AfterWhitespace = 1 << 1,
+ SuppressMacroExpansion = 1 << 2,
};
typedef unsigned int TokenFlags;
@@ -28,15 +29,21 @@ class Token
public:
TokenType type = TokenType::Unknown;
String Content;
- CodePosition Position;
+ SourceLoc Position;
TokenFlags flags = 0;
+
Token() = default;
- Token(TokenType type, const String & content, int line, int col, int pos, String fileName, TokenFlags flags = 0)
+
+ Token(
+ TokenType type,
+ const String & content,
+ SourceLoc loc,
+ TokenFlags flags = 0)
: flags(flags)
{
type = type;
Content = content;
- Position = CodePosition(line, col, pos, fileName);
+ Position = loc;
}
};
diff --git a/tests/preprocessor/line.slang b/tests/preprocessor/line.slang
new file mode 100644
index 000000000..7babcae82
--- /dev/null
+++ b/tests/preprocessor/line.slang
@@ -0,0 +1,19 @@
+//TEST:SIMPLE:
+// #line support
+
+FooA a() { return 0; }
+
+#line 99 "b.slang"
+FooB b() { return 0; }
+
+#line default
+FooC c() { return 0; }
+
+#line 603 "d.slang"
+FooD d() { return 0; }
+
+#line 40
+FooE e() { return 0; }
+
+#line
+FooF f() { return 0; }
diff --git a/tests/preprocessor/line.slang.expected b/tests/preprocessor/line.slang.expected
new file mode 100644
index 000000000..8c7f72ada
--- /dev/null
+++ b/tests/preprocessor/line.slang.expected
@@ -0,0 +1,11 @@
+result code = -1
+standard error = {
+tests/preprocessor/line.slang(4): error 30015: undefined identifier 'FooA'.
+b.slang(99): error 30015: undefined identifier 'FooB'.
+tests/preprocessor/line.slang(10): error 30015: undefined identifier 'FooC'.
+d.slang(603): error 30015: undefined identifier 'FooD'.
+d.slang(40): error 30015: undefined identifier 'FooE'.
+tests/preprocessor/line.slang(19): error 30015: undefined identifier 'FooF'.
+}
+standard output = {
+}