1/**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef ES2PANDA_IR_AST_DUMP_H
17#define ES2PANDA_IR_AST_DUMP_H
18
19#include "ir/astNode.h"
20#include "lexer/token/sourceLocation.h"
21#include "lexer/token/tokenType.h"
22#include "lexer/token/number.h"
23#include "util/ustring.h"
24
25#include <sstream>
26#include <variant>
27
28namespace ark::es2panda::ir {
29class AstDumper {
30public:
31    class Nullish {
32    public:
33        explicit Nullish(const ir::AstNode *node) : node_(node) {}
34
35        const ir::AstNode *Node() const
36        {
37            return node_;
38        }
39
40    private:
41        const ir::AstNode *node_;
42    };
43
44    class Optional {
45    public:
46        using Val = std::variant<const char *, const AstNode *, bool, std::vector<const AstNode *>>;
47        explicit Optional(const ir::AstNode *node) : value_(node) {}
48        explicit Optional(const char *string) : value_(const_cast<char *>(string)) {}
49        explicit Optional(bool boolean) : value_(boolean) {}
50
51        template <typename T>
52        explicit Optional(const ArenaVector<T> &array)
53        {
54            std::vector<const AstNode *> nodes;
55            nodes.reserve(array.size());
56
57            for (auto &it : array) {
58                nodes.push_back(it);
59            }
60
61            value_ = std::move(nodes);
62        }
63
64        const Val &Value() const
65        {
66            return value_;
67        }
68
69    private:
70        Val value_;
71    };
72
73    class Property {
74    public:
75        class Ignore {
76        public:
77            Ignore() = default;
78        };
79
80        enum class Constant {
81            PROP_NULL,
82            PROP_UNDEFINED,
83            EMPTY_ARRAY,
84        };
85
86        using Val = std::variant<const char *, lexer::TokenType, std::initializer_list<Property>, util::StringView,
87                                 bool, char16_t, lexer::Number, const ir::AstNode *, std::vector<const ir::AstNode *>,
88                                 Constant, Nullish, Ignore>;
89
90        Property(const char *key, const char *string) : key_(key), value_(string) {}
91        Property(const char *key, util::StringView str) : key_(key), value_(str) {}
92        Property(const char *key, bool boolean) : key_(key), value_(boolean) {}
93        Property(const char *key, char16_t c16) : key_(key), value_(c16) {}
94        Property(const char *key, lexer::Number number) : key_(key), value_(number) {}
95        Property(const char *key, lexer::TokenType token) : key_(key), value_(token) {}
96        Property(const char *key, std::initializer_list<Property> props) : key_(key), value_(props) {}
97        Property(const char *key, const ir::AstNode *node) : key_(key), value_(const_cast<ir::AstNode *>(node)) {}
98
99        Property(const char *key, Constant constant) : key_(key), value_(constant) {}
100        Property(const char *key, Nullish nullish) : key_(key)
101        {
102            if (nullish.Node() != nullptr) {
103                value_ = nullish.Node();
104            } else {
105                value_ = Property::Constant::PROP_NULL;
106            }
107        }
108
109        Property(const char *key, const Optional &optional) : key_(key)
110        {
111            const auto &value = optional.Value();
112            if (std::holds_alternative<const ir::AstNode *>(value) &&
113                (std::get<const ir::AstNode *>(value) != nullptr)) {
114                value_ = std::get<const ir::AstNode *>(value);
115                return;
116            }
117
118            if (std::holds_alternative<const char *>(value) && (std::get<const char *>(value) != nullptr)) {
119                value_ = std::get<const char *>(value);
120                return;
121            }
122
123            if (std::holds_alternative<bool>(value) && std::get<bool>(value)) {
124                value_ = std::get<bool>(value);
125                return;
126            }
127
128            if (std::holds_alternative<std::vector<const AstNode *>>(value)) {
129                const auto &array = std::get<std::vector<const AstNode *>>(value);
130                if (!array.empty()) {
131                    value_ = array;
132                    return;
133                }
134            }
135
136            value_ = Ignore();
137        }
138
139        template <typename T>
140        Property(const char *key, const ArenaVector<T> &array) : key_(key)
141        {
142            if (array.empty()) {
143                value_ = Constant::EMPTY_ARRAY;
144                return;
145            }
146
147            std::vector<const ir::AstNode *> nodes;
148            nodes.reserve(array.size());
149
150            for (auto &it : array) {
151                nodes.push_back(it);
152            }
153
154            value_ = std::move(nodes);
155        }
156
157        template <typename T>
158        Property(const char *key, const ArenaVector<T> &array, const std::function<bool(AstNode *)> &filter) : key_(key)
159        {
160            std::vector<const ir::AstNode *> nodes;
161            nodes.reserve(array.size());
162
163            for (auto &it : array) {
164                if (filter(it)) {
165                    nodes.push_back(it);
166                }
167            }
168
169            if (nodes.empty()) {
170                value_ = Constant::EMPTY_ARRAY;
171                return;
172            }
173
174            value_ = std::move(nodes);
175        }
176
177        const char *Key() const
178        {
179            return key_;
180        }
181
182        const Val &Value() const
183        {
184            return value_;
185        }
186
187    private:
188        const char *key_;
189        Val value_ {};
190    };
191
192    explicit AstDumper(const ir::AstNode *node, util::StringView sourceCode = "");
193
194    void Add(std::initializer_list<Property> props);
195    void Add(const AstDumper::Property &prop);
196
197    static const char *ModifierToString(ModifierFlags flags);
198    static const char *TypeOperatorToString(TSOperatorType operatorType);
199
200    std::string Str() const
201    {
202        return ss_.str();
203    }
204
205private:
206    using WrapperCb = std::function<void()>;
207
208    template <typename T>
209    void AddList(T props)
210    {
211        for (auto it = props.begin(); it != props.end();) {
212            Serialize(*it);
213
214            do {
215                if (++it == props.end()) {
216                    return;
217                }
218            } while (std::holds_alternative<Property::Ignore>((*it).Value()));
219
220            ss_ << ',';
221        }
222    }
223
224    void Indent();
225
226    void Serialize(const AstDumper::Property &prop);
227    void SerializePropKey(const char *str);
228    void SerializeString(const char *str);
229    void SerializeString(const util::StringView &str);
230    void SerializeNumber(size_t number);
231    void SerializeNumber(lexer::Number number);
232    void SerializeChar16(char16_t c16);
233    void SerializeBoolean(bool boolean);
234    void SerializeObject(const ir::AstNode *object);
235    void SerializeToken(lexer::TokenType token);
236    void SerializePropList(std::initializer_list<AstDumper::Property> props);
237    void SerializeConstant(Property::Constant constant);
238    void Wrap(const WrapperCb &cb, char delimStart = '{', char delimEnd = '}');
239
240    void SerializeLoc(const lexer::SourceRange &loc);
241    void SerializeSourcePosition(const lexer::SourcePosition &pos);
242
243    void SerializeArray(std::vector<const ir::AstNode *> array);
244
245    const lexer::LineIndex index_;
246    std::stringstream ss_;
247    int32_t indent_ {};
248    bool isSrcEmpty_ = false;
249};
250}  // namespace ark::es2panda::ir
251
252#endif  // AST_DUMP_H
253