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