snaplogger: /home/snapwebsites/snapcpp/contrib/snaplogger/snaplogger/format.cpp Source File

format.cpp
Go to the documentation of this file.
1 /*
2  * License:
3  * Copyright (c) 2013-2019 Made to Order Software Corp. All Rights Reserved
4  *
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Authors:
23  * Alexis Wilke alexis@m2osw.com
24  */
25 
34 // self
35 //
36 #include "snaplogger/format.h"
37 
38 #include "snaplogger/exception.h"
39 
40 
41 // C++ lib
42 //
43 #include <numeric>
44 #include <iostream>
45 
46 
47 // last include
48 //
49 #include <snapdev/poison.h>
50 
51 
52 
53 namespace snaplogger
54 {
55 
56 
57 namespace
58 {
59 
60 
61 
62 class parser
63 {
64 public:
65  enum class token_t
66  {
67  TOKEN_EOF,
68  TOKEN_COLON,
69  TOKEN_EQUAL,
70  TOKEN_STRING,
71  TOKEN_INTEGER,
72  TOKEN_IDENTIFIER,
73  TOKEN_END
74  };
75 
76  parser(std::string const & f, variable::vector_t & variables)
77  : f_input(f)
78  , f_variables(variables)
79  {
80  }
81 
82  int getc()
83  {
84  if(f_pos >= f_input.length())
85  {
86  return EOF;
87  }
88 
89  int const c(f_input[f_pos]);
90  ++f_pos;
91  return c;
92  }
93 
94  void ungetc(int c)
95  {
96  if(f_pos == 0)
97  {
98  throw logger_logic_error("ungetc() called too many times.");
99  }
100  --f_pos;
101  if(f_input[f_pos] != c)
102  {
103  throw logger_logic_error("ungetc() called with the wrong character.");
104  }
105  }
106 
107  token_t get_token()
108  {
109  for(;;)
110  {
111  int c(getc());
112  if(c == EOF)
113  {
114  return token_t::TOKEN_EOF;
115  }
116  switch(c)
117  {
118  case ' ':
119  case '\t':
120  case '\n':
121  case '\r':
122  // ignore spaces
123  break;
124 
125  case ':':
126  return token_t::TOKEN_COLON;
127 
128  case '=':
129  return token_t::TOKEN_EQUAL;
130 
131  case '}':
132  return token_t::TOKEN_END;
133 
134  case '"':
135  case '\'':
136  case '`':
137  {
138  f_text.clear();
139  int quote(c);
140  for(;;)
141  {
142  c = getc();
143  if(c == '\\')
144  {
145  c = getc();
146  }
147  else if(c == quote)
148  {
149  break;
150  }
151  if(c == EOF)
152  {
153  throw invalid_variable("unterminated string in format variable.");
154  }
155  f_text += c;
156  }
157  return token_t::TOKEN_STRING;
158  }
159  break;
160 
161  case '0':
162  case '1':
163  case '2':
164  case '3':
165  case '4':
166  case '5':
167  case '6':
168  case '7':
169  case '8':
170  case '9':
171  {
172  f_integer = c - '0';
173  for(;;)
174  {
175  c = getc();
176  if(c < '0' || c > '9')
177  {
178  ungetc(c);
179  break;
180  }
181  f_integer *= 10;
182  f_integer += c - '0';
183  }
184  return token_t::TOKEN_INTEGER;
185  }
186  break;
187 
188  default:
189  if((c >= 'a' && c <= 'z')
190  || (c >= 'A' && c <= 'Z')
191  || c == '_')
192  {
193  f_text.clear();
194  f_text += c;
195  for(;;)
196  {
197  c = getc();
198  if((c < 'a' || c > 'z')
199  && (c < 'A' || c > 'Z')
200  && (c < '0' || c > '9')
201  && c != '_')
202  {
203  ungetc(c);
204  break;
205  }
206  f_text += c;
207  }
208  return token_t::TOKEN_IDENTIFIER;
209  }
210  else
211  {
212  throw invalid_variable("unexpected character in format variable.");
213  }
214  break;
215 
216  }
217  }
218  }
219 
220  void parse_variable()
221  {
222  token_t tok(get_token());
223  if(tok != token_t::TOKEN_IDENTIFIER)
224  {
225  throw invalid_variable("expected a token as the variable name.");
226  }
227  variable::pointer_t var(get_variable(f_text));
228  if(var == nullptr)
229  {
230  throw invalid_variable("unknown variable \"" + f_text + "\".");
231  }
232  f_variables.push_back(var);
233 
234  tok = get_token();
235  for(;;)
236  {
237  if(tok == token_t::TOKEN_END)
238  {
239  break;
240  }
241  if(tok != token_t::TOKEN_COLON)
242  {
243  throw invalid_variable("variable parameters must be delimited by colon (:) characters.");
244  }
245  tok = get_token();
246  if(tok != token_t::TOKEN_IDENTIFIER)
247  {
248  throw invalid_variable("variable parameters must be given a name (an identifier).");
249  }
250  param::pointer_t p(std::make_shared<param>(f_text));
251  var->add_param(p);
252 
253  tok = get_token();
254  if(tok == token_t::TOKEN_EQUAL)
255  {
256  // the token is followed by a value
257  //
258  tok = get_token();
259  if(tok == token_t::TOKEN_STRING
260  || tok == token_t::TOKEN_IDENTIFIER)
261  {
262  p->set_value(f_text);
263  }
264  else if(tok == token_t::TOKEN_INTEGER)
265  {
266  p->set_integer(f_integer);
267  }
268  else
269  {
270  throw invalid_variable("unexpected token for a parameter value.");
271  }
272 
273  tok = get_token();
274  }
275  }
276  }
277 
278  void parse()
279  {
280  auto add_text = [this](std::string const & text)
281  {
282  if(!text.empty())
283  {
284  variable::pointer_t var(get_variable("direct"));
285  if(var == nullptr)
286  {
287  throw logger_logic_error("variable type \"direct\" not registered?.");
288  }
289  param::pointer_t p(std::make_shared<param>("msg"));
290  var->add_param(p);
291  p->set_value(text);
292  f_variables.push_back(var);
293  }
294  };
295 
296  std::string text;
297 
298  for(;;)
299  {
300  int c(getc());
301  if(c == EOF)
302  {
303  break;
304  }
305  if(c == '$')
306  {
307  c = getc();
308  if(c == '{')
309  {
310  // we found a variable definition
311  //
312  add_text(text);
313  text.clear();
314 
315  parse_variable();
316  }
317  else
318  {
319  text += '$';
320  text += c;
321  }
322  }
323  else
324  {
325  text += c;
326  }
327  }
328  add_text(text);
329  }
330 
331 private:
332  std::string const f_input;
333  size_t f_pos = 0;
334  variable::vector_t & f_variables;
335  std::string f_text = std::string();
336  std::int64_t f_integer = 0;
337 };
338 
339 
340 
341 }
342 // no name namespace
343 
344 
345 format::format(std::string const & f)
346 {
347  parser p(f, f_variables);
348  p.parse();
349 }
350 
351 
352 std::string format::process_message(message const & msg)
353 {
354  return std::accumulate(
355  f_variables.begin()
356  , f_variables.end()
357  , std::string()
358  , [&msg](std::string const & r, variable::pointer_t v)
359  {
360  return r + v->get_value(msg);
361  });
362 }
363 
364 
365 
366 
367 
368 } // snaplogger namespace
369 // vim: ts=4 sw=4 et
Logger exceptions.
Format a message.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.

Syndicate content

Snap! Websites
An Open Source CMS System in C++

Contact Us Directly