solinject  1.0.0
C++17 Dependency Injection header-only library
ConfigurationParser.hpp
Go to the documentation of this file.
1 // SPDX-License-Identifier: LGPL-3.0-or-later
2 
3 /*
4  * solinject - C++ Dependency Injection header-only library
5  * Copyright (C) 2022 SemperSolus0x3d
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program. If not, see <https://www.gnu.org/licenses/>.
19  */
20 
22 
23 #pragma once
24 #include <vector>
25 #include <string>
26 #include <array>
27 #include <stdexcept>
28 #include <cstring>
29 #include "Defines.hpp"
30 #include "Configuration.hpp"
32 
33 namespace sol::di
34 {
37  {
38  public:
43  {
44  m_Input = input;
45 
46  Configuration result;
47 
48  SkipBom();
49 
50  Token token(impl::TokenType::Key, "");
51 
52  while (GetNextToken(token))
53  {
54  auto configurationItems = ParseConfigurationItem(token);
55 
56  for (auto&& item : configurationItems)
57  result.AddConfigurationItem(std::move(item));
58  }
59 
60  return result;
61  }
62  private:
64  using Token = impl::ConfigurationParserToken;
65 
67  using byte = unsigned char;
68 
70  std::string m_Input;
71 
73  size_t m_Pos = 0;
74 
76  std::string m_Lexem;
77 
79  impl::TokenType m_CurrentType = impl::TokenType::Key;
80 
84  bool IsEndOfInput() { return m_Pos >= m_Input.size(); }
85 
88  void Skip(size_t bytesCount = 1)
89  {
90  m_Pos += bytesCount;
91  }
92 
94  void SkipBom()
95  {
96  solinject_req_assert(m_Pos == 0);
97 
98  if (m_Input.size() <= 3)
99  return;
100 
101  byte bom[] = { 0xEF, 0xBB, 0xBF };
102 
103  if (std::memcmp(m_Input.data(), bom, 3) == 0)
104  Skip(3);
105  }
106 
109  size_t GetCodepointSize()
110  {
111  byte firstByte = m_Input[m_Pos];
112 
113  if ((firstByte & 0b1000'0000u) == 0)
114  return 1;
115  else if ((firstByte & 0b1110'0000u) == 0b1100'0000u)
116  return 2;
117  else if ((firstByte & 0b1111'0000u) == 0b1110'0000u)
118  return 3;
119  else if ((firstByte & 0b1111'1000u) == 0b1111'0000u)
120  return 4;
121  else
122  throw std::runtime_error("Invalid UTF8 character");
123  }
124 
128  void ResetStateAndReturnToken(Token& token, bool skipTypeGuess = false)
129  {
130  using namespace impl;
131 
132  if (!skipTypeGuess)
133  if (m_Lexem == "Self")
134  m_CurrentType = TokenType::Self;
135  else if (m_Lexem == "Singleton")
136  m_CurrentType = TokenType::Singleton;
137  else if (m_Lexem == "Transient")
138  m_CurrentType = TokenType::Transient;
139  else if (m_Lexem == "Shared")
140  m_CurrentType = TokenType::Shared;
141  else if (m_Lexem == "Scoped")
142  m_CurrentType = TokenType::Scoped;
143  else if (m_Lexem == "None")
144  m_CurrentType = TokenType::None;
145 
146  token = Token(m_CurrentType, m_Lexem);
147 
148  m_CurrentType = TokenType::Key;
149  m_Lexem = "";
150  }
151 
153  void SkipComment()
154  {
155  while (!IsEndOfInput())
156  {
157  size_t codepointSize = GetCodepointSize();
158 
159  if (codepointSize > 1)
160  {
161  Skip(codepointSize);
162  continue;
163  }
164  else
165  {
166  byte codepoint = m_Input[m_Pos];
167 
168  Skip();
169 
170  if (codepoint == '\n')
171  break;
172  }
173  }
174  }
175 
177  void TokenizeQuotedLiteral()
178  {
179  Skip();
180 
181  while (!IsEndOfInput())
182  {
183  size_t codepointSize = GetCodepointSize();
184 
185  if (codepointSize > 1)
186  {
187  m_Lexem.append(m_Input.data() + m_Pos, codepointSize);
188  Skip(codepointSize - 1);
189  }
190  else
191  {
192  byte codepoint = m_Input[m_Pos];
193 
194  Skip();
195 
196  if (codepoint == '\"')
197  break;
198  else
199  m_Lexem += codepoint;
200  }
201  }
202 
203  m_CurrentType = impl::TokenType::Key;
204  }
205 
207  void TokenizeEscapedCharacter()
208  {
209  Skip();
210 
211  size_t codepointSize = GetCodepointSize();
212 
213  m_Lexem.append(m_Input.data() + m_Pos, codepointSize);
214  Skip(codepointSize - 1);
215  }
216 
220  bool GetNextToken(Token& token)
221  {
222  using namespace impl;
223 
224  for (; !IsEndOfInput(); m_Pos++)
225  {
226  size_t codepointSize = GetCodepointSize();
227  if (codepointSize > 1)
228  {
229  m_Lexem.append(m_Input.data() + m_Pos, codepointSize);
230  Skip(codepointSize - 1);
231  }
232  else
233  {
234  byte codepoint = m_Input[m_Pos];
235 
236  switch (codepoint)
237  {
238  case '\"':
239  TokenizeQuotedLiteral();
240  ResetStateAndReturnToken(token, true);
241  return true;
242 
243  case '\\':
244  TokenizeEscapedCharacter();
245  break;
246 
247  case ' ':
248  case '\n':
249  case '\t':
250  if (m_Lexem != "")
251  {
252  ResetStateAndReturnToken(token);
253  return true;
254  }
255 
256  break;
257 
258  case '#':
259  if (m_Lexem != "")
260  {
261  ResetStateAndReturnToken(token);
262  return true;
263  }
264 
265  SkipComment();
266 
267  break;
268 
269  case '{':
270  if (m_Lexem != "")
271  {
272  ResetStateAndReturnToken(token);
273  return true;
274  }
275 
276  token = Token(TokenType::OpeningCurlyBracket, "");
277  Skip();
278  return true;
279 
280  case '}':
281  if (m_Lexem != "")
282  {
283  ResetStateAndReturnToken(token);
284  return true;
285  }
286 
287  token = Token(TokenType::ClosingCurlyBracket, "");
288  Skip();
289  return true;
290 
291  default:
292  m_Lexem += codepoint;
293  break;
294  }
295  }
296  }
297 
298  if (m_Lexem != "")
299  {
300  ResetStateAndReturnToken(token);
301  return true;
302  }
303 
304  return false;
305  }
306 
310  std::vector<ConfigurationItem> ParseConfigurationItem(const Token& initialToken)
311  {
312  using namespace impl;
313 
314  solinject_req_assert(initialToken.Type() == TokenType::Key);
315 
316  std::string interfaceKey = initialToken.Content();
317  std::vector<ConfigurationItem> result;
318  Token token(TokenType::Key, "");
319 
320  if (!GetNextToken(token))
321  throw std::runtime_error("Unexpected end of input");
322 
323  switch (token.Type())
324  {
325  case TokenType::Key:
326  case TokenType::Self:
327  result.push_back(
328  ParseImplementationRegistration(interfaceKey, token)
329  );
330  break;
331  case TokenType::OpeningCurlyBracket:
332  result = ParseMultipleImplementationRegistrations(interfaceKey);
333  break;
334  }
335 
336  return result;
337  }
338 
343  ConfigurationItem ParseImplementationRegistration(std::string interfaceKey, const Token& initialToken)
344  {
345  using namespace impl;
346 
347  solinject_assert(
348  initialToken.Type() == TokenType::Key ||
349  initialToken.Type() == TokenType::Self
350  );
351 
352  std::string implementationKey;
353 
354  switch (initialToken.Type())
355  {
356  case TokenType::Key:
357  implementationKey = initialToken.Content();
358  break;
359  case TokenType::Self:
360  implementationKey = interfaceKey;
361  break;
362  default:
363  using namespace std::string_literals;
364  throw std::runtime_error("Unexpected token: "s + initialToken.Content());
365  }
366 
367  Token token(TokenType::Key, "");
368 
369  if (!GetNextToken(token))
370  throw std::runtime_error("Unexpected end of input");
371 
372  ServiceLifetime lifetime = ServiceLifetime::Singleton;
373 
374  switch (token.Type())
375  {
376  case TokenType::Singleton:
377  lifetime = ServiceLifetime::Singleton;
378  break;
379 
380  case TokenType::Transient:
381  lifetime = ServiceLifetime::Transient;
382  break;
383 
384  case TokenType::Shared:
385  lifetime = ServiceLifetime::Shared;
386  break;
387 
388  case TokenType::Scoped:
389  lifetime = ServiceLifetime::Scoped;
390  break;
391 
392  default:
393  using namespace std::string_literals;
394  throw std::runtime_error("Unexpected token: "s + token.Content());
395  }
396 
397  return ConfigurationItem(interfaceKey, implementationKey, lifetime);
398  }
399 
403  std::vector<ConfigurationItem> ParseMultipleImplementationRegistrations(std::string interfaceKey)
404  {
405  using namespace impl;
406 
407  Token token(TokenType::Key, "");
408  std::vector<ConfigurationItem> result;
409 
410  while(GetNextToken(token))
411  {
412  if (token.Type() == TokenType::ClosingCurlyBracket)
413  break;
414 
415  result.push_back(
416  ParseImplementationRegistration(interfaceKey, token)
417  );
418  }
419 
420  return result;
421  }
422  };
423 }
sol::di::ConfigurationParser::Parse
Configuration Parse(const std::string &input)
Parses configuration.
Definition: ConfigurationParser.hpp:42
std::string
std::move
T move(T... args)
sol::di::impl::TokenType
TokenType
sol::di::impl::ConfigurationParserToken type
Definition: ConfigurationParserToken.hpp:29
std::string::size
T size(T... args)
sol::di::ConfigurationParser
DI Configuration parser.
Definition: ConfigurationParser.hpp:36
solinject_req_assert
#define solinject_req_assert(expression)
Required assert, which is disabled only when the assert() macro from assert.h is disabled.
Definition: Defines.hpp:41
sol::di::Configuration::AddConfigurationItem
void AddConfigurationItem(ConfigurationItem item)
Adds a configuration item.
Definition: Configuration.hpp:80
sol::di::impl::ConfigurationParserToken
ConfigurationParser token
Definition: ConfigurationParserToken.hpp:43
sol::di::Configuration
DI configuration.
Definition: Configuration.hpp:31
ConfigurationParserToken.hpp
Configuration.hpp
std::memcmp
T memcmp(T... args)
std::string::data
T data(T... args)
Defines.hpp