diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2020-05-20 10:56:49 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-05-20 10:56:49 -0400 |
| commit | 96a00c8e8d14964c8e1f45c7c3c85d321b2a1b61 (patch) | |
| tree | 5cda70f1c650db831afa4b3efb1b0d2bda497856 | |
| parent | c54c957d2e647d2f9bfdc0bf31561fca5a02c5df (diff) | |
AST dumping via C++ Extractor reflection (#1348)
* Add support for parsing array types to C++ extractor.
* C++ extractor looks for 'balanced tokens'. Use for extracting array suffixes.
* First pass at field dumping.
* Update project for field dumping.
* WIP AST Dumper.
* More AST dump compiling.
* Fix bug in StringSlicePool where it doesn't use the copy of the UnownedStringSlice in the map.
* Add support for SLANG_RELFECTED and SLANG_UNREFLECTED
More AST dump support.
* Support for hierarchical dumping/flat dumping.
Use SourceWriter to dump.
* Add -dump-ast command line option.
* Add fixes to VS project to incude AST dump.
* Fix compilation on gcc.
* Add fix for type ambiguity issue on x86 VS.
* Fixes from merge of reducing Token size.
* Fix comment about using SourceWriter.
| -rw-r--r-- | premake5.lua | 2 | ||||
| -rw-r--r-- | source/core/slang-string-slice-pool.cpp | 13 | ||||
| -rw-r--r-- | source/slang/slang-ast-base.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-ast-decl.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-ast-dump.cpp | 613 | ||||
| -rw-r--r-- | source/slang/slang-ast-dump.h | 28 | ||||
| -rw-r--r-- | source/slang/slang-ast-reflect.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-compiler.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-options.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 21 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj | 10 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj.filters | 6 | ||||
| -rw-r--r-- | tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp | 317 |
13 files changed, 899 insertions, 123 deletions
diff --git a/premake5.lua b/premake5.lua index d148ce6dd..93d310ee0 100644 --- a/premake5.lua +++ b/premake5.lua @@ -784,7 +784,7 @@ standardProject "slang" -- path with forward slashes, which confused the shell if we don't -- quote the executable path. - local buildcmd = '"%{cfg.targetdir}/slang-cpp-extractor" -d ' .. sourcePath .. " " .. table.concat(inputFiles, " ") .. " -strip-prefix slang-ast- -o slang-ast-generated" + local buildcmd = '"%{cfg.targetdir}/slang-cpp-extractor" -d ' .. sourcePath .. " " .. table.concat(inputFiles, " ") .. " -strip-prefix slang-ast- -o slang-ast-generated -output-fields" buildcommands { buildcmd } diff --git a/source/core/slang-string-slice-pool.cpp b/source/core/slang-string-slice-pool.cpp index 24187ba5c..a0af3ba68 100644 --- a/source/core/slang-string-slice-pool.cpp +++ b/source/core/slang-string-slice-pool.cpp @@ -61,16 +61,23 @@ StringSlicePool::Handle StringSlicePool::add(const Slice& slice) bool StringSlicePool::findOrAdd(const Slice& slice, Handle& outHandle) { - Handle newHandle = Handle(m_slices.getCount()); - const Handle* handlePtr = m_map.TryGetValueOrAdd(slice, newHandle); + const Handle* handlePtr = m_map.TryGetValue(slice); if (handlePtr) { outHandle = *handlePtr; return true; } - // Need to add + // Need to add. + + // Make a copy stored in the arena UnownedStringSlice scopeSlice(m_arena.allocateString(slice.begin(), slice.getLength()), slice.getLength()); + + // Add using the arenas copy + Handle newHandle = Handle(m_slices.getCount()); + m_map.Add(scopeSlice, newHandle); + + // Add to slices list m_slices.add(scopeSlice); outHandle = newHandle; return false; diff --git a/source/slang/slang-ast-base.h b/source/slang/slang-ast-base.h index 1175f0288..522bdd817 100644 --- a/source/slang/slang-ast-base.h +++ b/source/slang/slang-ast-base.h @@ -118,6 +118,8 @@ class Type: public Val { SLANG_ABSTRACT_CLASS(Type) + friend struct ASTDumpAccess; + typedef ITypeVisitor Visitor; virtual void accept(IValVisitor* visitor, void* extra) override; diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h index 5020debfa..069f1ac11 100644 --- a/source/slang/slang-ast-decl.h +++ b/source/slang/slang-ast-decl.h @@ -441,6 +441,8 @@ class SyntaxDecl : public Decl // What type of syntax node will be produced when parsing with this keyword? SyntaxClass<RefObject> syntaxClass; + SLANG_UNREFLECTED + // Callback to invoke in order to parse syntax with this keyword. SyntaxParseCallback parseCallback; void* parseUserData; diff --git a/source/slang/slang-ast-dump.cpp b/source/slang/slang-ast-dump.cpp new file mode 100644 index 000000000..a6d947619 --- /dev/null +++ b/source/slang/slang-ast-dump.cpp @@ -0,0 +1,613 @@ +// slang-ast-dump.cpp +#include "slang-ast-dump.h" +#include <assert.h> + +#include "slang-compiler.h" + +#include "../core/slang-string.h" + +#include "slang-ast-generated-macro.h" + +namespace Slang { + +namespace { // anonymous + +struct Context +{ + struct ObjectInfo + { + const ReflectClassInfo* m_typeInfo; + RefObject* m_object; + bool m_isDumped; + }; + + struct ScopeWrite + { + ScopeWrite(Context* context): + m_context(context) + { + if (m_context->m_scopeWriteCount == 0) + { + m_context->m_buf.Clear(); + } + m_context->m_scopeWriteCount++; + } + + ~ScopeWrite() + { + if (--m_context->m_scopeWriteCount == 0) + { + m_context->m_writer->emit(m_context->m_buf); + } + } + + StringBuilder& getBuf() { return m_context->m_buf; } + + operator StringBuilder&() { return m_context->m_buf; } + + Context* m_context; + }; + + void dumpObject(const ReflectClassInfo& type, RefObject* obj); + + void dumpObjectFull(const ReflectClassInfo& type, RefObject* obj, Index objIndex); + void dumpObjectReference(const ReflectClassInfo& type, RefObject* obj, Index objIndex); + + void dump(NodeBase* node) + { + if (node == nullptr) + { + dumpPtr(nullptr); + } + else + { + dumpObject(node->getClassInfo(), node); + } + } + + void dump(Substitutions* subs) + { + if (subs == nullptr) + { + dumpPtr(nullptr); + } + else + { + dumpObject(subs->getClassInfo(), subs); + } + } + + void dump(const Name* name) + { + if (name == nullptr) + { + dumpPtr(nullptr); + } + else + { + dump(name->text); + } + } + + void dump(const RefObject* obj) + { + if (obj == nullptr) + { + dumpPtr(nullptr); + } + else + { + // We don't know what this is! + ScopeWrite(this).getBuf() << "Unknown@" << size_t(obj); + } + } + + template <typename T> + void dump(const List<T>& list) + { + m_writer->emit(" { \n"); + m_writer->indent(); + for (Index i = 0; i < list.getCount(); ++i) + { + dump(list[i]); + if (i < list.getCount() - 1) + { + m_writer->emit(",\n"); + } + else + { + m_writer->emit("\n"); + } + } + m_writer->dedent(); + m_writer->emit("}"); + } + + void dump(SourceLoc sourceLoc) + { + SourceManager* manager = m_writer->getSourceManager(); + + { + ScopeWrite(this).getBuf() << "SourceLoc(" << sourceLoc.getRaw() << ")"; + } + + if (manager && sourceLoc.isValid()) + { + HumaneSourceLoc humaneLoc = manager->getHumaneLoc(sourceLoc); + ScopeWrite(this).getBuf() << " " << humaneLoc.pathInfo.foundPath << ":" << humaneLoc.line; + } + } + + static char _getHexDigit(UInt v) + { + return (v < 10) ? char(v + '0') : char('a' + v - 10); + } + + void dump(const UnownedStringSlice& slice) + { + m_writer->emitChar('\"'); + + { + ScopeWrite scope(this); + auto& buf = scope.getBuf(); + for (const char c : slice) + { + if (c < 0x20 || c >= 0x80) + { + buf << "\\0x" << _getHexDigit(UInt32(c) >> 4) << _getHexDigit(c & 0xf); + } + else + { + buf << c; + } + } + } + m_writer->emitChar('\"'); + } + + void dump(const Token& token) + { + ScopeWrite(this).getBuf() << " { " << TokenTypeToString(token.type) << ", "; + dump(token.loc); + dump(token.getContent()); + m_writer->emit(" }"); + } + + Index getObjectIndex(const ReflectClassInfo& typeInfo, RefObject* obj) + { + Index* indexPtr = m_objectMap.TryGetValueOrAdd(obj, m_objects.getCount()); + if (indexPtr) + { + return *indexPtr; + } + + ObjectInfo info; + info.m_isDumped = false; + info.m_object = obj; + info.m_typeInfo = &typeInfo; + + m_objects.add(info); + return m_objects.getCount() - 1; + } + + void dump(uint32_t v) + { + m_writer->emit(UInt(v)); + } + void dump(int32_t v) + { + m_writer->emit(v); + } + void dump(FloatingPointLiteralValue v) + { + m_writer->emit(v); + } + + void dump(IntegerLiteralValue v) + { + m_writer->emit(v); + } + + + void dump(const SemanticVersion& version) + { + ScopeWrite(this).getBuf() << UInt(version.m_major) << "." << UInt(version.m_minor) << "." << UInt(version.m_patch); + } + void dump(const NameLoc& nameLoc) + { + m_writer->emit("NameLoc{"); + if (nameLoc.name) + { + dump(nameLoc.name->text.getUnownedSlice()); + } + else + { + dumpPtr(nullptr); + } + m_writer->emit(", "); + dump(nameLoc.loc); + m_writer->emit(" }"); + } + void dump(BaseType baseType) + { + m_writer->emit(BaseTypeInfo::asText(baseType)); + } + void dump(Stage stage) + { + m_writer->emit(getStageName(stage)); + } + void dump(ImageFormat imageFormat) + { + m_writer->emit(getGLSLNameForImageFormat(imageFormat)); + } + + void dump(const String& string) + { + dump(string.getUnownedSlice()); + } + + void dump(const DiagnosticInfo* info) + { + ScopeWrite(this).getBuf() << "DiagnosticInfo {" << info->id << "}"; + } + void dump(const Layout* layout) + { + ScopeWrite(this).getBuf() << "Layout@" << size_t(layout); + } + + void dump(const Modifiers& modifiers) + { + auto& nonConstModifiers = const_cast<Modifiers&>(modifiers); + + m_writer->emit(" { \n"); + m_writer->indent(); + + for (const auto& mod : nonConstModifiers) + { + dump(mod); + m_writer->emit("\n"); + } + + m_writer->dedent(); + m_writer->emit("}"); + } + + template <typename T> + void dump(const SyntaxClass<T>& cls) + { + m_writer->emit(cls.classInfo->m_name); + } + + template <typename KEY, typename VALUE> + void dump(const Dictionary<KEY, VALUE>& dict) + { + m_writer->emit(" { \n"); + m_writer->indent(); + + for (auto iter : dict) + { + const auto& key = iter.Key; + const auto& value = iter.Value; + + dump(key); + m_writer->emit(" : "); + dump(value); + + m_writer->emit("\n"); + } + + m_writer->dedent(); + m_writer->emit("}"); + } + + void dump(const DeclCheckStateExt& extState) + { + auto state = extState.getState(); + + ScopeWrite(this).getBuf() << "DeclCheckStateExt{" << extState.isBeingChecked() << ", " << Index(state) << "}"; + } + + void dump(TextureFlavor texFlavor) + { + m_buf.Clear(); + m_buf << "TextureFlavor{" << Index(texFlavor.flavor) << "}"; + m_writer->emit(m_buf); + } + + void dump(SamplerStateFlavor flavor) + { + switch (flavor) + { + case SamplerStateFlavor::SamplerState: m_writer->emit("sampler"); break; + case SamplerStateFlavor::SamplerComparisonState: m_writer->emit("samplerComparison"); break; + default: m_writer->emit("unknown"); break; + } + } + + void dump(const QualType& qualType) + { + if (qualType.IsLeftValue) + { + m_writer->emit("left "); + } + else + { + m_writer->emit("right "); + } + dump(qualType.type); + } + + void dumpPtr(const void* ptr) + { + if (ptr) + { + ScopeWrite(this).getBuf() << "Unknown@" << size_t(ptr); + } + else + { + m_writer->emit("null"); + } + } + + void dump(SyntaxParseCallback callback) { dumpPtr((const void*)callback); } + + template <typename T, int SIZE> + void dump(const T (&in)[SIZE]) + { + m_writer->emit(" { \n"); + m_writer->indent(); + + for (Index i = 0; i < Index(SIZE); ++i) + { + dump(in[i]); + if (i < Index(SIZE) - 1) + { + m_writer->emit(", "); + } + m_writer->emit("\n"); + } + + m_writer->dedent(); + m_writer->emit("}"); + } + + //void dump(const void* ptr) { dumpPtr(ptr); } + + void dump(const LookupResult& result) + { + auto& nonConstResult = const_cast<LookupResult&>(result); + + m_writer->emit(" { \n"); + m_writer->indent(); + + for (auto item : nonConstResult) + { + // TODO(JS): + m_writer->emit("...\n"); + } + + m_writer->dedent(); + m_writer->emit("}"); + } + void dump(const GlobalGenericParamSubstitution::ConstraintArg& arg) + { + m_writer->emit(" { \n"); + m_writer->indent(); + + dump(arg.decl); + m_writer->emit(",\n"); + dump(arg.val); + m_writer->emit("\n"); + + m_writer->dedent(); + m_writer->emit("}"); + } + void dump(const TypeExp& exp) + { + m_writer->emit(" { \n"); + m_writer->indent(); + + dump(exp.exp); + m_writer->emit(",\n"); + dump(exp.type); + m_writer->emit("\n"); + + m_writer->dedent(); + m_writer->emit("}"); + } + void dump(const ExpandedSpecializationArg& arg) + { + dump(arg.witness); + } + + void dump(const TransparentMemberInfo& memInfo) + { + dump(memInfo.decl); + } + + void dumpRemaining() + { + // Have to keep checking count, as dumping objects can add objects + for (Index i = 0; i < m_objects.getCount(); ++i) + { + ObjectInfo& info = m_objects[i]; + if (!info.m_isDumped) + { + dumpObjectFull(*info.m_typeInfo, info.m_object, i); + } + } + } + + template <typename T> + void dumpField(const char* name, const T& value) + { + m_writer->emit(name); + m_writer->emit(" : "); + dump(value); + m_writer->emit("\n"); + } + + void dumpObjectFull(NodeBase* node); + void dumpObjectFull(Substitutions* subs); + + Context(SourceWriter* writer, ASTDumpUtil::Style dumpStyle): + m_writer(writer), + m_scopeWriteCount(0), + m_dumpStyle(dumpStyle) + { + } + + ASTDumpUtil::Style m_dumpStyle; + + Index m_scopeWriteCount; + + // Using the SourceWriter, for automatic indentation. + SourceWriter* m_writer; + + Dictionary<RefObject*, Index> m_objectMap; ///< Object index + List<ObjectInfo> m_objects; + + StringBuilder m_buf; +}; + +} // anonymous + +// Lets generate functions one for each that attempts to write out *it's* fields. +// We can write out the Super types fields by looking that up + +struct ASTDumpAccess +{ +#define SLANG_AST_DUMP_FIELD(FIELD_NAME, TYPE, param) context.dumpField(#FIELD_NAME, node->FIELD_NAME); + +#define SLANG_AST_DUMP_FIELDS_IMPL(NAME, SUPER, ORIGIN, LAST, MARKER, TYPE, param) \ +static void dumpFields_##NAME(NAME* node, Context& context) \ +{ \ + SLANG_UNUSED(node); \ + SLANG_UNUSED(context); \ + SLANG_FIELDS_ASTNode_##NAME(SLANG_AST_DUMP_FIELD, _) \ +} + +SLANG_ALL_ASTNode_Substitutions(SLANG_AST_DUMP_FIELDS_IMPL, _) +SLANG_ALL_ASTNode_NodeBase(SLANG_AST_DUMP_FIELDS_IMPL, _) + +}; + +#define SLANG_AST_GET_DUMP_FUNC(NAME, SUPER, ORIGIN, LAST, MARKER, TYPE, param) m_funcs[Index(ASTNodeType::NAME)] = (DumpFieldsFunc)&ASTDumpAccess::dumpFields_##NAME; + +typedef void (*DumpFieldsFunc)(RefObject* obj, Context& context); + +struct DumpFieldFuncs +{ + DumpFieldFuncs() + { + memset(m_funcs, 0, sizeof(m_funcs)); + SLANG_ALL_ASTNode_Substitutions(SLANG_AST_GET_DUMP_FUNC, _) + SLANG_ALL_ASTNode_NodeBase(SLANG_AST_GET_DUMP_FUNC, _) + } + + DumpFieldsFunc m_funcs[Index(ASTNodeType::CountOf)]; +}; + +static const DumpFieldFuncs s_funcs; + +void Context::dumpObjectReference(const ReflectClassInfo& type, RefObject* obj, Index objIndex) +{ + SLANG_UNUSED(obj); + ScopeWrite(this).getBuf() << type.m_name << "@" << objIndex; +} + +void Context::dumpObjectFull(const ReflectClassInfo& type, RefObject* obj, Index objIndex) +{ + ObjectInfo& info = m_objects[objIndex]; + SLANG_ASSERT(info.m_isDumped == false); + info.m_isDumped = true; + + // We need to dump the fields. + + ScopeWrite(this).getBuf() << type.m_name << "(" << objIndex << ") {\n"; + m_writer->indent(); + + List<const ReflectClassInfo*> allTypes; + { + const ReflectClassInfo* curType = &type; + do + { + allTypes.add(curType); + curType = curType->m_superClass; + } while (curType); + } + + // Okay we go backwards so we output in the 'normal' order + for (Index i = allTypes.getCount() - 1; i >= 0; --i) + { + const ReflectClassInfo* curType = allTypes[i]; + DumpFieldsFunc func = s_funcs.m_funcs[Index(curType->m_classId)]; + if (func) + { + func(obj, *this); + } + } + + m_writer->dedent(); + m_writer->emit("}\n"); +} + +void Context::dumpObject(const ReflectClassInfo& typeInfo, RefObject* obj) +{ + Index index = getObjectIndex(typeInfo, obj); + + ObjectInfo& info = m_objects[index]; + if (info.m_isDumped || m_dumpStyle == ASTDumpUtil::Style::Flat) + { + dumpObjectReference(typeInfo, obj, index); + } + else + { + dumpObjectFull(typeInfo, obj, index); + } +} + +void Context::dumpObjectFull(NodeBase* node) +{ + if (!node) + { + dumpPtr(nullptr); + } + else + { + const ReflectClassInfo& typeInfo = node->getClassInfo(); + Index index = getObjectIndex(typeInfo, node); + dumpObjectFull(typeInfo, node, index); + } +} + +void Context::dumpObjectFull(Substitutions* subs) +{ + if (!subs) + { + dumpPtr(nullptr); + } + else + { + const ReflectClassInfo& typeInfo = subs->getClassInfo(); + Index index = getObjectIndex(typeInfo, subs); + dumpObjectFull(typeInfo, subs, index); + } +} + +/* static */void ASTDumpUtil::dump(NodeBase* node, Style style, SourceWriter* writer) +{ + Context context(writer, style); + context.dumpObjectFull(node); + context.dumpRemaining(); +} + +/* static */void ASTDumpUtil::dump(Substitutions* subs, Style style, SourceWriter* writer) +{ + Context context(writer, style); + context.dumpObjectFull(subs); + context.dumpRemaining(); + +} + +} // namespace Slang diff --git a/source/slang/slang-ast-dump.h b/source/slang/slang-ast-dump.h new file mode 100644 index 000000000..71df8aafc --- /dev/null +++ b/source/slang/slang-ast-dump.h @@ -0,0 +1,28 @@ +// slang-ast-dump.h +#ifndef SLANG_AST_DUMP_H +#define SLANG_AST_DUMP_H + +#include "slang-syntax.h" + +#include "slang-emit-source-writer.h" + +namespace Slang +{ + +struct ASTDumpAccess; + +struct ASTDumpUtil +{ + enum class Style + { + Hierachical, + Flat, + }; + + static void dump(NodeBase* node, Style style, SourceWriter* writer); + static void dump(Substitutions* subs, Style style, SourceWriter* writer); +}; + +} // namespace Slang + +#endif diff --git a/source/slang/slang-ast-reflect.h b/source/slang/slang-ast-reflect.h index 9c93d0621..0c57dc757 100644 --- a/source/slang/slang-ast-reflect.h +++ b/source/slang/slang-ast-reflect.h @@ -53,5 +53,7 @@ // Does nothing - just a mark to the C++ extractor #define SLANG_REFLECT_BASE_CLASS(NAME) +#define SLANG_REFLECTED +#define SLANG_UNREFLECTED #endif // SLANG_AST_REFLECT_H diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index fb13ab146..852898de6 100644 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -1449,6 +1449,8 @@ namespace Slang bool shouldDumpIR = false; bool shouldValidateIR = false; + bool shouldDumpAST = false; + protected: CompileRequestBase( Linkage* linkage, diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index c642c196a..8100fab4f 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -465,6 +465,10 @@ struct OptionsParser requestImpl->getFrontEndReq()->shouldDumpIR = true; requestImpl->getBackEndReq()->shouldDumpIR = true; } + else if (argStr == "-dump-ast") + { + requestImpl->getFrontEndReq()->shouldDumpAST = true; + } else if (argStr == "-dump-repro") { SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, requestImpl->dumpRepro)); diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 34f1402b5..82bc5e478 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -14,6 +14,8 @@ #include "slang-reflection.h" #include "slang-type-layout.h" +#include "slang-ast-dump.h" + #include "slang-state-serialize.h" #include "slang-file-system.h" @@ -996,6 +998,25 @@ void FrontEndCompileRequest::parseTranslationUnit( tokens, getSink(), languageScope); + + // Let's try dumping + + if (shouldDumpAST) + { + StringBuilder buf; + SourceWriter writer(linkage->getSourceManager(), LineDirectiveMode::None); + + ASTDumpUtil::dump(translationUnit->getModuleDecl(), ASTDumpUtil::Style::Flat, &writer); + + const String& path = sourceFile->getPathInfo().foundPath; + if (path.getLength()) + { + String fileName = Path::getFileNameWithoutExt(path); + fileName.append(".slang-ast"); + + File::writeAllText(fileName, writer.getContent()); + } + } } } diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index cf5a81499..1b391cac0 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -192,6 +192,7 @@ <ClInclude Include="slang-ast-all.h" /> <ClInclude Include="slang-ast-base.h" /> <ClInclude Include="slang-ast-decl.h" /> + <ClInclude Include="slang-ast-dump.h" /> <ClInclude Include="slang-ast-expr.h" /> <ClInclude Include="slang-ast-generated-macro.h" /> <ClInclude Include="slang-ast-generated.h" /> @@ -271,6 +272,7 @@ <ClInclude Include="slang-visitor.h" /> </ItemGroup> <ItemGroup> + <ClCompile Include="slang-ast-dump.cpp" /> <ClCompile Include="slang-ast-reflect.cpp" /> <ClCompile Include="slang-check-conformance.cpp" /> <ClCompile Include="slang-check-constraint.cpp" /> @@ -382,10 +384,10 @@ <Natvis Include="slang.natvis" /> <CustomBuild Include="slang-ast-reflect.h"> <FileType>Document</FileType> - <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"../../bin/windows-x86/debug/slang-cpp-extractor" -d %(RootDir)%(Directory)/ 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-ast- -o slang-ast-generated</Command> - <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"../../bin/windows-x64/debug/slang-cpp-extractor" -d %(RootDir)%(Directory)/ 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-ast- -o slang-ast-generated</Command> - <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"../../bin/windows-x86/release/slang-cpp-extractor" -d %(RootDir)%(Directory)/ 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-ast- -o slang-ast-generated</Command> - <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"../../bin/windows-x64/release/slang-cpp-extractor" -d %(RootDir)%(Directory)/ 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-ast- -o slang-ast-generated</Command> + <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"../../bin/windows-x86/debug/slang-cpp-extractor" -d %(RootDir)%(Directory)/ 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-ast- -o slang-ast-generated -output-fields</Command> + <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"../../bin/windows-x64/debug/slang-cpp-extractor" -d %(RootDir)%(Directory)/ 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-ast- -o slang-ast-generated -output-fields</Command> + <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"../../bin/windows-x86/release/slang-cpp-extractor" -d %(RootDir)%(Directory)/ 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-ast- -o slang-ast-generated -output-fields</Command> + <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"../../bin/windows-x64/release/slang-cpp-extractor" -d %(RootDir)%(Directory)/ 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-ast- -o slang-ast-generated -output-fields</Command> <Outputs>%(RootDir)%(Directory)/slang-ast-generated.h;%(RootDir)%(Directory)/slang-ast-generated-macro.h</Outputs> <Message>slang-cpp-extractor AST %(Identity)</Message> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../bin/windows-x86/debug/slang-cpp-extractor.exe;%(RootDir)%(Directory)/slang-ast-base.h;%(RootDir)%(Directory)/slang-ast-decl.h;%(RootDir)%(Directory)/slang-ast-expr.h;%(RootDir)%(Directory)/slang-ast-modifier.h;%(RootDir)%(Directory)/slang-ast-stmt.h;%(RootDir)%(Directory)/slang-ast-type.h;%(RootDir)%(Directory)/slang-ast-val.h</AdditionalInputs> diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index 4060a6019..c2c9268dc 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -27,6 +27,9 @@ <ClInclude Include="slang-ast-decl.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="slang-ast-dump.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="slang-ast-expr.h"> <Filter>Header Files</Filter> </ClInclude> @@ -260,6 +263,9 @@ </ClInclude> </ItemGroup> <ItemGroup> + <ClCompile Include="slang-ast-dump.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="slang-ast-reflect.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp b/tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp index 5118a3a9b..792cf3458 100644 --- a/tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp +++ b/tools/slang-cpp-extractor/slang-cpp-extractor-main.cpp @@ -42,6 +42,9 @@ enum class IdentifierStyle Namespace, ///< namespace Access, ///< public, protected, private + Reflected, + Unreflected, + CountOf, }; @@ -53,6 +56,7 @@ struct IdentifierFlag StartScope = 0x1, ///< namespace, struct or class ClassLike = 0x2, ///< Struct or class Keyword = 0x4, + Reflection = 0x8, }; }; @@ -67,7 +71,10 @@ static const IdentifierFlags kIdentifierFlags[Index(IdentifierStyle::CountOf)] = IdentifierFlag::Keyword | IdentifierFlag::StartScope | IdentifierFlag::ClassLike, /// Class IdentifierFlag::Keyword | IdentifierFlag::StartScope | IdentifierFlag::ClassLike, /// Struct IdentifierFlag::Keyword | IdentifierFlag::StartScope, /// Namespace - IdentifierFlag::Keyword, + IdentifierFlag::Keyword, /// Access + IdentifierFlag::Reflection, /// Reflected + IdentifierFlag::Reflection, /// Unreflected + }; SLANG_FORCE_INLINE IdentifierFlags getFlags(IdentifierStyle style) @@ -118,6 +125,12 @@ public: set(UnownedStringSlice(names[i]), style); } } + void reset() + { + m_styles.clear(); + m_pool.clear(); + } + IdentifierLookup(): m_pool(StringSlicePool::Style::Empty) { @@ -128,6 +141,12 @@ protected: StringSlicePool m_pool; }; +enum class ReflectionType +{ + NotReflected, + Reflected, +}; + class SourceOrigin; class Node : public RefObject @@ -144,8 +163,11 @@ public: struct Field { + bool isReflected() const { return reflectionType == ReflectionType::Reflected; } + UnownedStringSlice type; Token name; + ReflectionType reflectionType; }; enum class BaseType @@ -193,6 +215,12 @@ public: /// Find the last (reflected) derived type Node* findLastDerived(); + /// True if reflected + bool isReflected() const { return m_reflectionType == ReflectionType::Reflected; } + + /// Gets the reflection for any contained types + ReflectionType getContainedReflectionType() const { return m_reflectionType == ReflectionType::NotReflected ? ReflectionType::NotReflected : m_reflectionOverride; } + /// True if has a derived type that is reflected bool hasReflectedDerivedType() const; /// Stores in out any reflected derived types @@ -205,7 +233,8 @@ public: Node(Type type): m_type(type), m_parentScope(nullptr), - m_isReflected(false), + m_reflectionType(ReflectionType::NotReflected), + m_reflectionOverride(ReflectionType::Reflected), m_superNode(nullptr), m_baseType(BaseType::None), m_origin(nullptr) @@ -235,7 +264,11 @@ public: SourceOrigin* m_origin; /// Classes can be traversed, but not reflected. To be reflected they have to contain the marker - bool m_isReflected; + ReflectionType m_reflectionType; + + /// For child types, fields, how reflection is handled. If this type is not reflected + ReflectionType m_reflectionOverride; + /// The base type of this BaseType m_baseType; @@ -393,11 +426,9 @@ void Node::addChild(Node* child) child->m_parentScope = this; m_children.add(child); - UnownedStringSlice content = child->m_name.getContent(); - - if (content.getLength()) + if (child->m_name.hasContent()) { - m_childMap.Add(content, child); + m_childMap.Add(child->m_name.getContent(), child); } } @@ -443,7 +474,7 @@ static void _indent(Index indentCount, StringBuilder& out) void Node::dumpDerived(int indentCount, StringBuilder& out) { - if (isClassLike() && m_isReflected && m_name.hasContent()) + if (isClassLike() && isReflected() && m_name.hasContent()) { _indent(indentCount, out); out << m_name.getContent() << "\n"; @@ -484,12 +515,12 @@ void Node::dump(int indentCount, StringBuilder& out) out << typeName << " "; - if (!m_isReflected) + if (!isReflected()) { out << " ("; } out << m_name.getContent(); - if (!m_isReflected) + if (!isReflected()) { out << ") "; } @@ -511,8 +542,11 @@ void Node::dump(int indentCount, StringBuilder& out) for (const Field& field : m_fields) { - _indent(indentCount + 1, out); - out << field.type << " " << field.name.getContent() << "\n"; + if (field.isReflected()) + { + _indent(indentCount + 1, out); + out << field.type << " " << field.name.getContent() << "\n"; + } } _indent(indentCount, out); @@ -559,7 +593,7 @@ Index Node::calcDerivedDepth() const Node* Node::findLastDerived() { - if (!m_isReflected) + if (!isReflected()) { return nullptr; } @@ -594,7 +628,7 @@ bool Node::hasReflectedDerivedType() const { for (Node* type : m_derivedTypes) { - if (type->m_isReflected) + if (type->isReflected()) { return true; } @@ -607,7 +641,7 @@ void Node::getReflectedDerivedTypes(List<Node*>& out) const out.clear(); for (Node* type : m_derivedTypes) { - if (type->m_isReflected) + if (type->isReflected()) { out.add(type); } @@ -621,7 +655,7 @@ void Node::getReflectedDerivedTypes(List<Node*>& out) const for (Index j = 0; j < count; ) { Node* node = ioNodes[j]; - if (!node->isClassLike() || !node->m_isReflected) + if (!node->isClassLike() || !node->isReflected()) { ioNodes.removeAt(j); count--; @@ -651,6 +685,8 @@ 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_outputFields = false; ///< When dumping macros also dump field definitions + List<String> m_inputPaths; ///< The input paths to the files to be processed String m_outputPath; ///< The ouput path. Note that the extractor can generate multiple output files, and this will actually be the 'stem' of several files @@ -766,7 +802,12 @@ SlangResult OptionsParser::parse(int argc, const char*const* argv, DiagnosticSin else if (arg == "-defs") { outOptions.m_defs = true; - continue; + continue; + } + else if (arg == "-output-fields") + { + outOptions.m_outputFields = true; + break; } else if (arg == "-strip-prefix") { @@ -809,6 +850,7 @@ CPPExtractor::CPPExtractor(StringSlicePool* typePool, NamePool* namePool, Diagno m_identifierLookup(identifierLookup) { m_rootNode = new Node(Node::Type::Namespace); + m_rootNode->m_reflectionType = ReflectionType::Reflected; } bool CPPExtractor::_isMarker(const UnownedStringSlice& name) @@ -1038,6 +1080,9 @@ SlangResult CPPExtractor::_maybeParseNode(Node::Type type) RefPtr<Node> node(new Node(type)); node->m_name = name; + // Defaults to not reflected + SLANG_ASSERT(!node->isReflected()); + if (advanceIfToken(TokenType::Colon)) { // Could have public @@ -1068,8 +1113,6 @@ SlangResult CPPExtractor::_maybeParseNode(Node::Type type) m_reader.advanceToken(); } - // Node does define a class, but it's not reflected - node->m_isReflected = false; return pushNode(node); } @@ -1094,7 +1137,6 @@ SlangResult CPPExtractor::_maybeParseNode(Node::Type type) 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(); @@ -1102,7 +1144,6 @@ SlangResult CPPExtractor::_maybeParseNode(Node::Type type) } default: { - node->m_isReflected = false; SLANG_RETURN_ON_FAIL(pushNode(node)); return SLANG_OK; } @@ -1114,8 +1155,6 @@ SlangResult CPPExtractor::_maybeParseNode(Node::Type type) 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; @@ -1134,7 +1173,7 @@ SlangResult CPPExtractor::_maybeParseNode(Node::Type type) return SLANG_FAIL; } - node->m_isReflected = true; + node->m_reflectionType = ReflectionType::Reflected; return pushNode(node); } @@ -1478,6 +1517,7 @@ SlangResult CPPExtractor::_maybeParseField() Node::Field field; field.type = typeName; field.name = fieldName; + field.reflectionType = m_currentNode->getContainedReflectionType(); m_currentNode->m_fields.add(field); break; @@ -1538,8 +1578,8 @@ SlangResult CPPExtractor::parse(SourceFile* sourceFile, const Options* options) { case TokenType::Identifier: { - IdentifierStyle style = m_identifierLookup->get(m_reader.peekToken().getContent()); - + const IdentifierStyle style = m_identifierLookup->get(m_reader.peekToken().getContent()); + switch (style) { case IdentifierStyle::BaseClass: @@ -1555,10 +1595,31 @@ SlangResult CPPExtractor::parse(SourceFile* sourceFile, const Options* options) node->m_name = nameToken; node->m_baseType = Node::BaseType::Marked; + // Classes defined this way are not reflected, as the mark means the type exists, but isn't visible + node->m_reflectionType = ReflectionType::NotReflected; + SLANG_RETURN_ON_FAIL(pushNode(node)); popBrace(); break; } + case IdentifierStyle::Reflected: + { + m_reader.advanceToken(); + if (m_currentNode) + { + m_currentNode->m_reflectionOverride = ReflectionType::Reflected; + } + break; + } + case IdentifierStyle::Unreflected: + { + m_reader.advanceToken(); + if (m_currentNode) + { + m_currentNode->m_reflectionOverride = ReflectionType::NotReflected; + } + break; + } case IdentifierStyle::Root: { if (m_currentNode && m_currentNode->isClassLike()) @@ -1660,7 +1721,7 @@ SlangResult CPPExtractor::_calcDerivedTypesRec(Node* node) Node* superType = parentScope->findChild(node->m_super.getContent()); if (!superType) { - if (node->m_isReflected) + if (node->isReflected()) { m_sink->diagnose(node->m_name, CPPDiagnostics::superTypeNotFound, node->m_name.getContent()); return SLANG_FAIL; @@ -1775,48 +1836,18 @@ public: 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: - + + /// 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; - IdentifierLookup m_identifierLookup; - StringSlicePool m_slicePool; }; @@ -1864,7 +1895,7 @@ SlangResult CPPExtractorApp::calcDef(CPPExtractor& extractor, SourceOrigin* orig for (Node* node : origin->m_nodes) { - if (node->isClassLike() && node->m_isReflected) + if (node->isClassLike() && node->isReflected()) { if (node->m_marker.getContent().indexOf(UnownedStringSlice::fromLiteral("ABSTRACT")) >= 0) { @@ -1878,8 +1909,6 @@ SlangResult CPPExtractorApp::calcDef(CPPExtractor& extractor, SourceOrigin* orig return SLANG_OK; } - - SlangResult CPPExtractorApp::calcChildrenHeader(CPPExtractor& extractor, StringBuilder& out) { const List<Node*>& baseTypes = extractor.getBaseTypes(); @@ -1948,6 +1977,46 @@ SlangResult CPPExtractorApp::calcChildrenHeader(CPPExtractor& extractor, StringB } out << "\n\n"; } + + if (m_options.m_outputFields) + { + out << "\n\n /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! FIELDS !!!!!!!!!!!!!!!!!!!!!!!!!!!! */\n\n"; + + for (Node* node : nodes) + { + // Define the derived types + out << "#define " << m_options.m_prefixMark << "FIELDS_" << reflectTypeName << "_" << node->m_name.getContent() << "(_x_, _param_)"; + + if (node->m_fields.getCount() > 0) + { + out << "\\\n"; + + const Index fieldsCount = node->m_fields.getCount(); + bool previousField = false; + for (Index j = 0; j < fieldsCount; ++j) + { + const auto& field = node->m_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.name.getContent() << ", (" << field.type << "), _param_)"; + previousField = true; + } + } + } + + out << "\n\n"; + } + } } return SLANG_OK; @@ -2010,33 +2079,6 @@ SlangResult CPPExtractorApp::calcHeader(CPPExtractor& extractor, StringBuilder& out << "};\n\n"; } -#if 0 - 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"; -#endif - // Predeclare the classes { out << "// Predeclare\n\n"; @@ -2044,7 +2086,7 @@ SlangResult CPPExtractorApp::calcHeader(CPPExtractor& extractor, StringBuilder& { SLANG_ASSERT(node->isClassLike()); // If it's not reflected we don't output, in the enum list - if (node->m_isReflected) + if (node->isReflected()) { const char* type = (node->m_type == Node::Type::ClassType) ? "class" : "struct"; out << type << " " << node->m_name.getContent() << ";\n"; @@ -2052,24 +2094,6 @@ SlangResult CPPExtractorApp::calcHeader(CPPExtractor& extractor, StringBuilder& } } -#if 0 - 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"; -#endif - // Do the macros for each of the types { @@ -2129,7 +2153,7 @@ SlangResult CPPExtractorApp::calcHeader(CPPExtractor& extractor, StringBuilder& } out << marker << ", "; - if (node->m_baseType != Node::BaseType::None || node->m_superNode && node->m_superNode->m_isReflected == false) + if (node->m_baseType != Node::BaseType::None || node->m_superNode && node->m_superNode->isReflected() == false) { out << "BASE, "; } @@ -2164,7 +2188,7 @@ SlangResult CPPExtractorApp::calcHeader(CPPExtractor& extractor, StringBuilder& for (Node* node : origin->m_nodes) { - if (!(node->m_isReflected && node->isClassLike())) + if (!(node->isReflected() && node->isClassLike())) { continue; } @@ -2208,7 +2232,6 @@ SlangResult CPPExtractorApp::writeDefs(CPPExtractor& extractor) SlangResult CPPExtractorApp::writeOutput(CPPExtractor& extractor) { - String path; if (m_options.m_inputDirectory.getLength()) { @@ -2257,13 +2280,77 @@ SlangResult CPPExtractorApp::writeOutput(CPPExtractor& extractor) return SLANG_OK; } +/* static */void CPPExtractorApp::_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" }; + 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 + { + { + StringBuilder buf; + buf << options.m_prefixMark; + buf << "CLASS_ROOT"; + + outLookup.set(buf.getUnownedSlice(), IdentifierStyle::Root); + } + { + StringBuilder buf; + buf << options.m_prefixMark; + buf << "REFLECT_BASE_CLASS"; + + outLookup.set(buf.getUnownedSlice(), IdentifierStyle::BaseClass); + } + { + StringBuilder buf; + buf << options.m_prefixMark; + buf << "REFLECTED"; + + outLookup.set(buf.getUnownedSlice(), IdentifierStyle::Reflected); + } + { + StringBuilder buf; + buf << options.m_prefixMark; + buf << "UNREFLECTED"; + + outLookup.set(buf.getUnownedSlice(), IdentifierStyle::Unreflected); + } + } + + + // 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 CPPExtractorApp::execute(const Options& options) { m_options = options; - - CPPExtractor extractor(&m_slicePool, &m_namePool, m_sink, &m_identifierLookup); + IdentifierLookup identifierLookup; + _initIdentifierLookup(options, identifierLookup); + + CPPExtractor extractor(&m_slicePool, &m_namePool, m_sink, &identifierLookup); // Read in each of the input files for (Index i = 0; i < m_options.m_inputPaths.getCount(); ++i) |
