Serenity Operating System
1/*
2 * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com>
3 * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include "INILexer.h"
9#include <AK/CharacterTypes.h>
10#include <AK/Vector.h>
11
12namespace GUI {
13
14IniLexer::IniLexer(StringView input)
15 : m_input(input)
16 , m_iterator(m_input.begin())
17{
18}
19
20u32 IniLexer::peek(size_t offset) const
21{
22 return m_iterator.peek(offset).value_or(0);
23}
24
25u32 IniLexer::consume()
26{
27 VERIFY(m_iterator != m_input.end());
28 u32 ch = *m_iterator;
29 ++m_iterator;
30 if (ch == '\n') {
31 m_position.line++;
32 m_position.column = 0;
33 } else {
34 m_position.column++;
35 }
36 return ch;
37}
38
39Vector<IniToken> IniLexer::lex()
40{
41 Vector<IniToken> tokens;
42 IniPosition token_start_position;
43
44 auto emit_token = [&](auto type) {
45 IniToken token;
46 token.m_type = type;
47 token.m_start = m_position;
48 consume();
49 token.m_end = m_position;
50 tokens.append(token);
51 };
52
53 auto begin_token = [&] {
54 token_start_position = m_position;
55 };
56
57 auto commit_token = [&](auto type) {
58 IniToken token;
59 token.m_type = type;
60 token.m_start = token_start_position;
61 token.m_end = m_position;
62 tokens.append(token);
63 };
64
65 while (m_iterator != m_input.end()) {
66 auto ch = peek();
67
68 if (is_ascii_space(ch)) {
69 begin_token();
70 while (is_ascii_space(peek()))
71 consume();
72 commit_token(IniToken::Type::Whitespace);
73 continue;
74 }
75
76 // ;Comment or #Comment
77 if (ch == ';' || ch == '#') {
78 begin_token();
79 while (peek() && peek() != '\n')
80 consume();
81 commit_token(IniToken::Type::Comment);
82 continue;
83 }
84
85 // [Section]
86 if (ch == '[') {
87 // [ Token
88 begin_token();
89 consume();
90 commit_token(IniToken::Type::LeftBracket);
91
92 // Section
93 begin_token();
94 while (peek() && !(peek() == ']' || peek() == '\n'))
95 consume();
96 commit_token(IniToken::Type::Section);
97
98 // ] Token
99 if (peek() && peek() == ']') {
100 begin_token();
101 consume();
102 commit_token(IniToken::Type::RightBracket);
103 }
104
105 continue;
106 }
107
108 // Empty Line
109 if (ch == '\n') {
110 consume();
111 emit_token(IniToken::Type::Unknown);
112 continue;
113 }
114
115 // Name=Value
116 begin_token();
117 while (peek() && !(peek() == '=' || peek() == '\n'))
118 consume();
119 commit_token(IniToken::Type::Name);
120
121 if (peek() && peek() == '=') {
122 begin_token();
123 consume();
124 commit_token(IniToken::Type::Equal);
125 }
126
127 if (peek()) {
128 begin_token();
129 while (peek() && peek() != '\n')
130 consume();
131 commit_token(IniToken::Type::Value);
132 }
133 }
134 return tokens;
135}
136
137}