summaryrefslogtreecommitdiffstats
path: root/tools/slang-cpp-extractor
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2020-05-04 13:46:24 -0400
committerGitHub <noreply@github.com>2020-05-04 10:46:24 -0700
commitf59978893fed309ff19bacd787398c4bab4aa7c8 (patch)
treead28eb79ca5bab13770f035d551c77ac67aa5756 /tools/slang-cpp-extractor
parent5d3a737e75346b6ced204829a60be2837589e9ad (diff)
C++ Extractor (#1337)
* WIP: Doing texing using slangs lexer for cpp-extractor * Node tree for C++ extraction. * Bug fixing. Add dump of hierarchy. * First pass at extracting fields. * Parse template types. * Use diagnostics defs for C++ extractor. * Simplify Diagnostic Defs. * Remove the brace stack. * Added IdentifierLookup. * Add handling for >> style template close. * Improved identifier handling/keywords. * Added ability to check if reader is at cursor position. * Handling of an unspecified root type. * Parsing code comments. Tidy up some parsing - to use advanceIf functions more. * Improve path handling. * Fixes around changes to Path interface. * Working Range, Type and Scope header. * Extract the middle part of marker and put in output. Gives more flexibility at macro injection, and in class definitions. * Split DERIVED types into it's own macro, to provide way to generate for derived types. * Fix clang/g++ compile issue. * Tabs -> spaces. * Fix small bug in getFileNameWithoutExt * Small improvement around naming. Co-authored-by: Tim Foley <tfoleyNV@users.noreply.github.com>
Diffstat (limited to 'tools/slang-cpp-extractor')
-rw-r--r--tools/slang-cpp-extractor/slang-cpp-extractor-diagnostic-defs.h45
-rw-r--r--tools/slang-cpp-extractor/slang-cpp-extractor-diagnostics.cpp13
-rw-r--r--tools/slang-cpp-extractor/slang-cpp-extractor-diagnostics.h17
-rw-r--r--tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp1944
4 files changed, 2019 insertions, 0 deletions
diff --git a/tools/slang-cpp-extractor/slang-cpp-extractor-diagnostic-defs.h b/tools/slang-cpp-extractor/slang-cpp-extractor-diagnostic-defs.h
new file mode 100644
index 000000000..715f74c23
--- /dev/null
+++ b/tools/slang-cpp-extractor/slang-cpp-extractor-diagnostic-defs.h
@@ -0,0 +1,45 @@
+//
+
+// The file is meant to be included multiple times, to produce different
+// pieces of declaration/definition code related to diagnostic messages
+//
+// Each diagnostic is declared here with:
+//
+// DIAGNOSTIC(id, severity, name, messageFormat)
+//
+// Where `id` is the unique diagnostic ID, `severity` is the default
+// severity (from the `Severity` enum), `name` is a name used to refer
+// to this diagnostic from code, and `messageFormat` is the default
+// (non-localized) message for the diagnostic, with placeholders
+// for any arguments.
+
+#ifndef DIAGNOSTIC
+#error Need to #define DIAGNOSTIC(...) before including "slang-cpp-extractor-diagnostics-defs.h"
+#define DIAGNOSTIC(id, severity, name, messageFormat) /* */
+#endif
+
+DIAGNOSTIC(-1, Note, seeDeclarationOf, "see declaration of '$0'")
+DIAGNOSTIC(-1, Note, seeOpenBrace, "see open brace")
+
+DIAGNOSTIC(1, Error, cannotOpenFile, "cannot open file '$0'.")
+
+// Parsing errors
+DIAGNOSTIC(100000, Error, expectingToken, "Expecting token $0")
+DIAGNOSTIC(100001, Error, typeAlreadyDeclared, "Type '$0' already declared")
+DIAGNOSTIC(100002, Error, scopeNotClosed, "Scope not closed")
+DIAGNOSTIC(100003, Error, typeNameDoesntMatch, "Type name doesn't match $0")
+DIAGNOSTIC(100004, Error, didntFindMatchingBrace, "Didn't find brace matching $0")
+DIAGNOSTIC(100005, Error, braceOpenAtEndOfFile, "Brace open at file end")
+DIAGNOSTIC(100006, Error, unexpectedTemplateClose, "Unexpected template close")
+DIAGNOSTIC(100007, Error, superTypeNotFound, "Super type not found for $0")
+DIAGNOSTIC(100008, Error, superTypeNotAType, "Named super type is not a type $0")
+
+// Command line errors 100100
+
+DIAGNOSTIC(100101, Error, optionAlreadyDefined, "Option '$0' is already defined '$1'")
+DIAGNOSTIC(100102, Error, requireValueAfterOption, "Require a value after $0 option")
+DIAGNOSTIC(100103, Error, unknownOption, "Unknown option '$0'")
+DIAGNOSTIC(100104, Error, noInputPathsSpecified, "No input paths specified")
+DIAGNOSTIC(100105, Error, noOutputPathSpecified, "No -o output path specified")
+
+#undef DIAGNOSTIC
diff --git a/tools/slang-cpp-extractor/slang-cpp-extractor-diagnostics.cpp b/tools/slang-cpp-extractor/slang-cpp-extractor-diagnostics.cpp
new file mode 100644
index 000000000..4c8de5757
--- /dev/null
+++ b/tools/slang-cpp-extractor/slang-cpp-extractor-diagnostics.cpp
@@ -0,0 +1,13 @@
+#include "slang-cpp-extractor-diagnostics.h"
+
+namespace SlangExperimental {
+
+namespace CPPDiagnostics
+{
+using namespace Slang;
+
+#define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, messageFormat };
+#include "slang-cpp-extractor-diagnostic-defs.h"
+}
+
+} // namespace SlangExperimental
diff --git a/tools/slang-cpp-extractor/slang-cpp-extractor-diagnostics.h b/tools/slang-cpp-extractor/slang-cpp-extractor-diagnostics.h
new file mode 100644
index 000000000..419559fa8
--- /dev/null
+++ b/tools/slang-cpp-extractor/slang-cpp-extractor-diagnostics.h
@@ -0,0 +1,17 @@
+#ifndef SLANG_CPP_EXTRACTOR_DIAGNOSTICS_H
+#define SLANG_CPP_EXTRACTOR_DIAGNOSTICS_H
+
+#include "../../source/slang/slang-diagnostics.h"
+
+namespace SlangExperimental {
+using namespace Slang;
+
+namespace CPPDiagnostics {
+
+#define DIAGNOSTIC(id, severity, name, messageFormat) extern const DiagnosticInfo name;
+#include "slang-cpp-extractor-diagnostic-defs.h"
+
+} // CPPDiagnostics
+} // SlangExperimental
+
+#endif
diff --git a/tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp b/tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp
new file mode 100644
index 000000000..389bc976a
--- /dev/null
+++ b/tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp
@@ -0,0 +1,1944 @@
+// 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/slang/slang-source-loc.h"
+#include "../../source/slang/slang-lexer.h"
+#include "../../source/slang/slang-diagnostics.h"
+#include "../../source/slang/slang-file-system.h"
+#include "../../source/slang/slang-name.h"
+
+#include "slang-cpp-extractor-diagnostics.h"
+
+namespace SlangExperimental
+{
+
+using namespace Slang;
+
+enum class IdentifierStyle
+{
+ None, ///< It's not an identifier
+
+ Identifier, ///< Just an identifier
+ Root,
+ BaseClass, ///< Has the name of a base class defined elsewhere
+
+ TypeModifier, ///< const, volatile etc
+ Keyword, ///< A keyword C/C++ keyword that is not another type
+ Class, ///< class
+ Struct, ///< struct
+ Namespace, ///< namespace
+ Access, ///< public, protected, private
+
+ CountOf,
+};
+
+typedef uint32_t IdentifierFlags;
+struct IdentifierFlag
+{
+ enum Enum : IdentifierFlags
+ {
+ StartScope = 0x1, ///< namespace, struct or class
+ ClassLike = 0x2, ///< Struct or class
+ Keyword = 0x4,
+ };
+};
+
+static const IdentifierFlags kIdentifierFlags[Index(IdentifierStyle::CountOf)] =
+{
+ 0, /// None
+ 0, /// Identifier
+ 0, /// Root
+ 0, /// BaseClass
+ 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,
+};
+
+SLANG_FORCE_INLINE IdentifierFlags getFlags(IdentifierStyle style)
+{
+ return kIdentifierFlags[Index(style)];
+}
+
+SLANG_FORCE_INLINE bool hasFlag(IdentifierStyle style, IdentifierFlag::Enum flag)
+{
+ return (getFlags(style) & flag) != 0;
+}
+
+class IdentifierLookup
+{
+public:
+
+ IdentifierStyle get(const UnownedStringSlice& slice) const
+ {
+ Index index = m_pool.findIndex(slice);
+ return (index >= 0) ? m_styles[index] : IdentifierStyle::None;
+ }
+
+ void set(const char* name, IdentifierStyle style)
+ {
+ set(UnownedStringSlice(name), style);
+ }
+
+ void set(const UnownedStringSlice& name, IdentifierStyle style)
+ {
+ StringSlicePool::Handle handle;
+ if (m_pool.findOrAdd(name, handle))
+ {
+ // Add the extra flags
+ m_styles[Index(handle)] = style;
+ }
+ else
+ {
+ Index index = Index(handle);
+ SLANG_ASSERT(index == m_styles.getCount());
+ m_styles.add(style);
+ }
+ }
+
+ void set(const char*const* names, size_t namesCount, IdentifierStyle style)
+ {
+ for (size_t i = 0; i < namesCount; ++i)
+ {
+ set(UnownedStringSlice(names[i]), style);
+ }
+ }
+ IdentifierLookup():
+ m_pool(StringSlicePool::Style::Empty)
+ {
+ SLANG_ASSERT(m_pool.getSlicesCount() == 0);
+ }
+protected:
+ List<IdentifierStyle> m_styles;
+ StringSlicePool m_pool;
+};
+
+class Node : public RefObject
+{
+public:
+ enum class Type
+ {
+ Invalid,
+ StructType,
+ ClassType,
+ Namespace,
+ AnonymousNamespace,
+ };
+
+ struct Field
+ {
+ UnownedStringSlice type;
+ Token name;
+ };
+
+ enum class BaseType
+ {
+ None, ///< Neither a base or marked base
+ Marked, ///< It's a marked base
+ Unmarked, ///< It's a base, but it's not marked
+ };
+
+ bool isClassLike() const { return m_type == Type::StructType || m_type == Type::ClassType; }
+
+ /// Add a child node to this nodes scope
+ void addChild(Node* child);
+
+ /// Find a child node in this scope with the specified name. Return nullptr if not found
+ Node* findChild(const UnownedStringSlice& name) const;
+
+ /// Add a node that is derived from this
+ void addDerived(Node* derived);
+
+ /// True if can accept fields (class like types can)
+ bool acceptsFields() const { return isClassLike(); }
+
+ void dump(int indent, StringBuilder& out);
+ void dumpDerived(int indentCount, StringBuilder& out);
+
+ /// Calculate the absolute name for this namespace/type
+ void calcAbsoluteName(StringBuilder& outName) const;
+
+ /// Do depth first traversal of nodes
+ void calcScopeDepthFirst(List<Node*>& outNodes);
+
+ /// Traverse the hierarchy of derived nodes, in depth first order
+ void calcDerivedDepthFirst(List<Node*>& outNodes);
+
+ /// Calculate the scope path to this node, from the root
+ void calcScopePath(List<Node*>& outPath) { calcScopePath(this, outPath); }
+
+ /// Calculates the derived depth
+ Index calcDerivedDepth() const;
+
+ /// Gets the anonymous namespace associated with this scope
+ Node* getAnonymousNamespace();
+
+ /// Find the last (reflected) derived type
+ Node* findLastDerived();
+
+ /// True if has a derived type that is reflected
+ bool hasReflectedDerivedType() const;
+
+ static void calcScopePath(Node* node, List<Node*>& outPath);
+
+ Node(Type type):
+ m_type(type),
+ m_parentScope(nullptr),
+ m_isReflected(false),
+ m_superNode(nullptr),
+ m_baseType(BaseType::None)
+ {
+ m_anonymousNamespace = nullptr;
+ }
+
+ /// The type of node this is
+ Type m_type;
+
+ /// All of the types and namespaces in this *scope*
+ List<RefPtr<Node>> m_children;
+
+ /// All of the types derived from this type
+ List<RefPtr<Node>> m_derivedTypes;
+
+ /// Map from a name (in this scope) to the Node
+ Dictionary<UnownedStringSlice, Node*> m_childMap;
+
+ /// All of the fields within a *type*
+ List<Field> m_fields;
+
+ /// There can only be one anonymousNamespace for a scope. If there is one it's held here
+ Node* m_anonymousNamespace;
+
+ /// Classes can be traversed, but not reflected. To be reflected they have to contain the marker
+ bool m_isReflected;
+ /// The base type of this
+ BaseType m_baseType;
+
+ Token m_name; ///< The name of this scope/type
+ Token m_super; ///< Super class name
+ Token m_marker; ///< The marker associated with this scope (typically the marker is SLANG_CLASS etc, that is used to identify reflectedType)
+
+ Node* m_parentScope; ///< The scope this type/scope is defined in
+ Node* m_superNode; ///< If this is a class/struct, the type it is derived from (or nullptr if base)
+};
+
+Node* Node::getAnonymousNamespace()
+{
+
+ if (!m_anonymousNamespace)
+ {
+ m_anonymousNamespace = new Node(Type::AnonymousNamespace);
+ m_anonymousNamespace->m_parentScope = this;
+ m_children.add(m_anonymousNamespace);
+ }
+
+ return m_anonymousNamespace;
+}
+
+void Node::addChild(Node* child)
+{
+ SLANG_ASSERT(child->m_parentScope == nullptr);
+ // Can't add anonymous namespace this way - should be added via getAnonymousNamespace
+ SLANG_ASSERT(child->m_type != Type::AnonymousNamespace);
+
+ child->m_parentScope = this;
+ m_children.add(child);
+
+ if (child->m_name.Content.getLength())
+ {
+ m_childMap.Add(child->m_name.Content, child);
+ }
+}
+
+Node* Node::findChild(const UnownedStringSlice& name) const
+{
+ Node** nodePtr = m_childMap.TryGetValue(name);
+ return (nodePtr) ? *nodePtr : nullptr;
+}
+
+/// Add a node that is derived from this
+void Node::addDerived(Node* derived)
+{
+ SLANG_ASSERT(derived->m_superNode == nullptr);
+ derived->m_superNode = this;
+ m_derivedTypes.add(derived);
+}
+
+void Node::calcScopeDepthFirst(List<Node*>& outNodes)
+{
+ outNodes.add(this);
+ for (Node* child : m_children)
+ {
+ child->calcScopeDepthFirst(outNodes);
+ }
+}
+
+void Node::calcDerivedDepthFirst(List<Node*>& outNodes)
+{
+ outNodes.add(this);
+ for (Node* derivedType : m_derivedTypes)
+ {
+ derivedType->calcDerivedDepthFirst(outNodes);
+ }
+}
+
+static void _indent(Index indentCount, StringBuilder& out)
+{
+ for (Index i = 0; i < indentCount; ++i)
+ {
+ out << " ";
+ }
+}
+
+void Node::dumpDerived(int indentCount, StringBuilder& out)
+{
+ if (isClassLike() && m_isReflected && m_name.Content.getLength() > 0)
+ {
+ _indent(indentCount, out);
+ out << m_name.Content << "\n";
+ }
+
+ for (Node* derivedType : m_derivedTypes)
+ {
+ derivedType->dumpDerived(indentCount + 1, out);
+ }
+}
+
+void Node::dump(int indentCount, StringBuilder& out)
+{
+ _indent(indentCount, out);
+
+ switch (m_type)
+ {
+ case Type::AnonymousNamespace:
+ {
+ out << "namespace {\n";
+ }
+ case Type::Namespace:
+ {
+ if (m_name.Content.getLength())
+ {
+ out << "namespace " << m_name.Content << " {\n";
+ }
+ else
+ {
+ out << "{\n";
+ }
+ break;
+ }
+ case Type::StructType:
+ case Type::ClassType:
+ {
+ const char* typeName = (m_type == Type::StructType) ? "struct" : "class";
+
+ out << typeName << " ";
+
+ if (!m_isReflected)
+ {
+ out << " (";
+ }
+ out << m_name.Content;
+ if (!m_isReflected)
+ {
+ out << ") ";
+ }
+
+ if (m_super.Content.getLength())
+ {
+ out << " : " << m_super.Content;
+ }
+
+ out << " {\n";
+ break;
+ }
+ }
+
+ for (Node* child : m_children)
+ {
+ child->dump(indentCount + 1, out);
+ }
+
+ for (const Field& field : m_fields)
+ {
+ _indent(indentCount + 1, out);
+ out << field.type << " " << field.name.Content << "\n";
+ }
+
+ _indent(indentCount, out);
+ out << "}\n";
+}
+
+void Node::calcAbsoluteName(StringBuilder& outName) const
+{
+ if (m_parentScope == nullptr)
+ {
+ if (m_name.Content.getLength() == 0)
+ {
+ return;
+ }
+ outName << m_name.Content;
+ }
+ else
+ {
+ outName << "::";
+ if (m_type == Type::AnonymousNamespace)
+ {
+ outName << "{Anonymous}";
+ }
+ else
+ {
+ outName << m_name.Content;
+ }
+ }
+}
+
+Index Node::calcDerivedDepth() const
+{
+ const Node* node = this;
+ Index count = 0;
+
+ while (node)
+ {
+ count++;
+ node = node->m_superNode;
+ }
+
+ return count;
+}
+
+Node* Node::findLastDerived()
+{
+ if (!m_isReflected)
+ {
+ return nullptr;
+ }
+
+ for (Index i = m_derivedTypes.getCount() - 1; i >= 0; --i)
+ {
+ Node* derivedType = m_derivedTypes[i];
+ Node* found = derivedType->findLastDerived();
+ if (found)
+ {
+ return found;
+ }
+ }
+ return this;
+}
+
+/* static */void Node::calcScopePath(Node* node, List<Node*>& outPath)
+{
+ outPath.clear();
+
+ while (node)
+ {
+ outPath.add(node);
+ node = node->m_parentScope;
+ }
+
+ // reverse the order, so we go from root to the node
+ outPath.reverse();
+}
+
+bool Node::hasReflectedDerivedType() const
+{
+ for (Node* type : m_derivedTypes)
+ {
+ if (type->m_isReflected)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Options !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+struct Options
+{
+ void reset()
+ {
+ m_inputPaths.clear();
+ m_outputPath = String();
+ m_dump = false;
+
+ m_prefixMark = "SLANG_";
+ m_postfixMark = "_CLASS";
+ }
+
+ bool m_dump = false;
+ List<String> m_inputPaths;
+ String m_outputPath;
+ String m_inputDirectory;
+ String m_reflectType; ///< The typename used for output
+ String m_prefixMark;
+ String m_postfixMark;
+};
+
+struct OptionsParser
+{
+ /// Parse the parameters. NOTE! Must have the program path removed
+ SlangResult parse(int argc, const char*const* argv, DiagnosticSink* sink, Options& outOptions);
+
+ SlangResult _parseArgWithValue(const char* option, String& outValue);
+ SlangResult _parseArgReplaceValue(const char* option, String& outValue);
+
+ String m_reflectType;
+
+ Index m_index;
+ Int m_argCount;
+ const char*const* m_args;
+ DiagnosticSink* m_sink;
+};
+
+SlangResult OptionsParser::_parseArgWithValue(const char* option, String& ioValue)
+{
+ SLANG_ASSERT(UnownedStringSlice(m_args[m_index]) == option);
+ if (m_index + 1 < m_argCount)
+ {
+ // Next parameter is the output path, there can only be one
+ if (ioValue.getLength())
+ {
+ // There already is output
+ m_sink->diagnose(SourceLoc(), CPPDiagnostics::optionAlreadyDefined, option, ioValue);
+ return SLANG_FAIL;
+ }
+ }
+ else
+ {
+ m_sink->diagnose(SourceLoc(), CPPDiagnostics::requireValueAfterOption, option);
+ return SLANG_FAIL;
+ }
+
+ ioValue = m_args[m_index + 1];
+ m_index += 2;
+ return SLANG_OK;
+}
+
+SlangResult OptionsParser::_parseArgReplaceValue(const char* option, String& ioValue)
+{
+ SLANG_ASSERT(UnownedStringSlice(m_args[m_index]) == option);
+ if (m_index + 1 >= m_argCount)
+ {
+ m_sink->diagnose(SourceLoc(), CPPDiagnostics::requireValueAfterOption, option);
+ return SLANG_FAIL;
+ }
+
+ ioValue = m_args[m_index + 1];
+ m_index += 2;
+ return SLANG_OK;
+}
+
+SlangResult OptionsParser::parse(int argc, const char*const* argv, DiagnosticSink* sink, Options& outOptions)
+{
+ outOptions.reset();
+
+ m_index = 0;
+ m_argCount = argc;
+ m_args = argv;
+ m_sink = sink;
+
+ outOptions.reset();
+
+ while (m_index < m_argCount)
+ {
+ const UnownedStringSlice arg = UnownedStringSlice(argv[m_index]);
+
+ if (arg.getLength() > 0 && arg[0] == '-')
+ {
+ if (arg == "-d")
+ {
+ SLANG_RETURN_ON_FAIL(_parseArgWithValue("-d", outOptions.m_inputDirectory));
+ continue;
+ }
+ else if (arg == "-o")
+ {
+ SLANG_RETURN_ON_FAIL(_parseArgWithValue("-o", outOptions.m_outputPath));
+ continue;
+ }
+ else if (arg == "-dump")
+ {
+ outOptions.m_dump = true;
+ m_index++;
+ continue;
+ }
+ else if (arg == "-reflect-type")
+ {
+ SLANG_RETURN_ON_FAIL(_parseArgWithValue("-reflect-type", outOptions.m_reflectType));
+ continue;
+ }
+ else if (arg == "-prefix-mark")
+ {
+ SLANG_RETURN_ON_FAIL(_parseArgReplaceValue("-prefix-mark", outOptions.m_prefixMark));
+ continue;
+ }
+ else if (arg == "-postfix-mark")
+ {
+ SLANG_RETURN_ON_FAIL(_parseArgReplaceValue("-postfix-mark", outOptions.m_postfixMark));
+ continue;
+ }
+
+ m_sink->diagnose(SourceLoc(), CPPDiagnostics::unknownOption, arg);
+ return SLANG_FAIL;
+ }
+ else
+ {
+ // If it starts with - then it an unknown option
+ outOptions.m_inputPaths.add(arg);
+ m_index++;
+ }
+ }
+
+ if (outOptions.m_inputPaths.getCount() < 0)
+ {
+ m_sink->diagnose(SourceLoc(), CPPDiagnostics::noInputPathsSpecified);
+ return SLANG_FAIL;
+ }
+
+ // Set default name
+ if (outOptions.m_reflectType.getLength() == 0)
+ {
+ outOptions.m_reflectType = "ASTNode";
+ }
+
+ return SLANG_OK;
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPExtractor !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+class CPPExtractor
+{
+public:
+
+ SlangResult expect(TokenType type, Token* outToken = nullptr);
+
+ bool advanceIfMarker(Token* outToken = nullptr);
+ bool advanceIfToken(TokenType type, Token* outToken = nullptr);
+ bool advanceIfStyle(IdentifierStyle style, Token* outToken = nullptr);
+
+ SlangResult pushAnonymousNamespace();
+ SlangResult pushNode(Node* node);
+ SlangResult consumeToClosingBrace(const Token* openBraceToken = nullptr);
+ SlangResult popBrace();
+
+ /// Parse the contents of the source file
+ SlangResult parse(SourceFile* sourceFile, const Options* options);
+
+ /// 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();
+
+ /// Only valid after calcDerivedTypes has been executed
+ const List<Node*>& getBaseTypes() const { return m_baseTypes; }
+
+ /// Get the root node
+ Node* getRootNode() const { return m_rootNode; }
+
+ CPPExtractor(StringSlicePool* typePool, NamePool* namePool, DiagnosticSink* sink, IdentifierLookup* identifierLookup);
+
+protected:
+ static Node::Type _toNodeType(IdentifierStyle style);
+
+ bool _isMarker(const UnownedStringSlice& name)
+ {
+ return name.startsWith(m_options->m_prefixMark.getUnownedSlice()) && name.endsWith(m_options->m_postfixMark.getUnownedSlice());
+ }
+
+ SlangResult _maybeParseNode(Node::Type type);
+ SlangResult _maybeParseField();
+
+ SlangResult _maybeParseType(UnownedStringSlice& outType);
+
+ SlangResult _maybeParseType(UnownedStringSlice& outType, Index& ioTemplateDepth);
+ SlangResult _maybeParseTemplateArgs(Index& ioTemplateDepth);
+ SlangResult _maybeParseTemplateArg(Index& ioTemplateDepth);
+
+ SlangResult _calcDerivedTypesRec(Node* node);
+
+ void _consumeTypeModifiers();
+
+ SlangResult _consumeToSync();
+
+ TokenList m_tokenList;
+ TokenReader m_reader;
+
+ Node* m_currentNode; ///< The current scope being processed
+
+ RefPtr<Node> m_rootNode; ///< The root scope
+
+ List<Node*> m_baseTypes; ///< All of the types which are base. Only set after calcDerivedTypes
+
+ DiagnosticSink* m_sink;
+
+ NamePool* m_namePool;
+
+ const Options* m_options;
+
+ IdentifierLookup* m_identifierLookup;
+ StringSlicePool* m_typePool;
+};
+
+CPPExtractor::CPPExtractor(StringSlicePool* typePool, NamePool* namePool, DiagnosticSink* sink, IdentifierLookup* identifierLookup):
+ m_typePool(typePool),
+ m_sink(sink),
+ m_namePool(namePool),
+ m_identifierLookup(identifierLookup)
+{
+ m_rootNode = new Node(Node::Type::Namespace);
+}
+
+SlangResult CPPExtractor::expect(TokenType type, Token* outToken)
+{
+ if (m_reader.peekTokenType() != type)
+ {
+ m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::expectingToken, type);
+ return SLANG_FAIL;
+ }
+
+ if (outToken)
+ {
+ *outToken = m_reader.advanceToken();
+ }
+ else
+ {
+ m_reader.advanceToken();
+ }
+ return SLANG_OK;
+}
+
+bool CPPExtractor::advanceIfToken(TokenType type, Token* outToken)
+{
+ if (m_reader.peekTokenType() == type)
+ {
+ Token token = m_reader.advanceToken();
+ if (outToken)
+ {
+ *outToken = token;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool CPPExtractor::advanceIfMarker(Token* outToken)
+{
+ const Token peekToken = m_reader.peekToken();
+ if (peekToken.type == TokenType::Identifier && _isMarker(peekToken.Content))
+ {
+ m_reader.advanceToken();
+ if (outToken)
+ {
+ *outToken = peekToken;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool CPPExtractor::advanceIfStyle(IdentifierStyle style, Token* outToken)
+{
+ if (m_reader.peekTokenType() == TokenType::Identifier)
+ {
+ IdentifierStyle readStyle = m_identifierLookup->get(m_reader.peekToken().Content);
+ if (readStyle == style)
+ {
+ Token token = m_reader.advanceToken();
+ if (outToken)
+ {
+ *outToken = token;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+
+SlangResult CPPExtractor::pushAnonymousNamespace()
+{
+ m_currentNode = m_currentNode->getAnonymousNamespace();
+ return SLANG_OK;
+}
+
+SlangResult CPPExtractor::pushNode(Node* node)
+{
+ if (node->m_name.Content.getLength())
+ {
+ // For anonymous namespace, we should look if we already have one and just reopen that. Doing so will mean will
+ // find anonymous namespace clashes
+
+ if (Node* foundNode = m_currentNode->findChild(node->m_name.Content))
+ {
+ if (node->isClassLike())
+ {
+ m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::typeAlreadyDeclared, node->m_name.Content);
+ m_sink->diagnose(foundNode->m_name, CPPDiagnostics::seeDeclarationOf, node->m_name.Content);
+ return SLANG_FAIL;
+ }
+
+ if (node->m_type == Node::Type::Namespace)
+ {
+ // Make sure the node is empty, as we are *not* going to add it, we are just going to use
+ // the pre-existing namespace
+ SLANG_ASSERT(node->m_children.getCount() == 0);
+
+ // We can just use the pre-existing namespace
+ m_currentNode = foundNode;
+ return SLANG_OK;
+ }
+ }
+ }
+
+ m_currentNode->addChild(node);
+ m_currentNode = node;
+ return SLANG_OK;
+}
+
+SlangResult CPPExtractor::consumeToClosingBrace(const Token* inOpenBraceToken)
+{
+ Token openToken;
+ if (inOpenBraceToken)
+ {
+ openToken = *inOpenBraceToken;
+ }
+ else
+ {
+ openToken = m_reader.advanceToken();
+ }
+
+ while (true)
+ {
+ switch (m_reader.peekTokenType())
+ {
+ case TokenType::EndOfFile:
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::didntFindMatchingBrace);
+ m_sink->diagnose(openToken, CPPDiagnostics::seeOpenBrace);
+ return SLANG_FAIL;
+ }
+ case TokenType::LBrace:
+ {
+ SLANG_RETURN_ON_FAIL(consumeToClosingBrace());
+ break;
+ }
+ case TokenType::RBrace:
+ {
+ m_reader.advanceToken();
+ return SLANG_OK;
+ }
+ default:
+ {
+ m_reader.advanceToken();
+ break;
+ }
+ }
+ }
+}
+
+SlangResult CPPExtractor::popBrace()
+{
+ if (m_currentNode->m_parentScope == nullptr)
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::scopeNotClosed);
+ return SLANG_FAIL;
+ }
+
+ m_currentNode = m_currentNode->m_parentScope;
+ return SLANG_OK;
+}
+
+SlangResult CPPExtractor::_maybeParseNode(Node::Type type)
+{
+ // We are looking for
+ // struct/class identifier [: [public|private|protected] Identifier ] { [public|private|proctected:]* marker ( identifier );
+
+ if (type == Node::Type::Namespace)
+ {
+ // consume namespace
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier));
+
+ Token name;
+ if (advanceIfToken(TokenType::LBrace))
+ {
+ return pushAnonymousNamespace();
+ }
+ else if (advanceIfToken(TokenType::Identifier, &name))
+ {
+ if (advanceIfToken(TokenType::LBrace))
+ {
+ // Okay looks like we are opening a namespace
+ RefPtr<Node> node(new Node(Node::Type::Namespace));
+ node->m_name = name;
+ // Push the node
+ return pushNode(node);
+ }
+ }
+
+ // Just ignore it then
+ return SLANG_OK;
+ }
+
+ // Must be class | struct
+
+ SLANG_ASSERT(type == Node::Type::ClassType || type == Node::Type::StructType);
+
+ Token name;
+
+ // consume class | struct
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier));
+ // Next is the class name
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &name));
+
+ if (m_reader.peekTokenType() == TokenType::Semicolon)
+ {
+ // pre declaration;
+ return SLANG_OK;
+ }
+
+ RefPtr<Node> node(new Node(type));
+ node->m_name = name;
+
+ if (advanceIfToken(TokenType::Colon))
+ {
+ // Could have public
+ advanceIfStyle(IdentifierStyle::Access);
+
+ if (!advanceIfToken(TokenType::Identifier, &node->m_super))
+ {
+ return SLANG_OK;
+ }
+ }
+
+ if (m_reader.peekTokenType() != TokenType::LBrace)
+ {
+ // Consume up until we see a brace else it's an error
+ while (true)
+ {
+ const TokenType peekTokenType = m_reader.peekTokenType();
+ if (peekTokenType == TokenType::EndOfFile)
+ {
+ // Expecting brace
+ m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::expectingToken, TokenType::LBrace);
+ return SLANG_FAIL;
+ }
+ else if (peekTokenType == TokenType::LBrace)
+ {
+ break;
+ }
+ m_reader.advanceToken();
+ }
+
+ // Node does define a class, but it's not reflected
+ node->m_isReflected = false;
+ return pushNode(node);
+ }
+
+ Token braceToken = m_reader.advanceToken();
+
+ while (true)
+ {
+ // Okay now we are looking for the markers, or visibility qualifiers
+ if (advanceIfStyle(IdentifierStyle::Access))
+ {
+ // Consume it and a colon
+ if (SLANG_FAILED(expect(TokenType::Colon)))
+ {
+ consumeToClosingBrace(&braceToken);
+ return SLANG_OK;
+ }
+ continue;
+ }
+
+ switch (m_reader.peekTokenType())
+ {
+ case TokenType::Identifier: break;
+ case TokenType::RBrace:
+ {
+ node->m_isReflected = false;
+ SLANG_RETURN_ON_FAIL(pushNode(node));
+ SLANG_RETURN_ON_FAIL(popBrace());
+ m_reader.advanceToken();
+ return SLANG_OK;
+ }
+ default:
+ {
+ node->m_isReflected = false;
+ SLANG_RETURN_ON_FAIL(pushNode(node));
+ return SLANG_OK;
+ }
+ }
+
+ // If it's one of the markers, then we continue to extract parameter
+ if (advanceIfMarker(&node->m_marker))
+ {
+ break;
+ }
+
+ // Looks like a class, but looks like non-reflected
+ node->m_isReflected = false;
+ // We still need to add the node,
+ SLANG_RETURN_ON_FAIL(pushNode(node));
+ return SLANG_OK;
+ }
+
+ // Okay now looking for ( identifier)
+ Token typeNameToken;
+
+ SLANG_RETURN_ON_FAIL(expect(TokenType::LParent));
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &typeNameToken));
+ SLANG_RETURN_ON_FAIL(expect(TokenType::RParent));
+
+ if (typeNameToken.Content != node->m_name.Content)
+ {
+ m_sink->diagnose(typeNameToken, CPPDiagnostics::typeNameDoesntMatch, node->m_name.Content);
+ return SLANG_FAIL;
+ }
+
+ node->m_isReflected = true;
+ return pushNode(node);
+}
+
+SlangResult CPPExtractor::_consumeToSync()
+{
+ while (true)
+ {
+ TokenType type = m_reader.peekTokenType();
+
+ switch (type)
+ {
+ case TokenType::Semicolon:
+ {
+ m_reader.advanceToken();
+ return SLANG_OK;
+ }
+ case TokenType::Pound:
+ case TokenType::EndOfFile:
+ case TokenType::LBrace:
+ case TokenType::RBrace:
+ {
+ return SLANG_OK;
+ }
+ }
+
+ m_reader.advanceToken();
+ }
+}
+
+SlangResult CPPExtractor::_maybeParseTemplateArg(Index& ioTemplateDepth)
+{
+ switch (m_reader.peekTokenType())
+ {
+ case TokenType::Identifier:
+ {
+ UnownedStringSlice name;
+ SLANG_RETURN_ON_FAIL(_maybeParseType(name, ioTemplateDepth));
+ return SLANG_OK;
+ }
+ case TokenType::IntegerLiteral:
+ {
+ m_reader.advanceToken();
+ return SLANG_OK;
+ }
+ default: break;
+ }
+ return SLANG_FAIL;
+}
+
+SlangResult CPPExtractor::_maybeParseTemplateArgs(Index& ioTemplateDepth)
+{
+ if (!advanceIfToken(TokenType::OpLess))
+ {
+ return SLANG_FAIL;
+ }
+
+ ioTemplateDepth++;
+
+ while (true)
+ {
+ if (ioTemplateDepth == 0)
+ {
+ return SLANG_OK;
+ }
+
+ switch (m_reader.peekTokenType())
+ {
+ case TokenType::OpGreater:
+ {
+ if (ioTemplateDepth <= 0)
+ {
+ m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::unexpectedTemplateClose);
+ return SLANG_FAIL;
+ }
+ ioTemplateDepth--;
+ m_reader.advanceToken();
+ return SLANG_OK;
+ }
+ case TokenType::OpRsh:
+ {
+ if (ioTemplateDepth <= 1)
+ {
+ m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::unexpectedTemplateClose);
+ return SLANG_FAIL;
+ }
+ ioTemplateDepth -= 2;
+ m_reader.advanceToken();
+ return SLANG_OK;
+ }
+ default:
+ {
+ while (true)
+ {
+ SLANG_RETURN_ON_FAIL(_maybeParseTemplateArg(ioTemplateDepth));
+
+ if (m_reader.peekTokenType() == TokenType::Comma)
+ {
+ m_reader.advanceToken();
+ // If there is a comma parse another arg
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+ }
+ }
+}
+
+void CPPExtractor::_consumeTypeModifiers()
+{
+ while (advanceIfStyle(IdentifierStyle::TypeModifier));
+}
+
+SlangResult CPPExtractor::_maybeParseType(UnownedStringSlice& outType, Index& ioTemplateDepth)
+{
+ auto startCursor = m_reader.getCursor();
+
+ _consumeTypeModifiers();
+
+ advanceIfToken(TokenType::Scope);
+ while (true)
+ {
+ Token identifierToken;
+ if (!advanceIfToken(TokenType::Identifier, &identifierToken))
+ {
+ return SLANG_FAIL;
+ }
+
+ const IdentifierStyle style = m_identifierLookup->get(identifierToken.Content);
+ if (hasFlag(style, IdentifierFlag::Keyword))
+ {
+ return SLANG_FAIL;
+ }
+
+ if (advanceIfToken(TokenType::Scope))
+ {
+ continue;
+ }
+ break;
+ }
+
+ if (m_reader.peekTokenType() == TokenType::OpLess)
+ {
+ SLANG_RETURN_ON_FAIL(_maybeParseTemplateArgs(ioTemplateDepth));
+ }
+
+ // Strip all the consts etc modifiers
+ _consumeTypeModifiers();
+
+ // It's a reference and we are done
+ if (advanceIfToken(TokenType::OpBitAnd))
+ {
+ return SLANG_OK;
+ }
+
+ while (true)
+ {
+ if (advanceIfToken(TokenType::OpMul))
+ {
+ // Strip all the consts
+ _consumeTypeModifiers();
+ continue;
+ }
+ break;
+ }
+
+ // We can build up the out type, from the tokens we found
+ auto endCursor = m_reader.getCursor();
+
+ m_reader.setCursor(startCursor);
+
+ StringBuilder buf;
+ while (!m_reader.isAtCursor(endCursor))
+ {
+ Token token = m_reader.advanceToken();
+ // Concat the type.
+ buf << token.Content;
+ }
+
+ auto handle = m_typePool->add(buf);
+
+ outType = m_typePool->getSlice(handle);
+ return SLANG_OK;
+}
+
+SlangResult CPPExtractor::_maybeParseType(UnownedStringSlice& outType)
+{
+ Index templateDepth = 0;
+ SlangResult res = _maybeParseType(outType, templateDepth);
+ if (SLANG_FAILED(res) && m_sink->errorCount)
+ {
+ return res;
+ }
+
+ if (templateDepth != 0)
+ {
+ m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::unexpectedTemplateClose);
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
+}
+
+SlangResult CPPExtractor::_maybeParseField()
+{
+ Node::Field field;
+
+ UnownedStringSlice typeName;
+ if (SLANG_FAILED(_maybeParseType(typeName)))
+ {
+ if (m_sink->errorCount)
+ {
+ return SLANG_FAIL;
+ }
+
+ _consumeToSync();
+ return SLANG_OK;
+ }
+
+ if (m_reader.peekTokenType() != TokenType::Identifier)
+ {
+ _consumeToSync();
+ return SLANG_OK;
+ }
+
+ Token fieldName = m_reader.advanceToken();
+
+ switch (m_reader.peekTokenType())
+ {
+ case TokenType::OpAssign:
+ case TokenType::Semicolon:
+ {
+ Node::Field field;
+ field.type = typeName;
+ field.name = fieldName;
+
+ m_currentNode->m_fields.add(field);
+
+ break;
+ }
+ default: break;
+ }
+
+ _consumeToSync();
+ return SLANG_OK;
+}
+
+/* static */Node::Type CPPExtractor::_toNodeType(IdentifierStyle style)
+{
+ switch (style)
+ {
+ case IdentifierStyle::Class: return Node::Type::ClassType;
+ case IdentifierStyle::Struct: return Node::Type::StructType;
+ case IdentifierStyle::Namespace: return Node::Type::Namespace;
+ default: return Node::Type::Invalid;
+ }
+}
+
+SlangResult CPPExtractor::parse(SourceFile* sourceFile, const Options* options)
+{
+ m_options = options;
+
+ SourceManager* manager = sourceFile->getSourceManager();
+
+ SourceView* sourceView = manager->createSourceView(sourceFile, nullptr);
+
+ Lexer lexer;
+
+ m_currentNode = m_rootNode;
+
+ lexer.initialize(sourceView, m_sink, m_namePool, manager->getMemoryArena());
+ m_tokenList = lexer.lexAllTokens();
+ // See if there were any errors
+ if (m_sink->errorCount)
+ {
+ return SLANG_FAIL;
+ }
+
+ m_reader = TokenReader(m_tokenList);
+
+ while (true)
+ {
+ switch (m_reader.peekTokenType())
+ {
+ case TokenType::Identifier:
+ {
+ IdentifierStyle style = m_identifierLookup->get(m_reader.peekToken().Content);
+
+ switch (style)
+ {
+ case IdentifierStyle::BaseClass:
+ {
+ m_reader.advanceToken();
+
+ Token nameToken;
+ SLANG_RETURN_ON_FAIL(expect(TokenType::LParent));
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &nameToken));
+ SLANG_RETURN_ON_FAIL(expect(TokenType::RParent));
+
+ RefPtr<Node> node(new Node(Node::Type::ClassType));
+ node->m_name = nameToken;
+ node->m_baseType = Node::BaseType::Marked;
+
+ SLANG_RETURN_ON_FAIL(pushNode(node));
+ popBrace();
+ break;
+ }
+ case IdentifierStyle::Root:
+ {
+ if (m_currentNode && m_currentNode->isClassLike())
+ {
+ m_currentNode->m_baseType = Node::BaseType::Marked;
+ }
+ m_reader.advanceToken();
+ break;
+ }
+ default:
+ {
+ IdentifierFlags flags = getFlags(style);
+
+ if (flags & IdentifierFlag::StartScope)
+ {
+ Node::Type type = _toNodeType(style);
+ SLANG_RETURN_ON_FAIL(_maybeParseNode(type));
+ }
+ else
+ {
+ // Special case the node that's the root of the hierarchy (as far as reflection is concerned)
+ // This could be a field
+ if (m_currentNode->acceptsFields())
+ {
+ SLANG_RETURN_ON_FAIL(_maybeParseField());
+ }
+ else
+ {
+ m_reader.advanceToken();
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case TokenType::LBrace:
+ {
+ SLANG_RETURN_ON_FAIL(consumeToClosingBrace());
+ break;
+ }
+ case TokenType::RBrace:
+ {
+ SLANG_RETURN_ON_FAIL(popBrace());
+ m_reader.advanceToken();
+ break;
+ }
+ case TokenType::EndOfFile:
+ {
+ // Okay we need to confirm that we are in the root node, and with no open braces
+ if (m_currentNode != m_rootNode)
+ {
+ m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::braceOpenAtEndOfFile);
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+ }
+ case TokenType::Pound:
+ {
+ Token token = m_reader.peekToken();
+ if (token.flags & TokenFlag::AtStartOfLine)
+ {
+ // We are just going to ignore all of these for now....
+ m_reader.advanceToken();
+ while (m_reader.peekTokenType() != TokenType::EndOfDirective && m_reader.peekTokenType() != TokenType::EndOfFile)
+ {
+ m_reader.advanceToken();
+ }
+ break;
+ }
+ // Skip it then
+ m_reader.advanceToken();
+ break;
+ }
+ default:
+ {
+ // Skip it then
+ m_reader.advanceToken();
+ break;
+ }
+ }
+ }
+}
+
+SlangResult CPPExtractor::_calcDerivedTypesRec(Node* node)
+{
+ if (node->isClassLike() && node->m_baseType == Node::BaseType::None)
+ {
+ if (node->m_super.Content.getLength())
+ {
+ Node* parentScope = node->m_parentScope;
+ if (parentScope == nullptr)
+ {
+ m_sink->diagnoseRaw(Severity::Error, UnownedStringSlice::fromLiteral("Can't lookup in scope if there is none!"));
+ return SLANG_FAIL;
+ }
+
+ Node* superType = parentScope->findChild(node->m_super.Content);
+ if (!superType)
+ {
+ if (node->m_isReflected)
+ {
+ m_sink->diagnose(node->m_name, CPPDiagnostics::superTypeNotFound, node->m_name.Content);
+ return SLANG_FAIL;
+ }
+ }
+ else
+ {
+ if (!superType->isClassLike())
+ {
+ m_sink->diagnose(node->m_name, CPPDiagnostics::superTypeNotAType, node->m_name.Content);
+ return SLANG_FAIL;
+ }
+
+ // The base class must be defined in same scope (as we didn't allow different scopes for base classes)
+
+ superType->addDerived(node);
+ }
+ }
+ else
+ {
+ // If it has no super class defined, then we can just make it a root without being set
+ node->m_baseType = Node::BaseType::Unmarked;
+ }
+ }
+
+ if (node->m_baseType != Node::BaseType::None)
+ {
+ m_baseTypes.add(node);
+ }
+
+ for (Node* child : node->m_children)
+ {
+ SLANG_RETURN_ON_FAIL(_calcDerivedTypesRec(child));
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult CPPExtractor::calcDerivedTypes()
+{
+ return _calcDerivedTypesRec(m_rootNode);
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPExtractorApp !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+class CPPExtractorApp
+{
+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(CPPExtractor& extractor);
+
+ /// Calculate the header
+ SlangResult calcHeader(CPPExtractor& extractor, StringBuilder& out);
+ SlangResult calcMacroHeader(CPPExtractor& exctractor, StringBuilder& out);
+
+ CPPExtractorApp(DiagnosticSink* sink, SourceManager* sourceManager, RootNamePool* rootNamePool):
+ m_sink(sink),
+ m_sourceManager(sourceManager),
+ m_slicePool(StringSlicePool::Style::Default)
+ {
+ m_namePool.setRootNamePool(rootNamePool);
+
+ // Some keywords
+ {
+ const char* names[] = { "virtual", "typedef", "continue", "if", "case", "break", "catch", "default", "delete", "do", "else", "for", "new", "goto", "return", "switch", "throw", "using", "while" };
+ m_identifierLookup.set(names, SLANG_COUNT_OF(names), IdentifierStyle::Keyword);
+ }
+
+ // Type modifier keywords
+ {
+ const char* names[] = { "const", "volatile" };
+ m_identifierLookup.set(names, SLANG_COUNT_OF(names), IdentifierStyle::TypeModifier);
+ }
+
+ // Special markers
+ {
+ m_identifierLookup.set("SLANG_CLASS_ROOT", IdentifierStyle::Root);
+ m_identifierLookup.set("SLANG_REFLECT_BASE_CLASS", IdentifierStyle::BaseClass);
+ }
+
+ // Keywords which introduce types/scopes
+ {
+ m_identifierLookup.set("struct", IdentifierStyle::Struct);
+ m_identifierLookup.set("class", IdentifierStyle::Class);
+ m_identifierLookup.set("namespace", IdentifierStyle::Namespace);
+ }
+
+ // Keywords that control access
+ {
+ const char* names[] = { "private", "protected", "public" };
+ m_identifierLookup.set(names, SLANG_COUNT_OF(names), IdentifierStyle::Access);
+ }
+ }
+
+
+protected:
+ NamePool m_namePool;
+
+ Options m_options;
+ DiagnosticSink* m_sink;
+ SourceManager* m_sourceManager;
+ IdentifierLookup m_identifierLookup;
+
+ StringSlicePool m_slicePool;
+};
+
+SlangResult CPPExtractorApp::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 CPPExtractorApp::writeAllText(const Slang::String& fileName, const UnownedStringSlice& text)
+{
+ try
+ {
+ 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 CPPExtractorApp::calcMacroHeader(CPPExtractor& extractor, StringBuilder& out)
+{
+ const List<Node*>& baseTypes = extractor.getBaseTypes();
+
+ const String& reflectTypeName = m_options.m_reflectType;
+
+ out << "#pragma once\n\n";
+ out << "// Do not edit this file is generated from slang-cpp-extractor tool\n\n";
+
+ for (Index i = 0; i < baseTypes.getCount(); ++i)
+ {
+ Node* baseType = baseTypes[i];
+ if (baseType->m_baseType != Node::BaseType::Marked)
+ {
+ continue;
+ }
+
+ List<Node*> nodes;
+ baseType->calcDerivedDepthFirst(nodes);
+
+ for (Index j = 0; j < nodes.getCount(); j++)
+ {
+ Node* node = nodes[j];
+
+ if (!node->isClassLike() || !node->m_isReflected)
+ {
+ continue;
+ }
+
+ // Define the derived types
+ out << "#define " << m_options.m_prefixMark << "DERIVED_" << reflectTypeName << "_" << node->m_name.Content << "(x, param) \\\n";
+ for (Node* derivedType : node->m_derivedTypes)
+ {
+ if (!derivedType->m_isReflected)
+ {
+ continue;
+ }
+ _indent(1, out);
+ out << "SLANG_" << reflectTypeName << "_" << derivedType->m_name.Content << "(x, param) \\\n";
+ }
+ out << "/* */\n";
+
+
+ out << "#define SLANG_" << reflectTypeName << "_" << node->m_name.Content << "(x, param) \\\n";
+
+ // Output the X macro part
+ _indent(1, out);
+ out << "x(" << node->m_name.Content << ", ";
+
+ UnownedStringSlice marker = node->m_marker.Content;
+ // Need to extract the name
+ if (marker.getLength() > m_options.m_prefixMark.getLength() + m_options.m_postfixMark.getLength())
+ {
+ marker = UnownedStringSlice(marker.begin() + m_options.m_prefixMark.getLength(), marker.end() - m_options.m_postfixMark.getLength());
+ }
+ else
+ {
+ marker = UnownedStringSlice::fromLiteral("NORMAL");
+ }
+ out << marker << ", ";
+
+ if (node->m_baseType != Node::BaseType::None || node->m_superNode && node->m_superNode->m_isReflected == false)
+ {
+ out << "BASE, ";
+ }
+ else if (node->hasReflectedDerivedType())
+ {
+ out << "INNER, ";
+ }
+ else
+ {
+ out << "LEAF, ";
+ }
+
+ out << "param) \\\n";
+
+ // Reference the derived types
+ _indent(1, out);
+ out << m_options.m_prefixMark << "DERIVED_" << reflectTypeName << "_" << node->m_name.Content << "(x, param) \n";
+
+ out << "\n";
+ }
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult CPPExtractorApp::calcHeader(CPPExtractor& extractor, StringBuilder& out)
+{
+ const List<Node*>& baseTypes = extractor.getBaseTypes();
+
+ const String& reflectTypeName = m_options.m_reflectType;
+
+ out << "#pragma once\n\n";
+ out << "// Do not edit this file is generated from slang-cpp-extractor tool\n\n";
+
+ for (Index i = 0; i < baseTypes.getCount(); ++i)
+ {
+ Node* baseType = baseTypes[i];
+ if (baseType->m_baseType != Node::BaseType::Marked)
+ {
+ continue;
+ }
+
+ List<Node*> baseScopePath;
+ baseType->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.Content << " {\n";
+ }
+
+ List<Node*> nodes;
+ baseType->calcDerivedDepthFirst(nodes);
+
+ out << "\n";
+ out << "enum class " << reflectTypeName << "Type\n";
+ out << "{\n";
+
+
+ Index typeIndex = 0;
+
+ for (Node* node : nodes)
+ {
+ SLANG_ASSERT(node->isClassLike());
+ // If it's not reflected we don't output, in the enum list
+ if (!node->m_isReflected)
+ {
+ continue;
+ }
+
+ // Okay first we are going to output the enum values
+ const Index depth = node->calcDerivedDepth() - 1;
+ _indent(depth, out);
+
+ out << node->m_name.Content << " = " << typeIndex << ",\n";
+
+ typeIndex++;
+ }
+
+ out << "};\n\n";
+
+ out << "\n";
+ out << "enum class " << reflectTypeName << "Last\n";
+ out << "{\n";
+
+ for (Node* node : nodes)
+ {
+ SLANG_ASSERT(node->isClassLike());
+ // If it's not reflected we don't output, in the enum list
+ if (!node->m_isReflected)
+ {
+ continue;
+ }
+
+ Node* lastDerived = node->findLastDerived();
+ if (lastDerived)
+ {
+ // Okay first we are going to output the enum values
+ const Index depth = node->calcDerivedDepth() - 1;
+ _indent(depth, out);
+ out << node->m_name.Content << " = int(" << reflectTypeName << "Type::" << lastDerived->m_name.Content << "),\n";
+ }
+ }
+
+ out << "};\n\n";
+
+ // Predeclare
+
+ out << "// Predeclare\n\n";
+ for (Node* node : nodes)
+ {
+ SLANG_ASSERT(node->isClassLike());
+ // If it's not reflected we don't output, in the enum list
+ if (node->m_isReflected)
+ {
+ const char* type = (node->m_type == Node::Type::ClassType) ? "class" : "struct";
+ out << type << " " << node->m_name.Content << ";\n";
+ }
+ }
+
+ out << "struct " << reflectTypeName << "Super\n";
+ out << "{\n";
+
+ for (Node* node : nodes)
+ {
+ // If it's not reflected we don't output, in the enum list
+ if (node->m_isReflected && node->m_superNode)
+ {
+ _indent(1, out);
+ // We concat _Super so the typedef are distinct from the ones being referenced
+ out << "typedef " << node->m_superNode->m_name.Content << " " << node->m_name.Content << "_Super;\n";
+ }
+ }
+
+ out << "};\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.Content << "\n";
+ }
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult CPPExtractorApp::writeOutput(CPPExtractor& extractor)
+{
+
+ 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);
+
+ {
+ /// Calculate the header
+ StringBuilder header;
+ SLANG_RETURN_ON_FAIL(calcHeader(extractor, header));
+
+ // Write it out
+
+ StringBuilder headerPath;
+ headerPath << path << "." << ext;
+ SLANG_RETURN_ON_FAIL(writeAllText(headerPath, header.getUnownedSlice()));
+ }
+
+ {
+ StringBuilder macroHeader;
+
+ SLANG_RETURN_ON_FAIL(calcMacroHeader(extractor, macroHeader));
+
+ StringBuilder headerPath;
+ headerPath << path << "-macro." + ext;
+ SLANG_RETURN_ON_FAIL(writeAllText(headerPath, macroHeader.getUnownedSlice()));
+ }
+
+ // Write to output
+ // m_sink->writer->write(header.getBuffer(), header.getLength());
+
+ return SLANG_OK;
+}
+
+
+SlangResult CPPExtractorApp::execute(const Options& options)
+{
+ m_options = options;
+
+ CPPExtractor extractor(&m_slicePool, &m_namePool, m_sink, &m_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);
+
+ SLANG_RETURN_ON_FAIL(extractor.parse(sourceFile, &m_options));
+ }
+
+ SLANG_RETURN_ON_FAIL(extractor.calcDerivedTypes());
+
+ // Dump out the tree
+ if (options.m_dump)
+ {
+ {
+ StringBuilder buf;
+ extractor.getRootNode()->dump(0, buf);
+ m_sink->writer->write(buf.getBuffer(), buf.getLength());
+ }
+
+ {
+ const List<Node*>& baseTypes = extractor.getBaseTypes();
+
+ for (Node* baseType : baseTypes)
+ {
+ StringBuilder buf;
+ baseType->dumpDerived(0, buf);
+ m_sink->writer->write(buf.getBuffer(), buf.getLength());
+ }
+ }
+ }
+
+ if (options.m_outputPath.getLength())
+ {
+ SLANG_RETURN_ON_FAIL(writeOutput(extractor));
+ }
+
+ return SLANG_OK;
+}
+
+/// Execute
+SlangResult CPPExtractorApp::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 SlangExperimental
+
+int main(int argc, const char*const* argv)
+{
+ using namespace SlangExperimental;
+ using namespace Slang;
+
+ {
+ RootNamePool rootNamePool;
+
+ SourceManager sourceManager;
+ sourceManager.initialize(nullptr, nullptr);
+
+ ComPtr<ISlangWriter> writer(new FileWriter(stderr, WriterFlag::AutoFlush));
+
+ DiagnosticSink sink(&sourceManager);
+ sink.writer = writer;
+
+ CPPExtractorApp app(&sink, &sourceManager, &rootNamePool);
+ if (SLANG_FAILED(app.executeWithArgs(argc - 1, argv + 1)))
+ {
+ return 1;
+ }
+ if (sink.errorCount)
+ {
+ return 1;
+ }
+
+ }
+ return 0;
+}
+