summaryrefslogtreecommitdiffstats
path: root/source/core/slang-command-options.h
blob: 9d7af44d9459bcdc6aaff9b0569f96768fecf992 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
#ifndef SLANG_CORE_COMMAND_OPTIONS_H
#define SLANG_CORE_COMMAND_OPTIONS_H

#include "slang-basic.h"
#include "slang-name-value.h"
#include "slang-string-slice-pool.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
    };

    /// 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
    };

    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
    };

    /// Get the first name
    UnownedStringSlice getFirstNameForOption(Index optionIndex);
    /// Get the first name for the category
    UnownedStringSlice getFirstNameForCategory(Index categoryIndex);

    /// Get a name key for an opton
    NameKey getNameKeyForOption(Index optionIndex);
    /// Get a name key for a category
    NameKey getNameKeyForCategory(Index optionIndex);

    /// 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,
        NameKey* outNameKey = nullptr) 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
    {
        return findTargetIndexByName(LookupKind::Option, name);
    }
    /// 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;

    /// Splits usage into category slices
    void splitUsage(const UnownedStringSlice& usageSlice, List<UnownedStringSlice>& outSlices)
        const;

    /// Get all the option names associated with a category index
    void getCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const;
    void appendCategoryOptionNames(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)
    {
    }

protected:
    /// 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);

    Index _findTargetIndexByName(
        LookupKind kind,
        const UnownedStringSlice& name,
        NameKey* outNameKey) const;

    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
};

} // namespace Slang

#endif