ScriptStack 1.0.4
Loading...
Searching...
No Matches
Interpreter.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Collections.ObjectModel;
4using System.Text;
5
8
10{
11
17 public class Interpreter
18 {
19
20 #region Private Classes
21
26 private class FunctionFrame
27 {
28 public Function function;
29 public Memory localMemory;
30 public int nextInstruction;
31 }
32
33 #endregion
34
35 #region Private Variables
36
37 private Function function;
38 private Script script;
39 private Executable executable;
40 private Stack<FunctionFrame> functionStack;
41 private Stack<object> parameterStack;
42 private Dictionary<object, Instruction> locks;
43 private List<Interpreter> jobs;
44 private Instruction instruction;
45 private Memory localMemory;
46 private bool interrupt;
47 private bool interrupted;
48 private bool finished;
49
50 #endregion
51
52 #region Private Methods
53
54 private Host host;
55
56 // \todo make models accessible like members
57 private object Evaluate(Operand operand)
58 {
59
60 object src = null;
61
62 switch (operand.Type)
63 {
64
65 case OperandType.Literal:
66 return operand.Value;
67
68 case OperandType.Variable:
69 return localMemory[(string) operand.Value];
70
71 /*
72 * Members are indexed with a '.' like
73 *
74 * var a = [];
75 * a.b = 1;
76 *
77 * currently only arrays can have members but strings can be numerically indexed
78 *
79 */
80 case OperandType.Member:
81
82 src = localMemory[(string)operand.Value];
83
84 if (src.GetType() == typeof(ArrayList))
85 {
86
87 /*
88 * An array has an internal member "toString"
89 * \deprecated
90 */
91 if ((string)operand.Member == "toString")
92 {
93
94 ArrayList array = (ArrayList)src;
95
96 StringBuilder sb = new StringBuilder();
97
98 foreach (KeyValuePair<object, object> element in array)
99 {
100 sb.Append(element.Value.ToString());
101 }
102
103 return sb;
104
105 }
106
107 ArrayList associativeArray = (ArrayList)src;
108
109 object objectValue = associativeArray[operand.Member];
110
111 return objectValue;
112 }
113
114 else if (src.GetType() == typeof(string))
115 {
116
117 string strSource = (string)src;
118
119 object objectIndex = operand.Member;
120
121 if (objectIndex.GetType() == typeof(string))
122 if (((string)objectIndex) == "length")
123 return strSource.Length;
124
125 if (objectIndex.GetType() != typeof(int))
126 throw new ExecutionException("Ein String ist nur numerisch indexierbar.");
127
128 return strSource[(int)objectIndex] + "";
129
130 }
131
132 // \todo member access test
133 else
134 {
135
136 object objectIndex = operand.Member;
137
138 object res = false;
139
140 System.Reflection.MethodInfo method = ((object)src).GetType().GetMethod((string)objectIndex);
141
142 try
143 {
144 res = method.Invoke(src, new object[0]);
145 }
146 catch(Exception) {
147 parameterStack.Push(NullReference.Instance);
148 return null;
149 }
150
151 return res;
152
153 }
154
155 //else throw new ExecutionException("Der Typ '"+ operand.Type + "' kann an dieser Stelle nicht verarbeitet werden.");
156
157 /*
158 * Arrays can be indexed with a "string" like
159 *
160 * var a = [];
161 * a["str"] = "Hello world";
162 *
163 * accually a pointer is an associative array
164 *
165 */
166 case OperandType.Pointer:
167
168 src = localMemory[(string)operand.Value];
169
170 if (src.GetType() == typeof(ArrayList))
171 {
172
173 object objectIndex = localMemory[operand.Pointer];
174
175 return ((ArrayList)src)[objectIndex];
176
177 }
178
179 else if (src.GetType() == typeof(string))
180 {
181
182 string strSource = (string)src;
183
184 object objectIndex = localMemory[operand.Pointer];
185
186 if (objectIndex.GetType() != typeof(int))
187 throw new ExecutionException("Ein String ist nur numerisch indexierbar.");
188
189 return strSource[(int)objectIndex] + "";
190
191 }
192
193 else
194 throw new ExecutionException("Nur Arrays und Strings sind indexierbar.");
195
196 default:
197 throw new ExecutionException("Der Typ '"+ operand.Type + "' kann an dieser Stelle nicht verarbeitet werden.");
198
199 }
200
201 }
202
208 private void Assignment(Operand dst, object val)
209 {
210
211 string identifier = (string)dst.Value;
212
213 switch (dst.Type)
214 {
215
216 case OperandType.Variable:
217 localMemory[identifier] = val;
218 break;
219
220 case OperandType.Member:
221 case OperandType.Pointer:
222 ArrayList array = null;
223
224 object tmp = null;
225
226 if (localMemory.Exists(identifier))
227 tmp = localMemory[identifier];
228
229 else
230 tmp = NullReference.Instance;
231
232 if (tmp.GetType() != typeof(ArrayList))
233 throw new ExecutionException("Das Ziel '" + dst + "' vom Typ '" + tmp.GetType().ToString() + "' ist nicht indexierbar.");
234
235 else
236 array = (ArrayList)tmp;
237
238 if (dst.Type == OperandType.Member)
239 array[dst.Member] = val;
240
241 else
242 array[localMemory[dst.Pointer]] = val;
243
244 break;
245
246 case OperandType.Literal:
247 throw new ExecutionException("Einem Literal kann nichts zugewiesen werden.");
248
249 }
250
251 }
252
257 private void Arithmetic()
258 {
259
260 String strIdentifierDest = (String)instruction.First.Value;
261 object objectValueDest = Evaluate(instruction.First);
262 object objectValueSource = Evaluate(instruction.Second);
263 Type typeDest = objectValueDest.GetType();
264 Type typeSource = objectValueSource.GetType();
265
266 // handle array and string concatenation
267 if (instruction.OpCode == OpCode.ADD)
268 {
269 if (typeDest == typeof(String))
270 {
271 Assignment(instruction.First, objectValueDest.ToString() + objectValueSource.ToString());
272 return;
273 }
274
275 if (typeDest == typeof(ArrayList))
276 {
277 ((ArrayList)objectValueDest).Add(objectValueSource);
278 return;
279 }
280 }
281
282 // handle array and string subtraction
283 if (instruction.OpCode == OpCode.SUB)
284 {
285 if (typeDest == typeof(String))
286 {
287 Assignment(instruction.First,
288 objectValueDest.ToString().Replace(objectValueSource.ToString(), ""));
289 return;
290 }
291 if (typeDest == typeof(ArrayList))
292 {
293 ((ArrayList)objectValueDest).Subtract(objectValueSource);
294 return;
295 }
296 }
297
298 float fValueDest = 0.0f;
299 float fValueSource = 0.0f;
300 float fResult = 0.0f;
301
302 if (typeDest == typeof(int))
303 fValueDest = (float)(int)objectValueDest;
304 else if (typeDest == typeof(float))
305 fValueDest = (float)objectValueDest;
306 else
307 throw new ScriptStackException("Values of type '" + typeDest.Name + "' cannot be used in arithmetic instructions.");
308
309 if (typeSource == typeof(int))
310 fValueSource = (float)(int)objectValueSource;
311 else if (typeSource == typeof(float))
312 fValueSource = (float)objectValueSource;
313 else
314 throw new ScriptStackException("Values of type '" + typeSource.Name + "' cannot be used in arithmetic instructions.");
315
316 switch (instruction.OpCode)
317 {
318 case OpCode.ADD: fResult = fValueDest + fValueSource; break;
319 case OpCode.SUB: fResult = fValueDest - fValueSource; break;
320 case OpCode.MUL: fResult = fValueDest * fValueSource; break;
321 case OpCode.DIV: fResult = fValueDest / fValueSource; break;
322 case OpCode.MOD: fResult = fValueDest % fValueSource; break;
323 default:
324 throw new ExecutionException("Invalid arithmetic instruction '" + instruction.OpCode + "'.");
325 }
326
327 if (typeDest == typeof(int) && typeSource == typeof(int))
328 Assignment(instruction.First, (int)fResult);
329 else
330 Assignment(instruction.First, fResult);
331
332 }
333
344 private void Relation()
345 {
346
347 string identifier = (string)instruction.First.Value;
348
349 object dst = Evaluate(instruction.First);
350
351 object src = Evaluate(instruction.Second);
352
353 Type typeDest = dst.GetType();
354
355 Type typeSource = src.GetType();
356
357 bool result = false;
358
363 if (typeDest == typeof(NullReference) || typeSource == typeof(NullReference))
364 {
365
366 switch (instruction.OpCode)
367 {
368
369 case OpCode.CEQ:
370 result = dst == src;
371 break;
372
373 case OpCode.CNE:
374 result = dst != src;
375 break;
376
377 default:
378 string message = "";
379
380 if (typeDest == typeof(NullReference) && typeSource == typeof(NullReference))
381 message = "Die Operation '" + instruction.OpCode + "' kann nicht auf den Typ 'null' angewendet werden.";
382
383 else if (typeDest == typeof(NullReference))
384 message = "Die Operation '" + instruction.OpCode + "' kann nicht auf den Typ 'null' als Ziel (links des Operators) angewendet werden.";
385
386 else
387 message = "Die Operation '" + instruction.OpCode + "' kann nicht auf den Typ 'null' als Quelle (rechts des Operators) angewendet werden.";
388
389 throw new ExecutionException(message);
390
391
392 }
393
394 // As a last resort, just assign it
395 Assignment(instruction.First, result);
396
397 return;
398
399 }
400
405 if (typeDest == typeof(string) || typeSource == typeof(string))
406 {
407
408 string strDst = "" + dst;
409
410 string strSrc = "" + src;
411
412 switch (instruction.OpCode)
413 {
414
415 case OpCode.CEQ:
416 result = strDst == strSrc;
417 break;
418
419 case OpCode.CNE:
420 result = strDst != strSrc;
421 break;
422
423 case OpCode.CG:
424 result = strDst.CompareTo(strSrc) > 0;
425 break;
426
427 case OpCode.CGE:
428 result = strDst.CompareTo(strSrc) >= 0;
429 break;
430
431 case OpCode.CL:
432 result = strDst.CompareTo(strSrc) < 0;
433 break;
434
435 case OpCode.CLE:
436 result = strDst.CompareTo(strSrc) <= 0;
437 break;
438
439 default:
440 throw new ExecutionException("Die Operation '" + instruction.OpCode + "' kann nicht auf den Typ 'String' angewendet werden.");
441
442 }
443
444 Assignment(instruction.First, result);
445
446 return;
447
448 }
449
450 double dstVal = 0.0;
451
452 double srcVal = 0.0;
453
454 if (typeDest == typeof(int))
455 dstVal = double.Parse("" + (int)dst);
456
457 else if (typeDest == typeof(float))
458 dstVal = double.Parse("" + (float)dst);
459
460 else if (typeDest == typeof(char))
461 dstVal = double.Parse("" + (char)dst);
462
463 else if (typeDest == typeof(double))
464 dstVal = (double)dst;
465
466 else
467 throw new ExecutionException("Der Typ '" + typeDest.Name + "' kann in relationalen Operationen als Ziel (links des Operators) nicht verarbeitet werden.");
468
469 if (typeSource == typeof(int))
470 srcVal = double.Parse("" + (int)src);
471
472 else if (typeSource == typeof(float))
473 srcVal = double.Parse("" + (float)src);
474
475 else if (typeSource == typeof(char))
476 srcVal = double.Parse("" + (char)src);
477
478 else if (typeSource == typeof(double))
479 srcVal = (double)src;
480
481 else
482 throw new ExecutionException("Der Typ '" + typeSource.Name + "' kann in relationalen Operationen als Quelle (rechts des Operators) nicht verarbeitet werden.");
483
484 switch (instruction.OpCode)
485 {
486
487 case OpCode.CEQ:
488 result = dstVal == srcVal;
489 break;
490
491 case OpCode.CNE:
492 result = dstVal != srcVal;
493 break;
494
495 case OpCode.CG:
496 result = dstVal > srcVal;
497 break;
498
499 case OpCode.CGE:
500 result = dstVal >= srcVal;
501 break;
502
503 case OpCode.CL:
504 result = dstVal < srcVal;
505 break;
506
507 case OpCode.CLE:
508 result = dstVal <= srcVal;
509 break;
510
511 default:
512 throw new ExecutionException("Der OpCode '" + instruction.OpCode + "' kann in einer relationalen Operation nicht verarbeitet werden.");
513
514 }
515
516 Assignment(instruction.First, result);
517
518 }
519
525 private void Logic()
526 {
527
528 string identifier = (string)instruction.First.Value;
529
530 object dst = Evaluate(instruction.First);
531
532 object src = Evaluate(instruction.Second);
533
534 Type typeDest = dst.GetType();
535
536 Type typeSource = src.GetType();
537
538 bool result = false;
539
540 bool dstVal = false;
541
542 bool srcVal = false;
543
544 if (typeSource == typeof(bool))
545 srcVal = (bool)src;
546
547 else if (typeSource == typeof(NullReference))
548 srcVal = false;
549
550 else
551 srcVal = ((double)src != 0.0) ? true : false;
552
553
554 if (typeDest == typeof(bool))
555 dstVal = (bool)dst;
556
557 else if (typeDest == typeof(NullReference))
558 dstVal = false;
559
560 else
561 dstVal = ((double)dst != 0.0) ? true : false;
562
563 switch (instruction.OpCode)
564 {
565
566 case OpCode.AND:
567 result = dstVal && srcVal;
568 break;
569
570 case OpCode.OR:
571 result = dstVal || srcVal;
572 break;
573
574 default:
575 throw new ExecutionException("Der OpCode '" + instruction.OpCode + "' kann in einer logischen Operation nicht verarbeitet werden.");
576
577 }
578
579 Assignment(instruction.First, result);
580
581 }
582
583 private void Iterator(ArrayList array)
584 {
585
586 if (array.Count == 0)
587 return;
588
589 object iterator = Evaluate(instruction.First);
590
591 bool key = false;
592
593 object next = null;
594
595 foreach (object tmp in array.Keys)
596 {
597
598 if (key)
599 {
600 next = tmp;
601 break;
602 }
603
604 if (tmp == iterator)
605 key = true;
606
607 }
608
609 if (!key)
610 {
611
612 Dictionary<object, object>.KeyCollection.Enumerator keys = array.Keys.GetEnumerator();
613
614 keys.MoveNext();
615
616 next = keys.Current;
617
618 }
619
620 if (next == null)
621 next = NullReference.Instance;
622
623 localMemory[instruction.First.Value.ToString()] = next;
624
625 }
626
627 private void Iterator(string str)
628 {
629
630 if (str.Length == 0)
631 return;
632
633 object iterator = Evaluate(instruction.First);
634
635 if (iterator.GetType() != typeof(int))
636 {
637
638 localMemory[instruction.First.Value.ToString()] = 0;
639
640 return;
641
642 }
643
644 int elements = (int)iterator;
645
646 if (elements < str.Length - 1)
647 localMemory[instruction.First.Value.ToString()] = elements + 1;
648
649 else
650 localMemory[instruction.First.Value.ToString()] = NullReference.Instance;
651
652 }
653
654
655
656 private void DBG()
657 {
658 }
659
660 private void NOP()
661 {
662 }
663
664 private void INT()
665 {
666 interrupted = true;
667 }
668
672 private void RET()
673 {
674
675 functionStack.Pop();
676
677 if (functionStack.Count == 0)
678 {
679 finished = true;
680 return;
681 }
682
683 localMemory = functionStack.Peek().localMemory;
684
685 }
686
687
688
689 private void PUSH()
690 {
691 parameterStack.Push(Evaluate(instruction.First));
692 }
693
697 private void POP()
698 {
699
700 object tmp = parameterStack.Pop();
701
702 Operand operand = instruction.First;
703
704 switch (operand.Type)
705 {
706
707 case OperandType.Variable:
708 localMemory[operand.Value.ToString()] = tmp;
709 break;
710
711 case OperandType.Member:
712 case OperandType.Pointer:
713 if (localMemory[operand.Value.ToString()].GetType() != typeof(ArrayList))
714 throw new ExecutionException("Ein 'Array' wurde erwartet.");
715
716 ArrayList array = (ArrayList)localMemory[operand.Value.ToString()];
717
718 if (operand.Type == OperandType.Member)
719 array[operand.Member] = tmp;
720
721 else
722 array[localMemory[operand.Pointer]] = tmp;
723
724 break;
725
726 default:
727 throw new ExecutionException("Der Typ '" + operand.Type + "' kann an dieser Stelle nicht verarbeitet werden.");
728
729 }
730
731 }
732
736 private void MOV()
737 {
738
739 Assignment(instruction.First, Evaluate(instruction.Second));
740 }
741
742
746 private void ADD()
747 {
748 Arithmetic();
749 }
750
754 private void SUB()
755 {
756 Arithmetic();
757 }
758
762 private void MUL()
763 {
764 Arithmetic();
765 }
766
770 private void DIV()
771 {
772 Arithmetic();
773 }
774
778 private void MOD()
779 {
780 Arithmetic();
781 }
782
783
784
788 private void INC()
789 {
790
791 string identifier = (string)instruction.First.Value;
792
793 object val = Evaluate(instruction.First);
794
795 Type typeDest = val.GetType();
796
797 if (typeDest == typeof(int))
798 localMemory[identifier] = (int)val + 1;
799
800 else if (typeDest == typeof(float))
801 localMemory[identifier] = (float)val + 1;
802
803 else if (typeDest == typeof(double))
804 localMemory[identifier] = (double)val + 1;
805
806 else if (typeDest == typeof(char))
807 localMemory[identifier] = (char)val + 1;
808
809 else
810 throw new ExecutionException("Der Typ '" + typeDest.Name + "' kann nicht inkrementiert werden.");
811
812 }
813
817 private void DEC()
818 {
819
820 string identifier = (string)instruction.First.Value;
821
822 object val = Evaluate(instruction.First);
823
824 Type typeDest = val.GetType();
825
826 if (typeDest == typeof(int))
827 localMemory[identifier] = (int)val - 1;
828
829 else if (typeDest == typeof(float))
830 localMemory[identifier] = (float)val - 1;
831
832 else if (typeDest == typeof(double))
833 localMemory[identifier] = (double)val - 1;
834
835 else if (typeDest == typeof(char))
836 localMemory[identifier] = (char)val - 1;
837
838 else
839 throw new ExecutionException("Der Typ '" + typeDest.Name + "' kann nicht dekrementiert werden.");
840
841 }
842
846 private void NEG()
847 {
848
849 string identifier = (string)instruction.First.Value;
850
851 object val = Evaluate(instruction.First);
852
853 Type typeDest = val.GetType();
854
855 if (typeDest == typeof(int))
856 localMemory[identifier] = (int)val * -1;
857
858 else if (typeDest == typeof(float))
859 localMemory[identifier] = (float)val * -1;
860
861 else if (typeDest == typeof(double))
862 localMemory[identifier] = (double)val * -1;
863
864 else if (typeDest == typeof(char))
865 localMemory[identifier] = (char)val * -1;
866
867 else
868 throw new ExecutionException("Der Typ '" + typeDest.Name + "' kann nicht negiert werden.");
869
870 }
871
875 private void SHL()
876 {
877
878 string identifier = null;
879
880 object val = Evaluate(instruction.Second);
881
882 Operand operand = instruction.First;
883
884 switch (operand.Type)
885 {
886
887 case OperandType.Variable:
888 int res = (int)localMemory[(string)operand.Value] << (int)val;
889
890 identifier = operand.Value.ToString();
891
892 localMemory[identifier] = res;
893
894 break;
895
896 default:
897 throw new ExecutionException("Der Typ '" + operand.Type + "' kann in Bitoperationen nicht verarbeitet werden.");
898
899 }
900
901 }
902
906 private void SHR()
907 {
908
909 string identifier = null;
910
911 object val = Evaluate(instruction.Second);
912
913 Operand operand = instruction.First;
914
915 switch (operand.Type)
916 {
917
918 case OperandType.Variable:
919 int res = (int)localMemory[(string)operand.Value] >> (int)val;
920
921 identifier = operand.Value.ToString();
922
923 localMemory[identifier] = res;
924
925 break;
926
927 default:
928 throw new ExecutionException("Der Typ '" + operand.Type + "' kann in Bitoperationen nicht verarbeitet werden.");
929
930 }
931
932 }
933
934
935
936 private void TEST()
937 {
938
939 string identifier = (string)instruction.First.Value;
940
941 object val = Evaluate(instruction.First);
942
943 localMemory[identifier] = val == NullReference.Instance;
944
945 }
946
950 private void CEQ()
951 {
952 Relation();
953 }
954
958 private void CNE()
959 {
960 Relation();
961 }
962
966 private void CG()
967 {
968 Relation();
969 }
970
974 private void CGE()
975 {
976 Relation();
977 }
978
982 private void CL()
983 {
984 Relation();
985 }
986
990 private void CLE()
991 {
992 Relation();
993 }
994
995
996
997 private void OR()
998 {
999 Logic();
1000 }
1001
1002 private void AND()
1003 {
1004 Logic();
1005 }
1006
1012 private void NOT()
1013 {
1014
1015 string identifier = (string)instruction.First.Value;
1016
1017 object val = Evaluate(instruction.First);
1018
1019 Type typeDest = val.GetType();
1020
1021 if (typeDest != typeof(bool) && typeDest != typeof(int))
1022 throw new ExecutionException("Der Typ '" + typeDest.Name + "' kann nicht negiert werden.");
1023
1024 if (typeDest == typeof(bool))
1025 localMemory[identifier] = !((bool)val);
1026
1027 else if (typeDest == typeof(int))
1028 localMemory[identifier] = (int)val * -1;
1029
1030 }
1031
1032
1033
1034 private void ORB()
1035 {
1036
1037 string identifier = null;
1038
1039 object val = Evaluate(instruction.Second);
1040
1041 Operand operand = instruction.First;
1042
1043 switch (operand.Type)
1044 {
1045
1046 case OperandType.Variable:
1047 int res = (int)val | (int)localMemory[(string)operand.Value];
1048
1049 identifier = operand.Value.ToString();
1050
1051 localMemory[identifier] = res;
1052
1053 break;
1054
1055 default:
1056 throw new ExecutionException("Der Typ '" + operand.Type + "' kann an dieser Stelle nicht verarbeitet werden.");
1057
1058 }
1059
1060 }
1061
1062 private void ANDB()
1063 {
1064
1065 string identifier = null;
1066
1067 object val = Evaluate(instruction.Second);
1068
1069 Operand operand = instruction.First;
1070
1071 switch (operand.Type)
1072 {
1073
1074 case OperandType.Variable:
1075 int res = (int)localMemory[(string)operand.Value] & (int)val;
1076
1077 identifier = operand.Value.ToString();
1078
1079 localMemory[identifier] = res;
1080
1081 break;
1082
1083 default:
1084 throw new ExecutionException("Operand type '" + operand.Type + "' not supported by logical AND instruction.");
1085
1086 }
1087
1088 }
1089
1090 private void NOTB()
1091 {
1092
1093 string identifier = null;
1094
1095 object val = Evaluate(instruction.Second);
1096
1097 Operand operand = instruction.First;
1098
1099 switch (operand.Type)
1100 {
1101
1102 case OperandType.Variable:
1103 var res = ~(int)val;
1104
1105 identifier = operand.Value.ToString();
1106
1107 localMemory[identifier] = res;
1108
1109 break;
1110
1111 default:
1112 throw new ExecutionException("Der Typ '" + operand.Type + "' kann an dieser Stelle nicht verarbeitet werden.");
1113
1114 }
1115
1116 }
1117
1118 private void XOR()
1119 {
1120
1121 string identifier = null;
1122
1123 object val = Evaluate(instruction.Second);
1124
1125 Operand operand = instruction.First;
1126
1127 switch (operand.Type)
1128 {
1129
1130 case OperandType.Variable:
1131 int res = (int)val ^ (int)localMemory[(string)operand.Value];
1132
1133 identifier = operand.Value.ToString();
1134
1135 localMemory[identifier] = res;
1136
1137 break;
1138
1139 default:
1140 throw new ExecutionException("Der Typ '" + operand.Type + "' kann an dieser Stelle nicht verarbeitet werden.");
1141
1142 }
1143
1144 }
1145
1149 private void JMP()
1150 {
1151
1152 FunctionFrame frame = functionStack.Peek();
1153
1154 frame.nextInstruction = (int)instruction.First.InstructionPointer.Address;
1155
1156 }
1157
1161 private void JZ()
1162 {
1163
1164 if (!(bool)Evaluate(instruction.First))
1165 return;
1166
1167 Instruction target = instruction.Second.InstructionPointer;
1168
1169 FunctionFrame frame = functionStack.Peek();
1170
1171 frame.nextInstruction = (int)target.Address;
1172
1173 }
1174
1178 private void JNZ()
1179 {
1180
1181 if ((bool)Evaluate(instruction.First))
1182 return;
1183
1184 FunctionFrame frame = functionStack.Peek();
1185
1186 frame.nextInstruction = (int)instruction.Second.InstructionPointer.Address;
1187
1188 }
1189
1190
1191
1192 private void DSB()
1193 {
1194
1195 throw new ExecutionException("DCG opcodes cannot be executed within a function frame.");
1196
1197 }
1198
1199 private void DB()
1200 {
1201
1202 localMemory[(string)instruction.First.Value] = NullReference.Instance;
1203
1204 }
1205
1206 private void DC()
1207 {
1208
1209 if (instruction.First.Type != OperandType.Variable)
1210 throw new ExecutionException("Error in array declaration.");
1211
1212 ArrayList array = new ArrayList();
1213
1214 localMemory[instruction.First.Value.ToString()] = array;
1215
1216 }
1217
1218 private void DCO()
1219 {
1220
1221 string strIdentifier = null;
1222
1223 object objectValue = Evaluate(instruction.Second);
1224
1225 Operand operand = instruction.First;
1226
1227 switch (operand.Type)
1228 {
1229
1230 case OperandType.Variable:
1231 int res = (int)objectValue ^ (int)localMemory[(string)operand.Value];
1232
1233 strIdentifier = operand.Value.ToString();
1234
1235 localMemory[strIdentifier] = res;
1236
1237 break;
1238
1239 default:
1240 throw new ExecutionException("Operand type '" + operand.Type + "' not supported by logical LNEG instruction.");
1241
1242 }
1243
1244 }
1245
1250 private void PTR()
1251 {
1252
1253 if (instruction.First.Type != OperandType.Variable)
1254 throw new ExecutionException("Error in PTR.");
1255
1256 if (instruction.Second.Type != OperandType.Variable)
1257 throw new ExecutionException("Error in PTR.");
1258
1259 object enumerable = localMemory[instruction.Second.Value.ToString()];
1260
1261 if (enumerable.GetType() == typeof(ArrayList))
1262 Iterator((ArrayList)enumerable);
1263
1264 else if (enumerable.GetType() == typeof(string))
1265 Iterator((string)enumerable);
1266
1267 else
1268 throw new ExecutionException("Error in PTR.");
1269
1270 }
1271
1272
1273 // if any errors add "localMemory = frame.localMemory;" after the allocation
1277 private void CALL()
1278 {
1279
1280 Function function = instruction.First.FunctionPointer;
1281
1282 FunctionFrame frame = new FunctionFrame();
1283
1284 frame.function = function;
1285
1286 frame.localMemory = Memory.AllocateLocalMemory(executable.ScriptMemory);
1287
1288 frame.nextInstruction = (int) function.EntryPoint.Address;
1289
1290 functionStack.Push(frame);
1291
1292 }
1293
1300 private void INV()
1301 {
1302
1303 Routine routine = instruction.First.RoutinePointer;
1304
1305 Host stackHandler = null;
1306
1307 if (routine.Handler == null)
1308 {
1309 if (host != null)
1310 stackHandler = host;
1311 }
1312
1313 else
1314 stackHandler = routine.Handler;
1315
1316 List<object> parameters = new List<object>();
1317
1318 for (int i = 0; i < routine.ParameterTypes.Count; i++)
1319 parameters.Insert(0, parameterStack.Pop());
1320
1321 routine.Verify(parameters);
1322
1323 object objectResult = null;
1324
1325 if (stackHandler != null)
1326 {
1327
1328 try {
1329 objectResult = stackHandler.Invoke(routine.Name, parameters);
1330 }
1331 catch (Exception) {
1332
1333 parameterStack.Push(NullReference.Instance);
1334 return;
1335
1336 }
1337
1338 routine.Verify(objectResult);
1339
1340 }
1341
1342 if (objectResult == null)
1343 objectResult = NullReference.Instance;
1344
1345 parameterStack.Push(objectResult);
1346
1347 if (interrupt)
1348 interrupted = true;
1349
1350 }
1351
1352
1428 private void RUN()
1429 {
1430
1431 Function function = instruction.First.FunctionPointer;
1432
1433 List<object> parameters = new List<object>();
1434
1435 for (int i = 0; i < function.ParameterCount; i++)
1436 parameters.Insert(0, parameterStack.Pop());
1437
1438 Interpreter job = new Interpreter(function, parameters);
1439
1440 job.Handler = host;
1441
1442 jobs.Add(job);
1443
1444 }
1445
1446 private void LOCK()
1447 {
1448
1449 object first = Evaluate(instruction.First);
1450
1451 if (first.GetType() == typeof(NullReference))
1452 throw new ExecutionException("Lock key must be a literal value.");
1453
1454 if (script.Manager.Locks.ContainsKey(first))
1455 {
1456
1457 Interpreter locked = script.Manager.Locks[first];
1458
1459 if (locked == this && locks[first] != instruction)
1460 throw new ExecutionException("Nested locks cannot share the same locking key.");
1461
1462 FunctionFrame functionFrame = functionStack.Peek();
1463 --functionFrame.nextInstruction;
1464 interrupted = true;
1465
1466 }
1467
1468 else
1469 {
1470
1471 script.Manager.Locks[first] = this;
1472
1473 locks[first] = instruction;
1474
1475 }
1476
1477 }
1478
1479 private void FREE()
1480 {
1481
1482 object first = Evaluate(instruction.First);
1483
1484 if (first.GetType() == typeof(NullReference))
1485 throw new ExecutionException("Lock key must be a literal value.");
1486
1487 if (!script.Manager.Locks.ContainsKey(first))
1488 throw new ExecutionException("Lock '" + first + "' is already unlocked.");
1489
1490 locks.Remove(first);
1491
1492 script.Manager.Locks.Remove(first);
1493
1494 }
1495
1496
1497 private uint ExecuteBackgroundJobs()
1498 {
1499
1500 uint executed = 0;
1501
1502 foreach (Interpreter job in jobs)
1503 if (!job.Finished)
1504 executed += job.Interpret(1);
1505
1506 for (int i = jobs.Count - 1; i >= 0; i--)
1507 if (jobs[i].Finished)
1508 jobs.RemoveAt(i);
1509
1510 return executed;
1511
1512 }
1513
1514 private void ExecuteInstruction()
1515 {
1516
1517 instruction = executable.InstructionsInternal[functionStack.Peek().nextInstruction++];
1518
1519 switch (instruction.OpCode)
1520 {
1521
1522 case OpCode.DBG: DBG(); break;
1523 case OpCode.NOP: NOP(); break;
1524
1525 case OpCode.INT: INT(); break;
1526 case OpCode.RET: RET(); break;
1527
1528 case OpCode.PUSH: PUSH(); break;
1529 case OpCode.POP: POP(); break;
1530 case OpCode.MOV: MOV(); break;
1531
1532 case OpCode.ADD: Arithmetic(); break;
1533 case OpCode.SUB: Arithmetic(); break;
1534 case OpCode.MUL: Arithmetic(); break;
1535 case OpCode.DIV: Arithmetic(); break;
1536 case OpCode.MOD: Arithmetic(); break;
1537
1538 case OpCode.INC: INC(); break;
1539 case OpCode.DEC: DEC(); break;
1540 case OpCode.NEG: NEG(); break;
1541 case OpCode.SHL: SHL(); break;
1542 case OpCode.SHR: SHR(); break;
1543
1544 case OpCode.TEST: TEST(); break;
1545 case OpCode.CEQ: CEQ(); break;
1546 case OpCode.CNE: CNE(); break;
1547 case OpCode.CG: CG(); break;
1548 case OpCode.CGE: CGE(); break;
1549 case OpCode.CL: CL(); break;
1550 case OpCode.CLE: CLE(); break;
1551
1552 case OpCode.OR: OR(); break;
1553 case OpCode.AND: AND(); break;
1554 case OpCode.NOT: NOT(); break;
1555
1556 case OpCode.ORB: ORB(); break;
1557 case OpCode.ANDB: ANDB(); break;
1558 case OpCode.NOTB: NOTB(); break;
1559 case OpCode.XOR: XOR(); break;
1560
1561 case OpCode.JMP: JMP(); break;
1562 case OpCode.JZ: JZ(); break;
1563 case OpCode.JNZ: JNZ(); break;
1564
1565 case OpCode.DSB: DSB(); break;
1566 case OpCode.DB: DB(); break;
1567 case OpCode.DC: DC(); break;
1568 case OpCode.DCO: DCO(); break;
1569 case OpCode.PTR: PTR(); break;
1570
1571 case OpCode.CALL: CALL(); break;
1572 case OpCode.INV: INV(); break;
1573 case OpCode.RUN: RUN(); break;
1574
1575 case OpCode.LOCK: LOCK(); break;
1576 case OpCode.FREE: FREE(); break;
1577
1578 }
1579
1580 }
1581
1582 #endregion
1583
1584 #region Public Methods
1585
1586 public Interpreter(Function function, List<object> parameters)
1587 {
1588
1589 if (function.ParameterCount != parameters.Count)
1590 throw new ExecutionException("Die Funktion '" + function.Name + "' wurde mit " + parameters.Count + " statt erwartet " + function.ParameterCount + " Parametern aufgerufen.");
1591
1592 this.function = function;
1593
1594 script = function.Executable.Script;
1595
1596 executable = script.Executable;
1597
1598 functionStack = new Stack<FunctionFrame>();
1599
1600 parameterStack = new Stack<object>();
1601
1602 locks = new Dictionary<object, Instruction>();
1603
1604 jobs = new List<Interpreter>();
1605
1606 host = null;
1607
1608 interrupt = false;
1609
1610 Reset();
1611
1612 foreach (object parameter in parameters)
1613 {
1614
1615 if (parameter == null)
1616 parameterStack.Push(NullReference.Instance);
1617
1618 else
1619 {
1620
1621 Type parameterType = parameter.GetType();
1622
1623 if (parameterType == typeof(NullReference))
1624 parameterStack.Push(NullReference.Instance);
1625
1626 else if (parameterType == typeof(int)
1627 || parameterType == typeof(float)
1628 || parameterType == typeof(double)
1629 || parameterType == typeof(bool)
1630 || parameterType == typeof(string)
1631 || parameterType == typeof(char)
1632 || parameterType == typeof(ArrayList))
1633 parameterStack.Push(parameter);
1634
1635 else
1636 throw new ExecutionException("Der Typ '" + parameterType.Name + "' ist kein generischer Typ.");
1637
1638 }
1639
1640 }
1641
1642 }
1643
1644 public Interpreter(Function function) :
1645 this(function, new List<object>())
1646 {
1647 }
1648
1649 public Interpreter(Script script, List<object> parameters) :
1650 this(script.Executable.MainFunction, parameters)
1651 {
1652 }
1653
1654 public Interpreter(Script script) :
1655 this(script.Executable.MainFunction, new List<object>())
1656 {
1657 }
1658
1659 public void Reset()
1660 {
1661
1662 functionStack.Clear();
1663
1664 FunctionFrame functionFrame = new FunctionFrame();
1665
1666 functionFrame.function = function;
1667
1668 functionFrame.localMemory = Memory.AllocateLocalMemory(executable.ScriptMemory);
1669
1670 functionFrame.nextInstruction = (int) function.EntryPoint.Address;
1671
1672 functionStack.Push(functionFrame);
1673
1674 parameterStack.Clear();
1675
1676 instruction = null;
1677
1678 localMemory = functionFrame.localMemory;
1679
1680 foreach (object currentLock in locks.Keys)
1681 script.Manager.Locks.Remove(currentLock);
1682
1683 locks.Clear();
1684
1685 finished = false;
1686
1687 interrupted = false;
1688
1689 }
1690
1691 public uint Interpret(uint instructions)
1692 {
1693
1694 localMemory.ExposeTemporaryVariables();
1695
1696 interrupted = false;
1697
1698 uint executed = 0;
1699
1700 while (!Finished && !interrupted && executed < instructions)
1701 {
1702
1703 ExecuteInstruction();
1704
1705 ++executed;
1706
1707 executed += ExecuteBackgroundJobs();
1708
1709 }
1710
1711 localMemory.HideTemporaryVariables();
1712
1713 return executed;
1714
1715 }
1716
1717 public uint Interpret(TimeSpan interval)
1718 {
1719
1720 DateTime end = DateTime.Now + interval;
1721
1722 localMemory.ExposeTemporaryVariables();
1723
1724 interrupted = false;
1725
1726 uint executed = 0;
1727
1728 while (!Finished && !interrupted)
1729 {
1730
1731 ExecuteInstruction();
1732
1733 ++executed;
1734
1735 executed += ExecuteBackgroundJobs();
1736
1737 if (DateTime.Now >= end)
1738 break;
1739
1740 }
1741
1742 localMemory.HideTemporaryVariables();
1743
1744 return executed;
1745
1746 }
1747
1748 public uint Interpret()
1749 {
1750
1751 localMemory.ExposeTemporaryVariables();
1752
1753 interrupted = false;
1754
1755 uint executed = 0;
1756
1757 while (!Finished && !interrupted)
1758 {
1759
1760 ExecuteInstruction();
1761
1762 ++executed;
1763
1764 executed += ExecuteBackgroundJobs();
1765
1766 }
1767
1768 localMemory.HideTemporaryVariables();
1769
1770 return executed;
1771
1772 }
1773
1774 #endregion
1775
1776 #region Public Properties
1777
1779 {
1780 get { return script; }
1781 }
1782
1783 public bool Interrupt
1784 {
1785 get { return interrupt; }
1786 set { interrupt = value; }
1787 }
1788
1789 public ReadOnlyCollection<Interpreter> Jobs
1790 {
1791 get { return jobs.AsReadOnly(); }
1792 }
1793
1794 public bool Interrupted
1795 {
1796 get { return interrupted; }
1797 }
1798
1799 public bool Finished
1800 {
1801 get { return finished; }
1802 }
1803
1805 {
1806 get
1807 {
1808 if (functionStack.Count == 0) return -1;
1809 return functionStack.Peek().nextInstruction;
1810 }
1811 }
1812
1813 public ReadOnlyCollection<Function> FunctionStack
1814 {
1815 get
1816 {
1817 List<Function> listFunctions = new List<Function>();
1818 foreach (FunctionFrame functionFrame in functionStack)
1819 listFunctions.Add(functionFrame.function);
1820 return new List<Function>(listFunctions).AsReadOnly();
1821 }
1822 }
1823
1824 public ReadOnlyCollection<object> ParameterStack
1825 {
1826 get { return new List<object>(parameterStack).AsReadOnly(); }
1827 }
1828
1830 {
1831 get { return localMemory; }
1832 }
1833
1835 {
1836 get { return host; }
1837 set { host = value; }
1838 }
1839
1840 #endregion
1841
1842 }
1843
1844}
A function, forward declared in a script.
Definition Function.cs:15
An instruction in a virtual intermediate language.
Interpreter(Script script, List< object > parameters)
ReadOnlyCollection< object > ParameterStack
uint Interpret(TimeSpan interval)
ReadOnlyCollection< Interpreter > Jobs
ReadOnlyCollection< Function > FunctionStack
uint Interpret(uint instructions)
Interpreter(Function function, List< object > parameters)
void Remove(string identifier)
Definition Memory.cs:105
static Memory AllocateLocalMemory(Memory scriptMemory)
Definition Memory.cs:65
Instruction InstructionPointer
Definition Operand.cs:242
A Routine is an abstract representation of a method.
Definition Routine.cs:70
void Verify(List< object > parameters)
Verify the parameter types of a Routine. If null or void was specified values arent verified.
Definition Routine.cs:223
List< Type > ParameterTypes
Definition Routine.cs:312
The main interface to create a Host. A Host can implement Routine's to extend its functionality.
Definition Host.cs:80
object Invoke(string routine, List< object > parameters)
Called when a Routine is invoked.