summaryrefslogtreecommitdiff
path: root/source/compiler-core/slang-json-parser.h
blob: 2e907abb2bf2fbdbf997d8b45cc37ece9293cab9 (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
// slang-json-parser.h
#ifndef SLANG_JSON_PARSER_H
#define SLANG_JSON_PARSER_H

#include "slang-json-lexer.h"


namespace Slang {

class JSONListener
{
public:
        /// Start an object
    virtual void startObject() = 0;
        /// End an object
    virtual void endObject() = 0;
        /// Start an array
    virtual void startArray() = 0;
        /// End and array
    virtual void endArray() = 0;

        /// Add the key lexeme. Must be followed by addLexemeValue.
    virtual void addLexemeKey(const UnownedStringSlice& key) = 0;
        /// Can be performed in an array or after an addLexemeKey in an object
    virtual void addLexemeValue(JSONTokenType type, const UnownedStringSlice& value) = 0; 
};

class JSONWriter : public JSONListener
{
public:
    /* 
    https://en.wikipedia.org/wiki/Indentation_style
    */
    enum class IndentationStyle
    {
        Allman,           ///< After every value, and opening, closing all other types
        KNR,              ///< K&R like. Fields have CR.
    };

    enum class LocationType : uint8_t 
    {
        Object,
        Array,
        Comma,
    };

    // NOTE! Order must be kept the same without fixing is functions below
    enum class Location
    {
        BeforeOpenObject,
        BeforeCloseObject,
        AfterOpenObject,
        AfterCloseObject,

        BeforeOpenArray,
        BeforeCloseArray,
        AfterOpenArray,
        AfterCloseArray,

        FieldComma,
        Comma,

        CountOf,
    };

    static LocationType getLocationType(Location loc) { return isObject(loc) ? LocationType::Object : (isComma(loc) ? LocationType::Comma : LocationType::Array); }

    static bool isObjectLike(Location loc) { return Index(loc) <= Index(Location::AfterCloseArray); }
    static bool isObject(Location loc) { return Index(loc) <= Index(Location::AfterCloseObject); }
    static bool isArray(Location loc) { return Index(loc) >= Index(Location::BeforeOpenArray) && Index(loc) <= Index(Location::AfterCloseArray); }
    static bool isComma(Location loc) { return Index(loc) >= Index(Location::FieldComma); }
    static bool isOpen(Location loc) { return isObjectLike(loc) && (Index(loc) & 1) == 0; }
    static bool isClose(Location loc) { return isObjectLike(loc) && (Index(loc) & 1) != 0; }
    static bool isBefore(Location loc) { return isObjectLike(loc) && (Index(loc) & 2) == 0; }
    static bool isAfter(Location loc) { return isObjectLike(loc) && (Index(loc) & 2) != 0; }

    // Implement JSONListener
    virtual void startObject() SLANG_OVERRIDE;
    virtual void endObject() SLANG_OVERRIDE;
    virtual void startArray() SLANG_OVERRIDE;
    virtual void endArray() SLANG_OVERRIDE;
    virtual void addLexemeKey(const UnownedStringSlice& key) SLANG_OVERRIDE;
    virtual void addLexemeValue(JSONTokenType type, const UnownedStringSlice& value) SLANG_OVERRIDE;

        /// Get the builder
    StringBuilder& getBuilder() { return m_builder; }

    JSONWriter(IndentationStyle format, Index lineLengthLimit = -1)
    {
        m_format = format;
        m_lineLengthLimit = lineLengthLimit;

        m_state.m_kind = State::Kind::Root;
        m_state.m_flags = 0;
    }

protected:
    struct State
    {
        enum class Kind : uint8_t
        {
            Root,
            Object,
            Array,
        };

        typedef uint8_t Flags;
        struct Flag
        {
            enum Enum : Flags
            {
                HasPrevious = 0x01,
                HasKey      = 0x02,
            };
        };

        bool canEmitValue() const
        {
            switch (m_kind)
            {
                case Kind::Root:    return (m_flags & Flag::HasPrevious) == 0;
                case Kind::Array:   return true;
                case Kind::Object:  return (m_flags & Flag::HasKey) != 0;
                default:            return false;
            }
        }

        Kind m_kind;
        Flags m_flags;
    };

    void _maybeNextLine();
    void _nextLine();
    void _handleFormat(Location loc);

    Index _getLineLengthAfterIndent();

        /// Only emits the indent if at start of line
    void _maybeEmitIndent();
    void _emitIndent();

    void _maybeEmitComma();
    void _maybeEmitFieldComma();

    void _indent() { m_currentIndent++; }
    void _dedent() { --m_currentIndent; SLANG_ASSERT(m_currentIndent >= 0); }

        /// True if the line is indented at the required level
    bool _hasIndent() { return m_emittedIndent >= 0 && m_emittedIndent == m_currentIndent; }
    
    Index m_currentIndent = 0;
    char m_indentChar = ' ';
    Index m_indentCharCount = 4;

    Index m_lineIndex = 0;
    Index m_lineStart = 0;
    Index m_emittedIndent = -1;             /// If -1 for current line there is no indent emitted

    Index m_lineLengthLimit = -1;           /// The limit is only applied *AFTER* indentation                 

    IndentationStyle m_format;

    StringBuilder m_builder;
    List<State> m_stack;
    State m_state;
};

class JSONParser
{
public:
    SlangResult parse(JSONLexer* lexer, SourceView* sourceView, JSONListener* listener, DiagnosticSink* sink);

protected:
    SlangResult _parseValue();
    SlangResult _parseObject();
    SlangResult _parseArray();

    SourceView* m_sourceView;
    DiagnosticSink* m_sink;
    JSONListener* m_listener;
    JSONLexer* m_lexer;
};



} // namespace Slang

#endif