summaryrefslogtreecommitdiffstats
path: root/source/compiler-core/slang-command-line-args.h
blob: 388847f5c9c752882cd618be4020d8631a17a4de (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
#ifndef SLANG_COMMAND_LINE_ARGS_H
#define SLANG_COMMAND_LINE_ARGS_H

// This file defines the `Name` type, used to represent
// the name of types, variables, etc. in the AST.

#include "../core/slang-basic.h"
#include "slang-diagnostic-sink.h"
#include "slang-source-loc.h"

namespace Slang
{

struct CommandLineArg
{
    String value;  ///< The value of the arg
    SourceLoc loc; ///< The location of the arg
};

/* This type ends up being really just a container for the sourceManager that has the CommandLine
specific SourceLocs. That it would perhaps be better to just have SourceManager derive from
RefObject, and then we could remove this type. */
class CommandLineContext : public RefObject
{
public:
    /// Get the source manager
    SourceManager* getSourceManager() { return &m_sourceManager; }

    CommandLineContext(ISlangFileSystemExt* fileSystemExt = nullptr)
    {
        m_sourceManager.initialize(nullptr, fileSystemExt);
        // Make range start from high value, so can be differentiated from other uses
        // That this doesn't not assume exclusive use of this range - just that in normal use
        // scenarios there is no confusion, and using the wrong source manager, will typically
        // report nothing is found.
        m_sourceManager.allocateSourceRange(~(~SourceLoc::RawValue(0) >> 1));
    }

protected:
    SourceManager m_sourceManager;
};

struct CommandLineArgs
{
    typedef CommandLineArg Arg;

    SLANG_FORCE_INLINE Index getArgCount() const { return m_args.getCount(); }
    const Arg& operator[](Index i) const { return m_args[i]; }

    const Arg* begin() const { return m_args.begin(); }
    const Arg* end() const { return m_args.end(); }

    /// NOTE! Should NOT include the executable name
    void setArgs(const char* const* args, size_t argCount);

    /// True if has args in same order
    bool hasArgs(const char* const* args, Index count) const;

    /// Add an arg
    void add(const Arg& arg) { m_args.add(arg); }

    /// Ctor with a context
    CommandLineArgs(CommandLineContext* context)
        : m_context(context)
    {
    }
    /// Default Ctor
    CommandLineArgs() {}

    // String m_executablePath;                ///< Can be optionally be set
    List<Arg> m_args;                     ///< The args
    RefPtr<CommandLineContext> m_context; ///< The context, which mainly has source manager

    String serialize();
    void deserialize(String content);
};

struct CommandLineReader
{
    /// Peek the current location
    SourceLoc peekLoc() const
    {
        return m_index < m_args->getArgCount() ? (*m_args)[m_index].loc : SourceLoc();
    }
    /// Peek the current arg
    const CommandLineArg& peekArg() const
    {
        SLANG_ASSERT(hasArg());
        return (*m_args)[m_index];
    }

    /// Peek the string value at that position
    const String& peekValue() const
    {
        SLANG_ASSERT(hasArg());
        return (*m_args)[m_index].value;
    }

    /// Get the arg and advance
    CommandLineArg getArgAndAdvance()
    {
        CommandLineArg arg(peekArg());
        advance();
        return arg;
    }

    const String& getValueAndAdvance()
    {
        const String& value = peekValue();
        advance();
        return value;
    }

    /// True if at end
    bool atEnd() const { return m_index >= m_args->getArgCount(); }
    /// True if has a current arg
    bool hasArg() const { return !atEnd(); }

    /// Advance to next arg
    void advance()
    {
        SLANG_ASSERT(m_index < m_args->getArgCount());
        m_index++;
    }
    /// Removes arg at current position
    void removeArg()
    {
        SLANG_ASSERT(hasArg());
        m_args->m_args.removeAt(m_index);
    }

    /// Get the value from the arg previous to the current position. Will assert if there isn't one.
    String getPreviousValue() const;

    /// If there is an arg outArg is set and advanced
    /// Note, this *assumes* the previous arg is the option that initated this
    SlangResult expectArg(String& outArg);
    SlangResult expectArg(CommandLineArg& outArg);

    /// Get the current index
    Index getIndex() const { return m_index; }
    /// Set the current index
    void setIndex(Index index)
    {
        SLANG_ASSERT(index >= 0 && index <= m_args->getArgCount());
        m_index = index;
    }

    void init(CommandLineArgs* args, DiagnosticSink* sink)
    {
        m_args = args;
        m_sink = sink;
        m_index = 0;
    }

    /// Set up reader with args
    CommandLineReader(CommandLineArgs* args, DiagnosticSink* sink) { init(args, sink); }
    CommandLineReader() = default;

    DiagnosticSink* m_sink = nullptr;
    CommandLineArgs* m_args = nullptr;
    Index m_index = 0;
};

struct DownstreamArgs
{
    typedef uint32_t Flags;
    struct Flag
    {
        enum Enum : Flags
        {
            AllowNewNames = 0x01,
        };
    };

    struct Entry
    {
        String name;          ///< The name of the 'tool' that these args are associated with
        CommandLineArgs args; ///< The args to be passed to the tool
    };

    /// Add a name, returns the index
    Index addName(const String& name);
    /// Find the index of a name. Returns < 0 if not found.
    Index findName(const String& name) const
    {
        return m_entries.findFirstIndex(
            [&](const Entry& entry) -> bool { return entry.name == name; });
    }

    /// Get the args at the nameIndex
    CommandLineArgs& getArgsAt(Index nameIndex) { return m_entries[nameIndex].args; }
    /// Get args by name - will assert if name isn't found
    CommandLineArgs& getArgsByName(const char* name);
    const CommandLineArgs& getArgsByName(const char* name) const;

    /// Looks for '-X' expressions, removing them from ioArgs and putting in appropriate args
    SlangResult stripDownstreamArgs(CommandLineArgs& ioArgs, Flags flags, DiagnosticSink* sink);

    /// Get the context used
    CommandLineContext* getContext() const { return m_context; }

    /// Ctor
    DownstreamArgs(CommandLineContext* context);

    /// Default ctor - for convenience, should really use with context normally
    DownstreamArgs() {}

    List<Entry> m_entries; ///< All of the entries

protected:
    Index _findOrAddName(
        SourceLoc loc,
        const UnownedStringSlice& name,
        Flags flags,
        DiagnosticSink* sink);

    RefPtr<CommandLineContext> m_context; ///< The context that is being used (primarily for loc
                                          ///< tracking) across all entries/args
};


} // namespace Slang

#endif