summaryrefslogtreecommitdiffstats
path: root/source/core
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2023-04-29 09:24:26 -0400
committerGitHub <noreply@github.com>2023-04-29 09:24:26 -0400
commit19c0866b050a022406867aa650302f4efbf8e010 (patch)
treef5ed4e1f5d27865518daf81c7e861b4908186b23 /source/core
parentc571bcb025009f9c662e8d631fa49dbfed560287 (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.cpp842
-rw-r--r--source/core/slang-command-options.h253
-rw-r--r--source/core/slang-name-value.cpp163
-rw-r--r--source/core/slang-name-value.h76
-rw-r--r--source/core/slang-string-util.cpp24
-rw-r--r--source/core/slang-string-util.h7
-rw-r--r--source/core/slang-type-text-util.cpp292
-rw-r--r--source/core/slang-type-text-util.h31
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);