Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Expression.cs
1using System;
4using System.Globalization;
5using System.Numerics;
6using System.Reflection;
7using System.Text;
8using System.Threading.Tasks;
9using Waher.Events;
34
35namespace Waher.Script
36{
40 public class Expression
41 {
42 private readonly static object searchSynch = new object();
43 private static Dictionary<Type, Dictionary<Type, ITypeConverter>> converters = null;
44 private static Dictionary<string, FunctionRef> functions = null;
45 private static Dictionary<string, IConstant> constants = null;
46 private static Dictionary<string, IKeyWord> customKeyWords = null;
47 private static readonly Dictionary<Type, ICustomStringOutput> output = new Dictionary<Type, ICustomStringOutput>();
48 internal static readonly Dictionary<string, bool> keywords = GetKeywords();
49
50 private ScriptNode root;
51 private readonly string script;
52 private readonly string source;
53 private object tag;
54 private int pos;
55 private readonly int len;
56 private bool containsImplicitPrint = false;
57 private bool canSkipWhitespace = true;
58
63 public Expression(string Script)
64 : this(Script, null, null)
65 {
66 }
67
73 public Expression(string Script, string Source)
74 : this(Script, Source, null)
75 {
76 }
77
84 public Expression(string Script, object Tag)
85 : this(Script, null, Tag)
86 {
87 }
88
96 public Expression(string Script, string Source, object Tag)
97 {
98 this.script = Script;
99 this.source = Source;
100 this.tag = Tag;
101 this.pos = 0;
102 this.len = this.script.Length;
103
104 this.root = this.ParseSequence();
105 if (this.pos < this.len)
106 throw new SyntaxException("Unexpected end of script.", this.pos, this.script);
107 }
108
109 static Expression()
110 {
111 Types.OnInvalidated += Types_OnInvalidated;
112 }
113
114 private static void Types_OnInvalidated(object Sender, EventArgs e)
115 {
116 functions = null;
117 constants = null;
118
119 lock (output)
120 {
121 output.Clear();
122 }
123 }
124
125 private static Dictionary<string, bool> GetKeywords()
126 {
127 Dictionary<string, bool> Result = new Dictionary<string, bool>(StringComparer.CurrentCultureIgnoreCase)
128 {
129 { "AND", true },
130 { "AS", true },
131 { "CARTESIAN", true },
132 { "CATCH", true },
133 { "CROSS", true },
134 { "DO", true },
135 { "DOT", true },
136 { "EACH", true },
137 { "ELSE", true },
138 { "FINALLY", true },
139 { "FOR", true },
140 { "FOREACH", true },
141 { "IF", true },
142 { "IN", true },
143 { "INHERITS", true },
144 { "INTERSECT", true },
145 { "INTERSECTION", true },
146 { "IS", true },
147 { "LIKE", true },
148 { "MATCHES", true },
149 { "MOD", true },
150 { "NAND", true },
151 { "NOR", true },
152 { "NOT", true },
153 { "NOTIN", true },
154 { "NOTLIKE", true },
155 { "OR", true },
156 { "OVER", true },
157 { "STEP", true },
158 { "THEN", true },
159 { "TO", true },
160 { "TRY", true },
161 { "UNION", true },
162 { "UNLIKE", true },
163 { "WHILE", true },
164 { "XNOR", true },
165 { "XOR", true }
166 };
167
168 if (customKeyWords is null)
169 Search();
170
171 foreach (IKeyWord KeyWord in customKeyWords.Values)
172 {
173 Result[KeyWord.KeyWord.ToUpper()] = true;
174
175 string[] Aliases = KeyWord.Aliases;
176 if (!(Aliases is null))
177 {
178 foreach (string s in Aliases)
179 Result[s.ToUpper()] = true;
180 }
181
182 Aliases = KeyWord.InternalKeywords;
183 if (!(Aliases is null))
184 {
185 foreach (string s in Aliases)
186 Result[s.ToUpper()] = true;
187 }
188 }
189
190 return Result;
191 }
192
193 internal int Position => this.pos;
194
195 internal bool EndOfScript => this.pos >= this.len;
196 internal bool InScript => this.pos < this.len;
197
198 internal bool CanSkipWhitespace
199 {
200 get => this.canSkipWhitespace;
201 set => this.canSkipWhitespace = value;
202 }
203
207 public string Script => this.script;
208
212 public string Source => this.source;
213
214 internal char NextChar()
215 {
216 if (this.pos < this.len)
217 return this.script[this.pos++];
218 else
219 return (char)0;
220 }
221
222 internal void UndoChar()
223 {
224 if (this.pos > 0)
225 this.pos--;
226 }
227
228 internal char PeekNextChar()
229 {
230 if (this.pos < this.len)
231 return this.script[this.pos];
232 else
233 return (char)0;
234 }
235
236 internal string PeekNextChars(int NrChars)
237 {
238 if (this.pos + NrChars > this.len)
239 NrChars = this.len - this.pos;
240
241 if (NrChars <= 0)
242 return string.Empty;
243
244 return this.script.Substring(this.pos, NrChars);
245 }
246
247 internal bool IsNextChars(string Token)
248 {
249 int c = Token.Length;
250 if (c == 0)
251 return true;
252
253 if (this.pos + c > this.len)
254 return false;
255
256 int i;
257
258 for (i = 0; i < c; i++)
259 {
260 if (this.script[this.pos + i] != Token[i])
261 return false;
262 }
263
264 return true;
265 }
266
267 internal bool IsNextChars(char ch, int Count)
268 {
269 if (Count < 0)
270 return false;
271
272 if (this.pos + Count > this.len)
273 return false;
274
275 int i;
276
277 for (i = 0; i < Count; i++)
278 {
279 if (this.script[this.pos + i] != ch)
280 return false;
281 }
282
283 return true;
284 }
285
286 internal void SkipChars(int NrChars)
287 {
288 this.pos += NrChars;
289 }
290
291 internal string NextToken()
292 {
293 this.SkipWhiteSpace();
294
295 if (this.pos >= this.len)
296 return string.Empty;
297
298 int Start = this.pos;
299 char ch = this.script[this.pos];
300
301 if (char.IsLetter(ch))
302 {
303 while (this.pos < this.len && char.IsLetterOrDigit(this.script[this.pos]))
304 this.pos++;
305 }
306 else if (char.IsDigit(ch))
307 {
308 while (this.pos < this.len && char.IsDigit(this.script[this.pos]))
309 this.pos++;
310 }
311 else if (char.IsSymbol(ch))
312 {
313 while (this.pos < this.len && char.IsSymbol(this.script[this.pos]))
314 this.pos++;
315 }
316 else
317 this.pos++;
318
319 return this.script.Substring(Start, this.pos - Start);
320 }
321
322 internal string PeekNextToken()
323 {
324 int Bak = this.pos;
325 string Token = this.NextToken();
326 this.pos = Bak;
327
328 return Token;
329 }
330
331 internal void SkipWhiteSpace()
332 {
333 if (this.canSkipWhitespace)
334 {
335 char ch, ch2;
336
337 while (this.pos < this.len)
338 {
339 ch = this.script[this.pos];
340
341 if (ch <= ' ' || ch == 160)
342 this.pos++;
343 else if (ch == '/' &&
344 this.pos + 1 < this.len &&
345 ((ch2 = this.script[this.pos + 1]) == '/' || ch2 == '*'))
346 {
347 this.pos += 2;
348
349 if (ch2 == '/')
350 {
351 while (this.pos < this.len &&
352 (ch = this.script[this.pos]) != '\n' && ch != '\r')
353 {
354 this.pos++;
355 }
356 }
357 else
358 {
359 while (this.pos < this.len &&
360 (this.script[this.pos] != '*' ||
361 this.pos + 1 == this.len ||
362 this.script[this.pos + 1] != '/'))
363 {
364 this.pos++;
365 }
366
367 if (this.pos + 1 < this.len)
368 this.pos += 2;
369 }
370 }
371 else
372 break;
373 }
374 }
375 }
376
377 internal ScriptNode AssertOperandNotNull(ScriptNode Node)
378 {
379 if (Node is null)
380 throw new SyntaxException("Operand missing.", this.pos, this.script);
381
382 return Node;
383 }
384
385 internal ScriptNode AssertRightOperandNotNull(ScriptNode Node)
386 {
387 if (Node is null)
388 throw new SyntaxException("Right operand missing.", this.pos, this.script);
389
390 return Node;
391 }
392
393 internal ScriptNode ParseSequence()
394 {
395 ScriptNode Node = this.ParseStatement(true);
396 this.SkipWhiteSpace();
397
398 if (Node is null)
399 {
400 while (Node is null && this.PeekNextChar() == ';')
401 {
402 this.pos++;
403 Node = this.ParseStatement(true);
404 this.SkipWhiteSpace();
405 }
406 }
407
408 if (Node is null)
409 return null;
410
411 int Start = Node.Start;
412
413 if (!(Node is null) && this.PeekNextChar() == ';')
414 {
415 this.pos++;
416 ScriptNode Node2 = this.ParseStatement(true);
417 if (!(Node2 is null))
418 {
420 {
421 Node,
422 Node2
423 };
424
425 this.SkipWhiteSpace();
426 while (this.PeekNextChar() == ';')
427 {
428 this.pos++;
429 Node2 = this.ParseStatement(true);
430 if (Node2 is null)
431 break;
432
433 Statements.Add(Node2);
434 this.SkipWhiteSpace();
435 }
436
437 Node = new Sequence(Statements, Start, this.pos - Start, this);
438 }
439 }
440
441 return Node;
442 }
443
444 internal ScriptNode ParseStatement(bool ParseLists)
445 {
446 this.SkipWhiteSpace();
447
448 int Start = this.pos;
449
450 switch (char.ToUpper(this.PeekNextChar()))
451 {
452 case 'D':
453 if (string.Compare(this.PeekNextToken(), "DO", true) == 0)
454 {
455 this.pos += 2;
456
457 ScriptNode Statement = this.AssertOperandNotNull(this.ParseStatement(false));
458
459 this.SkipWhiteSpace();
460 if (string.Compare(this.PeekNextToken(), "WHILE", true) != 0)
461 throw new SyntaxException("Expected WHILE.", this.pos, this.script);
462
463 this.pos += 5;
464
465 ScriptNode Condition = this.AssertOperandNotNull(this.ParseIf());
466
467 return new DoWhile(Statement, Condition, Start, this.pos - Start, this);
468 }
469 else
470 return ParseLists ? this.ParseList() : this.ParseIf();
471
472 case 'W':
473 if (string.Compare(this.PeekNextToken(), "WHILE", true) == 0)
474 {
475 this.pos += 5;
476
477 ScriptNode Condition = this.AssertOperandNotNull(this.ParseIf());
478
479 this.SkipWhiteSpace();
480 if (this.PeekNextChar() == ':')
481 this.pos++;
482 else if (string.Compare(this.PeekNextToken(), "DO", true) == 0)
483 this.pos += 2;
484 else
485 throw new SyntaxException("DO or : expected.", this.pos, this.script);
486
487 ScriptNode Statement = this.AssertOperandNotNull(this.ParseStatement(false));
488
489 return new WhileDo(Condition, Statement, Start, this.pos - Start, this);
490 }
491 else
492 return ParseLists ? this.ParseList() : this.ParseIf();
493
494 case 'F':
495 switch (this.PeekNextToken().ToUpper())
496 {
497 case "FOREACH":
498 this.pos += 7;
499 if (!(this.AssertOperandNotNull(this.ParseIf()) is In In))
500 throw new SyntaxException("IN statement expected", this.pos, this.script);
501
502 if (!(In.LeftOperand is VariableReference Ref))
503 throw new SyntaxException("Variable reference expected", In.LeftOperand.Start, this.script);
504
505 this.SkipWhiteSpace();
506 if (this.PeekNextChar() == ':')
507 this.pos++;
508 else if (string.Compare(this.PeekNextToken(), "DO", true) == 0)
509 this.pos += 2;
510 else
511 throw new SyntaxException("DO or : expected.", this.pos, this.script);
512
513 ScriptNode Statement = this.AssertOperandNotNull(this.ParseStatement(false));
514
515 return new ForEach(Ref.VariableName, In.RightOperand, Statement, Start, this.pos - Start, this);
516
517 case "FOR":
518 this.pos += 3;
519 this.SkipWhiteSpace();
520
521 if (string.Compare(this.PeekNextToken(), "EACH", true) == 0)
522 {
523 this.pos += 4;
524 In = this.AssertOperandNotNull(this.ParseIf()) as In;
525 if (In is null)
526 throw new SyntaxException("IN statement expected", this.pos, this.script);
527
528 Ref = In.LeftOperand as VariableReference;
529 if (Ref is null)
530 throw new SyntaxException("Variable reference expected", In.LeftOperand.Start, this.script);
531
532 this.SkipWhiteSpace();
533 if (this.PeekNextChar() == ':')
534 this.pos++;
535 else if (string.Compare(this.PeekNextToken(), "DO", true) == 0)
536 this.pos += 2;
537 else
538 throw new SyntaxException("DO or : expected.", this.pos, this.script);
539
540 Statement = this.AssertOperandNotNull(this.ParseStatement(false));
541
542 return new ForEach(Ref.VariableName, In.RightOperand, Statement, Start, this.pos - Start, this);
543 }
544 else
545 {
546 if (!(this.AssertOperandNotNull(this.ParseIf()) is Assignment Assignment))
547 throw new SyntaxException("Assignment expected", this.pos, this.script);
548
549 this.SkipWhiteSpace();
550 if (string.Compare(this.PeekNextToken(), "TO", true) != 0)
551 throw new SyntaxException("Expected TO.", this.pos, this.script);
552
553 this.pos += 2;
554
555 ScriptNode To = this.AssertOperandNotNull(this.ParseIf());
556 ScriptNode Step;
557
558 this.SkipWhiteSpace();
559 if (string.Compare(this.PeekNextToken(), "STEP", true) == 0)
560 {
561 this.pos += 4;
562 Step = this.AssertOperandNotNull(this.ParseIf());
563 }
564 else
565 Step = null;
566
567 this.SkipWhiteSpace();
568 if (this.PeekNextChar() == ':')
569 this.pos++;
570 else if (string.Compare(this.PeekNextToken(), "DO", true) == 0)
571 this.pos += 2;
572 else
573 throw new SyntaxException("DO or : expected.", this.pos, this.script);
574
575 Statement = this.AssertOperandNotNull(this.ParseStatement(false));
576
577 return new For(Assignment.VariableName, Assignment.Operand, To, Step, Statement, Start, this.pos - Start, this);
578 }
579
580 default:
581 return ParseLists ? this.ParseList() : this.ParseIf();
582 }
583
584 case 'T':
585 if (string.Compare(this.PeekNextToken(), "TRY", true) == 0)
586 {
587 this.pos += 3;
588
589 ScriptNode Statement = this.AssertOperandNotNull(this.ParseStatement(false));
590
591 this.SkipWhiteSpace();
592 switch (this.PeekNextToken().ToUpper())
593 {
594 case "FINALLY":
595 this.pos += 7;
596 ScriptNode Finally = this.AssertOperandNotNull(this.ParseStatement(false));
597 return new TryFinally(Statement, Finally, Start, this.pos - Start, this);
598
599 case "CATCH":
600 this.pos += 5;
601 ScriptNode Catch = this.AssertOperandNotNull(this.ParseStatement(false));
602
603 this.SkipWhiteSpace();
604 if (string.Compare(this.PeekNextToken(), "FINALLY", true) == 0)
605 {
606 this.pos += 7;
607 Finally = this.AssertOperandNotNull(this.ParseStatement(false));
608 return new TryCatchFinally(Statement, Catch, Finally, Start, this.pos - Start, this);
609 }
610 else
611 return new TryCatch(Statement, Catch, Start, this.pos - Start, this);
612
613 default:
614 throw new SyntaxException("Expected CATCH or FINALLY.", this.pos, this.script);
615 }
616 }
617 else
618 return ParseLists ? this.ParseList() : this.ParseIf();
619
620 case ']':
621 this.pos++;
622 if (this.PeekNextChar() == ']')
623 {
624 this.pos++;
625
626 StringBuilder sb = new StringBuilder();
627 char ch;
628
629 while ((ch = this.NextChar()) != '[' || this.PeekNextChar() != '[')
630 {
631 if (ch == 0)
632 throw new SyntaxException("Expected [[.", this.pos, this.script);
633
634 sb.Append(ch);
635 }
636
637 this.pos++;
638 this.containsImplicitPrint = true;
639 return new ImplicitPrint(sb.ToString(), Start, this.pos - Start, this);
640 }
641 else
642 {
643 this.pos--;
644 return ParseLists ? this.ParseList() : this.ParseIf();
645 }
646
647 default:
648 return ParseLists ? this.ParseList() : this.ParseIf();
649 }
650 }
651
652 internal ScriptNode ParseList()
653 {
654 ScriptNode Node = this.ParseIf();
655 int Start;
656
657 if (Node is null) // Allow null
658 Start = this.pos;
659 else
660 Start = Node.Start;
661
662 this.SkipWhiteSpace();
663 if (this.PeekNextChar() == ',')
664 {
666 {
667 Node
668 };
669
670 while (this.PeekNextChar() == ',')
671 {
672 this.pos++;
673 Node = this.ParseIf();
674
675 Elements.Add(Node);
676
677 this.SkipWhiteSpace();
678 }
679
680 Node = new ElementList(Elements.ToArray(), Start, this.pos - Start, this);
681 }
682
683 return Node;
684 }
685
686 internal ScriptNode ParseIf()
687 {
688 this.SkipWhiteSpace();
689
690 ScriptNode Condition;
691 ScriptNode IfTrue;
692 ScriptNode IfFalse;
693 int Start = this.pos;
694
695 if (char.ToUpper(this.PeekNextChar()) == 'I' && string.Compare(this.PeekNextToken(), "IF", true) == 0)
696 {
697 this.pos += 2;
698 this.SkipWhiteSpace();
699
700 Condition = this.AssertOperandNotNull(this.ParseAssignments());
701
702 this.SkipWhiteSpace();
703 if (string.Compare(this.PeekNextToken(), "THEN", true) == 0)
704 this.pos += 4;
705 else
706 throw new SyntaxException("THEN expected.", this.pos, this.script);
707
708 IfTrue = this.AssertOperandNotNull(this.ParseStatement(false));
709
710 this.SkipWhiteSpace();
711 if (string.Compare(this.PeekNextToken(), "ELSE", true) == 0)
712 {
713 this.pos += 4;
714 IfFalse = this.AssertOperandNotNull(this.ParseStatement(false));
715 }
716 else
717 IfFalse = null;
718 }
719 else
720 {
721 Condition = this.ParseAssignments();
722 if (Condition is null)
723 return null;
724
725 this.SkipWhiteSpace();
726 if (this.PeekNextChar() != '?')
727 return Condition;
728
729 this.pos++;
730
731 switch (this.PeekNextChar())
732 {
733 case '.':
734 case '(':
735 case '[':
736 case '{':
737 this.pos--;
738 return Condition; // Null-check operator
739
740 case '?':
741 this.pos++;
742 if (this.PeekNextChar() == '?')
743 {
744 this.pos++;
745 IfTrue = this.AssertOperandNotNull(this.ParseStatement(false));
746 return new TryCatch(Condition, IfTrue, Start, this.pos - Start, this);
747 }
748 else
749 {
750 IfTrue = this.AssertOperandNotNull(this.ParseStatement(false));
751 return new NullCheck(Condition, IfTrue, Start, this.pos - Start, this);
752 }
753
754 default:
755 IfTrue = this.AssertOperandNotNull(this.ParseStatement(false));
756
757 this.SkipWhiteSpace();
758 if (this.PeekNextChar() == ':')
759 {
760 this.pos++;
761 IfFalse = this.AssertOperandNotNull(this.ParseStatement(false));
762 }
763 else
764 IfFalse = null;
765
766 break;
767 }
768 }
769
770 return new If(Condition, IfTrue, IfFalse, Start, this.pos - Start, this);
771 }
772
773 internal ScriptNode ParseAssignments()
774 {
775 ScriptNode Left = this.ParseLambdaExpression();
776 if (Left is null)
777 return null;
778
779 int Start = Left.Start;
781
782 this.SkipWhiteSpace();
783
784 switch (this.PeekNextChar())
785 {
786 case ':':
787 this.pos++;
788 if (this.PeekNextChar() == '=')
789 {
790 this.pos++;
791 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
792
793 if (!(Ref is null))
794 return new Assignment(Ref.VariableName, Right, Start, this.pos - Start, this);
795 else if (Left is NamedMember NamedMember)
796 return new NamedMemberAssignment(NamedMember, Right, Start, this.pos - Start, this);
797 else if (Left is DynamicMember DynamicMember)
798 return new DynamicMemberAssignment(DynamicMember, Right, Start, this.pos - Start, this);
799 else if (Left is VectorIndex VectorIndex)
800 return new VectorIndexAssignment(VectorIndex, Right, Start, this.pos - Start, this);
801 else if (Left is MatrixIndex MatrixIndex)
802 return new MatrixIndexAssignment(MatrixIndex, Right, Start, this.pos - Start, this);
803 else if (Left is ColumnVector ColumnVector)
804 return new MatrixColumnAssignment(ColumnVector, Right, Start, this.pos - Start, this);
805 else if (Left is RowVector RowVector)
806 return new MatrixRowAssignment(RowVector, Right, Start, this.pos - Start, this);
807 else if (Left is DynamicIndex DynamicIndex)
808 return new DynamicIndexAssignment(DynamicIndex, Right, Start, this.pos - Start, this);
809 else if (Left is NamedFunctionCall f)
810 {
811 ChunkedList<string> ArgumentNames = new ChunkedList<string>();
814
815 foreach (ScriptNode Argument in f.Arguments)
816 {
817 if (Argument is ToVector ToVector)
818 {
819 ArgumentType = ArgumentType.Vector;
820
821 if ((Ref = ToVector.Operand as VariableReference) is null)
822 {
823 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
824 Argument.Start, this.script);
825 }
826 }
827 else if (Argument is ToMatrix ToMatrix)
828 {
829 ArgumentType = ArgumentType.Matrix;
830
831 if ((Ref = ToMatrix.Operand as VariableReference) is null)
832 {
833 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
834 Argument.Start, this.script);
835 }
836 }
837 else if (Argument is ToSet ToSet)
838 {
840
841 if ((Ref = ToSet.Operand as VariableReference) is null)
842 {
843 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
844 Argument.Start, this.script);
845 }
846 }
847 else if (Argument is VectorDefinition Def)
848 {
849 ArgumentType = ArgumentType.Scalar;
850
851 if (Def.Elements.Length != 1 || (Ref = Def.Elements[0] as VariableReference) is null)
852 {
853 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
854 Argument.Start, this.script);
855 }
856 }
857 else if (!((Ref = Argument as VariableReference) is null))
858 {
859 ArgumentType = ArgumentType.Normal;
860 }
861 else
862 {
863 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
864 Argument.Start, this.script);
865 }
866
867 if (ArgumentNames.Contains(Ref.VariableName))
868 throw new SyntaxException("Argument name already used.", Argument.Start, this.script);
869
870 ArgumentNames.Add(Ref.VariableName);
871 ArgumentTypes.Add(ArgumentType);
872 }
873
874 return new FunctionDefinition(f.FunctionName, ArgumentNames.ToArray(), ArgumentTypes.ToArray(), Right, Start, this.pos - Start, this);
875 }
876 else
877 return new PatternMatch(Left, Right, Start, this.pos - Start, this);
878 }
879 else
880 {
881 this.pos--;
882 return Left;
883 }
884
885 case '+':
886 this.pos++;
887 if (this.PeekNextChar() == '=')
888 {
889 this.pos++;
890
891 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
892
893 if (!(Ref is null))
894 return new Operators.Assignments.WithSelf.AddToSelf(Ref.VariableName, Right, Start, this.pos - Start, this);
895 else if (Left is NamedMember NamedMember)
896 return new NamedMemberAssignment(NamedMember, new Add(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
897 else if (Left is VectorIndex VectorIndex)
898 return new VectorIndexAssignment(VectorIndex, new Add(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
899 else if (Left is MatrixIndex MatrixIndex)
900 return new MatrixIndexAssignment(MatrixIndex, new Add(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
901 else if (Left is ColumnVector ColumnVector)
902 return new MatrixColumnAssignment(ColumnVector, new Add(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
903 else if (Left is RowVector RowVector)
904 return new MatrixRowAssignment(RowVector, new Add(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
905 else
906 throw new SyntaxException("Invalid use of the += operator.", this.pos, this.script);
907 }
908 else
909 {
910 this.pos--;
911 return Left;
912 }
913
914 case '-':
915 this.pos++;
916 if (this.PeekNextChar() == '=')
917 {
918 this.pos++;
919
920 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
921
922 if (!(Ref is null))
923 return new Operators.Assignments.WithSelf.SubtractFromSelf(Ref.VariableName, Right, Start, this.pos - Start, this);
924 else if (Left is NamedMember NamedMember)
925 return new NamedMemberAssignment(NamedMember, new Subtract(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
926 else if (Left is VectorIndex VectorIndex)
927 return new VectorIndexAssignment(VectorIndex, new Subtract(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
928 else if (Left is MatrixIndex MatrixIndex)
929 return new MatrixIndexAssignment(MatrixIndex, new Subtract(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
930 else if (Left is ColumnVector ColumnVector)
931 return new MatrixColumnAssignment(ColumnVector, new Subtract(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
932 else if (Left is RowVector RowVector)
933 return new MatrixRowAssignment(RowVector, new Subtract(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
934 else
935 throw new SyntaxException("Invalid use of the -= operator.", this.pos, this.script);
936 }
937 else
938 {
939 this.pos--;
940 return Left;
941 }
942
943 case '⋅':
944 case '*':
945 this.pos++;
946 if (this.PeekNextChar() == '=')
947 {
948 this.pos++;
949
950 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
951
952 if (!(Ref is null))
953 return new Operators.Assignments.WithSelf.MultiplyWithSelf(Ref.VariableName, Right, Start, this.pos - Start, this);
954 else if (Left is NamedMember NamedMember)
955 return new NamedMemberAssignment(NamedMember, new Multiply(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
956 else if (Left is VectorIndex VectorIndex)
957 return new VectorIndexAssignment(VectorIndex, new Multiply(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
958 else if (Left is MatrixIndex MatrixIndex)
959 return new MatrixIndexAssignment(MatrixIndex, new Multiply(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
960 else if (Left is ColumnVector ColumnVector)
961 return new MatrixColumnAssignment(ColumnVector, new Multiply(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
962 else if (Left is RowVector RowVector)
963 return new MatrixRowAssignment(RowVector, new Multiply(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
964 else
965 throw new SyntaxException("Invalid use of the *= operator.", this.pos, this.script);
966 }
967 else
968 {
969 this.pos--;
970 return Left;
971 }
972
973 case '/':
974 this.pos++;
975 if (this.PeekNextChar() == '=')
976 {
977 this.pos++;
978
979 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
980
981 if (!(Ref is null))
982 return new Operators.Assignments.WithSelf.DivideFromSelf(Ref.VariableName, Right, Start, this.pos - Start, this);
983 else if (Left is NamedMember NamedMember)
984 return new NamedMemberAssignment(NamedMember, new Divide(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
985 else if (Left is VectorIndex VectorIndex)
986 return new VectorIndexAssignment(VectorIndex, new Divide(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
987 else if (Left is MatrixIndex MatrixIndex)
988 return new MatrixIndexAssignment(MatrixIndex, new Divide(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
989 else if (Left is ColumnVector ColumnVector)
990 return new MatrixColumnAssignment(ColumnVector, new Divide(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
991 else if (Left is RowVector RowVector)
992 return new MatrixRowAssignment(RowVector, new Divide(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
993 else
994 throw new SyntaxException("Invalid use of the /= operator.", this.pos, this.script);
995 }
996 else
997 {
998 this.pos--;
999 return Left;
1000 }
1001
1002 case '^':
1003 this.pos++;
1004 if (this.PeekNextChar() == '=')
1005 {
1006 this.pos++;
1007
1008 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
1009
1010 if (!(Ref is null))
1011 return new Operators.Assignments.WithSelf.PowerOfSelf(Ref.VariableName, Right, Start, this.pos - Start, this);
1012 else if (Left is NamedMember NamedMember)
1013 return new NamedMemberAssignment(NamedMember, new Power(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1014 else if (Left is VectorIndex VectorIndex)
1015 return new VectorIndexAssignment(VectorIndex, new Power(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1016 else if (Left is MatrixIndex MatrixIndex)
1017 return new MatrixIndexAssignment(MatrixIndex, new Power(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1018 else if (Left is ColumnVector ColumnVector)
1019 return new MatrixColumnAssignment(ColumnVector, new Power(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1020 else if (Left is RowVector RowVector)
1021 return new MatrixRowAssignment(RowVector, new Power(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1022 else
1023 throw new SyntaxException("Invalid use of the ^= operator.", this.pos, this.script);
1024 }
1025 else
1026 {
1027 this.pos--;
1028 return Left;
1029 }
1030
1031 case '&':
1032 this.pos++;
1033 switch (this.PeekNextChar())
1034 {
1035 case '=':
1036 this.pos++;
1037
1038 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
1039
1040 if (!(Ref is null))
1041 return new Operators.Assignments.WithSelf.BinaryAndWithSelf(Ref.VariableName, Right, Start, this.pos - Start, this);
1042 else if (Left is NamedMember NamedMember)
1043 return new NamedMemberAssignment(NamedMember, new Operators.Binary.And(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1044 else if (Left is VectorIndex VectorIndex)
1045 return new VectorIndexAssignment(VectorIndex, new Operators.Binary.And(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1046 else if (Left is MatrixIndex MatrixIndex)
1047 return new MatrixIndexAssignment(MatrixIndex, new Operators.Binary.And(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1048 else if (Left is ColumnVector ColumnVector)
1049 return new MatrixColumnAssignment(ColumnVector, new Operators.Binary.And(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1050 else if (Left is RowVector RowVector)
1051 return new MatrixRowAssignment(RowVector, new Operators.Binary.And(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1052 else
1053 throw new SyntaxException("Invalid use of the &= operator.", this.pos, this.script);
1054
1055 case '&':
1056 this.pos++;
1057 if (this.PeekNextChar() == '=')
1058 {
1059 this.pos++;
1060
1061 Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
1062
1063 if (!(Ref is null))
1064 return new Operators.Assignments.WithSelf.LogicalAndWithSelf(Ref.VariableName, Right, Start, this.pos - Start, this);
1065 else if (Left is NamedMember NamedMember)
1066 return new NamedMemberAssignment(NamedMember, new Operators.Logical.And(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1067 else if (Left is VectorIndex VectorIndex)
1068 return new VectorIndexAssignment(VectorIndex, new Operators.Logical.And(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1069 else if (Left is MatrixIndex MatrixIndex)
1070 return new MatrixIndexAssignment(MatrixIndex, new Operators.Logical.And(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1071 else if (Left is ColumnVector ColumnVector)
1072 return new MatrixColumnAssignment(ColumnVector, new Operators.Logical.And(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1073 else if (Left is RowVector RowVector)
1074 return new MatrixRowAssignment(RowVector, new Operators.Logical.And(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1075 else
1076 throw new SyntaxException("Invalid use of the &&= operator.", this.pos, this.script);
1077 }
1078 else
1079 {
1080 this.pos -= 2;
1081 return Left;
1082 }
1083
1084 default:
1085 this.pos--;
1086 return Left;
1087 }
1088
1089 case '|':
1090 this.pos++;
1091 switch (this.PeekNextChar())
1092 {
1093 case '=':
1094 this.pos++;
1095
1096 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
1097
1098 if (!(Ref is null))
1099 return new Operators.Assignments.WithSelf.BinaryOrWithSelf(Ref.VariableName, Right, Start, this.pos - Start, this);
1100 else if (Left is NamedMember NamedMember)
1101 return new NamedMemberAssignment(NamedMember, new Operators.Binary.Or(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1102 else if (Left is VectorIndex VectorIndex)
1103 return new VectorIndexAssignment(VectorIndex, new Operators.Binary.Or(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1104 else if (Left is MatrixIndex MatrixIndex)
1105 return new MatrixIndexAssignment(MatrixIndex, new Operators.Binary.Or(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1106 else if (Left is ColumnVector ColumnVector)
1107 return new MatrixColumnAssignment(ColumnVector, new Operators.Binary.Or(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1108 else if (Left is RowVector RowVector)
1109 return new MatrixRowAssignment(RowVector, new Operators.Binary.Or(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1110 else
1111 throw new SyntaxException("Invalid use of the |= operator.", this.pos, this.script);
1112
1113 case '|':
1114 this.pos++;
1115 if (this.PeekNextChar() == '=')
1116 {
1117 this.pos++;
1118
1119 Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
1120
1121 if (!(Ref is null))
1122 return new Operators.Assignments.WithSelf.LogicalOrWithSelf(Ref.VariableName, Right, Start, this.pos - Start, this);
1123 else if (Left is NamedMember NamedMember)
1124 return new NamedMemberAssignment(NamedMember, new Operators.Logical.Or(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1125 else if (Left is VectorIndex VectorIndex)
1126 return new VectorIndexAssignment(VectorIndex, new Operators.Logical.Or(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1127 else if (Left is MatrixIndex MatrixIndex)
1128 return new MatrixIndexAssignment(MatrixIndex, new Operators.Logical.Or(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1129 else if (Left is ColumnVector ColumnVector)
1130 return new MatrixColumnAssignment(ColumnVector, new Operators.Logical.Or(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1131 else if (Left is RowVector RowVector)
1132 return new MatrixRowAssignment(RowVector, new Operators.Logical.Or(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1133 else
1134 throw new SyntaxException("Invalid use of the ||= operator.", this.pos, this.script);
1135 }
1136 else
1137 {
1138 this.pos -= 2;
1139 return Left;
1140 }
1141
1142 default:
1143 this.pos--;
1144 return Left;
1145 }
1146
1147 case '<':
1148 this.pos++;
1149 if (this.PeekNextChar() == '<')
1150 {
1151 this.pos++;
1152 if (this.PeekNextChar() == '=')
1153 {
1154 this.pos++;
1155
1156 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
1157
1158 if (!(Ref is null))
1159 return new Operators.Assignments.WithSelf.ShiftSelfLeft(Ref.VariableName, Right, Start, this.pos - Start, this);
1160 else if (Left is NamedMember NamedMember)
1161 return new NamedMemberAssignment(NamedMember, new ShiftLeft(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1162 else if (Left is VectorIndex VectorIndex)
1163 return new VectorIndexAssignment(VectorIndex, new ShiftLeft(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1164 else if (Left is MatrixIndex MatrixIndex)
1165 return new MatrixIndexAssignment(MatrixIndex, new ShiftLeft(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1166 else if (Left is ColumnVector ColumnVector)
1167 return new MatrixColumnAssignment(ColumnVector, new ShiftLeft(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1168 else if (Left is RowVector RowVector)
1169 return new MatrixRowAssignment(RowVector, new ShiftLeft(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1170 else
1171 throw new SyntaxException("Invalid use of the <<= operator.", this.pos, this.script);
1172 }
1173 else
1174 {
1175 this.pos -= 2;
1176 return Left;
1177 }
1178 }
1179 else
1180 {
1181 this.pos--;
1182 return Left;
1183 }
1184
1185 case '>':
1186 this.pos++;
1187 if (this.PeekNextChar() == '>')
1188 {
1189 this.pos++;
1190 if (this.PeekNextChar() == '=')
1191 {
1192 this.pos++;
1193
1194 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseStatement(false));
1195
1196 if (!(Ref is null))
1197 return new Operators.Assignments.WithSelf.ShiftSelfRight(Ref.VariableName, Right, Start, this.pos - Start, this);
1198 else if (Left is NamedMember NamedMember)
1199 return new NamedMemberAssignment(NamedMember, new ShiftRight(NamedMember, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1200 else if (Left is VectorIndex VectorIndex)
1201 return new VectorIndexAssignment(VectorIndex, new ShiftRight(VectorIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1202 else if (Left is MatrixIndex MatrixIndex)
1203 return new MatrixIndexAssignment(MatrixIndex, new ShiftRight(MatrixIndex, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1204 else if (Left is ColumnVector ColumnVector)
1205 return new MatrixColumnAssignment(ColumnVector, new ShiftRight(ColumnVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1206 else if (Left is RowVector RowVector)
1207 return new MatrixRowAssignment(RowVector, new ShiftRight(RowVector, Right, Start, this.pos - Start, this), Start, this.pos - Start, this);
1208 else
1209 throw new SyntaxException("Invalid use of the >>= operator.", this.pos, this.script);
1210 }
1211 else
1212 {
1213 this.pos -= 2;
1214 return Left;
1215 }
1216 }
1217 else
1218 {
1219 this.pos--;
1220 return Left;
1221 }
1222
1223 default:
1224 return Left;
1225 }
1226 }
1227
1228 internal ScriptNode ParseLambdaExpression()
1229 {
1230 ScriptNode Left = this.ParseEquivalence();
1231 if (Left is null)
1232 return null;
1233
1234 this.SkipWhiteSpace();
1235
1236 if (this.PeekNextChar() == '-')
1237 {
1238 this.pos++;
1239 if (this.PeekNextChar() == '>')
1240 {
1241 this.pos++;
1242
1243 int Start = Left.Start;
1244 string[] ArgumentNames;
1245 ArgumentType[] ArgumentTypes;
1246
1247 if (Left is VariableReference Ref)
1248 {
1249 ArgumentNames = new string[] { Ref.VariableName };
1250 ArgumentTypes = new ArgumentType[] { ArgumentType.Normal };
1251 }
1252 else if (Left is ToVector ToVector)
1253 {
1254 Ref = ToVector.Operand as VariableReference;
1255 if (Ref is null)
1256 {
1257 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
1258 Left.Start, this.script);
1259 }
1260
1261 ArgumentNames = new string[] { Ref.VariableName };
1262 ArgumentTypes = new ArgumentType[] { ArgumentType.Vector };
1263 }
1264 else if (Left is ToMatrix ToMatrix)
1265 {
1266 Ref = ToMatrix.Operand as VariableReference;
1267 if (Ref is null)
1268 {
1269 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
1270 Left.Start, this.script);
1271 }
1272
1273 ArgumentNames = new string[] { Ref.VariableName };
1274 ArgumentTypes = new ArgumentType[] { ArgumentType.Matrix };
1275 }
1276 else if (Left is ToSet ToSet)
1277 {
1278 Ref = ToSet.Operand as VariableReference;
1279 if (Ref is null)
1280 {
1281 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
1282 Left.Start, this.script);
1283 }
1284
1285 ArgumentNames = new string[] { Ref.VariableName };
1286 ArgumentTypes = new ArgumentType[] { ArgumentType.Set };
1287 }
1288 else if (Left is VectorDefinition Def)
1289 {
1290 if (Def.Elements.Length != 1 || (Ref = Def.Elements[0] as VariableReference) is null)
1291 {
1292 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
1293 Left.Start, this.script);
1294 }
1295
1296 ArgumentNames = new string[] { Ref.VariableName };
1297 ArgumentTypes = new ArgumentType[] { ArgumentType.Scalar };
1298 }
1299 else if (Left.GetType() == typeof(ElementList))
1300 {
1301 ElementList List = (ElementList)Left;
1302 int i, c = List.Elements.Length;
1303 ScriptNode Argument;
1304
1305 ArgumentNames = new string[c];
1306 ArgumentTypes = new ArgumentType[c];
1307
1308 for (i = 0; i < c; i++)
1309 {
1310 Argument = List.Elements[i];
1311
1312 if (!((Ref = Argument as VariableReference) is null))
1313 ArgumentTypes[i] = ArgumentType.Normal;
1314 else if (Argument is ToVector ToVector2)
1315 {
1316 Ref = ToVector2.Operand as VariableReference;
1317 if (Ref is null)
1318 {
1319 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
1320 Argument.Start, this.script);
1321 }
1322
1323 ArgumentTypes[i] = ArgumentType.Vector;
1324 }
1325 else if (Argument is ToMatrix ToMatrix2)
1326 {
1327 Ref = ToMatrix2.Operand as VariableReference;
1328 if (Ref is null)
1329 {
1330 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
1331 Argument.Start, this.script);
1332 }
1333
1334 ArgumentTypes[i] = ArgumentType.Matrix;
1335 }
1336 else if (Argument is ToSet ToSet2)
1337 {
1338 Ref = ToSet2.Operand as VariableReference;
1339 if (Ref is null)
1340 {
1341 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
1342 Argument.Start, this.script);
1343 }
1344
1345 ArgumentTypes[i] = ArgumentType.Set;
1346 }
1347 else if (Argument is VectorDefinition Def2)
1348 {
1349 if (Def2.Elements.Length != 1 || (Ref = Def2.Elements[0] as VariableReference) is null)
1350 {
1351 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
1352 Left.Start, this.script);
1353 }
1354
1355 ArgumentTypes[i] = ArgumentType.Scalar;
1356 }
1357 else
1358 {
1359 throw new SyntaxException("Expected variable reference, with optional scalar, vector, set or matrix attribute types.",
1360 Argument.Start, this.script);
1361 }
1362
1363 ArgumentNames[i] = Ref.VariableName;
1364 }
1365 }
1366 else
1367 throw new SyntaxException("Invalid argument list.", Left.Start, this.script);
1368
1369 if (!(this.ParseEquivalence() is ScriptNode Operand))
1370 throw new SyntaxException("Lambda function body missing.", this.pos, this.script);
1371
1372 return new LambdaDefinition(ArgumentNames, ArgumentTypes, Operand, Start, this.pos - Start, this);
1373 }
1374
1375 this.pos--;
1376 }
1377
1378 return Left;
1379 }
1380
1381 internal ScriptNode ParseEquivalence()
1382 {
1383 ScriptNode Left = this.ParseOrs();
1384 if (Left is null)
1385 return null;
1386
1387 int Start = Left.Start;
1388 char ch;
1389
1390 this.SkipWhiteSpace();
1391
1392 if ((ch = this.PeekNextChar()) == '=')
1393 {
1394 int Bak = this.pos;
1395
1396 this.pos++;
1397 if (this.PeekNextChar() == '>')
1398 {
1399 this.pos++;
1400 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseOrs());
1401 return new Implication(Left, Right, Start, this.pos - Start, this);
1402 }
1403
1404 this.pos = Bak;
1405 }
1406 else if (ch == '<')
1407 {
1408 int Bak = this.pos;
1409
1410 this.pos++;
1411 if (this.PeekNextChar() == '=')
1412 {
1413 this.pos++;
1414 if (this.PeekNextChar() == '>')
1415 {
1416 this.pos++;
1417 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseOrs());
1418 return new Equivalence(Left, Right, Start, this.pos - Start, this);
1419 }
1420 }
1421
1422 this.pos = Bak;
1423 }
1424
1425 return Left;
1426 }
1427
1428 internal ScriptNode ParseOrs()
1429 {
1430 ScriptNode Left = this.ParseAnds();
1431 if (Left is null)
1432 return null;
1433
1434 ScriptNode Right;
1435 int Start = Left.Start;
1436
1437 while (true)
1438 {
1439 this.SkipWhiteSpace();
1440 switch (char.ToUpper(this.PeekNextChar()))
1441 {
1442 case '∨':
1443 this.pos++;
1444 Right = this.AssertRightOperandNotNull(this.ParseAnds());
1445 Left = new Operators.Logical.Or(Left, Right, Start, this.pos - Start, this);
1446 break;
1447
1448 case '|':
1449 this.pos++;
1450 switch (this.PeekNextChar())
1451 {
1452 case '|':
1453 this.pos++;
1454 if (this.PeekNextChar() == '=')
1455 {
1456 this.pos -= 2;
1457 return Left;
1458 }
1459
1460 Right = this.AssertRightOperandNotNull(this.ParseAnds());
1461 Left = new Operators.Logical.Or(Left, Right, Start, this.pos - Start, this);
1462 break;
1463
1464 case '=':
1465 this.pos--;
1466 return Left;
1467
1468 default:
1469 Right = this.AssertRightOperandNotNull(this.ParseAnds());
1470 Left = new Operators.Binary.Or(Left, Right, Start, this.pos - Start, this);
1471 break;
1472 }
1473 break;
1474
1475 case 'O':
1476 case 'X':
1477 case 'N':
1478 switch (this.PeekNextToken().ToUpper())
1479 {
1480 case "OR":
1481 this.pos += 2;
1482 Right = this.AssertRightOperandNotNull(this.ParseAnds());
1483 Left = new Operators.Dual.Or(Left, Right, Start, this.pos - Start, this);
1484 continue;
1485
1486 case "XOR":
1487 this.pos += 3;
1488 Right = this.AssertRightOperandNotNull(this.ParseAnds());
1489 Left = new Operators.Dual.Xor(Left, Right, Start, this.pos - Start, this);
1490 continue;
1491
1492 case "XNOR":
1493 this.pos += 4;
1494 Right = this.AssertRightOperandNotNull(this.ParseAnds());
1495 Left = new Operators.Dual.Xnor(Left, Right, Start, this.pos - Start, this);
1496 continue;
1497
1498 case "NOR":
1499 this.pos += 3;
1500 Right = this.AssertRightOperandNotNull(this.ParseAnds());
1501 Left = new Operators.Dual.Nor(Left, Right, Start, this.pos - Start, this);
1502 continue;
1503
1504 default:
1505 return Left;
1506 }
1507
1508 default:
1509 return Left;
1510 }
1511 }
1512 }
1513
1514 internal ScriptNode ParseAnds()
1515 {
1516 ScriptNode Left = this.ParseMembership();
1517 if (Left is null)
1518 return null;
1519
1520 ScriptNode Right;
1521 int Start = Left.Start;
1522
1523 while (true)
1524 {
1525 this.SkipWhiteSpace();
1526 switch (char.ToUpper(this.PeekNextChar()))
1527 {
1528 case '∧':
1529 this.pos++;
1530 Right = this.AssertRightOperandNotNull(this.ParseMembership());
1531 Left = new Operators.Logical.And(Left, Right, Start, this.pos - Start, this);
1532 break;
1533
1534 case '&':
1535 this.pos++;
1536 switch (this.PeekNextChar())
1537 {
1538 case '&':
1539 this.pos++;
1540 if (this.PeekNextChar() == '=')
1541 {
1542 this.pos -= 2;
1543 return Left;
1544 }
1545
1546 Right = this.AssertRightOperandNotNull(this.ParseMembership());
1547 Left = new Operators.Logical.And(Left, Right, Start, this.pos - Start, this);
1548 break;
1549
1550 case '=':
1551 this.pos--;
1552 return Left;
1553
1554 default:
1555 Right = this.AssertRightOperandNotNull(this.ParseMembership());
1556 Left = new Operators.Binary.And(Left, Right, Start, this.pos - Start, this);
1557 break;
1558 }
1559 break;
1560
1561 case 'A':
1562 case 'N':
1563 switch (this.PeekNextToken().ToUpper())
1564 {
1565 case "AND":
1566 this.pos += 3;
1567 Right = this.AssertRightOperandNotNull(this.ParseMembership());
1568 Left = new Operators.Dual.And(Left, Right, Start, this.pos - Start, this);
1569 continue;
1570
1571 case "NAND":
1572 this.pos += 4;
1573 Right = this.AssertRightOperandNotNull(this.ParseMembership());
1574 Left = new Operators.Dual.Nand(Left, Right, Start, this.pos - Start, this);
1575 continue;
1576
1577 default:
1578 return Left;
1579 }
1580
1581 default:
1582 return Left;
1583 }
1584 }
1585 }
1586
1587 internal ScriptNode ParseMembership()
1588 {
1589 ScriptNode Left = this.ParseComparison();
1590 if (Left is null)
1591 return null;
1592
1593 ScriptNode Right;
1594 int Start = Left.Start;
1595
1596 while (true)
1597 {
1598 this.SkipWhiteSpace();
1599 switch (char.ToUpper(this.PeekNextChar()))
1600 {
1601 case 'A':
1602 case 'I':
1603 case 'M':
1604 case 'N':
1605 case '∈':
1606 case '∉':
1607 switch (this.PeekNextToken().ToUpper())
1608 {
1609 case "IS":
1610 this.pos += 2;
1611
1612 this.SkipWhiteSpace();
1613 if (string.Compare(this.PeekNextToken(), "NOT", true) == 0)
1614 {
1615 this.pos += 3;
1616 Right = this.AssertRightOperandNotNull(this.ParseComparison());
1617 Left = new IsNot(Left, Right, Start, this.pos - Start, this);
1618 }
1619 else
1620 {
1621 Right = this.AssertRightOperandNotNull(this.ParseComparison());
1622 Left = new Is(Left, Right, Start, this.pos - Start, this);
1623 }
1624 continue;
1625
1626 case "INHERITS":
1627 this.pos += 8;
1628 Right = this.AssertRightOperandNotNull(this.ParseComparison());
1629 Left = new Inherits(Left, Right, Start, this.pos - Start, this);
1630 continue;
1631
1632 case "AS":
1633 this.pos += 2;
1634 Right = this.AssertRightOperandNotNull(this.ParseComparison());
1635 Left = new As(Left, Right, Start, this.pos - Start, this);
1636 continue;
1637
1638 case "MATCHES":
1639 this.pos += 7;
1640 Right = this.AssertRightOperandNotNull(this.ParseComparison());
1641 Left = new Matches(Left, Right, Start, this.pos - Start, this);
1642 continue;
1643
1644 case "∈":
1645 this.pos++;
1646 Right = this.AssertRightOperandNotNull(this.ParseComparison());
1647 Left = new In(Left, Right, Start, this.pos - Start, this);
1648 continue;
1649
1650 case "IN":
1651 this.pos += 2;
1652 Right = this.AssertRightOperandNotNull(this.ParseComparison());
1653 Left = new In(Left, Right, Start, this.pos - Start, this);
1654 continue;
1655
1656 case "∉":
1657 this.pos++;
1658 Right = this.AssertRightOperandNotNull(this.ParseComparison());
1659 Left = new NotIn(Left, Right, Start, this.pos - Start, this);
1660 continue;
1661
1662 case "NOTIN":
1663 this.pos += 5;
1664 Right = this.AssertRightOperandNotNull(this.ParseComparison());
1665 Left = new NotIn(Left, Right, Start, this.pos - Start, this);
1666 continue;
1667
1668 case "NOT":
1669 int Bak = this.pos;
1670 this.pos += 3;
1671
1672 this.SkipWhiteSpace();
1673 if (string.Compare(this.PeekNextToken(), "IN", true) == 0)
1674 {
1675 this.pos += 2;
1676 Right = this.AssertRightOperandNotNull(this.ParseComparison());
1677 Left = new NotIn(Left, Right, Start, this.pos - Start, this);
1678 continue;
1679 }
1680 else
1681 {
1682 this.pos = Bak;
1683 return Left;
1684 }
1685
1686 default:
1687 return Left;
1688 }
1689
1690 default:
1691 return Left;
1692 }
1693 }
1694 }
1695
1696 internal ScriptNode ParseComparison()
1697 {
1698 ScriptNode Left = this.ParseShifts();
1699 if (Left is null)
1700 return null;
1701
1702 ScriptNode Right;
1703 int Start = Left.Start;
1704 char ch;
1705
1706 while (true)
1707 {
1708 this.SkipWhiteSpace();
1709 switch (char.ToUpper(this.PeekNextChar()))
1710 {
1711 case '<':
1712 this.pos++;
1713 if ((ch = this.PeekNextChar()) == '=')
1714 {
1715 this.pos++;
1716
1717 if (this.PeekNextChar() == '>')
1718 {
1719 this.pos -= 2;
1720 return Left;
1721 }
1722 else
1723 {
1724 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1725 if (Left is LesserThan LT)
1726 Left = new Range(LT.LeftOperand, LT.RightOperand, Right, false, true, LT.Start, Right.Start + Right.Length - LT.Start, this);
1727 else if (Left is LesserThanOrEqualTo LTE)
1728 Left = new Range(LTE.LeftOperand, LTE.RightOperand, Right, true, true, LTE.Start, Right.Start + Right.Length - LTE.Start, this);
1729 else
1730 Left = new LesserThanOrEqualTo(Left, Right, Start, this.pos - Start, this);
1731 }
1732 }
1733 else if (ch == '>')
1734 {
1735 this.pos++;
1736 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1737 Left = new NotEqualTo(Left, Right, Start, this.pos - Start, this);
1738 }
1739 else if (ch == '-')
1740 {
1741 this.pos++;
1742 if (this.PeekNextChar() == '>')
1743 {
1744 this.pos -= 2;
1745 return Left;
1746 }
1747 else
1748 {
1749 this.pos--;
1750 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1751 Left = new LesserThan(Left, Right, Start, this.pos - Start, this);
1752 }
1753 }
1754 else if (ch == '<')
1755 {
1756 this.pos--;
1757 return Left;
1758 }
1759 else
1760 {
1761 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1762 if (Left is LesserThan LT)
1763 Left = new Range(LT.LeftOperand, LT.RightOperand, Right, false, false, LT.Start, Right.Start + Right.Length - LT.Start, this);
1764 else if (Left is LesserThanOrEqualTo LTE)
1765 Left = new Range(LTE.LeftOperand, LTE.RightOperand, Right, true, false, LTE.Start, Right.Start + Right.Length - LTE.Start, this);
1766 else
1767 Left = new LesserThan(Left, Right, Start, this.pos - Start, this);
1768 }
1769 break;
1770
1771 case '>':
1772 this.pos++;
1773 if ((ch = this.PeekNextChar()) == '=')
1774 {
1775 this.pos++;
1776 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1777 if (Left is GreaterThan GT)
1778 Left = new Range(Right, GT.RightOperand, GT.LeftOperand, true, false, GT.Start, Right.Start + Right.Length - GT.Start, this);
1779 else if (Left is GreaterThanOrEqualTo GTE)
1780 Left = new Range(Right, GTE.RightOperand, GTE.LeftOperand, true, true, GTE.Start, Right.Start + Right.Length - GTE.Start, this);
1781 else
1782 Left = new GreaterThanOrEqualTo(Left, Right, Start, this.pos - Start, this);
1783 }
1784 else if (ch == '>')
1785 {
1786 this.pos--;
1787 return Left;
1788 }
1789 else
1790 {
1791 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1792 if (Left is GreaterThan GT)
1793 Left = new Range(Right, GT.RightOperand, GT.LeftOperand, false, false, GT.Start, Right.Start + Right.Length - GT.Start, this);
1794 else if (Left is GreaterThanOrEqualTo GTE)
1795 Left = new Range(Right, GTE.RightOperand, GTE.LeftOperand, false, true, GTE.Start, Right.Start + Right.Length - GTE.Start, this);
1796 else
1797 Left = new GreaterThan(Left, Right, Start, this.pos - Start, this);
1798 }
1799 break;
1800
1801 case '=':
1802 this.pos++;
1803 if ((ch = this.PeekNextChar()) == '=')
1804 {
1805 this.pos++;
1806 if (this.PeekNextChar() == '=')
1807 {
1808 this.pos++;
1809 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1810 Left = new IdenticalTo(Left, Right, Start, this.pos - Start, this);
1811 }
1812 else
1813 {
1814 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1815 Left = new EqualTo(Left, Right, Start, this.pos - Start, this);
1816 }
1817 }
1818 else if (ch == '>')
1819 {
1820 this.pos--;
1821 return Left;
1822 }
1823 else
1824 {
1825 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1826 Left = new EqualTo(Left, Right, Start, this.pos - Start, this);
1827 }
1828 break;
1829
1830 case '≠':
1831 this.pos++;
1832 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1833 Left = new NotEqualTo(Left, Right, Start, this.pos - Start, this);
1834 break;
1835
1836 case '≡':
1837 this.pos++;
1838 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1839 Left = new IdenticalTo(Left, Right, Start, this.pos - Start, this);
1840 break;
1841
1842 case '≤':
1843 this.pos++;
1844 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1845 {
1846 if (Left is LesserThan LT)
1847 Left = new Range(LT.LeftOperand, LT.RightOperand, Right, false, true, LT.Start, Right.Start + Right.Length - LT.Start, this);
1848 else if (Left is LesserThanOrEqualTo LTE)
1849 Left = new Range(LTE.LeftOperand, LTE.RightOperand, Right, true, true, LTE.Start, Right.Start + Right.Length - LTE.Start, this);
1850 else
1851 Left = new LesserThanOrEqualTo(Left, Right, Start, this.pos - Start, this);
1852 }
1853 break;
1854
1855 case '≥':
1856 this.pos++;
1857 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1858 {
1859 if (Left is GreaterThan GT)
1860 Left = new Range(Right, GT.RightOperand, GT.LeftOperand, true, false, GT.Start, Right.Start + Right.Length - GT.Start, this);
1861 else if (Left is GreaterThanOrEqualTo GTE)
1862 Left = new Range(Right, GTE.RightOperand, GTE.LeftOperand, true, true, GTE.Start, Right.Start + Right.Length - GTE.Start, this);
1863 else
1864 Left = new GreaterThanOrEqualTo(Left, Right, Start, this.pos - Start, this);
1865 }
1866 break;
1867
1868 case '!':
1869 this.pos++;
1870 if (this.PeekNextChar() == '=')
1871 {
1872 this.pos++;
1873 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1874 Left = new NotEqualTo(Left, Right, Start, this.pos - Start, this);
1875 }
1876 else
1877 {
1878 this.pos--;
1879 return Left;
1880 }
1881 break;
1882
1883 case '.':
1884 this.pos++;
1885 switch (this.PeekNextChar())
1886 {
1887 case '=':
1888 this.pos++;
1889 if (this.PeekNextChar() == '=')
1890 {
1891 this.pos++;
1892 if (this.PeekNextChar() == '=')
1893 {
1894 this.pos++;
1895 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1896 Left = new IdenticalToElementWise(Left, Right, Start, this.pos - Start, this);
1897 }
1898 else
1899 {
1900 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1901 Left = new EqualToElementWise(Left, Right, Start, this.pos - Start, this);
1902 }
1903 }
1904 else
1905 {
1906 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1907 Left = new EqualToElementWise(Left, Right, Start, this.pos - Start, this);
1908 }
1909 continue;
1910
1911 case '<':
1912 this.pos++;
1913 if (this.PeekNextChar() == '>')
1914 {
1915 this.pos++;
1916 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1917 Left = new NotEqualToElementWise(Left, Right, Start, this.pos - Start, this);
1918 continue;
1919 }
1920 else
1921 {
1922 this.pos -= 2;
1923 return Left;
1924 }
1925
1926 case '!':
1927 this.pos++;
1928 if (this.PeekNextChar() == '=')
1929 {
1930 this.pos++;
1931 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1932 Left = new NotEqualToElementWise(Left, Right, Start, this.pos - Start, this);
1933 continue;
1934 }
1935 else
1936 {
1937 this.pos -= 2;
1938 return Left;
1939 }
1940
1941 default:
1942 this.pos--;
1943 return Left;
1944 }
1945
1946 case 'L':
1947 case 'N':
1948 case 'U':
1949 switch (this.PeekNextToken().ToUpper())
1950 {
1951 case "LIKE":
1952 this.pos += 4;
1953 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1954 Left = new Like(Left, Right, Start, this.pos - Start, this);
1955 continue;
1956
1957 case "NOTLIKE":
1958 this.pos += 7;
1959 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1960 Left = new NotLike(Left, Right, Start, this.pos - Start, this);
1961 continue;
1962
1963 case "UNLIKE":
1964 this.pos += 6;
1965 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1966 Left = new NotLike(Left, Right, Start, this.pos - Start, this);
1967 continue;
1968
1969 case "NOT":
1970 int Bak = this.pos;
1971 this.pos += 3;
1972 this.SkipWhiteSpace();
1973 if (string.Compare(this.PeekNextToken(), "LIKE", true) == 0)
1974 {
1975 this.pos += 4;
1976 Right = this.AssertRightOperandNotNull(this.ParseShifts());
1977 Left = new NotLike(Left, Right, Start, this.pos - Start, this);
1978 continue;
1979 }
1980 else
1981 {
1982 this.pos = Bak;
1983 return Left;
1984 }
1985
1986 default:
1987 return Left;
1988 }
1989
1990 default:
1991 return Left;
1992 }
1993 }
1994 }
1995
1996 internal ScriptNode ParseShifts()
1997 {
1998 ScriptNode Left = this.ParseUnions();
1999 if (Left is null)
2000 return null;
2001
2002 ScriptNode Right;
2003 int Start = Left.Start;
2004
2005 while (true)
2006 {
2007 this.SkipWhiteSpace();
2008 switch (this.PeekNextChar())
2009 {
2010 case '<':
2011 this.pos++;
2012 if (this.PeekNextChar() == '<')
2013 {
2014 this.pos++;
2015 if (this.PeekNextChar() == '=')
2016 {
2017 this.pos -= 2;
2018 return Left;
2019 }
2020
2021 Right = this.AssertRightOperandNotNull(this.ParseUnions());
2022 Left = new ShiftLeft(Left, Right, Start, this.pos - Start, this);
2023 }
2024 else
2025 {
2026 this.pos--;
2027 return Left;
2028 }
2029 break;
2030
2031 case '>':
2032 this.pos++;
2033 if (this.PeekNextChar() == '>')
2034 {
2035 this.pos++;
2036 if (this.PeekNextChar() == '=')
2037 {
2038 this.pos -= 2;
2039 return Left;
2040 }
2041
2042 Right = this.AssertRightOperandNotNull(this.ParseUnions());
2043 Left = new ShiftRight(Left, Right, Start, this.pos - Start, this);
2044 }
2045 else
2046 {
2047 this.pos--;
2048 return Left;
2049 }
2050 break;
2051
2052 default:
2053 return Left;
2054 }
2055 }
2056 }
2057
2058 internal ScriptNode ParseUnions()
2059 {
2060 ScriptNode Left = this.ParseIntersections();
2061 if (Left is null)
2062 return null;
2063
2064 ScriptNode Right;
2065 int Start = Left.Start;
2066 char ch;
2067
2068 while (true)
2069 {
2070 this.SkipWhiteSpace();
2071 if (char.ToUpper(ch = this.PeekNextChar()) == 'U')
2072 {
2073 if (string.Compare(this.PeekNextToken(), "UNION", true) == 0)
2074 {
2075 this.pos += 5;
2076 Right = this.AssertRightOperandNotNull(this.ParseIntersections());
2077 Left = new Union(Left, Right, Start, this.pos - Start, this);
2078 }
2079 else
2080 return Left;
2081 }
2082 else if (ch == '∪')
2083 {
2084 this.pos++;
2085 Right = this.AssertRightOperandNotNull(this.ParseIntersections());
2086 Left = new Union(Left, Right, Start, this.pos - Start, this);
2087 }
2088 else
2089 return Left;
2090 }
2091 }
2092
2093 internal ScriptNode ParseIntersections()
2094 {
2095 ScriptNode Left = this.ParseInterval();
2096 if (Left is null)
2097 return null;
2098
2099 ScriptNode Right;
2100 int Start = Left.Start;
2101 char ch;
2102
2103 while (true)
2104 {
2105 this.SkipWhiteSpace();
2106 if (char.ToUpper(ch = this.PeekNextChar()) == 'I')
2107 {
2108 switch (this.PeekNextToken().ToUpper())
2109 {
2110 case "INTERSECTION":
2111 this.pos += 12;
2112 Right = this.AssertRightOperandNotNull(this.ParseInterval());
2113 Left = new Intersection(Left, Right, Start, this.pos - Start, this);
2114 continue;
2115
2116 case "INTERSECT":
2117 this.pos += 9;
2118 Right = this.AssertRightOperandNotNull(this.ParseInterval());
2119 Left = new Intersection(Left, Right, Start, this.pos - Start, this);
2120 continue;
2121
2122 default:
2123 return Left;
2124 }
2125 }
2126 else if (ch == '∩')
2127 {
2128 this.pos++;
2129 Right = this.AssertRightOperandNotNull(this.ParseInterval());
2130 Left = new Intersection(Left, Right, Start, this.pos - Start, this);
2131 }
2132 else
2133 return Left;
2134 }
2135 }
2136
2137 internal ScriptNode ParseInterval()
2138 {
2139 ScriptNode From = this.ParseTerms();
2140 if (From is null)
2141 return null;
2142
2143 this.SkipWhiteSpace();
2144 if (this.PeekNextChar() != '.')
2145 return From;
2146
2147 this.pos++;
2148 if (this.PeekNextChar() != '.')
2149 {
2150 this.pos--;
2151 return From;
2152 }
2153
2154 this.pos++;
2155 ScriptNode To = this.AssertRightOperandNotNull(this.ParseTerms());
2156 int Start = From.Start;
2157
2158 this.SkipWhiteSpace();
2159 if (this.PeekNextChar() == '|')
2160 {
2161 this.pos++;
2162 ScriptNode StepSize = this.AssertRightOperandNotNull(this.ParseTerms());
2163 return new Interval(From, To, StepSize, Start, this.pos - Start, this);
2164 }
2165 else
2166 return new Interval(From, To, Start, this.pos - Start, this);
2167 }
2168
2169 internal ScriptNode ParseTerms()
2170 {
2171 ScriptNode Left = this.ParseBinomialCoefficients();
2172 if (Left is null)
2173 return null;
2174
2175 ScriptNode Right;
2176 int Start = Left.Start;
2177 char ch;
2178
2179 while (true)
2180 {
2181 this.SkipWhiteSpace();
2182 switch (this.PeekNextChar())
2183 {
2184 case '+':
2185 this.pos++;
2186 ch = this.PeekNextChar();
2187
2188 if (ch == '=' || ch == '+')
2189 {
2190 this.pos--;
2191 return Left;
2192 }
2193 else if (ch == '-')
2194 {
2195 this.pos++;
2196
2197 Right = this.AssertRightOperandNotNull(this.ParseBinomialCoefficients());
2198 Left = new CreateMeasurement(Left, Right, Start, this.pos - Start, this);
2199 }
2200 else
2201 {
2202 Right = this.AssertRightOperandNotNull(this.ParseBinomialCoefficients());
2203 Left = new Add(Left, Right, Start, this.pos - Start, this);
2204 }
2205 continue;
2206
2207 case '-':
2208 this.pos++;
2209 if ((ch = this.PeekNextChar()) == '=' || ch == '>' || ch == '-')
2210 {
2211 this.pos--;
2212 return Left;
2213 }
2214
2215 Right = this.AssertRightOperandNotNull(this.ParseBinomialCoefficients());
2216 Left = new Subtract(Left, Right, Start, this.pos - Start, this);
2217 continue;
2218
2219 case '.':
2220 this.pos++;
2221 switch (this.PeekNextChar())
2222 {
2223 case '+':
2224 this.pos++;
2225 Right = this.AssertRightOperandNotNull(this.ParseBinomialCoefficients());
2226 Left = new AddElementWise(Left, Right, Start, this.pos - Start, this);
2227 continue;
2228
2229 case '-':
2230 this.pos++;
2231 Right = this.AssertRightOperandNotNull(this.ParseBinomialCoefficients());
2232 Left = new SubtractElementWise(Left, Right, Start, this.pos - Start, this);
2233 continue;
2234
2235 default:
2236 this.pos--;
2237 return Left;
2238 }
2239
2240 case '±':
2241 this.pos++;
2242
2243 Right = this.AssertRightOperandNotNull(this.ParseBinomialCoefficients());
2244 Left = new CreateMeasurement(Left, Right, Start, this.pos - Start, this);
2245 break;
2246
2247 default:
2248 return Left;
2249 }
2250 }
2251 }
2252
2253 internal ScriptNode ParseBinomialCoefficients()
2254 {
2255 ScriptNode Left = this.ParseFactors();
2256 if (Left is null)
2257 return null;
2258
2259 ScriptNode Right;
2260 int Start = Left.Start;
2261
2262 while (true)
2263 {
2264 this.SkipWhiteSpace();
2265 if (char.ToUpper(this.PeekNextChar()) == 'O' && string.Compare(this.PeekNextToken(), "OVER", true) == 0)
2266 {
2267 this.pos += 4;
2268 Right = this.AssertRightOperandNotNull(this.ParseFactors());
2269 Left = new BinomialCoefficient(Left, Right, Start, this.pos - Start, this);
2270 }
2271 else
2272 return Left;
2273 }
2274 }
2275
2276 internal ScriptNode ParseFactors()
2277 {
2278 ScriptNode Left = this.ParsePowers();
2279 if (Left is null)
2280 return null;
2281
2282 ScriptNode Right;
2283 int Start = Left.Start;
2284
2285 while (true)
2286 {
2287 this.SkipWhiteSpace();
2288 switch (char.ToUpper(this.PeekNextChar()))
2289 {
2290 case '⋅':
2291 case '*':
2292 this.pos++;
2293 if (this.PeekNextChar() == '=')
2294 {
2295 this.pos--;
2296 return Left;
2297 }
2298
2299 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2300 Left = new Multiply(Left, Right, Start, this.pos - Start, this);
2301 continue;
2302
2303 case '/':
2304 this.pos++;
2305 if (this.PeekNextChar() == '=')
2306 {
2307 this.pos--;
2308 return Left;
2309 }
2310
2311 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2312 Left = new Divide(Left, Right, Start, this.pos - Start, this);
2313 continue;
2314
2315 case '\\':
2316 this.pos++;
2317 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2318 Left = new LeftDivide(Left, Right, Start, this.pos - Start, this);
2319 continue;
2320
2321 case 'C':
2322 switch (this.PeekNextToken().ToUpper())
2323 {
2324 case "CROSS":
2325 this.pos += 5;
2326 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2327 Left = new CrossProduct(Left, Right, Start, this.pos - Start, this);
2328 continue;
2329
2330 case "CARTESIAN":
2331 this.pos += 9;
2332 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2333 Left = new CartesianProduct(Left, Right, Start, this.pos - Start, this);
2334 continue;
2335
2336 default:
2337 return Left;
2338 }
2339
2340 case 'D':
2341 if (string.Compare(this.PeekNextToken(), "DOT", true) == 0)
2342 {
2343 this.pos += 3;
2344 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2345 Left = new DotProduct(Left, Right, Start, this.pos - Start, this);
2346 continue;
2347 }
2348 else
2349 return Left;
2350
2351 case 'M':
2352 if (string.Compare(this.PeekNextToken(), "MOD", true) == 0)
2353 {
2354 this.pos += 3;
2355 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2356 Left = new Residue(Left, Right, Start, this.pos - Start, this);
2357 continue;
2358 }
2359 else
2360 return Left;
2361
2362 case '.':
2363 this.pos++;
2364 switch (char.ToUpper(this.PeekNextChar()))
2365 {
2366 case '⋅':
2367 case '*':
2368 this.pos++;
2369 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2370 Left = new MultiplyElementWise(Left, Right, Start, this.pos - Start, this);
2371 continue;
2372
2373 case '/':
2374 this.pos++;
2375 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2376 Left = new DivideElementWise(Left, Right, Start, this.pos - Start, this);
2377 continue;
2378
2379 case '\\':
2380 this.pos++;
2381 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2382 Left = new LeftDivideElementWise(Left, Right, Start, this.pos - Start, this);
2383 continue;
2384
2385 case 'M':
2386 if (string.Compare(this.PeekNextToken(), "MOD", true) == 0)
2387 {
2388 this.pos += 3;
2389 Right = this.AssertRightOperandNotNull(this.ParsePowers());
2390 Left = new ResidueElementWise(Left, Right, Start, this.pos - Start, this);
2391 continue;
2392 }
2393 else
2394 {
2395 this.pos--;
2396 return Left;
2397 }
2398
2399 default:
2400 this.pos--;
2401 return Left;
2402 }
2403
2404 default:
2405 return Left;
2406 }
2407 }
2408 }
2409
2410 internal ScriptNode ParsePowers()
2411 {
2412 ScriptNode Left = this.ParseUnaryPrefixOperator();
2413 if (Left is null)
2414 return null;
2415
2416 ScriptNode Right;
2417 int Start = Left.Start;
2418
2419 while (true)
2420 {
2421 this.SkipWhiteSpace();
2422 switch (this.PeekNextChar())
2423 {
2424 case '^':
2425 this.pos++;
2426 if (this.PeekNextChar() == '=')
2427 {
2428 this.pos--;
2429 return Left;
2430 }
2431
2432 Right = this.AssertRightOperandNotNull(this.ParseUnaryPrefixOperator());
2433 Left = new Power(Left, Right, Start, this.pos - Start, this);
2434 continue;
2435
2436 case '²':
2437 this.pos++;
2438 Left = new Square(Left, Start, this.pos - Start, this);
2439 continue;
2440
2441 case '³':
2442 this.pos++;
2443 Left = new Cube(Left, Start, this.pos - Start, this);
2444 continue;
2445
2446 case '.':
2447 this.pos++;
2448 switch (this.PeekNextChar())
2449 {
2450 case '^':
2451 this.pos++;
2452 Right = this.AssertRightOperandNotNull(this.ParseUnaryPrefixOperator());
2453 Left = new PowerElementWise(Left, Right, Start, this.pos - Start, this);
2454 continue;
2455
2456 default:
2457 this.pos--;
2458 return Left;
2459 }
2460
2461 default:
2462 return Left;
2463 }
2464 }
2465 }
2466
2467 internal ScriptNode ParseUnaryPrefixOperator()
2468 {
2469 this.SkipWhiteSpace();
2470
2471 int Start = this.pos;
2472 char ch;
2473
2474 switch (char.ToUpper(this.PeekNextChar()))
2475 {
2476 case '-':
2477 this.pos++;
2478 if ((ch = this.PeekNextChar()) == '-')
2479 {
2480 this.pos++;
2481
2482 ScriptNode Op = this.ParseUnaryPrefixOperator();
2483
2484 if (Op is VariableReference Ref)
2485 return new Operators.Assignments.Pre.PreDecrement(Ref.VariableName, Start, this.pos - Start, this);
2486 else if (Op is NamedMember NamedMember)
2487 return new NamedMemberAssignment(NamedMember, new MinusOne(NamedMember, Start, this.pos - Start, this), Start, this.pos - Start, this);
2488 else if (Op is VectorIndex VectorIndex)
2489 return new VectorIndexAssignment(VectorIndex, new MinusOne(VectorIndex, Start, this.pos - Start, this), Start, this.pos - Start, this);
2490 else if (Op is MatrixIndex MatrixIndex)
2491 return new MatrixIndexAssignment(MatrixIndex, new MinusOne(MatrixIndex, Start, this.pos - Start, this), Start, this.pos - Start, this);
2492 else if (Op is ColumnVector ColumnVector)
2493 return new MatrixColumnAssignment(ColumnVector, new MinusOne(ColumnVector, Start, this.pos - Start, this), Start, this.pos - Start, this);
2494 else if (Op is RowVector RowVector)
2495 return new MatrixRowAssignment(RowVector, new MinusOne(RowVector, Start, this.pos - Start, this), Start, this.pos - Start, this);
2496 else
2497 throw new SyntaxException("Invalid use of the -- operator.", this.pos, this.script);
2498 }
2499 else if ((ch >= '0' && ch <= '9') || (ch == '.'))
2500 {
2501 this.pos--;
2502 return this.ParseSuffixOperator();
2503 }
2504 else if (ch == '>')
2505 {
2506 this.pos--;
2507 return this.ParseSuffixOperator();
2508 }
2509 else
2510 return new Negate(this.AssertOperandNotNull(this.ParseFactors()), Start, this.pos - Start, this);
2511
2512 case '+':
2513 this.pos++;
2514 if ((ch = this.PeekNextChar()) == '+')
2515 {
2516 this.pos++;
2517
2518 ScriptNode Op = this.ParseUnaryPrefixOperator();
2519
2520 if (Op is VariableReference Ref)
2521 return new Operators.Assignments.Pre.PreIncrement(Ref.VariableName, Start, this.pos - Start, this);
2522 else if (Op is NamedMember NamedMember)
2523 return new NamedMemberAssignment(NamedMember, new PlusOne(NamedMember, Start, this.pos - Start, this), Start, this.pos - Start, this);
2524 else if (Op is VectorIndex VectorIndex)
2525 return new VectorIndexAssignment(VectorIndex, new PlusOne(VectorIndex, Start, this.pos - Start, this), Start, this.pos - Start, this);
2526 else if (Op is MatrixIndex MatrixIndex)
2527 return new MatrixIndexAssignment(MatrixIndex, new PlusOne(MatrixIndex, Start, this.pos - Start, this), Start, this.pos - Start, this);
2528 else if (Op is ColumnVector ColumnVector)
2529 return new MatrixColumnAssignment(ColumnVector, new PlusOne(ColumnVector, Start, this.pos - Start, this), Start, this.pos - Start, this);
2530 else if (Op is RowVector RowVector)
2531 return new MatrixRowAssignment(RowVector, new PlusOne(RowVector, Start, this.pos - Start, this), Start, this.pos - Start, this);
2532 else
2533 throw new SyntaxException("Invalid use of the ++ operator.", this.pos, this.script);
2534 }
2535 else if ((ch >= '0' && ch <= '9') || (ch == '.'))
2536 return this.ParseSuffixOperator();
2537 else
2538 return this.AssertOperandNotNull(this.ParseFactors());
2539
2540 case '!':
2541 this.pos++;
2542 return new Not(this.AssertOperandNotNull(this.ParseUnaryPrefixOperator()), Start, this.pos - Start, this);
2543
2544 case 'N':
2545 if (string.Compare(this.PeekNextToken(), "NOT", true) == 0)
2546 {
2547 this.pos += 3;
2548 return new Not(this.AssertOperandNotNull(this.ParseUnaryPrefixOperator()), Start, this.pos - Start, this);
2549 }
2550 else
2551 return this.ParseSuffixOperator();
2552
2553 case '~':
2554 this.pos++;
2555 return new Complement(this.AssertOperandNotNull(this.ParseUnaryPrefixOperator()), Start, this.pos - Start, this);
2556
2557 default:
2558 return this.ParseSuffixOperator();
2559 }
2560 }
2561
2562 internal ScriptNode ParseSuffixOperator()
2563 {
2564 ScriptNode Node = this.ParseObject();
2565 if (Node is null)
2566 return null;
2567
2568 bool NullCheck = false;
2569 int Start = Node.Start;
2570 char ch;
2571
2572 while (true)
2573 {
2574 this.SkipWhiteSpace();
2575 switch (ch = this.PeekNextChar())
2576 {
2577 case '?':
2578 if (NullCheck)
2579 {
2580 this.pos++;
2581 if (this.PeekNextChar() == '?')
2582 {
2583 this.pos -= 2;
2584 return Node;
2585 }
2586 else
2587 {
2588 ScriptNode IfNull = this.AssertOperandNotNull(this.ParseStatement(false));
2589 Node = new NullCheck(Node, IfNull, Start, this.pos - Start, this);
2590 }
2591 break;
2592 }
2593 else
2594 {
2595 this.pos++;
2596 ch = this.PeekNextChar();
2597 switch (ch)
2598 {
2599 case '.':
2600 case '(':
2601 case '[':
2602 case '{':
2603 case '?':
2604 NullCheck = true;
2605 continue;
2606
2607 default:
2608 this.pos--;
2609 return Node;
2610 }
2611 }
2612
2613 case '.':
2614 this.pos++;
2615
2616 ch = this.PeekNextChar();
2617 if (ch == '=' || ch == '+' || ch == '-' || ch == '^' || ch == '.' || ch == '*' || ch == '⋅' || ch == '/' || ch == '\\' || ch == '<' || ch == '!')
2618 {
2619 this.pos--;
2620 return Node;
2621 }
2622
2623 if (char.ToUpper(ch) == 'M' && string.Compare(this.PeekNextToken(), "MOD", true) == 0)
2624 {
2625 this.pos--;
2626 return Node;
2627 }
2628
2629 ScriptNode Right = this.AssertRightOperandNotNull(this.ParseObject());
2630
2631 if (Right is VariableReference Ref)
2632 Node = new NamedMember(Node, Ref.VariableName, NullCheck, Start, this.pos - Start, this);
2633 else
2634 Node = new DynamicMember(Node, Right, NullCheck, Start, this.pos - Start, this);
2635
2636 break;
2637
2638 case '(':
2639 bool WsBak = this.canSkipWhitespace;
2640 this.canSkipWhitespace = true;
2641 this.pos++;
2642 Right = this.ParseList();
2643
2644 this.SkipWhiteSpace();
2645 if (this.PeekNextChar() != ')')
2646 throw new SyntaxException("Expected ).", this.pos, this.script);
2647
2648 this.canSkipWhitespace = WsBak;
2649 this.pos++;
2650
2651 Ref = Node as VariableReference;
2652 if (Ref is null)
2653 {
2654 if (Node is NamedMember NamedMember)
2655 {
2656 if (Right is null)
2657 Node = new NamedMethodCall(NamedMember.Operand, NamedMember.Name, Array.Empty<ScriptNode>(), NamedMember.NullCheck || NullCheck, Start, this.pos - Start, this);
2658 else if (Right.GetType() == typeof(ElementList))
2659 Node = new NamedMethodCall(NamedMember.Operand, NamedMember.Name, ((ElementList)Right).Elements, NamedMember.NullCheck || NullCheck, Start, this.pos - Start, this);
2660 else
2661 Node = new NamedMethodCall(NamedMember.Operand, NamedMember.Name, new ScriptNode[] { Right }, NamedMember.NullCheck || NullCheck, Start, this.pos - Start, this);
2662 }// TODO: Dynamic named method call.
2663 else
2664 {
2665 if (Right is null)
2666 Node = new DynamicFunctionCall(Node, Array.Empty<ScriptNode>(), NullCheck, Start, this.pos - Start, this);
2667 else if (Right.GetType() == typeof(ElementList))
2668 Node = new DynamicFunctionCall(Node, ((ElementList)Right).Elements, NullCheck, Start, this.pos - Start, this);
2669 else
2670 Node = new DynamicFunctionCall(Node, new ScriptNode[] { Right }, NullCheck, Start, this.pos - Start, this);
2671 }
2672 }
2673 else
2674 Node = GetFunction(Ref.VariableName, Right, NullCheck, Start, this.pos - Start, this);
2675
2676 break;
2677
2678 case '[':
2679 WsBak = this.canSkipWhitespace;
2680 this.canSkipWhitespace = true;
2681 this.pos++;
2682 Right = this.ParseList();
2683
2684 this.SkipWhiteSpace();
2685 if (this.PeekNextChar() != ']')
2686 throw new SyntaxException("Expected ].", this.pos, this.script);
2687
2688 this.canSkipWhitespace = WsBak;
2689 this.pos++;
2690
2691 if (Right is null)
2692 Node = new ToVector(Node, NullCheck, Start, this.pos - Start, this);
2693 else if (Right.GetType() == typeof(ElementList))
2694 {
2695 ElementList List = (ElementList)Right;
2696
2697 if (List.Elements.Length == 2)
2698 {
2699 if (List.Elements[0] is null)
2700 {
2701 if (List.Elements[1] is null)
2702 Node = new ToMatrix(Node, NullCheck, Start, this.pos - Start, this);
2703 else
2704 Node = new RowVector(Node, List.Elements[1], NullCheck, Start, this.pos - Start, this);
2705 }
2706 else if (List.Elements[1] is null)
2707 Node = new ColumnVector(Node, List.Elements[0], NullCheck, Start, this.pos - Start, this);
2708 else
2709 Node = new MatrixIndex(Node, List.Elements[0], List.Elements[1], NullCheck, Start, this.pos - Start, this);
2710 }
2711 else
2712 Node = new DynamicIndex(Node, List, NullCheck, Start, this.pos - Start, this);
2713 }
2714 else
2715 Node = new VectorIndex(Node, Right, NullCheck, Start, this.pos - Start, this);
2716 break;
2717
2718 case '{':
2719 int Bak = this.pos;
2720 this.pos++;
2721 this.SkipWhiteSpace();
2722 if (this.PeekNextChar() == '}')
2723 {
2724 this.pos++;
2725 Node = new ToSet(Node, NullCheck, Start, this.pos - Start, this);
2726 break;
2727 }
2728 else
2729 {
2730 this.pos = Bak;
2731 return Node;
2732 }
2733
2734 case '+':
2735 this.pos++;
2736 if (this.PeekNextChar() == '+')
2737 {
2738 this.pos++;
2739
2740 Ref = Node as VariableReference;
2741
2742 if (!(Ref is null))
2743 Node = new Operators.Assignments.Post.PostIncrement(Ref.VariableName, Start, this.pos - Start, this);
2744 else
2745 {
2746 if (Node is NamedMember NamedMember)
2747 Node = new NamedMemberAssignment(NamedMember, new PlusOne(NamedMember, Start, this.pos - Start, this), Start, this.pos - Start, this);
2748 else if (Node is VectorIndex VectorIndex)
2749 Node = new VectorIndexAssignment(VectorIndex, new PlusOne(VectorIndex, Start, this.pos - Start, this), Start, this.pos - Start, this);
2750 else if (Node is MatrixIndex MatrixIndex)
2751 Node = new MatrixIndexAssignment(MatrixIndex, new PlusOne(MatrixIndex, Start, this.pos - Start, this), Start, this.pos - Start, this);
2752 else if (Node is ColumnVector ColumnVector)
2753 Node = new MatrixColumnAssignment(ColumnVector, new PlusOne(ColumnVector, Start, this.pos - Start, this), Start, this.pos - Start, this);
2754 else if (Node is RowVector RowVector)
2755 Node = new MatrixRowAssignment(RowVector, new PlusOne(RowVector, Start, this.pos - Start, this), Start, this.pos - Start, this);
2756 else
2757 {
2758 this.pos -= 2; // Can be a prefix operator.
2759 return Node;
2760 }
2761
2762 Node = new MinusOne(Node, Start, this.pos - Start, this);
2763 }
2764
2765 if (NullCheck)
2766 throw new SyntaxException("Null-checked post increment operator not defined.", this.pos, this.script);
2767
2768 break;
2769 }
2770 else
2771 {
2772 this.pos--;
2773 return Node;
2774 }
2775
2776 case '-':
2777 this.pos++;
2778 if (this.PeekNextChar() == '-')
2779 {
2780 this.pos++;
2781
2782 Ref = Node as VariableReference;
2783
2784 if (!(Ref is null))
2785 Node = new Operators.Assignments.Post.PostDecrement(Ref.VariableName, Start, this.pos - Start, this);
2786 else
2787 {
2788 if (Node is NamedMember NamedMember)
2789 Node = new NamedMemberAssignment(NamedMember, new MinusOne(NamedMember, Start, this.pos - Start, this), Start, this.pos - Start, this);
2790 else if (Node is VectorIndex VectorIndex)
2791 Node = new VectorIndexAssignment(VectorIndex, new MinusOne(VectorIndex, Start, this.pos - Start, this), Start, this.pos - Start, this);
2792 else if (Node is MatrixIndex MatrixIndex)
2793 Node = new MatrixIndexAssignment(MatrixIndex, new MinusOne(MatrixIndex, Start, this.pos - Start, this), Start, this.pos - Start, this);
2794 else if (Node is ColumnVector ColumnVector)
2795 Node = new MatrixColumnAssignment(ColumnVector, new MinusOne(ColumnVector, Start, this.pos - Start, this), Start, this.pos - Start, this);
2796 else if (Node is RowVector RowVector)
2797 Node = new MatrixRowAssignment(RowVector, new MinusOne(RowVector, Start, this.pos - Start, this), Start, this.pos - Start, this);
2798 else
2799 {
2800 this.pos -= 2; // Can be a prefix operator.
2801 return Node;
2802 }
2803
2804 Node = new PlusOne(Node, Start, this.pos - Start, this);
2805 }
2806
2807 if (NullCheck)
2808 throw new SyntaxException("Null-checked post increment operator not defined.", this.pos, this.script);
2809
2810 break;
2811 }
2812 else
2813 {
2814 this.pos--;
2815 return Node;
2816 }
2817
2818 case '%':
2819 this.pos++;
2820
2821 if (NullCheck)
2822 throw new SyntaxException("Null-checked % operator not defined.", this.pos, this.script);
2823
2824 if (this.PeekNextChar() == '0')
2825 {
2826 this.pos++;
2827
2828 if (this.PeekNextChar() == '0')
2829 {
2830 this.pos++;
2831 Node = new Perdiezmil(Node, Start, this.pos - Start, this);
2832 }
2833 else
2834 Node = new Permil(Node, Start, this.pos - Start, this);
2835 }
2836 else
2837 Node = new Percent(Node, Start, this.pos - Start, this);
2838 break;
2839
2840 case '‰':
2841 this.pos++;
2842
2843 if (NullCheck)
2844 throw new SyntaxException("Null-checked ‰ operator not defined.", this.pos, this.script);
2845
2846 if (this.PeekNextChar() == '0')
2847 {
2848 this.pos++;
2849 Node = new Perdiezmil(Node, Start, this.pos - Start, this);
2850 }
2851 else
2852 Node = new Permil(Node, Start, this.pos - Start, this);
2853 break;
2854
2855 case '‱':
2856 this.pos++;
2857
2858 if (NullCheck)
2859 throw new SyntaxException("Null-checked ‱ operator not defined.", this.pos, this.script);
2860
2861 Node = new Perdiezmil(Node, Start, this.pos - Start, this);
2862 break;
2863
2864 case '°':
2865 this.pos++;
2866
2867 if (NullCheck)
2868 throw new SyntaxException("Null-checked ° operator not defined.", this.pos, this.script);
2869
2870 if ((ch = this.PeekNextChar()) == 'C' || ch == 'F')
2871 {
2872 this.pos++;
2873
2874 Unit Unit = new Unit(Prefix.None, new UnitFactor("°" + new string(ch, 1)));
2875
2876 if (Node is ConstantElement ConstantElement)
2877 {
2879
2880 if (C.AssociatedObjectValue is double d)
2881 {
2882 Node = new ConstantElement(new PhysicalQuantity(d, Unit),
2883 ConstantElement.Start, this.pos - ConstantElement.Start, this);
2884 }
2886 Node = new SetUnit(Node, Unit, Start, this.pos - Start, this);
2887 else
2888 {
2889 this.pos--;
2890 Node = new DegToRad(Node, Start, this.pos - Start, this);
2891 }
2892 }
2893 else
2894 Node = new SetUnit(Node, Unit, Start, this.pos - Start, this);
2895 }
2896 else
2897 Node = new DegToRad(Node, Start, this.pos - Start, this);
2898
2899 break;
2900
2901 case '\'':
2902 case '"':
2903 case '′':
2904 case '″':
2905 case '‴':
2906 int i = 0;
2907
2908 if (NullCheck)
2909 throw new SyntaxException("Null-checked differencial operators not defined.", this.pos, this.script);
2910
2911 while (true)
2912 {
2913 switch (this.PeekNextChar())
2914 {
2915 case '\'':
2916 case '′':
2917 i++;
2918 this.pos++;
2919 continue;
2920
2921 case '"':
2922 case '″':
2923 i += 2;
2924 this.pos++;
2925 continue;
2926
2927 case '‴':
2928 i += 3;
2929 this.pos++;
2930 continue;
2931 }
2932
2933 break;
2934 }
2935
2936 Node = new DefaultDifferentiation(Node, i, NullCheck, Start, this.pos - Start, this);
2937 break;
2938
2939 case 'T':
2940 this.pos++;
2941 ch = this.PeekNextChar();
2942 if (char.IsLetter(ch) || char.IsDigit(ch))
2943 {
2944 this.pos--;
2945
2946 if (!this.TryParseUnit(ref Node)) // T might be referencing the T prefix.
2947 return Node;
2948 }
2949 else
2950 {
2951 if (NullCheck)
2952 throw new SyntaxException("Null-checked T operator not defined.", this.pos, this.script);
2953
2954 Node = new Transpose(Node, Start, this.pos - Start, this);
2955 }
2956 break;
2957
2958 case 'H':
2959 this.pos++;
2960 ch = this.PeekNextChar();
2961 if (char.IsLetter(ch) || char.IsDigit(ch))
2962 {
2963 this.pos--;
2964 return Node;
2965 }
2966 else
2967 {
2968 if (NullCheck)
2969 throw new SyntaxException("Null-checked H operator not defined.", this.pos, this.script);
2970
2971 Node = new ConjugateTranspose(Node, Start, this.pos - Start, this);
2972 break;
2973 }
2974
2975 case '†':
2976 if (NullCheck)
2977 throw new SyntaxException("Null-checked † operator not defined.", this.pos, this.script);
2978
2979 this.pos++;
2980 Node = new ConjugateTranspose(Node, Start, this.pos - Start, this);
2981 break;
2982
2983 case '!':
2984 if (NullCheck)
2985 throw new SyntaxException("Null-checked ! operator not defined.", this.pos, this.script);
2986
2987 this.pos++;
2988 switch (this.PeekNextChar())
2989 {
2990 case '!':
2991 this.pos++;
2992 Node = new SemiFaculty(Node, Start, this.pos - Start, this);
2993 break;
2994
2995 case '=':
2996 this.pos--;
2997 return Node;
2998
2999 default:
3000 Node = new Faculty(Node, Start, this.pos - Start, this);
3001 break;
3002 }
3003 break;
3004
3005 default:
3006 if (NullCheck)
3007 throw new SyntaxException("Null-checked unit operator not defined.", this.pos, this.script);
3008
3009 if (char.IsLetter(ch))
3010 {
3011 if (!this.TryParseUnit(ref Node))
3012 return Node;
3013 }
3014 else
3015 return Node;
3016 break;
3017 }
3018
3019 NullCheck = false;
3020 }
3021 }
3022
3023 private bool TryParseUnit(ref ScriptNode Node)
3024 {
3025 int Bak = this.pos;
3026
3027 Unit Unit = this.ParseUnit(true);
3028 if (Unit is null)
3029 {
3030 this.pos = Bak;
3031 return false;
3032 }
3033
3034 int Start = Node.Start;
3035
3036 if (Node is ConstantElement ConstantElement)
3037 {
3039
3040 if (C.AssociatedObjectValue is double d)
3041 {
3042 Node = new ConstantElement(new PhysicalQuantity(d, Unit),
3043 ConstantElement.Start, this.pos - ConstantElement.Start, this);
3044 }
3046 Node = new SetUnit(Node, Unit, Start, this.pos - Start, this);
3047 else
3048 {
3049 this.pos = Bak;
3050 return false;
3051 }
3052 }
3053 else
3054 Node = new SetUnit(Node, Unit, Start, this.pos - Start, this);
3055
3056 return true;
3057 }
3058
3059 internal Unit ParseUnit(bool PermitPrefix)
3060 {
3061 Prefix Prefix;
3063 KeyValuePair<Prefix, UnitFactor[]> CompoundFactors;
3064 bool HasCompoundFactors;
3065 string Name, Name2, s;
3066 int Start = this.pos;
3067 int LastCompletion = Start;
3068 int Exponent;
3069 int i;
3070 char ch = this.NextChar();
3071 bool LastDivision = false;
3072
3073 if (PermitPrefix)
3074 {
3075 if (ch == 'd' && this.PeekNextChar() == 'a')
3076 {
3077 this.pos++;
3078 Prefix = Prefix.Deka;
3079 }
3080 else if (!Prefixes.TryParsePrefix(ch, out Prefix))
3081 this.pos--;
3082
3083 i = this.pos;
3084 ch = this.NextChar();
3085 }
3086 else
3087 {
3088 Prefix = Prefix.None;
3089 i = this.pos - 1;
3090 }
3091
3092 if (!char.IsLetter(ch) && Prefix != Prefix.None)
3093 {
3094 this.pos = i = Start;
3095 Prefix = Prefix.None;
3096 ch = this.NextChar();
3097 }
3098 else if (ch == '/')
3099 {
3100 LastDivision = true;
3101 ch = this.NextChar();
3102 while (ch > 0 && (ch <= ' ' || ch == 160))
3103 ch = this.NextChar();
3104 }
3105
3106 while (char.IsLetter(ch) || ch == '(')
3107 {
3108 if (ch == '(')
3109 {
3110 Unit Unit = this.ParseUnit(false);
3111
3112 if (Unit is null)
3113 {
3114 this.pos = Start;
3115 return null;
3116 }
3117
3118 ch = this.NextChar();
3119 while (ch > 0 && (ch <= ' ' || ch == 160))
3120 ch = this.NextChar();
3121
3122 if (ch != ')')
3123 throw new SyntaxException("Expected ).", this.pos, this.script);
3124
3125 ch = this.NextChar();
3126 while (ch > 0 && (ch <= ' ' || ch == 160))
3127 ch = this.NextChar();
3128
3129 if (ch == '^')
3130 {
3131 ch = this.NextChar();
3132 while (ch > 0 && (ch <= ' ' || ch == 160))
3133 ch = this.NextChar();
3134
3135 if (ch == '-' || char.IsDigit(ch))
3136 {
3137 i = this.pos - 1;
3138
3139 if (ch == '-')
3140 ch = this.NextChar();
3141
3142 while (char.IsDigit(ch))
3143 ch = this.NextChar();
3144
3145 if (ch == 0)
3146 s = this.script.Substring(i, this.pos - i);
3147 else
3148 s = this.script.Substring(i, this.pos - i - 1);
3149
3150 if (!int.TryParse(s, out Exponent))
3151 {
3152 this.pos = Start;
3153 return null;
3154 }
3155 }
3156 else
3157 {
3158 this.pos = Start;
3159 return null;
3160 }
3161 }
3162 else if (ch == '²')
3163 {
3164 Exponent = 2;
3165 ch = this.NextChar();
3166 }
3167 else if (ch == '³')
3168 {
3169 Exponent = 3;
3170 ch = this.NextChar();
3171 }
3172 else
3173 Exponent = 1;
3174
3175 if (LastDivision)
3176 {
3177 foreach (UnitFactor Factor in Unit.Factors)
3178 Factors.Add(new UnitFactor(Factor.Unit, -Factor.Exponent * Exponent));
3179 }
3180 else
3181 {
3182 foreach (UnitFactor Factor in Unit.Factors)
3183 Factors.Add(new UnitFactor(Factor.Unit, Factor.Exponent * Exponent));
3184 }
3185 }
3186 else
3187 {
3188 while (char.IsLetter(ch))
3189 ch = this.NextChar();
3190
3191 if (ch == 0)
3192 Name = this.script.Substring(i, this.pos - i);
3193 else
3194 Name = this.script.Substring(i, this.pos - i - 1);
3195
3196 if (PermitPrefix)
3197 {
3198 if (keywords.ContainsKey(Name2 = this.script.Substring(Start, i - Start) + Name))
3199 {
3200 this.pos = Start;
3201 return null;
3202 }
3203 else if (HasCompoundFactors = Unit.TryGetCompoundUnit(Name2, out CompoundFactors))
3204 {
3205 Prefix = CompoundFactors.Key;
3206 Name = Name2;
3207 }
3208 else if (Unit.ContainsDerivedOrBaseUnit(Name2))
3209 {
3210 Prefix = Prefix.None;
3211 Name = Name2;
3212 }
3213 else
3214 HasCompoundFactors = Unit.TryGetCompoundUnit(Name, out CompoundFactors);
3215 }
3216 else
3217 HasCompoundFactors = Unit.TryGetCompoundUnit(Name, out CompoundFactors);
3218
3219 while (ch > 0 && (ch <= ' ' || ch == 160))
3220 ch = this.NextChar();
3221
3222 if (ch == '^')
3223 {
3224 ch = this.NextChar();
3225 while (ch > 0 && (ch <= ' ' || ch == 160))
3226 ch = this.NextChar();
3227
3228 if (ch == '-' || char.IsDigit(ch))
3229 {
3230 i = this.pos - 1;
3231
3232 if (ch == '-')
3233 ch = this.NextChar();
3234
3235 while (char.IsDigit(ch))
3236 ch = this.NextChar();
3237
3238 if (ch == 0)
3239 s = this.script.Substring(i, this.pos - i);
3240 else
3241 s = this.script.Substring(i, this.pos - i - 1);
3242
3243 if (!int.TryParse(s, out Exponent))
3244 {
3245 this.pos = Start;
3246 return null;
3247 }
3248 }
3249 else
3250 {
3251 this.pos = Start;
3252 return null;
3253 }
3254 }
3255 else if (ch == '²')
3256 {
3257 Exponent = 2;
3258 ch = this.NextChar();
3259 }
3260 else if (ch == '³')
3261 {
3262 Exponent = 3;
3263 ch = this.NextChar();
3264 }
3265 else
3266 Exponent = 1;
3267
3268 if (HasCompoundFactors)
3269 {
3270 if (LastDivision)
3271 {
3272 foreach (UnitFactor Segment in CompoundFactors.Value)
3273 Factors.Add(new UnitFactor(Segment.Unit, -Segment.Exponent * Exponent));
3274 }
3275 else
3276 {
3277 foreach (UnitFactor Segment in CompoundFactors.Value)
3278 Factors.Add(new UnitFactor(Segment.Unit, Segment.Exponent * Exponent));
3279 }
3280 }
3281 else
3282 {
3283 if (LastDivision)
3284 Factors.Add(new UnitFactor(Name, -Exponent));
3285 else
3286 Factors.Add(new UnitFactor(Name, Exponent));
3287 }
3288 }
3289
3290 while (ch > 0 && (ch <= ' ' || ch == 160))
3291 ch = this.NextChar();
3292
3293 if (ch == 0)
3294 LastCompletion = this.pos;
3295 else
3296 LastCompletion = this.pos - 1;
3297
3298 if (ch == '*' || ch == '⋅')
3299 LastDivision = false;
3300 else if (ch == '/')
3301 LastDivision = true;
3302 else
3303 break;
3304
3305 ch = this.NextChar();
3306 while (ch > 0 && (ch <= ' ' || ch == 160))
3307 ch = this.NextChar();
3308
3309 i = this.pos - 1;
3310 PermitPrefix = false;
3311 }
3312
3313 this.pos = LastCompletion;
3314
3315 if (!Factors.HasFirstItem)
3316 {
3317 this.pos = Start;
3318 return null;
3319 }
3320
3321 return new Unit(Prefix, Factors);
3322 }
3323
3324 private static ScriptNode GetFunction(string FunctionName, ScriptNode Arguments,
3325 bool NullCheck, int Start, int Length, Expression Expression)
3326 {
3327 Dictionary<string, FunctionRef> F;
3328 int NrParameters;
3329 ElementList ElementList = null;
3330 object[] P;
3331
3332 if (Arguments is null)
3333 {
3334 NrParameters = 0;
3335 P = new object[3];
3336 }
3337 else if (Arguments.GetType() == typeof(ElementList))
3338 {
3339 ElementList = (ElementList)Arguments;
3340 NrParameters = ElementList.Elements.Length;
3341 P = new object[NrParameters + 3];
3342 ElementList.Elements.CopyTo(P, 0);
3343 }
3344 else
3345 {
3346 NrParameters = 1;
3347 P = new object[4];
3348 P[0] = Arguments;
3349 }
3350
3351 P[NrParameters] = Start;
3352 P[NrParameters + 1] = Length;
3353 P[NrParameters + 2] = Expression;
3354
3355 F = functions;
3356 if (F is null)
3357 {
3358 Search();
3359 F = functions;
3360 }
3361
3362 if (F.TryGetValue(FunctionName + " " + NrParameters.ToString(), out FunctionRef Ref))
3363 return Ref.CreateFunction(P, Expression);
3364 else
3365 {
3366 if (!(ElementList is null))
3367 return new NamedFunctionCall(FunctionName, ElementList.Elements, NullCheck, Start, Length, Expression);
3368 else if (Arguments is null)
3369 return new NamedFunctionCall(FunctionName, Array.Empty<ScriptNode>(), NullCheck, Start, Length, Expression);
3370 else
3371 return new NamedFunctionCall(FunctionName, new ScriptNode[] { Arguments }, NullCheck, Start, Length, Expression);
3372 }
3373 }
3374
3382 public static bool TryGetConstant(string Name, Variables Variables, out IElement ValueElement)
3383 {
3384 Dictionary<string, IConstant> C = constants;
3385 if (C is null)
3386 {
3387 Search();
3388 C = constants;
3389 }
3390
3391 if (!C.TryGetValue(Name, out IConstant Constant))
3392 {
3393 ValueElement = null;
3394 return false;
3395 }
3396
3397 ValueElement = Constant.GetValueElement(Variables ?? new Variables());
3398 return !(ValueElement is null);
3399 }
3400
3401 internal static IElement GetFunctionLambdaDefinition(string FunctionName, int Start, int Length,
3403 {
3404 Dictionary<string, FunctionRef> F;
3405
3406 F = functions;
3407 if (F is null)
3408 {
3409 Search();
3410 F = functions;
3411 }
3412
3413 if (!F.TryGetValue(FunctionName, out FunctionRef Ref))
3414 return null;
3415
3416 LambdaDefinition CreateLambda(Function Function, ConstructorInfo Constructor,
3417 ParameterInfo[] ConstructorParameters)
3418 {
3419 int i, c = ConstructorParameters.Length - 3;
3420 ArgumentType[] ArgumentTypes = new ArgumentType[c];
3421 object[] Arguments = new object[c + 3];
3422 string[] DefaultNames = Function.DefaultArgumentNames;
3423 bool UseDefaultNames = DefaultNames.Length == c;
3424 string[] Names = UseDefaultNames ? DefaultNames : new string[c];
3425
3426 Arguments[c] = Start;
3427 Arguments[c + 1] = Length;
3428 Arguments[c + 2] = Expression;
3429
3430 for (i = 0; i < c; i++)
3431 {
3432 if (!UseDefaultNames)
3433 Names[i] = ConstructorParameters[i].Name;
3434
3435 Arguments[i] = new VariableReference(Names[i], Start, Length, Expression);
3436 ArgumentTypes[i] = ArgumentType.Normal;
3437 }
3438
3439 ScriptNode FunctionCall = (ScriptNode)Constructor.Invoke(Arguments);
3440
3441 return new LambdaDefinition(Names, ArgumentTypes, FunctionCall, Start, Length, Expression);
3442 }
3443
3444 if (!Ref.Multiple)
3445 return CreateLambda(Ref.MainFunction, Ref.MainConstructor, Ref.MainConstructorParameters);
3446
3448 {
3449 CreateLambda(Ref.MainFunction, Ref.MainConstructor, Ref.MainConstructorParameters)
3450 };
3451
3452 for (int i = 0; i < Ref.NrAdditional; i++)
3453 {
3454 Lambdas.Add(CreateLambda(Ref.Additional[i], Ref.AdditionalConstructors[i],
3455 Ref.AdditionalConstructorParameters[i]));
3456 }
3457
3458 return new ObjectVector(Lambdas.ToArray());
3459 }
3460
3461 private static void Search()
3462 {
3463 lock (searchSynch)
3464 {
3465 if (functions is null)
3466 {
3467 Dictionary<int, object[]> ParameterValuesPerNrParameters = new Dictionary<int, object[]>();
3468 Dictionary<string, FunctionRef> Found = new Dictionary<string, FunctionRef>(StringComparer.CurrentCultureIgnoreCase);
3469 ParameterInfo[] Parameters;
3470 ParameterInfo PInfo;
3471 FunctionRef Ref;
3472 string[] Aliases;
3474 string s;
3475 int i, c;
3476 TypeInfo TI;
3477
3478 void RegisterFunction(string Name, int NrArguments, Type T,
3479 ConstructorInfo CI, ParameterInfo[] ConstructorParameters)
3480 {
3481 if (NrArguments < 0)
3482 s = Name;
3483 else
3484 s = Name + " " + NrArguments.ToString();
3485
3486 if (Found.TryGetValue(s, out FunctionRef Prev))
3487 {
3488 if (!Prev.Multiple)
3489 {
3490 Prev.Multiple = true;
3491 Prev.Additional = new Function[] { Function };
3492 Prev.AdditionalConstructors = new ConstructorInfo[] { CI };
3493 Prev.AdditionalConstructorParameters = new ParameterInfo[][] { ConstructorParameters };
3494 Prev.NrAdditional = 1;
3495 }
3496 else
3497 {
3498 Array.Resize(ref Prev.Additional, Prev.NrAdditional + 1);
3499 Array.Resize(ref Prev.AdditionalConstructors, Prev.NrAdditional + 1);
3500 Array.Resize(ref Prev.AdditionalConstructorParameters, Prev.NrAdditional + 1);
3501 Prev.Additional[Prev.NrAdditional] = Function;
3502 Prev.AdditionalConstructors[Prev.NrAdditional] = CI;
3503 Prev.AdditionalConstructorParameters[Prev.NrAdditional] = ConstructorParameters;
3504 Prev.NrAdditional++;
3505 }
3506 }
3507 else
3508 {
3509 Ref = new FunctionRef()
3510 {
3511 MainConstructor = CI,
3512 MainConstructorParameters = ConstructorParameters,
3513 MainFunction = Function,
3514 NrParameters = c - 3,
3515 Multiple = false,
3516 Additional = null,
3517 AdditionalConstructors = null,
3518 AdditionalConstructorParameters = null,
3519 NrAdditional = 0,
3520 Name = Name
3521 };
3522
3523 Found[s] = Ref;
3524 }
3525
3526 if (NrArguments >= 0)
3527 RegisterFunction(Name, -1, T, CI, ConstructorParameters);
3528 }
3529
3530 foreach (Type T in Types.GetTypesImplementingInterface(typeof(IFunction)))
3531 {
3532 TI = T.GetTypeInfo();
3533 if (TI.IsAbstract || TI.IsInterface || TI.IsGenericTypeDefinition)
3534 continue;
3535
3536 foreach (ConstructorInfo CI in TI.DeclaredConstructors)
3537 {
3538 Parameters = CI.GetParameters();
3539 c = Parameters.Length;
3540 if (c < 3)
3541 continue;
3542
3543 PInfo = Parameters[c - 1];
3544 if (PInfo.IsOut || PInfo.IsRetval || PInfo.IsOptional || PInfo.ParameterType != typeof(Expression))
3545 continue;
3546
3547 PInfo = Parameters[c - 2];
3548 if (PInfo.IsOut || PInfo.IsRetval || PInfo.IsOptional || PInfo.ParameterType != typeof(int))
3549 continue;
3550
3551 PInfo = Parameters[c - 3];
3552 if (PInfo.IsOut || PInfo.IsRetval || PInfo.IsOptional || PInfo.ParameterType != typeof(int))
3553 continue;
3554
3555 for (i = c - 4; i >= 0; i--)
3556 {
3557 PInfo = Parameters[i];
3558 if (PInfo.IsOut || PInfo.IsRetval || PInfo.IsOptional || PInfo.ParameterType != typeof(ScriptNode))
3559 break;
3560 }
3561
3562 if (i >= 0)
3563 continue;
3564
3565 try
3566 {
3567 if (!ParameterValuesPerNrParameters.TryGetValue(c, out object[] ParameterValues))
3568 {
3569 ParameterValues = new object[c];
3570 ParameterValues[c - 1] = null;
3571 ParameterValues[c - 2] = 0;
3572 ParameterValues[c - 3] = 0;
3573 ParameterValuesPerNrParameters[c] = ParameterValues;
3574 }
3575
3576 Function = CI.Invoke(ParameterValues) as Function;
3577 if (Function is null)
3578 continue;
3579
3580 RegisterFunction(Function.FunctionName, c - 3, T, CI, Parameters);
3581
3582 Aliases = Function.Aliases;
3583 if (!(Aliases is null))
3584 {
3585 foreach (string Alias in Aliases)
3586 RegisterFunction(Alias, c - 3, T, CI, Parameters);
3587 }
3588 }
3589 catch (Exception ex)
3590 {
3591 ex = Log.UnnestException(ex);
3592
3593 if (ex is AggregateException ex2)
3594 {
3595 foreach (Exception ex3 in ex2.InnerExceptions)
3596 Log.Exception(ex3);
3597 }
3598 else
3599 Log.Exception(ex);
3600 }
3601 }
3602 }
3603
3604 functions = Found;
3605 }
3606
3607 if (constants is null)
3608 {
3609 Dictionary<string, IConstant> Found = new Dictionary<string, IConstant>(StringComparer.CurrentCultureIgnoreCase);
3610 string[] Aliases;
3611 string s;
3612
3613 foreach (Type T in Types.GetTypesImplementingInterface(typeof(IConstant)))
3614 {
3615 ConstructorInfo CI = Types.GetDefaultConstructor(T);
3616 if (CI is null)
3617 continue;
3618
3619 try
3620 {
3621 IConstant Constant = (IConstant)CI.Invoke(Types.NoParameters);
3622
3623 s = Constant.ConstantName;
3624 if (Found.TryGetValue(s, out IConstant PrevConstant))
3625 {
3626 if (PrevConstant.GetType() != T)
3627 {
3628 Log.Warning("Constant with name " + s + " previously registered. Constant ignored.",
3629 T.FullName, new KeyValuePair<string, object>("Previous", Constant.GetType().FullName));
3630 }
3631 }
3632 else
3633 Found[s] = Constant;
3634
3635 Aliases = Constant.Aliases;
3636 if (!(Aliases is null))
3637 {
3638 foreach (string Alias in Aliases)
3639 {
3640 if (Found.TryGetValue(Alias, out PrevConstant))
3641 {
3642 if (PrevConstant.GetType() != T)
3643 {
3644 Log.Warning("Constant with name " + Alias + " previously registered. Constant ignored.",
3645 T.FullName, new KeyValuePair<string, object>("Previous", Constant.GetType().FullName));
3646 }
3647 }
3648 else
3649 Found[Alias] = Constant;
3650 }
3651 }
3652 }
3653 catch (Exception ex)
3654 {
3655 Log.Exception(ex);
3656 }
3657 }
3658
3659 constants = Found;
3660 }
3661
3662 if (customKeyWords is null)
3663 {
3664 Dictionary<string, IKeyWord> Found = new Dictionary<string, IKeyWord>(StringComparer.CurrentCultureIgnoreCase);
3665 string[] Aliases;
3666 string s;
3667
3668 foreach (Type T in Types.GetTypesImplementingInterface(typeof(IKeyWord)))
3669 {
3670 ConstructorInfo CI = Types.GetDefaultConstructor(T);
3671 if (CI is null)
3672 continue;
3673
3674 try
3675 {
3676 IKeyWord KeyWord = (IKeyWord)CI.Invoke(Types.NoParameters);
3677
3678 s = KeyWord.KeyWord;
3679 if (Found.ContainsKey(s))
3680 {
3681 Log.Warning("Keyword with name " + s + " previously registered. Keyword ignored.",
3682 T.FullName, new KeyValuePair<string, object>("Previous", KeyWord.GetType().FullName));
3683 }
3684 else
3685 Found[s] = KeyWord;
3686
3687 Aliases = KeyWord.Aliases;
3688 if (!(Aliases is null))
3689 {
3690 foreach (string Alias in Aliases)
3691 {
3692 if (Found.ContainsKey(Alias))
3693 {
3694 Log.Warning("Keyword with name " + Alias + " previously registered. Keyword ignored.",
3695 T.FullName, new KeyValuePair<string, object>("Previous", KeyWord.GetType().FullName));
3696 }
3697 else
3698 Found[Alias] = KeyWord;
3699 }
3700 }
3701 }
3702 catch (Exception ex)
3703 {
3704 Log.Exception(ex);
3705 }
3706 }
3707
3708 customKeyWords = Found;
3709 }
3710 }
3711 }
3712
3713 private class FunctionRef
3714 {
3715 public ConstructorInfo MainConstructor;
3716 public ParameterInfo[] MainConstructorParameters;
3717 public Function MainFunction;
3718 public Function[] Additional;
3719 public ConstructorInfo[] AdditionalConstructors;
3720 public ParameterInfo[][] AdditionalConstructorParameters;
3721 public string Name;
3722 public int NrParameters;
3723 public int NrAdditional;
3724 public bool Multiple;
3725
3726 public Function CreateFunction(object[] Parameters, Expression Expression)
3727 {
3728 if (!this.Multiple)
3729 return (Function)this.MainConstructor.Invoke(Parameters);
3730
3731 Function F;
3732
3733 if (this.MainFunction.ContextSpecific(Expression))
3734 F = (Function)this.MainConstructor.Invoke(Parameters);
3735 else
3736 F = null;
3737
3738 for (int i = 0; i < this.NrAdditional; i++)
3739 {
3740 if (this.Additional[i].ContextSpecific(Expression))
3741 {
3742 if (F is null)
3743 F = (Function)this.AdditionalConstructors[i].Invoke(Parameters);
3744 else
3745 {
3746 throw new SyntaxException("Multiple functions with the same name recognized the same context: " + this.Name,
3747 Expression.pos, Expression.script);
3748 }
3749 break;
3750 }
3751 }
3752
3753 if (F is null)
3754 {
3755 throw new SyntaxException("Multiple functions registered with the name " +
3756 this.Name + " but none recognized the current context.", Expression.pos,
3757 Expression.script);
3758 }
3759
3760 return F;
3761 }
3762 }
3763
3764 internal ScriptNode ParseObject()
3765 {
3766 this.SkipWhiteSpace();
3767
3768 ScriptNode Node;
3769 int Start = this.pos;
3770 char ch = this.PeekNextChar();
3771
3772 if (ch == '(')
3773 {
3774 bool WsBak = this.canSkipWhitespace;
3775 this.canSkipWhitespace = true;
3776 this.pos++;
3777 Node = this.ParseSequence();
3778
3779 this.SkipWhiteSpace();
3780 if (this.PeekNextChar() != ')')
3781 throw new SyntaxException("Expected ).", this.pos, this.script);
3782
3783 this.canSkipWhitespace = WsBak;
3784 this.pos++;
3785
3786 if (Node is null)
3787 {
3788 this.SkipWhiteSpace();
3789 if (this.PeekNextChar() == '-')
3790 {
3791 this.pos++;
3792 if (this.PeekNextChar() == '>')
3793 {
3794 this.pos++;
3795
3796 if (!(this.ParseEquivalence() is ScriptNode Operand))
3797 throw new SyntaxException("Lambda function body missing.", this.pos, this.script);
3798
3799 return new LambdaDefinition(Array.Empty<string>(), Array.Empty<ArgumentType>(), Operand, Start, this.pos - Start, this);
3800 }
3801 }
3802
3803 throw new SyntaxException("Expected argument-less Lambda expression", this.pos, this.script);
3804 }
3805 else
3806 {
3807 Node.Start = Start;
3808 Node.Length = this.pos - Start;
3809 return Node;
3810 }
3811 }
3812 else if (ch == '[')
3813 {
3814 this.pos++;
3815 this.SkipWhiteSpace();
3816 if (this.PeekNextChar() == ']')
3817 {
3818 this.pos++;
3819 return new VectorDefinition(Array.Empty<ScriptNode>(), Start, this.pos - Start, this);
3820 }
3821
3822 bool WsBak = this.canSkipWhitespace;
3823 this.canSkipWhitespace = true;
3824 Node = this.ParseStatement(true);
3825
3826 this.SkipWhiteSpace();
3827 switch (this.PeekNextChar())
3828 {
3829 case ']':
3830 this.pos++;
3831 this.canSkipWhitespace = WsBak;
3832
3833 if (Node is For For)
3834 {
3835 if (IsVectorDefinition(For.RightOperand))
3836 return new MatrixForDefinition(For, Start, this.pos - Start, this);
3837 else
3838 return new VectorForDefinition(For, Start, this.pos - Start, this);
3839 }
3840 else if (Node is ForEach ForEach)
3841 {
3842 if (IsVectorDefinition(ForEach.RightOperand))
3843 return new MatrixForEachDefinition(ForEach, Start, this.pos - Start, this);
3844 else
3845 return new VectorForEachDefinition(ForEach, Start, this.pos - Start, this);
3846 }
3847 else if (Node is DoWhile DoWhile)
3848 {
3849 if (IsVectorDefinition(DoWhile.LeftOperand))
3850 return new MatrixDoWhileDefinition(DoWhile, Start, this.pos - Start, this);
3851 else
3852 return new VectorDoWhileDefinition(DoWhile, Start, this.pos - Start, this);
3853 }
3854 else if (Node is WhileDo WhileDo)
3855 {
3856 if (IsVectorDefinition(WhileDo.RightOperand))
3857 return new MatrixWhileDoDefinition(WhileDo, Start, this.pos - Start, this);
3858 else
3859 return new VectorWhileDoDefinition(WhileDo, Start, this.pos - Start, this);
3860 }
3861 else if (Node.GetType() == typeof(ElementList))
3862 {
3864 bool AllVectors = true;
3865
3867 {
3868 if (!IsVectorDefinition(Element))
3869 {
3870 AllVectors = false;
3871 break;
3872 }
3873 }
3874
3875 if (AllVectors)
3876 return new MatrixDefinition(((ElementList)Node).Elements, Start, this.pos - Start, this);
3877 else
3878 return new VectorDefinition(((ElementList)Node).Elements, Start, this.pos - Start, this);
3879 }
3880 else if (IsVectorDefinition(Node))
3881 return new MatrixDefinition(new ScriptNode[] { Node }, Start, this.pos - Start, this);
3882 else
3883 return new VectorDefinition(new ScriptNode[] { Node }, Start, this.pos - Start, this);
3884
3885 case ':':
3886 this.pos++;
3887
3888 ScriptNode Temp = this.ParseList();
3889 ScriptNode[] Conditions;
3890 ScriptNode SuperSet;
3891
3892 if (Temp is ElementList List)
3893 Conditions = List.Elements;
3894 else
3895 Conditions = new ScriptNode[] { Temp };
3896
3897 if (Node is In In && !(Node is NotIn))
3898 {
3899 SuperSet = In.RightOperand;
3900 Node = In.LeftOperand;
3901 }
3902 else
3903 SuperSet = null;
3904
3905 this.SkipWhiteSpace();
3906 if (this.PeekNextChar() != ']')
3907 throw new SyntaxException("Expected ].", this.pos, this.script);
3908
3909 this.canSkipWhitespace = WsBak;
3910 this.pos++;
3911
3912 return new ImplicitVectorDefinition(Node, SuperSet, Conditions, Start, this.pos - Start, this);
3913
3914 default:
3915 throw new SyntaxException("Expected ] or :.", this.pos, this.script);
3916 }
3917 }
3918 else if (ch == '{')
3919 {
3920 bool ObjectWildcard = false;
3921 bool WsBak = this.canSkipWhitespace;
3922
3923 this.pos++;
3924 this.canSkipWhitespace = true;
3925 this.SkipWhiteSpace();
3926
3927 switch (this.PeekNextChar())
3928 {
3929 case '}':
3930 this.pos++;
3931 this.CanSkipWhitespace = WsBak;
3932 return new ObjectExNihilo(new ChunkedList<KeyValuePair<string, ScriptNode>>(), false, Start, this.pos - Start, this);
3933
3934 case '*':
3935 this.pos++;
3936 ObjectWildcard = true;
3937 Node = null;
3938 break;
3939
3940 default:
3941 Node = this.ParseStatement(true);
3942 break;
3943 }
3944
3945 this.SkipWhiteSpace();
3946 if (ObjectWildcard || (ch = this.PeekNextChar()) == ':')
3947 {
3948 bool DoubleColon = false;
3949
3950 if (!ObjectWildcard)
3951 {
3952 this.pos++;
3953
3954 if (this.PeekNextChar() == ':')
3955 {
3956 this.pos++;
3957 DoubleColon = true;
3958 }
3959 }
3960
3961 if (!DoubleColon && (ObjectWildcard || Node is VariableReference || Node is ConstantElement))
3962 {
3964 Dictionary<string, bool> MembersFound = new Dictionary<string, bool>();
3967 string s;
3968
3969 if (!ObjectWildcard)
3970 {
3973 else if (!((ConstantElement = Node as ConstantElement) is null) &&
3975 {
3976 s = StringValue.Value;
3977 }
3978 else
3979 throw new SyntaxException("Expected a variable reference or a string constant.", this.pos, this.script);
3980
3981 MembersFound[s] = true;
3982 Members.Add(new KeyValuePair<string, ScriptNode>(s, this.ParseLambdaExpression()));
3983
3984 this.SkipWhiteSpace();
3985 }
3986
3987 while ((ch = this.PeekNextChar()) == ',')
3988 {
3989 this.pos++;
3990 this.SkipWhiteSpace();
3991
3992 if (this.PeekNextChar() == '*')
3993 {
3994 this.pos++;
3995 ObjectWildcard = true;
3996 }
3997 else
3998 {
3999 Node = this.ParseStatement(false);
4000
4001 this.SkipWhiteSpace();
4002 if (this.PeekNextChar() != ':')
4003 throw new SyntaxException("Expected :.", this.pos, this.script);
4004
4005 if (Node is VariableReference VariableReference2)
4006 s = VariableReference2.VariableName;
4007 else if (!((ConstantElement = Node as ConstantElement) is null) &&
4009 {
4010 s = StringValue.Value;
4011 }
4012 else
4013 throw new SyntaxException("Expected a variable reference or a string constant.", this.pos, this.script);
4014
4015 if (MembersFound.ContainsKey(s))
4016 throw new SyntaxException("Member already defined.", this.pos, this.script);
4017
4018 this.pos++;
4019 MembersFound[s] = true;
4020 Members.Add(new KeyValuePair<string, ScriptNode>(s, this.ParseLambdaExpression()));
4021 }
4022
4023 this.SkipWhiteSpace();
4024 }
4025
4026 if (ch != '}')
4027 throw new SyntaxException("Expected }.", this.pos, this.script);
4028
4029 this.canSkipWhitespace = WsBak;
4030 this.pos++;
4031 return new ObjectExNihilo(Members, ObjectWildcard, Start, this.pos - Start, this);
4032 }
4033
4034 ScriptNode Temp = this.ParseList();
4035 ScriptNode[] Conditions;
4036 ScriptNode SuperSet;
4037
4038 if (Temp is ElementList List)
4039 Conditions = List.Elements;
4040 else
4041 Conditions = new ScriptNode[] { Temp };
4042
4043 if (Node is In In && !(Node is NotIn))
4044 {
4045 SuperSet = In.RightOperand;
4046 Node = In.LeftOperand;
4047 }
4048 else
4049 SuperSet = null;
4050
4051 this.SkipWhiteSpace();
4052 if (this.PeekNextChar() != '}')
4053 throw new SyntaxException("Expected }.", this.pos, this.script);
4054
4055 this.canSkipWhitespace = WsBak;
4056 this.pos++;
4057
4058 return new ImplicitSetDefinition(Node, SuperSet, Conditions, DoubleColon, Start, this.pos - Start, this);
4059 }
4060
4061 if (ch != '}')
4062 throw new SyntaxException("Expected }.", this.pos, this.script);
4063
4064 this.canSkipWhitespace = WsBak;
4065 this.pos++;
4066
4067 if (Node is For For)
4068 return new SetForDefinition(For, Start, this.pos - Start, this);
4069 else if (Node is ForEach ForEach)
4070 return new SetForEachDefinition(ForEach, Start, this.pos - Start, this);
4071 else if (Node is DoWhile DoWhile)
4072 return new SetDoWhileDefinition(DoWhile, Start, this.pos - Start, this);
4073 else if (Node is WhileDo WhileDo)
4074 return new SetWhileDoDefinition(WhileDo, Start, this.pos - Start, this);
4075 else if (Node.GetType() == typeof(ElementList))
4076 return new SetDefinition(((ElementList)Node).Elements, Start, this.pos - Start, this);
4077 else
4078 return new SetDefinition(new ScriptNode[] { Node }, Start, this.pos - Start, this);
4079 }
4080 else if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' || ch == '-')
4081 {
4082 if (ch == '+' || ch == '-')
4083 {
4084 this.pos++;
4085 ch = this.PeekNextChar();
4086 }
4087
4088 while (ch >= '0' && ch <= '9')
4089 {
4090 this.pos++;
4091 ch = this.PeekNextChar();
4092 }
4093
4094 if (ch == '.')
4095 {
4096 this.pos++;
4097 ch = this.PeekNextChar();
4098
4099 if (ch >= '0' && ch <= '9')
4100 {
4101 while (ch >= '0' && ch <= '9')
4102 {
4103 this.pos++;
4104 ch = this.PeekNextChar();
4105 }
4106 }
4107 else
4108 {
4109 this.pos--;
4110 ch = '.';
4111 }
4112 }
4113
4114 if (char.ToUpper(ch) == 'E')
4115 {
4116 this.pos++;
4117 ch = this.PeekNextChar();
4118
4119 if (ch == '+' || ch == '-')
4120 {
4121 this.pos++;
4122 ch = this.PeekNextChar();
4123 }
4124
4125 while (ch >= '0' && ch <= '9')
4126 {
4127 this.pos++;
4128 ch = this.PeekNextChar();
4129 }
4130 }
4131
4132 if (!double.TryParse(this.script.Substring(Start, this.pos - Start).
4133 Replace(".", NumberFormatInfo.CurrentInfo.NumberDecimalSeparator), out double d))
4134 {
4135 throw new SyntaxException("Invalid double number.", this.pos, this.script);
4136 }
4137
4138 return new ConstantElement(new DoubleNumber(d), Start, this.pos - Start, this);
4139 }
4140 else if (ch == '#')
4141 {
4142 char Base;
4143 bool Sign = false;
4144
4145 this.pos++;
4146 ch = this.PeekNextChar();
4147
4148 if (ch == '-')
4149 {
4150 Sign = true;
4151 this.pos++;
4152 ch = this.PeekNextChar();
4153 }
4154 else if (ch == '+')
4155 {
4156 this.pos++;
4157 ch = this.PeekNextChar();
4158 }
4159
4160 ch = char.ToLower(ch);
4161 int Start2 = this.pos;
4162
4163 if (ch >= '0' && ch <= '9')
4164 Base = 'd';
4165 else if (ch == 'd' || ch == 'x' || ch == 'o' || ch == 'b')
4166 {
4167 Base = ch;
4168 Start2 = ++this.pos;
4169 }
4170 else
4171 throw new SyntaxException("Invalid numerical base.", this.pos, this.script);
4172
4173 BigInteger n = BigInteger.Zero;
4174
4175 switch (Base)
4176 {
4177 case 'd':
4178 while (this.pos < this.len && (ch = this.script[this.pos]) >= '0' && ch <= '9')
4179 this.pos++;
4180
4181 if (Start2 == this.pos)
4182 throw new SyntaxException("Invalid integer.", this.pos, this.script);
4183
4184 n = BigInteger.Parse(this.script.Substring(Start2, this.pos - Start2));
4185 break;
4186
4187 case 'x':
4188 n = 0;
4189 while (this.pos < this.len)
4190 {
4191 ch = this.script[this.pos];
4192
4193 if (ch >= '0' && ch <= '9')
4194 ch -= '0';
4195 else if (ch >= 'a' && ch <= 'f')
4196 ch -= (char)('a' - 10);
4197 else if (ch >= 'A' && ch <= 'F')
4198 ch -= (char)('A' - 10);
4199 else
4200 break;
4201
4202 this.pos++;
4203 n <<= 4;
4204 n += ch;
4205 }
4206 break;
4207
4208 case 'o':
4209 while (this.pos < this.len && (ch = this.script[this.pos]) >= '0' && ch <= '7')
4210 {
4211 this.pos++;
4212 n <<= 3;
4213 n += ch - '0';
4214 }
4215 break;
4216
4217 case 'b':
4218 while (this.pos < this.len && (ch = this.script[this.pos]) >= '0' && ch <= '1')
4219 {
4220 this.pos++;
4221 n <<= 1;
4222 n += ch - '0';
4223 }
4224 break;
4225 }
4226
4227 if (Start2 == this.pos)
4228 throw new SyntaxException("Invalid integer.", this.pos, this.script);
4229
4230 if (Sign)
4231 n = -n;
4232
4233 return new ConstantElement(new Integer(n), Start, this.pos - Start, this);
4234 }
4235 else if (ch == '"' || ch == '\'')
4236 {
4237 StringBuilder sb = new StringBuilder();
4238 char ch2;
4239
4240 this.pos++;
4241
4242 while ((ch2 = this.NextChar()) != ch)
4243 {
4244 if (ch2 == 0 || ch2 == '\r' || ch2 == '\n')
4245 throw new SyntaxException("Expected end of string.", this.pos, this.script);
4246
4247 if (ch2 == '\\')
4248 {
4249 ch2 = this.NextChar();
4250 switch (ch2)
4251 {
4252 case (char)0:
4253 throw new SyntaxException("Expected end of string.", this.pos, this.script);
4254
4255 case 'n':
4256 ch2 = '\n';
4257 break;
4258
4259 case 'r':
4260 ch2 = '\r';
4261 break;
4262
4263 case 't':
4264 ch2 = '\t';
4265 break;
4266
4267 case 'b':
4268 ch2 = '\b';
4269 break;
4270
4271 case 'f':
4272 ch2 = '\f';
4273 break;
4274
4275 case 'a':
4276 ch2 = '\a';
4277 break;
4278
4279 case 'v':
4280 ch2 = '\v';
4281 break;
4282
4283 case 'x':
4284 ch2 = this.NextChar();
4285 if (ch2 >= '0' && ch2 <= '9')
4286 ch2 -= '0';
4287 else if (ch2 >= 'a' && ch2 <= 'f')
4288 ch2 -= (char)('a' - 10);
4289 else if (ch2 >= 'A' && ch2 <= 'F')
4290 ch2 -= (char)('A' - 10);
4291 else
4292 throw new SyntaxException("Hexadecimal digit expected.", this.pos, this.script);
4293
4294 char ch3 = this.NextChar();
4295 if (ch3 >= '0' && ch3 <= '9')
4296 ch3 -= '0';
4297 else if (ch3 >= 'a' && ch3 <= 'f')
4298 ch3 -= (char)('a' - 10);
4299 else if (ch3 >= 'A' && ch3 <= 'F')
4300 ch3 -= (char)('A' - 10);
4301 else
4302 throw new SyntaxException("Hexadecimal digit expected.", this.pos, this.script);
4303
4304 ch2 <<= 4;
4305 ch2 += ch3;
4306 break;
4307 }
4308 }
4309
4310 sb.Append(ch2);
4311 }
4312
4313 return new ConstantElement(new StringValue(sb.ToString()), Start, this.pos - Start, this);
4314 }
4315 else if (char.IsLetter(ch) || ch == '_')
4316 {
4317 this.pos++;
4318
4319 if (ch == '_')
4320 {
4321 while ((ch = this.PeekNextChar()) == '_')
4322 this.pos++;
4323
4324 if (!char.IsLetter(ch))
4325 throw new SyntaxException("Expected a letter.", this.pos, this.script);
4326 }
4327
4328 while (char.IsLetter((ch = this.PeekNextChar())) || char.IsDigit(ch) || ch == '_')
4329 this.pos++;
4330
4331 string s = this.script.Substring(Start, this.pos - Start);
4332
4333 switch (s.ToUpper())
4334 {
4335 case "TRUE":
4336 return new ConstantElement(BooleanValue.True, Start, this.pos - Start, this);
4337
4338 case "FALSE":
4339 return new ConstantElement(BooleanValue.False, Start, this.pos - Start, this);
4340
4341 case "NULL":
4342 return new ConstantElement(ObjectValue.Null, Start, this.pos - Start, this);
4343
4344 default:
4345 Node = this.ParseCustomNode(s, false, Start);
4346 if (Node is null)
4347 return new VariableReference(s, Start, this.pos - Start, this);
4348 else
4349 return Node;
4350 }
4351 }
4352 else
4353 {
4354 switch (ch)
4355 {
4356 case '∅':
4357 case '∞':
4358 this.pos++;
4359 return new VariableReference(new string(ch, 1), Start, this.pos - Start, this);
4360
4361 case '⊤':
4362 this.pos++;
4363 return new ConstantElement(BooleanValue.True, Start, this.pos - Start, this);
4364
4365 case '⊥':
4366 this.pos++;
4367 return new ConstantElement(BooleanValue.False, Start, this.pos - Start, this);
4368 }
4369
4370 return this.ParseCustomNode(new string(ch, 1), true, Start);
4371 }
4372 }
4373
4374 private ScriptNode ParseCustomNode(string KeyWord, bool IncPosIfKeyword, int Start)
4375 {
4376 if (customKeyWords is null)
4377 Search();
4378
4379 if (customKeyWords.TryGetValue(KeyWord, out IKeyWord KeyWordParser))
4380 {
4381 ScriptParser Parser = new ScriptParser(this, Start);
4382 int PosBak = this.pos;
4383
4384 if (IncPosIfKeyword)
4385 this.pos += KeyWord.Length;
4386
4387 bool CanParseWhitespace = this.canSkipWhitespace;
4388 bool Result = KeyWordParser.TryParse(Parser, out ScriptNode Node);
4389
4390 this.canSkipWhitespace = CanParseWhitespace;
4391
4392 if (Result)
4393 return Node;
4394 else
4395 this.pos = PosBak;
4396 }
4397
4398 return null;
4399 }
4400
4401 private static bool IsVectorDefinition(ScriptNode Node)
4402 {
4403 return Node is VectorDefinition ||
4404 Node is VectorForDefinition ||
4405 Node is VectorForEachDefinition ||
4406 Node is VectorDoWhileDefinition ||
4408 }
4409
4414 public bool IsAsynchronous => this.root?.IsAsynchronous ?? false;
4415
4422 [Obsolete("Use the EvaluateAsync method for more efficient processing of script containing asynchronous processing elements in parallel environments.")]
4424 {
4425 IElement Result;
4426
4427 try
4428 {
4429 if (this.root is null)
4430 Result = ObjectValue.Null;
4431 else if (this.root.IsAsynchronous)
4432 Result = this.root.EvaluateAsync(Variables).Result;
4433 else
4434 Result = this.root.Evaluate(Variables);
4435 }
4437 {
4438 Result = ex.ReturnValue;
4439 //ScriptReturnValueException.Reuse(ex);
4440 }
4441 catch (ScriptBreakLoopException ex)
4442 {
4443 Result = ex.LoopValue ?? ObjectValue.Null;
4444 //ScriptBreakLoopException.Reuse(ex);
4445 }
4447 {
4448 Result = ex.LoopValue ?? ObjectValue.Null;
4449 //ScriptContinueLoopException.Reuse(ex);
4450 }
4451
4452 return Result.AssociatedObjectValue;
4453 }
4454
4461 public async Task<object> EvaluateAsync(Variables Variables)
4462 {
4463 IElement Result;
4464
4465 try
4466 {
4467 if (this.root is null)
4468 Result = ObjectValue.Null;
4469 else if (this.root.IsAsynchronous)
4470 Result = await this.root.EvaluateAsync(Variables);
4471 else
4472 Result = this.root.Evaluate(Variables);
4473 }
4475 {
4476 Result = ex.ReturnValue;
4477 //ScriptReturnValueException.Reuse(ex);
4478 }
4479 catch (ScriptBreakLoopException ex)
4480 {
4481 Result = ex.LoopValue ?? ObjectValue.Null;
4482 //ScriptBreakLoopException.Reuse(ex);
4483 }
4485 {
4486 Result = ex.LoopValue ?? ObjectValue.Null;
4487 //ScriptContinueLoopException.Reuse(ex);
4488 }
4489
4490 return Result.AssociatedObjectValue;
4491 }
4492
4496 public ScriptNode Root => this.root;
4497
4499 public override bool Equals(object obj)
4500 {
4501 if (obj is Expression Exp)
4502 return this.script.Equals(Exp.script);
4503 else
4504 return false;
4505 }
4506
4508 public override int GetHashCode()
4509 {
4510 return this.script.GetHashCode();
4511 }
4512
4516 public bool ContainsImplicitPrint => this.containsImplicitPrint;
4517
4524 {
4525 if (this.ContainsImplicitPrint)
4526 return true;
4527
4528 Dictionary<string, bool> Processed = null;
4529 bool CheckFunctionCalls(ScriptNode Node, out ScriptNode NewNode, object State)
4530 {
4531 NewNode = null;
4532
4533 if (Node is NamedFunctionCall f)
4534 {
4535 Expression Exp;
4536
4537 if (Variables.TryGetVariable(f.FunctionName + " " + f.Arguments.Length.ToString(), out Variable v) &&
4538 v.ValueObject is ScriptNode N)
4539 {
4540 Exp = N.Expression;
4541 }
4542 else if (Variables.TryGetVariable(f.FunctionName, out v) &&
4543 v.ValueObject is ScriptNode N2)
4544 {
4545 Exp = N2.Expression;
4546 }
4547 else
4548 return true;
4549
4550 if (Processed is null)
4551 Processed = new Dictionary<string, bool>() { { this.script, true } };
4552
4553 if (Processed.ContainsKey(Exp.script))
4554 return true;
4555
4556 Processed[Exp.script] = true;
4557
4558 if (Exp.ContainsImplicitPrint || !Exp.ForAll(CheckFunctionCalls, null, SearchMethod.TreeOrder))
4559 return false;
4560 }
4561
4562 return true;
4563 }
4564 ;
4565
4566 if (!this.ForAll(CheckFunctionCalls, null, SearchMethod.TreeOrder))
4567 return true;
4568
4569 return false;
4570 }
4571
4580 [Obsolete("Use the TransformAsync method for more efficient processing of script containing asynchronous processing elements in parallel environments.")]
4581 public static string Transform(string s, string StartDelimiter, string StopDelimiter, Variables Variables)
4582 {
4583 return Transform(s, StartDelimiter, StopDelimiter, Variables, null);
4584 }
4585
4595 [Obsolete("Use the TransformAsync method for more efficient processing of script containing asynchronous processing elements in parallel environments.")]
4596 public static string Transform(string s, string StartDelimiter, string StopDelimiter, Variables Variables, string Source)
4597 {
4598 int i = s.IndexOf(StartDelimiter);
4599 if (i < 0)
4600 return s;
4601
4602 StringBuilder Transformed = new StringBuilder();
4603 Expression Exp;
4604 string Script;
4605 object Result;
4606 int j;
4607 int StartLen = StartDelimiter.Length;
4608 int StopLen = StopDelimiter.Length;
4609 int From = 0;
4610
4611 while (i >= 0)
4612 {
4613 j = s.IndexOf(StopDelimiter, i + StartLen);
4614 if (j < 0)
4615 {
4616 if (From == 0)
4617 return s;
4618 else
4619 break;
4620 }
4621
4622 if (i > From)
4623 Transformed.Append(s.Substring(From, i - From));
4624
4625 From = j + StopLen;
4626
4627 Script = s.Substring(i + StartLen, j - i - StartLen);
4628
4629 Exp = new Expression(Script, Source);
4630 Result = Exp.Evaluate(Variables);
4631
4632 if (!IsNullOrVoid(Result))
4633 Transformed.Append(Result.ToString());
4634
4635 i = s.IndexOf(StartDelimiter, From);
4636 }
4637
4638 if (From < s.Length)
4639 Transformed.Append(s.Substring(From));
4640
4641 return Transformed.ToString();
4642 }
4643
4652 public static Task<string> TransformAsync(string s, string StartDelimiter, string StopDelimiter, Variables Variables)
4653 {
4654 return TransformAsync(s, StartDelimiter, StopDelimiter, Variables, null);
4655 }
4656
4666 public static async Task<string> TransformAsync(string s, string StartDelimiter, string StopDelimiter, Variables Variables, string Source)
4667 {
4668 int i = s.IndexOf(StartDelimiter);
4669 if (i < 0)
4670 return s;
4671
4672 StringBuilder Transformed = new StringBuilder();
4673 ValuePrinter Printer = Variables.Printer;
4674 Expression Exp;
4675 string Script;
4676 object Result;
4677 int j;
4678 int StartLen = StartDelimiter.Length;
4679 int StopLen = StopDelimiter.Length;
4680 int From = 0;
4681
4682 while (i >= 0)
4683 {
4684 j = s.IndexOf(StopDelimiter, i + StartLen);
4685 if (j < 0)
4686 {
4687 if (From == 0)
4688 return s;
4689 else
4690 break;
4691 }
4692
4693 if (i > From)
4694 Transformed.Append(s.Substring(From, i - From));
4695
4696 From = j + StopLen;
4697
4698 Script = s.Substring(i + StartLen, j - i - StartLen);
4699
4700 Exp = new Expression(Script, Source);
4701 Result = await Exp.EvaluateAsync(Variables);
4702
4703 if (!IsNullOrVoid(Result))
4704 Transformed.Append(Printer is null ? Result.ToString() : await Printer(Result, Variables));
4705
4706 i = s.IndexOf(StartDelimiter, From);
4707 }
4708
4709 if (From < s.Length)
4710 Transformed.Append(s.Substring(From));
4711
4712 return Transformed.ToString();
4713 }
4714
4721 public static bool IsNullOrVoid(object Result)
4722 {
4723 if (Result is null)
4724 return true;
4725 else
4726 return IsVoid(Result.GetType());
4727 }
4728
4735 public static bool IsVoid(Type ResultType)
4736 {
4737 if (ResultType == typeof(void))
4738 return true;
4739 else if (VoidTaskResultType is null)
4740 {
4741 if (ResultType.FullName == "System.Threading.Tasks.VoidTaskResult")
4742 {
4743 VoidTaskResultType = ResultType;
4744 return true;
4745 }
4746 else
4747 return false;
4748 }
4749 else
4750 return ResultType == VoidTaskResultType;
4751 }
4752
4753 private static Type VoidTaskResultType = null;
4754
4760 public static string ToString(double Value)
4761 {
4762 return Value.ToString(CultureInfo.InvariantCulture);
4763 }
4764
4770 public static string ToString(decimal Value)
4771 {
4772 return Value.ToString(CultureInfo.InvariantCulture);
4773 }
4774
4781 public static bool TryParse(string s, out double Value)
4782 {
4783 return double.TryParse(s.Replace(".", NumberFormatInfo.CurrentInfo.NumberDecimalSeparator), out Value);
4784 }
4785
4792 public static bool TryParse(string s, out float Value)
4793 {
4794 return float.TryParse(s.Replace(".", NumberFormatInfo.CurrentInfo.NumberDecimalSeparator), out Value);
4795 }
4796
4803 public static bool TryParse(string s, out decimal Value)
4804 {
4805 return decimal.TryParse(s.Replace(".", NumberFormatInfo.CurrentInfo.NumberDecimalSeparator), out Value);
4806 }
4807
4813 public static string ToString(Complex Value)
4814 {
4815 return "(" + ToString(Value.Real) + ", " + ToString(Value.Imaginary) + ")";
4816 }
4817
4823 public static string ToString(BigInteger Value)
4824 {
4825 return "#" + Value.ToString();
4826 }
4827
4833 public static string ToString(bool Value)
4834 {
4835 return Value ? "⊤" : "⊥";
4836 }
4837
4843 public static string ToString(double[] Value)
4844 {
4845 StringBuilder sb = null;
4846
4847 foreach (double d in Value)
4848 {
4849 if (sb is null)
4850 sb = new StringBuilder("[");
4851 else
4852 sb.Append(", ");
4853
4854 sb.Append(ToString(d));
4855 }
4856
4857 if (sb is null)
4858 return "[]";
4859 else
4860 {
4861 sb.Append(']');
4862 return sb.ToString();
4863 }
4864 }
4865
4871 public static string ToString(Complex[] Value)
4872 {
4873 StringBuilder sb = null;
4874
4875 foreach (Complex z in Value)
4876 {
4877 if (sb is null)
4878 sb = new StringBuilder("[");
4879 else
4880 sb.Append(", ");
4881
4882 sb.Append(Expression.ToString(z));
4883 }
4884
4885 if (sb is null)
4886 return "[]";
4887 else
4888 {
4889 sb.Append(']');
4890 return sb.ToString();
4891 }
4892 }
4893
4899 public static string ToString(DateTime Value)
4900 {
4901 StringBuilder Output = new StringBuilder();
4902
4903 Output.Append("DateTime");
4904
4905 if (Value.Kind == DateTimeKind.Utc)
4906 Output.Append("Utc");
4907
4908 Output.Append('(');
4909 Output.Append(Value.Year.ToString("D4"));
4910 Output.Append(',');
4911 Output.Append(Value.Month.ToString("D2"));
4912 Output.Append(',');
4913 Output.Append(Value.Day.ToString("D2"));
4914
4915 if (Value.Hour != 0 || Value.Minute != 0 || Value.Second != 0 || Value.Millisecond != 0)
4916 {
4917 Output.Append(',');
4918 Output.Append(Value.Hour.ToString("D2"));
4919 Output.Append(',');
4920 Output.Append(Value.Minute.ToString("D2"));
4921 Output.Append(',');
4922 Output.Append(Value.Second.ToString("D2"));
4923
4924 if (Value.Millisecond != 0)
4925 {
4926 Output.Append(',');
4927 Output.Append(Value.Millisecond.ToString("D3"));
4928 }
4929 }
4930
4931 Output.Append(')');
4932
4933 return Output.ToString();
4934 }
4935
4941 public static string ToString(TimeSpan Value)
4942 {
4943 StringBuilder Output = new StringBuilder();
4944
4945 Output.Append("TimeSpan(");
4946 Output.Append(Value.Days.ToString());
4947 Output.Append(',');
4948 Output.Append(Value.Hours.ToString("D2"));
4949 Output.Append(',');
4950 Output.Append(Value.Minutes.ToString("D2"));
4951 Output.Append(',');
4952 Output.Append(Value.Seconds.ToString("D2"));
4953
4954 if (Value.Milliseconds != 0)
4955 {
4956 Output.Append(',');
4957 Output.Append(Value.Milliseconds.ToString("D3"));
4958 }
4959
4960 Output.Append(')');
4961
4962 return Output.ToString();
4963 }
4964
4970 public static string ToString(Enum Value)
4971 {
4972 StringBuilder Output = new StringBuilder();
4973
4974 Output.Append(Value.GetType().FullName);
4975 Output.Append('.');
4976 Output.Append(Value.ToString());
4977
4978 return Output.ToString();
4979 }
4980
4986 public static string EncodeString(string s)
4987 {
4988 if (s is null)
4989 return "null";
4990
4991 StringBuilder sb = new StringBuilder();
4992 int i = s.IndexOfAny(stringCharactersToEscape);
4993 int j = 0;
4994 int k;
4995
4996 sb.Append('"');
4997
4998 if (i < 0)
4999 sb.Append(s);
5000 else
5001 {
5002 while (i >= 0)
5003 {
5004 if (i > j)
5005 sb.Append(s.Substring(j, i - j));
5006
5007 k = Array.IndexOf(stringCharactersToEscape, s[i]);
5008 sb.Append(stringEscapeSequences[k]);
5009 j = i + 1;
5010 i = s.IndexOfAny(stringCharactersToEscape, j);
5011 }
5012
5013 if (j < s.Length)
5014 sb.Append(s.Substring(j));
5015 }
5016
5017 sb.Append('"');
5018
5019 return sb.ToString();
5020 }
5021
5022 private static readonly char[] stringCharactersToEscape = new char[] { '\\', '"', '\n', '\r', '\t', '\b', '\f', '\a', '\v' };
5023 private static readonly string[] stringEscapeSequences = new string[] { "\\\\", "\\\"", "\\n", "\\r", "\\t", "\\b", "\\f", "\\a", "\\v" };
5024
5030 [Obsolete("Use the ToExpressionString method instead.")]
5031 public static string ToString(object Value)
5032 {
5033 return ToExpressionString(Value);
5034 }
5035
5041 [Obsolete("Use the ToExpressionString method instead.")]
5042 public static string ToString(string Value)
5043 {
5044 return ToExpressionString(Value);
5045 }
5046
5052 public static string ToExpressionString(object Value)
5053 {
5054 if (Value is null)
5055 return "null";
5056 else
5057 {
5058 Type T = Value.GetType();
5059 bool Found;
5061
5062 lock (output)
5063 {
5064 Found = output.TryGetValue(T, out StringOutput);
5065 }
5066
5067 if (!Found)
5068 {
5069 StringOutput = Types.FindBest<ICustomStringOutput, Type>(T);
5070
5071 lock (output)
5072 {
5073 output[T] = StringOutput;
5074 }
5075 }
5076
5077 if (!(StringOutput is null))
5078 return StringOutput.GetString(Value);
5079 else if (Value is IEnumerable Enumerable)
5080 {
5081 StringBuilder sb = new StringBuilder();
5082 bool First = true;
5083
5084 sb.Append('[');
5085
5086 foreach (object Element in Enumerable)
5087 {
5088 if (First)
5089 First = false;
5090 else
5091 sb.Append(',');
5092
5093 sb.Append(ToExpressionString(Element));
5094 }
5095
5096 sb.Append(']');
5097
5098 return sb.ToString();
5099 }
5100 else
5101 return Value.ToString();
5102 }
5103 }
5104
5110 public static double ToDouble(object Object)
5111 {
5112 if (Object is double db)
5113 return db;
5114 else if (Object is int i)
5115 return i;
5116 else if (Object is bool b)
5117 return b ? 1 : 0;
5118 else if (Object is byte bt)
5119 return bt;
5120 else if (Object is char ch)
5121 return ch;
5122 else if (Object is decimal dc)
5123 return (double)dc;
5124 else if (Object is short sh)
5125 return sh;
5126 else if (Object is long l)
5127 return l;
5128 else if (Object is sbyte sb)
5129 return sb;
5130 else if (Object is float f)
5131 return f;
5132 else if (Object is ushort us)
5133 return us;
5134 else if (Object is uint ui)
5135 return ui;
5136 else if (Object is ulong ul)
5137 return ul;
5138 else if (Object is BigInteger i2)
5139 return (double)i2;
5140 else if (Object is Complex z)
5141 {
5142 if (z.Imaginary == 0)
5143 return z.Real;
5144 else
5145 throw new ScriptException("Expected a double value.");
5146 }
5147 else
5148 {
5149 string s = Object.ToString();
5150
5151 if (double.TryParse(s, out double d))
5152 return d;
5153
5154 if (NumberFormatInfo.CurrentInfo.NumberDecimalSeparator != "." &&
5155 double.TryParse(s.Replace(".", NumberFormatInfo.CurrentInfo.NumberDecimalSeparator), out d))
5156 {
5157 return d;
5158 }
5159
5160 throw new ScriptException("Expected a double value.");
5161 }
5162 }
5163
5169 public static decimal ToDecimal(object Object)
5170 {
5171 if (Object is double db)
5172 return (decimal)db;
5173 else if (Object is int i)
5174 return i;
5175 else if (Object is bool b)
5176 return b ? 1 : 0;
5177 else if (Object is byte bt)
5178 return bt;
5179 else if (Object is char ch)
5180 return ch;
5181 else if (Object is decimal dc)
5182 return dc;
5183 else if (Object is short sh)
5184 return sh;
5185 else if (Object is long l)
5186 return l;
5187 else if (Object is sbyte sb)
5188 return sb;
5189 else if (Object is float f)
5190 return (decimal)f;
5191 else if (Object is ushort us)
5192 return us;
5193 else if (Object is uint ui)
5194 return ui;
5195 else if (Object is ulong ul)
5196 return ul;
5197 else if (Object is BigInteger i2)
5198 return (decimal)i2;
5199 else if (Object is Complex z)
5200 {
5201 if (z.Imaginary == 0)
5202 return (decimal)z.Real;
5203 else
5204 throw new ScriptException("Expected a double value.");
5205 }
5206 else
5207 {
5208 string s = Object.ToString();
5209
5210 if (decimal.TryParse(s, out decimal d))
5211 return d;
5212
5213 if (NumberFormatInfo.CurrentInfo.NumberDecimalSeparator != "." &&
5214 decimal.TryParse(s.Replace(".", NumberFormatInfo.CurrentInfo.NumberDecimalSeparator), out d))
5215 {
5216 return d;
5217 }
5218
5219 throw new ScriptException("Expected a decimal value.");
5220 }
5221 }
5222
5228 public static Complex ToComplex(object Object)
5229 {
5230 if (Object is Complex z)
5231 return z;
5232 else
5233 return new Complex(ToDouble(Object), 0);
5234 }
5235
5241 public static IElement Encapsulate(object Value)
5242 {
5243 if (Value is null)
5244 return ObjectValue.Null;
5245 else if (Value is double db)
5246 return new DoubleNumber(db);
5247 else if (Value is bool b)
5248 return new BooleanValue(b);
5249 else if (Value is string s)
5250 return new StringValue(s);
5251 else if (Value is int i)
5252 return new DoubleNumber(i);
5253 else if (Value is long l)
5254 return new DoubleNumber(l);
5255 else if (Value is byte bt)
5256 return new DoubleNumber(bt);
5257 else if (Value is char ch)
5258 return new StringValue(new string(ch, 1));
5259 else if (Value is DateTime DT)
5260 return new DateTimeValue(DT);
5261 else if (Value is decimal dc)
5262 return new DoubleNumber((double)dc);
5263 else if (Value is short sh)
5264 return new DoubleNumber(sh);
5265 else if (Value is sbyte sb)
5266 return new DoubleNumber(sb);
5267 else if (Value is float f)
5268 return new DoubleNumber(f);
5269 else if (Value is ushort us)
5270 return new DoubleNumber(us);
5271 else if (Value is uint ui)
5272 return new DoubleNumber(ui);
5273 else if (Value is ulong ul)
5274 return new DoubleNumber(ul);
5275 else if (Value is Complex c)
5276 return new ComplexNumber(c);
5277 else if (Value is BigInteger i2)
5278 return new Integer(i2);
5279 else if (Value is Type t)
5280 return new TypeValue(t);
5281 else
5282 {
5283 if (Value is IElement e)
5284 return e;
5285
5286 else if (Value is double[] dv)
5287 return new DoubleVector(dv);
5288 else if (Value is double[,] dm)
5289 return new DoubleMatrix(dm);
5290
5291 else if (Value is Complex[] cv)
5292 return new ComplexVector(cv);
5293 else if (Value is Complex[,] cm)
5294 return new ComplexMatrix(cm);
5295
5296 else if (Value is bool[] bv)
5297 return new BooleanVector(bv);
5298 else if (Value is bool[,] bm)
5299 return new BooleanMatrix(bm);
5300
5301 else if (Value is DateTime[] dv2)
5302 return new DateTimeVector(dv2);
5303
5304 else if (Value is IElement[] ev)
5305 return new ObjectVector((ICollection<IElement>)ev);
5306 else if (Value is IElement[,] em)
5307 return new ObjectMatrix(em);
5308 else if (Value is object[] ov)
5309 return new ObjectVector(ov);
5310 else if (Value is object[,] om)
5311 return new ObjectMatrix(om);
5312
5313 else
5314 return new ObjectValue(Value);
5315 }
5316 }
5317
5327 public static bool UpgradeField(ref IElement E1, ref ISet Set1,
5328 ref IElement E2, ref ISet Set2)
5329 {
5330 object O1 = E1?.AssociatedObjectValue;
5331 object O2 = E2?.AssociatedObjectValue;
5332 Type T1 = O1?.GetType() ?? typeof(object);
5333 Type T2 = O2?.GetType() ?? typeof(object);
5334
5335 if (T1 == T2)
5336 return true;
5337
5338 if (TryConvert(E1, T2, false, out IElement E1asT2))
5339 {
5340 E1 = E1asT2;
5341 Set1 = E1asT2.AssociatedSet;
5342 return true;
5343 }
5344
5345 if (TryConvert(E2, T1, false, out IElement E2asT1))
5346 {
5347 E2 = E2asT1;
5348 Set2 = E2asT1.AssociatedSet;
5349 return true;
5350 }
5351
5352 // TODO: Update to common extension field
5353
5354 if (O1 is Enum Enum1 && O2 is double)
5355 {
5356 T1 = Enum.GetUnderlyingType(Enum1.GetType());
5357 if (T1 == typeof(int))
5358 {
5359 E1 = new DoubleNumber(Convert.ToInt32(Enum1));
5360 Set1 = DoubleNumbers.Instance;
5361 return true;
5362 }
5363 }
5364 else if (O2 is Enum Enum2 && O1 is double)
5365 {
5366 T2 = Enum.GetUnderlyingType(Enum2.GetType());
5367 if (T2 == typeof(int))
5368 {
5369 E2 = new DoubleNumber(Convert.ToInt32(Enum2));
5370 Set2 = DoubleNumbers.Instance;
5371 return true;
5372 }
5373 }
5374
5375 return false;
5376 }
5377
5385 public static object ConvertTo(IElement Value, Type DesiredType, ScriptNode Node)
5386 {
5387 return ConvertTo(Value.AssociatedObjectValue, DesiredType, Node);
5388 }
5389
5397 public static object ConvertTo(object Obj, Type DesiredType, ScriptNode Node)
5398 {
5399 if (Obj is null)
5400 return null;
5401
5402 if (TryConvert(Obj, DesiredType, true, out object Result))
5403 return Result;
5404
5405 Type T = Obj.GetType();
5406 if (T == DesiredType)
5407 return Obj;
5408
5409 if (DesiredType.IsArray)
5410 {
5411 Type DesiredItemType = DesiredType.GetElementType();
5412 Array Dest;
5413
5414 if (T.IsArray)
5415 {
5416 Array Source = (Array)Obj;
5417 int c = Source.Length;
5418 int i;
5419
5420 Dest = (Array)Activator.CreateInstance(DesiredType, c);
5421
5422 for (i = 0; i < c; i++)
5423 Dest.SetValue(ConvertTo(Source.GetValue(i), DesiredItemType, Node), i);
5424 }
5425 else
5426 {
5427 Dest = (Array)Activator.CreateInstance(DesiredType, 1);
5428 Dest.SetValue(ConvertTo(Obj, DesiredItemType, Node), 0);
5429 }
5430
5431 return Dest;
5432 }
5433 else if (DesiredType.IsEnum && Obj is string s)
5434 return Enum.Parse(DesiredType, s);
5435
5436 return Convert.ChangeType(Obj, DesiredType);
5437 }
5438
5442 public object Tag
5443 {
5444 get => this.tag;
5445 set => this.tag = value;
5446 }
5447
5455 [Obsolete("Use ForAll(ScriptNodeEventHandler, object, SearchMethod) instead.")]
5456 public bool ForAll(ScriptNodeEventHandler Callback, object State, bool DepthFirst)
5457 {
5458 return this.ForAll(Callback, State, DepthFirst ? SearchMethod.DepthFirst : SearchMethod.BreadthFirst);
5459 }
5460
5468 public bool ForAll(ScriptNodeEventHandler Callback, object State, SearchMethod Order)
5469 {
5470 if (Order == SearchMethod.DepthFirst)
5471 {
5472 if (!(this.root?.ForAllChildNodes(Callback, State, Order) ?? true))
5473 return false;
5474 }
5475
5476 if (!(this.root is null))
5477 {
5478 if (!Callback(this.root, out ScriptNode NewRoot, State))
5479 return false;
5480
5481 if (!(NewRoot is null))
5482 this.root = NewRoot;
5483 }
5484
5485 if (Order != SearchMethod.DepthFirst)
5486 {
5487 if (!(this.root?.ForAllChildNodes(Callback, State, Order) ?? true))
5488 return false;
5489 }
5490
5491 return true;
5492 }
5493
5501 public static bool TryConvert<T>(object Value, out T Result)
5502 {
5503 if (TryConvert(Value, typeof(T), true, out object Obj))
5504 {
5505 if (Obj is T Result2)
5506 {
5507 Result = Result2;
5508 return true;
5509 }
5510 else if (Value is null && !typeof(T).IsValueType)
5511 {
5512 Result = default;
5513 return true;
5514 }
5515 }
5516
5517 Result = default;
5518 return false;
5519 }
5520
5530 public static bool TryConvert(object Value, Type DesiredType,
5531 bool AcceptInformationLoss, out object Result)
5532 {
5533 return TryConvert(Value, DesiredType, AcceptInformationLoss ? 0 : 1, out Result);
5534 }
5535
5545 public static bool TryConvert(object Value, Type DesiredType,
5546 double WeightThreshold, out object Result)
5547 {
5548 if (Value is null)
5549 {
5550 Result = null;
5551 return !DesiredType.IsValueType;
5552 }
5553
5554 Type T = Value.GetType();
5555 TypeInfo TI = T.GetTypeInfo();
5556
5557 if (DesiredType.IsAssignableFrom(TI))
5558 {
5559 Result = Value;
5560 return true;
5561 }
5562
5563 if (TryGetTypeConverter(T, DesiredType, out ITypeConverter Converter) &&
5564 Converter.Weight >= WeightThreshold &&
5565 Converter.TryConvert(Value, out Result))
5566 {
5567 return true;
5568 }
5569
5570 if (DesiredType.IsEnum)
5571 {
5572 switch (Type.GetTypeCode(Value.GetType()))
5573 {
5574 case TypeCode.Empty:
5575 case TypeCode.DBNull:
5576 case TypeCode.Boolean:
5577 case TypeCode.DateTime:
5578 Result = null;
5579 return false;
5580
5581 case TypeCode.String:
5582 case TypeCode.Char:
5583 string s = Value.ToString();
5584 string[] Names = Enum.GetNames(DesiredType);
5585 int i = Array.IndexOf(Names, s);
5586
5587 if (i < 0)
5588 {
5589 Result = null;
5590 return false;
5591 }
5592 else
5593 {
5594 Array Values = Enum.GetValues(DesiredType);
5595 Result = Values.GetValue(i);
5596 return true;
5597 }
5598
5599 case TypeCode.Object:
5600 if (TryConvert(Value, typeof(string), WeightThreshold, out object Obj) &&
5601 Obj is string s2)
5602 {
5603 s = s2;
5604 }
5605 else
5606 s = Value.ToString();
5607
5608 Names = Enum.GetNames(DesiredType);
5609 i = Array.IndexOf(Names, s);
5610
5611 if (i < 0)
5612 {
5613 Result = null;
5614 return false;
5615 }
5616 else
5617 {
5618 Array Values = Enum.GetValues(DesiredType);
5619 Result = Values.GetValue(i);
5620 return true;
5621 }
5622
5623 case TypeCode.SByte:
5624 Result = Enum.ToObject(DesiredType, (sbyte)Value);
5625 return true;
5626
5627 case TypeCode.Byte:
5628 Result = Enum.ToObject(DesiredType, (byte)Value);
5629 return true;
5630
5631 case TypeCode.Int16:
5632 Result = Enum.ToObject(DesiredType, (short)Value);
5633 return true;
5634
5635 case TypeCode.UInt16:
5636 Result = Enum.ToObject(DesiredType, (ushort)Value);
5637 return true;
5638
5639 case TypeCode.Int32:
5640 Result = Enum.ToObject(DesiredType, (int)Value);
5641 return true;
5642
5643 case TypeCode.UInt32:
5644 Result = Enum.ToObject(DesiredType, (uint)Value);
5645 return true;
5646
5647 case TypeCode.Int64:
5648 Result = Enum.ToObject(DesiredType, (long)Value);
5649 return true;
5650
5651 case TypeCode.UInt64:
5652 Result = Enum.ToObject(DesiredType, (ulong)Value);
5653 return true;
5654
5655 case TypeCode.Single:
5656 float f = (float)Value;
5657
5658 if (f >= int.MinValue && f <= int.MaxValue)
5659 Result = Enum.ToObject(DesiredType, (int)f);
5660 else if (f >= long.MinValue && f <= long.MaxValue)
5661 Result = Enum.ToObject(DesiredType, (long)f);
5662 else if (f >= ulong.MinValue && f <= ulong.MaxValue)
5663 Result = Enum.ToObject(DesiredType, (ulong)f);
5664 else
5665 {
5666 Result = null;
5667 return false;
5668 }
5669
5670 return true;
5671
5672 case TypeCode.Double:
5673 double d = (double)Value;
5674
5675 if (d >= int.MinValue && d <= int.MaxValue)
5676 Result = Enum.ToObject(DesiredType, (int)d);
5677 else if (d >= long.MinValue && d <= long.MaxValue)
5678 Result = Enum.ToObject(DesiredType, (long)d);
5679 else if (d >= ulong.MinValue && d <= ulong.MaxValue)
5680 Result = Enum.ToObject(DesiredType, (ulong)d);
5681 else
5682 {
5683 Result = null;
5684 return false;
5685 }
5686
5687 return true;
5688
5689 case TypeCode.Decimal:
5690 decimal dec = (decimal)Value;
5691
5692 if (dec >= int.MinValue && dec <= int.MaxValue)
5693 Result = Enum.ToObject(DesiredType, (int)dec);
5694 else if (dec >= long.MinValue && dec <= long.MaxValue)
5695 Result = Enum.ToObject(DesiredType, (long)dec);
5696 else if (dec >= ulong.MinValue && dec <= ulong.MaxValue)
5697 Result = Enum.ToObject(DesiredType, (ulong)dec);
5698 else
5699 {
5700 Result = null;
5701 return false;
5702 }
5703
5704 return true;
5705 }
5706 }
5707
5708 Result = null;
5709 return false;
5710 }
5711
5722 public static bool TryConvert(IElement Value, Type DesiredType,
5723 bool AcceptInformationLoss, out IElement Result)
5724 {
5725 return TryConvert(Value, DesiredType, AcceptInformationLoss ? 0 : 1, out Result);
5726 }
5727
5738 public static bool TryConvert(IElement Value, Type DesiredType,
5739 double WeightThreshold, out IElement Result)
5740 {
5741 object Obj = Value?.AssociatedObjectValue;
5742 if (Obj is null)
5743 {
5744 Result = ObjectValue.Null;
5745 return !DesiredType.IsValueType;
5746 }
5747
5748 Type T = Obj.GetType();
5749
5750 if (TryGetTypeConverter(T, DesiredType, out ITypeConverter Converter) &&
5751 Converter.Weight >= WeightThreshold &&
5752 Converter.TryConvertToElement(Obj, out Result))
5753 {
5754 return true;
5755 }
5756 else
5757 {
5758 Result = null;
5759 return false;
5760 }
5761 }
5762
5771 public static bool TryGetTypeConverter(Type From, Type To, out ITypeConverter Converter)
5772 {
5773 if (converters is null)
5774 {
5775 Dictionary<Type, Dictionary<Type, ITypeConverter>> Converters = GetTypeConverters();
5776
5777 if (converters is null)
5778 {
5779 converters = Converters;
5780 Types.OnInvalidated += (Sender, e) => converters = GetTypeConverters();
5781 }
5782 }
5783
5784 lock (converters)
5785 {
5786 if (!converters.TryGetValue(From, out Dictionary<Type, ITypeConverter> Converters) &&
5787 (!From.IsEnum || !converters.TryGetValue(typeof(Enum), out Converters)))
5788 {
5789 Converter = null;
5790 return false;
5791 }
5792
5793 if (Converters.TryGetValue(To, out Converter))
5794 return !(Converter is null);
5795
5796 Dictionary<Type, double> Explored = new Dictionary<Type, double>() { { From, 1.0 } };
5798 ITypeConverter Best = null;
5799 double BestWeight = 0;
5800 double Weight;
5801
5802 foreach (ITypeConverter Converter3 in Converters.Values)
5803 {
5804 if (!(Converter3 is null))
5805 {
5806 Search.Add(Converter3);
5807 Explored[Converter3.To] = Converter3.Weight;
5808 }
5809 }
5810
5811 while (Search.HasFirstItem)
5812 {
5813 ITypeConverter C = Search.RemoveFirst();
5814
5815 if (converters.TryGetValue(C.To, out Dictionary<Type, ITypeConverter> Converters2) &&
5816 !(Converters2 is null))
5817 {
5818 if (Converters2.TryGetValue(To, out ITypeConverter Converter2) &&
5819 !(Converter2 is null))
5820 {
5822
5824 {
5825 int c = Sequence.Converters.Length + 1;
5826 ITypeConverter[] A = new ITypeConverter[c];
5827 Sequence.Converters.CopyTo(A, 0);
5828 A[c - 1] = Converter2;
5829
5831 }
5832 else
5833 ConversionSequence = new ConversionSequence(C, Converter2);
5834
5835 Weight = ConversionSequence.Weight;
5836
5837 if (!Converters.TryGetValue(To, out ITypeConverter Converter3) ||
5838 Weight > Converter3.Weight)
5839 {
5840 Converters[To] = ConversionSequence;
5841 }
5842
5843 if (Weight == 1)
5844 {
5845 Converter = ConversionSequence;
5846 return true;
5847 }
5848 else if (Weight > BestWeight)
5849 {
5850 Best = ConversionSequence;
5851 BestWeight = Weight;
5852 }
5853 }
5854
5855 foreach (ITypeConverter Converter3 in Converters2.Values)
5856 {
5857 if (!(Converter3 is null))
5858 {
5859 Weight = C.Weight * Converter3.Weight;
5860
5861 if (!Explored.TryGetValue(Converter3.To, out double Weight2) ||
5862 Weight > Weight2)
5863 {
5864 Search.Add(Converter3);
5865 Explored[Converter3.To] = Weight;
5866 }
5867 }
5868 }
5869 }
5870 }
5871
5872 Converters[To] = Best;
5873 Converter = Best;
5874 return !(Best is null);
5875 }
5876 }
5877
5878 private static Dictionary<Type, Dictionary<Type, ITypeConverter>> GetTypeConverters()
5879 {
5880 Dictionary<Type, Dictionary<Type, ITypeConverter>> Converters = new Dictionary<Type, Dictionary<Type, ITypeConverter>>();
5881
5882 foreach (Type T2 in Types.GetTypesImplementingInterface(typeof(ITypeConverter)))
5883 {
5884 ConstructorInfo DefaultConstructor = Types.GetDefaultConstructor(T2);
5885 if (DefaultConstructor is null)
5886 continue;
5887
5888 try
5889 {
5890 ITypeConverter Converter = (ITypeConverter)DefaultConstructor.Invoke(Types.NoParameters);
5891 Type From = Converter.From;
5892 Type To = Converter.To;
5893
5894 if (!Converters.TryGetValue(From, out Dictionary<Type, ITypeConverter> List))
5895 {
5896 List = new Dictionary<Type, ITypeConverter>();
5897 Converters[From] = List;
5898 }
5899
5900 if (List.TryGetValue(To, out ITypeConverter Converter2))
5901 {
5902 Log.Warning("There's already a type converter registered converting from " +
5903 From.FullName + " to " + To.FullName, Converter2.GetType().FullName);
5904 }
5905 else
5906 List[To] = Converter;
5907 }
5908 catch (Exception ex)
5909 {
5910 Log.Exception(ex, T2.FullName);
5911 }
5912 }
5913
5914 return Converters;
5915 }
5916
5922 [Obsolete("Use the EvalAsync method for more efficient processing of script containing asynchronous processing elements in parallel environments.")]
5923 public static object Eval(string Script)
5924 {
5925 return Eval(Script, new Variables());
5926 }
5927
5934 [Obsolete("Use the EvalAsync method for more efficient processing of script containing asynchronous processing elements in parallel environments.")]
5935 public static object Eval(string Script, Variables Variables)
5936 {
5937 Expression Exp = new Expression(Script);
5938 return Exp.Evaluate(Variables);
5939 }
5940
5946 public static Task<object> EvalAsync(string Script)
5947 {
5948 return EvalAsync(Script, new Variables());
5949 }
5950
5957 public static Task<object> EvalAsync(string Script, Variables Variables)
5958 {
5959 Expression Exp = new Expression(Script);
5960 return Exp.EvaluateAsync(Variables);
5961 }
5962
5963 // TODO: Optimize constants
5964 // TODO: Integers (0d, 0x, 0o, 0b), Big Integers (0D, 0X, 0O, 0B)
5965 // TODO: Matrix*Vector = Vector
5966 // TODO: Vector*Matrix = Vector
5967 // TODO: Matrix\Vector = Solutionvector.
5968 // TODO: Call member method.
5969 /*
5970 Covariance
5971 Correlation
5972
5973 Linear Algebra:
5974
5975 Determinant
5976 Columns
5977 Rows
5978 Diagonal
5979 Eliminate
5980 FlipLeftRight
5981 FlipUpDown
5982 IsDiagonal
5983 IsLowerTriangular
5984 IsNullMatrix
5985 IsUpperTriangular
5986 LookUp
5987 Rank
5988 Reduce
5989 Regression
5990 Slope
5991 Trace
5992
5993 Statistics
5994 Security
5995 Polynomials
5996 Probability
5997
5998 Strings:
5999
6000 EndsWith
6001 StartsWith
6002 Transform
6003 Last(s,n)
6004 First(s,n)
6005
6006 Vectors:
6007 Axis
6008 Count
6009 First
6010 IndexOf
6011 Join
6012 Last
6013 Norm
6014 Normalize
6015 Order
6016 Permutate
6017 Reverse
6018 Slice
6019 Sort
6020
6021 */
6022 }
6023}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
Definition: Log.cs:576
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:828
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
bool Contains(T Item)
Checks if an item is a member of the collection.
Definition: ChunkedList.cs:324
bool HasFirstItem
If there is a first item in the collection
Definition: ChunkedList.cs:778
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
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:572
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:85
static ConstructorInfo GetDefaultConstructor(Type Type)
Gets the default constructor of a type, if one exists.
Definition: Types.cs:1742
Base class for all types of elements.
Definition: Element.cs:14
Base class for script exceptions.
Class managing a script expression.
Definition: Expression.cs:41
static Complex ToComplex(object Object)
Converts an object to a complex value.
Definition: Expression.cs:5228
static bool TryGetTypeConverter(Type From, Type To, out ITypeConverter Converter)
Tries to get a type converter, converting objects from type From to objects of type To .
Definition: Expression.cs:5771
string Script
Original script string.
Definition: Expression.cs:207
static Task< object > EvalAsync(string Script)
Evaluates script, in string format.
Definition: Expression.cs:5946
ScriptNode Root
Root script node.
Definition: Expression.cs:4496
object Evaluate(Variables Variables)
Evaluates the expression, using the variables provided in the Variables collection....
Definition: Expression.cs:4423
static string ToString(decimal Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4770
async Task< object > EvaluateAsync(Variables Variables)
Evaluates the expression, using the variables provided in the Variables collection....
Definition: Expression.cs:4461
Expression(string Script, object Tag)
Class managing a script expression.
Definition: Expression.cs:84
static string ToString(Enum Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4970
Expression(string Script)
Class managing a script expression.
Definition: Expression.cs:63
static string ToString(object Value)
Converts an object to a string, that can be parsed as part of an expression.
Definition: Expression.cs:5031
static bool IsVoid(Type ResultType)
Checks if a result object type is equal to void (i.e. its type equal to System.Threading....
Definition: Expression.cs:4735
static bool TryConvert(object Value, Type DesiredType, bool AcceptInformationLoss, out object Result)
Tries to convert an object Value to an object of type DesiredType .
Definition: Expression.cs:5530
static bool TryGetConstant(string Name, Variables Variables, out IElement ValueElement)
Tries to get a constant value, given its name.
Definition: Expression.cs:3382
static string ToString(TimeSpan Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4941
static string ToString(Complex[] Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4871
bool ContainsImplicitPrint
If the expression contains implicit print operations.
Definition: Expression.cs:4516
static string ToString(BigInteger Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4823
static string Transform(string s, string StartDelimiter, string StopDelimiter, Variables Variables, string Source)
Transforms a string by executing embedded script.
Definition: Expression.cs:4596
static string Transform(string s, string StartDelimiter, string StopDelimiter, Variables Variables)
Transforms a string by executing embedded script.
Definition: Expression.cs:4581
static IElement Encapsulate(object Value)
Encapsulates an object.
Definition: Expression.cs:5241
static bool TryParse(string s, out decimal Value)
Tries to parse a decimal-precision floating-point value.
Definition: Expression.cs:4803
static bool UpgradeField(ref IElement E1, ref ISet Set1, ref IElement E2, ref ISet Set2)
Upgrades elements if necessary, to a common field extension, trying to make them compatible....
Definition: Expression.cs:5327
static bool TryParse(string s, out double Value)
Tries to parse a double-precision floating-point value.
Definition: Expression.cs:4781
static object ConvertTo(IElement Value, Type DesiredType, ScriptNode Node)
Tries to conevert an element value to a desired type.
Definition: Expression.cs:5385
Expression(string Script, string Source)
Class managing a script expression.
Definition: Expression.cs:73
static object ConvertTo(object Obj, Type DesiredType, ScriptNode Node)
Tries to conevert an object to a desired type.
Definition: Expression.cs:5397
static string EncodeString(string s)
Converts a string value to a parsable expression string.
Definition: Expression.cs:4986
static bool IsNullOrVoid(object Result)
Checks if a result object value is equal to null or void (i.e. its type equal to System....
Definition: Expression.cs:4721
static string ToString(DateTime Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4899
bool ForAll(ScriptNodeEventHandler Callback, object State, SearchMethod Order)
Calls the callback method for all script nodes defined for the expression.
Definition: Expression.cs:5468
static object Eval(string Script, Variables Variables)
Evaluates script, in string format.
Definition: Expression.cs:5935
static decimal ToDecimal(object Object)
Converts an object to a double value.
Definition: Expression.cs:5169
static Task< object > EvalAsync(string Script, Variables Variables)
Evaluates script, in string format.
Definition: Expression.cs:5957
static async Task< string > TransformAsync(string s, string StartDelimiter, string StopDelimiter, Variables Variables, string Source)
Transforms a string by executing embedded script.
Definition: Expression.cs:4666
static string ToString(bool Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4833
bool ReferencesImplicitPrint(Variables Variables)
If the expression, or any function call references, contain implicit print operations.
Definition: Expression.cs:4523
static string ToString(string Value)
Converts an object to a string, that can be parsed as part of an expression.
Definition: Expression.cs:5042
object Tag
This property allows the caller to tag the expression with an arbitrary object.
Definition: Expression.cs:5443
bool IsAsynchronous
If the node (or its decendants) include asynchronous evaluation. Asynchronous nodes should be evaluat...
Definition: Expression.cs:4414
static bool TryConvert(IElement Value, Type DesiredType, bool AcceptInformationLoss, out IElement Result)
Tries to convert an element Value to an element whose associated object is of type DesiredType .
Definition: Expression.cs:5722
string Source
Source of script, or null if not defined.
Definition: Expression.cs:212
static bool TryConvert(IElement Value, Type DesiredType, double WeightThreshold, out IElement Result)
Tries to convert an element Value to an element whose associated object is of type DesiredType .
Definition: Expression.cs:5738
static bool TryParse(string s, out float Value)
Tries to parse a single-precision floating-point value.
Definition: Expression.cs:4792
override bool Equals(object obj)
Definition: Expression.cs:4499
static double ToDouble(object Object)
Converts an object to a double value.
Definition: Expression.cs:5110
static bool TryConvert< T >(object Value, out T Result)
Tries to convert an object Value to an object of type T .
Definition: Expression.cs:5501
override int GetHashCode()
Definition: Expression.cs:4508
static string ToString(double Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4760
static string ToString(double[] Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4843
static bool TryConvert(object Value, Type DesiredType, double WeightThreshold, out object Result)
Tries to convert an object Value to an object of type DesiredType .
Definition: Expression.cs:5545
bool ForAll(ScriptNodeEventHandler Callback, object State, bool DepthFirst)
Calls the callback method for all script nodes defined for the expression.
Definition: Expression.cs:5456
static string ToExpressionString(object Value)
Converts an object to a string, that can be parsed as part of an expression.
Definition: Expression.cs:5052
Expression(string Script, string Source, object Tag)
Class managing a script expression.
Definition: Expression.cs:96
static string ToString(Complex Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4813
static Task< string > TransformAsync(string s, string StartDelimiter, string StopDelimiter, Variables Variables)
Transforms a string by executing embedded script.
Definition: Expression.cs:4652
static object Eval(string Script)
Evaluates script, in string format.
Definition: Expression.cs:5923
ScriptNode RightOperand
Right operand.
ScriptNode LeftOperand
Left operand.
Represents a constant element value.
IElement Constant
Constant value.
Base class for all funcions.
Definition: Function.cs:7
abstract string FunctionName
Name of the function
Definition: Function.cs:23
virtual bool ContextSpecific(Expression Expression)
If the function is specific to a given context, as apparent from the expression object....
Definition: Function.cs:62
abstract string[] DefaultArgumentNames
Default Argument names
Definition: Function.cs:36
virtual string[] Aliases
Optional aliases. If there are no aliases for the function, null is returned.
Definition: Function.cs:30
bool NullCheck
If null check is to be used.
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
virtual bool IsAsynchronous
If the node (or its decendants) include asynchronous evaluation. Asynchronous nodes should be evaluat...
Definition: ScriptNode.cs:142
abstract IElement Evaluate(Variables Variables)
Evaluates the node, using the variables provided in the Variables collection. This method should be ...
virtual Task< IElement > EvaluateAsync(Variables Variables)
Evaluates the node, using the variables provided in the Variables collection. This method should be ...
Definition: ScriptNode.cs:158
Script parser, for custom parsers.
Definition: ScriptParser.cs:10
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.
Pseudo-field of double numbers, as an approximation of the field of real numbers.
static readonly DoubleNumbers Instance
Instance of the set of complex numbers.
Integer-valued number.
Definition: Integer.cs:13
static readonly ObjectValue Null
Null value.
Definition: ObjectValue.cs:88
string Value
String value.
Definition: StringValue.cs:47
Degrees to radians operator.
Definition: DegToRad.cs:13
Represents a list of elements.
Definition: ElementList.cs:15
ScriptNode[] Elements
Elements.
Definition: ElementList.cs:59
Represents an implicit string to be printed.
Creates a matrix using a DO-WHILE statement.
Creates a matrix using a FOR statement.
Creates a matrix using a WHILE-DO statement.
Creates an object from nothing.
Represents a sequence of statements.
Definition: Sequence.cs:12
Sets a physical unit
Definition: SetUnit.cs:13
Defines a set, by implicitly limiting its members to members of an optional superset,...
Creates a set using a DO-WHILE statement.
Creates a set using a FOR statement.
Creates a set using a FOREACH statement.
Creates a set using a WHILE-DO statement.
Defines a vector, by implicitly limiting its members to members of an optional vector,...
Creates a vector using a DO-WHILE statement.
Creates a vector using a FOR statement.
Creates a vector using a WHILE-DO statement.
Converts values of type String to expression strings.
Definition: StringOutput.cs:10
string GetString(object Value)
Gets a string representing a value.
Definition: StringOutput.cs:23
Performs a sequence of type conversions to convert an object from one type to another.
double Weight
Weight of the converter. An estimate of how well the converter performs, or how much information is r...
Static class managing units.
Definition: Prefixes.cs:122
static bool TryParsePrefix(char ch, out Prefix Prefix)
Tries to parse a character into a prefix.
Definition: Prefixes.cs:373
A unit factor, used to form compound units.
Definition: UnitFactor.cs:7
int Exponent
Exponent of the unit factor.
Definition: UnitFactor.cs:58
AtomicUnit Unit
Unit factor, without its exponent.
Definition: UnitFactor.cs:53
Represents a unit.
Definition: Unit.cs:16
ICollection< UnitFactor > Factors
Sequence of atomic unit factors, and their corresponding exponents.
Definition: Unit.cs:427
Contains information about a variable.
Definition: Variable.cs:10
Collection of variables.
Definition: Variables.cs:25
ValuePrinter Printer
Delegate that converts values to strings for (implicit) printing. Default is null,...
Definition: Variables.cs:242
virtual bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
Definition: Variables.cs:56
Basic interface for all types of elements.
Definition: IElement.cs:21
object AssociatedObjectValue
Associated object value.
Definition: IElement.cs:34
Basic interface for all types of sets.
Definition: ISet.cs:10
Base interface for constants that integrate into the script engine.
Definition: IConstant.cs:10
string[] Aliases
Optional aliases. If there are no aliases for the constant, null is returned.
Definition: IConstant.cs:23
string ConstantName
Name of the constant
Definition: IConstant.cs:15
Base interface for functions that integrate into the script engine.
Definition: IFunction.cs:43
Interface for keywords with custom parsing.
Definition: IKeyWord.cs:9
string[] InternalKeywords
Any keywords used internally by the custom parser.
Definition: IKeyWord.cs:30
string[] Aliases
Keyword aliases, if available, null if none.
Definition: IKeyWord.cs:22
string KeyWord
Keyword associated with custom parser.
Definition: IKeyWord.cs:14
Interface for objects that can be represented as a physical quantity.
Interface for custom string output classes. Converts objects of a given type to an expression string.
Converts an object of one type to an object of another type.
Type To
Converter converts objects to this type.
double Weight
Weight of the converter. An estimate of how well the converter performs, or how much information is r...
Type From
Converter converts objects of this type.
class ContextSpecific(int Tag, Array Elements, byte[] SubSection)
A context-specific object (or set of objects).
delegate bool ScriptNodeEventHandler(ScriptNode Node, out ScriptNode NewNode, object State)
Delegate for ScriptNode callback methods.
ArgumentType
Type of parameter used in a function definition or a lambda definition.
Definition: IFunction.cs:9
SearchMethod
Method to traverse the expression structure
Definition: ScriptNode.cs:38
Prefix
SI prefixes. http://physics.nist.gov/cuu/Units/prefixes.html
Definition: Prefixes.cs:11
delegate Task< string > ValuePrinter(object Value, Variables Variables)
Converts a value to a printable string.