diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2023-04-29 09:24:26 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-29 09:24:26 -0400 |
| commit | 19c0866b050a022406867aa650302f4efbf8e010 (patch) | |
| tree | f5ed4e1f5d27865518daf81c7e861b4908186b23 /source/core | |
| parent | c571bcb025009f9c662e8d631fa49dbfed560287 (diff) | |
CommandOptions (#2856)
* WIP CommandOptions
* Fix some output issues.
* Simplify word wrapping.
* Add file extensions.
* Change how lookup takes place.
Add appendSplit functions to StringUtil.
Make Categories hold the index range of their options.
* Small improvement.
* Lookup with partial option names.
* Associate user values.
* Encoding flags in the name.
* Refactor setting up of command options.
* Use CommandOptions in slang-options.
* Remove old help text.
* Cache the CommandOptions on the Session.
* Range checking.
Fix bug in the Options handling.
* Extra checks for validity.
* Get categories directly.
* Slight improvements over output.
* Added NameValue types.
* Fix typo.
Remove some now unused diagnostics.
Fix diagnostic in testing, as output has changed.
* Add minimal usage message.
* Remove platform executable extension from diagnostics output.
* Some improvements around getting names from NameValue types.
* Improve some option descriptions.
* Small fixes.
Diffstat (limited to 'source/core')
| -rw-r--r-- | source/core/slang-command-options.cpp | 842 | ||||
| -rw-r--r-- | source/core/slang-command-options.h | 253 | ||||
| -rw-r--r-- | source/core/slang-name-value.cpp | 163 | ||||
| -rw-r--r-- | source/core/slang-name-value.h | 76 | ||||
| -rw-r--r-- | source/core/slang-string-util.cpp | 24 | ||||
| -rw-r--r-- | source/core/slang-string-util.h | 7 | ||||
| -rw-r--r-- | source/core/slang-type-text-util.cpp | 292 | ||||
| -rw-r--r-- | source/core/slang-type-text-util.h | 31 |
8 files changed, 1514 insertions, 174 deletions
diff --git a/source/core/slang-command-options.cpp b/source/core/slang-command-options.cpp new file mode 100644 index 000000000..4577bcb8e --- /dev/null +++ b/source/core/slang-command-options.cpp @@ -0,0 +1,842 @@ +// slang-command-options.cpp + +#include "slang-command-options.h" + +#include "slang-string-util.h" +#include "slang-char-util.h" +#include "slang-byte-encode-util.h" + +namespace Slang { + +SlangResult CommandOptions::_addName(LookupKind kind, const UnownedStringSlice& name, Index targetIndex) +{ + NameKey nameKey; + nameKey.kind = kind; + nameKey.nameIndex = (Index)m_pool.add(name); + + if (m_nameMap.tryGetValueOrAdd(nameKey, targetIndex)) + { + SLANG_ASSERT(!"Option is already added!"); + return SLANG_FAIL; + } + return SLANG_OK; +} + +SlangResult CommandOptions::_addOptionName(const UnownedStringSlice& name, Flags flags, Index targetIndex) +{ + SLANG_RETURN_ON_FAIL(_addName(LookupKind::Option, name, targetIndex)); + + // Add to prefix flags + if (flags & (Flag::CanPrefix | Flag::IsPrefix)) + { + const auto length = name.getLength(); + SLANG_ASSERT(length < 32); + m_prefixSizes |= uint32_t(1) << length; + } + + return SLANG_OK; +} + +SlangResult CommandOptions::_addValueName(const UnownedStringSlice& name, Index categoryIndex, Index optionIndex) +{ + return _addName(LookupKind(categoryIndex), name, optionIndex); +} + +SlangResult CommandOptions::_addUserValue(LookupKind kind, UserValue userValue, Index targetIndex) +{ + // If it's invalid we don't need to add it + if (userValue == kInvalidUserValue) + { + return SLANG_OK; + } + + UserValueKey userValueKey; + userValueKey.kind = kind; + userValueKey.userValue = userValue; + + if (m_userValueMap.tryGetValueOrAdd(userValueKey, targetIndex)) + { + SLANG_ASSERT(!"UserValue is already used for this kind!"); + return SLANG_FAIL; + } + return SLANG_OK; +} + +UnownedStringSlice CommandOptions::_addString(const char* text) +{ + if (text == nullptr) + { + return UnownedStringSlice(); + } + return _addString(UnownedStringSlice(text)); +} + +UnownedStringSlice CommandOptions::_addString(const UnownedStringSlice& slice) +{ + const auto length = slice.getLength(); + const char* dst = m_arena.allocateString(slice.begin(), length); + return UnownedStringSlice(dst, length); +} + +Index CommandOptions::_addOption(const UnownedStringSlice& name, const Option& inOption) +{ + return _addOption(&name, 1, inOption); +} + +Index CommandOptions::_addOption(const UnownedStringSlice* names, Count namesCount, const Option& inOption) +{ + SLANG_ASSERT(namesCount > 0); + SLANG_ASSERT(inOption.categoryIndex >= 0); + + if (namesCount <= 0 || inOption.categoryIndex < 0) + { + return -1; + } + + auto& cat = m_categories[inOption.categoryIndex]; + + // If there are already options associated with this category, we have to be in the run of the last ones added + if (cat.optionStartIndex != cat.optionEndIndex) + { + // If we aren't at the end then this is an error + if (cat.optionEndIndex != m_options.getCount()) + { + return -1; + } + } + else + { + // Move to the end of the option list + cat.optionStartIndex = m_options.getCount(); + cat.optionEndIndex = cat.optionStartIndex; + } + + Option option(inOption); + + const Index optionIndex = m_options.getCount(); + + if (cat.kind == CategoryKind::Option) + { + for (Index i = 0; i < namesCount; ++i) + { + if (SLANG_FAILED(_addOptionName(names[i], inOption.flags, optionIndex))) + { + return -1; + } + } + if (SLANG_FAILED(_addUserValue(LookupKind::Option, inOption.userValue, optionIndex))) + { + return -1; + } + } + else + { + for (Index i = 0; i < namesCount; ++i) + { + _addValueName(names[i], inOption.categoryIndex, optionIndex); + } + if (SLANG_FAILED(_addUserValue(LookupKind(inOption.categoryIndex), inOption.userValue, optionIndex))) + { + return -1; + } + } + + if (namesCount == 1) + { + // We already have storage on the slice + option.names = m_pool.addAndGetSlice(names[0]); + } + else + { + // Put all of the names in the list + StringBuilder buf; + StringUtil::join(names, namesCount, ',', buf); + // Allocate storage no in the pool + option.names = _addString(buf.getUnownedSlice()); + } + + m_options.add(option); + + // Set the end index + cat.optionEndIndex = optionIndex + 1; + + return optionIndex; +} + +static void _handlePostFix(UnownedStringSlice& ioSlice, CommandOptions::Flags& ioFlags) +{ + if (ioSlice.endsWith(toSlice("..."))) + { + if (ioSlice.endsWith(toSlice("?..."))) + { + ioFlags |= CommandOptions::Flag::CanPrefix; + ioSlice = ioSlice.head(ioSlice.getLength() - 4); + } + else + { + ioFlags |= CommandOptions::Flag::IsPrefix; + ioSlice = ioSlice.head(ioSlice.getLength() - 3); + } + } +} + +void CommandOptions::add(const char* inName, const char* usage, const char* description, UserValue userValue) +{ + UnownedStringSlice nameSlice(inName); + + Option option; + option.categoryIndex = m_currentCategoryIndex; + option.usage = _addString(usage); + option.description = _addString(UnownedStringSlice(description)); + option.userValue = userValue; + option.flags = 0; + + if (nameSlice.indexOf(',') >= 0) + { + List<UnownedStringSlice> names; + StringUtil::split(nameSlice, ',', names); + + for (auto& name : names) + { + _handlePostFix(name, option.flags); + } + + _addOption(names.getBuffer(), names.getCount(), option); + } + else + { + _handlePostFix(nameSlice, option.flags); + + _addOption(&nameSlice, 1, option); + } +} + +void CommandOptions::add(const UnownedStringSlice* names, Count namesCount, const char* usage, const char* description, UserValue userValue, Flags flags) +{ + Option option; + option.categoryIndex = m_currentCategoryIndex; + option.usage = _addString(usage); + option.description = _addString(UnownedStringSlice(description)); + option.flags = flags; + option.userValue = userValue; + + _addOption(names, namesCount, option); +} + +Index CommandOptions::_addValue(const UnownedStringSlice& name, const Option& inOption) +{ + SLANG_ASSERT(m_currentCategoryIndex >= 0); + SLANG_ASSERT(m_categories[m_currentCategoryIndex].kind == CategoryKind::Value); + + return _addOption(name, inOption); +} + +void CommandOptions::addValues(const ValuePair* pairs, Count pairsCount) +{ + for (auto& pair : makeConstArrayView(pairs, pairsCount)) + { + addValue(pair.name, pair.description); + } +} + +void CommandOptions::addValues(const ConstArrayView<NameValue>& values) +{ + for (const auto& value : values) + { + addValue(value.name, UserValue(value.value)); + } +} + +void CommandOptions::addValues(const ConstArrayView<NamesValue>& values) +{ + for (const auto& value : values) + { + addValue(value.names, UserValue(value.value)); + } +} + +void CommandOptions::addValues(const ConstArrayView<NamesDescriptionValue>& values) +{ + for (const auto& value : values) + { + addValue(value.names, value.description, UserValue(value.value)); + } +} + +void CommandOptions::addValuesWithAliases(const ConstArrayView<NameValue>& inValues) +{ + List<NameValue> values; + values.addRange(inValues.getBuffer(), inValues.getCount()); + + values.sort([](const NameValue& a, const NameValue& b) -> bool { return a.value < b.value; }); + + List<UnownedStringSlice> names; + + const Count count = values.getCount(); + Index i = 0; + while (i < count) + { + names.clear(); + + const auto value = values[i].value; + names.add(UnownedStringSlice(values[i++].name)); + + for (; i < count && values[i].value == value; ++i) + { + names.add(UnownedStringSlice(values[i].name)); + } + + addValue(names.getBuffer(), names.getCount(), UserValue(value)); + } +} + +void CommandOptions::addValue(const UnownedStringSlice& name, UserValue userValue) +{ + Option option; + option.categoryIndex = m_currentCategoryIndex; + option.userValue = userValue; + _addValue(name, option); +} + +void CommandOptions::addValue(const UnownedStringSlice& name, const UnownedStringSlice& description, UserValue userValue) +{ + Option option; + option.categoryIndex = m_currentCategoryIndex; + option.description = _addString(description); + option.userValue = userValue; + _addValue(name, option); +} + +void CommandOptions::addValue(const UnownedStringSlice* names, Count namesCount, UserValue userValue) +{ + Option option; + option.categoryIndex = m_currentCategoryIndex; + option.userValue = userValue; + + SLANG_ASSERT(m_currentCategoryIndex >= 0); + SLANG_ASSERT(m_categories[m_currentCategoryIndex].kind == CategoryKind::Value); + + _addOption(names, namesCount, option); +} + +void CommandOptions::addValue(const char* inName, const char* description, UserValue userValue) +{ + const UnownedStringSlice name(inName); + + if (description) + { + addValue(name, UnownedStringSlice(description), userValue); + } + else + { + addValue(name, userValue); + } +} + +void CommandOptions::addValue(const char* name, UserValue userValue) +{ + addValue(UnownedStringSlice(name), userValue); +} + +Index CommandOptions::addCategory(CategoryKind kind, const char* name, const char* description, UserValue userValue) +{ + const UnownedStringSlice nameSlice(name); + + const auto categoryIndex = m_categories.getCount(); + + if (SLANG_FAILED(_addName(LookupKind::Category, nameSlice, categoryIndex))) + { + return -1; + } + + if (userValue != kInvalidUserValue) + { + _addUserValue(LookupKind::Category, userValue, categoryIndex); + } + + Category cat; + cat.kind = kind; + cat.name = _addString(nameSlice); + cat.description = _addString(description); + cat.userValue = userValue; + + m_currentCategoryIndex = categoryIndex; + + m_categories.add(cat); + + return categoryIndex; +} + +void CommandOptions::setCategory(const char* name) +{ + const UnownedStringSlice nameSlice(name); + + for (Index i = 0; i < m_categories.getCount(); ++i) + { + auto& cat = m_categories[i]; + if (cat.name == nameSlice) + { + m_currentCategoryIndex = i; + return; + } + } + + SLANG_ASSERT(!"Category not found"); + + m_currentCategoryIndex = -1; +} + +Index CommandOptions::findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name) const +{ + const auto nameIndex = m_pool.findIndex(name); + // If the name isn't in the pool then there isn't a category with this name + if (nameIndex < 0) + { + return -1; + } + + NameKey key; + key.kind = kind; + key.nameIndex = nameIndex; + + if (auto ptr = m_nameMap.tryGetValue(key)) + { + return *ptr; + } + + return -1; +} + +Index CommandOptions::findTargetIndexByUserValue(LookupKind kind, UserValue userValue) const +{ + UserValueKey key; + key.kind = kind; + key.userValue = userValue; + + if (auto ptr = m_userValueMap.tryGetValue(key)) + { + return *ptr; + } + + return -1; +} + +Index CommandOptions::findCategoryByCaseInsensitiveName(const UnownedStringSlice& slice) const +{ + const Count count = m_categories.getCount(); + for (Index i = 0; i < count; ++i) + { + const auto& cat = m_categories[i]; + + if (cat.name.caseInsensitiveEquals(slice)) + { + return i; + } + } + return -1; +} + +Index CommandOptions::findOptionByCategoryUserValue(UserValue categoryUserValue, const UnownedStringSlice& name) const +{ + Index categoryIndex = findTargetIndexByUserValue(LookupKind::Category, categoryUserValue); + if (categoryIndex < 0) + { + return -1; + } + + return findValueByName(categoryIndex, name); +} + +Index CommandOptions::findOptionByName(const UnownedStringSlice& name) const +{ + { + auto index = findTargetIndexByName(LookupKind::Option, name); + if (index >= 0) + { + return index; + } + } + + // We need to search for partials + auto prefixSizes = m_prefixSizes; + + while (prefixSizes) + { + auto prefixSize = ByteEncodeUtil::calcMsb32(prefixSizes); + + if (prefixSize < name.getLength()) + { + // Look it up + const auto index = findTargetIndexByName(LookupKind::Option, name.head(prefixSize)); + if (index >= 0) + { + auto& option = m_options[index]; + + // If the option accepts prefixes, we return the index + if (option.flags & (Flag::CanPrefix | Flag::IsPrefix)) + { + return index; + } + } + } + + // Remove the bit + prefixSizes &= ~(uint32_t(1) << prefixSize); + } + + // Was not found + return -1; +} + +ConstArrayView<CommandOptions::Option> CommandOptions::getOptionsForCategory(Index categoryIndex) const +{ + const auto& cat = m_categories[categoryIndex]; + return makeConstArrayView(m_options.getBuffer() + cat.optionStartIndex, cat.optionEndIndex - cat.optionStartIndex); +} + +void CommandOptions::getCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const +{ + outNames.clear(); + for (const auto& option : getOptionsForCategory(categoryIndex)) + { + StringUtil::appendSplit(option.names, ',', outNames); + } +} + +void CommandOptions::findCategoryIndicesFromUsage(const UnownedStringSlice& slice, List<Index>& outCategories) const +{ + const auto* cur = slice.begin(); + const auto* end = slice.end(); + + while (cur < end) + { + // Find < + while (cur < end && *cur != '<') cur++; + + // If we found it look for the end + if (cur < end && *cur == '<') + { + ++cur; + auto start = cur; + while (cur < end && (CharUtil::isAlphaOrDigit(*cur) || *cur == '-' || *cur == '_') && *cur != '>') + { + cur++; + } + + // If we hit closing > we want to lookup + if (cur < end && *cur == '>') + { + const UnownedStringSlice categoryName(start, cur); + + Index categoryIndex = findCategoryByName(categoryName); + if (categoryIndex >= 0 && outCategories.indexOf(categoryIndex) < 0) + { + outCategories.add(categoryIndex); + } + } + + cur++; + } + } +} + +Count CommandOptions::getOptionCountInRange(Index categoryIndex, UserValue start, UserValue nonInclEnd) const +{ + const UserIndex startIndex = UserIndex(start); + const UserIndex endIndex = UserIndex(nonInclEnd); + + Count count = 0; + + for (auto& opt : getOptionsForCategory(categoryIndex)) + { + const auto val = opt.userValue; + if (val == kInvalidUserValue) + { + continue; + } + + const auto valIndex = UserIndex(val); + count += Index(valIndex >= startIndex && valIndex < endIndex); + } + + return count; +} + +Count CommandOptions::getOptionCountInRange(LookupKind kind, UserValue start, UserValue nonInclEnd) const +{ + Index count = 0; + + if (kind == LookupKind::Category) + { + const UserIndex startIndex = UserIndex(start); + const UserIndex endIndex = UserIndex(nonInclEnd); + + for (auto& cat : m_categories) + { + if (cat.userValue != kInvalidUserValue) + { + const auto valIndex = UserIndex(cat.userValue); + count += Index(valIndex >= startIndex && valIndex < endIndex); + } + } + } + if (kind == LookupKind::Option) + { + // If we are lookup up options, then we iterate over all option categories + const auto catCount = m_categories.getCount(); + for (Index categoryIndex = 0; categoryIndex < catCount; ++categoryIndex) + { + if (m_categories[categoryIndex].kind == CategoryKind::Option) + { + count += getOptionCountInRange(categoryIndex, start, nonInclEnd); + } + } + } + else if (Index(kind) >= 0) + { + // It's a regular category + count = getOptionCountInRange(Index(kind), start, nonInclEnd); + } + + return count; +} + + +bool CommandOptions::hasContiguousUserValueRange(LookupKind kind, UserValue start, UserValue nonInclEnd) const +{ + const Count rangeCount = Count(nonInclEnd) - Count(start); + SLANG_ASSERT(rangeCount >= 0); + + if (rangeCount <= 0) + { + return true; + } + + const Count count = getOptionCountInRange(kind, start, nonInclEnd); + return rangeCount == count; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!! CommandOptionsWriter !!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +void CommandOptionsWriter::appendDescriptionForCategory(const CommandOptions& options, Index categoryIndex) +{ + const auto& categories = options.getCategories(); + + const auto& category = categories[categoryIndex]; + + // Header + { + const auto count = m_builder.getLength(); + if (category.kind == CategoryKind::Value) + { + m_builder << "<" << category.name << ">"; + } + else + { + m_builder << category.name; + } + + const auto length = m_builder.getLength() - count; + m_builder << "\n"; + + m_builder.appendRepeatedChar('=', length); + + m_builder << "\n\n"; + + // If there is a description output it + if (category.description.getLength() > 0) + { + _appendText(0, category.description); + m_builder << "\n"; + } + } + + for (auto& option : options.getOptionsForCategory(categoryIndex)) + { + m_builder << m_indentSlice; + + if (option.usage.getLength()) + { + m_builder << option.usage; + } + else + { + List<UnownedStringSlice> names; + StringUtil::split(option.names, ',', names); + + _appendWithWrap(1, names, toSlice(", ")); + } + + if (option.description.getLength() == 0) + { + m_builder << "\n"; + continue; + } + + m_builder << ": "; + + _appendText(2, option.description); + + if (option.usage.getLength()) + { + List<Index> usageCategoryIndices; + options.findCategoryIndicesFromUsage(option.usage, usageCategoryIndices); + + for (auto usageCategoryIndex : usageCategoryIndices) + { + auto& usageCat = categories[usageCategoryIndex]; + + m_builder << m_indentSlice << m_indentSlice; + m_builder << "<" << usageCat.name << "> can be: "; + + List<UnownedStringSlice> optionNames; + options.getCategoryOptionNames(usageCategoryIndex, optionNames); + + _appendWithWrap(2, optionNames, toSlice(", ")); + + m_builder << "\n"; + } + } + } + + m_builder << "\n"; +} + +void CommandOptionsWriter::appendDescription(const CommandOptions& options) +{ + // Go through categories in order + + const auto& categories = options.getCategories(); + + for (Index categoryIndex = 0; categoryIndex < categories.getCount(); ++categoryIndex) + { + appendDescriptionForCategory(options, categoryIndex); + } +} + +void CommandOptionsWriter::_appendText(Count indentCount, const UnownedStringSlice& text) +{ + List<UnownedStringSlice> lines; + StringUtil::calcLines(text, lines); + + // Remove very last line if it's empty + if (lines.getCount() > 1 && lines.getLast().trim().getLength() == 0) + { + lines.removeLast(); + } + + _appendWithWrap(indentCount, lines); +} + +Count CommandOptionsWriter::_getCurrentLineLength() +{ + // Work out the current line length + const char* start = m_builder.begin(); + const char* cur = m_builder.end(); + + Count lineLength = 0; + + if (cur > start) + { + for (--cur; cur > start; --cur) + { + const auto c = *cur; + if (c == '\n' || c == '\r') + { + ++cur; + break; + } + } + + lineLength = Count(ptrdiff_t(m_builder.end() - cur)); + } + + return lineLength; +} + +void CommandOptionsWriter::_requireIndent(Count indentCount) +{ + const auto length = m_builder.getLength(); + if (length) + { + const auto c = m_builder[length - 1]; + if (c == '\n' || c == '\r') + { + for (Index j = 0; j < indentCount; j++) + { + m_builder.append(m_indentSlice); + } + } + } +} + +void CommandOptionsWriter::_appendWithWrap(Count indentCount, List<UnownedStringSlice>& lines) +{ + List<UnownedStringSlice> words; + + for (auto line : lines) + { + if (line.trim().getLength() == 0 || line.startsWith(toSlice(" "))) + { + // Append the line as is after the indent + _requireIndent(indentCount); + m_builder << line << "\n"; + } + else + { + words.clear(); + StringUtil::split(line, ' ', words); + + _requireIndent(indentCount); + + _appendWithWrap(indentCount, words, toSlice(" ")); + m_builder << "\n"; + } + } +} + + + +void CommandOptionsWriter::_appendWithWrap(Count indentCount, List<UnownedStringSlice>& slices, const UnownedStringSlice& delimit) +{ + Count lineLength = _getCurrentLineLength(); + + const auto count = slices.getCount(); + + for (Index i = 0; i < count; ++i) + { + auto slice = slices[i]; + + auto sliceLength = slice.getLength(); + + if (i < count - 1) + { + sliceLength += delimit.getLength(); + } + + // If out of space onto the next line + if (lineLength + sliceLength > m_lineLength) + { + m_builder.append("\n"); + + lineLength = indentCount * m_indentSlice.getLength(); + + for (Index j = 0; j < indentCount; j++) + { + m_builder.append(m_indentSlice); + } + } + + m_builder.append(slice); + if (i < count - 1) + { + m_builder.append(delimit); + } + + lineLength += sliceLength; + } +} + +} // namespace Slang + + diff --git a/source/core/slang-command-options.h b/source/core/slang-command-options.h new file mode 100644 index 000000000..a92d4d8f8 --- /dev/null +++ b/source/core/slang-command-options.h @@ -0,0 +1,253 @@ +#ifndef SLANG_CORE_COMMAND_OPTIONS_H +#define SLANG_CORE_COMMAND_OPTIONS_H + +#include "slang-basic.h" +#include "slang-string-slice-pool.h" +#include "slang-name-value.h" + +namespace Slang +{ + +/* For convenience we encode within "names" flags. +"-D..." means that -D *must* be followed by the value +"-D?..." means that -D *can* be a prefix, or it might be followed with the arg +*/ + +struct CommandOptions +{ + typedef uint32_t Flags; + + typedef int32_t UserIndex; + enum class UserValue : UserIndex; + static const UserValue kInvalidUserValue = UserValue(0x80000000); + + enum class LookupKind : int32_t + { + Category = -2, ///< Lookup a category name + Option = -1, ///< Lookup an option name (all options use the same lookup index even if in different categories) + Base = 0, ///< Lookup via category index + }; + + enum class CategoryKind + { + Option, ///< Command line option (like "-D") + Value, ///< One of a set of values (such as an enum or some other kind of list of values) + }; + + struct ValuePair + { + const char* name; + const char* description; + }; + + struct Category + { + UserValue userValue = kInvalidUserValue; + + CategoryKind kind; + UnownedStringSlice name; + UnownedStringSlice description; + + // Holds the span that defines all of the options associated with the category + Index optionStartIndex = 0; + Index optionEndIndex = 0; + }; + + struct Flag + { + enum Enum : Flags + { + CanPrefix = 0x1, /// Allows -Dfsggf or -D fdsfsd + IsPrefix = 0x2, /// Is an option that can only be a prefix + }; + }; + + struct Option + { + UnownedStringSlice names; ///< Comma delimited list of names, first name is the default + UnownedStringSlice usage; ///< Describes usage, can be empty + UnownedStringSlice description; ///< A description of usage + + UserValue userValue = kInvalidUserValue; + + Index categoryIndex = -1; ///< Category this option belongs to + Flags flags = 0; ///< Flags about this option + }; + + /// Add a category + Index addCategory(CategoryKind kind, const char* name, const char* description, UserValue userValue = kInvalidUserValue); + /// Use an already known category. It's an error if the category isn't found + void setCategory(const char* name); + + void add(const char* name, const char* usage, const char* description, UserValue userValue = kInvalidUserValue); + void add(const UnownedStringSlice* names, Count namesCount, const char* usage, const char* description, UserValue userValue = kInvalidUserValue, Flags flags = 0); + + void addValue(const UnownedStringSlice& name, UserValue userValue = kInvalidUserValue); + void addValue(const UnownedStringSlice& name, const UnownedStringSlice& description, UserValue userValue = kInvalidUserValue); + void addValue(const char* name, const char* description, UserValue userValue = kInvalidUserValue); + void addValue(const char* name, UserValue userValue = kInvalidUserValue); + void addValue(const UnownedStringSlice* names, Count namesCount, UserValue userValue = kInvalidUserValue); + + /// Add values (without UserValue association) + void addValues(const ValuePair* pairs, Count pairsCount); + + /// Add values + void addValues(const ConstArrayView<NameValue>& values); + void addValues(const ConstArrayView<NamesValue>& values); + void addValues(const ConstArrayView<NamesDescriptionValue>& values); + + /// Sometimes values are listed with *names* per value. This method will take into account the aliases + void addValuesWithAliases(const ConstArrayView<NameValue>& values); + + /// Get the target index based off the name and the kind + Index findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name) const; + /// Given a kind and a user value lookup the target index + Index findTargetIndexByUserValue(LookupKind kind, UserValue userValue) const; + + /// Finds the category by name or -1 if not found + Index findCategoryByName(const UnownedStringSlice& name) const { return findTargetIndexByName(LookupKind::Category, name); } + /// Finds the option index by name or -1 if not found + Index findOptionByName(const UnownedStringSlice& name) const; + /// Find the option index of a value, using it's category index and the name + Index findValueByName(Index categoryIndex, const UnownedStringSlice& name) const { return findTargetIndexByName(LookupKind(categoryIndex), name); } + + /// Get the category index from a user value + Index findCategoryByUserValue(UserValue userValue) const { return findTargetIndexByUserValue(LookupKind::Category, userValue); } + /// Can only get options + Index findOptionByUserValue(UserValue userValue) const { return findTargetIndexByUserValue(LookupKind::Option, userValue); } + /// Get a value associated with a category + Index findValueByUserValue(Index categoryIndex, UserValue userValue) const { return findTargetIndexByUserValue(LookupKind(categoryIndex), userValue); } + + /// Given a category user value, find the associated name + /// Returns -1 if not found + Index findOptionByCategoryUserValue(UserValue categoryUserValue, const UnownedStringSlice& name) const; + + /// Find a category by case insensitive name. Returns -1 if not found + Index findCategoryByCaseInsensitiveName(const UnownedStringSlice& slice) const; + + /// Given a category index returns all the options associated. + ConstArrayView<Option> getOptionsForCategory(Index categoryIndex) const; + + /// Get the categories + const List<Category>& getCategories() const { return m_categories; } + + /// Get all the options + const List<Option>& getOptions() const { return m_options; } + + /// Get the option at the specified index + const Option& getOptionAt(Index index) const { return m_options[index]; } + + /// Find all of the categories in the usage slice + void findCategoryIndicesFromUsage(const UnownedStringSlice& usageSlice, List<Index>& outCategories) const; + /// Get all the option names associated with a category index + void getCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const; + + /// Set up a lookup kind from a category index + static LookupKind makeLookupKind(Index categoryIndex) { return LookupKind(categoryIndex); } + + /// Returns true, if all values from [start, end) are found for the kind + bool hasContiguousUserValueRange(LookupKind kind, UserValue start, UserValue nonInclEnd) const; + + /// Returns the number of options in the range + Count getOptionCountInRange(Index categoryIndex, UserValue start, UserValue nonInclEnd) const; + Count getOptionCountInRange(LookupKind kind, UserValue start, UserValue nonInclEnd) const; + + + /// Ctor + CommandOptions() : + m_pool(StringSlicePool::Style::Default), + m_arena(1024 * 2) + { + } + + /// Returns name in the m_optionPool or -1 on error + SlangResult _addOptionName(const UnownedStringSlice& name, Flags flags, Index targetIndex); + SlangResult _addValueName(const UnownedStringSlice& name, Index categoryIndex, Index targetIndex); + SlangResult _addName(LookupKind kind, const UnownedStringSlice& name, Index targetIndex); + + SlangResult _addUserValue(LookupKind kind, UserValue userValue, Index targetIndex); + + Index _addOption(const UnownedStringSlice& name, const Option& inOption); + Index _addOption(const UnownedStringSlice* names, Count namesCount, const Option& option); + + Index _addValue(const UnownedStringSlice& name, const Option& inOption); + + UnownedStringSlice _addString(const char* text); + UnownedStringSlice _addString(const UnownedStringSlice& slice); + + /// A key type that uses the combination of the lookup kind and a name index. + /// Maps to a target index that could be a category or an option index. + struct NameKey + { + typedef NameKey ThisType; + + SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return kind == rhs.kind && nameIndex == rhs.nameIndex; } + SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } + HashCode getHashCode() const { return combineHash(Slang::getHashCode(kind), Slang::getHashCode(nameIndex)); } + + LookupKind kind; ///< The kind of lookup + Index nameIndex; ///< The name index in the pool + }; + + struct UserValueKey + { + typedef UserValueKey ThisType; + + SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return kind == rhs.kind && userValue == rhs.userValue; } + SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } + HashCode getHashCode() const { return combineHash(Slang::getHashCode(kind), Slang::getHashCode(userValue)); } + + LookupKind kind; ///< The kind of lookup + UserValue userValue; ///< The user value + }; + + Index m_currentCategoryIndex = -1; + + List<Category> m_categories; + + // Holds a bit for all valid prefix sizes. Max prefix size is therefore 32 chars + uint32_t m_prefixSizes = 0; + + List<Option> m_options; ///< All of the entries describing each of the options + StringSlicePool m_pool; ///< Only holds options, and handle therefore matches up to m_entries + Dictionary<NameKey, Index> m_nameMap; ///< Maps a name to an option index + Dictionary<UserValueKey, Index> m_userValueMap; ///< Maps a user value (for a kind) to an index + + MemoryArena m_arena; ///< For other misc storage +}; + +struct CommandOptionsWriter +{ + typedef CommandOptions::CategoryKind CategoryKind; + + /// Append descirption for a category + void appendDescriptionForCategory(const CommandOptions& options, Index categoryIndex); + /// Appends a description of all of the options + void appendDescription(const CommandOptions& options); + + /// Get the builder that string is being written to + StringBuilder& getBuilder() { return m_builder; } + + /// Ctor + CommandOptionsWriter(): + m_indentSlice(toSlice(" ")) + { + } + + Count _getCurrentLineLength(); + + void _appendWithWrap(Count indentCount, List<UnownedStringSlice>& slices, const UnownedStringSlice& delimit); + void _appendWithWrap(Count indentCount, List<UnownedStringSlice>& lines); + void _requireIndent(Count indentCount); + void _appendText(Count indentCount, const UnownedStringSlice& text); + + + UnownedStringSlice m_indentSlice; + Count m_lineLength = 80; + + StringBuilder m_builder; +}; + +} // namespace Slang + +#endif diff --git a/source/core/slang-name-value.cpp b/source/core/slang-name-value.cpp new file mode 100644 index 000000000..5418043f5 --- /dev/null +++ b/source/core/slang-name-value.cpp @@ -0,0 +1,163 @@ +// slang-name-value.cpp + +#include "slang-name-value.h" + +#include "slang-string-util.h" +#include "slang-char-util.h" + +namespace Slang { + +/* static */ValueInt NameValueUtil::findValue(const ConstArrayView<NameValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue) +{ + for (const auto& opt : opts) + { + if (UnownedStringSlice(opt.name) == slice) + { + return opt.value; + } + } + return defaultValue; +} + +/* static */ValueInt NameValueUtil::findValue(const ConstArrayView<NamesValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue) +{ + for (const auto& opt : opts) + { + UnownedStringSlice names(opt.names); + + if (StringUtil::indexOfInSplit(names, ',', slice) >= 0) + { + return opt.value; + } + } + return defaultValue; +} + +/* static */ValueInt NameValueUtil::findValue(const ConstArrayView<NamesDescriptionValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue) +{ + for (const auto& opt : opts) + { + UnownedStringSlice names(opt.names); + if (StringUtil::indexOfInSplit(names, ',', slice) >= 0) + { + return opt.value; + } + } + return defaultValue; +} + +/* static */ UnownedStringSlice NameValueUtil::findName(const ConstArrayView<NameValue>& opts, ValueInt value, const UnownedStringSlice& defaultName) +{ + for (const auto& opt : opts) + { + if (opt.value == value) + { + return UnownedStringSlice(opt.name); + } + } + return defaultName; +} + +/* static */ UnownedStringSlice NameValueUtil::findName(const ConstArrayView<NamesValue>& opts, ValueInt value, const UnownedStringSlice& defaultName) +{ + for (const auto& opt : opts) + { + if (opt.value == value) + { + // Get the first name + return StringUtil::getAtInSplit(UnownedStringSlice(opt.names), ',', 0); + } + } + + return defaultName; +} + +/* static */ UnownedStringSlice NameValueUtil::findName(const ConstArrayView<NamesDescriptionValue>& opts, ValueInt value, const UnownedStringSlice& defaultName) +{ + for (const auto& opt : opts) + { + if (opt.value == value) + { + // Get the first name + return StringUtil::getAtInSplit(UnownedStringSlice(opt.names), ',', 0); + } + } + + return defaultName; +} + + +/* static */UnownedStringSlice NameValueUtil::findDescription(const ConstArrayView<NamesDescriptionValue>& opts, ValueInt value, const UnownedStringSlice& defaultDescription) +{ + for (const auto& opt : opts) + { + if (opt.value == value && opt.description) + { + return UnownedStringSlice(opt.description); + } + } + + return defaultDescription; +} + +/* static */ void NameValueUtil::appendNames(NameKind kind, const ConstArrayView<NameValue>& opts, List<UnownedStringSlice>& out) +{ + SLANG_UNUSED(kind); + for (auto& opt : opts) + { + out.add(UnownedStringSlice(opt.name)); + } +} + +static void _appendNames(NameValueUtil::NameKind kind, const char* names, List<UnownedStringSlice>& out) +{ + if (kind == NameValueUtil::NameKind::All) + { + StringUtil::appendSplit(UnownedStringSlice(names), ',', out); + } + else + { + out.add(StringUtil::getAtInSplit(UnownedStringSlice(names), ',', 0)); + } +} + +/* static */ void NameValueUtil::appendNames(NameKind kind, const ConstArrayView<NamesValue>& opts, List<UnownedStringSlice>& out) +{ + for (auto& opt : opts) + { + _appendNames(kind, opt.names, out); + } +} + +/* static */ void NameValueUtil::appendNames(NameKind kind, const ConstArrayView<NamesDescriptionValue>& opts, List<UnownedStringSlice>& out) +{ + for (auto& opt : opts) + { + _appendNames(kind, opt.names, out); + } +} + +/* static */ List<UnownedStringSlice> NameValueUtil::getNames(NameKind kind, const ConstArrayView<NameValue>& opts) +{ + List<UnownedStringSlice> names; + appendNames(kind, opts, names); + return names; +} + +/* static */ List<UnownedStringSlice> NameValueUtil::getNames(NameKind kind, const ConstArrayView<NamesValue>& opts) +{ + List<UnownedStringSlice> names; + appendNames(kind, opts, names); + return names; +} + +/* static */ List<UnownedStringSlice> NameValueUtil::getNames(NameKind kind, const ConstArrayView<NamesDescriptionValue>& opts) +{ + List<UnownedStringSlice> names; + appendNames(kind, opts, names); + return names; +} + +} // namespace Slang + + diff --git a/source/core/slang-name-value.h b/source/core/slang-name-value.h new file mode 100644 index 000000000..a50e149a8 --- /dev/null +++ b/source/core/slang-name-value.h @@ -0,0 +1,76 @@ +#ifndef SLANG_CORE_NAME_VALUE_H +#define SLANG_CORE_NAME_VALUE_H + +#include "slang-basic.h" + +#include "slang-array-view.h" + +namespace Slang +{ + +// When associating values with names we use this type +typedef int32_t ValueInt; + +struct NameValue +{ + ValueInt value; + const char* name; +}; + +struct NamesValue +{ + ValueInt value; + const char* names; ///< Can hold comma delimited list of names + +}; + +struct NamesDescriptionValue +{ + ValueInt value; + const char* names; ///< Can hold comma delimited list of names + const char* description; ///< Optional description, can hold null ptr or empty string if not defined +}; + +struct NameValueUtil +{ + enum class NameKind + { + First, ///< Only the first name if there is more than one + All, ///< All valid associated names + }; + + /// Use the default type to infer the actual type desired + template <typename T> + static T findValue(const ConstArrayView<NameValue>& opts, const UnownedStringSlice& slice, T defaultValue) { return (T)findValue(opts, slice, ValueInt(defaultValue)); } + template <typename T> + static T findValue(const ConstArrayView<NamesValue>& opts, const UnownedStringSlice& slice, T defaultValue) { return (T)findValue(opts, slice, ValueInt(defaultValue)); } + template <typename T> + static T findValue(const ConstArrayView<NamesDescriptionValue>& opts, const UnownedStringSlice& slice, T defaultValue) { return (T)findValue(opts, slice, ValueInt(defaultValue)); } + + /// Given a slice finds the associated value. If no entry is found, defaultValue is returned + static ValueInt findValue(const ConstArrayView<NameValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue = -1); + static ValueInt findValue(const ConstArrayView<NamesValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue = -1); + static ValueInt findValue(const ConstArrayView<NamesDescriptionValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue = -1); + + /// Given a value find the name. If there are multiple names, returns the first name + static UnownedStringSlice findName(const ConstArrayView<NameValue>& opts, ValueInt value, const UnownedStringSlice& defaultName = UnownedStringSlice()); + static UnownedStringSlice findName(const ConstArrayView<NamesValue>& opts, ValueInt value, const UnownedStringSlice& defaultName = UnownedStringSlice()); + static UnownedStringSlice findName(const ConstArrayView<NamesDescriptionValue>& opts, ValueInt value, const UnownedStringSlice& defaultName = UnownedStringSlice()); + + /// Append all the names from opts to out + static void appendNames(NameKind kind, const ConstArrayView<NameValue>& opts, List<UnownedStringSlice>& out); + static void appendNames(NameKind kind, const ConstArrayView<NamesValue>& opts, List<UnownedStringSlice>& out); + static void appendNames(NameKind kind, const ConstArrayView<NamesDescriptionValue>& opts, List<UnownedStringSlice>& out); + + /// Return the list of all valid names + static List<UnownedStringSlice> getNames(NameKind kind, const ConstArrayView<NameValue>& opts); + static List<UnownedStringSlice> getNames(NameKind kind, const ConstArrayView<NamesValue>& opts); + static List<UnownedStringSlice> getNames(NameKind kind, const ConstArrayView<NamesDescriptionValue>& opts); + + /// Get the description + static UnownedStringSlice findDescription(const ConstArrayView<NamesDescriptionValue>& opts, ValueInt value, const UnownedStringSlice& defaultDescription = UnownedStringSlice()); +}; + +} // namespace Slang + +#endif diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp index 29e0dad5d..7de6846d5 100644 --- a/source/core/slang-string-util.cpp +++ b/source/core/slang-string-util.cpp @@ -35,10 +35,8 @@ namespace Slang { return areAllEqual(slicesA, slicesB, equalFn); } -/* static */void StringUtil::split(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& outSlices) +/* static */void StringUtil::appendSplit(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& outSlices) { - outSlices.clear(); - const char* start = in.begin(); const char* end = in.end(); @@ -59,17 +57,15 @@ namespace Slang { } } -/* static */void StringUtil::split(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& outSlices) +/* static */void StringUtil::appendSplit(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& outSlices) { const Index splitLen = splitSlice.getLength(); if (splitLen == 1) { - return split(in, splitSlice[0], outSlices); + return appendSplit(in, splitSlice[0], outSlices); } - outSlices.clear(); - SLANG_ASSERT(splitLen > 0); if (splitLen <= 0) { @@ -96,7 +92,7 @@ namespace Slang { cur++; } - + // Add to output outSlices.add(UnownedStringSlice(start, cur)); @@ -105,6 +101,18 @@ namespace Slang { } } +/* static */void StringUtil::split(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& outSlices) +{ + outSlices.clear(); + appendSplit(in, splitChar, outSlices); +} + +/* static */void StringUtil::split(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& outSlices) +{ + outSlices.clear(); + appendSplit(in, splitSlice, outSlices); +} + /* static */Index StringUtil::split(const UnownedStringSlice& in, char splitChar, Index maxSlices, UnownedStringSlice* outSlices) { Index index = 0; diff --git a/source/core/slang-string-util.h b/source/core/slang-string-util.h index b7576da1d..2293acf8d 100644 --- a/source/core/slang-string-util.h +++ b/source/core/slang-string-util.h @@ -33,6 +33,13 @@ struct StringUtil /// Splits into outSlices up to maxSlices. Returns SLANG_OK if of 'in' consumed. static SlangResult split(const UnownedStringSlice& in, char splitChar, Index maxSlices, UnownedStringSlice* outSlices, Index& outSlicesCount); + /// Split in, by specified splitChar append into slices out + /// Slices contents will directly address into in, so contents will only stay valid as long as in does. + static void appendSplit(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& slicesOut); + /// Split in by the specified splitSlice + /// Slices contents will directly address into in, so contents will only stay valid as long as in does. + static void appendSplit(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& slicesOut); + /// Append the joining of in items, separated by 'separator' onto out static void join(const List<String>& in, char separator, StringBuilder& out); static void join(const List<String>& in, const UnownedStringSlice& separator, StringBuilder& out); diff --git a/source/core/slang-type-text-util.cpp b/source/core/slang-type-text-util.cpp index 0d396195f..172426bba 100644 --- a/source/core/slang-type-text-util.cpp +++ b/source/core/slang-type-text-util.cpp @@ -7,6 +7,11 @@ namespace Slang { + + + +namespace { // anonymous + #define SLANG_SCALAR_TYPES(x) \ x(None, none) \ x(Void, void) \ @@ -19,28 +24,6 @@ namespace Slang x(Float32, float) \ x(Float64, double) -#define SLANG_PASS_THROUGH_TYPES(x) \ - x(none, NONE) \ - x(fxc, FXC) \ - x(dxc, DXC) \ - x(glslang, GLSLANG) \ - x(visualstudio, VISUAL_STUDIO) \ - x(clang, CLANG) \ - x(gcc, GCC) \ - x(genericcpp, GENERIC_C_CPP) \ - x(nvrtc, NVRTC) \ - x(llvm, LLVM) - -#define SLANG_DEBUG_INFO_FORMATS(x) \ - x(default-format, DEFAULT) \ - x(c7, C7) \ - x(pdb, PDB) \ - x(stabs, STABS) \ - x(coff, COFF) \ - x(dwarf, DWARF) - -namespace { // anonymous - struct ScalarTypeInfo { slang::TypeReflection::ScalarType type; @@ -54,105 +37,160 @@ static const ScalarTypeInfo s_scalarTypeInfos[] = SLANG_SCALAR_TYPES(SLANG_SCALAR_TYPE_INFO) }; -struct CompileTargetInfo -{ - SlangCompileTarget target; ///< The target - const char* extensions; ///< Comma delimited list of extensions associated with the target - const char* names; ///< Comma delimited list of names associated with the target. NOTE! First name is taken as the normal display name. -}; - // Make sure to keep this table in sync with that in slang/slang-options.cpp getHelpText -static const CompileTargetInfo s_compileTargetInfos[] = +static const TypeTextUtil::CompileTargetInfo s_compileTargetInfos[] = { { SLANG_TARGET_UNKNOWN, "", "unknown"}, { SLANG_TARGET_NONE, "", "none"}, - { SLANG_HLSL, "hlsl,fx", "hlsl"}, - { SLANG_DXBC, "dxbc", "dxbc"}, - { SLANG_DXBC_ASM, "dxbc-asm", "dxbc-asm,dxbc-assembly" }, - { SLANG_DXIL, "dxil", "dxil" }, - { SLANG_DXIL_ASM, "dxil-asm", "dxil-asm,dxil-assembly" }, - { SLANG_GLSL, "glsl,vert,frag,geom,tesc,tese,comp", "glsl" }, - { SLANG_GLSL_VULKAN, "", "glsl-vulkan" }, - { SLANG_GLSL_VULKAN_ONE_DESC, "", "glsl-vulkan-one-desc" }, - { SLANG_SPIRV, "spv", "spirv"}, - { SLANG_SPIRV_ASM, "spv-asm", "spirv-asm,spirv-assembly" }, - { SLANG_C_SOURCE, "c", "c" }, - { SLANG_CPP_SOURCE, "cpp,c++,cxx", "cpp,c++,cxx" }, - { SLANG_CPP_PYTORCH_BINDING, "cpp,c++,cxx", "torch,torch-binding,torch-cpp,torch-cpp-binding" }, - { SLANG_HOST_CPP_SOURCE, "cpp,c++,cxx", "host-cpp,host-c++,host-cxx"}, - { SLANG_HOST_EXECUTABLE,"exe", "exe,executable" }, - { SLANG_SHADER_SHARED_LIBRARY, "dll,so", "sharedlib,sharedlibrary,dll" }, - { SLANG_CUDA_SOURCE, "cu", "cuda,cu" }, - { SLANG_PTX, "ptx", "ptx" }, - { SLANG_CUDA_OBJECT_CODE, "obj,o", "cuobj,cubin" }, - { SLANG_SHADER_HOST_CALLABLE, "", "host-callable,callable" }, - { SLANG_OBJECT_CODE, "obj,o", "object-code" }, - { SLANG_HOST_HOST_CALLABLE, "", "host-host-callable" }, + { SLANG_HLSL, "hlsl,fx", "hlsl", "HLSL source code"}, + { SLANG_DXBC, "dxbc", "dxbc", "DirectX shader bytecode binary"}, + { SLANG_DXBC_ASM, "dxbc-asm", "dxbc-asm,dxbc-assembly", "DirectX shader bytecode assembly" }, + { SLANG_DXIL, "dxil", "dxil", "DirectX Intermediate Language binary" }, + { SLANG_DXIL_ASM, "dxil-asm", "dxil-asm,dxil-assembly", "DirectX Intermediate Language assembly"}, + { SLANG_GLSL, "glsl,vert,frag,geom,tesc,tese,comp", "glsl", "GLSL source code" }, + { SLANG_GLSL_VULKAN, "", "glsl-vulkan", "GLSL Vulkan source code" }, + { SLANG_GLSL_VULKAN_ONE_DESC, "", "glsl-vulkan-one-desc", "GLSL Vulkan source code" }, + { SLANG_SPIRV, "spv", "spirv", "SPIR-V binary"}, + { SLANG_SPIRV_ASM, "spv-asm", "spirv-asm,spirv-assembly", "SPIR-V assembly" }, + { SLANG_C_SOURCE, "c", "c", "C source code" }, + { SLANG_CPP_SOURCE, "cpp,c++,cxx", "cpp,c++,cxx", "C++ source code" }, + { SLANG_CPP_PYTORCH_BINDING, "cpp,c++,cxx", "torch,torch-binding,torch-cpp,torch-cpp-binding", "C++ for pytorch binding" }, + { SLANG_HOST_CPP_SOURCE, "cpp,c++,cxx", "host-cpp,host-c++,host-cxx", "C++ source for host execution"}, + { SLANG_HOST_EXECUTABLE,"exe", "exe,executable", "Executable binary" }, + { SLANG_SHADER_SHARED_LIBRARY, "dll,so", "sharedlib,sharedlibrary,dll", "Shared library/Dll" }, + { SLANG_CUDA_SOURCE, "cu", "cuda,cu", "CUDA source code" }, + { SLANG_PTX, "ptx", "ptx", "PTX assembly" }, + { SLANG_CUDA_OBJECT_CODE, "obj,o", "cuobj,cubin", "CUDA binary" }, + { SLANG_SHADER_HOST_CALLABLE, "", "host-callable,callable", "Host callable" }, + { SLANG_OBJECT_CODE, "obj,o", "object-code", "Object code" }, + { SLANG_HOST_HOST_CALLABLE, "", "host-host-callable", "Host callable for host execution" }, +}; + +static const NamesValue s_languageInfos[] = +{ + { SLANG_SOURCE_LANGUAGE_C, "c,C" }, + { SLANG_SOURCE_LANGUAGE_CPP, "cpp,c++,C++,cxx" }, + { SLANG_SOURCE_LANGUAGE_SLANG, "slang" }, + { SLANG_SOURCE_LANGUAGE_GLSL, "glsl" }, + { SLANG_SOURCE_LANGUAGE_HLSL, "hlsl" }, + { SLANG_SOURCE_LANGUAGE_CUDA, "cu,cuda" }, +}; + +static const NamesDescriptionValue s_compilerInfos[] = +{ + { SLANG_PASS_THROUGH_NONE, "none", "Unknown" }, + { SLANG_PASS_THROUGH_FXC, "fxc", "FXC HLSL compiler" }, + { SLANG_PASS_THROUGH_DXC, "dxc", "DXC HLSL compiler" }, + { SLANG_PASS_THROUGH_GLSLANG, "glslang", "GLSLANG GLSL compiler" }, + { SLANG_PASS_THROUGH_VISUAL_STUDIO, "visualstudio,vs", "Visual Studio C/C++ compiler" }, + { SLANG_PASS_THROUGH_CLANG, "clang", "Clang C/C++ compiler" }, + { SLANG_PASS_THROUGH_GCC, "gcc", "GCC C/C++ compiler" }, + { SLANG_PASS_THROUGH_GENERIC_C_CPP, "genericcpp,c,cpp", "A generic C++ compiler (can be any one of visual studio, clang or gcc depending on system and availability)" }, + { SLANG_PASS_THROUGH_NVRTC, "nvrtc", "NVRTC CUDA compiler" }, + { SLANG_PASS_THROUGH_LLVM, "llvm", "LLVM/Clang `slang-llvm`" }, +}; +static const NameValue s_archiveTypeInfos[] = +{ + { SLANG_ARCHIVE_TYPE_RIFF_DEFLATE, "riff-deflate"}, + { SLANG_ARCHIVE_TYPE_RIFF_LZ4, "riff-lz4"}, + { SLANG_ARCHIVE_TYPE_ZIP, "zip"}, + { SLANG_ARCHIVE_TYPE_RIFF, "riff"}, }; -struct ArchiveTypeInfo + +static const NamesDescriptionValue s_debugInfoFormatInfos[] = { - SlangArchiveType type; - UnownedStringSlice text; + { SLANG_DEBUG_INFO_FORMAT_DEFAULT, "default-format", "Use the default debugging format for the target" }, + { SLANG_DEBUG_INFO_FORMAT_C7, "c7", "CodeView C7 format (typically means debugging infomation is embedded in the binary)" }, + { SLANG_DEBUG_INFO_FORMAT_PDB, "pdb", "Program database" }, + { SLANG_DEBUG_INFO_FORMAT_STABS, "stabs", "STABS debug format" }, + { SLANG_DEBUG_INFO_FORMAT_COFF, "coff", "COFF debug format" }, + { SLANG_DEBUG_INFO_FORMAT_DWARF, "dwarf", "DWARF debug format"}, }; -static const ArchiveTypeInfo s_archiveTypeInfos[] = +static const NamesDescriptionValue s_lineDirectiveInfos[] = { - { SLANG_ARCHIVE_TYPE_RIFF_DEFLATE, UnownedStringSlice::fromLiteral("riff-deflate")}, - { SLANG_ARCHIVE_TYPE_RIFF_LZ4, UnownedStringSlice::fromLiteral("riff-lz4")}, - { SLANG_ARCHIVE_TYPE_ZIP, UnownedStringSlice::fromLiteral("zip")}, - { SLANG_ARCHIVE_TYPE_RIFF, UnownedStringSlice::fromLiteral("riff")}, + { SLANG_LINE_DIRECTIVE_MODE_NONE, "none", "Don't emit `#line` directives at all"}, + { SLANG_LINE_DIRECTIVE_MODE_SOURCE_MAP, "source-map", "Use source map to track line associations (doen't emit #line)"}, + { SLANG_LINE_DIRECTIVE_MODE_DEFAULT, "default", "Default behavior"}, + { SLANG_LINE_DIRECTIVE_MODE_STANDARD, "standard", "Emit standard C-style `#line` directives." }, + { SLANG_LINE_DIRECTIVE_MODE_GLSL, "glsl", "Emit GLSL-style directives with file *number* instead of name." }, +}; + +static const NamesDescriptionValue s_floatingPointModes[] = +{ + { SLANG_FLOATING_POINT_MODE_PRECISE, "precise", + "Disable optimization that could change the output of floating-" + "point computations, including around infinities, NaNs, denormalized " + "values, and negative zero. Prefer the most precise versions of special " + "functions supported by the target."}, + { SLANG_FLOATING_POINT_MODE_FAST,"fast", + "Allow optimizations that may change results of floating-point " + "computations. Prefer the fastest version of special functions supported " + "by the target."}, + { SLANG_FLOATING_POINT_MODE_DEFAULT, "default", + "Default floating point mode" } }; } // anonymous -/* static */SlangArchiveType TypeTextUtil::findArchiveType(const UnownedStringSlice& slice) +/* static */ConstArrayView<TypeTextUtil::CompileTargetInfo> TypeTextUtil::getCompileTargetInfos() { - for (const auto& entry : s_archiveTypeInfos) - { - if (slice == entry.text) - { - return entry.type; - } - } - return SLANG_ARCHIVE_TYPE_UNDEFINED; + return makeConstArrayView(s_compileTargetInfos); } -struct DebugInfoFormatTable +/* static */ConstArrayView<NamesValue> TypeTextUtil::getLanguageInfos() { - UnownedStringSlice entries[SLANG_DEBUG_INFO_FORMAT_COUNT_OF]; + return makeConstArrayView(s_languageInfos); +} - static DebugInfoFormatTable _makeTable() - { - DebugInfoFormatTable dst; -#define SLANG_DEBUG_INFO_FORMAT_ENTRY(name, value) \ - dst.entries[SLANG_DEBUG_INFO_FORMAT_##value] = toSlice(#name); - SLANG_DEBUG_INFO_FORMATS(SLANG_DEBUG_INFO_FORMAT_ENTRY) - return dst; - } +/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getCompilerInfos() +{ + return makeConstArrayView(s_compilerInfos); +} - static Index findIndex(const UnownedStringSlice slice) { return makeConstArrayView(table.entries).indexOf(slice); } - static UnownedStringSlice getSlice(SlangDebugInfoFormat format) { return table.entries[Index(format)]; } +/* static */ConstArrayView<NameValue> TypeTextUtil::getArchiveTypeInfos() +{ + return makeConstArrayView(s_archiveTypeInfos); +} - static const DebugInfoFormatTable table; -}; +/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getDebugInfoFormatInfos() +{ + return makeConstArrayView(s_debugInfoFormatInfos); +} -/* static */const DebugInfoFormatTable DebugInfoFormatTable::table = DebugInfoFormatTable::_makeTable(); +/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getLineDirectiveInfos() +{ + return makeConstArrayView(s_lineDirectiveInfos); +} + +/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getFloatingPointModeInfos() +{ + return makeConstArrayView(s_floatingPointModes); +} + +/* static */SlangArchiveType TypeTextUtil::findArchiveType(const UnownedStringSlice& slice) +{ + return NameValueUtil::findValue(getArchiveTypeInfos(), slice, SLANG_ARCHIVE_TYPE_UNDEFINED); +} /* static */SlangResult TypeTextUtil::findDebugInfoFormat(const Slang::UnownedStringSlice& text, SlangDebugInfoFormat& out) { - const auto index = DebugInfoFormatTable::findIndex(text); - if (index >= 0) + const ValueInt value = NameValueUtil::findValue(getDebugInfoFormatInfos(), text, -1); + if (value >= 0) { - out = SlangDebugInfoFormat(index); + out = SlangDebugInfoFormat(value); return SLANG_OK; } return SLANG_FAIL; } -/* static */UnownedStringSlice TypeTextUtil::getDebugInfoFormatName(SlangDebugInfoFormat format) { return DebugInfoFormatTable::getSlice(format); } +/* static */UnownedStringSlice TypeTextUtil::getDebugInfoFormatName(SlangDebugInfoFormat format) +{ + return NameValueUtil::findName(getDebugInfoFormatInfos(), format, toSlice("unknown")); +} /* static */UnownedStringSlice TypeTextUtil::getScalarTypeName(slang::TypeReflection::ScalarType scalarType) { @@ -180,82 +218,20 @@ struct DebugInfoFormatTable return slang::TypeReflection::ScalarType::None; } -#define SLANG_PASS_THROUGH_HUMAN_TEXT(x) \ - x(NONE, "Unknown") \ - x(VISUAL_STUDIO, "Visual Studio") \ - x(GCC, "GCC") \ - x(CLANG, "Clang") \ - x(NVRTC, "NVRTC") \ - x(FXC, "fxc") \ - x(DXC, "dxc") \ - x(GLSLANG, "glslang") \ - x(LLVM, "LLVM/Clang") /* static */UnownedStringSlice TypeTextUtil::getPassThroughAsHumanText(SlangPassThrough type) { -#define SLANG_PASS_THROUGH_HUMAN_CASE(value, text) case SLANG_PASS_THROUGH_##value: return UnownedStringSlice::fromLiteral(text); - - switch (type) - { - default: [[fallthrough]]; /* fall-through to none */ - SLANG_PASS_THROUGH_HUMAN_TEXT(SLANG_PASS_THROUGH_HUMAN_CASE) - } -} - -/* static */SlangResult TypeTextUtil::findPassThroughFromHumanText(const UnownedStringSlice& inText, SlangPassThrough& outPassThrough) -{ - #define SLANG_PASS_THROUGH_HUMAN_IF(value, text) if (inText == UnownedStringSlice::fromLiteral(text)) { outPassThrough = SLANG_PASS_THROUGH_##value; return SLANG_OK; } else - SLANG_PASS_THROUGH_HUMAN_TEXT(SLANG_PASS_THROUGH_HUMAN_IF) - return SLANG_FAIL; + return NameValueUtil::findName(getCompilerInfos(), type, toSlice("unknown")); } /* static */SlangSourceLanguage TypeTextUtil::findSourceLanguage(const UnownedStringSlice& text) { - if (text == "c" || text == "C") - { - return SLANG_SOURCE_LANGUAGE_C; - } - else if (text == "cpp" || text == "c++" || text == "C++" || text == "cxx") - { - return SLANG_SOURCE_LANGUAGE_CPP; - } - else if (text == "slang") - { - return SLANG_SOURCE_LANGUAGE_SLANG; - } - else if (text == "glsl") - { - return SLANG_SOURCE_LANGUAGE_GLSL; - } - else if (text == "hlsl") - { - return SLANG_SOURCE_LANGUAGE_HLSL; - } - else if (text == "cu" || text == "cuda") - { - return SLANG_SOURCE_LANGUAGE_CUDA; - } - return SLANG_SOURCE_LANGUAGE_UNKNOWN; + return NameValueUtil::findValue(getLanguageInfos(), text, SLANG_SOURCE_LANGUAGE_UNKNOWN); } /* static */SlangPassThrough TypeTextUtil::findPassThrough(const UnownedStringSlice& slice) { -#define SLANG_PASS_THROUGH_NAME_TO_TYPE(x, y) \ - if (slice == UnownedStringSlice::fromLiteral(#x)) return SLANG_PASS_THROUGH_##y; - - SLANG_PASS_THROUGH_TYPES(SLANG_PASS_THROUGH_NAME_TO_TYPE) - - // Other options - if (slice == "c" || slice == "cpp") - { - return SLANG_PASS_THROUGH_GENERIC_C_CPP; - } - else if (slice == "vs") - { - return SLANG_PASS_THROUGH_VISUAL_STUDIO; - } - - return SLANG_PASS_THROUGH_NONE; + return NameValueUtil::findValue(getCompilerInfos(), slice, SLANG_PASS_THROUGH_NONE); } /* static */SlangResult TypeTextUtil::findPassThrough(const UnownedStringSlice& slice, SlangPassThrough& outPassThrough) @@ -271,15 +247,7 @@ struct DebugInfoFormatTable /* static */UnownedStringSlice TypeTextUtil::getPassThroughName(SlangPassThrough passThru) { -#define SLANG_PASS_THROUGH_TYPE_TO_NAME(x, y) \ - case SLANG_PASS_THROUGH_##y: return UnownedStringSlice::fromLiteral(#x); - - switch (passThru) - { - SLANG_PASS_THROUGH_TYPES(SLANG_PASS_THROUGH_TYPE_TO_NAME) - default: break; - } - return UnownedStringSlice::fromLiteral("unknown"); + return NameValueUtil::findName(getCompilerInfos(), passThru, toSlice("unknown")); } /* static */SlangCompileTarget TypeTextUtil::findCompileTargetFromExtension(const UnownedStringSlice& slice) diff --git a/source/core/slang-type-text-util.h b/source/core/slang-type-text-util.h index df9b83013..2249b1215 100644 --- a/source/core/slang-type-text-util.h +++ b/source/core/slang-type-text-util.h @@ -4,7 +4,8 @@ #include "../../slang.h" #include "slang-string.h" - +#include "slang-array-view.h" +#include "slang-name-value.h" namespace Slang { @@ -12,6 +13,30 @@ namespace Slang /// Utility class to allow conversion of types (such as enums) to and from text types struct TypeTextUtil { + struct CompileTargetInfo + { + SlangCompileTarget target; ///< The target + const char* extensions; ///< Comma delimited list of extensions associated with the target + const char* names; ///< Comma delimited list of names associated with the target. NOTE! First name is taken as the normal display name. + const char* description; ///< Description, can be null + }; + + /// Get the compile target infos + static ConstArrayView<CompileTargetInfo> getCompileTargetInfos(); + + /// Get the language infos + static ConstArrayView<NamesValue> getLanguageInfos(); + /// Get the compiler infos + static ConstArrayView<NamesDescriptionValue> getCompilerInfos(); + /// Get the archive type infos + static ConstArrayView<NameValue> getArchiveTypeInfos(); + /// Get the debug format types + static ConstArrayView<NamesDescriptionValue> getDebugInfoFormatInfos(); + + static ConstArrayView<NamesDescriptionValue> getFloatingPointModeInfos(); + + static ConstArrayView<NamesDescriptionValue> getLineDirectiveInfos(); + /// Get the scalar type as text. static Slang::UnownedStringSlice getScalarTypeName(slang::TypeReflection::ScalarType scalarType); @@ -26,9 +51,7 @@ struct TypeTextUtil /// As human readable text static UnownedStringSlice getPassThroughAsHumanText(SlangPassThrough type); - /// Gets pass through from human text (as from getPassThroughAsHumanText) - static SlangResult findPassThroughFromHumanText(const UnownedStringSlice& text, SlangPassThrough& outPassThrough); - + /// Given a source language name returns a source language. Name here is distinct from extension static SlangSourceLanguage findSourceLanguage(const UnownedStringSlice& text); |
