Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
SparqlParser.cs
1using System;
3using System.Numerics;
4using System.Text;
5using Waher.Content;
33
35{
39 public class SparqlParser : IKeyWord
40 {
44 public static readonly SparqlParser RefInstance = new SparqlParser(string.Empty);
45
46 private static readonly Dictionary<string, IExtensionFunction> functionsPerUri = new Dictionary<string, IExtensionFunction>(StringComparer.CurrentCultureIgnoreCase);
47
48 static SparqlParser()
49 {
50 Types.OnInvalidated += (Sender, e) =>
51 {
52 lock (functionsPerUri)
53 {
54 functionsPerUri.Clear();
55 }
56 };
57 }
58
59 private readonly string preamble;
60 private readonly int preambleLen;
61 private readonly Dictionary<string, string> namespaces = new Dictionary<string, string>()
62 {
63 { "xsd", XmlSchema.Namespace },
64 { "rdf", Rdf.Namespace },
65 { "rdfs", RdfSchema.Namespace },
66 { "fn", "http://www.w3.org/2005/xpath-functions#" },
67 { "sfn", "http://www.w3.org/ns/sparql#" }
68 };
69 private QueryType queryType;
70 private SparqlRegularPattern currentRegularPattern = null;
71 private ISparqlPattern currentPattern = null;
72 private int preamblePos;
73 private System.Uri baseUri = null;
74 private int blankNodeIndex = 0;
75
80 public SparqlParser(string Preamble)
81 {
82 this.queryType = QueryType.Select;
83 this.preamble = Preamble;
84 this.preambleLen = Preamble?.Length ?? 0;
85 this.preamblePos = 0;
86 }
87
91 public string KeyWord => string.Empty;
92
96 public string[] Aliases => null;
97
101 public string[] InternalKeywords => new string[]
102 {
103 "DISTINCT",
104 "REDUCED",
105 "FROM",
106 "AS",
107 "NAMED",
108 "WHERE",
109 "OPTIONAL",
110 "UNION",
111 "MINUS",
112 "ORDER",
113 "BY",
114 "ASK",
115 "CONSTRUCT",
116 "ASC",
117 "DESC",
118 "VALUES",
119 "GRAPH",
120 "SERVICE",
121 "UNDEF",
122 "LIMIT",
123 "OFFSET"
124 };
125
132 public bool TryParse(ScriptParser Parser, out ScriptNode Result)
133 {
134 return this.TryParse(Parser, Parser.Start, out Result);
135 }
136
144 public bool TryParse(ScriptParser Parser, int Start, out ScriptNode Result)
145 {
146 Result = null;
147
148 ScriptNode Node;
149 string s;
150 bool Distinct = false;
151 bool Reduced = false;
152 char ch;
153
154 s = this.PeekNextToken(Parser).ToUpper();
155 if (string.IsNullOrEmpty(s))
156 return false;
157
158 while (s != "SELECT" && s != "ASK" && s != "CONSTRUCT")
159 {
160 switch (s)
161 {
162 case "BASE":
163 this.NextToken(Parser);
164 this.SkipWhiteSpace(Parser);
165 if (this.preamblePos < this.preambleLen)
166 return false;
167
168 ch = Parser.NextNonWhitespaceChar();
169 if (ch != '<')
170 throw Parser.SyntaxError("Expected <");
171
172 this.baseUri = this.ParseUri(Parser).Uri;
173
174 break;
175
176 case "PREFIX":
177 this.NextToken(Parser);
178 this.SkipWhiteSpace(Parser);
179 if (this.preamblePos < this.preambleLen)
180 return false;
181
182 Parser.SkipWhiteSpace();
183
184 s = this.ParseName(Parser);
185
186 if (Parser.NextNonWhitespaceChar() != ':')
187 {
188 Parser.UndoChar();
189 throw Parser.SyntaxError("Expected :");
190 }
191
192 if (Parser.NextNonWhitespaceChar() != '<')
193 {
194 Parser.UndoChar();
195 throw Parser.SyntaxError("Expected <");
196 }
197
198 this.namespaces[s] = this.ParseUri(Parser).Uri.ToString();
199 break;
200
201 default:
202 return false;
203 }
204
205 s = Parser.PeekNextToken().ToUpper();
206 if (string.IsNullOrEmpty(s))
207 return false;
208 }
209
210 ChunkedList<ScriptNode> Columns = null;
211 ChunkedList<ScriptNode> ColumnNames = null;
212 ChunkedList<ScriptNode> GroupBy = null;
213 ChunkedList<ScriptNode> GroupByNames = null;
215 SparqlRegularPattern Construct = null;
216 ISparqlPattern Where;
217 ScriptNode Having;
219 Dictionary<UriNode, ISemanticCube> NamedGraphs = null;
220
221 switch (s)
222 {
223 case "ASK":
224 this.queryType = QueryType.Ask;
225 this.NextToken(Parser);
226 s = this.PeekNextToken(Parser).ToUpper();
227 if (string.IsNullOrEmpty(s))
228 return false;
229 break;
230
231 case "CONSTRUCT":
232 this.queryType = QueryType.Construct;
233 this.NextToken(Parser);
234
235 s = Parser.PeekNextToken().ToUpper();
236 if (s == "WHERE")
237 Construct = null;
238 else
239 {
240 ISparqlPattern Pattern = this.ParsePattern(Parser)
241 ?? throw Parser.SyntaxError("Expected pattern.");
242
243 if (!(Pattern is SparqlRegularPattern RegularPattern))
244 throw Parser.SyntaxError("Expected regular pattern.");
245
246 Construct = RegularPattern;
247 if (!(Construct.BoundVariables is null))
248 throw Parser.SyntaxError("Bound variables not permitted in construct statement.");
249
250 if (!(Construct.Filter is null))
251 throw Parser.SyntaxError("Filters not permitted in construct statement.");
252
253 s = Parser.PeekNextToken().ToUpper();
254 }
255 break;
256
257 case "SELECT":
258 this.queryType = QueryType.Select;
259 this.NextToken(Parser);
260 s = this.PeekNextToken(Parser).ToUpper();
261 if (string.IsNullOrEmpty(s))
262 return false;
263
264 switch (s)
265 {
266 case "DISTINCT":
267 this.NextToken(Parser);
268 Distinct = true;
269
270 s = this.PeekNextToken(Parser).ToUpper();
271 break;
272
273 case "REDUCED":
274 this.NextToken(Parser);
275 Reduced = true;
276
277 s = this.PeekNextToken(Parser).ToUpper();
278 break;
279 }
280
281 if (s == "*")
282 {
283 this.NextToken(Parser);
284 s = this.PeekNextToken(Parser).ToUpper();
285
286 Columns = null;
287 ColumnNames = null;
288 }
289 else
290 {
291 Columns = new ChunkedList<ScriptNode>();
292 ColumnNames = new ChunkedList<ScriptNode>();
293
294 while (!string.IsNullOrEmpty(s) && s != "WHERE" && s != "FROM" && s != "{")
295 {
296 Node = this.ParseNamedExpression(Parser);
297 if (Node is NamedNode NamedNode)
298 {
299 Columns.Add(NamedNode.LeftOperand);
300 ColumnNames.Add(NamedNode.RightOperand);
301 }
302 else
303 {
304 Columns.Add(Node);
305 ColumnNames.Add(null);
306 }
307
308 s = Parser.PeekNextToken().ToUpper();
309 }
310 }
311 break;
312
313 default:
314 throw Parser.SyntaxError("Expected SELECT or ASK.");
315 }
316
317 From = null;
318
319 while (s == "FROM")
320 {
321 if (From is null)
322 From = new ChunkedList<ScriptNode>();
323
324 Parser.NextToken();
325 Parser.SkipWhiteSpace();
326
327 int Start2 = Parser.Position;
328
329 switch (Parser.PeekNextChar())
330 {
331 case '<':
332 Parser.NextChar();
333 UriNode FromUri = this.ParseUri(Parser);
334
335 From.Add(new ConstantElement(FromUri, Start2, Parser.Position - Start2, Parser.Expression));
336 break;
337
338 case 'n':
339 case 'N':
340 if (string.Compare(Parser.PeekNextToken(), "NAMED", true) == 0)
341 {
342 Parser.NextToken();
343 Parser.SkipWhiteSpace();
344
345 if (Parser.PeekNextChar() == '<')
346 {
347 Parser.NextChar();
348 FromUri = this.ParseUri(Parser);
349 }
350 else
351 {
352 Node = Parser.ParseObject();
353
354 if (Node is VariableReference Ref3 && this.PeekNextChar(Parser) == ':')
355 {
356 Parser.NextChar();
357 FromUri = this.ParsePrefixedToken(Parser, Ref3.VariableName);
358 }
359 else
360 throw Parser.SyntaxError("Aboslute or prefixed URI expected.");
361 }
362
363 if (NamedGraphs is null)
364 NamedGraphs = new Dictionary<UriNode, ISemanticCube>();
365
366 NamedGraphs[FromUri] = null;
367 }
368 else
369 {
370 Node = Parser.ParseObject();
371
372 this.SkipWhiteSpace(Parser);
373 if (Node is VariableReference Ref2 && this.PeekNextChar(Parser) == ':')
374 {
375 Parser.NextChar();
376 FromUri = this.ParsePrefixedToken(Parser, Ref2.VariableName);
377 From.Add(new ConstantElement(FromUri, Start2, Parser.Position - Start2, Parser.Expression));
378 }
379 else
380 From.Add(Node);
381 }
382 break;
383
384 default:
385 Node = Parser.ParseObject();
386
387 this.SkipWhiteSpace(Parser);
388 if (Node is VariableReference Ref && this.PeekNextChar(Parser) == ':')
389 {
390 Parser.NextChar();
391 FromUri = this.ParsePrefixedToken(Parser, Ref.VariableName);
392 From.Add(new ConstantElement(FromUri, Start2, Parser.Position - Start2, Parser.Expression));
393 }
394 else
395 From.Add(Node);
396 break;
397 }
398
399 s = Parser.PeekNextToken().ToUpper();
400 }
401
402 if (s == "WHERE")
403 {
404 Parser.NextToken();
405 Where = this.ParsePattern(Parser);
406 s = Parser.PeekNextToken().ToUpper();
407 }
408 else if (s == "{")
409 {
410 Where = this.ParsePattern(Parser);
411 s = Parser.PeekNextToken().ToUpper();
412 }
413 else
414 Where = null;
415
416 if (this.queryType == QueryType.Construct && Construct is null)
417 {
418 if (Where is SparqlRegularPattern RegularPattern)
419 Construct = RegularPattern;
420 else
421 throw Parser.SyntaxError("CONSTRUCT WHERE queries require a regular WHERE pattern.");
422 }
423
424 if (s == "VALUES")
425 {
426 Parser.NextToken();
427
428 ValuesPattern Values = this.ParseValues(Parser, out ChunkedList<SemanticQueryTriple> AdditionalTriples);
429 if (!(AdditionalTriples is null))
430 throw Parser.SyntaxError("Blank Nodes not supported in a VALUES statement.");
431
432 if (Where is null)
433 Where = Values;
434 else
435 Where = new IntersectionPattern(Values, Where);
436
437 s = Parser.PeekNextToken().ToUpper();
438 }
439
440 if (s == "GROUP")
441 {
442 Parser.NextToken();
443
444 if (string.Compare(Parser.NextToken(), "BY", true) != 0)
445 throw Parser.SyntaxError("Expected BY");
446
447 GroupBy = new ChunkedList<ScriptNode>();
448 GroupByNames = new ChunkedList<ScriptNode>();
449
450 while (!string.IsNullOrEmpty(s) && s != "HAVING" && s != "ORDER" && s != ";" && s != ")" && s != "}")
451 {
452 Node = this.ParseNamedExpression(Parser);
453 if (Node is NamedNode NamedNode)
454 {
455 GroupBy.Add(NamedNode.LeftOperand);
456 GroupByNames.Add(NamedNode.RightOperand);
457 }
458 else
459 {
460 GroupBy.Add(Node);
461 GroupByNames.Add(null);
462 }
463
464 s = Parser.PeekNextToken().ToUpper();
465 }
466
467 if (s == "HAVING")
468 {
469 Parser.NextToken();
470 Having = this.ParseExpression(Parser, false);
471 s = Parser.PeekNextToken().ToUpper();
472 }
473 else
474 Having = null;
475 }
476 else
477 Having = null;
478
479 if (s == "ORDER")
480 {
481 if (this.queryType == QueryType.Ask)
482 throw Parser.SyntaxError("ORDER BY not expected in ASK queries.");
483
484 Parser.NextToken();
485 s = Parser.NextToken().ToUpper();
486 if (s != "BY")
487 throw Parser.SyntaxError("Expected BY");
488
490
491 while (true)
492 {
493 Node = this.ParseExpression(Parser, true);
494 if (Node is null)
495 break;
496 else if (Node is Asc Asc)
497 OrderBy.Add(new KeyValuePair<ScriptNode, bool>(Asc.Argument, true));
498 else if (Node is Desc Desc)
499 OrderBy.Add(new KeyValuePair<ScriptNode, bool>(Desc.Argument, false));
500 else
501 OrderBy.Add(new KeyValuePair<ScriptNode, bool>(Node, true));
502 }
503
504 s = Parser.PeekNextToken().ToUpper();
505 }
506
507 int? Offset = null;
508 int? Limit = null;
509
510 while (true)
511 {
512 switch (s)
513 {
514 case "LIMIT":
515 Parser.NextToken();
516 Limit = this.ParsePositiveInteger(Parser);
517 break;
518
519 case "OFFSET":
520 Parser.NextToken();
521 Offset = this.ParsePositiveInteger(Parser);
522 break;
523
524 default:
525 Result = new SparqlQuery(this.queryType, Distinct, Reduced, Columns?.ToArray(),
526 ColumnNames?.ToArray(), From?.ToArray(), NamedGraphs, Where,
527 GroupBy?.ToArray(), GroupByNames?.ToArray(), Having, OrderBy?.ToArray(),
528 Limit, Offset, Construct, Start, Parser.Position - Start, Parser.Expression);
529
530 return true;
531 }
532
533 s = Parser.PeekNextToken().ToUpper();
534 }
535 }
536
537 private char PeekNextChar(ScriptParser Parser)
538 {
539 if (this.preamblePos < this.preambleLen)
540 return this.preamble[this.preamblePos];
541 else
542 return Parser.PeekNextChar();
543 }
544
545 private char NextChar(ScriptParser Parser)
546 {
547 if (this.preamblePos < this.preambleLen)
548 return this.preamble[this.preamblePos++];
549 else
550 return Parser.NextChar();
551 }
552
553 private void SkipWhiteSpace(ScriptParser Parser)
554 {
555 char ch = this.PeekNextChar(Parser);
556
557 while (ch != 0 && (ch <= ' ' || ch == 160))
558 {
559 this.NextChar(Parser);
560 ch = this.PeekNextChar(Parser);
561 }
562 }
563
564 private string NextToken(ScriptParser Parser)
565 {
566 this.SkipWhiteSpace(Parser);
567
568 if (this.preamblePos < this.preambleLen)
569 {
570 int Start = this.preamblePos;
571 char ch = this.preamble[this.preamblePos];
572
573 if (char.IsLetter(ch))
574 {
575 while (this.preamblePos < this.preambleLen && char.IsLetterOrDigit(this.preamble[this.preamblePos]))
576 this.preamblePos++;
577 }
578 else if (char.IsDigit(ch))
579 {
580 while (this.preamblePos < this.preambleLen && char.IsDigit(this.preamble[this.preamblePos]))
581 this.preamblePos++;
582 }
583 else if (char.IsSymbol(ch))
584 {
585 while (this.preamblePos < this.preambleLen && char.IsSymbol(this.preamble[this.preamblePos]))
586 this.preamblePos++;
587 }
588 else
589 this.preamblePos++;
590
591 return this.preamble.Substring(Start, this.preamblePos - Start);
592 }
593 else
594 return Parser.NextToken();
595 }
596
597 private string PeekNextToken(ScriptParser Parser)
598 {
599 this.SkipWhiteSpace(Parser);
600
601 if (this.preamblePos < this.preambleLen)
602 {
603 int Bak = this.preamblePos;
604 string Token = this.NextToken(Parser);
605 this.preamblePos = Bak;
606
607 return Token;
608 }
609 else
610 return Parser.PeekNextToken();
611 }
612
613 private ISparqlPattern ParsePattern(ScriptParser Parser)
614 {
615 if (Parser.NextNonWhitespaceChar() != '{')
616 {
617 Parser.UndoChar();
618 throw Parser.SyntaxError("Expected {");
619 }
620
621 Parser.SkipWhiteSpace();
622 if (Parser.PeekNextChar() == '}')
623 {
624 Parser.NextChar();
625 return null;
626 }
627
628 SparqlRegularPattern Bak = this.currentRegularPattern;
629 ISparqlPattern Bak2 = this.currentPattern;
630
631 this.currentRegularPattern = new SparqlRegularPattern();
632 this.currentPattern = this.currentRegularPattern;
633
634 this.ParseTriples(Parser);
635
636 if (Parser.NextNonWhitespaceChar() != '}')
637 {
638 Parser.UndoChar();
639 throw Parser.SyntaxError("Expected }");
640 }
641
642 ISparqlPattern Result = this.currentPattern;
643
644 this.currentRegularPattern = Bak;
645 this.currentPattern = Bak2;
646
647 return Result;
648 }
649
650 private void ParseTriples(ScriptParser Parser)
651 {
652 this.ParseTriples(Parser, null);
653 }
654
655 private void ParseTriples(ScriptParser Parser, ISemanticElement Subject)
656 {
659 int TriplePosition = Subject is null ? 0 : 1;
660 bool InBlankNode = !(Subject is null);
661
662 while (Parser.InScript)
663 {
664 if (TriplePosition == 0)
665 {
666 Parser.SkipWhiteSpace();
667
668 switch (Parser.PeekNextChar())
669 {
670 case '.':
671 if (TriplePosition == 0)
672 break;
673
674 throw Parser.SyntaxError("Unexpected .");
675
676 case '{':
677 ISparqlPattern Left = this.currentPattern;
678 ISparqlPattern Pattern = this.ParsePattern(Parser);
679
680 if (Left.IsEmpty)
681 {
682 this.currentPattern = Pattern;
683 this.currentRegularPattern = Pattern as SparqlRegularPattern;
684 }
685 else
686 {
687 this.currentRegularPattern = null;
688 this.currentPattern = new IntersectionPattern(Left, Pattern);
689 }
690
691 Parser.SkipWhiteSpace();
692 switch (Parser.PeekNextChar())
693 {
694 case '.':
695 Parser.NextChar();
696 Subject = null;
697 Predicate = null;
698 TriplePosition = 0;
699 break;
700
701 case '}':
702 return;
703 }
704 continue;
705
706 case 'o':
707 case 'O':
708 case 'u':
709 case 'U':
710 case 'm':
711 case 'M':
712 case 'v':
713 case 'V':
714 case 's':
715 case 'S':
716 case 'g':
717 case 'G':
718 if (!this.ParsePatternOperator(Parser))
719 break;
720
721 Parser.SkipWhiteSpace();
722 switch (Parser.PeekNextChar())
723 {
724 case '.':
725 Parser.NextChar();
726 Subject = null;
727 Predicate = null;
728 TriplePosition = 0;
729 break;
730
731 case '}':
732 return;
733 }
734 continue;
735 }
736 }
737
738 Object = this.ParseElement(Parser, TriplePosition,
739 out ChunkedList<SemanticQueryTriple> AdditionalTriples);
740
741 if (Object is null)
742 {
743 if (Subject is null)
744 return;
745 else if (Predicate is null)
746 {
747 if (InBlankNode)
748 return;
749 else
750 throw Parser.SyntaxError("Expected predicate.");
751 }
752 else
753 throw Parser.SyntaxError("Expected object.");
754 }
755
756 if (Subject is null)
757 {
758 Subject = Object;
759 TriplePosition++;
760
761 if (!(AdditionalTriples is null))
762 this.AddTriples(AdditionalTriples);
763 }
764 else if (Predicate is null)
765 {
767 TriplePosition++;
768
769 if (!(AdditionalTriples is null))
770 this.AddTriples(AdditionalTriples);
771 }
772 else
773 {
774 if (this.currentRegularPattern is null)
775 {
776 this.currentRegularPattern = new SparqlRegularPattern();
777 this.currentPattern = new IntersectionPattern(this.currentPattern, this.currentRegularPattern);
778 }
779
780 this.currentRegularPattern.AddTriple(new SemanticQueryTriple(Subject, Predicate, Object));
781
782 if (!(AdditionalTriples is null))
783 this.AddTriples(AdditionalTriples);
784
785 switch (Parser.NextNonWhitespaceChar())
786 {
787 case '.':
788 if (InBlankNode)
789 throw Parser.SyntaxError("Expected ]");
790
791 Subject = null;
792 Predicate = null;
793 TriplePosition = 0;
794 break;
795
796 case '}':
797 if (InBlankNode)
798 throw Parser.SyntaxError("Expected ]");
799
800 Parser.UndoChar();
801 return;
802
803 case ';':
804 Predicate = null;
805 TriplePosition = 1;
806
807 Parser.SkipWhiteSpace();
808 if (Parser.PeekNextChar() == '.')
809 {
810 if (InBlankNode)
811 throw Parser.SyntaxError("Expected ]");
812
813 Parser.NextChar();
814 Subject = null;
815 TriplePosition = 0;
816 }
817 break;
818
819 case ',':
820 break;
821
822 case ']':
823 return;
824
825 case 'b':
826 case 'B':
827 if (char.ToUpper(Parser.NextChar()) != 'I' ||
828 char.ToUpper(Parser.NextChar()) != 'N' ||
829 char.ToUpper(Parser.NextChar()) != 'D')
830 {
831 throw Parser.SyntaxError("Expected BIND");
832 }
833
834 if (InBlankNode)
835 throw Parser.SyntaxError("Expected ]");
836
837 Subject = null;
838 Predicate = null;
839 TriplePosition = 0;
840
841 if (Parser.NextNonWhitespaceChar() != '(')
842 {
843 Parser.UndoChar();
844 throw Parser.SyntaxError("Expected (");
845 }
846
847 ScriptNode Node = this.ParseNamedExpression(Parser);
848 if (!(Node is NamedNode NamedNode))
849 throw Parser.SyntaxError("Expected name.");
850
851 if (this.currentRegularPattern is null)
852 {
853 this.currentRegularPattern = new SparqlRegularPattern();
854 this.currentPattern = new IntersectionPattern(this.currentPattern, this.currentRegularPattern);
855 }
856
857 this.currentRegularPattern.AddVariableBinding(
859
860 if (Parser.NextNonWhitespaceChar() != ')')
861 {
862 Parser.UndoChar();
863 throw Parser.SyntaxError("Expected )");
864 }
865
866 break;
867
868 case 'f':
869 case 'F':
870 if (char.ToUpper(Parser.NextChar()) != 'I' ||
871 char.ToUpper(Parser.NextChar()) != 'L' ||
872 char.ToUpper(Parser.NextChar()) != 'T' ||
873 char.ToUpper(Parser.NextChar()) != 'E' ||
874 char.ToUpper(Parser.NextChar()) != 'R')
875 {
876 throw Parser.SyntaxError("Expected FILTER");
877 }
878
879 if (InBlankNode)
880 throw Parser.SyntaxError("Expected ]");
881
882 Subject = null;
883 Predicate = null;
884 TriplePosition = 0;
885
886 Node = this.ParseUnary(Parser, false);
887
888 if (this.currentRegularPattern is null)
889 {
890 this.currentRegularPattern = new SparqlRegularPattern();
891 this.currentPattern = new IntersectionPattern(this.currentPattern, this.currentRegularPattern);
892 }
893
894 this.currentRegularPattern.AddFilter(Node);
895 break;
896
897 case 'u':
898 case 'U':
899 case 'o':
900 case 'O':
901 case 'm':
902 case 'M':
903 case 'v':
904 case 'V':
905 case 's':
906 case 'S':
907 case 'g':
908 case 'G':
909 Parser.UndoChar();
910
911 if (!this.ParsePatternOperator(Parser))
912 throw Parser.SyntaxError("Unexpected token.");
913
914 if (InBlankNode)
915 throw Parser.SyntaxError("Expected ]");
916
917 Subject = null;
918 Predicate = null;
919 TriplePosition = 0;
920 break;
921
922 default:
923 throw Parser.SyntaxError("Unexpected token.");
924 }
925 }
926 }
927 }
928
929 private void AddTriples(ChunkedList<SemanticQueryTriple> AdditionalTriples)
930 {
931 if (this.currentRegularPattern is null)
932 {
933 this.currentRegularPattern = new SparqlRegularPattern();
934 this.currentPattern = new IntersectionPattern(this.currentPattern, this.currentRegularPattern);
935 }
936
937 foreach (SemanticQueryTriple Triple in AdditionalTriples)
938 this.currentRegularPattern.AddTriple(Triple);
939 }
940
941 private bool ParsePatternOperator(ScriptParser Parser)
942 {
943 switch (Parser.PeekNextToken().ToUpper())
944 {
945 case "OPTIONAL":
946 Parser.NextToken();
947
948 ISparqlPattern Left = this.currentPattern;
949 ISparqlPattern Pattern = this.ParsePattern(Parser);
950
951 this.currentRegularPattern = null;
952 this.currentPattern = new OptionalPattern(Left, Pattern);
953 return true;
954
955 case "UNION":
956 Parser.NextToken();
957
958 Left = this.currentPattern;
959 Pattern = this.ParsePattern(Parser);
960
961 this.currentRegularPattern = null;
962 this.currentPattern = new UnionPattern(Left, Pattern);
963 return true;
964
965 case "MINUS":
966 Parser.NextToken();
967
968 Left = this.currentPattern;
969 Pattern = this.ParsePattern(Parser);
970
971 this.currentRegularPattern = null;
972 this.currentPattern = new ComplementPattern(Left, Pattern);
973 return true;
974
975 case "VALUES":
976 Parser.NextToken();
977
978 ValuesPattern Values = this.ParseValues(Parser,
979 out ChunkedList<SemanticQueryTriple> AdditionalTriples);
980
981 if (!(AdditionalTriples is null))
982 throw Parser.SyntaxError("Blank Nodes not supported in a VALUES statement.");
983
984 this.currentRegularPattern = null;
985
986 if (this.currentPattern.IsEmpty)
987 this.currentPattern = Values;
988 else
989 this.currentPattern = new IntersectionPattern(Values, this.currentPattern);
990
991 return true;
992
993 case "SELECT":
994 SparqlParser SubParser = new SparqlParser(string.Empty);
995
996 foreach (KeyValuePair<string, string> P in this.namespaces)
997 SubParser.namespaces[P.Key] = P.Value;
998
999 if (!SubParser.TryParse(Parser, Parser.Position, out ScriptNode Node))
1000 throw Parser.SyntaxError("Unable to parse subquery.");
1001
1002 if (!(Node is SparqlQuery SubQuery))
1003 throw Parser.SyntaxError("Expected subquery.");
1004
1006
1007 this.currentRegularPattern = null;
1008
1009 if (this.currentPattern.IsEmpty)
1010 this.currentPattern = SubQueryPattern;
1011 else
1012 this.currentPattern = new IntersectionPattern(this.currentPattern, SubQueryPattern);
1013
1014 return true;
1015
1016 case "GRAPH":
1017 Parser.NextToken();
1018
1019 ScriptNode Graph = this.ParseExpression(Parser, false);
1020 Pattern = this.ParsePattern(Parser);
1021
1022 GraphPattern GraphPattern = new GraphPattern(Graph, Pattern);
1023
1024 this.currentRegularPattern = null;
1025
1026 if (this.currentPattern.IsEmpty)
1027 this.currentPattern = GraphPattern;
1028 else
1029 this.currentPattern = new IntersectionPattern(this.currentPattern, GraphPattern);
1030
1031 return true;
1032
1033
1034 default:
1035 return false;
1036 }
1037 }
1038
1039 private ValuesPattern ParseValues(ScriptParser Parser,
1040 out ChunkedList<SemanticQueryTriple> AdditionalTriples)
1041 {
1042 AdditionalTriples = null;
1043
1044 Parser.SkipWhiteSpace();
1045 switch (Parser.PeekNextChar())
1046 {
1047 case '?':
1048 Parser.NextChar();
1049
1050 string s = this.ParseName(Parser);
1051
1052 if (Parser.NextNonWhitespaceChar() != '{')
1053 {
1054 Parser.UndoChar();
1055 throw Parser.SyntaxError("Expected {");
1056 }
1057
1059
1060 while (true)
1061 {
1062 Parser.SkipWhiteSpace();
1063 switch (Parser.PeekNextChar())
1064 {
1065 case (char)0:
1066 throw Parser.SyntaxError("Expected }");
1067
1068 case '}':
1069 Parser.NextChar();
1070
1071 return new ValuesPattern(s, Values.ToArray());
1072
1073 default:
1074 ISemanticElement Element = this.ParseElement(Parser, 2,
1075 out ChunkedList<SemanticQueryTriple> AdditionalTriples2);
1076
1077 if (!(AdditionalTriples2 is null))
1078 {
1079 if (AdditionalTriples is null)
1080 AdditionalTriples = new ChunkedList<SemanticQueryTriple>();
1081
1082 AdditionalTriples.AddRange(AdditionalTriples2);
1083 }
1084
1085 if (Element is UndefinedLiteral)
1086 Values.Add(null);
1087 else
1088 Values.Add(Element);
1089 break;
1090 }
1091 }
1092
1093 case '(':
1094 Parser.NextChar();
1095
1097
1098 while (true)
1099 {
1100 switch (Parser.NextNonWhitespaceChar())
1101 {
1102 case '?':
1103 Names.Add(this.ParseName(Parser));
1104 continue;
1105
1106 case ')':
1107 break;
1108
1109 default:
1110 throw Parser.SyntaxError("Expected ? or )");
1111 }
1112
1113 break;
1114 }
1115
1116 int i, c = Names.Count;
1117 if (c == 0)
1118 throw Parser.SyntaxError("Expected variable name.");
1119
1120 if (Parser.NextNonWhitespaceChar() != '{')
1121 {
1122 Parser.UndoChar();
1123 throw Parser.SyntaxError("Expected {");
1124 }
1125
1127
1128 while (true)
1129 {
1130 switch (Parser.NextNonWhitespaceChar())
1131 {
1132 case '(':
1133 Values = new ChunkedList<ISemanticElement>();
1134
1135 for (i = 0; i < c; i++)
1136 {
1137 ISemanticElement Element = this.ParseElement(Parser, 2,
1138 out ChunkedList<SemanticQueryTriple> AdditionalTriples2);
1139
1140 if (!(AdditionalTriples2 is null))
1141 {
1142 if (AdditionalTriples is null)
1143 AdditionalTriples = new ChunkedList<SemanticQueryTriple>();
1144
1145 AdditionalTriples.AddRange(AdditionalTriples2);
1146 }
1147
1148 if (Element is UndefinedLiteral)
1149 Values.Add(null);
1150 else
1151 Values.Add(Element);
1152 }
1153
1154 if (Parser.NextNonWhitespaceChar() != ')')
1155 {
1156 Parser.UndoChar();
1157 throw Parser.SyntaxError("Expected )");
1158 }
1159
1160 Records.Add(Values.ToArray());
1161 continue;
1162
1163 case '}':
1164 return new ValuesPattern(Names.ToArray(), Records.ToArray());
1165
1166 default:
1167 throw Parser.SyntaxError("Expected ( or }");
1168 }
1169 }
1170
1171 default:
1172 throw Parser.SyntaxError("Expected ? or (");
1173 }
1174 }
1175
1176 private ScriptNode ParseNamedExpression(ScriptParser Parser)
1177 {
1178 ScriptNode Node = this.ParseExpression(Parser, false);
1179 string s = Parser.PeekNextToken().ToUpper();
1180
1181 if (s == "AS")
1182 {
1183 Parser.NextToken();
1184 ScriptNode Name = this.ParseExpression(Parser, false);
1185 Node = new NamedNode(Node, Name, Node.Start, Parser.Position - Node.Start, Parser.Expression);
1186 }
1187
1188 return Node;
1189 }
1190
1191 private ScriptNode ParseExpression(ScriptParser Parser, bool Optional)
1192 {
1193 return this.ParseOrs(Parser, Optional);
1194 }
1195
1196 private ScriptNode ParseOrs(ScriptParser Parser, bool Optional)
1197 {
1198 ScriptNode Left = this.ParseAnds(Parser, Optional);
1199 if (Left is null)
1200 return null;
1201
1202 Parser.SkipWhiteSpace();
1203 while (Parser.PeekNextChars(2) == "||")
1204 {
1205 Parser.SkipChars(2);
1206 ScriptNode Right = this.ParseAnds(Parser, false);
1207 Left = new Operators.Logical.Or(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1208 Parser.SkipWhiteSpace();
1209 }
1210
1211 return Left;
1212 }
1213
1214 private ScriptNode ParseAnds(ScriptParser Parser, bool Optional)
1215 {
1216 ScriptNode Left = this.ParseComparisons(Parser, Optional);
1217 if (Left is null)
1218 return null;
1219
1220 Parser.SkipWhiteSpace();
1221 while (Parser.PeekNextChars(2) == "&&")
1222 {
1223 Parser.SkipChars(2);
1224 ScriptNode Right = this.ParseComparisons(Parser, false);
1225 Left = new Operators.Logical.And(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1226 Parser.SkipWhiteSpace();
1227 }
1228
1229 return Left;
1230 }
1231
1232 private ScriptNode ParseComparisons(ScriptParser Parser, bool Optional)
1233 {
1234 ScriptNode Left = this.ParseTerms(Parser, Optional);
1235 if (Left is null)
1236 return null;
1237
1238 while (true)
1239 {
1240 Parser.SkipWhiteSpace();
1241
1242 switch (Parser.PeekNextChar())
1243 {
1244 case '=':
1245 Parser.NextChar();
1246 ScriptNode Right = this.ParseTerms(Parser, false);
1247 Left = new EqualTo(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1248 break;
1249
1250 case '!':
1251 Parser.NextChar();
1252 if (Parser.PeekNextChar() == '=')
1253 {
1254 Parser.NextChar();
1255 Right = this.ParseTerms(Parser, false);
1256 Left = new NotEqualTo(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1257 }
1258 else
1259 {
1260 Parser.UndoChar();
1261 return Left;
1262 }
1263 break;
1264
1265 case '<':
1266 Parser.NextChar();
1267
1268 if (Parser.PeekNextChar() == '=')
1269 {
1270 Parser.NextChar();
1271 Right = this.ParseTerms(Parser, false);
1273 }
1274 else
1275 {
1276 Right = this.ParseTerms(Parser, false);
1277 Left = new LesserThan(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1278 }
1279 break;
1280
1281 case '>':
1282 Parser.NextChar();
1283
1284 if (Parser.PeekNextChar() == '=')
1285 {
1286 Parser.NextChar();
1287 Right = this.ParseTerms(Parser, false);
1289 }
1290 else
1291 {
1292 Right = this.ParseTerms(Parser, false);
1293 Left = new GreaterThan(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1294 }
1295 break;
1296
1297 case 'i':
1298 case 'I':
1299 if (string.Compare(Parser.PeekNextToken(), "IN", true) == 0)
1300 {
1301 Parser.NextToken();
1302 Right = this.ParseTerms(Parser, false);
1303 Left = new In(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1304 }
1305 else
1306 return Left;
1307 break;
1308
1309 case 'n':
1310 case 'N':
1311 if (string.Compare(Parser.PeekNextToken(), "NOT", true) == 0)
1312 {
1313 if (string.Compare(Parser.NextToken(), "IN", true) != 0)
1314 throw Parser.SyntaxError("Expected IN");
1315
1316 Right = this.ParseTerms(Parser, false);
1317 Left = new NotIn(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1318 }
1319 else
1320 return Left;
1321 break;
1322
1323 default:
1324 return Left;
1325 }
1326 }
1327 }
1328
1329 private ScriptNode ParseTerms(ScriptParser Parser, bool Optional)
1330 {
1331 ScriptNode Left = this.ParseFactors(Parser, Optional);
1332
1333 while (true)
1334 {
1335 Parser.SkipWhiteSpace();
1336
1337 switch (Parser.PeekNextChar())
1338 {
1339 case '+':
1340 Parser.NextChar();
1341 ScriptNode Right = this.ParseFactors(Parser, false);
1342 Left = new Add(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1343 break;
1344
1345 case '-':
1346 Parser.NextChar();
1347 Right = this.ParseFactors(Parser, false);
1348 Left = new Subtract(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1349 break;
1350
1351 default:
1352 return Left;
1353 }
1354 }
1355 }
1356
1357 private ScriptNode ParseFactors(ScriptParser Parser, bool Optional)
1358 {
1359 ScriptNode Left = this.ParseUnary(Parser, Optional);
1360
1361 while (true)
1362 {
1363 Parser.SkipWhiteSpace();
1364
1365 switch (Parser.PeekNextChar())
1366 {
1367 case '*':
1368 Parser.NextChar();
1369 ScriptNode Right = this.ParseUnary(Parser, false);
1370 Left = new Multiply(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1371 break;
1372
1373 case '/':
1374 Parser.NextChar();
1375 Right = this.ParseUnary(Parser, false);
1376 Left = new Divide(Left, Right, Left.Start, Parser.Position - Left.Start, Parser.Expression);
1377 break;
1378
1379 default:
1380 return Left;
1381 }
1382 }
1383 }
1384
1385 private ScriptNode ParseUnary(ScriptParser Parser, bool Optional)
1386 {
1387 Parser.SkipWhiteSpace();
1388
1389 int Start = Parser.Position;
1390 char ch;
1391
1392 switch (ch = Parser.PeekNextChar())
1393 {
1394 case '!':
1395 Parser.NextChar();
1396 ScriptNode Node = this.ParseUnary(Parser, false);
1397 return new Not(Node, Start, Parser.Position - Start, Parser.Expression);
1398
1399 case '+':
1400 Parser.NextChar();
1401 return this.ParseUnary(Parser, false);
1402
1403 case '-':
1404 Parser.NextChar();
1405
1406 switch (Parser.PeekNextChar())
1407 {
1408 case '0':
1409 case '1':
1410 case '2':
1411 case '3':
1412 case '4':
1413 case '5':
1414 case '6':
1415 case '7':
1416 case '8':
1417 case '9':
1418 case '.':
1419 Parser.UndoChar();
1420 ISemanticElement Element2 = this.ParseElement(Parser, 2, out _);
1421
1422 return new ConstantElement(Element2, Start, Parser.Position - Start, Parser.Expression);
1423
1424 default:
1425 Node = this.ParseUnary(Parser, false);
1426 return new Negate(Node, Start, Parser.Position - Start, Parser.Expression);
1427 }
1428
1429 case '(':
1430 Parser.NextChar();
1431 Node = this.ParseNamedExpression(Parser);
1432 if (Parser.NextChar() != ')')
1433 throw Parser.SyntaxError("Expected )");
1434 return Node;
1435
1436 case '?':
1437 case '$':
1438 Parser.NextChar();
1439 string s = this.ParseName(Parser);
1440 return new VariableReference(s, Start, Parser.Position - Start, Parser.Expression);
1441
1442 case '\'':
1443 case '"':
1444 case '0':
1445 case '1':
1446 case '2':
1447 case '3':
1448 case '4':
1449 case '5':
1450 case '6':
1451 case '7':
1452 case '8':
1453 case '9':
1454 case '.':
1455 ISemanticElement Element = this.ParseElement(Parser, 2, out _);
1456
1457 return new ConstantElement(Element, Start, Parser.Position - Start, Parser.Expression);
1458
1459 case '<':
1460 Parser.NextChar();
1461 Element = this.ParseUri(Parser);
1462 return new ConstantElement(Element, Start, Parser.Position - Start, Parser.Expression);
1463
1464 case ':':
1465 Parser.NextChar();
1466 Element = this.ParsePrefixedToken(Parser, string.Empty);
1467 return new ConstantElement(Element, Start, Parser.Position - Start, Parser.Expression);
1468
1469 default:
1470 if (!char.IsLetter(ch))
1471 {
1472 if (Optional)
1473 return null;
1474
1475 throw Parser.SyntaxError("Expected value.");
1476 }
1477
1478 s = Parser.NextToken();
1479 if (Parser.PeekNextChar() == ':')
1480 {
1481 Parser.NextChar();
1482 UriNode Fqn = this.ParsePrefixedToken(Parser, s);
1483
1484 Parser.SkipWhiteSpace();
1485 if (Parser.PeekNextChar() == '(')
1486 return this.ParseExtensionFunction(Fqn.Uri.AbsoluteUri, Parser, Start);
1487 else
1488 return new ConstantElement(Fqn, Start, Parser.Position - Start, Parser.Expression);
1489 }
1490
1491 return this.ParseFunction(Parser, s, Start, Optional);
1492 }
1493 }
1494
1495 private ScriptNode ParseFunction(ScriptParser Parser, string s, int Start, bool Optional)
1496 {
1497 switch (s.ToUpper())
1498 {
1499 case "BIND":
1500 if (Parser.NextNonWhitespaceChar() != '(')
1501 {
1502 Parser.UndoChar();
1503 throw Parser.SyntaxError("Expected (");
1504 }
1505
1506 ScriptNode Node = this.ParseNamedExpression(Parser);
1507 if (!(Node is NamedNode NamedNode))
1508 throw Parser.SyntaxError("Expected name.");
1509
1510 if (this.currentRegularPattern is null)
1511 {
1512 this.currentRegularPattern = new SparqlRegularPattern();
1513 this.currentPattern = new IntersectionPattern(this.currentPattern, this.currentRegularPattern);
1514 }
1515
1516 this.currentRegularPattern.AddVariableBinding(NamedNode.LeftOperand, NamedNode.RightOperand);
1517
1518 if (Parser.NextNonWhitespaceChar() != ')')
1519 {
1520 Parser.UndoChar();
1521 throw Parser.SyntaxError("Expected )");
1522 }
1523
1524 return this.ParseUnary(Parser, Optional);
1525
1526 case "FILTER":
1527 Node = this.ParseUnary(Parser, false);
1528
1529 if (this.currentRegularPattern is null)
1530 {
1531 this.currentRegularPattern = new SparqlRegularPattern();
1532 this.currentPattern = new IntersectionPattern(this.currentPattern, this.currentRegularPattern);
1533 }
1534
1535 this.currentRegularPattern.AddFilter(Node);
1536
1537 return this.ParseUnary(Parser, Optional);
1538
1539 case "TRUE":
1540 return new ConstantElement(BooleanValue.True, Start, Parser.Position - Start, Parser.Expression);
1541
1542 case "FALSE":
1543 return new ConstantElement(BooleanValue.False, Start, Parser.Position - Start, Parser.Expression);
1544
1545 case "CONCAT":
1546 int Start2 = Parser.Position;
1547 ScriptNode[] Arguments = this.ParseArguments(Parser, 1, int.MaxValue);
1548
1549 VectorDefinition Vector = new VectorDefinition(Arguments,
1550 Start2, Parser.Position - Start2, Parser.Expression);
1551
1552 return new Concat(Vector, Start2, Parser.Position - Start2, Parser.Expression);
1553
1554 case "ASC":
1555 Node = this.ParseArgument(Parser);
1556 return new Asc(Node, Start, Parser.Position - Start, Parser.Expression);
1557
1558 case "DESC":
1559 Node = this.ParseArgument(Parser);
1560 return new Desc(Node, Start, Parser.Position - Start, Parser.Expression);
1561
1562 case "REGEX":
1563 Arguments = this.ParseArguments(Parser, 2, 3);
1564 if (Arguments.Length == 2)
1565 {
1566 return new LikeWithOptions(Arguments[0], Arguments[1], null,
1567 Start, Parser.Position - Start, Parser.Expression);
1568 }
1569 else
1570 {
1571 return new LikeWithOptions(Arguments[0], Arguments[1], Arguments[2],
1572 Start, Parser.Position - Start, Parser.Expression);
1573 }
1574
1575 case "EXISTS":
1576 return new Exists(this.ParsePattern(Parser),
1577 Start, Parser.Position - Start, Parser.Expression);
1578
1579 case "NOT": // EXISTS
1580 if (Parser.NextToken() != "EXISTS")
1581 throw Parser.SyntaxError("Expected EXISTS.");
1582
1583 return new NotExists(this.ParsePattern(Parser),
1584 Start, Parser.Position - Start, Parser.Expression);
1585
1586 case "COUNT":
1587 Node = this.ParseArgument(Parser);
1588 return new Count(Node, Start, Parser.Position - Start, Parser.Expression);
1589
1590 case "SUM":
1591 Node = this.ParseArgument(Parser);
1592 return new Sum(Node, Start, Parser.Position - Start, Parser.Expression);
1593
1594 case "MIN":
1595 Node = this.ParseArgument(Parser);
1596 return new Script.Functions.Vectors.Min(Node, Start, Parser.Position - Start, Parser.Expression);
1597
1598 case "MAX":
1599 Node = this.ParseArgument(Parser);
1600 return new Script.Functions.Vectors.Max(Node, Start, Parser.Position - Start, Parser.Expression);
1601
1602 case "AVG":
1603 Node = this.ParseArgument(Parser);
1604 return new Average(Node, Start, Parser.Position - Start, Parser.Expression);
1605
1606 case "GROUP_CONCAT":
1607 Node = this.ParseArgumentOptionalScalarVal(Parser, "separator", out ScriptNode Node2);
1608 if (Node2 is null)
1609 return new Concat(Node, Start, Parser.Position - Start, Parser.Expression);
1610 else
1611 return new Concat(Node, Node2, Start, Parser.Position - Start, Parser.Expression);
1612
1613 case "SAMPLE":
1614 Node = this.ParseArgument(Parser);
1615 return new Sample(Node, Start, Parser.Position - Start, Parser.Expression);
1616
1617 case "STR":
1618 Node = this.ParseArgument(Parser);
1619 return new Script.Functions.Scalar.String(Node, Start, Parser.Position - Start, Parser.Expression);
1620
1621 case "ABS":
1622 Node = this.ParseArgument(Parser);
1623 return new Abs(Node, Start, Parser.Position - Start, Parser.Expression);
1624
1625 case "CEIL":
1626 Node = this.ParseArgument(Parser);
1627 return new Ceiling(Node, Start, Parser.Position - Start, Parser.Expression);
1628
1629 case "FLOOR":
1630 Node = this.ParseArgument(Parser);
1631 return new Floor(Node, Start, Parser.Position - Start, Parser.Expression);
1632
1633 case "ROUND":
1634 Node = this.ParseArgument(Parser);
1635 return new Round(Node, Start, Parser.Position - Start, Parser.Expression);
1636
1637 case "STRLEN":
1638 Node = this.ParseArgument(Parser);
1639 return new Length(Node, Start, Parser.Position - Start, Parser.Expression);
1640
1641 case "UCASE":
1642 Node = this.ParseArgument(Parser);
1643 return new UpperCase(Node, Start, Parser.Position - Start, Parser.Expression);
1644
1645 case "LCASE":
1646 Node = this.ParseArgument(Parser);
1647 return new LowerCase(Node, Start, Parser.Position - Start, Parser.Expression);
1648
1649 case "CONTAINS":
1650 this.Parse2Arguments(Parser, out Node, out Node2);
1651 return new Contains(Node, Node2, Start, Parser.Position - Start, Parser.Expression);
1652
1653 case "STRSTARTS":
1654 this.Parse2Arguments(Parser, out Node, out Node2);
1655 return new StartsWith(Node, Node2, Start, Parser.Position - Start, Parser.Expression);
1656
1657 case "STRENDS":
1658 this.Parse2Arguments(Parser, out Node, out Node2);
1659 return new EndsWith(Node, Node2, Start, Parser.Position - Start, Parser.Expression);
1660
1661 case "STRBEFORE":
1662 this.Parse2Arguments(Parser, out Node, out Node2);
1663 return new Before(Node, Node2, Start, Parser.Position - Start, Parser.Expression);
1664
1665 case "STRAFTER":
1666 this.Parse2Arguments(Parser, out Node, out Node2);
1667 return new After(Node, Node2, Start, Parser.Position - Start, Parser.Expression);
1668
1669 case "SUBSTR":
1670 Arguments = this.ParseArguments(Parser, 2, 3);
1671 int NodeStart = Arguments[1].Start;
1672 int NodeLen = Arguments[1].Length;
1673
1674 Arguments[1] = new Subtract(Arguments[1],
1675 new ConstantElement(new DoubleNumber(1), NodeStart, NodeLen, Parser.Expression),
1676 NodeStart, NodeLen, Parser.Expression);
1677
1678 if (Arguments.Length == 2)
1679 {
1680 return new Mid(Arguments[0], Arguments[1], null,
1681 Start, Parser.Position - Start, Parser.Expression);
1682 }
1683 else
1684 {
1685 return new Mid(Arguments[0], Arguments[1], Arguments[2],
1686 Start, Parser.Position - Start, Parser.Expression);
1687 }
1688
1689 case "YEAR":
1690 Node = this.ParseArgument(Parser);
1691 return new Year(Node, Start, Parser.Position - Start, Parser.Expression);
1692
1693 case "MONTH":
1694 Node = this.ParseArgument(Parser);
1695 return new Month(Node, Start, Parser.Position - Start, Parser.Expression);
1696
1697 case "DAY":
1698 Node = this.ParseArgument(Parser);
1699 return new Day(Node, Start, Parser.Position - Start, Parser.Expression);
1700
1701 case "HOURS":
1702 Node = this.ParseArgument(Parser);
1703 return new Hour(Node, Start, Parser.Position - Start, Parser.Expression);
1704
1705 case "MINUTES":
1706 Node = this.ParseArgument(Parser);
1707 return new Minute(Node, Start, Parser.Position - Start, Parser.Expression);
1708
1709 case "SECONDS":
1710 Node = this.ParseArgument(Parser);
1711 return new Second(Node, Start, Parser.Position - Start, Parser.Expression);
1712
1713 case "NOW":
1714 this.Parse0Arguments(Parser);
1715 return new VariableReference("Now", Start, Parser.Position - Start, Parser.Expression);
1716
1717 case "MD5":
1718 Node = this.ParseArgument(Parser);
1719 NodeStart = Node.Start;
1720 NodeLen = Node.Length;
1721
1722 return new HexEncode(
1723 new Md5(Node, NodeStart, NodeLen, Parser.Expression),
1724 NodeStart, NodeLen, Parser.Expression);
1725
1726 case "SHA1":
1727 Node = this.ParseArgument(Parser);
1728 NodeStart = Node.Start;
1729 NodeLen = Node.Length;
1730
1731 return new HexEncode(
1732 new Sha1(Node, NodeStart, NodeLen, Parser.Expression),
1733 NodeStart, NodeLen, Parser.Expression);
1734
1735 case "SHA256":
1736 Node = this.ParseArgument(Parser);
1737 NodeStart = Node.Start;
1738 NodeLen = Node.Length;
1739
1740 return new HexEncode(
1741 new Sha2_256(Node, NodeStart, NodeLen, Parser.Expression),
1742 NodeStart, NodeLen, Parser.Expression);
1743
1744 case "SHA384":
1745 Node = this.ParseArgument(Parser);
1746 NodeStart = Node.Start;
1747 NodeLen = Node.Length;
1748
1749 return new HexEncode(
1750 new Sha2_384(Node, NodeStart, NodeLen, Parser.Expression),
1751 NodeStart, NodeLen, Parser.Expression);
1752
1753 case "SHA512":
1754 Node = this.ParseArgument(Parser);
1755 NodeStart = Node.Start;
1756 NodeLen = Node.Length;
1757
1758 return new HexEncode(
1759 new Sha2_512(Node, NodeStart, NodeLen, Parser.Expression),
1760 NodeStart, NodeLen, Parser.Expression);
1761
1762 case "RAND":
1763 this.Parse0Arguments(Parser);
1764 return new Uniform(Start, Parser.Position - Start, Parser.Expression);
1765
1766 case "ENCODE_FOR_URI":
1767 Node = this.ParseArgument(Parser);
1768 return new UrlEncode(Node, Start, Parser.Position - Start, Parser.Expression);
1769
1770 case "STRUUID":
1771 this.Parse0Arguments(Parser);
1772 NodeLen = Parser.Position - Start;
1773
1774 return new Script.Functions.Scalar.String(
1775 new NewGuid(Start, NodeLen, Parser.Expression),
1776 Start, NodeLen, Parser.Expression);
1777
1778 case "UUID":
1779 this.Parse0Arguments(Parser);
1780 NodeLen = Parser.Position - Start;
1781
1782 return new Script.Functions.Scalar.Uri(
1783 new Add(
1784 new ConstantElement(new StringValue("urn:uuid:"), Start, NodeLen, Parser.Expression),
1785 new Script.Functions.Scalar.String(
1786 new NewGuid(Start, NodeLen, Parser.Expression),
1787 Start, NodeLen, Parser.Expression),
1788 Start, NodeLen, Parser.Expression),
1789 Start, NodeLen, Parser.Expression);
1790
1791 case "IRI":
1792 case "URI":
1793 Node = this.ParseArgument(Parser);
1794 return new Script.Functions.Scalar.Uri(Node, Start, Parser.Position - Start, Parser.Expression);
1795
1796 case "IF":
1797 Arguments = this.ParseArguments(Parser, 3, 3);
1798 return new If(Arguments[0], Arguments[1], Arguments[2],
1799 Start, Parser.Position - Start, Parser.Expression);
1800
1801 case "SAMETERM":
1802 this.Parse2Arguments(Parser, out Node, out Node2);
1803 return new EqualTo(Node, Node2, Start, Parser.Position - Start, Parser.Expression);
1804
1805 case "BOUND":
1806 Node = this.ParseArgument(Parser);
1807 return new Script.Functions.Runtime.Exists(Node, Start, Parser.Position - Start, Parser.Expression);
1808
1809 case "ISIRI":
1810 case "ISURI":
1811 Node = this.ParseArgument(Parser);
1812 return new IsUri(Node, Start, Parser.Position - Start, Parser.Expression);
1813
1814 case "ISBLANK":
1815 Node = this.ParseArgument(Parser);
1816 return new IsBlank(Node, Start, Parser.Position - Start, Parser.Expression);
1817
1818 case "ISNUMERIC":
1819 Node = this.ParseArgument(Parser);
1820 return new IsNumeric(Node, Start, Parser.Position - Start, Parser.Expression);
1821
1822 case "ISLITERAL":
1823 Node = this.ParseArgument(Parser);
1824 return new IsLiteral(Node, Start, Parser.Position - Start, Parser.Expression);
1825
1826 case "LANG":
1827 Node = this.ParseArgument(Parser);
1828 return new Lang(Node, Start, Parser.Position - Start, Parser.Expression);
1829
1830 case "DATATYPE":
1831 Node = this.ParseArgument(Parser);
1832 return new DataType(Node, Start, Parser.Position - Start, Parser.Expression);
1833
1834 case "BNODE":
1835 Arguments = this.ParseArguments(Parser, 0, 1);
1836
1837 if (Arguments.Length == 0)
1838 return new BNode(Start, Parser.Position - Start, Parser.Expression);
1839 else
1840 return new BNode(Arguments[0], Start, Parser.Position - Start, Parser.Expression);
1841
1842 case "STRDT":
1843 this.Parse2Arguments(Parser, out Node, out Node2);
1844 return new StrDt(Node, Node2, Start, Parser.Position - Start, Parser.Expression);
1845
1846 case "STRLANG":
1847 this.Parse2Arguments(Parser, out Node, out Node2);
1848 return new StrLang(Node, Node2, Start, Parser.Position - Start, Parser.Expression);
1849
1850 case "LANGMATCHES":
1851 this.Parse2Arguments(Parser, out Node, out Node2);
1852 return new LangMatches(Node, Node2, Start, Parser.Position - Start, Parser.Expression);
1853
1854 case "REPLACE":
1855 Arguments = this.ParseArguments(Parser, 3, 4);
1856 if (Arguments.Length == 2)
1857 {
1858 return new Replace(Arguments[0], Arguments[1], Arguments[2], true,
1859 Start, Parser.Position - Start, Parser.Expression);
1860 }
1861 else
1862 {
1863 return new Replace(Arguments[0], Arguments[1], Arguments[2], Arguments[3],
1864 Start, Parser.Position - Start, Parser.Expression);
1865 }
1866
1867 case "TIMEZONE":
1868 Node = this.ParseArgument(Parser);
1869 return new Waher.Content.Semantic.Functions.TimeZone(Node, Start, Parser.Position - Start, Parser.Expression);
1870
1871 case "TZ":
1872 Node = this.ParseArgument(Parser);
1873 return new Tz(Node, Start, Parser.Position - Start, Parser.Expression);
1874
1875 case "COALESCE":
1876 Arguments = this.ParseArguments(Parser, 1, int.MaxValue);
1877 return new Coalesce(Arguments, Start, Parser.Position - Start, Parser.Expression);
1878
1879 case "ERROR":
1880 Node = this.ParseArgument(Parser);
1881 return new Script.Functions.Runtime.Error(Node, Start, Parser.Position - Start, Parser.Expression);
1882
1883 case "TRIPLE":
1884 Arguments = this.ParseArguments(Parser, 3, 3);
1885 return new Triple(Arguments[0], Arguments[1], Arguments[2], Start, Parser.Position - Start, Parser.Expression);
1886
1887 case "SUBJECT":
1888 Node = this.ParseArgument(Parser);
1889 return new Subject(Node, Start, Parser.Position - Start, Parser.Expression);
1890
1891 case "PREDICATE":
1892 Node = this.ParseArgument(Parser);
1893 return new Predicate(Node, Start, Parser.Position - Start, Parser.Expression);
1894
1895 case "OBJECT":
1896 Node = this.ParseArgument(Parser);
1897 return new Waher.Content.Semantic.Functions.Object(Node, Start, Parser.Position - Start, Parser.Expression);
1898
1899 case "ISTRIPLE":
1900 Node = this.ParseArgument(Parser);
1901 return new IsTriple(Node, Start, Parser.Position - Start, Parser.Expression);
1902
1903 default:
1904 if (Parser.PeekNextChar() == ':')
1905 {
1906 s = this.ParsePrefixedToken(Parser, s).Uri.AbsoluteUri;
1907 return this.ParseExtensionFunction(s, Parser, Start);
1908 }
1909
1910 if (Optional)
1911 {
1912 int i = s.Length;
1913 while (i-- > 0)
1914 Parser.UndoChar();
1915
1916 return null;
1917 }
1918
1919 throw Parser.SyntaxError("Unexpected token: " + s);
1920 }
1921 }
1922
1923 private ScriptNode ParseExtensionFunction(string FullyQualifiedName, ScriptParser Parser, int Start)
1924 {
1926
1927 lock (functionsPerUri)
1928 {
1929 if (!functionsPerUri.TryGetValue(FullyQualifiedName, out Function))
1930 {
1931 Function = Types.FindBest<IExtensionFunction, string>(FullyQualifiedName);
1932 functionsPerUri[FullyQualifiedName] = Function;
1933 }
1934 }
1935
1936 if (Function is null)
1937 throw Parser.SyntaxError("Function not found.");
1938
1939 ScriptNode[] Arguments = this.ParseArguments(Parser, Function.MinArguments, Function.MaxArguments);
1940
1941 return Function.CreateFunction(Arguments, Start, Parser.Position - Start, Parser.Expression);
1942 }
1943
1944 private void Parse0Arguments(ScriptParser Parser)
1945 {
1946 if (Parser.NextNonWhitespaceChar() != '(')
1947 {
1948 Parser.UndoChar();
1949 throw Parser.SyntaxError("Expected (");
1950 }
1951
1952 if (Parser.NextNonWhitespaceChar() != ')')
1953 {
1954 Parser.UndoChar();
1955 throw Parser.SyntaxError("Expected )");
1956 }
1957 }
1958
1959 private ScriptNode ParseArgument(ScriptParser Parser)
1960 {
1961 if (Parser.NextNonWhitespaceChar() != '(')
1962 {
1963 Parser.UndoChar();
1964 throw Parser.SyntaxError("Expected (");
1965 }
1966
1967 ScriptNode Argument = this.ParseExpression(Parser, false);
1968
1969 if (Parser.NextNonWhitespaceChar() != ')')
1970 {
1971 Parser.UndoChar();
1972 throw Parser.SyntaxError("Expected )");
1973 }
1974
1975 return Argument;
1976 }
1977
1978 private void Parse2Arguments(ScriptParser Parser, out ScriptNode Argument1, out ScriptNode Argument2)
1979 {
1980 if (Parser.NextNonWhitespaceChar() != '(')
1981 {
1982 Parser.UndoChar();
1983 throw Parser.SyntaxError("Expected (");
1984 }
1985
1986 Argument1 = this.ParseExpression(Parser, false);
1987
1988 if (Parser.NextNonWhitespaceChar() != ',')
1989 {
1990 Parser.UndoChar();
1991 throw Parser.SyntaxError("Expected ,");
1992 }
1993
1994 Argument2 = this.ParseExpression(Parser, false);
1995
1996 if (Parser.NextNonWhitespaceChar() != ')')
1997 {
1998 Parser.UndoChar();
1999 throw Parser.SyntaxError("Expected )");
2000 }
2001 }
2002
2003 private ScriptNode ParseArgumentOptionalScalarVal(ScriptParser Parser, string ExpectedScalarName, out ScriptNode ScalarVal)
2004 {
2005 if (Parser.NextNonWhitespaceChar() != '(')
2006 {
2007 Parser.UndoChar();
2008 throw Parser.SyntaxError("Expected (");
2009 }
2010
2011 ScriptNode Argument = this.ParseExpression(Parser, false);
2012
2013 switch (Parser.NextNonWhitespaceChar())
2014 {
2015 case ')':
2016 ScalarVal = null;
2017 return Argument;
2018
2019 case ';':
2020 string s = this.ParseName(Parser);
2021 if (s != ExpectedScalarName)
2022 throw Parser.SyntaxError("Expected " + ExpectedScalarName);
2023
2024 if (Parser.NextNonWhitespaceChar() != '=')
2025 {
2026 Parser.UndoChar();
2027 throw Parser.SyntaxError("Expected =");
2028 }
2029
2030 ScalarVal = this.ParseExpression(Parser, false);
2031
2032 if (Parser.NextNonWhitespaceChar() != ')')
2033 {
2034 Parser.UndoChar();
2035 throw Parser.SyntaxError("Expected )");
2036 }
2037
2038 return Argument;
2039
2040 default:
2041 throw Parser.SyntaxError("Expected ) or ;");
2042 }
2043 }
2044
2045 private ScriptNode[] ParseArguments(ScriptParser Parser, int Min, int Max)
2046 {
2047 if (Parser.NextNonWhitespaceChar() != '(')
2048 {
2049 Parser.UndoChar();
2050 throw Parser.SyntaxError("Expected (");
2051 }
2052
2054
2055 while (true)
2056 {
2057 Arguments.Add(this.ParseExpression(Parser, false));
2058
2059 if (Parser.NextNonWhitespaceChar() != ',')
2060 {
2061 Parser.UndoChar();
2062 break;
2063 }
2064 }
2065
2066 int c = Arguments.Count;
2067 if (c < Min)
2068 throw Parser.SyntaxError("Expected at least " + Min.ToString() + " arguments.");
2069
2070 if (c > Max)
2071 throw Parser.SyntaxError("Expected at most " + Max.ToString() + " arguments.");
2072
2073 if (Parser.NextNonWhitespaceChar() != ')')
2074 {
2075 Parser.UndoChar();
2076 throw Parser.SyntaxError("Expected )");
2077 }
2078
2079 return Arguments.ToArray();
2080 }
2081
2082 private ISemanticElement ParseElement(ScriptParser Parser, int TriplePosition,
2083 out ChunkedList<SemanticQueryTriple> AdditionalTriples)
2084 {
2085 AdditionalTriples = null;
2086
2087 while (true)
2088 {
2089 char ch = Parser.NextNonWhitespaceChar();
2090
2091 switch (ch)
2092 {
2093 case (char)0:
2094 return null;
2095
2096 case '[':
2097 switch (TriplePosition)
2098 {
2099 case 0:
2100 BlankNode Node = this.CreateBlankNode();
2101 this.ParseTriples(Parser, Node);
2102 return Node;
2103
2104 case 1:
2105 throw Parser.SyntaxError("Predicate cannot be a blank node.");
2106
2107 case 2:
2108 Node = this.CreateBlankNode();
2109
2110 SparqlRegularPattern Bak = this.currentRegularPattern;
2111 ISparqlPattern Bak2 = this.currentPattern;
2112
2113 this.currentRegularPattern = new SparqlRegularPattern();
2114 this.currentPattern = this.currentRegularPattern;
2115
2116 this.ParseTriples(Parser, Node);
2117
2118 AdditionalTriples = this.currentRegularPattern.Triples;
2119
2120 this.currentRegularPattern = Bak;
2121 this.currentPattern = Bak2;
2122
2123 return Node;
2124
2125 default:
2126 throw Parser.SyntaxError("Unrecognized triple position.");
2127 }
2128
2129 case '(':
2130 return this.ParseCollection(Parser, out AdditionalTriples);
2131
2132 case ']':
2133 return null;
2134
2135 case '}':
2136 Parser.UndoChar();
2137 return null;
2138
2139 case '<':
2140 return this.ParseUri(Parser);
2141
2142 case '"':
2143 if (TriplePosition != 2)
2144 throw Parser.SyntaxError("Literals can only occur in object position.");
2145
2146 string s;
2147
2148 if (Parser.IsNextChars('"', 2))
2149 {
2150 Parser.SkipChars(2);
2151 s = this.ParseString(Parser, '"', true, true);
2152 }
2153 else
2154 s = this.ParseString(Parser, '"', false, true);
2155
2156 string Language = null;
2157
2158 if (Parser.PeekNextChar() == '@')
2159 {
2160 Parser.NextChar();
2161 Language = this.ParseName(Parser);
2162 }
2163
2164 if (Parser.IsNextChars('^', 2))
2165 {
2166 Parser.SkipChars(2);
2167
2168 string DataType = this.ParseUriOrPrefixedToken(Parser).Uri.ToString();
2169
2170 return SemanticElements.Parse(s, DataType, Language);
2171 }
2172 else if (!string.IsNullOrEmpty(Language))
2173 return new StringLiteral(s, Language);
2174 else
2175 return new StringLiteral(s);
2176
2177 case ':':
2178 return this.ParsePrefixedToken(Parser, string.Empty);
2179
2180 case '?':
2181 case '$':
2182 int Start2 = Parser.Position;
2183 s = this.ParseName(Parser);
2184 return new SemanticScriptElement(new VariableReference(s, Start2, Parser.Position - Start2, Parser.Expression));
2185
2186 default:
2187 if (char.IsWhiteSpace(ch))
2188 break;
2189
2190 if (ch == '_')
2191 {
2192 if (Parser.NextNonWhitespaceChar() != ':')
2193 {
2194 Parser.UndoChar();
2195 throw Parser.SyntaxError("Expected :");
2196 }
2197
2198 return new BlankNode(this.ParseName(Parser));
2199 }
2200 else if (char.IsLetter(ch) || ch == ':')
2201 {
2202 Parser.UndoChar();
2203 Start2 = Parser.Position;
2204 s = this.ParseName(Parser);
2205
2206 if (Parser.PeekNextChar() == ':')
2207 {
2208 Parser.NextChar();
2209 return this.ParsePrefixedToken(Parser, s);
2210 }
2211
2212 switch (s)
2213 {
2214 case "a":
2215 if (TriplePosition == 1)
2216 return RdfDocument.RdfType;
2217 break;
2218
2219 case "true":
2220 if (TriplePosition == 2)
2221 return new BooleanLiteral(true);
2222 break;
2223
2224 case "false":
2225 if (TriplePosition == 2)
2226 return new BooleanLiteral(false);
2227 break;
2228 }
2229
2230 if (string.Compare(s, "UNDEF", true) == 0)
2231 {
2232 if (TriplePosition != 2)
2233 throw Parser.SyntaxError("UNDEF not permitted.");
2234
2235 return new UndefinedLiteral();
2236 }
2237
2238 ScriptNode ScriptNode = this.ParseFunction(Parser, s, Start2, true);
2239
2240 if (ScriptNode is null)
2241 {
2242 if (TriplePosition == 0)
2243 {
2244 Parser.UndoChar();
2245 return null;
2246 }
2247 else
2248 throw Parser.SyntaxError("Expected :");
2249 }
2250
2252 }
2253 else
2254 {
2255 if (TriplePosition != 2)
2256 throw Parser.SyntaxError("Literals can only occur in object position.");
2257
2258 Parser.UndoChar();
2259 return this.ParseNumber(Parser);
2260 }
2261 }
2262 }
2263 }
2264
2265 private BlankNode CreateBlankNode()
2266 {
2267 return new BlankNode("n" + (++this.blankNodeIndex).ToString());
2268 }
2269
2270 private ISemanticElement ParseCollection(ScriptParser Parser,
2271 out ChunkedList<SemanticQueryTriple> AdditionalTriples)
2272 {
2273 ChunkedList<ISemanticElement> Elements = null;
2274 AdditionalTriples = null;
2275
2276 Parser.SkipWhiteSpace();
2277
2278 while (Parser.InScript)
2279 {
2280 if (Parser.PeekNextChar() == ')')
2281 {
2282 Parser.NextChar();
2283
2284 if (Elements is null)
2285 return RdfDocument.RdfNil;
2286
2288 BlankNode Result = this.CreateBlankNode();
2289 BlankNode Current = Result;
2290 int i, c;
2291
2292 if (this.currentRegularPattern is null)
2293 {
2294 this.currentRegularPattern = new SparqlRegularPattern();
2295 this.currentPattern = new IntersectionPattern(this.currentPattern, this.currentRegularPattern);
2296 }
2297
2298 while (!(Loop is null))
2299 {
2300 for (i = Loop.Start, c = Loop.Pos; i < c; i++)
2301 {
2302 this.currentRegularPattern.AddTriple(new SemanticQueryTriple(Current, RdfDocument.RdfFirst, Loop[i]));
2303
2304 if (i < c - 1 || !(Loop.Next is null))
2305 {
2306 BlankNode Next = this.CreateBlankNode();
2307 this.currentRegularPattern.AddTriple(new SemanticQueryTriple(Current, RdfDocument.RdfRest, Next));
2308 Current = Next;
2309 }
2310 }
2311
2312 Loop = Loop.Next;
2313 }
2314
2315 this.currentRegularPattern.AddTriple(new SemanticQueryTriple(Current, RdfDocument.RdfRest, RdfDocument.RdfNil));
2316
2317 return Result;
2318 }
2319
2320 ISemanticElement Element = this.ParseElement(Parser, 2,
2321 out ChunkedList<SemanticQueryTriple> AdditionalTriples2);
2322
2323 if (!(AdditionalTriples2 is null))
2324 {
2325 if (AdditionalTriples is null)
2326 AdditionalTriples = new ChunkedList<SemanticQueryTriple>();
2327
2328 AdditionalTriples.AddRange(AdditionalTriples2);
2329 }
2330
2331 if (Element is null)
2332 break;
2333
2334 if (Elements is null)
2335 Elements = new ChunkedList<ISemanticElement>();
2336
2337 Elements.Add(Element);
2338 Parser.SkipWhiteSpace();
2339 }
2340
2341 throw Parser.SyntaxError("Expected )");
2342 }
2343
2344 private UriNode ParseUriOrPrefixedToken(ScriptParser Parser)
2345 {
2346 if (Parser.EndOfScript)
2347 throw Parser.SyntaxError("Expected URI or prefixed token.");
2348
2349 if (Parser.PeekNextChar() == '<')
2350 {
2351 Parser.NextChar();
2352 return this.ParseUri(Parser);
2353 }
2354
2355 string Prefix = this.ParseName(Parser);
2356
2357 if (Parser.NextChar() != ':')
2358 throw Parser.SyntaxError("Expected :");
2359
2360 return this.ParsePrefixedToken(Parser, Prefix);
2361 }
2362
2363 private UriNode ParsePrefixedToken(ScriptParser Parser, string Prefix)
2364 {
2365 if (!this.namespaces.TryGetValue(Prefix, out string Namespace))
2366 throw Parser.SyntaxError("Prefix unknown.");
2367
2368 Parser.SkipWhiteSpace();
2369
2370 string LocalName = this.ParseName(Parser);
2371
2372 return new UriNode(new System.Uri(Namespace + LocalName), Prefix + ":" + LocalName);
2373 }
2374
2375 private string ParseName(ScriptParser Parser)
2376 {
2378 return string.Empty;
2379
2380 int Start = Parser.Position;
2381 bool LastPeriod = false;
2382
2383 Parser.NextChar();
2384
2385 while (TurtleDocument.IsNameChar(Parser.PeekNextChar(), ref LastPeriod))
2386 Parser.NextChar();
2387
2388 if (LastPeriod)
2389 Parser.UndoChar();
2390
2391 return Parser.Expression.Script.Substring(Start, Parser.Position - Start);
2392 }
2393
2394 private int ParsePositiveInteger(ScriptParser Parser)
2395 {
2396 Parser.SkipWhiteSpace();
2397
2398 int Start = Parser.Position;
2399
2400 while (char.IsDigit(Parser.PeekNextChar()))
2401 Parser.NextChar();
2402
2403 string s = Parser.Expression.Script.Substring(Start, Parser.Position - Start);
2404
2405 if (!int.TryParse(s, out int i))
2406 throw Parser.SyntaxError("Expected non-negative integer.");
2407
2408 return i;
2409 }
2410
2411 private SemanticLiteral ParseNumber(ScriptParser Parser)
2412 {
2413 int Start = Parser.Position;
2414 char ch = Parser.PeekNextChar();
2415 bool HasDigits = false;
2416 bool HasDecimal = false;
2417 bool HasExponent = false;
2418
2419 if (ch == '+' || ch == '-')
2420 {
2421 Parser.NextChar();
2422 ch = Parser.PeekNextChar();
2423 }
2424
2425 while (char.IsDigit(ch))
2426 {
2427 Parser.NextChar();
2428 ch = Parser.PeekNextChar();
2429 HasDigits = true;
2430 }
2431
2432 if (ch == '.')
2433 {
2434 HasDecimal = true;
2435 Parser.NextChar();
2436 ch = Parser.PeekNextChar();
2437
2438 while (char.IsDigit(ch))
2439 {
2440 Parser.NextChar();
2441 ch = Parser.PeekNextChar();
2442 }
2443 }
2444
2445 if (ch == 'e' || ch == 'E')
2446 {
2447 HasExponent = true;
2448 Parser.NextChar();
2449 ch = Parser.PeekNextChar();
2450
2451 if (ch == '+' || ch == '-')
2452 {
2453 Parser.NextChar();
2454 ch = Parser.PeekNextChar();
2455 }
2456
2457 while (char.IsDigit(ch))
2458 {
2459 Parser.NextChar();
2460 ch = Parser.PeekNextChar();
2461 }
2462 }
2463
2464 if (Parser.Position > Start)
2465 {
2466 string s = Parser.Expression.Script.Substring(Start, Parser.Position - Start);
2467
2468 if (HasExponent)
2469 {
2470 if (CommonTypes.TryParse(s, out double dbl))
2471 return new DoubleLiteral(dbl, s);
2472 else
2473 throw Parser.SyntaxError("Invalid double number.");
2474 }
2475 else if (HasDecimal)
2476 {
2477 if (CommonTypes.TryParse(s, out decimal dec))
2478 return new DecimalLiteral(dec, s);
2479 else
2480 throw Parser.SyntaxError("Invalid decimal number.");
2481 }
2482 else if (HasDigits)
2483 {
2484 if (BigInteger.TryParse(s, out BigInteger bi))
2485 return new IntegerLiteral(bi, s);
2486 else
2487 throw Parser.SyntaxError("Invalid integer number.");
2488 }
2489 }
2490
2491 throw Parser.SyntaxError("Expected value element.");
2492 }
2493
2494 private string ParseString(ScriptParser Parser, char EndChar, bool MultiLine, bool IncludeWhiteSpace)
2495 {
2496 StringBuilder sb = null;
2497 int Start = Parser.Position;
2498 char ch;
2499
2500 while ((ch = Parser.PeekNextChar()) != (char)0)
2501 {
2502 Parser.NextChar();
2503
2504 if (ch == EndChar)
2505 {
2506 if (MultiLine)
2507 {
2508 if (Parser.IsNextChars(EndChar, 2))
2509 {
2510 Parser.SkipChars(2);
2511 return sb?.ToString() ?? Parser.Expression.Script.Substring(Start, Parser.Position - Start - 3);
2512 }
2513 else
2514 sb?.Append(ch);
2515 }
2516 else
2517 return sb?.ToString() ?? Parser.Expression.Script.Substring(Start, Parser.Position - Start - 1);
2518 }
2519 else if (ch == '\\')
2520 {
2521 if (sb is null)
2522 {
2523 sb = new StringBuilder();
2524
2525 if (Parser.Position > Start + 1)
2526 sb.Append(Parser.Expression.Script.Substring(Start, Parser.Position - Start - 1));
2527 }
2528
2529 switch (ch = Parser.NextChar())
2530 {
2531 case (char)0:
2532 throw Parser.SyntaxError("Expected escape code.");
2533
2534 case 't':
2535 sb.Append('\t');
2536 break;
2537
2538 case 'n':
2539 sb.Append('\n');
2540 break;
2541
2542 case 'r':
2543 sb.Append('\r');
2544 break;
2545
2546 case 'v':
2547 sb.Append('\v');
2548 break;
2549
2550 case 'f':
2551 sb.Append('\f');
2552 break;
2553
2554 case 'b':
2555 sb.Append('\b');
2556 break;
2557
2558 case 'a':
2559 sb.Append('\a');
2560 break;
2561
2562 case 'u':
2563 if (Parser.HasCharacters(4) && int.TryParse(Parser.PeekNextChars(4), System.Globalization.NumberStyles.HexNumber, null, out int i))
2564 {
2565 sb.Append((char)i);
2566 Parser.SkipChars(4);
2567 }
2568 else
2569 throw Parser.SyntaxError("Expected 4-character hexadecimal code.");
2570 break;
2571
2572 case 'U':
2573 if (Parser.HasCharacters(8) && int.TryParse(Parser.PeekNextChars(8), System.Globalization.NumberStyles.HexNumber, null, out i))
2574 {
2575 sb.Append((char)i);
2576 Parser.SkipChars(8);
2577 }
2578 else
2579 throw Parser.SyntaxError("Expected 8-character hexadecimal code.");
2580 break;
2581
2582 default:
2583 sb.Append(ch);
2584 break;
2585 }
2586 }
2587 else if (IncludeWhiteSpace || !char.IsWhiteSpace(ch))
2588 sb?.Append(ch);
2589 }
2590
2591 throw Parser.SyntaxError("Expected " + new string(EndChar, MultiLine ? 3 : 1));
2592 }
2593
2594 private UriNode ParseUri(ScriptParser Parser)
2595 {
2596 string Short = this.ParseString(Parser, '>', false, false);
2597
2598 if (this.baseUri is null)
2599 {
2600 if (System.Uri.TryCreate(Short, UriKind.RelativeOrAbsolute, out System.Uri URI))
2601 return new UriNode(URI, Short);
2602 else
2603 throw Parser.SyntaxError("Invalid URI.");
2604 }
2605 else
2606 {
2607 if (string.IsNullOrEmpty(Short))
2608 return new UriNode(this.baseUri, Short);
2609 else if (System.Uri.TryCreate(this.baseUri, Short, out System.Uri URI))
2610 return new UriNode(URI, Short);
2611 else
2612 throw Parser.SyntaxError("Invalid URI.");
2613 }
2614 }
2615
2616 }
2617}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:15
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:48
Coalesce(Expression, ...)
Definition: Coalesce.cs:15
LangMatches(Language,Pattern)
Definition: LangMatches.cs:13
Triple(Subject,Predicate,Object)
Definition: Triple.cs:13
Represents a blank node
Definition: BlankNode.cs:7
Represents an integer literal of undefined size.
static ISemanticElement Parse(string Value, string DataType, string Language)
Parses a string literal value.
Abstract base class for semantic literal values.
Contains semantic information stored in an RDF document.
Definition: RdfDocument.cs:23
static UriNode RdfType
rdf:type predicate
Definition: RdfDocument.cs:27
static UriNode RdfNil
Predefined reference to end of collection.
Definition: RdfDocument.cs:42
static UriNode RdfRest
Predefined reference to next element in a collection.
Definition: RdfDocument.cs:37
static UriNode RdfFirst
Predefined reference to first element in a collection.
Definition: RdfDocument.cs:32
Contains semantic information stored in a turtle document. https://www.w3.org/TR/rdf12-turtle/ https:...
static bool IsNameChar(char ch, ref bool LastPeriod)
Checks if a character can be included in a name.
static bool IsNameStartChar(char ch)
Checks if a character is a character that can start a name.
Node referencing a chunk in a ChunkedList<T>
Definition: ChunkNode.cs:11
ChunkNode< T > Next
Next chunk
Definition: ChunkNode.cs:26
int Pos
Index after the last element in chunk.
Definition: ChunkNode.cs:51
int Start
Index of first element in chunk.
Definition: ChunkNode.cs:46
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
ChunkNode< T > FirstChunk
First chunk
Definition: ChunkedList.cs:259
int Count
Number of elements in collection.
Definition: ChunkedList.cs:68
void Add(T Item)
Adds an item to the collection.
Definition: ChunkedList.cs:272
T[] ToArray()
Returns an array containing all elements of the collection.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
string Script
Original script string.
Definition: Expression.cs:207
Concatenates the elements of a vector, optionally delimiting the elements with a Delimiter.
Definition: Concat.cs:12
EndsWith(String,Substring)
Definition: EndsWith.cs:11
Replace(String,From,To)
Definition: Replace.cs:13
StartsWith(String,Substring)
Definition: StartsWith.cs:11
ScriptNode RightOperand
Right operand.
ScriptNode LeftOperand
Left operand.
Represents a constant element value.
Base class for all funcions.
Definition: Function.cs:7
ScriptNode Argument
Function argument.
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
int Length
Length of expression covered by node.
Definition: ScriptNode.cs:101
override string ToString()
Definition: ScriptNode.cs:359
int Start
Start position in script expression.
Definition: ScriptNode.cs:92
Script parser, for custom parsers.
Definition: ScriptParser.cs:10
SyntaxException SyntaxError(string Message)
Returns a Syntax Error Exception object.
string PeekNextToken()
Returns the next token to be parsed, without moving the position forward. If at the end of the expres...
bool HasCharacters(int NrCharacters)
If there are a given number of characters left to parse.
bool IsNextChars(string Token)
If the next characters to be parsed is a given token.
string NextToken()
Returns the next token to be parsed, and moves the position forward correspondingly....
int Start
Start position in expression
Definition: ScriptParser.cs:28
int Position
Current parsing position.
Definition: ScriptParser.cs:38
bool InScript
If position is in script.
Expression Expression
Expression being parsed.
Definition: ScriptParser.cs:43
void UndoChar()
Undoes a character in the parsing of an expression.
Definition: ScriptParser.cs:82
string PeekNextChars(int NrChars)
Returns the next given number of characters to be parsed, without moving the position forward one cha...
Definition: ScriptParser.cs:92
void SkipChars(int NrChars)
Skips a predefined number of characters.
char PeekNextChar()
Returns the next character to be parsed, without moving the position forward one character....
bool EndOfScript
If position is at end of script.
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....
ScriptNode ParseObject()
Parses an object ex nihilo.
char NextNonWhitespaceChar()
Returns the next non-whitespace character to be parsed, and moves the position forward accordingly....
Definition: ScriptParser.cs:66
Represents a variable reference.
Boolean-valued number.
Definition: BooleanValue.cs:12
static readonly BooleanValue False
Constant false value.
static readonly BooleanValue True
Constant true value.
Checks if a pattern has at least a match.
Definition: Exists.cs:15
Extension of the Like operator, that allows the script to set options.
Checks if a pattern has at least no matches.
Definition: NotExists.cs:15
SparqlParser(string Preamble)
Parses a SPARQL statement
Definition: SparqlParser.cs:80
bool TryParse(ScriptParser Parser, int Start, out ScriptNode Result)
Tries to parse a script node.
string[] InternalKeywords
Any keywords used internally by the custom parser.
static readonly SparqlParser RefInstance
Reference instance of SPARQL parser.
Definition: SparqlParser.cs:44
string[] Aliases
Keyword aliases, if available, null if none.
Definition: SparqlParser.cs:96
string KeyWord
Keyword associated with custom parser.
Definition: SparqlParser.cs:91
bool TryParse(ScriptParser Parser, out ScriptNode Result)
Tries to parse a script node.
Complement of a pattern (right) in another (left).
A pattern referencing a named source.
Definition: GraphPattern.cs:16
void AddFilter(ScriptNode Filter)
Adds a filter to the pattern.
void AddTriple(SemanticQueryTriple Triple)
Adds a triple to the pattern
ChunkedList< SemanticQueryTriple > Triples
Triples, null if none.
void AddVariableBinding(ScriptNode Value, ScriptNode Variable)
Adds a variable binding to the pattern.
IEnumerable< IFilterNode > Filter
Filter, null if none.
IEnumerable< KeyValuePair< ScriptNode, ScriptNode > > BoundVariables
Bound variables, null if none.
Generates a random number using the uniform distribution.
Definition: Uniform.cs:15
Interface for semantic nodes.
Interface for keywords with custom parsing.
Definition: IKeyWord.cs:9
Definition: ImplTypes.g.cs:58
class Names(Vector NamesVector)
Contains a collection of distinguished names.
Definition: Names.cs:9
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.
QueryType
SPARQL query type.
Definition: SparqlQuery.cs:27
Prefix
SI prefixes. http://physics.nist.gov/cuu/Units/prefixes.html
Definition: Prefixes.cs:11
Definition: App.xaml.cs:4