From c5c1a25ab6d0e509e893d737a679ac47949df2f6 Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 18 Jan 2024 16:46:00 -0800 Subject: Capability def parsing & codegen + disjoint sets (#3451) * Capability def parsing & codegen + disjoint sets This change adds a capability definition file, and a code generator to produce C++ code that defines the capability enums and necessary data structures around the capabilities. Extends the existing CapabilitySet class to support expressing disjoint sets of capabilities. This sets up for the next change that will enhance our type checking with reasoning of capability requirements. * Fix cmake. * Fix warning. * Fix. * Fix isBetterForTarget to prefer less specialized option. * Fix. * Fix premake. * Fix intrinsic. * Fix vs sln file. --------- Co-authored-by: Yong He --- .../capability-generator-main.cpp | 674 +++++++++++++++++++++ .../slang-capability-diagnostic-defs.h | 57 ++ .../lookup-generator-main.cpp | 76 +-- 3 files changed, 734 insertions(+), 73 deletions(-) create mode 100644 tools/slang-capability-generator/capability-generator-main.cpp create mode 100644 tools/slang-capability-generator/slang-capability-diagnostic-defs.h (limited to 'tools') diff --git a/tools/slang-capability-generator/capability-generator-main.cpp b/tools/slang-capability-generator/capability-generator-main.cpp new file mode 100644 index 000000000..79582a8c5 --- /dev/null +++ b/tools/slang-capability-generator/capability-generator-main.cpp @@ -0,0 +1,674 @@ +// capabilities-generator-main.cpp + +#include +#include "../../source/compiler-core/slang-lexer.h" +#include "../../source/compiler-core/slang-perfect-hash-codegen.h" +#include "../../source/core/slang-io.h" +#include "../../source/core/slang-secure-crt.h" +#include "../../source/core/slang-string-util.h" +#include "../../source/core/slang-file-system.h" + +using namespace Slang; + +namespace Diagnostics +{ +#define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, #name, messageFormat }; +#include "slang-capability-diagnostic-defs.h" +#undef DIAGNOSTIC +} + +enum class CapabilityFlavor +{ + Normal, + Abstract, + Alias +}; + +struct CapabilityDef; + +struct CapabilityConjunctionExpr +{ + List atoms; +}; + +struct CapabilityDisjunctionExpr +{ + List conjunctions; +}; + +struct SerializedArrayView +{ + Index first; + Index count; +}; + +struct CapabilityDef : public RefObject +{ + Index enumValue; + String name; + CapabilityDisjunctionExpr expr; + CapabilityFlavor flavor; + int rank; + List> canonicalRepresentation; + SerializedArrayView serializedCanonicalRepresentation; + + CapabilityDef* getAbstractBase() + { + if (flavor != CapabilityFlavor::Normal) + return nullptr; + if (expr.conjunctions.getCount() != 1) + return nullptr; + if (expr.conjunctions[0].atoms.getCount() == 0) + return nullptr; + if (expr.conjunctions[0].atoms[0]->flavor != CapabilityFlavor::Abstract) + return nullptr; + return expr.conjunctions[0].atoms[0]; + } +}; + +struct CapabilityDefParser +{ + CapabilityDefParser(Lexer* lexer, DiagnosticSink* sink) + : m_lexer(lexer) + , m_sink(sink) + { + } + + Lexer* m_lexer; + DiagnosticSink* m_sink; + + Dictionary m_mapNameToCapability; + List> m_defs; + + TokenReader m_tokenReader; + + bool advanceIf(TokenType type) + { + if (m_tokenReader.peekTokenType() == type) + { + m_tokenReader.advanceToken(); + return true; + } + return false; + } + + SlangResult readToken(TokenType type, Token& nextToken) + { + nextToken = m_tokenReader.advanceToken(); + if (nextToken.type != type) + { + m_sink->diagnose(nextToken.loc, Diagnostics::unexpectedTokenExpectedTokenType, nextToken, type); + return SLANG_FAIL; + } + return SLANG_OK; + } + + SlangResult readToken(TokenType type) + { + Token nextToken; + return readToken(type, nextToken); + } + + SlangResult parseConjunction(CapabilityConjunctionExpr& expr) + { + for (;;) + { + Token nameToken; + SLANG_RETURN_ON_FAIL(readToken(TokenType::Identifier, nameToken)); + CapabilityDef* def = nullptr; + if (m_mapNameToCapability.tryGetValue(nameToken.getContent(), def)) + { + expr.atoms.add(def); + } + else + { + m_sink->diagnose(nameToken.loc, Diagnostics::undefinedIdentifier, nameToken); + return SLANG_FAIL; + } + if (!(advanceIf(TokenType::OpAnd) || advanceIf(TokenType::OpAdd))) + break; + } + return SLANG_OK; + } + + SlangResult parseExpr(CapabilityDisjunctionExpr& expr) + { + for (;;) + { + CapabilityConjunctionExpr conjunction; + SLANG_RETURN_ON_FAIL(parseConjunction(conjunction)); + expr.conjunctions.add(conjunction); + if (!advanceIf(TokenType::OpBitOr)) + break; + } + return SLANG_OK; + } + + SlangResult parseDefs() + { + auto tokens = m_lexer->lexAllSemanticTokens(); + m_tokenReader = TokenReader(tokens); + for (;;) + { + RefPtr def = new CapabilityDef(); + def->flavor = CapabilityFlavor::Normal; + auto nextToken = m_tokenReader.advanceToken(); + if (nextToken.getContent() == "alias") + { + def->flavor = CapabilityFlavor::Alias; + } + else if (nextToken.getContent() == "abstract") + { + def->flavor = CapabilityFlavor::Abstract; + } + else if (nextToken.getContent() == "def") + { + def->flavor = CapabilityFlavor::Normal; + } + else if (nextToken.type == TokenType::EndOfFile) + { + break; + } + else + { + m_sink->diagnose(nextToken.loc, Diagnostics::unexpectedToken, nextToken); + return SLANG_FAIL; + } + + Token nameToken; + SLANG_RETURN_ON_FAIL(readToken(TokenType::Identifier, nameToken)); + def->name = nameToken.getContent(); + + if (def->flavor == CapabilityFlavor::Normal) + { + if (advanceIf(TokenType::Colon)) + { + SLANG_RETURN_ON_FAIL(parseExpr(def->expr)); + } + if (advanceIf(TokenType::OpAssign)) + { + Token rankToken; + SLANG_RETURN_ON_FAIL(readToken(TokenType::IntegerLiteral, rankToken)); + def->rank = stringToInt(rankToken.getContent()); + } + } + else if (def->flavor == CapabilityFlavor::Alias) + { + SLANG_RETURN_ON_FAIL(readToken(TokenType::OpAssign)); + SLANG_RETURN_ON_FAIL(parseExpr(def->expr)); + } + else if (def->flavor == CapabilityFlavor::Abstract) + { + if (advanceIf(TokenType::Colon)) + { + SLANG_RETURN_ON_FAIL(parseExpr(def->expr)); + } + } + SLANG_RETURN_ON_FAIL(readToken(TokenType::Semicolon)); + m_defs.add(def); + if (!m_mapNameToCapability.addIfNotExists(def->name, def)) + { + m_sink->diagnose(nextToken.loc, Diagnostics::redefinition, def->name); + return SLANG_FAIL; + } + } + return SLANG_OK; + } +}; + +struct CapabilityConjunction +{ + HashSet atoms; + bool implies(const CapabilityConjunction& c) const + { + for (auto& atom : c.atoms) + { + if (!atoms.contains(atom)) + return false; + } + return true; + } + + bool isImpossible() const + { + // Keep a map from an abstract base to the concrete atom defined in this conjunction that implements the base. + Dictionary abstractKV; + + for (auto& atom : atoms) + { + auto abstractBase = atom->getAbstractBase(); + if (!abstractBase) + continue; + + // Have we already seen another concrete atom that implements the same abstract base of the current atom? + // If so, we have a conflict and the conjunction is impossible. + // + CapabilityDef* value = nullptr; + if (abstractKV.tryGetValue(abstractBase, value)) + { + if (value != atom) + return true; + } + else + { + abstractKV[abstractBase] = atom; + } + } + return false; + } +}; + +struct CapabilityDisjunction +{ + List conjunctions; + + void addConjunction(const CapabilityConjunction& c) + { + if (c.isImpossible()) + return; + for (auto& conjunction : conjunctions) + { + if (c.implies(conjunction)) + return; + } + for (Index i = 0; i < conjunctions.getCount();) + { + if (conjunctions[i].implies(c)) + { + conjunctions.fastRemoveAt(i); + } + else + { + i++; + } + } + conjunctions.add(_Move(c)); + } + + CapabilityDisjunction joinWith(const CapabilityDisjunction& other) + { + if (conjunctions.getCount() == 0) + { + return other; + } + if (other.conjunctions.getCount() == 0) + { + return *this; + } + + CapabilityDisjunction result; + + for (auto& thisC : conjunctions) + { + for (auto& thatC : other.conjunctions) + { + CapabilityConjunction newC; + for (auto atom : thisC.atoms) + newC.atoms.add(atom); + for (auto atom : thatC.atoms) + newC.atoms.add(atom); + result.addConjunction(_Move(newC)); + } + } + return result; + } + + List> canonicalize() + { + List> result; + for (auto& c : conjunctions) + { + List atoms; + for (auto& atom : c.atoms) + atoms.add(atom); + atoms.sort([](CapabilityDef* c1, CapabilityDef* c2) {return c1->enumValue < c2->enumValue; }); + result.add(_Move(atoms)); + } + result.sort([](const List& c1, const List& c2) + { + for (Index i = 0; i < Math::Min(c1.getCount(), c2.getCount()); i++) + { + if (c1[i]->enumValue < c2[i]->enumValue) + return true; + else if (c1[i]->enumValue > c2[i]->enumValue) + return false; + } + return c1.getCount() < c2.getCount(); + }); + return result; + } +}; + +CapabilityDisjunction getCanonicalRepresentation(CapabilityDef* def) +{ + CapabilityDisjunction result; + for (auto& c : def->canonicalRepresentation) + { + CapabilityConjunction conj; + for (auto& atom : c) + conj.atoms.add(atom); + result.conjunctions.add(conj); + } + return result; +} + +CapabilityDisjunction evaluateConjunction(const List& atoms) +{ + CapabilityDisjunction result; + for (auto& def : atoms) + { + CapabilityDisjunction defCanonical = getCanonicalRepresentation(def); + result = result.joinWith(defCanonical); + } + return result; +} + +void calcCanonicalRepresentation(CapabilityDef* def, const List& mapEnumValueToDef) +{ + CapabilityDisjunction disjunction; + if (def->flavor == CapabilityFlavor::Normal) + { + CapabilityConjunction c; + c.atoms.add(def); + disjunction.conjunctions.add(c); + } + CapabilityDisjunction exprVal; + for (auto& c : def->expr.conjunctions) + { + CapabilityDisjunction evalD = evaluateConjunction(c.atoms); + for (auto& cc : evalD.conjunctions) + exprVal.addConjunction(cc); + } + disjunction = disjunction.joinWith(exprVal); + def->canonicalRepresentation = disjunction.canonicalize(); +} + +void calcCanonicalRepresentations(const List>& defs, const List& mapEnumValueToDef) +{ + for (auto def : defs) + calcCanonicalRepresentation(def, mapEnumValueToDef); +} + +SlangResult generateDefinitions(const List>& defs, StringBuilder& sbHeader, StringBuilder& sbCpp) +{ + sbHeader << "enum class CapabilityAtom\n{\n"; + sbHeader << " Invalid,\n"; + for (auto def : defs) + { + if (def->flavor == CapabilityFlavor::Normal) + { + sbHeader << " " << def->name << ",\n"; + } + } + sbHeader << " Count\n"; + sbHeader << "};\n"; + CapabilityDef* firstAbstractDef = nullptr; + CapabilityDef* firstAliasDef = nullptr; + sbHeader << "enum class CapabilityName\n{\n"; + sbHeader << " Invalid,\n"; + Index enumValueCounter = 1; + List mapEnumValueToDef; + mapEnumValueToDef.add(nullptr); // For Invalid. + for (auto def : defs) + { + if (def->flavor == CapabilityFlavor::Normal) + { + def->enumValue = enumValueCounter; + ++enumValueCounter; + mapEnumValueToDef.add(def); + sbHeader << " " << def->name << " = (int)CapabilityAtom::" << def->name << ",\n"; + } + } + for (auto def : defs) + { + if (def->flavor == CapabilityFlavor::Abstract) + { + if (firstAbstractDef == nullptr) + firstAbstractDef = def; + def->enumValue = enumValueCounter; + ++enumValueCounter; + mapEnumValueToDef.add(def); + sbHeader << " " << def->name << ",\n"; + } + } + for (auto def : defs) + { + if (def->flavor == CapabilityFlavor::Alias) + { + if (firstAliasDef == nullptr) + firstAliasDef = def; + def->enumValue = enumValueCounter; + ++enumValueCounter; + mapEnumValueToDef.add(def); + sbHeader << " " << def->name << ",\n"; + } + } + sbHeader << " Count\n"; + sbHeader << "};\n"; + + calcCanonicalRepresentations(defs, mapEnumValueToDef); + + List capabiltiyNameArray; + List serializedCapabilityArrays; + + List serializedAtomDisjunctions; + auto serializeConjunction = [&](const List& capabilities) -> SerializedArrayView + { + // Do we already have a serialized capability array that is the same the one we are trying to serialize? + for (auto existingArray : serializedCapabilityArrays) + { + if (existingArray.count == capabilities.getCount()) + { + bool match = true; + for (Index i = 0; i < capabilities.getCount(); i++) + { + if (capabiltiyNameArray[existingArray.first+i] != capabilities[i]->name) + { + match = false; + break; + } + } + if (match) + return existingArray; + } + } + SerializedArrayView result; + result.first = capabiltiyNameArray.getCount(); + for (auto capability : capabilities) + { + capabiltiyNameArray.add(capability->name); + } + result.count = capabilities.getCount(); + serializedCapabilityArrays.add(result); + return result; + }; + auto serializeDisjunction = [&](const List& conjunctions) -> SerializedArrayView + { + SerializedArrayView result; + result.first = serializedAtomDisjunctions.getCount(); + for (auto c : conjunctions) + { + serializedAtomDisjunctions.add(c); + } + result.count = conjunctions.getCount(); + return result; + }; + for (auto& def : defs) + { + List conjunctions; + for (auto& c : def->canonicalRepresentation) + conjunctions.add(serializeConjunction(c)); + def->serializedCanonicalRepresentation = serializeDisjunction(conjunctions); + } + + sbCpp << "static CapabilityName kCapabilityArray[] = {\n"; + Index arrayIndex = 0; + sbCpp << " /* [0] @0: */ "; + for (Index i = 0; i < capabiltiyNameArray.getCount(); ++i) + { + sbCpp << " CapabilityName::" << capabiltiyNameArray[i] << ","; + if (i + 1 == serializedCapabilityArrays[arrayIndex].first + serializedCapabilityArrays[arrayIndex].count) + { + arrayIndex++; + if (arrayIndex == serializedCapabilityArrays.getCount()) + sbCpp << "\n"; + else + sbCpp << "\n /* [" << arrayIndex << "] @" << serializedCapabilityArrays[arrayIndex].first <<": */ "; + } + } + sbCpp << "};\n"; + sbCpp << "static ArrayView kCapabilityConjunctions[] = {\n"; + for (auto c : serializedAtomDisjunctions) + { + sbCpp << " { kCapabilityArray + " << c.first << ", " << c.count << " },\n"; + } + sbCpp << "};\n"; + + sbCpp << "static const CapabilityAtomInfo kCapabilityNameInfos[int(CapabilityName::Count)] = {\n"; + for (auto def : mapEnumValueToDef) + { + if (!def) + { + sbCpp << R"( { "Invalid", CapabilityNameFlavor::Concrete, CapabilityName::Invalid, 0, {nullptr, 0} },)" << "\n"; + continue; + } + + // name. + sbCpp << " { \"" << def->name << "\", "; + + // flavor. + switch (def->flavor) + { + case CapabilityFlavor::Normal: + sbCpp << "CapabilityNameFlavor::Concrete"; + break; + case CapabilityFlavor::Abstract: + sbCpp << "CapabilityNameFlavor::Abstract"; + break; + case CapabilityFlavor::Alias: + sbCpp << "CapabilityNameFlavor::Alias"; + break; + } + sbCpp << ", "; + + // abstract base. + auto abstractBase = def->getAbstractBase(); + if (abstractBase) + { + sbCpp << "CapabilityName::" << abstractBase->name; + } + else + { + sbCpp << "CapabilityName::Invalid"; + } + sbCpp << ", "; + + // canonnical representation. + sbCpp << def->rank << ", { kCapabilityConjunctions + " << def->serializedCanonicalRepresentation.first << ", " << def->serializedCanonicalRepresentation.count << "} },\n"; + } + + sbCpp << "};\n"; + return SLANG_OK; +} + + +SlangResult parseDefFile(DiagnosticSink* sink, String inputPath, List>& outDefs) +{ + auto sourceManager = sink->getSourceManager(); + + String contents; + SLANG_RETURN_ON_FAIL(File::readAllText(inputPath, contents)); + PathInfo pathInfo = PathInfo::makeFromString(inputPath); + SourceFile* sourceFile = sourceManager->createSourceFileWithString(pathInfo, contents); + SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr, SourceLoc()); + Lexer lexer; + NamePool namePool; + RootNamePool rootPool; + namePool.setRootNamePool(&rootPool); + lexer.initialize(sourceView, sink, &namePool, sourceManager->getMemoryArena()); + + CapabilityDefParser parser(&lexer, sink); + + SLANG_RETURN_ON_FAIL(parser.parseDefs()); + outDefs = _Move(parser.m_defs); + return SLANG_OK; +} + +void printDiagnostics(DiagnosticSink* sink) +{ + ComPtr blob; + sink->getBlobIfNeeded(blob.writeRef()); + if (blob) + { + fprintf(stderr, "%s", (const char*)blob->getBufferPointer()); + } +} + +void writeIfChanged(String fileName, String content) +{ + if (File::exists(fileName)) + { + String existingContent; + File::readAllText(fileName, existingContent); + if (existingContent.getUnownedSlice().trim() == content.getUnownedSlice().trim()) + return; + } + File::writeAllText(fileName, content); +} + +int main(int argc, const char* const* argv) +{ + if (argc < 2) + { + fprintf( + stderr, + "Usage: %s\n", + argc >= 1 ? argv[0] : "slang-capabilities-generator"); + return 1; + } + String targetDir; + for (int i = 0; i < argc - 1; i++) + { + if (strcmp(argv[i], "--target-directory") == 0) + targetDir = argv[i + 1]; + } + + String inPath = argv[1]; + if (targetDir.getLength() == 0) + targetDir = Path::getParentDirectory(inPath); + + auto outCppPath = Path::combine(targetDir, "slang-generated-capability-defs-impl.h"); + auto outHeaderPath = Path::combine(targetDir, "slang-generated-capability-defs.h"); + auto outLookupPath = Path::combine(targetDir, "slang-lookup-capability-defs.cpp"); + SourceManager sourceManager; + sourceManager.initialize(nullptr, OSFileSystem::getExtSingleton()); + DiagnosticSink sink(&sourceManager, nullptr); + List> defs; + if (SLANG_FAILED(parseDefFile(&sink, inPath, defs))) + { + printDiagnostics(&sink); + return 1; + } + + StringBuilder sbHeader, sbCpp; + if (SLANG_FAILED(generateDefinitions(defs, sbHeader, sbCpp))) + { + return 1; + } + + writeIfChanged(outHeaderPath, sbHeader.produceString()); + writeIfChanged(outCppPath, sbCpp.produceString()); + + List opnames; + for (auto def : defs) + { + opnames.add(def->name); + } + + if (SLANG_FAILED(writePerfectHashLookupCppFile(outLookupPath, opnames, "CapabilityName", "CapabilityName::", "slang-capability.h", &sink))) + { + printDiagnostics(&sink); + return 1; + } + return 0; +} diff --git a/tools/slang-capability-generator/slang-capability-diagnostic-defs.h b/tools/slang-capability-generator/slang-capability-diagnostic-defs.h new file mode 100644 index 000000000..a9436cd9c --- /dev/null +++ b/tools/slang-capability-generator/slang-capability-diagnostic-defs.h @@ -0,0 +1,57 @@ +// + +// 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 "DiagnosticDefs.h" +#define DIAGNOSTIC(id, severity, name, messageFormat) /* */ +#endif + +// +// -1 - Notes that decorate another diagnostic. +// + +DIAGNOSTIC(-1, Note, seeDefinitionOf, "see definition of '$0'") + +// +// 0xxxx - Command line and interaction with host platform APIs. +// + +DIAGNOSTIC( 1, Error, cannotOpenFile, "cannot open file '$0'.") +DIAGNOSTIC( 2, Error, cannotFindFile, "cannot find file '$0'.") +DIAGNOSTIC( 4, Error, cannotWriteOutputFile, "cannot write output file '$0'.") +DIAGNOSTIC( 5, Error, failedToLoadDynamicLibrary, "failed to load dynamic library '$0'") +DIAGNOSTIC( 6, Error, tooManyOutputPathsSpecified, "$0 output paths specified, but only $1 entry points given") + +// +// 2xxxx - Parsing +// + +DIAGNOSTIC(20003, Error, unexpectedToken, "unexpected $0") +DIAGNOSTIC(20001, Error, unexpectedTokenExpectedTokenType, "unexpected $0, expected $1") +DIAGNOSTIC(20001, Error, unexpectedTokenExpectedTokenName, "unexpected $0, expected '$1'") + +DIAGNOSTIC(0, Error, tokenNameExpectedButEOF, "\"$0\" expected but end of file encountered.") +DIAGNOSTIC(0, Error, tokenTypeExpectedButEOF, "$0 expected but end of file encountered.") +DIAGNOSTIC(20001, Error, tokenNameExpected, "\"$0\" expected") +DIAGNOSTIC(20001, Error, tokenNameExpectedButEOF2, "\"$0\" expected but end of file encountered.") +DIAGNOSTIC(20001, Error, tokenTypeExpected, "$0 expected") +DIAGNOSTIC(20001, Error, tokenTypeExpectedButEOF2, "$0 expected but end of file encountered.") +DIAGNOSTIC(20001, Error, typeNameExpectedBut, "unexpected $0, expected type name") +DIAGNOSTIC(20001, Error, typeNameExpectedButEOF, "type name expected but end of file encountered.") +DIAGNOSTIC(20001, Error, unexpectedEOF, " Unexpected end of file.") +DIAGNOSTIC(20002, Error, syntaxError, "syntax error.") +DIAGNOSTIC(20003, Error, undefinedIdentifier, "undefined identifier \"$0\".") +DIAGNOSTIC(20004, Error, redefinition, "capability redefinition: '$0'.") +#undef DIAGNOSTIC diff --git a/tools/slang-lookup-generator/lookup-generator-main.cpp b/tools/slang-lookup-generator/lookup-generator-main.cpp index a6f43c102..b99cf0e53 100644 --- a/tools/slang-lookup-generator/lookup-generator-main.cpp +++ b/tools/slang-lookup-generator/lookup-generator-main.cpp @@ -4,7 +4,7 @@ #include "../../source/compiler-core/slang-json-parser.h" #include "../../source/compiler-core/slang-json-value.h" #include "../../source/compiler-core/slang-lexer.h" -#include "../../source/compiler-core/slang-perfect-hash.h" +#include "../../source/compiler-core/slang-perfect-hash-codegen.h" #include "../../source/core/slang-io.h" #include "../../source/core/slang-secure-crt.h" #include "../../source/core/slang-string-util.h" @@ -69,46 +69,6 @@ static List extractOpNames(UnownedStringSlice& error, const JSONValue& v return opnames; } -void writeHashFile( - const char* const outCppPath, - const char* valueType, - const char* valuePrefix, - const List includes, - const HashParams& hashParams, - const List values) -{ - StringBuilder sb; - StringWriter writer(&sb, WriterFlags(0)); - WriterHelper w(&writer); - - w.print("// Hash function for %s\n", valueType); - w.print("//\n"); - w.print("// This file was thoughtfully generated by a machine,\n"); - w.print("// don't even think about modifying it yourself!\n"); - w.print("//\n"); - w.print("\n"); - for (const auto& i : includes) - { - w.print("#include \"%s\"\n", i.getBuffer()); - } - w.print("\n"); - w.print("\n"); - w.print("namespace Slang\n"); - w.print("{\n"); - w.print("\n"); - - w.put(perfectHashToEmbeddableCpp( - hashParams, - UnownedStringSlice(valueType), - (String("lookup") + valueType).getUnownedSlice(), - values - ).getBuffer()); - - w.print("}\n"); - - File::writeAllTextIfChanged(outCppPath, sb.getUnownedSlice()); -} - int main(int argc, const char* const* argv) { using namespace Slang; @@ -166,38 +126,8 @@ int main(int argc, const char* const* argv) opnames.add(w); } - HashParams hashParams; - auto r = minimalPerfectHash(opnames, hashParams); - switch (r) - { - case HashFindResult::UnavoidableHashCollision: - { - sink.diagnoseRaw( - Severity::Error, - "Unable to find a non-overlapping hash function.\n" - "The hash function probably has a unavoidable " - "collision for some input words\n"); - return 1; - } - case HashFindResult::NonUniqueKeys: - { - sink.diagnoseRaw(Severity::Error, "Input word list has duplicates\n"); - return 1; - } - case HashFindResult::Success:; - } - - List values; - values.reserve (hashParams.destTable.getCount()); - for(const auto& v : hashParams.destTable) - values.add(enumerantPrefix + v); - writeHashFile( - outCppPath, - enumName, - enumerantPrefix, - { "../core/slang-common.h", "../core/slang-string.h", enumHeader }, - hashParams, - values); + if (SLANG_FAILED(writePerfectHashLookupCppFile(outCppPath, opnames, enumName, enumerantPrefix, enumHeader, &sink))) + return -1; return 0; } -- cgit v1.2.3