summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibGUI/INILexer.cpp
blob: e3e9300003918e449d5f2b207766f9f7ca2d56d7 (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
/*
 * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com>
 * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include "INILexer.h"
#include <AK/CharacterTypes.h>
#include <AK/Vector.h>

namespace GUI {

IniLexer::IniLexer(StringView input)
    : m_input(input)
    , m_iterator(m_input.begin())
{
}

u32 IniLexer::peek(size_t offset) const
{
    return m_iterator.peek(offset).value_or(0);
}

u32 IniLexer::consume()
{
    VERIFY(m_iterator != m_input.end());
    u32 ch = *m_iterator;
    ++m_iterator;
    if (ch == '\n') {
        m_position.line++;
        m_position.column = 0;
    } else {
        m_position.column++;
    }
    return ch;
}

Vector<IniToken> IniLexer::lex()
{
    Vector<IniToken> tokens;
    IniPosition token_start_position;

    auto emit_token = [&](auto type) {
        IniToken token;
        token.m_type = type;
        token.m_start = m_position;
        consume();
        token.m_end = m_position;
        tokens.append(token);
    };

    auto begin_token = [&] {
        token_start_position = m_position;
    };

    auto commit_token = [&](auto type) {
        IniToken token;
        token.m_type = type;
        token.m_start = token_start_position;
        token.m_end = m_position;
        tokens.append(token);
    };

    while (m_iterator != m_input.end()) {
        auto ch = peek();

        if (is_ascii_space(ch)) {
            begin_token();
            while (is_ascii_space(peek()))
                consume();
            commit_token(IniToken::Type::Whitespace);
            continue;
        }

        // ;Comment or #Comment
        if (ch == ';' || ch == '#') {
            begin_token();
            while (peek() && peek() != '\n')
                consume();
            commit_token(IniToken::Type::Comment);
            continue;
        }

        // [Section]
        if (ch == '[') {
            // [ Token
            begin_token();
            consume();
            commit_token(IniToken::Type::LeftBracket);

            // Section
            begin_token();
            while (peek() && !(peek() == ']' || peek() == '\n'))
                consume();
            commit_token(IniToken::Type::Section);

            // ] Token
            if (peek() && peek() == ']') {
                begin_token();
                consume();
                commit_token(IniToken::Type::RightBracket);
            }

            continue;
        }

        // Empty Line
        if (ch == '\n') {
            consume();
            emit_token(IniToken::Type::Unknown);
            continue;
        }

        //  Name=Value
        begin_token();
        while (peek() && !(peek() == '=' || peek() == '\n'))
            consume();
        commit_token(IniToken::Type::Name);

        if (peek() && peek() == '=') {
            begin_token();
            consume();
            commit_token(IniToken::Type::Equal);
        }

        if (peek()) {
            begin_token();
            while (peek() && peek() != '\n')
                consume();
            commit_token(IniToken::Type::Value);
        }
    }
    return tokens;
}

}