1 // =================================
  2 // Copyright (c) 2024 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 
  8 namespace System.XPath
  9 {
 10     [nodiscard]
 11     public Result<bool> ParseHexChar(const string& fileNameuchar& valueconst uchar*& pconst uchar* econst System.Lex.Token& token)
 12     {
 13         if (p != e)
 14         {
 15             bool notHex = false;
 16             uchar c = *p;
 17             switch (c)
 18             {
 19                 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
 20                 {
 21                     value = cast<uchar>(16 * cast<int>(value) + cast<int>(c) - cast<int>('0'));
 22                     break;
 23                 }
 24                 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
 25                 {
 26                     value = cast<uchar>(16 * cast<int>(value) + 10 + cast<int>(c) - cast<int>('A'));
 27                     break;
 28                 }
 29                 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
 30                 {
 31                     value = cast<uchar>(16 * cast<int>(value) + 10 + cast<int>(c) - cast<int>('a'));
 32                     break;
 33                 }
 34                 default:
 35                 {
 36                     notHex = true;
 37                     break;
 38                 }
 39             }
 40             if (notHex)
 41             {
 42                 string errorMessage = "hex character expected at " + fileName + ":" + ToString(token.line);
 43                 int errorId = AllocateError(errorMessage);
 44                 return Result<bool>(ErrorId(errorId));
 45             }
 46             ++p;
 47         }
 48         else
 49         {
 50             string errorMessage = "hex character expected at " + fileName + ":" + ToString(token.line);
 51             int errorId = AllocateError(errorMessage);
 52             return Result<bool>(ErrorId(errorId));
 53         }
 54         return Result<bool>(true);
 55     }
 56 
 57     [nodiscard]
 58     public Result<uchar> ParseEscape(const string& fileNameconst uchar*& pconst uchar* econst System.Lex.Token& token)
 59     {
 60         uchar value;
 61         if (p != e && (*p == 'x' || *p == 'X'))
 62         {
 63             ++p;
 64             while (p != e && ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')))
 65             {
 66                 auto hexCharResult = ParseHexChar(fileNamevaluepetoken);
 67                 if (hexCharResult.Error())
 68                 {
 69                     return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
 70                 }
 71             }
 72         }
 73         else if (p != e && (*p == 'd' || *p == 'D'))
 74         {
 75             ++p;
 76             while (p != e && *p >= '0' && *p <= '9')
 77             {
 78                 value = cast<uchar>(10 * cast<int>(value) + (cast<int>(*p) - cast<int>('0')));
 79                 ++p;
 80             }
 81         }
 82         else if (p != e && (*p >= '0' && *p <= '7'))
 83         {
 84             while (p != e && *p >= '0' && *p <= '7')
 85             {
 86                 value = cast<uchar>(8 * cast<int>(value) + (cast<int>(*p) - cast<int>('0')));
 87                 ++p;
 88             }
 89         }
 90         else if (p != e && *p == 'u')
 91         {
 92             ++p;
 93             auto hexCharResult = ParseHexChar(fileNamevaluepetoken);
 94             if (hexCharResult.Error())
 95             {
 96                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
 97             }
 98             hexCharResult = ParseHexChar(fileNamevaluepetoken);
 99             if (hexCharResult.Error())
100             {
101                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
102             }
103             hexCharResult = ParseHexChar(fileNamevaluepetoken);
104             if (hexCharResult.Error())
105             {
106                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
107             }
108             hexCharResult = ParseHexChar(fileNamevaluepetoken);
109             if (hexCharResult.Error())
110             {
111                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
112             }
113         }
114         else if (p != e && *p == 'U')
115         {
116             ++p;
117             auto hexCharResult = ParseHexChar(fileNamevaluepetoken);
118             if (hexCharResult.Error())
119             {
120                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
121             }
122             hexCharResult = ParseHexChar(fileNamevaluepetoken);
123             if (hexCharResult.Error())
124             {
125                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
126             }
127             hexCharResult = ParseHexChar(fileNamevaluepetoken);
128             if (hexCharResult.Error())
129             {
130                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
131             }
132             hexCharResult = ParseHexChar(fileNamevaluepetoken);
133             if (hexCharResult.Error())
134             {
135                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
136             }
137             hexCharResult = ParseHexChar(fileNamevaluepetoken);
138             if (hexCharResult.Error())
139             {
140                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
141             }
142             hexCharResult = ParseHexChar(fileNamevaluepetoken);
143             if (hexCharResult.Error())
144             {
145                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
146             }
147             hexCharResult = ParseHexChar(fileNamevaluepetoken);
148             if (hexCharResult.Error())
149             {
150                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
151             }
152             hexCharResult = ParseHexChar(fileNamevaluepetoken);
153             if (hexCharResult.Error())
154             {
155                 return Result<uchar>(ErrorId(hexCharResult.GetErrorId()));
156             }
157         }
158         else if (p != e)
159         {
160             uchar c = *p;
161             switch (c)
162             {
163                 case 'a': value = '\a'; break;
164                 case 'b': value = '\b'; break;
165                 case 'f': value = '\f'; break;
166                 case 'n': value = '\n'; break;
167                 case 'r': value = '\r'; break;
168                 case 't': value = '\t'; break;
169                 case 'v': value = '\v'; break;
170                 default: value = c; break;
171             }
172             ++p;
173         }
174         return Result<uchar>(value);
175     }
176 
177     [nodiscard]
178     public Result<string> ParseDQString(const string& fileNameconst System.Lex.Token& token)
179     {
180         ustring stringLiteral;
181         uchar* p = token.match.begin;
182         uchar* e = token.match.end;
183         if (p != e && *p == '\"')
184         {
185             ++p;
186             while (p != e && *p != '\r' && *p != '\n' && *p != '\"')
187             {
188                 if (*p == '\\')
189                 {
190                     ++p;
191                     auto escape = ParseEscape(fileNamepetoken);
192                     if (escape.Error())
193                     {
194                         return Result<string>(ErrorId(escape.GetErrorId()));
195                     }
196                     stringLiteral.Append(escape.Value());
197                 }
198                 else
199                 {
200                     stringLiteral.Append(*p);
201                     ++p;
202                 }
203             }
204             if (p != e && *p == '\"')
205             {
206                 ++p;
207             }
208             if (p != e)
209             {
210                 string matchValue;
211                 auto utf8Result = ToUtf8(token.match.ToString());
212                 if (!utf8Result.Error())
213                 {
214                     matchValue = utf8Result.Value();
215                 }
216                 int errorId = AllocateError("invalid string literal at " + fileName + ":" + ToString(token.line) + ": " + matchValue);
217                 return Result<string>(ErrorId(errorId));
218             }
219         }
220         else
221         {
222             string matchValue;
223             auto utf8Result = ToUtf8(token.match.ToString());
224             if (!utf8Result.Error())
225             {
226                 matchValue = utf8Result.Value();
227             }
228             int errorId = AllocateError("invalid string literal at " + fileName + ":" + ToString(token.line) + ": " + matchValue);
229             return Result<string>(ErrorId(errorId));
230         }
231         auto utf8Result = ToUtf8(stringLiteral);
232         if (utf8Result.Error())
233         {
234             return Result<string>(ErrorId(utf8Result.GetErrorId()));
235         }
236         return Result<string>(utf8Result.Value());
237     }
238 
239     [nodiscard]
240     public Result<string> ParseSQString(const string& fileNameconst System.Lex.Token& token)
241     {
242         ustring stringLiteral;
243         uchar* p = token.match.begin;
244         uchar* e = token.match.end;
245         if (p != e && *p == '\'')
246         {
247             ++p;
248             while (p != e && *p != '\r' && *p != '\n' && *p != '\'')
249             {
250                 if (*p == '\\')
251                 {
252                     ++p;
253                     auto escape = ParseEscape(fileNamepetoken);
254                     if (escape.Error())
255                     {
256                         return Result<string>(ErrorId(escape.GetErrorId()));
257                     }
258                     stringLiteral.Append(escape.Value());
259                 }
260                 else
261                 {
262                     stringLiteral.Append(*p);
263                     ++p;
264                 }
265             }
266             if (p != e && *p == '\'')
267             {
268                 ++p;
269             }
270             if (p != e)
271             {
272                 string matchValue;
273                 auto utf8Result = ToUtf8(token.match.ToString());
274                 if (!utf8Result.Error())
275                 {
276                     matchValue = utf8Result.Value();
277                 }
278                 int errorId = AllocateError("invalid string literal at " + fileName + ":" + ToString(token.line) + ": " + matchValue);
279                 return Result<string>(ErrorId(errorId));
280             }
281         }
282         else
283         {
284             string matchValue;
285             auto utf8Result = ToUtf8(token.match.ToString());
286             if (!utf8Result.Error())
287             {
288                 matchValue = utf8Result.Value();
289             }
290             int errorId = AllocateError("invalid string literal at " + fileName + ":" + ToString(token.line) + ": " + matchValue);
291             return Result<string>(ErrorId(errorId));
292         }
293         auto utf8Result = ToUtf8(stringLiteral);
294         if (utf8Result.Error())
295         {
296             return Result<string>(ErrorId(utf8Result.GetErrorId()));
297         }
298         return Result<string>(utf8Result.Value());
299     }