summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS/Parser.h
blob: d1f0ba023535752649ded1e385dd6a411da8b863 (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
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
/*
 * Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@serenityos.org>
 * Copyright (c) 2021-2022, David Tuin <davidot@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/Assertions.h>
#include <AK/HashTable.h>
#include <AK/NonnullRefPtr.h>
#include <AK/StringBuilder.h>
#include <LibJS/AST.h>
#include <LibJS/Lexer.h>
#include <LibJS/Runtime/FunctionConstructor.h>
#include <LibJS/SourceRange.h>
#include <LibJS/Token.h>
#include <initializer_list>
#include <stdio.h>

namespace JS {

enum class Associativity {
    Left,
    Right
};

struct FunctionNodeParseOptions {
    enum {
        CheckForFunctionAndName = 1 << 0,
        AllowSuperPropertyLookup = 1 << 1,
        AllowSuperConstructorCall = 1 << 2,
        IsGetterFunction = 1 << 3,
        IsSetterFunction = 1 << 4,
        IsArrowFunction = 1 << 5,
        IsGeneratorFunction = 1 << 6,
        IsAsyncFunction = 1 << 7,
    };
};

class ScopePusher;

class Parser {
public:
    struct EvalInitialState {
        bool in_eval_function_context { false };
        bool allow_super_property_lookup { false };
        bool allow_super_constructor_call { false };
        bool in_class_field_initializer { false };
    };

    explicit Parser(Lexer lexer, Program::Type program_type = Program::Type::Script, Optional<EvalInitialState> initial_state_for_eval = {});

    NonnullRefPtr<Program> parse_program(bool starts_in_strict_mode = false);

    template<typename FunctionNodeType>
    NonnullRefPtr<FunctionNodeType> parse_function_node(u8 parse_options = FunctionNodeParseOptions::CheckForFunctionAndName, Optional<Position> const& function_start = {});
    Vector<FunctionNode::Parameter> parse_formal_parameters(int& function_length, u8 parse_options = 0);

    enum class AllowDuplicates {
        Yes,
        No
    };

    enum class AllowMemberExpressions {
        Yes,
        No
    };

    RefPtr<BindingPattern> parse_binding_pattern(AllowDuplicates is_var_declaration = AllowDuplicates::No, AllowMemberExpressions allow_member_expressions = AllowMemberExpressions::No);

    struct PrimaryExpressionParseResult {
        NonnullRefPtr<Expression> result;
        bool should_continue_parsing_as_expression { true };
    };

    NonnullRefPtr<Declaration> parse_declaration();

    enum class AllowLabelledFunction {
        No,
        Yes
    };

    NonnullRefPtr<Statement> parse_statement(AllowLabelledFunction allow_labelled_function = AllowLabelledFunction::No);
    NonnullRefPtr<BlockStatement> parse_block_statement();
    NonnullRefPtr<FunctionBody> parse_function_body(Vector<FunctionDeclaration::Parameter> const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval);
    NonnullRefPtr<ReturnStatement> parse_return_statement();
    NonnullRefPtr<VariableDeclaration> parse_variable_declaration(bool for_loop_variable_declaration = false);
    NonnullRefPtr<Statement> parse_for_statement();

    enum class IsForAwaitLoop {
        No,
        Yes
    };

    struct ForbiddenTokens {
        ForbiddenTokens(std::initializer_list<TokenType> const& forbidden);
        ForbiddenTokens merge(ForbiddenTokens other) const;
        bool allows(TokenType token) const;
        ForbiddenTokens forbid(std::initializer_list<TokenType> const& forbidden) const;

    private:
        void forbid_tokens(std::initializer_list<TokenType> const& forbidden);
        bool m_forbid_in_token : 1 { false };
        bool m_forbid_logical_tokens : 1 { false };
        bool m_forbid_coalesce_token : 1 { false };
        bool m_forbid_paren_open : 1 { false };
        bool m_forbid_question_mark_period : 1 { false };
        bool m_forbid_equals : 1 { false };
    };

    struct ExpressionResult {
        template<typename T>
        ExpressionResult(NonnullRefPtr<T> expression, ForbiddenTokens forbidden = {})
            : expression(expression)
            , forbidden(forbidden)
        {
        }
        NonnullRefPtr<Expression> expression;
        ForbiddenTokens forbidden;
    };

    NonnullRefPtr<Statement> parse_for_in_of_statement(NonnullRefPtr<ASTNode> lhs, IsForAwaitLoop is_await);
    NonnullRefPtr<IfStatement> parse_if_statement();
    NonnullRefPtr<ThrowStatement> parse_throw_statement();
    NonnullRefPtr<TryStatement> parse_try_statement();
    NonnullRefPtr<CatchClause> parse_catch_clause();
    NonnullRefPtr<SwitchStatement> parse_switch_statement();
    NonnullRefPtr<SwitchCase> parse_switch_case();
    NonnullRefPtr<BreakStatement> parse_break_statement();
    NonnullRefPtr<ContinueStatement> parse_continue_statement();
    NonnullRefPtr<DoWhileStatement> parse_do_while_statement();
    NonnullRefPtr<WhileStatement> parse_while_statement();
    NonnullRefPtr<WithStatement> parse_with_statement();
    NonnullRefPtr<DebuggerStatement> parse_debugger_statement();
    NonnullRefPtr<ConditionalExpression> parse_conditional_expression(NonnullRefPtr<Expression> test, ForbiddenTokens);
    NonnullRefPtr<OptionalChain> parse_optional_chain(NonnullRefPtr<Expression> base);
    NonnullRefPtr<Expression> parse_expression(int min_precedence, Associativity associate = Associativity::Right, ForbiddenTokens forbidden = {});
    PrimaryExpressionParseResult parse_primary_expression();
    NonnullRefPtr<Expression> parse_unary_prefixed_expression();
    NonnullRefPtr<RegExpLiteral> parse_regexp_literal();
    NonnullRefPtr<ObjectExpression> parse_object_expression();
    NonnullRefPtr<ArrayExpression> parse_array_expression();
    NonnullRefPtr<StringLiteral> parse_string_literal(Token const& token, bool in_template_literal = false);
    NonnullRefPtr<TemplateLiteral> parse_template_literal(bool is_tagged);
    ExpressionResult parse_secondary_expression(NonnullRefPtr<Expression>, int min_precedence, Associativity associate = Associativity::Right, ForbiddenTokens forbidden = {});
    NonnullRefPtr<Expression> parse_call_expression(NonnullRefPtr<Expression>);
    NonnullRefPtr<NewExpression> parse_new_expression();
    NonnullRefPtr<ClassDeclaration> parse_class_declaration();
    NonnullRefPtr<ClassExpression> parse_class_expression(bool expect_class_name);
    NonnullRefPtr<YieldExpression> parse_yield_expression();
    NonnullRefPtr<AwaitExpression> parse_await_expression();
    NonnullRefPtr<Expression> parse_property_key();
    NonnullRefPtr<AssignmentExpression> parse_assignment_expression(AssignmentOp, NonnullRefPtr<Expression> lhs, int min_precedence, Associativity, ForbiddenTokens forbidden = {});
    NonnullRefPtr<Identifier> parse_identifier();
    NonnullRefPtr<ImportStatement> parse_import_statement(Program& program);
    NonnullRefPtr<ExportStatement> parse_export_statement(Program& program);

    RefPtr<FunctionExpression> try_parse_arrow_function_expression(bool expect_parens, bool is_async = false);
    RefPtr<LabelledStatement> try_parse_labelled_statement(AllowLabelledFunction allow_function);
    RefPtr<MetaProperty> try_parse_new_target_expression();
    RefPtr<MetaProperty> try_parse_import_meta_expression();
    NonnullRefPtr<ImportCall> parse_import_call();

    Vector<CallExpression::Argument> parse_arguments();

    struct Error {
        String message;
        Optional<Position> position;

        String to_string() const
        {
            if (!position.has_value())
                return message;
            return String::formatted("{} (line: {}, column: {})", message, position.value().line, position.value().column);
        }

        String source_location_hint(StringView source, char const spacer = ' ', char const indicator = '^') const
        {
            if (!position.has_value())
                return {};
            // We need to modify the source to match what the lexer considers one line - normalizing
            // line terminators to \n is easier than splitting using all different LT characters.
            String source_string = source.replace("\r\n", "\n", ReplaceMode::All).replace("\r", "\n", ReplaceMode::All).replace(LINE_SEPARATOR_STRING, "\n", ReplaceMode::All).replace(PARAGRAPH_SEPARATOR_STRING, "\n", ReplaceMode::All);
            StringBuilder builder;
            builder.append(source_string.split_view('\n', true)[position.value().line - 1]);
            builder.append('\n');
            for (size_t i = 0; i < position.value().column - 1; ++i)
                builder.append(spacer);
            builder.append(indicator);
            return builder.build();
        }
    };

    bool has_errors() const { return m_state.errors.size(); }
    Vector<Error> const& errors() const { return m_state.errors; }
    void print_errors(bool print_hint = true) const
    {
        for (auto& error : m_state.errors) {
            if (print_hint) {
                auto hint = error.source_location_hint(m_state.lexer.source());
                if (!hint.is_empty())
                    warnln("{}", hint);
            }
            warnln("SyntaxError: {}", error.to_string());
        }
    }

    struct TokenMemoization {
        bool try_parse_arrow_function_expression_failed;
    };

    // Needs to mess with m_state, and we're not going to expose a non-const getter for that :^)
    friend ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic_function(GlobalObject&, FunctionObject&, FunctionObject*, FunctionKind, MarkedVector<Value> const&);

private:
    friend class ScopePusher;

    void parse_script(Program& program, bool starts_in_strict_mode);
    void parse_module(Program& program);

    Associativity operator_associativity(TokenType) const;
    bool match_expression() const;
    bool match_unary_prefixed_expression() const;
    bool match_secondary_expression(ForbiddenTokens forbidden = {}) const;
    bool match_statement() const;
    bool match_export_or_import() const;
    bool match_assert_clause() const;
    bool match_declaration() const;
    bool try_match_let_declaration() const;
    bool match_variable_declaration() const;
    bool match_identifier() const;
    bool match_identifier_name() const;
    bool match_property_key() const;
    bool is_private_identifier_valid() const;
    bool match(TokenType type) const;
    bool done() const;
    void expected(char const* what);
    void syntax_error(String const& message, Optional<Position> = {});
    Token consume();
    Token consume_identifier();
    Token consume_identifier_reference();
    Token consume(TokenType type);
    Token consume_and_validate_numeric_literal();
    void consume_or_insert_semicolon();
    void save_state();
    void load_state();
    void discard_saved_state();
    Position position() const;

    RefPtr<BindingPattern> synthesize_binding_pattern(Expression const& expression);

    Token next_token(size_t steps = 1) const;

    void check_identifier_name_for_assignment_validity(FlyString const&, bool force_strict = false);

    bool try_parse_arrow_function_expression_failed_at_position(Position const&) const;
    void set_try_parse_arrow_function_expression_failed_at_position(Position const&, bool);

    bool match_invalid_escaped_keyword() const;

    bool parse_directive(ScopeNode& body);
    void parse_statement_list(ScopeNode& output_node, AllowLabelledFunction allow_labelled_functions = AllowLabelledFunction::No);

    FlyString consume_string_value();
    ModuleRequest parse_module_request();

    struct RulePosition {
        AK_MAKE_NONCOPYABLE(RulePosition);
        AK_MAKE_NONMOVABLE(RulePosition);

    public:
        RulePosition(Parser& parser, Position position)
            : m_parser(parser)
            , m_position(position)
        {
            m_parser.m_rule_starts.append(position);
        }

        ~RulePosition()
        {
            auto last = m_parser.m_rule_starts.take_last();
            VERIFY(last.line == m_position.line);
            VERIFY(last.column == m_position.column);
        }

        Position const& position() const { return m_position; }

    private:
        Parser& m_parser;
        Position m_position;
    };

    [[nodiscard]] RulePosition push_start() { return { *this, position() }; }

    struct ParserState {
        Lexer lexer;
        Token current_token;
        Vector<Error> errors;
        ScopePusher* current_scope_pusher { nullptr };

        HashMap<StringView, Optional<Position>> labels_in_scope;
        HashTable<StringView>* referenced_private_names { nullptr };

        bool strict_mode { false };
        bool allow_super_property_lookup { false };
        bool allow_super_constructor_call { false };
        bool in_function_context { false };
        bool in_eval_function_context { false }; // This controls if we allow new.target or not. Note that eval("return") is not allowed, so we have to have a separate state variable for eval.
        bool in_formal_parameter_context { false };
        bool in_generator_function_context { false };
        bool await_expression_is_valid { false };
        bool in_arrow_function_context { false };
        bool in_break_context { false };
        bool in_continue_context { false };
        bool string_legacy_octal_escape_sequence_in_scope { false };
        bool in_class_field_initializer { false };
        bool in_class_static_init_block { false };
        bool function_might_need_arguments_object { false };

        ParserState(Lexer, Program::Type);
    };

    class PositionKeyTraits {
    public:
        static int hash(Position const& position)
        {
            return int_hash(position.line) ^ int_hash(position.column);
        }

        static bool equals(Position const& a, Position const& b)
        {
            return a.column == b.column && a.line == b.line;
        }
    };

    Vector<Position> m_rule_starts;
    ParserState m_state;
    FlyString m_filename;
    Vector<ParserState> m_saved_state;
    HashMap<Position, TokenMemoization, PositionKeyTraits> m_token_memoizations;
    Program::Type m_program_type;
};
}