1 // =================================
  2 // Copyright (c) 2020 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <sngcpp/parser/TokenValueParsers.hpp>
  7 #include <soulng/util/Unicode.hpp>
  8 #include <sstream>
  9 
 10 namespace sngcpp { namespace cppparser {
 11 
 12 using namespace soulng::unicode;
 13 
 14 void ParseFloatingLiteral(const std::string& fileNameconst soulng::lexer::Token& tokendouble& valuesngcpp::ast::Suffix& suffix)
 15 {
 16     value = 0.0;
 17     suffix = sngcpp::ast::Suffix::none;
 18     const char32_t* p = token.match.begin;
 19     const char32_t* e = token.match.end;
 20     while (p != e && *p >= '0' && *p <= '9')
 21     {
 22         ++p;
 23     }
 24     if (p != e && *p == '.')
 25     {
 26         ++p;
 27     }
 28     while (p != e && *p >= '0' && *p <= '9')
 29     {
 30         ++p;
 31     }
 32     if (p != e&&(  *p == 'e' || *p == 'E'))
 33     {
 34         ++p;
 35     }
 36     if (p != e&&(  *p == '+' || *p == '-'))
 37     {
 38         ++p;
 39     }
 40     while (p != e && *p >= '0' && *p <= '9')
 41     {
 42         ++p;
 43     }
 44     if (p != e)
 45     {
 46         std::string s = ToUtf8(std::u32string(token.match.beginp));
 47         std::stringstream sstream;
 48         sstream.str(s);
 49         sstream >> value;
 50         if (!sstream)
 51         {
 52             throw std::runtime_error("invalid floating point literal in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
 53         }
 54         if (p != e&&(  *p == 'f' || *p == 'F'))
 55         {
 56             suffix = sngcpp::ast::Suffix::f;
 57             ++p;
 58         }
 59         else if (p != e&&(  *p == 'l' || *p == 'L'))
 60         {
 61             suffix = sngcpp::ast::Suffix::l;
 62             ++p;
 63         }
 64     }
 65     if (p != e)
 66     {
 67         throw std::runtime_error("invalid floating point literal in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
 68     }
 69 }
 70 
 71 void ParseIntegerLiteral(const std::string& fileNameconst soulng::lexer::Token& tokenuint64_t& valuesngcpp::ast::Base& basesngcpp::ast::Suffix& suffix)
 72 {
 73     value = 0;
 74     base = sngcpp::ast::Base::decimal;
 75     suffix = sngcpp::ast::Suffix::none;
 76     const char32_t* p = token.match.begin;
 77     const char32_t* e = token.match.end;
 78     if (p != e && *p == '0')
 79     {
 80         base = sngcpp::ast::Base::octal;
 81         ++p;
 82         if (p != e&&(  *p == 'x' || *p == 'X'))
 83         {
 84             base = sngcpp::ast::Base::hex;
 85             ++p;
 86             while (p != e&&(  (*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')))
 87             {
 88                 switch (*p)
 89                 {
 90                     case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
 91                     {
 92                         value = 16 * value + *p - '0';
 93                         break;
 94                     }
 95                     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
 96                     {
 97                         value = 16 * value + 10 + *p - 'A';
 98                         break;
 99                     }
100                     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
101                     {
102                         value = 16 * value + 10 + *p - 'a';
103                         break;
104                     }
105                 }
106                 ++p;
107             }
108         }
109         else
110         {
111             while (p != e && *p >= '0' && *p <= '7')
112             {
113                 value = 8 * value + *p - '0';
114                 ++p;
115             }
116         }
117     }
118     else
119     {
120         base = sngcpp::ast::Base::decimal;
121         while (p != e && *p >= '0' && *p <= '9')
122         {
123             value = 10 * value + *p - '0';
124             ++p;
125         }
126     }
127     if (p != e&&(  *p == 'u' || *p == 'U'))
128     {
129         suffix = sngcpp::ast::Suffix::u;
130         ++p;
131     }
132     if (p != e&&(  *p == 'l' || *p == 'L'))
133     {
134         ++p;
135         if (p != e&&(  *p == 'l' || *p == 'L'))
136         {
137             suffix = suffix | sngcpp::ast::Suffix::ll;
138             ++p;
139         }
140         else
141         {
142             suffix = suffix | sngcpp::ast::Suffix::l;
143         }
144         if (p != e&&(  *p == 'u' || *p == 'U'))
145         {
146             suffix = suffix | sngcpp::ast::Suffix::u;
147             ++p;
148         }
149     }
150     if (p != e)
151     {
152         throw std::runtime_error("invalid integer literal in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
153     }
154 }
155 
156 void ParseHexChar(const std::string& fileNameconst soulng::lexer::Token& tokenconst char32_t*& pconst char32_t* echar32_t& value)
157 {
158     if (p != e&&(  (*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')))
159     {
160         switch (*p)
161         {
162             case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
163             {
164                 value = 16 * value + *p - '0';
165                 break;
166             }
167             case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
168             {
169                 value = 16 * value + 10 + *p - 'A';
170                 break;
171             }
172             case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
173             {
174                 value = 16 * value + 10 + *p - 'a';
175                 break;
176             }
177         }
178         ++p;
179     }
180     else
181     {
182         throw std::runtime_error("hexadecimal character expected in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
183     }
184 }
185 
186 void ParseEscape(const std::string& fileNameconst soulng::lexer::Token& tokenconst char32_t*& pconst char32_t* echar32_t& value)
187 {
188     if (p != e && *p == '\\')
189     {
190         ++p;
191         if (p != e && *p == 'x')
192         {
193             ++p;
194             ParseHexChar(fileNametokenpevalue);
195             ParseHexChar(fileNametokenpevalue);
196         }
197         else if (p != e && *p == 'u')
198         {
199             ++p;
200             ParseHexChar(fileNametokenpevalue);
201             ParseHexChar(fileNametokenpevalue);
202             ParseHexChar(fileNametokenpevalue);
203             ParseHexChar(fileNametokenpevalue);
204         }
205         else if (p != e && *p == 'U')
206         {
207             ++p;
208             ParseHexChar(fileNametokenpevalue);
209             ParseHexChar(fileNametokenpevalue);
210             ParseHexChar(fileNametokenpevalue);
211             ParseHexChar(fileNametokenpevalue);
212             ParseHexChar(fileNametokenpevalue);
213             ParseHexChar(fileNametokenpevalue);
214             ParseHexChar(fileNametokenpevalue);
215             ParseHexChar(fileNametokenpevalue);
216         }
217         else if (p != e && *p >= '0' && *p <= '7')
218         {
219             value = *p - '0';
220             ++p;
221             if (p != e && *p >= '0' && *p <= '7')
222             {
223                 value = 8 * value + *p - '0';
224                 ++p;
225                 if (p != e && *p >= '0' && *p <= '7')
226                 {
227                     value = 8 * value + *p - '0';
228                     ++p;
229                 }
230             }
231         }
232         else if (p != e)
233         {
234             switch (*p)
235             {
236                 case 'a': value = '\a'; ++p; break;
237                 case 'b': value = '\b'; ++p; break;
238                 case 'f': value = '\f'; ++p; break;
239                 case 'n': value = '\n'; ++p; break;
240                 case 'r': value = '\r'; ++p; break;
241                 case 't': value = '\t'; ++p; break;
242                 case 'v': value = '\v'; ++p; break;
243                 case '\\': value = '\\'; ++p; break;
244                 case '?': value = '?'; ++p; break;
245                 default: value = *p; ++p; break;
246             }
247         }
248     }
249     else
250     {
251         throw std::runtime_error("invalid escape in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
252     }
253 }
254 
255 void ParseCharacterLiteral(const std::string& fileNameconst soulng::lexer::Token& tokenchar32_t& valuechar32_t& prefix)
256 {
257     value = '\0';
258     prefix = '\0';
259     const char32_t* p = token.match.begin;
260     const char32_t* e = token.match.end;
261     if (p != e && *p == 'u')
262     {
263         prefix = *p;
264         ++p;
265     }
266     else if (p != e && *p == 'U')
267     {
268         prefix = *p;
269         ++p;
270     }
271     else if (p != e && *p == 'L')
272     {
273         prefix = *p;
274         ++p;
275     }
276     if (p != e && *p == '\'')
277     {
278         ++p;
279         if (p != e && *p == '\\')
280         {
281             ParseEscape(fileNametokenpevalue);
282         }
283         else if (p != e)
284         {
285             value = *p;
286             ++p;
287         }
288         if (p != e && *p == '\'')
289         {
290             ++p;
291         }
292         if (p != e)
293         {
294             throw std::runtime_error("invalid character literal in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
295         }
296     }
297     else
298     {
299         throw std::runtime_error("invalid character literal in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
300     }
301 }
302 
303 void ParseStringLiteral(const std::string& fileNameconst soulng::lexer::Token& tokenstd::u32string& encodingPrefixstd::u32string& value)
304 {
305     encodingPrefix.clear();
306     value.clear();
307     const char32_t* p = token.match.begin;
308     const char32_t* e = token.match.end;
309     if (p != e && *p == 'u')
310     {
311         ++p;
312         if (p != e && *p == '8')
313         {
314             ++p;
315             encodingPrefix = U"u8";
316         }
317         else
318         {
319             encodingPrefix = U"u";
320         }
321     }
322     else if (p != e && *p == 'U')
323     {
324         ++p;
325         encodingPrefix = U"U";
326     }
327     else if (p != e && *p == 'L')
328     {
329         ++p;
330         encodingPrefix = U"L";
331     }
332     if (p != e && *p == 'R')
333     {
334         ++p;
335         if (p != e && *p == '"')
336         {
337             ++p;
338             while (p != e && *p != '"')
339             {
340                 value.append(1*p);
341                 ++p;
342             }
343             if (p != e && *p == '"')
344             {
345                 ++p;
346             }
347             if (p != e)
348             {
349                 throw std::runtime_error("invalid string literal in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
350             }
351         }
352         else
353         {
354             throw std::runtime_error("invalid string literal in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
355         }
356     }
357     else if (p != e && *p == '"')
358     {
359         ++p;
360         while (p != e && *p != '"')
361         {
362             if (*p == '\\')
363             {
364                 char32_t c = '\0';
365                 ParseEscape(fileNametokenpec);
366                 value.append(1c);
367             }
368             else
369             {
370                 value.append(1*p);
371                 ++p;
372             }
373         }
374         if (p != e && *p == '"')
375         {
376             ++p;
377         }
378         if (p != e)
379         {
380             throw std::runtime_error("invalid string literal in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
381         }
382     }
383     else
384     {
385         throw std::runtime_error("invalid string literal in '" + fileName + "' at line " + std::to_string(token.line) + ": " + ToUtf8(token.match.ToString()));
386     }
387 }
388 
389 } } // namespace sngcpp::cppparser