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