summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2021-04-22 09:32:25 -0400
committerGitHub <noreply@github.com>2021-04-22 09:32:25 -0400
commitda0d295d6c8b6fb03245dea0583437c198890349 (patch)
treeed17baba750b15f6ace1427f04cf19690269161e
parent34fba7b5e726136c6eee8a318ab9a75381399c00 (diff)
C++ extractor improvements (#1803)
* #include an absolute path didn't work - because paths were taken to always be relative. * Split of NodeTree. Split out FileUtil. Split out MacroWriter. * Rename slang-cpp-extractor-main.cpp -> cpp-extractor-main.cpp * First pass at extractor unit-tests * Initial parsing of enum. * Ability to disable/enable parsing of scope types. * Initial support for typedef. * Added operator== != to ArrayVIew. Added test for splitting to unit tests. * Improve comment in StringUtil. * Fix comment. * Fix typo.
-rw-r--r--build/visual-studio/slang-cpp-extractor/slang-cpp-extractor.vcxproj10
-rw-r--r--build/visual-studio/slang-cpp-extractor/slang-cpp-extractor.vcxproj.filters26
-rw-r--r--source/compiler-core/slang-diagnostic-sink.cpp17
-rw-r--r--source/compiler-core/slang-diagnostic-sink.h16
-rw-r--r--source/core/slang-array-view.h29
-rw-r--r--source/core/slang-string-util.cpp46
-rw-r--r--source/core/slang-string-util.h3
-rw-r--r--tools/slang-cpp-extractor/cpp-extractor-main.cpp250
-rw-r--r--tools/slang-cpp-extractor/diagnostic-defs.h5
-rw-r--r--tools/slang-cpp-extractor/file-util.cpp72
-rw-r--r--tools/slang-cpp-extractor/file-util.h25
-rw-r--r--tools/slang-cpp-extractor/identifier-lookup.cpp66
-rw-r--r--tools/slang-cpp-extractor/identifier-lookup.h15
-rw-r--r--tools/slang-cpp-extractor/macro-writer.cpp436
-rw-r--r--tools/slang-cpp-extractor/macro-writer.h47
-rw-r--r--tools/slang-cpp-extractor/node-tree.cpp159
-rw-r--r--tools/slang-cpp-extractor/node-tree.h102
-rw-r--r--tools/slang-cpp-extractor/node.cpp256
-rw-r--r--tools/slang-cpp-extractor/node.h96
-rw-r--r--tools/slang-cpp-extractor/options.cpp10
-rw-r--r--tools/slang-cpp-extractor/options.h1
-rw-r--r--tools/slang-cpp-extractor/parser.cpp495
-rw-r--r--tools/slang-cpp-extractor/parser.h104
-rw-r--r--tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp767
-rw-r--r--tools/slang-cpp-extractor/unit-test.cpp80
-rw-r--r--tools/slang-cpp-extractor/unit-test.h16
-rw-r--r--tools/slang-test/unit-test-string.cpp43
27 files changed, 2124 insertions, 1068 deletions
diff --git a/build/visual-studio/slang-cpp-extractor/slang-cpp-extractor.vcxproj b/build/visual-studio/slang-cpp-extractor/slang-cpp-extractor.vcxproj
index 6e5d1cdb2..707327ba7 100644
--- a/build/visual-studio/slang-cpp-extractor/slang-cpp-extractor.vcxproj
+++ b/build/visual-studio/slang-cpp-extractor/slang-cpp-extractor.vcxproj
@@ -164,18 +164,26 @@
<ItemGroup>
<ClInclude Include="..\..\..\tools\slang-cpp-extractor\diagnostic-defs.h" />
<ClInclude Include="..\..\..\tools\slang-cpp-extractor\diagnostics.h" />
+ <ClInclude Include="..\..\..\tools\slang-cpp-extractor\file-util.h" />
<ClInclude Include="..\..\..\tools\slang-cpp-extractor\identifier-lookup.h" />
+ <ClInclude Include="..\..\..\tools\slang-cpp-extractor\macro-writer.h" />
+ <ClInclude Include="..\..\..\tools\slang-cpp-extractor\node-tree.h" />
<ClInclude Include="..\..\..\tools\slang-cpp-extractor\node.h" />
<ClInclude Include="..\..\..\tools\slang-cpp-extractor\options.h" />
<ClInclude Include="..\..\..\tools\slang-cpp-extractor\parser.h" />
+ <ClInclude Include="..\..\..\tools\slang-cpp-extractor\unit-test.h" />
</ItemGroup>
<ItemGroup>
+ <ClCompile Include="..\..\..\tools\slang-cpp-extractor\cpp-extractor-main.cpp" />
<ClCompile Include="..\..\..\tools\slang-cpp-extractor\diagnostics.cpp" />
+ <ClCompile Include="..\..\..\tools\slang-cpp-extractor\file-util.cpp" />
<ClCompile Include="..\..\..\tools\slang-cpp-extractor\identifier-lookup.cpp" />
+ <ClCompile Include="..\..\..\tools\slang-cpp-extractor\macro-writer.cpp" />
+ <ClCompile Include="..\..\..\tools\slang-cpp-extractor\node-tree.cpp" />
<ClCompile Include="..\..\..\tools\slang-cpp-extractor\node.cpp" />
<ClCompile Include="..\..\..\tools\slang-cpp-extractor\options.cpp" />
<ClCompile Include="..\..\..\tools\slang-cpp-extractor\parser.cpp" />
- <ClCompile Include="..\..\..\tools\slang-cpp-extractor\slang-cpp-extractor-main.cpp" />
+ <ClCompile Include="..\..\..\tools\slang-cpp-extractor\unit-test.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\core\core.vcxproj">
diff --git a/build/visual-studio/slang-cpp-extractor/slang-cpp-extractor.vcxproj.filters b/build/visual-studio/slang-cpp-extractor/slang-cpp-extractor.vcxproj.filters
index c11f860de..ed10cd2c3 100644
--- a/build/visual-studio/slang-cpp-extractor/slang-cpp-extractor.vcxproj.filters
+++ b/build/visual-studio/slang-cpp-extractor/slang-cpp-extractor.vcxproj.filters
@@ -15,9 +15,18 @@
<ClInclude Include="..\..\..\tools\slang-cpp-extractor\diagnostics.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\tools\slang-cpp-extractor\file-util.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\tools\slang-cpp-extractor\identifier-lookup.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\tools\slang-cpp-extractor\macro-writer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\slang-cpp-extractor\node-tree.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\tools\slang-cpp-extractor\node.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -27,14 +36,29 @@
<ClInclude Include="..\..\..\tools\slang-cpp-extractor\parser.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\tools\slang-cpp-extractor\unit-test.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
+ <ClCompile Include="..\..\..\tools\slang-cpp-extractor\cpp-extractor-main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\tools\slang-cpp-extractor\diagnostics.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\tools\slang-cpp-extractor\file-util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\tools\slang-cpp-extractor\identifier-lookup.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\tools\slang-cpp-extractor\macro-writer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\slang-cpp-extractor\node-tree.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\tools\slang-cpp-extractor\node.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -44,7 +68,7 @@
<ClCompile Include="..\..\..\tools\slang-cpp-extractor\parser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\tools\slang-cpp-extractor\slang-cpp-extractor-main.cpp">
+ <ClCompile Include="..\..\..\tools\slang-cpp-extractor\unit-test.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
diff --git a/source/compiler-core/slang-diagnostic-sink.cpp b/source/compiler-core/slang-diagnostic-sink.cpp
index 2adf31f69..727c322a5 100644
--- a/source/compiler-core/slang-diagnostic-sink.cpp
+++ b/source/compiler-core/slang-diagnostic-sink.cpp
@@ -385,6 +385,23 @@ static void formatDiagnostic(
}
}
+void DiagnosticSink::init(SourceManager* sourceManager, SourceLocationLexer sourceLocationLexer)
+{
+ m_errorCount = 0;
+ m_internalErrorLocsNoted = 0;
+
+ m_flags = 0;
+
+ m_sourceManager = sourceManager;
+ m_sourceLocationLexer = sourceLocationLexer;
+
+ // If we have a source location lexer, we'll by default enable source location output
+ if (sourceLocationLexer)
+ {
+ setFlag(Flag::SourceLocationLine);
+ }
+}
+
void DiagnosticSink::diagnoseImpl(SourceLoc const& pos, DiagnosticInfo const& info, int argCount, DiagnosticArg const* const* args)
{
StringBuilder sb;
diff --git a/source/compiler-core/slang-diagnostic-sink.h b/source/compiler-core/slang-diagnostic-sink.h
index 84001e6f5..347ff761c 100644
--- a/source/compiler-core/slang-diagnostic-sink.h
+++ b/source/compiler-core/slang-diagnostic-sink.h
@@ -217,16 +217,16 @@ public:
/// character caret at location
SourceLocationLexer getSourceLocationLexer() const { return m_sourceLocationLexer; }
+ /// Initialize state.
+ void init(SourceManager* sourceManager, SourceLocationLexer sourceLocationLexer);
+
/// Ctor
- DiagnosticSink(SourceManager* sourceManager, SourceLocationLexer sourceLocationLexer)
- : m_sourceManager(sourceManager),
- m_sourceLocationLexer(sourceLocationLexer)
+ DiagnosticSink(SourceManager* sourceManager, SourceLocationLexer sourceLocationLexer) { init(sourceManager, sourceLocationLexer); }
+ /// Default Ctor
+ DiagnosticSink():
+ m_sourceManager(nullptr),
+ m_sourceLocationLexer (nullptr)
{
- // If we have a source location lexer, we'll by default enable source location output
- if (sourceLocationLexer)
- {
- setFlag(Flag::SourceLocationLine);
- }
}
// Public members
diff --git a/source/core/slang-array-view.h b/source/core/slang-array-view.h
index 56c936073..c67a53337 100644
--- a/source/core/slang-array-view.h
+++ b/source/core/slang-array-view.h
@@ -12,6 +12,8 @@ namespace Slang
class ConstArrayView
{
public:
+ typedef ConstArrayView ThisType;
+
const T* begin() const { return m_buffer; }
const T* end() const { return m_buffer + m_count; }
@@ -70,6 +72,30 @@ namespace Slang
return -1;
}
+ bool operator==(const ThisType& rhs) const
+ {
+ if (&rhs == this)
+ {
+ return true;
+ }
+ const Index count = getCount();
+ if (count != rhs.getCount())
+ {
+ return false;
+ }
+ const T* thisEle = getBuffer();
+ const T* rhsEle = rhs.getBuffer();
+ for (Index i = 0; i < count; ++i)
+ {
+ if (thisEle[i] != rhsEle[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
+
ConstArrayView() :
m_buffer(nullptr),
m_count(0)
@@ -111,6 +137,8 @@ namespace Slang
class ArrayView: public ConstArrayView<T>
{
public:
+ typedef ArrayView ThisType;
+
typedef ConstArrayView<T> Super;
using Super::m_buffer;
@@ -147,6 +175,7 @@ namespace Slang
{
return ArrayView<T>(buffer, count);
}
+
}
#endif
diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp
index b4c767c87..8108bdc98 100644
--- a/source/core/slang-string-util.cpp
+++ b/source/core/slang-string-util.cpp
@@ -56,6 +56,52 @@ namespace Slang {
}
}
+/* static */void StringUtil::split(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& outSlices)
+{
+ const Index splitLen = splitSlice.getLength();
+
+ if (splitLen == 1)
+ {
+ return split(in, splitSlice[0], outSlices);
+ }
+
+ outSlices.clear();
+
+ SLANG_ASSERT(splitLen > 0);
+ if (splitLen <= 0)
+ {
+ return;
+ }
+
+ const char* start = in.begin();
+ const char* end = in.end();
+
+ const char splitChar = splitSlice[0];
+
+ while (start < end)
+ {
+ // Move cur so it's either at the end or at next splitSlice
+ const char* cur = start;
+ while (cur < end)
+ {
+ if (*cur == splitChar &&
+ (cur + splitLen <= end && UnownedStringSlice(cur, splitLen) == splitSlice))
+ {
+ // We hit a split
+ break;
+ }
+
+ cur++;
+ }
+
+ // Add to output
+ outSlices.add(UnownedStringSlice(start, cur));
+
+ // Skip the split, if at end we are okay anyway
+ start = cur + splitLen;
+ }
+}
+
/* static */Index StringUtil::split(const UnownedStringSlice& in, char splitChar, Index maxSlices, UnownedStringSlice* outSlices)
{
Index index = 0;
diff --git a/source/core/slang-string-util.h b/source/core/slang-string-util.h
index 2850c3b4f..8031f5c8c 100644
--- a/source/core/slang-string-util.h
+++ b/source/core/slang-string-util.h
@@ -24,6 +24,9 @@ struct StringUtil
/// Split in, by specified splitChar into slices out
/// Slices contents will directly address into in, so contents will only stay valid as long as in does.
static void split(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& slicesOut);
+ /// Split in by the specified splitSlice
+ /// Slices contents will directly address into in, so contents will only stay valid as long as in does.
+ static void split(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& slicesOut);
/// Splits in into outSlices, up to maxSlices. May not consume all of in (for example if it runs out of space).
static Index split(const UnownedStringSlice& in, char splitChar, Index maxSlices, UnownedStringSlice* outSlices);
diff --git a/tools/slang-cpp-extractor/cpp-extractor-main.cpp b/tools/slang-cpp-extractor/cpp-extractor-main.cpp
new file mode 100644
index 000000000..d1ec47e69
--- /dev/null
+++ b/tools/slang-cpp-extractor/cpp-extractor-main.cpp
@@ -0,0 +1,250 @@
+// main.cpp
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../../source/core/slang-secure-crt.h"
+
+#include "../../slang-com-helper.h"
+
+#include "../../source/core/slang-list.h"
+#include "../../source/core/slang-string.h"
+#include "../../source/core/slang-string-util.h"
+#include "../../source/core/slang-io.h"
+#include "../../source/core/slang-string-slice-pool.h"
+#include "../../source/core/slang-writer.h"
+#include "../../source/core/slang-file-system.h"
+
+#include "../../source/compiler-core/slang-source-loc.h"
+#include "../../source/compiler-core/slang-lexer.h"
+#include "../../source/compiler-core/slang-diagnostic-sink.h"
+#include "../../source/compiler-core/slang-name.h"
+#include "../../source/compiler-core/slang-name-convention-util.h"
+
+#include "node.h"
+#include "diagnostics.h"
+#include "options.h"
+#include "parser.h"
+#include "macro-writer.h"
+#include "file-util.h"
+#include "unit-test.h"
+
+/*
+Some command lines:
+
+-d source/slang slang-ast-support-types.h slang-ast-base.h slang-ast-decl.h slang-ast-expr.h slang-ast-modifier.h slang-ast-stmt.h slang-ast-type.h slang-ast-val.h -strip-prefix slang- -o slang-generated -output-fields -mark-suffix _CLASS
+*/
+
+namespace CppExtract
+{
+
+using namespace Slang;
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! App !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+class App
+{
+public:
+
+ SlangResult execute(const Options& options);
+
+ /// Execute
+ SlangResult executeWithArgs(int argc, const char*const* argv);
+
+ const Options& getOptions() const { return m_options; }
+
+ App(DiagnosticSink* sink, SourceManager* sourceManager, RootNamePool* rootNamePool):
+ m_sink(sink),
+ m_sourceManager(sourceManager),
+ m_slicePool(StringSlicePool::Style::Default)
+ {
+ m_namePool.setRootNamePool(rootNamePool);
+ }
+
+protected:
+
+ NamePool m_namePool;
+
+ Options m_options;
+ DiagnosticSink* m_sink;
+ SourceManager* m_sourceManager;
+
+ StringSlicePool m_slicePool;
+};
+
+
+SlangResult App::execute(const Options& options)
+{
+ m_options = options;
+
+ if (options.m_runUnitTests)
+ {
+ SLANG_RETURN_ON_FAIL(UnitTestUtil::run());
+ }
+
+ IdentifierLookup identifierLookup;
+ identifierLookup.initDefault(options.m_markPrefix.getUnownedSlice());
+
+ NodeTree tree(&m_slicePool, &m_namePool, &identifierLookup);
+
+ // Read in each of the input files
+ for (Index i = 0; i < m_options.m_inputPaths.getCount(); ++i)
+ {
+ String inputPath;
+
+ if (m_options.m_inputDirectory.getLength())
+ {
+ inputPath = Path::combine(m_options.m_inputDirectory, m_options.m_inputPaths[i]);
+ }
+ else
+ {
+ inputPath = m_options.m_inputPaths[i];
+ }
+
+ // Read the input file
+ String contents;
+ SLANG_RETURN_ON_FAIL(FileUtil::readAllText(inputPath, m_sink, contents));
+
+ PathInfo pathInfo = PathInfo::makeFromString(inputPath);
+
+ SourceFile* sourceFile = m_sourceManager->createSourceFileWithString(pathInfo, contents);
+
+ SourceOrigin* sourceOrigin = tree.addSourceOrigin(sourceFile, options);
+
+ Parser parser(&tree, m_sink);
+ SLANG_RETURN_ON_FAIL(parser.parse(sourceOrigin, &m_options));
+ }
+
+ SLANG_RETURN_ON_FAIL(tree.calcDerivedTypes(m_sink));
+
+ // Okay let's check out the typeSets
+ {
+ for (TypeSet* typeSet : tree.getTypeSets())
+ {
+ // The macro name is in upper snake, so split it
+ List<UnownedStringSlice> slices;
+ NameConventionUtil::split(typeSet->m_macroName, slices);
+
+ if (typeSet->m_fileMark.getLength() == 0)
+ {
+ StringBuilder buf;
+ // Let's guess a 'fileMark' (it becomes part of the filename) based on the macro name. Use lower kabab.
+ NameConventionUtil::join(slices.getBuffer(), slices.getCount(), CharCase::Lower, NameConvention::Kabab, buf);
+ typeSet->m_fileMark = buf.ProduceString();
+ }
+
+ if (typeSet->m_typeName.getLength() == 0)
+ {
+ // Let's guess a typename if not set -> go with upper camel
+ StringBuilder buf;
+ NameConventionUtil::join(slices.getBuffer(), slices.getCount(), CharCase::Upper, NameConvention::Camel, buf);
+ typeSet->m_typeName = buf.ProduceString();
+ }
+ }
+ }
+
+ // Dump out the tree
+ if (options.m_dump)
+ {
+ {
+ StringBuilder buf;
+ tree.getRootNode()->dump(0, buf);
+ m_sink->writer->write(buf.getBuffer(), buf.getLength());
+ }
+
+ for (TypeSet* typeSet : tree.getTypeSets())
+ {
+ const List<ClassLikeNode*>& baseTypes = typeSet->m_baseTypes;
+
+ for (ClassLikeNode* baseType : baseTypes)
+ {
+ StringBuilder buf;
+ baseType->dumpDerived(0, buf);
+ m_sink->writer->write(buf.getBuffer(), buf.getLength());
+ }
+ }
+ }
+
+ if (options.m_defs)
+ {
+ MacroWriter macroWriter(m_sink, &m_options);
+ SLANG_RETURN_ON_FAIL(macroWriter.writeDefs(&tree));
+ }
+
+ if (options.m_outputPath.getLength())
+ {
+ MacroWriter macroWriter(m_sink, &m_options);
+ SLANG_RETURN_ON_FAIL(macroWriter.writeOutput(&tree));
+ }
+
+ return SLANG_OK;
+}
+
+/// Execute
+SlangResult App::executeWithArgs(int argc, const char*const* argv)
+{
+ Options options;
+ OptionsParser optionsParser;
+ SLANG_RETURN_ON_FAIL(optionsParser.parse(argc, argv, m_sink, options));
+ SLANG_RETURN_ON_FAIL(execute(options));
+ return SLANG_OK;
+}
+
+} // namespace CppExtract
+
+int main(int argc, const char*const* argv)
+{
+ using namespace CppExtract;
+ using namespace Slang;
+
+ {
+ ComPtr<ISlangWriter> writer(new FileWriter(stderr, WriterFlag::AutoFlush));
+
+ RootNamePool rootNamePool;
+
+ SourceManager sourceManager;
+ sourceManager.initialize(nullptr, nullptr);
+
+ DiagnosticSink sink(&sourceManager, Lexer::sourceLocationLexer);
+ sink.writer = writer;
+
+ // Set to true to see command line that initiated C++ extractor. Helpful when finding issues from solution building failing, and then so
+ // being able to repeat the issue
+ bool dumpCommandLine = false;
+
+ if (dumpCommandLine)
+ {
+ StringBuilder builder;
+
+ for (Index i = 1; i < argc; ++i)
+ {
+ builder << argv[i] << " ";
+ }
+
+ sink.diagnose(SourceLoc(), CPPDiagnostics::commandLine, builder);
+ }
+
+ App app(&sink, &sourceManager, &rootNamePool);
+
+ try
+ {
+ if (SLANG_FAILED(app.executeWithArgs(argc - 1, argv + 1)))
+ {
+ sink.diagnose(SourceLoc(), CPPDiagnostics::extractorFailed);
+ return 1;
+ }
+ if (sink.getErrorCount())
+ {
+ sink.diagnose(SourceLoc(), CPPDiagnostics::extractorFailed);
+ return 1;
+ }
+ }
+ catch (...)
+ {
+ sink.diagnose(SourceLoc(), CPPDiagnostics::internalError);
+ return 1;
+ }
+ }
+ return 0;
+}
+
diff --git a/tools/slang-cpp-extractor/diagnostic-defs.h b/tools/slang-cpp-extractor/diagnostic-defs.h
index 284e02d19..34b444206 100644
--- a/tools/slang-cpp-extractor/diagnostic-defs.h
+++ b/tools/slang-cpp-extractor/diagnostic-defs.h
@@ -41,7 +41,10 @@ DIAGNOSTIC(100009, Error, unexpectedUnbalancedToken, "Unexpected unbalanced toke
DIAGNOSTIC(100010, Error, unexpectedEndOfFile, "Unexpected end of file")
DIAGNOSTIC(100011, Error, expectingTypeKeyword, "Expecting type keyword - struct or class, found $0")
-DIAGNOSTIC(100011, Error, typeInDifferentTypeSet, "Type $0 in different type set $1 from super class $2")
+DIAGNOSTIC(100012, Error, typeInDifferentTypeSet, "Type $0 in different type set $1 from super class $2")
+DIAGNOSTIC(100013, Error, expectingIdentifier, "Expecting an identifier, found $0")
+DIAGNOSTIC(100014, Error, cannotDeclareTypeInScope, "Cannot declare types in this scope")
+DIAGNOSTIC(100015, Error, identifierAlreadyDefined, "Identifier already defined '$0'")
// Command line errors 100100
diff --git a/tools/slang-cpp-extractor/file-util.cpp b/tools/slang-cpp-extractor/file-util.cpp
new file mode 100644
index 000000000..1d37df650
--- /dev/null
+++ b/tools/slang-cpp-extractor/file-util.cpp
@@ -0,0 +1,72 @@
+#include "file-util.h"
+
+#include "../../source/core/slang-io.h"
+
+namespace CppExtract {
+using namespace Slang;
+
+/* static */SlangResult FileUtil::readAllText(const Slang::String& fileName, DiagnosticSink* sink, String& outRead)
+{
+ try
+ {
+ StreamReader reader(new FileStream(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite));
+ outRead = reader.ReadToEnd();
+ }
+ catch (const IOException&)
+ {
+ if (sink)
+ {
+ sink->diagnose(SourceLoc(), CPPDiagnostics::cannotOpenFile, fileName);
+ }
+ return SLANG_FAIL;
+ }
+ catch (...)
+ {
+ if (sink)
+ {
+ sink->diagnose(SourceLoc(), CPPDiagnostics::cannotOpenFile, fileName);
+ }
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+}
+
+/* static */SlangResult FileUtil::writeAllText(const Slang::String& fileName, DiagnosticSink* sink, const UnownedStringSlice& text)
+{
+ try
+ {
+ if (File::exists(fileName))
+ {
+ String existingText;
+
+ if (readAllText(fileName, nullptr, existingText) == SLANG_OK)
+ {
+ if (existingText == text)
+ return SLANG_OK;
+ }
+ }
+ StreamWriter writer(new FileStream(fileName, FileMode::Create));
+ writer.Write(text);
+ }
+ catch (const IOException&)
+ {
+ if (sink)
+ {
+ sink->diagnose(SourceLoc(), CPPDiagnostics::cannotOpenFile, fileName);
+ }
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+}
+
+/* static */ void FileUtil::indent(Index indentCount, StringBuilder& out)
+{
+ for (Index i = 0; i < indentCount; ++i)
+ {
+ out << CPP_EXTRACT_INDENT_STRING;
+ }
+}
+
+} // namespace CppExtract
diff --git a/tools/slang-cpp-extractor/file-util.h b/tools/slang-cpp-extractor/file-util.h
new file mode 100644
index 000000000..01aafeedf
--- /dev/null
+++ b/tools/slang-cpp-extractor/file-util.h
@@ -0,0 +1,25 @@
+#ifndef CPP_EXTRACT_FILE_UTIL_H
+#define CPP_EXTRACT_FILE_UTIL_H
+
+#include "diagnostics.h"
+
+namespace CppExtract {
+using namespace Slang;
+
+// A macro to define a single indent as a string
+#define CPP_EXTRACT_INDENT_STRING " "
+
+struct FileUtil
+{
+ /// Read text into outRead. Any failures written to sink (can be passed as nullptr, for no output)
+ static SlangResult readAllText(const Slang::String& fileName, DiagnosticSink* sink, String& outRead);
+ /// Write text to filename. Any failures written to sink. (can be passed as nullptr, for no output)
+ static SlangResult writeAllText(const Slang::String& fileName, DiagnosticSink* sink, const UnownedStringSlice& text);
+
+ /// Appends CPP_EXTRACT_INDENT_STRING indentCount number of times to out
+ static void indent(Index indentCount, StringBuilder& out);
+};
+
+} // CppExtract
+
+#endif
diff --git a/tools/slang-cpp-extractor/identifier-lookup.cpp b/tools/slang-cpp-extractor/identifier-lookup.cpp
index 07f155248..c7be75d82 100644
--- a/tools/slang-cpp-extractor/identifier-lookup.cpp
+++ b/tools/slang-cpp-extractor/identifier-lookup.cpp
@@ -11,15 +11,19 @@ using namespace Slang;
0, /// Type set
IdentifierFlag::Keyword, /// TypeModifier
IdentifierFlag::Keyword, /// Keyword
+
IdentifierFlag::Keyword | IdentifierFlag::StartScope | IdentifierFlag::ClassLike, /// Class
IdentifierFlag::Keyword | IdentifierFlag::StartScope | IdentifierFlag::ClassLike, /// Struct
IdentifierFlag::Keyword | IdentifierFlag::StartScope, /// Namespace
+ IdentifierFlag::Keyword | IdentifierFlag::StartScope, /// Enum
+
+ IdentifierFlag::Keyword, /// Typedef
+
IdentifierFlag::Keyword, /// Access
IdentifierFlag::Reflection, /// Reflected
IdentifierFlag::Reflection, /// Unreflected
};
-
void IdentifierLookup::set(const UnownedStringSlice& name, IdentifierStyle style)
{
StringSlicePool::Handle handle;
@@ -44,5 +48,65 @@ void IdentifierLookup::set(const char*const* names, size_t namesCount, Identifie
}
}
+void IdentifierLookup::set(const Pair* pairs, Index pairsCount)
+{
+ for (Index i = 0; i < pairsCount; ++i)
+ {
+ const auto& pair = pairs[i];
+ set(UnownedStringSlice(pair.name), pair.style);
+ }
+}
+
+void IdentifierLookup::initDefault(const UnownedStringSlice& markPrefix)
+{
+ reset();
+
+ // Some keywords
+ {
+ const char* names[] = { "virtual", "continue", "if", "case", "break", "catch", "default", "delete", "do", "else", "for", "new", "goto", "return", "switch", "throw", "using", "while", "operator" };
+ set(names, SLANG_COUNT_OF(names), IdentifierStyle::Keyword);
+ }
+
+ // Type modifier keywords
+ {
+ const char* names[] = { "const", "volatile" };
+ set(names, SLANG_COUNT_OF(names), IdentifierStyle::TypeModifier);
+ }
+
+ // Special markers
+ {
+ const char* names[] = { "PRE_DECLARE", "TYPE_SET", "REFLECTED", "UNREFLECTED" };
+ const IdentifierStyle styles[] = { IdentifierStyle::PreDeclare, IdentifierStyle::TypeSet, IdentifierStyle::Reflected, IdentifierStyle::Unreflected };
+ SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(names) == SLANG_COUNT_OF(styles));
+
+ StringBuilder buf;
+ for (Index i = 0; i < SLANG_COUNT_OF(names); ++i)
+ {
+ buf.Clear();
+ buf << markPrefix << names[i];
+ set(buf.getUnownedSlice(), styles[i]);
+ }
+ }
+
+ // Keywords which introduce types/scopes
+ {
+ const Pair pairs[] =
+ {
+ { "struct", IdentifierStyle::Struct },
+ { "class", IdentifierStyle::Class },
+ { "namespace", IdentifierStyle::Namespace },
+ { "enum", IdentifierStyle::Enum },
+ { "typedef", IdentifierStyle::TypeDef },
+ };
+
+ set(pairs, SLANG_COUNT_OF(pairs));
+ }
+
+ // Keywords that control access
+ {
+ const char* names[] = { "private", "protected", "public" };
+ set(names, SLANG_COUNT_OF(names), IdentifierStyle::Access);
+ }
+}
} // namespace CppExtract
diff --git a/tools/slang-cpp-extractor/identifier-lookup.h b/tools/slang-cpp-extractor/identifier-lookup.h
index b845f804c..3cee909ef 100644
--- a/tools/slang-cpp-extractor/identifier-lookup.h
+++ b/tools/slang-cpp-extractor/identifier-lookup.h
@@ -17,9 +17,14 @@ enum class IdentifierStyle
TypeModifier, ///< const, volatile etc
Keyword, ///< A keyword C/C++ keyword that is not another type
+
Class, ///< class
Struct, ///< struct
Namespace, ///< namespace
+ Enum, ///< enum
+
+ TypeDef, ///< typedef
+
Access, ///< public, protected, private
Reflected,
@@ -45,6 +50,12 @@ class IdentifierLookup
{
public:
+ struct Pair
+ {
+ const char* name;
+ IdentifierStyle style;
+ };
+
IdentifierStyle get(const UnownedStringSlice& slice) const
{
Index index = m_pool.findIndex(slice);
@@ -60,12 +71,16 @@ public:
void set(const char*const* names, size_t namesCount, IdentifierStyle style);
+ void set(const Pair* pairs, Index pairsCount);
+
void reset()
{
m_styles.clear();
m_pool.clear();
}
+ void initDefault(const UnownedStringSlice& markPrefix);
+
IdentifierLookup() :
m_pool(StringSlicePool::Style::Empty)
{
diff --git a/tools/slang-cpp-extractor/macro-writer.cpp b/tools/slang-cpp-extractor/macro-writer.cpp
new file mode 100644
index 000000000..2259f1800
--- /dev/null
+++ b/tools/slang-cpp-extractor/macro-writer.cpp
@@ -0,0 +1,436 @@
+#include "macro-writer.h"
+
+#include "../../slang-com-helper.h"
+
+#include "../../source/core/slang-list.h"
+#include "../../source/core/slang-string.h"
+//#include "../../source/core/slang-string-util.h"
+#include "../../source/core/slang-io.h"
+
+#include "../../source/core/slang-writer.h"
+
+#include "../../source/compiler-core/slang-diagnostic-sink.h"
+//#include "../../source/compiler-core/slang-name.h"
+
+#include "diagnostics.h"
+#include "options.h"
+#include "node-tree.h"
+#include "file-util.h"
+
+namespace CppExtract
+{
+using namespace Slang;
+
+SLANG_FORCE_INLINE static void _indent(Index indentCount, StringBuilder& out) { return FileUtil::indent(indentCount, out); }
+
+SlangResult MacroWriter::calcDef(NodeTree* tree, SourceOrigin* origin, StringBuilder& out)
+{
+ Node* currentScope = nullptr;
+
+ for (Node* node : origin->m_nodes)
+ {
+ if (node->isReflected())
+ {
+ if (auto classLikeNode = as<ClassLikeNode>(node))
+ {
+ if (classLikeNode->m_marker.getContent().indexOf(UnownedStringSlice::fromLiteral("ABSTRACT")) >= 0)
+ {
+ out << "ABSTRACT_";
+ }
+
+ out << "SYNTAX_CLASS(" << node->m_name.getContent() << ", " << classLikeNode->m_super.getContent() << ")\n";
+ out << "END_SYNTAX_CLASS()\n\n";
+ }
+ }
+ }
+ return SLANG_OK;
+}
+
+SlangResult MacroWriter::calcChildrenHeader(NodeTree* tree, TypeSet* typeSet, StringBuilder& out)
+{
+ const List<ClassLikeNode*>& baseTypes = typeSet->m_baseTypes;
+ const String& reflectTypeName = typeSet->m_typeName;
+
+ out << "#pragma once\n\n";
+ out << "// Do not edit this file is generated from slang-cpp-extractor tool\n\n";
+
+ List<ClassLikeNode*> classNodes;
+ for (Index i = 0; i < baseTypes.getCount(); ++i)
+ {
+ ClassLikeNode* baseType = baseTypes[i];
+ baseType->calcDerivedDepthFirst(classNodes);
+ }
+
+ //Node::filter(Node::isClassLike, nodes);
+
+ List<ClassLikeNode*> derivedTypes;
+
+ out << "\n\n /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! CHILDREN !!!!!!!!!!!!!!!!!!!!!!!!!!!! */ \n\n";
+
+ // Now the children
+ for (ClassLikeNode* classNode : classNodes)
+ {
+ classNode->getReflectedDerivedTypes(derivedTypes);
+
+ // Define the derived types
+ out << "#define " << m_options->m_markPrefix << "CHILDREN_" << reflectTypeName << "_" << classNode->m_name.getContent() << "(x, param)";
+
+ if (derivedTypes.getCount())
+ {
+ out << " \\\n";
+ for (Index j = 0; j < derivedTypes.getCount(); ++j)
+ {
+ Node* derivedType = derivedTypes[j];
+ _indent(1, out);
+ out << m_options->m_markPrefix << "ALL_" << reflectTypeName << "_" << derivedType->m_name.getContent() << "(x, param)";
+ if (j < derivedTypes.getCount() - 1)
+ {
+ out << "\\\n";
+ }
+ }
+ }
+ out << "\n\n";
+ }
+
+ out << "\n\n /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ALL !!!!!!!!!!!!!!!!!!!!!!!!!!!! */\n\n";
+
+ for (ClassLikeNode* classNode : classNodes)
+ {
+ // Define the derived types
+ out << "#define " << m_options->m_markPrefix << "ALL_" << reflectTypeName << "_" << classNode->m_name.getContent() << "(x, param) \\\n";
+ _indent(1, out);
+ out << m_options->m_markPrefix << reflectTypeName << "_" << classNode->m_name.getContent() << "(x, param)";
+
+ // If has derived types output them
+ if (classNode->hasReflectedDerivedType())
+ {
+ out << " \\\n";
+ _indent(1, out);
+ out << m_options->m_markPrefix << "CHILDREN_" << reflectTypeName << "_" << classNode->m_name.getContent() << "(x, param)";
+ }
+ out << "\n\n";
+ }
+
+ if (m_options->m_outputFields)
+ {
+ out << "\n\n /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! FIELDS !!!!!!!!!!!!!!!!!!!!!!!!!!!! */\n\n";
+
+ for (ClassLikeNode* classNode : classNodes)
+ {
+ // Define the derived types
+ out << "#define " << m_options->m_markPrefix << "FIELDS_" << reflectTypeName << "_" << classNode->m_name.getContent() << "(_x_, _param_)";
+
+ // Find all of the fields
+ List<FieldNode*> fields;
+ for (Node* child : classNode->m_children)
+ {
+ if (auto field = as<FieldNode>(child))
+ {
+ fields.add(field);
+ }
+ }
+
+ if (fields.getCount() > 0)
+ {
+ out << "\\\n";
+
+ const Index fieldsCount = fields.getCount();
+ bool previousField = false;
+ for (Index j = 0; j < fieldsCount; ++j)
+ {
+ const FieldNode* field = fields[j];
+
+ if (field->isReflected())
+ {
+ if (previousField)
+ {
+ out << "\\\n";
+ }
+
+ _indent(1, out);
+
+ // NOTE! We put the type field in brackets, such that there is no issue with templates containing a comma.
+ // If stringified
+ out << "_x_(" << field->m_name.getContent() << ", (" << field->m_fieldType << "), _param_)";
+ previousField = true;
+ }
+ }
+ }
+
+ out << "\n\n";
+ }
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult MacroWriter::calcOriginHeader(NodeTree* tree, StringBuilder& out)
+{
+ // Do macros by origin
+
+ out << "// Origin macros\n\n";
+
+ for (SourceOrigin* origin : tree->getSourceOrigins())
+ {
+ out << "#define " << m_options->m_markPrefix << "ORIGIN_" << origin->m_macroOrigin << "(x, param) \\\n";
+
+ for (Node* node : origin->m_nodes)
+ {
+ if (!(node->isReflected() && node->isClassLike()))
+ {
+ continue;
+ }
+
+ _indent(1, out);
+ out << "x(" << node->m_name.getContent() << ", param) \\\n";
+ }
+ out << "/* */\n\n";
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult MacroWriter::calcTypeHeader(NodeTree* tree, TypeSet* typeSet, StringBuilder& out)
+{
+ const List<ClassLikeNode*>& baseTypes = typeSet->m_baseTypes;
+ const String& reflectTypeName = typeSet->m_typeName;
+
+ out << "#pragma once\n\n";
+ out << "// Do not edit this file is generated from slang-cpp-extractor tool\n\n";
+
+ if (baseTypes.getCount() == 0)
+ {
+ return SLANG_OK;
+ }
+
+ // Set up the scope
+ List<Node*> baseScopePath;
+ baseTypes[0]->calcScopePath(baseScopePath);
+
+ // Remove the global scope
+ baseScopePath.removeAt(0);
+ // Remove the type itself
+ baseScopePath.removeLast();
+
+ for (Node* scopeNode : baseScopePath)
+ {
+ SLANG_ASSERT(scopeNode->m_type == Node::Type::Namespace);
+ out << "namespace " << scopeNode->m_name.getContent() << " {\n";
+ }
+
+ // Add all the base types, with in order traversals
+ List<ClassLikeNode*> nodes;
+ for (Index i = 0; i < baseTypes.getCount(); ++i)
+ {
+ ClassLikeNode* baseType = baseTypes[i];
+ baseType->calcDerivedDepthFirst(nodes);
+ }
+
+ Node::filter(Node::isClassLikeAndReflected, nodes);
+
+ // Write out the types
+ {
+ out << "\n";
+ out << "enum class " << reflectTypeName << "Type\n";
+ out << "{\n";
+
+ Index typeIndex = 0;
+ for (ClassLikeNode* node : nodes)
+ {
+ // Okay first we are going to output the enum values
+ const Index depth = node->calcDerivedDepth() - 1;
+ _indent(depth, out);
+ out << node->m_name.getContent() << " = " << typeIndex << ",\n";
+ typeIndex++;
+ }
+
+ _indent(1, out);
+ out << "CountOf\n";
+
+ out << "};\n\n";
+ }
+
+ // TODO(JS):
+ // Strictly speaking if we wanted the types to be in different scopes, we would have to
+ // change the namespaces here
+
+ // Predeclare the classes
+ {
+ out << "// Predeclare\n\n";
+ for (ClassLikeNode* node : nodes)
+ {
+ // If it's not reflected we don't output, in the enum list
+ if (node->isReflected())
+ {
+ const char* type = (node->m_type == Node::Type::ClassType) ? "class" : "struct";
+ out << type << " " << node->m_name.getContent() << ";\n";
+ }
+ }
+ }
+
+ // Do the macros for each of the types
+
+ {
+ out << "// Type macros\n\n";
+
+ out << "// Order is (NAME, SUPER, ORIGIN, LAST, MARKER, TYPE, param) \n";
+ out << "// NAME - is the class name\n";
+ out << "// SUPER - is the super class name (or NO_SUPER)\n";
+ out << "// ORIGIN - where the definition was found\n";
+ out << "// LAST - is the class name for the last in the range (or NO_LAST)\n";
+ out << "// MARKER - is the text inbetween in the prefix/postix (like ABSTRACT). If no inbetween text is is 'NONE'\n";
+ out << "// TYPE - Can be BASE, INNER or LEAF for the overall base class, an INNER class, or a LEAF class\n";
+ out << "// param is a user defined parameter that can be parsed to the invoked x macro\n\n";
+
+ // Output all of the definitions for each type
+ for (ClassLikeNode* node : nodes)
+ {
+ out << "#define " << m_options->m_markPrefix << reflectTypeName << "_" << node->m_name.getContent() << "(x, param) ";
+
+ // Output the X macro part
+ _indent(1, out);
+ out << "x(" << node->m_name.getContent() << ", ";
+
+ if (node->m_superNode)
+ {
+ out << node->m_superNode->m_name.getContent() << ", ";
+ }
+ else
+ {
+ out << "NO_SUPER, ";
+ }
+
+ // Output the (file origin)
+ out << node->m_origin->m_macroOrigin;
+ out << ", ";
+
+ // The last type
+ Node* lastDerived = node->findLastDerived();
+ if (lastDerived)
+ {
+ out << lastDerived->m_name.getContent() << ", ";
+ }
+ else
+ {
+ out << "NO_LAST, ";
+ }
+
+ // Output any specifics of the markup
+ UnownedStringSlice marker = node->m_marker.getContent();
+ // Need to extract the name
+ if (marker.getLength() > m_options->m_markPrefix.getLength() + m_options->m_markSuffix.getLength())
+ {
+ marker = UnownedStringSlice(marker.begin() + m_options->m_markPrefix.getLength(), marker.end() - m_options->m_markSuffix.getLength());
+ }
+ else
+ {
+ marker = UnownedStringSlice::fromLiteral("NONE");
+ }
+ out << marker << ", ";
+
+ if (node->m_superNode == nullptr)
+ {
+ out << "BASE, ";
+ }
+ else if (node->hasReflectedDerivedType())
+ {
+ out << "INNER, ";
+ }
+ else
+ {
+ out << "LEAF, ";
+ }
+ out << "param)\n";
+ }
+ }
+
+ // Now pop the scope in revers
+ for (Index j = baseScopePath.getCount() - 1; j >= 0; j--)
+ {
+ Node* scopeNode = baseScopePath[j];
+ out << "} // namespace " << scopeNode->m_name.getContent() << "\n";
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult MacroWriter::writeDefs(NodeTree* tree)
+{
+ const auto& origins = tree->getSourceOrigins();
+
+ for (SourceOrigin* origin : origins)
+ {
+ const String path = origin->m_sourceFile->getPathInfo().foundPath;
+
+ // We need to work out the name of the def file
+
+ String ext = Path::getPathExt(path);
+ String pathWithoutExt = Path::getPathWithoutExt(path);
+
+ // The output path
+
+ StringBuilder outPath;
+ outPath << pathWithoutExt << "-defs." << ext;
+
+ StringBuilder content;
+ SLANG_RETURN_ON_FAIL(calcDef(tree, origin, content));
+
+ // Write the defs file
+ SLANG_RETURN_ON_FAIL(FileUtil::writeAllText(outPath, m_sink, content.getUnownedSlice()));
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult MacroWriter::writeOutput(NodeTree* tree)
+{
+ String path;
+ if (m_options->m_inputDirectory.getLength())
+ {
+ path = Path::combine(m_options->m_inputDirectory, m_options->m_outputPath);
+ }
+ else
+ {
+ path = m_options->m_outputPath;
+ }
+
+ // Get the ext
+ String ext = Path::getPathExt(path);
+ if (ext.getLength() == 0)
+ {
+ // Default to .h if not specified
+ ext = "h";
+ }
+
+ // Strip the extension if set
+ path = Path::getPathWithoutExt(path);
+
+ for (TypeSet* typeSet : tree->getTypeSets())
+ {
+ {
+ /// Calculate the header
+ StringBuilder header;
+ SLANG_RETURN_ON_FAIL(calcTypeHeader(tree, typeSet, header));
+
+ // Write it out
+
+ StringBuilder headerPath;
+ headerPath << path << "-" << typeSet->m_fileMark << "." << ext;
+ SLANG_RETURN_ON_FAIL(FileUtil::writeAllText(headerPath, m_sink, header.getUnownedSlice()));
+ }
+
+ {
+ StringBuilder childrenHeader;
+ SLANG_RETURN_ON_FAIL(calcChildrenHeader(tree, typeSet, childrenHeader));
+
+ StringBuilder headerPath;
+ headerPath << path << "-" << typeSet->m_fileMark << "-macro." + ext;
+ SLANG_RETURN_ON_FAIL(FileUtil::writeAllText(headerPath, m_sink, childrenHeader.getUnownedSlice()));
+ }
+ }
+
+ return SLANG_OK;
+}
+
+} // namespace CppExtract
+
diff --git a/tools/slang-cpp-extractor/macro-writer.h b/tools/slang-cpp-extractor/macro-writer.h
new file mode 100644
index 000000000..b1754741d
--- /dev/null
+++ b/tools/slang-cpp-extractor/macro-writer.h
@@ -0,0 +1,47 @@
+#ifndef CPP_EXTRACT_MACRO_WRITER_H
+#define CPP_EXTRACT_MACRO_WRITER_H
+
+#include "diagnostics.h"
+
+#include "options.h"
+#include "node-tree.h"
+
+#include "../../source/compiler-core/slang-diagnostic-sink.h"
+
+namespace CppExtract {
+using namespace Slang;
+
+/* A class that writes out macros that define type hierarchies, as well as fields of types */
+class MacroWriter
+{
+public:
+
+ /// Write output
+ SlangResult writeOutput(NodeTree* tree);
+
+ /// Write def files
+ SlangResult writeDefs(NodeTree* tree);
+
+ /// Calculate the header
+ SlangResult calcTypeHeader(NodeTree* tree, TypeSet* typeSet, StringBuilder& out);
+ SlangResult calcChildrenHeader(NodeTree* tree, TypeSet* typeSet, StringBuilder& out);
+ SlangResult calcOriginHeader(NodeTree* tree, StringBuilder& out);
+
+ SlangResult calcDef(NodeTree* tree, SourceOrigin* origin, StringBuilder& out);
+
+ /// Ctor.
+ MacroWriter(DiagnosticSink* sink, const Options* options):
+ m_sink(sink),
+ m_options(options)
+ {
+ }
+
+protected:
+
+ const Options* m_options = nullptr;
+ DiagnosticSink* m_sink;
+};
+
+} // CppExtract
+
+#endif
diff --git a/tools/slang-cpp-extractor/node-tree.cpp b/tools/slang-cpp-extractor/node-tree.cpp
new file mode 100644
index 000000000..3002a7abd
--- /dev/null
+++ b/tools/slang-cpp-extractor/node-tree.cpp
@@ -0,0 +1,159 @@
+#include "node-tree.h"
+
+#include "options.h"
+#include "identifier-lookup.h"
+
+#include "../../source/compiler-core/slang-name-convention-util.h"
+
+#include "../../source/core/slang-io.h"
+
+namespace CppExtract {
+using namespace Slang;
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! NodeTree !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+NodeTree::NodeTree(StringSlicePool* typePool, NamePool* namePool, IdentifierLookup* identifierLookup):
+ m_typePool(typePool),
+ m_namePool(namePool),
+ m_identifierLookup(identifierLookup),
+ m_typeSetPool(StringSlicePool::Style::Empty)
+{
+ m_rootNode = new ScopeNode(Node::Type::Namespace);
+ m_rootNode->m_reflectionType = ReflectionType::Reflected;
+}
+
+TypeSet* NodeTree::getTypeSet(const UnownedStringSlice& slice)
+{
+ Index index = m_typeSetPool.findIndex(slice);
+ if (index < 0)
+ {
+ return nullptr;
+ }
+ return m_typeSets[index];
+}
+
+TypeSet* NodeTree::getOrAddTypeSet(const UnownedStringSlice& slice)
+{
+ const Index index = Index(m_typeSetPool.add(slice));
+ if (index >= m_typeSets.getCount())
+ {
+ SLANG_ASSERT(m_typeSets.getCount() == index);
+ TypeSet* typeSet = new TypeSet;
+
+ m_typeSets.add(typeSet);
+ typeSet->m_macroName = m_typeSetPool.getSlice(StringSlicePool::Handle(index));
+ return typeSet;
+ }
+ else
+ {
+ return m_typeSets[index];
+ }
+}
+
+SourceOrigin* NodeTree::addSourceOrigin(SourceFile* sourceFile, const Options& options)
+{
+ // Calculate from the path, a 'macro origin' name.
+ const String macroOrigin = calcMacroOrigin(sourceFile->getPathInfo().foundPath, options);
+
+ SourceOrigin* origin = new SourceOrigin(sourceFile, macroOrigin);
+ m_sourceOrigins.add(origin);
+ return origin;
+}
+
+/* static */String NodeTree::calcMacroOrigin(const String& filePath, const Options& options)
+{
+ // Get the filename without extension
+ String fileName = Path::getFileNameWithoutExt(filePath);
+
+ // We can work on just the slice
+ UnownedStringSlice slice = fileName.getUnownedSlice();
+
+ // Filename prefix
+ if (options.m_stripFilePrefix.getLength() && slice.startsWith(options.m_stripFilePrefix.getUnownedSlice()))
+ {
+ const Index len = options.m_stripFilePrefix.getLength();
+ slice = UnownedStringSlice(slice.begin() + len, slice.end());
+ }
+
+ // Trim -
+ slice = slice.trim('-');
+
+ StringBuilder out;
+ NameConventionUtil::convert(slice, CharCase::Upper, NameConvention::Snake, out);
+ return out;
+}
+
+SlangResult NodeTree::_calcDerivedTypesRec(ScopeNode* inScopeNode, DiagnosticSink* sink)
+{
+ if (inScopeNode->isClassLike())
+ {
+ ClassLikeNode* classLikeNode = static_cast<ClassLikeNode*>(inScopeNode);
+
+ if (classLikeNode->m_super.hasContent())
+ {
+ ScopeNode* parentScope = classLikeNode->m_parentScope;
+ if (parentScope == nullptr)
+ {
+ sink->diagnoseRaw(Severity::Error, UnownedStringSlice::fromLiteral("Can't lookup in scope if there is none!"));
+ return SLANG_FAIL;
+ }
+
+ Node* superNode = Node::lookup(parentScope, classLikeNode->m_super.getContent());
+
+ if (!superNode)
+ {
+ if (classLikeNode->isReflected())
+ {
+ sink->diagnose(classLikeNode->m_name, CPPDiagnostics::superTypeNotFound, classLikeNode->getAbsoluteName());
+ return SLANG_FAIL;
+ }
+ }
+ else
+ {
+ ClassLikeNode* superType = as<ClassLikeNode>(superNode);
+
+ if (!superType)
+ {
+ sink->diagnose(classLikeNode->m_name, CPPDiagnostics::superTypeNotAType, classLikeNode->getAbsoluteName());
+ return SLANG_FAIL;
+ }
+
+ if (superType->m_typeSet != classLikeNode->m_typeSet)
+ {
+ sink->diagnose(classLikeNode->m_name, CPPDiagnostics::typeInDifferentTypeSet, classLikeNode->m_name.getContent(), classLikeNode->m_typeSet->m_macroName, superType->m_typeSet->m_macroName);
+ return SLANG_FAIL;
+ }
+
+ // The base class must be defined in same scope (as we didn't allow different scopes for base classes)
+ superType->addDerived(classLikeNode);
+ }
+ }
+ else
+ {
+ // Add to it's own typeset
+ if (classLikeNode->isReflected())
+ {
+ classLikeNode->m_typeSet->m_baseTypes.add(classLikeNode);
+ }
+ }
+ }
+
+ for (Node* child : inScopeNode->m_children)
+ {
+ ScopeNode* childScope = as<ScopeNode>(child);
+ if (childScope)
+ {
+ SLANG_RETURN_ON_FAIL(_calcDerivedTypesRec(childScope, sink));
+ }
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult NodeTree::calcDerivedTypes(DiagnosticSink* sink)
+{
+ return _calcDerivedTypesRec(m_rootNode, sink);
+}
+
+
+} // namespace CppExtract
diff --git a/tools/slang-cpp-extractor/node-tree.h b/tools/slang-cpp-extractor/node-tree.h
new file mode 100644
index 000000000..b54321b09
--- /dev/null
+++ b/tools/slang-cpp-extractor/node-tree.h
@@ -0,0 +1,102 @@
+#ifndef CPP_EXTRACT_NODE_TREE_H
+#define CPP_EXTRACT_NODE_TREE_H
+
+#include "diagnostics.h"
+#include "node.h"
+#include "identifier-lookup.h"
+
+#include "../../source/compiler-core/slang-lexer.h"
+
+namespace CppExtract {
+using namespace Slang;
+
+class TypeSet : public RefObject
+{
+public:
+ /// This is the looked up name.
+ UnownedStringSlice m_macroName; ///< The name extracted from the macro SLANG_ABSTRACT_AST_CLASS -> AST
+
+ String m_typeName; ///< The enum type name associated with this type for AST it is ASTNode
+ String m_fileMark; ///< This 'mark' becomes of the output filename
+
+ List<ClassLikeNode*> m_baseTypes; ///< The base types for this type set
+};
+
+class SourceOrigin : public RefObject
+{
+public:
+
+ void addNode(Node* node)
+ {
+ if (auto classLike = as<ClassLikeNode>(node))
+ {
+ SLANG_ASSERT(classLike->m_origin == nullptr);
+ classLike->m_origin = this;
+ }
+
+ m_nodes.add(node);
+ }
+
+ SourceOrigin(SourceFile* sourceFile, const String& macroOrigin) :
+ m_sourceFile(sourceFile),
+ m_macroOrigin(macroOrigin)
+ {}
+
+ String m_macroOrigin; ///< The macro text is inserted into the macro to identify the origin. It is based on the filename
+ SourceFile* m_sourceFile; ///< The source file - also holds the path information
+
+ /// All of the nodes defined in this file in the order they were defined
+ /// Note that the same namespace may be listed multiple times.
+ List<RefPtr<Node> > m_nodes;
+};
+
+struct Options;
+class IdentifierLookup;
+
+/* NodeTree holds nodes that have been parsed into a tree rooted on the 'rootNode'.
+Also contains other state associated with or useful to a node tree */
+class NodeTree
+{
+public:
+ friend class Parser;
+ /// Get all of the parsed source origins
+ const List<RefPtr<SourceOrigin> >& getSourceOrigins() const { return m_sourceOrigins; }
+
+ TypeSet* getTypeSet(const UnownedStringSlice& slice);
+ TypeSet* getOrAddTypeSet(const UnownedStringSlice& slice);
+
+ SourceOrigin* addSourceOrigin(SourceFile* sourceFile, const Options& options);
+
+ /// Get all of the type sets
+ const List<RefPtr<TypeSet>>& getTypeSets() const { return m_typeSets; }
+
+ /// Get the root node
+ Node* getRootNode() const { return m_rootNode; }
+
+ /// When parsing we don't lookup all up super types/add derived types. This is because
+ /// we allow files to be processed in any order, so we have to do the type lookup as a separate operation
+ SlangResult calcDerivedTypes(DiagnosticSink* sink);
+
+ NodeTree(StringSlicePool* typePool, NamePool* namePool, IdentifierLookup* identifierLookup);
+
+ static String calcMacroOrigin(const String& filePath, const Options& options);
+
+protected:
+ SlangResult _calcDerivedTypesRec(ScopeNode* node, DiagnosticSink* sink);
+
+ StringSlicePool m_typeSetPool; ///< Pool for type set names
+ List<RefPtr<TypeSet> > m_typeSets; ///< The type sets
+
+ IdentifierLookup* m_identifierLookup;
+ StringSlicePool* m_typePool; ///< Pool for just types
+
+ NamePool* m_namePool;
+
+ RefPtr<ScopeNode> m_rootNode; ///< The root scope
+
+ List<RefPtr<SourceOrigin>> m_sourceOrigins;
+};
+
+} // CppExtract
+
+#endif
diff --git a/tools/slang-cpp-extractor/node.cpp b/tools/slang-cpp-extractor/node.cpp
index 3e259a81e..3b2403816 100644
--- a/tools/slang-cpp-extractor/node.cpp
+++ b/tools/slang-cpp-extractor/node.cpp
@@ -1,14 +1,29 @@
#include "node.h"
+#include "file-util.h"
+
+#include "../../source/core/slang-string-util.h"
+
namespace CppExtract {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Node Impl !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-static void _indent(Index indentCount, StringBuilder& out)
+SLANG_FORCE_INLINE static void _indent(Index indentCount, StringBuilder& out) { FileUtil::indent(indentCount, out); }
+
+ScopeNode* Node::getRootScope()
{
- for (Index i = 0; i < indentCount; ++i)
+ if (m_parentScope)
{
- out << CPP_EXTRACT_INDENT_STRING;
+ ScopeNode* scope = m_parentScope;
+ while (scope->m_parentScope)
+ {
+ scope = scope->m_parentScope;
+ }
+ return scope;
+ }
+ else
+ {
+ return as<ScopeNode>(this);
}
}
@@ -77,29 +92,158 @@ void Node::calcAbsoluteName(StringBuilder& outName) const
}
}
-/* static */Node* Node::findNode(ScopeNode* scope, const UnownedStringSlice& name)
+/* static */Node* Node::lookupNameInScope(ScopeNode* scope, const UnownedStringSlice& name)
{
- // TODO(JS): We may want to lookup based on the path.
- // If the name is qualified, we give up for not
- if (String(name).indexOf("::") >= 0)
+ // TODO(JS): Doesn't handle 'using namespace'.
+
+ // Must be unqualified name
+ SLANG_ASSERT(name.indexOf(UnownedStringSlice::fromLiteral("::")) < 0);
+
+ Node* childNode = scope->findChild(name);
+ if (childNode)
{
- return nullptr;
+ return childNode;
}
- // Okay try in all scopes up to the root
- while (scope)
+ // If we have an anonymous namespace in this scope, try looking up in there..
+ if (scope->m_anonymousNamespace)
{
- if (Node* node = scope->findChild(name))
+ Node* childNode = scope->m_anonymousNamespace->findChild(name);
+ if (childNode)
+ {
+ return childNode;
+ }
+ }
+
+ // I could have an enum (that's not an enum class)
+ for (Node* node : scope->m_children)
+ {
+ EnumNode* enumNode = as<EnumNode>(node);
+ if (enumNode && enumNode->m_type == Node::Type::Enum)
+ {
+ Node** nodePtr = enumNode->m_childMap.TryGetValue(name);
+ if (nodePtr)
+ {
+ return *nodePtr;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+/* static */Node* Node::lookupFromScope(ScopeNode* scope, const UnownedStringSlice* parts, Index partsCount)
+{
+ SLANG_ASSERT(partsCount > 0);
+ if (partsCount == 1)
+ {
+ return lookupNameInScope(scope, parts[0]);
+ }
+
+ for (Index i = 0; i < partsCount; ++i)
+ {
+ const UnownedStringSlice& part = parts[i];
+
+ Node* node = lookupNameInScope(scope, part);
+ if (node == nullptr)
+ {
+ return node;
+ }
+ // If at end, then we are done
+ if (i == partsCount - 1)
{
return node;
}
- scope = scope->m_parentScope;
+ // If there are more elements, then node must be some kind of scope,
+ // if we are going to find it
+ scope = as<ScopeNode>(node);
+ if (scope == nullptr)
+ {
+ break;
+ }
}
return nullptr;
}
+/* static */void Node::splitPath(const UnownedStringSlice& inPath, List<UnownedStringSlice>& outParts)
+{
+ if (inPath.indexOf(UnownedStringSlice::fromLiteral("::")) >= 0)
+ {
+ StringUtil::split(inPath, UnownedStringSlice::fromLiteral("::"), outParts);
+ // Remove any whitespace
+ for (auto& part : outParts)
+ {
+ part = part.trim();
+ }
+ }
+ else
+ {
+ outParts.clear();
+ outParts.add(inPath.trim());
+ }
+}
+
+/* static */Node* Node::lookupFromScope(ScopeNode* scope, const UnownedStringSlice& inPath)
+{
+ if (inPath.indexOf(UnownedStringSlice::fromLiteral("::")) >= 0)
+ {
+ List<UnownedStringSlice> parts;
+ splitPath(inPath, parts);
+
+ return lookupFromScope(scope, parts.getBuffer(), parts.getCount());
+ }
+ else
+ {
+ return lookupNameInScope(scope, inPath);
+ }
+}
+
+/* static */Node* Node::lookup(ScopeNode* scope, const UnownedStringSlice& inPath)
+{
+ if (inPath.indexOf(UnownedStringSlice::fromLiteral("::")) >= 0)
+ {
+ List<UnownedStringSlice> parts;
+ splitPath(inPath, parts);
+
+ if (parts[0].getLength() == 0)
+ {
+ // It's a lookup from global scope
+ ScopeNode* rootScope = scope->getRootScope();
+ return lookupFromScope(rootScope, parts.getBuffer() + 1, parts.getCount() + 1);
+ }
+
+ // Okay lets try a lookup from each scope up to the global scope
+ while (scope)
+ {
+ Node* node = lookupFromScope(scope, parts.getBuffer(), parts.getCount());
+ if (node)
+ {
+ return node;
+ }
+
+ scope = scope->m_parentScope;
+ }
+ }
+ else
+ {
+ while (scope)
+ {
+ // Lookup in this scope
+ Node* node = lookupNameInScope(scope, inPath);
+ if (node)
+ {
+ return node;
+ }
+
+ // Try parent scope
+ scope = scope->m_parentScope;
+ }
+ }
+
+ return nullptr;
+}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ScopeNode !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -133,7 +277,14 @@ void ScopeNode::addChild(Node* child)
Node* ScopeNode::findChild(const UnownedStringSlice& name) const
{
Node** nodePtr = m_childMap.TryGetValue(name);
- return (nodePtr) ? *nodePtr : nullptr;
+ if (nodePtr)
+ {
+ return *nodePtr;
+ }
+
+
+
+ return nullptr;
}
void ScopeNode::calcScopeDepthFirst(List<Node*>& outNodes)
@@ -178,6 +329,85 @@ void ScopeNode::dump(int indentCount, StringBuilder& out)
out << "}\n";
}
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! EnumCaseNode !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void EnumCaseNode::dump(int indent, StringBuilder& out)
+{
+ if (isReflected())
+ {
+ _indent(indent, out);
+ out << m_name.getContent();
+
+ if (m_value.type != TokenType::Invalid)
+ {
+ out << " = ";
+ out << m_value.getContent();
+ }
+
+ out << ",\n";
+ }
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! EnumNode !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void TypeDefNode::dump(int indent, StringBuilder& out)
+{
+ if (isReflected())
+ {
+ _indent(indent, out);
+
+ out << "typedef ";
+
+ for (auto& tok : m_targetTypeTokens)
+ {
+ out << tok.getContent() << " ";
+ }
+
+ out << m_name.getContent() << ";\n";
+ }
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! EnumNode !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void EnumNode::dump(int indent, StringBuilder& out)
+{
+ if (!isReflected())
+ {
+ return;
+ }
+
+ _indent(indent, out);
+
+ out << "enum ";
+
+ if (m_type == Type::EnumClass)
+ {
+ out << "class ";
+ }
+
+ if (m_name.type != TokenType::Invalid)
+ {
+ out << m_name.getContent();
+ }
+
+ if (m_backingToken.type != TokenType::Invalid)
+ {
+ out << " : " << m_backingToken.getContent();
+ }
+
+ out << "\n";
+ _indent(indent, out);
+ out << "{\n";
+
+ for (Node* child : m_children)
+ {
+ child->dump(indent + 1, out);
+ }
+
+ _indent(indent, out);
+ out << "}\n";
+}
+
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! FieldNode !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
void FieldNode::dump(int indent, StringBuilder& out)
diff --git a/tools/slang-cpp-extractor/node.h b/tools/slang-cpp-extractor/node.h
index c98a9204d..c741024e4 100644
--- a/tools/slang-cpp-extractor/node.h
+++ b/tools/slang-cpp-extractor/node.h
@@ -28,10 +28,18 @@ public:
StructType,
ClassType,
+ Enum,
+ EnumClass,
+
Namespace,
AnonymousNamespace,
Field,
+ EnumCase,
+
+ TypeDef,
+
+ CountOf,
};
enum class TypeRange
@@ -41,10 +49,29 @@ public:
ClassLikeStart = int(Type::StructType),
ClassLikeEnd = int(Type::ClassType),
+
+ EnumStart = int(Type::Enum),
+ EnumEnd = int(Type::EnumClass),
};
static bool isScopeType(Type type) { return int(type) >= int(TypeRange::ScopeStart) && int(type) <= int(TypeRange::ScopeEnd); }
static bool isClassLikeType(Type type) { return int(type) >= int(TypeRange::ClassLikeStart) && int(type) <= int(TypeRange::ClassLikeEnd); }
+ static bool isEnumLikeType(Type type) { return int(type) >= int(TypeRange::EnumStart) && int(type) <= int(TypeRange::EnumEnd); }
+ static bool canAcceptTypes(Type type)
+ {
+ switch (type)
+ {
+ case Type::StructType:
+ case Type::ClassType:
+ case Type::Namespace:
+ case Type::AnonymousNamespace:
+ {
+ return true;
+ }
+ default: break;
+ }
+ return false;
+ }
static bool isType(Type type) { return true; }
@@ -67,6 +94,8 @@ public:
/// True if reflected
bool isReflected() const { return m_reflectionType == ReflectionType::Reflected; }
+ ScopeNode* getRootScope();
+
typedef bool(*Filter)(Node* node);
static bool isClassLikeAndReflected(Node* node) { return node->isClassLike() && node->isReflected(); }
@@ -79,8 +108,19 @@ public:
static void calcScopePath(Node* node, List<Node*>& outPath);
- /// Find the name starting in specified scope
- static Node* findNode(ScopeNode* scope, const UnownedStringSlice& name);
+ /// Lookup a name in just the specified scope
+ /// Handles anonymous namespaces, or name lookups that are in the parents space
+ static Node* lookupNameInScope(ScopeNode* scope, const UnownedStringSlice& name);
+
+ /// Lookup from a path
+ static Node* lookupFromScope(ScopeNode* scope, const UnownedStringSlice* path, Index pathCount);
+ /// Looks up *just* from the specified scope.
+ static Node* lookupFromScope(ScopeNode* scope, const UnownedStringSlice& slice);
+
+ /// Look up name (which can contain ::)
+ static Node* lookup(ScopeNode* scope, const UnownedStringSlice& name);
+
+ static void splitPath(const UnownedStringSlice& slice, List<UnownedStringSlice>& outSplitPath);
Node(Type type) :
m_type(type),
@@ -108,6 +148,8 @@ struct ScopeNode : public Node
/// True if can accept fields (class like types can)
bool acceptsFields() const { return isClassLike(); }
+ /// True if the scope can accept types
+ bool acceptsTypes() const { return canAcceptTypes(m_type); }
/// Gets the reflection for any contained types
ReflectionType getContainedReflectionType() const { return m_reflectionType == ReflectionType::NotReflected ? ReflectionType::NotReflected : m_reflectionOverride; }
@@ -210,12 +252,56 @@ struct ClassLikeNode : public ScopeNode
ClassLikeNode* m_superNode; ///< If this is a class/struct, the type it is derived from (or nullptr if base)
};
+struct EnumCaseNode : public Node
+{
+ typedef Node Super;
+
+ static bool isType(Type type) { return type == Type::EnumCase; }
+
+ virtual void dump(int indent, StringBuilder& out) SLANG_OVERRIDE;
+
+ EnumCaseNode():
+ Super(Type::EnumCase)
+ {
+ }
+
+ Token m_value; ///< If not defined will be invalid
+};
+
+struct EnumNode : public ScopeNode
+{
+ typedef ScopeNode Super;
+ static bool isType(Type type) { return isEnumLikeType(type); }
+
+ virtual void dump(int indent, StringBuilder& out) SLANG_OVERRIDE;
+
+ EnumNode(Type type):
+ Super(type)
+ {
+ SLANG_ASSERT(isEnumLikeType(type));
+ }
+
+ Token m_backingToken;
+};
+
+struct TypeDefNode : public Node
+{
+ typedef Node Super;
+ static bool isType(Type type) { return type == Type::TypeDef; }
+
+ virtual void dump(int indent, StringBuilder& out) SLANG_OVERRIDE;
+
+ TypeDefNode():
+ Super(Type::TypeDef)
+ {
+ }
+
+ List<Token> m_targetTypeTokens;
+};
+
template <typename T>
T* as(Node* node) { return (node && T::isType(node->m_type)) ? static_cast<T*>(node) : nullptr; }
-// A macro to define a single indent as a string
-#define CPP_EXTRACT_INDENT_STRING " "
-
} // CppExtract
#endif
diff --git a/tools/slang-cpp-extractor/options.cpp b/tools/slang-cpp-extractor/options.cpp
index c7b6a9df5..9ec671652 100644
--- a/tools/slang-cpp-extractor/options.cpp
+++ b/tools/slang-cpp-extractor/options.cpp
@@ -81,8 +81,7 @@ SlangResult OptionsParser::parse(int argc, const char*const* argv, DiagnosticSin
}
else if (arg == "-dump")
{
- outOptions.m_dump = true;
- m_index++;
+ SLANG_RETURN_ON_FAIL(_parseArgFlag("-dump", outOptions.m_dump));
continue;
}
else if (arg == "-mark-prefix")
@@ -110,13 +109,18 @@ SlangResult OptionsParser::parse(int argc, const char*const* argv, DiagnosticSin
SLANG_RETURN_ON_FAIL(_parseArgWithValue("-strip-prefix", outOptions.m_stripFilePrefix));
continue;
}
+ else if (arg == "-unit-test")
+ {
+ SLANG_RETURN_ON_FAIL(_parseArgFlag("-unit-test", outOptions.m_runUnitTests));
+ continue;
+ }
m_sink->diagnose(SourceLoc(), CPPDiagnostics::unknownOption, arg);
return SLANG_FAIL;
}
else
{
- // If it starts with - then it an unknown option
+ // If it doesn't start with - then it's assumed to be an input path
outOptions.m_inputPaths.add(arg);
m_index++;
}
diff --git a/tools/slang-cpp-extractor/options.h b/tools/slang-cpp-extractor/options.h
index ff88a3974..14ef9ecfc 100644
--- a/tools/slang-cpp-extractor/options.h
+++ b/tools/slang-cpp-extractor/options.h
@@ -24,6 +24,7 @@ struct Options
bool m_defs = false; ///< If set will output a '-defs.h' file for each of the input files, that corresponds to previous defs files (although doesn't have fields/RAW)
bool m_dump = false; ///< If true will dump to stderr the types/fields and hierarchy it extracted
+ bool m_runUnitTests = false; ///< If true will run internal unit tests
bool m_outputFields = false; ///< When dumping macros also dump field definitions
diff --git a/tools/slang-cpp-extractor/parser.cpp b/tools/slang-cpp-extractor/parser.cpp
index 0d2cc31d2..9a2227b21 100644
--- a/tools/slang-cpp-extractor/parser.cpp
+++ b/tools/slang-cpp-extractor/parser.cpp
@@ -10,12 +10,46 @@
namespace CppExtract {
using namespace Slang;
-// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPExtractor !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// If fails then we need more bits to identify types
+SLANG_COMPILE_TIME_ASSERT(int(Node::Type::CountOf) <= 8 * sizeof(uint32_t));
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Parser !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Parser::Parser(NodeTree* nodeTree, DiagnosticSink* sink) :
m_sink(sink),
- m_nodeTree(nodeTree)
+ m_nodeTree(nodeTree),
+ m_nodeTypeEnabled(0)
{
+ // Enable types by default
+ const Node::Type defaultEnabled[] =
+ {
+ Node::Type::ClassType,
+ Node::Type::StructType,
+ Node::Type::Namespace,
+ Node::Type::AnonymousNamespace,
+ Node::Type::Field,
+ };
+ setTypesEnabled(defaultEnabled, SLANG_COUNT_OF(defaultEnabled));
+}
+
+void Parser::setTypeEnabled(Node::Type type, bool isEnabled )
+{
+ if (isEnabled)
+ {
+ m_nodeTypeEnabled |= (NodeTypeBitType(1) << int(type));
+ }
+ else
+ {
+ m_nodeTypeEnabled &= ~(NodeTypeBitType(1) << int(type));
+ }
+}
+
+void Parser::setTypesEnabled(const Node::Type* types, Index typesCount, bool isEnabled)
+{
+ for (Index i = 0; i < typesCount; ++i)
+ {
+ setTypeEnabled(types[i], isEnabled);
+ }
}
bool Parser::_isMarker(const UnownedStringSlice& name)
@@ -163,6 +197,31 @@ SlangResult Parser::popScope()
return SLANG_OK;
}
+SlangResult Parser::_maybeConsumeScope()
+{
+ // Look for either ; or { to open scope
+ while (true)
+ {
+ const TokenType type = m_reader.peekTokenType();
+ if (type == TokenType::Semicolon)
+ {
+ m_reader.advanceToken();
+ return SLANG_OK;
+ }
+ else if (type == TokenType::LBrace)
+ {
+ m_reader.advanceToken();
+ return consumeToClosingBrace();
+ }
+ else if (type == TokenType::EndOfFile)
+ {
+ return SLANG_OK;
+ }
+
+ m_reader.advanceToken();
+ }
+}
+
SlangResult Parser::consumeToClosingBrace(const Token* inOpenBraceToken)
{
Token openToken;
@@ -204,6 +263,165 @@ SlangResult Parser::consumeToClosingBrace(const Token* inOpenBraceToken)
}
}
+
+SlangResult Parser::_parseEnum()
+{
+ // We are looking for
+ // enum ([class name] | [name]) [: base] ( { | ; )
+
+ Token enumToken;
+
+ // consume enum
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &enumToken));
+
+ if (!m_currentScope->acceptsTypes())
+ {
+ m_sink->diagnose(enumToken.loc, CPPDiagnostics::cannotDeclareTypeInScope);
+ return SLANG_FAIL;
+ }
+
+ Node::Type type = Node::Type::Enum;
+
+ Token nameToken;
+ if (advanceIfToken(TokenType::Identifier, &nameToken))
+ {
+ const IdentifierStyle style = m_nodeTree->m_identifierLookup->get(nameToken.getContent());
+
+ if (style == IdentifierStyle::Class)
+ {
+ type = Node::Type::EnumClass;
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &nameToken));
+ }
+ else if (style == IdentifierStyle::None)
+ {
+ // It holds the name then
+ }
+ else
+ {
+ m_sink->diagnose(nameToken.loc, CPPDiagnostics::expectingIdentifier, nameToken.getContent());
+ return SLANG_FAIL;
+ }
+ }
+
+ RefPtr<EnumNode> node = new EnumNode(type);
+ node->m_name = nameToken;
+
+ if (advanceIfToken(TokenType::Colon))
+ {
+ // We may have tokens up to { or ;
+ List<Token> backingTokens;
+
+ while (true)
+ {
+ TokenType tokenType = m_reader.peekTokenType();
+ if (tokenType == TokenType::Semicolon ||
+ tokenType == TokenType::LBrace ||
+ tokenType == TokenType::EndOfFile)
+ {
+ break;
+ }
+
+ backingTokens.add(m_reader.advanceToken());
+ }
+
+ // TODO - Look up the backing type. It can only be an integral. We can assume it must be defined before lookup
+ // for our uses here.
+ // If we can't find the type, we could assume it's size is undefined
+
+ if (backingTokens.getCount() == 1)
+ {
+ node->m_backingToken = backingTokens[0];
+ }
+ }
+
+ pushScope(node);
+
+ if (advanceIfToken(TokenType::Semicolon))
+ {
+ if (nameToken.type != TokenType::Invalid)
+ {
+ Node* node = m_currentScope->findChild(nameToken.getContent());
+ if (node)
+ {
+ // Strictly speaking we should check the backing type etc, match, but for now ignore and assume it's ok
+
+ if (node->m_type == type)
+ {
+ return SLANG_OK;
+ }
+ m_sink->diagnose(nameToken.loc, CPPDiagnostics::typeAlreadyDeclared, nameToken.getContent());
+ return SLANG_FAIL;
+ }
+ return popScope();
+ }
+ }
+
+ SLANG_RETURN_ON_FAIL(expect(TokenType::LBrace));
+
+ while (true)
+ {
+ TokenType tokenType = m_reader.peekTokenType();
+ if (tokenType == TokenType::RBrace)
+ {
+ break;
+ }
+
+ RefPtr<EnumCaseNode> caseNode(new EnumCaseNode);
+
+ // We could also check if the name is a valid identifier for name, for now just assume.
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &caseNode->m_name));
+
+ if (node->findChild(caseNode->m_name.getContent()))
+ {
+ m_sink->diagnose(caseNode->m_name.loc, CPPDiagnostics::identifierAlreadyDefined, caseNode->m_name.getContent());
+ return SLANG_FAIL;
+ }
+
+ // Add the value
+ node->addChild(caseNode);
+
+ // TODO(JS):
+ // This could be better. We could lookup the value etc. For now just assume only one token is valid.
+
+ if (advanceIfToken(TokenType::OpAssign))
+ {
+ List<Token> valueTokens;
+ // Consume up to } or ,
+ while (true)
+ {
+ TokenType assignType = m_reader.peekTokenType();
+
+ if (assignType == TokenType::Comma ||
+ assignType == TokenType::RBrace ||
+ assignType == TokenType::EndOfFile)
+ {
+ break;
+ }
+ valueTokens.add(m_reader.advanceToken());
+ }
+
+ if (valueTokens.getCount() == 1)
+ {
+ caseNode->m_value = valueTokens[0];
+ }
+ }
+
+ tokenType = m_reader.peekTokenType();
+ if (tokenType == TokenType::Comma)
+ {
+ m_reader.advanceToken();
+ continue;
+ }
+
+ break;
+ }
+
+ SLANG_RETURN_ON_FAIL(expect(TokenType::RBrace));
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Semicolon));
+
+ return popScope();
+}
+
SlangResult Parser::_maybeParseNode(Node::Type type)
{
// We are looking for
@@ -234,6 +452,10 @@ SlangResult Parser::_maybeParseNode(Node::Type type)
// Just ignore it then
return SLANG_OK;
}
+ else if (Node::isEnumLikeType(type))
+ {
+ return _parseEnum();
+ }
// Must be class | struct
@@ -407,8 +629,7 @@ SlangResult Parser::_maybeParseTemplateArg(Index& ioTemplateDepth)
{
case TokenType::Identifier:
{
- UnownedStringSlice name;
- SLANG_RETURN_ON_FAIL(_maybeParseType(name, ioTemplateDepth));
+ SLANG_RETURN_ON_FAIL(_maybeParseType(ioTemplateDepth));
return SLANG_OK;
}
case TokenType::IntegerLiteral:
@@ -548,10 +769,8 @@ UnownedStringSlice Parser::_concatTokens(TokenReader::ParsingCursor start)
return typePool->getSlice(typePool->add(buf));
}
-SlangResult Parser::_maybeParseType(UnownedStringSlice& outType, Index& ioTemplateDepth)
+SlangResult Parser::_maybeParseType(Index& ioTemplateDepth)
{
- auto startCursor = m_reader.getCursor();
-
_consumeTypeModifiers();
advanceIfToken(TokenType::Scope);
@@ -601,15 +820,43 @@ SlangResult Parser::_maybeParseType(UnownedStringSlice& outType, Index& ioTempla
break;
}
- // We can build up the out type, from the tokens we found
- outType = _concatTokens(startCursor);
+ return SLANG_OK;
+}
+
+SlangResult Parser::_maybeParseType(List<Token>& outToks)
+{
+ auto startCursor = m_reader.getCursor();
+
+ Index templateDepth = 0;
+ SlangResult res = _maybeParseType(templateDepth);
+ if (SLANG_FAILED(res) && m_sink->getErrorCount())
+ {
+ return res;
+ }
+
+ if (templateDepth != 0)
+ {
+ m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::unexpectedTemplateClose);
+ return SLANG_FAIL;
+ }
+
+ auto endCursor = m_reader.getCursor();
+ m_reader.setCursor(startCursor);
+
+ while (!m_reader.isAtCursor(endCursor))
+ {
+ outToks.add(m_reader.advanceToken());
+ }
+
return SLANG_OK;
}
SlangResult Parser::_maybeParseType(UnownedStringSlice& outType)
{
+ auto startCursor = m_reader.getCursor();
+
Index templateDepth = 0;
- SlangResult res = _maybeParseType(outType, templateDepth);
+ SlangResult res = _maybeParseType(templateDepth);
if (SLANG_FAILED(res) && m_sink->getErrorCount())
{
return res;
@@ -620,6 +867,9 @@ SlangResult Parser::_maybeParseType(UnownedStringSlice& outType)
m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::unexpectedTemplateClose);
return SLANG_FAIL;
}
+
+ // We can build up the out type, from the tokens we found
+ outType = _concatTokens(startCursor);
return SLANG_OK;
}
@@ -710,6 +960,45 @@ SlangResult Parser::_parseBalanced(DiagnosticSink* sink)
}
}
+SlangResult Parser::_parseTypeDef()
+{
+ if (!m_currentScope->acceptsTypes())
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotDeclareTypeInScope);
+ return SLANG_FAIL;
+ }
+
+ // Consume the typedef
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier));
+
+ // Parse the type
+ List<Token> toks;
+ SLANG_RETURN_ON_FAIL(_maybeParseType(toks));
+
+ Token name;
+
+ // Get the name
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &name));
+
+ if (Node::lookupNameInScope(m_currentScope, name.getContent()))
+ {
+ m_sink->diagnose(name.loc, CPPDiagnostics::identifierAlreadyDefined, name.getContent());
+ return SLANG_FAIL;
+ }
+
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Semicolon));
+
+ RefPtr<TypeDefNode> node = new TypeDefNode;
+ node->m_name = name;
+
+ // Set what aliases too
+ node->m_targetTypeTokens.swapWith(toks);
+
+ m_currentScope->addChild(node);
+
+ return SLANG_OK;
+}
+
SlangResult Parser::_maybeParseField()
{
// Can only add a field if we are in a class
@@ -793,9 +1082,11 @@ SlangResult Parser::_maybeParseField()
{
switch (style)
{
- case IdentifierStyle::Class: return Node::Type::ClassType;
- case IdentifierStyle::Struct: return Node::Type::StructType;
- case IdentifierStyle::Namespace: return Node::Type::Namespace;
+ case IdentifierStyle::Class: return Node::Type::ClassType;
+ case IdentifierStyle::Struct: return Node::Type::StructType;
+ case IdentifierStyle::Namespace: return Node::Type::Namespace;
+ case IdentifierStyle::Enum: return Node::Type::Enum;
+ case IdentifierStyle::TypeDef: return Node::Type::TypeDef;
default: return Node::Type::Invalid;
}
}
@@ -914,14 +1205,6 @@ SlangResult Parser::parse(SourceOrigin* sourceOrigin, const Options* options)
SLANG_ASSERT(options);
m_options = options;
-#if 0
- // Calculate from the path, a 'macro origin' name.
- const String macroOrigin = calcMacroOrigin(sourceFile->getPathInfo().foundPath, *options);
-
- RefPtr<SourceOrigin> origin = new SourceOrigin(sourceFile, macroOrigin);
- m_sourceOrigins.add(origin);
-#endif
-
// Set the current origin
m_sourceOrigin = sourceOrigin;
@@ -989,6 +1272,19 @@ SlangResult Parser::parse(SourceOrigin* sourceOrigin, const Options* options)
SLANG_RETURN_ON_FAIL(expect(TokenType::Colon));
break;
}
+ case IdentifierStyle::TypeDef:
+ {
+ if (isTypeEnabled(Node::Type::TypeDef))
+ {
+ SLANG_RETURN_ON_FAIL(_parseTypeDef());
+ }
+ else
+ {
+ m_reader.advanceToken();
+ SLANG_RETURN_ON_FAIL(_consumeToSync());
+ }
+ break;
+ }
default:
{
IdentifierFlags flags = getFlags(style);
@@ -996,7 +1292,16 @@ SlangResult Parser::parse(SourceOrigin* sourceOrigin, const Options* options)
if (flags & IdentifierFlag::StartScope)
{
Node::Type type = _toNodeType(style);
- SLANG_RETURN_ON_FAIL(_maybeParseNode(type));
+ SLANG_ASSERT(type != Node::Type::Invalid);
+
+ if (isTypeEnabled(type))
+ {
+ SLANG_RETURN_ON_FAIL(_maybeParseNode(type));
+ }
+ else
+ {
+ SLANG_RETURN_ON_FAIL(_maybeConsumeScope());
+ }
}
else
{
@@ -1065,150 +1370,4 @@ SlangResult Parser::parse(SourceOrigin* sourceOrigin, const Options* options)
}
}
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ParseSharedState !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
-
-NodeTree::NodeTree(StringSlicePool* typePool, NamePool* namePool, IdentifierLookup* identifierLookup):
- m_typePool(typePool),
- m_namePool(namePool),
- m_identifierLookup(identifierLookup),
- m_typeSetPool(StringSlicePool::Style::Empty)
-{
- m_rootNode = new ScopeNode(Node::Type::Namespace);
- m_rootNode->m_reflectionType = ReflectionType::Reflected;
-}
-
-TypeSet* NodeTree::getTypeSet(const UnownedStringSlice& slice)
-{
- Index index = m_typeSetPool.findIndex(slice);
- if (index < 0)
- {
- return nullptr;
- }
- return m_typeSets[index];
-}
-
-TypeSet* NodeTree::getOrAddTypeSet(const UnownedStringSlice& slice)
-{
- const Index index = Index(m_typeSetPool.add(slice));
- if (index >= m_typeSets.getCount())
- {
- SLANG_ASSERT(m_typeSets.getCount() == index);
- TypeSet* typeSet = new TypeSet;
-
- m_typeSets.add(typeSet);
- typeSet->m_macroName = m_typeSetPool.getSlice(StringSlicePool::Handle(index));
- return typeSet;
- }
- else
- {
- return m_typeSets[index];
- }
-}
-
-SourceOrigin* NodeTree::addSourceOrigin(SourceFile* sourceFile, const Options& options)
-{
- // Calculate from the path, a 'macro origin' name.
- const String macroOrigin = calcMacroOrigin(sourceFile->getPathInfo().foundPath, options);
-
- SourceOrigin* origin = new SourceOrigin(sourceFile, macroOrigin);
- m_sourceOrigins.add(origin);
- return origin;
-}
-
-/* static */String NodeTree::calcMacroOrigin(const String& filePath, const Options& options)
-{
- // Get the filename without extension
- String fileName = Path::getFileNameWithoutExt(filePath);
-
- // We can work on just the slice
- UnownedStringSlice slice = fileName.getUnownedSlice();
-
- // Filename prefix
- if (options.m_stripFilePrefix.getLength() && slice.startsWith(options.m_stripFilePrefix.getUnownedSlice()))
- {
- const Index len = options.m_stripFilePrefix.getLength();
- slice = UnownedStringSlice(slice.begin() + len, slice.end());
- }
-
- // Trim -
- slice = slice.trim('-');
-
- StringBuilder out;
- NameConventionUtil::convert(slice, CharCase::Upper, NameConvention::Snake, out);
- return out;
-}
-
-SlangResult NodeTree::_calcDerivedTypesRec(ScopeNode* inScopeNode, DiagnosticSink* sink)
-{
- if (inScopeNode->isClassLike())
- {
- ClassLikeNode* classLikeNode = static_cast<ClassLikeNode*>(inScopeNode);
-
- if (classLikeNode->m_super.hasContent())
- {
- ScopeNode* parentScope = classLikeNode->m_parentScope;
- if (parentScope == nullptr)
- {
- sink->diagnoseRaw(Severity::Error, UnownedStringSlice::fromLiteral("Can't lookup in scope if there is none!"));
- return SLANG_FAIL;
- }
-
- Node* superNode = Node::findNode(parentScope, classLikeNode->m_super.getContent());
-
- if (!superNode)
- {
- if (classLikeNode->isReflected())
- {
- sink->diagnose(classLikeNode->m_name, CPPDiagnostics::superTypeNotFound, classLikeNode->getAbsoluteName());
- return SLANG_FAIL;
- }
- }
- else
- {
- ClassLikeNode* superType = as<ClassLikeNode>(superNode);
-
- if (!superType)
- {
- sink->diagnose(classLikeNode->m_name, CPPDiagnostics::superTypeNotAType, classLikeNode->getAbsoluteName());
- return SLANG_FAIL;
- }
-
- if (superType->m_typeSet != classLikeNode->m_typeSet)
- {
- sink->diagnose(classLikeNode->m_name, CPPDiagnostics::typeInDifferentTypeSet, classLikeNode->m_name.getContent(), classLikeNode->m_typeSet->m_macroName, superType->m_typeSet->m_macroName);
- return SLANG_FAIL;
- }
-
- // The base class must be defined in same scope (as we didn't allow different scopes for base classes)
- superType->addDerived(classLikeNode);
- }
- }
- else
- {
- // Add to it's own typeset
- if (classLikeNode->isReflected())
- {
- classLikeNode->m_typeSet->m_baseTypes.add(classLikeNode);
- }
- }
- }
-
- for (Node* child : inScopeNode->m_children)
- {
- ScopeNode* childScope = as<ScopeNode>(child);
- if (childScope)
- {
- SLANG_RETURN_ON_FAIL(_calcDerivedTypesRec(childScope, sink));
- }
- }
-
- return SLANG_OK;
-}
-
-SlangResult NodeTree::calcDerivedTypes(DiagnosticSink* sink)
-{
- return _calcDerivedTypesRec(m_rootNode, sink);
-}
-
-
} // namespace CppExtract
diff --git a/tools/slang-cpp-extractor/parser.h b/tools/slang-cpp-extractor/parser.h
index 58a13c19f..f0febb728 100644
--- a/tools/slang-cpp-extractor/parser.h
+++ b/tools/slang-cpp-extractor/parser.h
@@ -4,103 +4,19 @@
#include "diagnostics.h"
#include "node.h"
#include "identifier-lookup.h"
+#include "node-tree.h"
#include "../../source/compiler-core/slang-lexer.h"
namespace CppExtract {
using namespace Slang;
-class TypeSet : public RefObject
-{
-public:
- /// This is the looked up name.
- UnownedStringSlice m_macroName; ///< The name extracted from the macro SLANG_ABSTRACT_AST_CLASS -> AST
-
- String m_typeName; ///< The enum type name associated with this type for AST it is ASTNode
- String m_fileMark; ///< This 'mark' becomes of the output filename
-
- List<ClassLikeNode*> m_baseTypes; ///< The base types for this type set
-};
-
-class SourceOrigin : public RefObject
-{
-public:
-
- void addNode(Node* node)
- {
- if (auto classLike = as<ClassLikeNode>(node))
- {
- SLANG_ASSERT(classLike->m_origin == nullptr);
- classLike->m_origin = this;
- }
-
- m_nodes.add(node);
- }
-
- SourceOrigin(SourceFile* sourceFile, const String& macroOrigin) :
- m_sourceFile(sourceFile),
- m_macroOrigin(macroOrigin)
- {}
-
- String m_macroOrigin; ///< The macro text is inserted into the macro to identify the origin. It is based on the filename
- SourceFile* m_sourceFile; ///< The source file - also holds the path information
-
- /// All of the nodes defined in this file in the order they were defined
- /// Note that the same namespace may be listed multiple times.
- List<RefPtr<Node> > m_nodes;
-};
-
-struct Options;
-class IdentifierLookup;
-
-/* NodeTree holds nodes that have been parsed into a tree rooted on the 'rootNode'.
-Also contains other state associated with or useful to a node tree */
-class NodeTree
-{
-public:
- friend class Parser;
- /// Get all of the parsed source origins
- const List<RefPtr<SourceOrigin> >& getSourceOrigins() const { return m_sourceOrigins; }
-
- TypeSet* getTypeSet(const UnownedStringSlice& slice);
- TypeSet* getOrAddTypeSet(const UnownedStringSlice& slice);
-
- SourceOrigin* addSourceOrigin(SourceFile* sourceFile, const Options& options);
-
- /// Get all of the type sets
- const List<RefPtr<TypeSet>>& getTypeSets() const { return m_typeSets; }
-
- /// Get the root node
- Node* getRootNode() const { return m_rootNode; }
-
- /// When parsing we don't lookup all up super types/add derived types. This is because
- /// we allow files to be processed in any order, so we have to do the type lookup as a separate operation
- SlangResult calcDerivedTypes(DiagnosticSink* sink);
-
- NodeTree(StringSlicePool* typePool, NamePool* namePool, IdentifierLookup* identifierLookup);
-
- static String calcMacroOrigin(const String& filePath, const Options& options);
-
-protected:
- SlangResult _calcDerivedTypesRec(ScopeNode* node, DiagnosticSink* sink);
-
- StringSlicePool m_typeSetPool; ///< Pool for type set names
- List<RefPtr<TypeSet> > m_typeSets; ///< The type sets
-
- IdentifierLookup* m_identifierLookup;
- StringSlicePool* m_typePool; ///< Pool for just types
-
- NamePool* m_namePool;
-
- RefPtr<ScopeNode> m_rootNode; ///< The root scope
-
- List<RefPtr<SourceOrigin>> m_sourceOrigins;
-};
-
class Parser
{
public:
+ typedef uint32_t NodeTypeBitType;
+
SlangResult expect(TokenType type, Token* outToken = nullptr);
bool advanceIfMarker(Token* outToken = nullptr);
@@ -115,6 +31,10 @@ public:
/// Parse the contents of the source file
SlangResult parse(SourceOrigin* sourceOrigin, const Options* options);
+ void setTypeEnabled(Node::Type type, bool isEnabled = true);
+ bool isTypeEnabled(Node::Type type) { return (m_nodeTypeEnabled & (NodeTypeBitType(1) << int(type))) != 0; }
+ void setTypesEnabled(const Node::Type* types, Index typesCount, bool isEnabled = true);
+
Parser(NodeTree* nodeTree, DiagnosticSink* sink);
protected:
@@ -122,15 +42,21 @@ protected:
bool _isMarker(const UnownedStringSlice& name);
+ SlangResult _maybeConsumeScope();
+
SlangResult _parsePreDeclare();
SlangResult _parseTypeSet();
SlangResult _maybeParseNode(Node::Type type);
SlangResult _maybeParseField();
+ SlangResult _parseTypeDef();
+ SlangResult _parseEnum();
+
+ SlangResult _maybeParseType(List<Token>& outToks);
SlangResult _maybeParseType(UnownedStringSlice& outType);
- SlangResult _maybeParseType(UnownedStringSlice& outType, Index& ioTemplateDepth);
+ SlangResult _maybeParseType(Index& ioTemplateDepth);
SlangResult _maybeParseTemplateArgs(Index& ioTemplateDepth);
SlangResult _maybeParseTemplateArg(Index& ioTemplateDepth);
@@ -144,6 +70,8 @@ protected:
SlangResult _consumeToSync();
+ NodeTypeBitType m_nodeTypeEnabled;
+
TokenList m_tokenList;
TokenReader m_reader;
diff --git a/tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp b/tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp
deleted file mode 100644
index 7ec53e29f..000000000
--- a/tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp
+++ /dev/null
@@ -1,767 +0,0 @@
-// main.cpp
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "../../source/core/slang-secure-crt.h"
-
-#include "../../slang-com-helper.h"
-
-#include "../../source/core/slang-list.h"
-#include "../../source/core/slang-string.h"
-#include "../../source/core/slang-string-util.h"
-#include "../../source/core/slang-io.h"
-#include "../../source/core/slang-string-slice-pool.h"
-#include "../../source/core/slang-writer.h"
-#include "../../source/core/slang-file-system.h"
-
-#include "../../source/compiler-core/slang-source-loc.h"
-#include "../../source/compiler-core/slang-lexer.h"
-#include "../../source/compiler-core/slang-diagnostic-sink.h"
-#include "../../source/compiler-core/slang-name.h"
-#include "../../source/compiler-core/slang-name-convention-util.h"
-
-#include "node.h"
-#include "diagnostics.h"
-#include "options.h"
-#include "parser.h"
-
-/*
-Some command lines:
-
--d source/slang slang-ast-support-types.h slang-ast-base.h slang-ast-decl.h slang-ast-expr.h slang-ast-modifier.h slang-ast-stmt.h slang-ast-type.h slang-ast-val.h -strip-prefix slang- -o slang-generated -output-fields -mark-suffix _CLASS
-*/
-
-namespace CppExtract
-{
-
-using namespace Slang;
-
-static void _indent(Index indentCount, StringBuilder& out)
-{
- for (Index i = 0; i < indentCount; ++i)
- {
- out << CPP_EXTRACT_INDENT_STRING;
- }
-}
-
-// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPExtractorApp !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-class App
-{
-public:
-
- SlangResult readAllText(const Slang::String& fileName, String& outRead);
- SlangResult writeAllText(const Slang::String& fileName, const UnownedStringSlice& text);
-
- SlangResult execute(const Options& options);
-
- /// Execute
- SlangResult executeWithArgs(int argc, const char*const* argv);
-
- /// Write output
- SlangResult writeOutput(NodeTree* tree);
-
- /// Write def files
- SlangResult writeDefs(NodeTree* tree);
-
- /// Calculate the header
- SlangResult calcTypeHeader(NodeTree* tree, TypeSet* typeSet, StringBuilder& out);
- SlangResult calcChildrenHeader(NodeTree* tree, TypeSet* typeSet, StringBuilder& out);
- SlangResult calcOriginHeader(NodeTree* tree, StringBuilder& out);
-
- SlangResult calcDef(NodeTree* tree, SourceOrigin* origin, StringBuilder& out);
-
- const Options& getOptions() const { return m_options; }
-
- App(DiagnosticSink* sink, SourceManager* sourceManager, RootNamePool* rootNamePool):
- m_sink(sink),
- m_sourceManager(sourceManager),
- m_slicePool(StringSlicePool::Style::Default)
- {
- m_namePool.setRootNamePool(rootNamePool);
- }
-
-protected:
-
- /// Called to set up identifier lookup. Must be performed after options are initials
- static void _initIdentifierLookup(const Options& options, IdentifierLookup& outLookup);
-
- NamePool m_namePool;
-
- Options m_options;
- DiagnosticSink* m_sink;
- SourceManager* m_sourceManager;
-
- StringSlicePool m_slicePool;
-};
-
-SlangResult App::readAllText(const Slang::String& fileName, String& outRead)
-{
- try
- {
- StreamReader reader(new FileStream(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite));
- outRead = reader.ReadToEnd();
- }
- catch (const IOException&)
- {
- m_sink->diagnose(SourceLoc(), CPPDiagnostics::cannotOpenFile, fileName);
- return SLANG_FAIL;
- }
- catch (...)
- {
- m_sink->diagnose(SourceLoc(), CPPDiagnostics::cannotOpenFile, fileName);
- return SLANG_FAIL;
- }
-
- return SLANG_OK;
-}
-
-SlangResult App::writeAllText(const Slang::String& fileName, const UnownedStringSlice& text)
-{
- try
- {
- if (File::exists(fileName))
- {
- String existingText;
- if (readAllText(fileName, existingText) == SLANG_OK)
- {
- if (existingText == text)
- return SLANG_OK;
- }
- }
- StreamWriter writer(new FileStream(fileName, FileMode::Create));
- writer.Write(text);
- }
- catch (const IOException&)
- {
- m_sink->diagnose(SourceLoc(), CPPDiagnostics::cannotOpenFile, fileName);
- return SLANG_FAIL;
- }
-
- return SLANG_OK;
-}
-
-SlangResult App::calcDef(NodeTree* tree, SourceOrigin* origin, StringBuilder& out)
-{
- Node* currentScope = nullptr;
-
- for (Node* node : origin->m_nodes)
- {
- if (node->isReflected())
- {
- if (auto classLikeNode = as<ClassLikeNode>(node))
- {
- if (classLikeNode->m_marker.getContent().indexOf(UnownedStringSlice::fromLiteral("ABSTRACT")) >= 0)
- {
- out << "ABSTRACT_";
- }
-
- out << "SYNTAX_CLASS(" << node->m_name.getContent() << ", " << classLikeNode->m_super.getContent() << ")\n";
- out << "END_SYNTAX_CLASS()\n\n";
- }
- }
- }
- return SLANG_OK;
-}
-
-SlangResult App::calcChildrenHeader(NodeTree* tree, TypeSet* typeSet, StringBuilder& out)
-{
- const List<ClassLikeNode*>& baseTypes = typeSet->m_baseTypes;
- const String& reflectTypeName = typeSet->m_typeName;
-
- out << "#pragma once\n\n";
- out << "// Do not edit this file is generated from slang-cpp-extractor tool\n\n";
-
- List<ClassLikeNode*> classNodes;
- for (Index i = 0; i < baseTypes.getCount(); ++i)
- {
- ClassLikeNode* baseType = baseTypes[i];
- baseType->calcDerivedDepthFirst(classNodes);
- }
-
- //Node::filter(Node::isClassLike, nodes);
-
- List<ClassLikeNode*> derivedTypes;
-
- out << "\n\n /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! CHILDREN !!!!!!!!!!!!!!!!!!!!!!!!!!!! */ \n\n";
-
- // Now the children
- for (ClassLikeNode* classNode : classNodes)
- {
- classNode->getReflectedDerivedTypes(derivedTypes);
-
- // Define the derived types
- out << "#define " << m_options.m_markPrefix << "CHILDREN_" << reflectTypeName << "_" << classNode->m_name.getContent() << "(x, param)";
-
- if (derivedTypes.getCount())
- {
- out << " \\\n";
- for (Index j = 0; j < derivedTypes.getCount(); ++j)
- {
- Node* derivedType = derivedTypes[j];
- _indent(1, out);
- out << m_options.m_markPrefix << "ALL_" << reflectTypeName << "_" << derivedType->m_name.getContent() << "(x, param)";
- if (j < derivedTypes.getCount() - 1)
- {
- out << "\\\n";
- }
- }
- }
- out << "\n\n";
- }
-
- out << "\n\n /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ALL !!!!!!!!!!!!!!!!!!!!!!!!!!!! */\n\n";
-
- for (ClassLikeNode* classNode : classNodes)
- {
- // Define the derived types
- out << "#define " << m_options.m_markPrefix << "ALL_" << reflectTypeName << "_" << classNode->m_name.getContent() << "(x, param) \\\n";
- _indent(1, out);
- out << m_options.m_markPrefix << reflectTypeName << "_" << classNode->m_name.getContent() << "(x, param)";
-
- // If has derived types output them
- if (classNode->hasReflectedDerivedType())
- {
- out << " \\\n";
- _indent(1, out);
- out << m_options.m_markPrefix << "CHILDREN_" << reflectTypeName << "_" << classNode->m_name.getContent() << "(x, param)";
- }
- out << "\n\n";
- }
-
- if (m_options.m_outputFields)
- {
- out << "\n\n /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! FIELDS !!!!!!!!!!!!!!!!!!!!!!!!!!!! */\n\n";
-
- for (ClassLikeNode* classNode : classNodes)
- {
- // Define the derived types
- out << "#define " << m_options.m_markPrefix << "FIELDS_" << reflectTypeName << "_" << classNode->m_name.getContent() << "(_x_, _param_)";
-
- // Find all of the fields
- List<FieldNode*> fields;
- for (Node* child : classNode->m_children)
- {
- if (auto field = as<FieldNode>(child))
- {
- fields.add(field);
- }
- }
-
- if (fields.getCount() > 0)
- {
- out << "\\\n";
-
- const Index fieldsCount = fields.getCount();
- bool previousField = false;
- for (Index j = 0; j < fieldsCount; ++j)
- {
- const FieldNode* field = fields[j];
-
- if (field->isReflected())
- {
- if (previousField)
- {
- out << "\\\n";
- }
-
- _indent(1, out);
-
- // NOTE! We put the type field in brackets, such that there is no issue with templates containing a comma.
- // If stringified
- out << "_x_(" << field->m_name.getContent() << ", (" << field->m_fieldType << "), _param_)";
- previousField = true;
- }
- }
- }
-
- out << "\n\n";
- }
- }
-
- return SLANG_OK;
-}
-
-SlangResult App::calcOriginHeader(NodeTree* tree, StringBuilder& out)
-{
- // Do macros by origin
-
- out << "// Origin macros\n\n";
-
- for (SourceOrigin* origin : tree->getSourceOrigins())
- {
- out << "#define " << m_options.m_markPrefix << "ORIGIN_" << origin->m_macroOrigin << "(x, param) \\\n";
-
- for (Node* node : origin->m_nodes)
- {
- if (!(node->isReflected() && node->isClassLike()))
- {
- continue;
- }
-
- _indent(1, out);
- out << "x(" << node->m_name.getContent() << ", param) \\\n";
- }
- out << "/* */\n\n";
- }
-
- return SLANG_OK;
-}
-
-SlangResult App::calcTypeHeader(NodeTree* tree, TypeSet* typeSet, StringBuilder& out)
-{
- const List<ClassLikeNode*>& baseTypes = typeSet->m_baseTypes;
- const String& reflectTypeName = typeSet->m_typeName;
-
- out << "#pragma once\n\n";
- out << "// Do not edit this file is generated from slang-cpp-extractor tool\n\n";
-
- if (baseTypes.getCount() == 0)
- {
- return SLANG_OK;
- }
-
- // Set up the scope
- List<Node*> baseScopePath;
- baseTypes[0]->calcScopePath(baseScopePath);
-
- // Remove the global scope
- baseScopePath.removeAt(0);
- // Remove the type itself
- baseScopePath.removeLast();
-
- for (Node* scopeNode : baseScopePath)
- {
- SLANG_ASSERT(scopeNode->m_type == Node::Type::Namespace);
- out << "namespace " << scopeNode->m_name.getContent() << " {\n";
- }
-
- // Add all the base types, with in order traversals
- List<ClassLikeNode*> nodes;
- for (Index i = 0; i < baseTypes.getCount(); ++i)
- {
- ClassLikeNode* baseType = baseTypes[i];
- baseType->calcDerivedDepthFirst(nodes);
- }
-
- Node::filter(Node::isClassLikeAndReflected, nodes);
-
- // Write out the types
- {
- out << "\n";
- out << "enum class " << reflectTypeName << "Type\n";
- out << "{\n";
-
- Index typeIndex = 0;
- for (ClassLikeNode* node : nodes)
- {
- // Okay first we are going to output the enum values
- const Index depth = node->calcDerivedDepth() - 1;
- _indent(depth, out);
- out << node->m_name.getContent() << " = " << typeIndex << ",\n";
- typeIndex++;
- }
-
- _indent(1, out);
- out << "CountOf\n";
-
- out << "};\n\n";
- }
-
- // TODO(JS):
- // Strictly speaking if we wanted the types to be in different scopes, we would have to
- // change the namespaces here
-
- // Predeclare the classes
- {
- out << "// Predeclare\n\n";
- for (ClassLikeNode* node : nodes)
- {
- // If it's not reflected we don't output, in the enum list
- if (node->isReflected())
- {
- const char* type = (node->m_type == Node::Type::ClassType) ? "class" : "struct";
- out << type << " " << node->m_name.getContent() << ";\n";
- }
- }
- }
-
- // Do the macros for each of the types
-
- {
- out << "// Type macros\n\n";
-
- out << "// Order is (NAME, SUPER, ORIGIN, LAST, MARKER, TYPE, param) \n";
- out << "// NAME - is the class name\n";
- out << "// SUPER - is the super class name (or NO_SUPER)\n";
- out << "// ORIGIN - where the definition was found\n";
- out << "// LAST - is the class name for the last in the range (or NO_LAST)\n";
- out << "// MARKER - is the text inbetween in the prefix/postix (like ABSTRACT). If no inbetween text is is 'NONE'\n";
- out << "// TYPE - Can be BASE, INNER or LEAF for the overall base class, an INNER class, or a LEAF class\n";
- out << "// param is a user defined parameter that can be parsed to the invoked x macro\n\n";
-
- // Output all of the definitions for each type
- for (ClassLikeNode* node : nodes)
- {
- out << "#define " << m_options.m_markPrefix << reflectTypeName << "_" << node->m_name.getContent() << "(x, param) ";
-
- // Output the X macro part
- _indent(1, out);
- out << "x(" << node->m_name.getContent() << ", ";
-
- if (node->m_superNode)
- {
- out << node->m_superNode->m_name.getContent() << ", ";
- }
- else
- {
- out << "NO_SUPER, ";
- }
-
- // Output the (file origin)
- out << node->m_origin->m_macroOrigin;
- out << ", ";
-
- // The last type
- Node* lastDerived = node->findLastDerived();
- if (lastDerived)
- {
- out << lastDerived->m_name.getContent() << ", ";
- }
- else
- {
- out << "NO_LAST, ";
- }
-
- // Output any specifics of the markup
- UnownedStringSlice marker = node->m_marker.getContent();
- // Need to extract the name
- if (marker.getLength() > m_options.m_markPrefix.getLength() + m_options.m_markSuffix.getLength())
- {
- marker = UnownedStringSlice(marker.begin() + m_options.m_markPrefix.getLength(), marker.end() - m_options.m_markSuffix.getLength());
- }
- else
- {
- marker = UnownedStringSlice::fromLiteral("NONE");
- }
- out << marker << ", ";
-
- if (node->m_superNode == nullptr)
- {
- out << "BASE, ";
- }
- else if (node->hasReflectedDerivedType())
- {
- out << "INNER, ";
- }
- else
- {
- out << "LEAF, ";
- }
- out << "param)\n";
- }
- }
-
- // Now pop the scope in revers
- for (Index j = baseScopePath.getCount() - 1; j >= 0; j--)
- {
- Node* scopeNode = baseScopePath[j];
- out << "} // namespace " << scopeNode->m_name.getContent() << "\n";
- }
-
- return SLANG_OK;
-}
-
-SlangResult App::writeDefs(NodeTree* tree)
-{
- const auto& origins = tree->getSourceOrigins();
-
- for (SourceOrigin* origin : origins)
- {
- const String path = origin->m_sourceFile->getPathInfo().foundPath;
-
- // We need to work out the name of the def file
-
- String ext = Path::getPathExt(path);
- String pathWithoutExt = Path::getPathWithoutExt(path);
-
- // The output path
-
- StringBuilder outPath;
- outPath << pathWithoutExt << "-defs." << ext;
-
- StringBuilder content;
- SLANG_RETURN_ON_FAIL(calcDef(tree, origin, content));
-
- // Write the defs file
- SLANG_RETURN_ON_FAIL(writeAllText(outPath, content.getUnownedSlice()));
- }
-
- return SLANG_OK;
-}
-
-SlangResult App::writeOutput(NodeTree* tree)
-{
- String path;
- if (m_options.m_inputDirectory.getLength())
- {
- path = Path::combine(m_options.m_inputDirectory, m_options.m_outputPath);
- }
- else
- {
- path = m_options.m_outputPath;
- }
-
- // Get the ext
- String ext = Path::getPathExt(path);
- if (ext.getLength() == 0)
- {
- // Default to .h if not specified
- ext = "h";
- }
-
- // Strip the extension if set
- path = Path::getPathWithoutExt(path);
-
- for (TypeSet* typeSet : tree->getTypeSets())
- {
- {
- /// Calculate the header
- StringBuilder header;
- SLANG_RETURN_ON_FAIL(calcTypeHeader(tree, typeSet, header));
-
- // Write it out
-
- StringBuilder headerPath;
- headerPath << path << "-" << typeSet->m_fileMark << "." << ext;
- SLANG_RETURN_ON_FAIL(writeAllText(headerPath, header.getUnownedSlice()));
- }
-
- {
- StringBuilder childrenHeader;
- SLANG_RETURN_ON_FAIL(calcChildrenHeader(tree, typeSet, childrenHeader));
-
- StringBuilder headerPath;
- headerPath << path << "-" << typeSet->m_fileMark << "-macro." + ext;
- SLANG_RETURN_ON_FAIL(writeAllText(headerPath, childrenHeader.getUnownedSlice()));
- }
- }
-
- return SLANG_OK;
-}
-
-/* static */void App::_initIdentifierLookup(const Options& options, IdentifierLookup& outLookup)
-{
- outLookup.reset();
-
- // Some keywords
- {
- const char* names[] = { "virtual", "typedef", "continue", "if", "case", "break", "catch", "default", "delete", "do", "else", "for", "new", "goto", "return", "switch", "throw", "using", "while", "operator" };
- outLookup.set(names, SLANG_COUNT_OF(names), IdentifierStyle::Keyword);
- }
-
- // Type modifier keywords
- {
- const char* names[] = { "const", "volatile" };
- outLookup.set(names, SLANG_COUNT_OF(names), IdentifierStyle::TypeModifier);
- }
-
- // Special markers
- {
- const char* names[] = {"PRE_DECLARE", "TYPE_SET", "REFLECTED", "UNREFLECTED"};
- const IdentifierStyle styles[] = { IdentifierStyle::PreDeclare, IdentifierStyle::TypeSet, IdentifierStyle::Reflected, IdentifierStyle::Unreflected };
- SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(names) == SLANG_COUNT_OF(styles));
-
- StringBuilder buf;
- for (Index i = 0; i < SLANG_COUNT_OF(names); ++i)
- {
- buf.Clear();
- buf << options.m_markPrefix << names[i];
- outLookup.set(buf.getUnownedSlice(), styles[i]);
- }
- }
-
- // Keywords which introduce types/scopes
- {
- outLookup.set("struct", IdentifierStyle::Struct);
- outLookup.set("class", IdentifierStyle::Class);
- outLookup.set("namespace", IdentifierStyle::Namespace);
- }
-
- // Keywords that control access
- {
- const char* names[] = { "private", "protected", "public" };
- outLookup.set(names, SLANG_COUNT_OF(names), IdentifierStyle::Access);
- }
-}
-
-SlangResult App::execute(const Options& options)
-{
- m_options = options;
-
- IdentifierLookup identifierLookup;
- _initIdentifierLookup(options, identifierLookup);
-
- NodeTree tree(&m_slicePool, &m_namePool, &identifierLookup);
-
-
- // Read in each of the input files
- for (Index i = 0; i < m_options.m_inputPaths.getCount(); ++i)
- {
- String inputPath;
-
- if (m_options.m_inputDirectory.getLength())
- {
- inputPath = Path::combine(m_options.m_inputDirectory, m_options.m_inputPaths[i]);
- }
- else
- {
- inputPath = m_options.m_inputPaths[i];
- }
-
- // Read the input file
- String contents;
- SLANG_RETURN_ON_FAIL(readAllText(inputPath, contents));
-
- PathInfo pathInfo = PathInfo::makeFromString(inputPath);
-
- SourceFile* sourceFile = m_sourceManager->createSourceFileWithString(pathInfo, contents);
-
- SourceOrigin* sourceOrigin = tree.addSourceOrigin(sourceFile, options);
-
- Parser parser(&tree, m_sink);
- SLANG_RETURN_ON_FAIL(parser.parse(sourceOrigin, &m_options));
- }
-
- SLANG_RETURN_ON_FAIL(tree.calcDerivedTypes(m_sink));
-
- // Okay let's check out the typeSets
- {
- for (TypeSet* typeSet : tree.getTypeSets())
- {
- // The macro name is in upper snake, so split it
- List<UnownedStringSlice> slices;
- NameConventionUtil::split(typeSet->m_macroName, slices);
-
- if (typeSet->m_fileMark.getLength() == 0)
- {
- StringBuilder buf;
- // Let's guess a 'fileMark' (it becomes part of the filename) based on the macro name. Use lower kabab.
- NameConventionUtil::join(slices.getBuffer(), slices.getCount(), CharCase::Lower, NameConvention::Kabab, buf);
- typeSet->m_fileMark = buf.ProduceString();
- }
-
- if (typeSet->m_typeName.getLength() == 0)
- {
- // Let's guess a typename if not set -> go with upper camel
- StringBuilder buf;
- NameConventionUtil::join(slices.getBuffer(), slices.getCount(), CharCase::Upper, NameConvention::Camel, buf);
- typeSet->m_typeName = buf.ProduceString();
- }
- }
- }
-
- // Dump out the tree
- if (options.m_dump)
- {
- {
- StringBuilder buf;
- tree.getRootNode()->dump(0, buf);
- m_sink->writer->write(buf.getBuffer(), buf.getLength());
- }
-
- for (TypeSet* typeSet : tree.getTypeSets())
- {
- const List<ClassLikeNode*>& baseTypes = typeSet->m_baseTypes;
-
- for (ClassLikeNode* baseType : baseTypes)
- {
- StringBuilder buf;
- baseType->dumpDerived(0, buf);
- m_sink->writer->write(buf.getBuffer(), buf.getLength());
- }
- }
- }
-
- if (options.m_defs)
- {
- SLANG_RETURN_ON_FAIL(writeDefs(&tree));
- }
-
- if (options.m_outputPath.getLength())
- {
- SLANG_RETURN_ON_FAIL(writeOutput(&tree));
- }
-
- return SLANG_OK;
-}
-
-/// Execute
-SlangResult App::executeWithArgs(int argc, const char*const* argv)
-{
- Options options;
- OptionsParser optionsParser;
- SLANG_RETURN_ON_FAIL(optionsParser.parse(argc, argv, m_sink, options));
- SLANG_RETURN_ON_FAIL(execute(options));
- return SLANG_OK;
-}
-
-} // namespace CppExtract
-
-int main(int argc, const char*const* argv)
-{
- using namespace CppExtract;
- using namespace Slang;
-
- {
- ComPtr<ISlangWriter> writer(new FileWriter(stderr, WriterFlag::AutoFlush));
-
- RootNamePool rootNamePool;
-
- SourceManager sourceManager;
- sourceManager.initialize(nullptr, nullptr);
-
- DiagnosticSink sink(&sourceManager, Lexer::sourceLocationLexer);
- sink.writer = writer;
-
- // Set to true to see command line that initiated C++ extractor. Helpful when finding issues from solution building failing, and then so
- // being able to repeat the issue
- bool dumpCommandLine = false;
-
- if (dumpCommandLine)
- {
- StringBuilder builder;
-
- for (Index i = 1; i < argc; ++i)
- {
- builder << argv[i] << " ";
- }
-
- sink.diagnose(SourceLoc(), CPPDiagnostics::commandLine, builder);
- }
-
- App app(&sink, &sourceManager, &rootNamePool);
-
- try
- {
- if (SLANG_FAILED(app.executeWithArgs(argc - 1, argv + 1)))
- {
- sink.diagnose(SourceLoc(), CPPDiagnostics::extractorFailed);
- return 1;
- }
- if (sink.getErrorCount())
- {
- sink.diagnose(SourceLoc(), CPPDiagnostics::extractorFailed);
- return 1;
- }
- }
- catch (...)
- {
- sink.diagnose(SourceLoc(), CPPDiagnostics::internalError);
- return 1;
- }
- }
- return 0;
-}
-
diff --git a/tools/slang-cpp-extractor/unit-test.cpp b/tools/slang-cpp-extractor/unit-test.cpp
new file mode 100644
index 000000000..7648e4e50
--- /dev/null
+++ b/tools/slang-cpp-extractor/unit-test.cpp
@@ -0,0 +1,80 @@
+#include "unit-test.h"
+
+
+#include "../../source/compiler-core/slang-name-convention-util.h"
+
+#include "../../source/core/slang-io.h"
+
+#include "../../source/compiler-core/slang-source-loc.h"
+#include "../../source/compiler-core/slang-lexer.h"
+
+#include "identifier-lookup.h"
+#include "node-tree.h"
+#include "parser.h"
+#include "options.h"
+
+namespace CppExtract {
+using namespace Slang;
+
+
+struct TestState
+{
+ TestState():
+ m_slicePool(StringSlicePool::Style::Default)
+ {
+ m_identifierLookup.initDefault(UnownedStringSlice::fromLiteral("SLANG_"));
+
+ m_sourceManager.initialize(nullptr, nullptr);
+
+ m_sink.init(&m_sourceManager, Lexer::sourceLocationLexer);
+
+ m_namePool.setRootNamePool(&m_rootNamePool);
+ }
+
+ RootNamePool m_rootNamePool;
+ Options m_options;
+ SourceManager m_sourceManager;
+ DiagnosticSink m_sink;
+ NamePool m_namePool;
+ StringSlicePool m_slicePool;
+ IdentifierLookup m_identifierLookup;
+};
+
+static const char someSource[] =
+"enum SomeEnum\n"
+"{\n"
+" Value,\n"
+" Another = 10,\n"
+"};\n"
+"typedef SomeEnum AliasEnum;\n";
+
+
+/* static */SlangResult UnitTestUtil::run()
+{
+ {
+ TestState state;
+
+ NodeTree tree(&state.m_slicePool, &state.m_namePool, &state.m_identifierLookup);
+
+ UnownedStringSlice contents = UnownedStringSlice::fromLiteral(someSource);
+ PathInfo pathInfo = PathInfo::makeFromString("source.h");
+
+ SourceManager* sourceManager = &state.m_sourceManager;
+
+ SourceFile* sourceFile = sourceManager->createSourceFileWithString(pathInfo, contents);
+ SourceOrigin* sourceOrigin = tree.addSourceOrigin(sourceFile, state.m_options);
+
+ Parser parser(&tree, &state.m_sink);
+
+ {
+ const Node::Type enableTypes[] = { Node::Type::Enum, Node::Type::EnumClass, Node::Type::EnumCase, Node::Type::TypeDef };
+ parser.setTypesEnabled(enableTypes, SLANG_COUNT_OF(enableTypes));
+ }
+
+ SLANG_RETURN_ON_FAIL(parser.parse(sourceOrigin, &state.m_options));
+ }
+
+ return SLANG_OK;
+}
+
+} // namespace CppExtract
diff --git a/tools/slang-cpp-extractor/unit-test.h b/tools/slang-cpp-extractor/unit-test.h
new file mode 100644
index 000000000..9c8d9b08c
--- /dev/null
+++ b/tools/slang-cpp-extractor/unit-test.h
@@ -0,0 +1,16 @@
+#ifndef CPP_EXTRACT_UNIT_TEST_H
+#define CPP_EXTRACT_UNIT_TEST_H
+
+#include "diagnostics.h"
+
+namespace CppExtract {
+using namespace Slang;
+
+struct UnitTestUtil
+{
+ static SlangResult run();
+};
+
+} // CppExtract
+
+#endif
diff --git a/tools/slang-test/unit-test-string.cpp b/tools/slang-test/unit-test-string.cpp
index d585132c8..e27411b3e 100644
--- a/tools/slang-test/unit-test-string.cpp
+++ b/tools/slang-test/unit-test-string.cpp
@@ -84,24 +84,43 @@ static void stringUnitTest()
{
UnownedStringSlice values[] = { UnownedStringSlice("hello"), UnownedStringSlice("world"), UnownedStringSlice("!") };
+ ArrayView<UnownedStringSlice> valuesView(values, SLANG_COUNT_OF(values));
+ List<UnownedStringSlice> checkValues;
StringBuilder builder;
- builder.Clear();
- StringUtil::join(values, 0, ',', builder);
- SLANG_CHECK(builder == "");
- builder.Clear();
- StringUtil::join(values, 1, ',', builder);
- SLANG_CHECK(builder == "hello");
+ {
+ builder.Clear();
+ StringUtil::join(values, 0, ',', builder);
+ SLANG_CHECK(builder == "");
+ }
+
+ {
+ builder.Clear();
+ StringUtil::join(values, 1, ',', builder);
+ SLANG_CHECK(builder == "hello");
+ StringUtil::split(builder.getUnownedSlice(), ',', checkValues);
+ SLANG_CHECK(checkValues.getArrayView() == ArrayView<UnownedStringSlice>(values, 1));
+ }
- builder.Clear();
- StringUtil::join(values, 2, ',', builder);
- SLANG_CHECK(builder == "hello,world");
+ {
+ builder.Clear();
+ StringUtil::join(values, 2, ',', builder);
+ SLANG_CHECK(builder == "hello,world");
- builder.Clear();
- StringUtil::join(values, 3, UnownedStringSlice("ab"), builder);
- SLANG_CHECK(builder == "helloabworldab!");
+ StringUtil::split(builder.getUnownedSlice(), ',', checkValues);
+ SLANG_CHECK(checkValues.getArrayView() == ArrayView<UnownedStringSlice>(values, 2));
+ }
+
+ {
+ builder.Clear();
+ StringUtil::join(values, 3, UnownedStringSlice("ab"), builder);
+ SLANG_CHECK(builder == "helloabworldab!");
+
+ StringUtil::split(builder.getUnownedSlice(), UnownedStringSlice("ab"), checkValues);
+ SLANG_CHECK(checkValues.getArrayView() == ArrayView<UnownedStringSlice>(values, 3));
+ }
}
}