Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
SelectParser.cs
1using System.Collections.Generic;
2using System.Text;
10
12{
16 public class SelectParser : IKeyWord
17 {
21 public SelectParser()
22 {
23 }
24
28 public string KeyWord => "SELECT";
29
33 public string[] Aliases => null;
34
38 public string[] InternalKeywords => new string[]
39 {
40 "AS",
41 "FROM",
42 "INNER",
43 "OUTER",
44 "LEFT",
45 "RIGHT",
46 "JOIN",
47 "FULL",
48 "WHERE",
49 "GROUP",
50 "BY",
51 "HAVING",
52 "ORDER",
53 "TOP",
54 "OFFSET",
55 "ASC",
56 "DESC",
57 "DISTINCT",
58 "GENERIC"
59 };
60
67 public bool TryParse(ScriptParser Parser, out ScriptNode Result)
68 {
69 Result = null;
70
71 List<ScriptNode> Columns;
72 List<ScriptNode> ColumnNames;
73 ScriptNode Top = null;
74 string s;
75 bool Distinct = false;
76 bool Generic = false;
77
78 s = Parser.PeekNextToken().ToUpper();
79 if (string.IsNullOrEmpty(s))
80 return false;
81
82 while (s == "TOP" || s == "DISTINCT" || s == "GENERIC")
83 {
84 switch (s)
85 {
86 case "TOP":
87 Parser.NextToken();
88 Top = Parser.ParseNoWhiteSpace();
89 break;
90
91 case "DISTINCT":
92 Parser.NextToken();
93 Distinct = true;
94 break;
95
96 case "GENERIC":
97 Parser.NextToken();
98 Generic = true;
99 break;
100 }
101
102 s = Parser.PeekNextToken().ToUpper();
103 if (string.IsNullOrEmpty(s))
104 return false;
105 }
106
107 if (s == "*")
108 {
109 Parser.NextToken();
110 Columns = null;
111 ColumnNames = null;
112
113 if (Top is null && !Generic)
114 {
115 Parser.SkipWhiteSpace();
116 if (Parser.PeekNextChar() == '{')
117 {
119
120 if (Distinct)
121 SparqlParser = new SparqlParser("SELECT DISTINCT * ");
122 else
123 SparqlParser = new SparqlParser("SELECT * ");
124
125 return SparqlParser.TryParse(Parser, out Result);
126 }
127 }
128 }
129 else if (s == "?")
130 {
131 if (!(Top is null) || Generic)
132 return false;
133
135
136 if (Distinct)
137 SparqlParser = new SparqlParser("SELECT DISTINCT ?");
138 else
139 SparqlParser = new SparqlParser("SELECT ?");
140
141 return SparqlParser.TryParse(Parser, out Result);
142 }
143 else
144 {
145 Columns = new List<ScriptNode>();
146 ColumnNames = new List<ScriptNode>();
147
148 ScriptNode Node;
149 ScriptNode Name;
150
151 while (true)
152 {
153 if (s == "/" || s == "." || s == "@")
154 Node = ParseXPath(Parser, true);
155 else
156 {
157 Node = Parser.ParseNoWhiteSpace();
158
159 if (Node is XPath XPath)
160 XPath.ExtractValue = true;
161 }
162
163 Name = null;
164 Parser.SkipWhiteSpace();
165
166 s = Parser.PeekNextToken().ToUpper();
167 if (!string.IsNullOrEmpty(s) && s != "," && s != "FROM")
168 {
169 if (s == "AS")
170 Parser.NextToken();
171
172 Name = Parser.ParseNoWhiteSpace();
173 s = Parser.PeekNextToken();
174 }
175 else if (Node is VariableReference Ref)
176 Name = new ConstantElement(new StringValue(Ref.VariableName), Node.Start, Node.Length, Node.Expression);
177 else if (Node is NamedMember NamedMember)
178 Name = new ConstantElement(new StringValue(NamedMember.Name), Node.Start, Node.Length, Node.Expression);
179
180 Columns.Add(Node);
181 ColumnNames.Add(Name);
182
183 if (s != ",")
184 break;
185
186 Parser.NextToken();
187 s = Parser.PeekNextToken();
188 }
189 }
190
191 s = Parser.NextToken().ToUpper();
192 if (s != "FROM")
193 return false;
194
195 if (!TryParseSources(Parser, out SourceDefinition Source))
196 return false;
197
198 ScriptNode Where = null;
199
200 s = Parser.PeekNextToken().ToUpper();
201 if (s == "WHERE")
202 {
203 Parser.NextToken();
204
205 s = Parser.PeekNextToken();
206 if (s == "/" || s == "." || s == "@")
207 Where = ParseXPath(Parser, false);
208 else
209 Where = Parser.ParseOrs();
210
211 s = Parser.PeekNextToken().ToUpper();
212 }
213
214 List<ScriptNode> GroupBy = null;
215 List<ScriptNode> GroupByNames = null;
216 ScriptNode Having = null;
217
218 if (s == "GROUP")
219 {
220 Parser.NextToken();
221 if (string.Compare(Parser.NextToken(), "BY", true) != 0)
222 return false;
223
224 GroupBy = new List<ScriptNode>();
225 GroupByNames = new List<ScriptNode>();
226
227 while (true)
228 {
229 ScriptNode Node = Parser.ParseNoWhiteSpace();
230 ScriptNode Name;
231
232 Parser.SkipWhiteSpace();
233
234 s = Parser.PeekNextToken().ToUpper();
235 if (!string.IsNullOrEmpty(s) && s != "," && s != ";" && s != ")" && s != "]" && s != "}" && s != "HAVING" && s != "ORDER" && s != "OFFSET")
236 {
237 if (s == "AS")
238 Parser.NextToken();
239
240 Name = Parser.ParseNoWhiteSpace();
241 s = Parser.PeekNextToken().ToUpper();
242 }
243 else
244 Name = null;
245
246 if (Name is null)
247 {
248 if (Node is VariableReference Ref)
249 Name = new ConstantElement(new StringValue(Ref.VariableName), Node.Start, Node.Length, Node.Expression);
250 else if (Node is NamedMember NamedMember)
251 Name = new ConstantElement(new StringValue(NamedMember.Name), Node.Start, Node.Length, Node.Expression);
252 }
253
254 GroupBy.Add(Node);
255 GroupByNames.Add(Name);
256
257 if (s != ",")
258 break;
259
260 Parser.NextToken();
261 }
262
263 if (s == "HAVING")
264 {
265 Parser.NextToken();
266 Having = Parser.ParseOrs();
267 s = Parser.PeekNextToken().ToUpper();
268 }
269 }
270 else if (!(Columns is null))
271 {
272 bool ImplicitGrouping = false;
273
274 foreach (ScriptNode Column in Columns)
275 {
276 if (this.ContainsVectorFunction(Column))
277 {
278 ImplicitGrouping = true;
279 break;
280 }
281 }
282
283 if (ImplicitGrouping)
284 {
285 GroupBy = new List<ScriptNode>();
286 GroupByNames = new List<ScriptNode>();
287 }
288 }
289
290 List<KeyValuePair<ScriptNode, bool>> OrderBy = null;
291
292 if (s == "ORDER")
293 {
294 Parser.NextToken();
295 if (string.Compare(Parser.NextToken(), "BY", true) != 0)
296 return false;
297
298 OrderBy = new List<KeyValuePair<ScriptNode, bool>>();
299
300 while (true)
301 {
302 ScriptNode Node = Parser.ParseNoWhiteSpace();
303
304 s = Parser.PeekNextToken().ToUpper();
305 if (s == "ASC")
306 {
307 Parser.NextToken();
308 OrderBy.Add(new KeyValuePair<ScriptNode, bool>(Node, true));
309 s = Parser.PeekNextToken().ToUpper();
310 }
311 else if (s == "DESC")
312 {
313 Parser.NextToken();
314 OrderBy.Add(new KeyValuePair<ScriptNode, bool>(Node, false));
315 s = Parser.PeekNextToken().ToUpper();
316 }
317 else
318 OrderBy.Add(new KeyValuePair<ScriptNode, bool>(Node, true));
319
320 if (s != ",")
321 break;
322
323 Parser.NextToken();
324 }
325 }
326
327 ScriptNode Offset = null;
328
329 if (s == "OFFSET")
330 {
331 Parser.NextToken();
332 Offset = Parser.ParseNoWhiteSpace();
333 }
334
335 Result = new Select(Columns?.ToArray(), ColumnNames?.ToArray(), Source, Where, GroupBy?.ToArray(),
336 GroupByNames?.ToArray(), Having, OrderBy?.ToArray(), Top, Offset, Distinct, Generic,
337 Parser.Start, Parser.Length, Parser.Expression);
338
339 return true;
340 }
341
342 private static XPath ParseXPath(ScriptParser Parser, bool ExtractValue)
343 {
344 Parser.SkipWhiteSpace();
345
346 StringBuilder sb = new StringBuilder();
347 int Start = Parser.Position;
348 char ch;
349
350 while ((ch = Parser.PeekNextChar()) > 32 && ch != 160 && ch != ',' && ch != ';')
351 sb.Append(Parser.NextChar());
352
353 return new XPath(sb.ToString(), ExtractValue, Start, Parser.Position - Start, Parser.Expression);
354 }
355
356 internal static bool TryParseSources(ScriptParser Parser, out SourceDefinition Source)
357 {
358 if (!TryParseSource(Parser, out Source))
359 return false;
360
361 while (true)
362 {
363 string s = Parser.PeekNextToken().ToUpper();
364
365 switch (s)
366 {
367 case ",":
368 Parser.NextToken();
369 if (!TryParseSource(Parser, out SourceDefinition Source2))
370 return false;
371
372 Source = new CrossJoin(Source, Source2, Source.Start, Parser.Position - Source.Start, Parser.Expression);
373 break;
374
375 case "INNER":
376 case "JOIN":
377 Parser.NextToken();
378
379 if (s == "INNER")
380 {
381 if (string.Compare(Parser.NextToken(), "JOIN", true) != 0)
382 return false;
383 }
384
385 if (!TryParseSource(Parser, out Source2))
386 return false;
387
388 ScriptNode Conditions = ParseJoinConditions(Parser);
389 Source = new InnerJoin(Source, Source2, Conditions, Source.Start, Parser.Position - Source.Start, Parser.Expression);
390 break;
391
392 case "LEFT":
393 Parser.NextToken();
394
395 switch (Parser.NextToken().ToUpper())
396 {
397 case "JOIN":
398 break;
399
400 case "OUTER":
401 if (string.Compare(Parser.NextToken(), "JOIN", true) != 0)
402 return false;
403 break;
404
405 default:
406 return false;
407 }
408
409 if (!TryParseSource(Parser, out Source2))
410 return false;
411
412 Conditions = ParseJoinConditions(Parser);
413 Source = new LeftOuterJoin(Source, Source2, Conditions, Source.Start, Parser.Position - Source.Start, Parser.Expression);
414 break;
415
416 case "RIGHT":
417 Parser.NextToken();
418
419 switch (Parser.NextToken().ToUpper())
420 {
421 case "JOIN":
422 break;
423
424 case "OUTER":
425 if (string.Compare(Parser.NextToken(), "JOIN", true) != 0)
426 return false;
427 break;
428
429 default:
430 return false;
431 }
432
433 if (!TryParseSource(Parser, out Source2))
434 return false;
435
436 Conditions = ParseJoinConditions(Parser);
437 Source = new RightOuterJoin(Source, Source2, Conditions, Source.Start, Parser.Position - Source.Start, Parser.Expression);
438 break;
439
440 case "FULL":
441 Parser.NextToken();
442
443 switch (Parser.NextToken().ToUpper())
444 {
445 case "JOIN":
446 break;
447
448 case "OUTER":
449 if (string.Compare(Parser.NextToken(), "JOIN", true) != 0)
450 return false;
451 break;
452
453 default:
454 return false;
455 }
456
457 if (!TryParseSource(Parser, out Source2))
458 return false;
459
460 Conditions = ParseJoinConditions(Parser);
461 Source = new FullOuterJoin(Source, Source2, Conditions, Source.Start, Parser.Position - Source.Start, Parser.Expression);
462 break;
463
464 case "OUTER":
465 Parser.NextToken();
466
467 if (string.Compare(Parser.NextToken(), "JOIN", true) != 0)
468 return false;
469
470 if (!TryParseSource(Parser, out Source2))
471 return false;
472
473 Conditions = ParseJoinConditions(Parser);
474 Source = new FullOuterJoin(Source, Source2, Conditions, Source.Start, Parser.Position - Source.Start, Parser.Expression);
475 break;
476
477 default:
478 return true;
479 }
480 }
481 }
482
483 private static ScriptNode ParseJoinConditions(ScriptParser Parser)
484 {
485 if (string.Compare(Parser.PeekNextToken(), "ON", true) != 0)
486 return null;
487
488 Parser.NextToken();
489
490 return Parser.ParseOrs();
491 }
492
493 internal static bool TryParseSource(ScriptParser Parser, out SourceDefinition Source)
494 {
495 Parser.SkipWhiteSpace();
496
497 int Start = Parser.Position;
498 ScriptNode Node = Parser.ParseNoWhiteSpace();
499 ScriptNode Name = null;
500 string s;
501
502 Parser.SkipWhiteSpace();
503
504 s = Parser.PeekNextToken().ToUpper();
505 if (!string.IsNullOrEmpty(s) &&
506 IsAlias(s) &&
507 s != "INNER" &&
508 s != "OUTER" &&
509 s != "LEFT" &&
510 s != "RIGHT" &&
511 s != "FULL" &&
512 s != "JOIN" &&
513 s != "WHERE" &&
514 s != "GROUP" &&
515 s != "ORDER" &&
516 s != "OFFSET" &&
517 s != "ON" &&
518 s != "SET" &&
519 s != "SELECT" &&
520 s != "OBJECT" &&
521 s != "OBJECTS" &&
522 s != "NEW" &&
523 s != "UPDATE" &&
524 s != "DELETE" &&
525 s != "TO")
526 {
527 if (s == "AS")
528 Parser.NextToken();
529
530 Name = Parser.ParseNoWhiteSpace();
531 }
532 else if (Node is VariableReference Ref)
533 Name = new ConstantElement(new StringValue(Ref.VariableName), Node.Start, Node.Length, Node.Expression);
534
535 Source = new SourceReference(Node, Name, Start, Parser.Position - Start, Parser.Expression);
536
537 return true;
538 }
539
540 internal static bool IsAlias(string s)
541 {
542 foreach (char ch in s)
543 {
544 if (!char.IsLetterOrDigit(ch) && ch != '_')
545 return false;
546 }
547
548 return true;
549 }
550
551 private bool ContainsVectorFunction(ScriptNode Node)
552 {
553 if (!this.SearchForVectorFunction(Node, out _, null))
554 return true;
555
556 return !(Node?.ForAllChildNodes(this.SearchForVectorFunction, null, SearchMethod.TreeOrder) ?? true);
557 }
558
559 private bool SearchForVectorFunction(ScriptNode Node, out ScriptNode NewNode, object State)
560 {
561 NewNode = null;
562
563 if (Node is Function)
564 return !(Node is FunctionOneVectorVariable || Node is Count);
565
566 return true;
567 }
568
569 }
570}
Represents a constant element value.
Base class for all funcions.
Definition: Function.cs:9
Base class for funcions of one vector variable.
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
bool ForAllChildNodes(ScriptNodeEventHandler Callback, object State, bool DepthFirst)
Calls the callback method for all child nodes.
Definition: ScriptNode.cs:243
int Length
Length of expression covered by node.
Definition: ScriptNode.cs:101
Expression Expression
Expression of which the node is a part.
Definition: ScriptNode.cs:177
int Start
Start position in script expression.
Definition: ScriptNode.cs:92
Script parser, for custom parsers.
Definition: ScriptParser.cs:10
string PeekNextToken()
Returns the next token to be parsed, without moving the position forward. If at the end of the expres...
string NextToken()
Returns the next token to be parsed, and moves the position forward correspondingly....
int Length
Length of script parsed
Definition: ScriptParser.cs:33
ScriptNode ParseOrs()
Parses ORs.
int Start
Start position in expression
Definition: ScriptParser.cs:28
int Position
Current parsing position.
Definition: ScriptParser.cs:38
Expression Expression
Expression being parsed.
Definition: ScriptParser.cs:43
char PeekNextChar()
Returns the next character to be parsed, without moving the position forward one character....
void SkipWhiteSpace()
If current position is whitespace, moves the current position forward to the first non-whitespace cha...
char NextChar()
Returns the next character to be parsed, and moves the position forward one character....
Represents a variable reference.
Node repesenting an XPath expression
Definition: XPath.cs:16
bool TryParse(ScriptParser Parser, out ScriptNode Result)
Tries to parse a script node.
string[] InternalKeywords
Any keywords used internally by the custom parser.
Definition: SelectParser.cs:38
string[] Aliases
Keyword aliases, if available, null if none.
Definition: SelectParser.cs:33
string KeyWord
Keyword associated with custom parser.
Definition: SelectParser.cs:28
bool TryParse(ScriptParser Parser, out ScriptNode Result)
Tries to parse a script node.
Definition: SelectParser.cs:67
Executes a SELECT statement against the object database.
Definition: Select.cs:20
Abstract base class for source definitions
CROSS JOIN of two source definitions.
Definition: CrossJoin.cs:11
FULL [OUTER] JOIN of two source definitions.
[INNER] JOIN of two source definitions.
Definition: InnerJoin.cs:12
LEFT [OUTER] JOIN of two source definitions.
RIGHT [OUTER] JOIN of two source definitions.
Interface for keywords with custom parsing.
Definition: IKeyWord.cs:9
SearchMethod
Method to traverse the expression structure
Definition: ScriptNode.cs:38