ScriptStack 1.0.5
Loading...
Searching...
No Matches
Parser.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5using ScriptStack;
7
9{
10
21 public class Parser
22 {
23
24 #region Private Structs
25
26 private struct Variable
27 {
28
29 public string name;
30 public Scope scope;
31 public Type derivatedType;
32
33 public Variable(string name, Scope scope, Type derivatedType)
34 {
35
36 this.name = name;
37
38 this.scope = scope;
39
40 this.derivatedType = derivatedType;
41
42 }
43
44 }
45
46 private struct FunctionDescriptor
47 {
48 public string name;
49 public uint parameterCount;
51 }
52
53 private struct LoopControl
54 {
57 }
58
59 #endregion
60
61 #region Private Variables
62
63 private Script script;
64 private bool debugMode;
65 private List<Token> tokenStream;
66 private int nextToken;
67 private Dictionary<string, bool> variables;
68 private Dictionary<string, bool> localVariables;
69 private int functionFrameIndex;
70 private Dictionary<Instruction, FunctionDescriptor> forwardDeclarations;
71 private Stack<LoopControl> loopControl;
72 private Derivation derivation;
74
75 #endregion
76
77 #region Private Methods
78
83 private bool More()
84 {
85 return nextToken < tokenStream.Count;
86 }
87
92 private Token ReadToken()
93 {
94
95 if (!More())
96 throw new ParserException("Es sind keine weiteren Token vorhanden.");
97
98 return tokenStream[nextToken++];
99
100 }
101
106 private Token LookAhead()
107 {
108
109 if (!More())
110 throw new ParserException("Es sind keine weiteren Token vorhanden.");
111
112 return tokenStream[nextToken];
113
114 }
115
121 private Token LookAhead(int i)
122 {
123
124 if (!More() || null == tokenStream[nextToken + i] )
125 throw new ParserException("Es sind keine weiteren Token vorhanden.");
126
127 return tokenStream[nextToken + i];
128
129 }
130
134 private void UndoToken()
135 {
136
137 if (nextToken <= 0)
138 throw new ParserException("Es sind keine vorangehenden Token mehr vorhanden.");
139
140 --nextToken;
141
142 }
143
149 private bool AssignmentOperator(TokenType tokenType)
150 {
151
152 switch (tokenType)
153 {
154
155 case TokenType.Assign:
156 case TokenType.AssignPlus:
157 case TokenType.AssignMinus:
158 case TokenType.AssignMultiply:
159 case TokenType.AssignDivide:
160 case TokenType.AssignBinaryAnd:
161 case TokenType.AssignBinaryOr:
162 case TokenType.AssignXor:
163 case TokenType.AssignBinaryNot:
164 case TokenType.AssignModulo:
165 return true;
166
167 default:
168 return false;
169
170 }
171
172 }
173
180 {
181
182 switch (tokenType)
183 {
184
185 case TokenType.Assign: return OpCode.MOV;
186 case TokenType.AssignPlus: return OpCode.ADD;
187 case TokenType.AssignMinus: return OpCode.SUB;
188 case TokenType.AssignMultiply: return OpCode.MUL;
189 case TokenType.AssignDivide: return OpCode.DIV;
190 case TokenType.AssignBinaryAnd: return OpCode.ANDB;
191 case TokenType.AssignBinaryOr: return OpCode.ORB;
192 case TokenType.AssignXor: return OpCode.XOR;
193 case TokenType.AssignBinaryNot: return OpCode.NOTB;
194 case TokenType.AssignModulo: return OpCode.MOD;
195 default:
196 throw new ParserException("Der Token '" + tokenType + "' ist kein Zuweisungsoperator.");
197
198 }
199
200 }
201
207 private bool RelationalOperator(TokenType tokenType)
208 {
209
210 switch (tokenType)
211 {
212
213 case TokenType.Equal:
214 case TokenType.NotEqual:
215 case TokenType.Greater:
216 case TokenType.GreaterEqual:
217 case TokenType.Less:
218 case TokenType.LessEqual:
219 return true;
220
221 default:
222 return false;
223
224 }
225
226 }
227
234 {
235
236 switch (tokenType)
237 {
238
239 case TokenType.Equal: return OpCode.CEQ;
240 case TokenType.NotEqual: return OpCode.CNE;
241 case TokenType.Greater: return OpCode.CG;
242 case TokenType.GreaterEqual: return OpCode.CGE;
243 case TokenType.Less: return OpCode.CL;
244 case TokenType.LessEqual: return OpCode.CLE;
245 default:
246 throw new ParserException("Der Token '" + tokenType + "' ist kein relationaler Operator.");
247
248 }
249
250 }
251
258 private Type Literal(TokenType tokenType)
259 {
260
261 switch (tokenType)
262 {
263
264 case TokenType.Integer:
265 return typeof(int);
266 case TokenType.Float:
267 return typeof(float);
268 case TokenType.Boolean:
269 return typeof(bool);
270 case TokenType.String:
271 return typeof(string);
272 case TokenType.Char:
273 return typeof(char);
274 case TokenType.Double:
275 return typeof(double);
276 case TokenType.Decimal:
277 return typeof(decimal);
278 default:
279 throw new ParserException( "Der Token '" + tokenType.ToString() + "' kann keinem Literal zugeordnet werden.");
280
281 }
282
283 }
284
292 private Type Derivate(Token token, Type first, Type second)
293 {
294
295 return derivation.Derivate(token, first, second);
296
297 }
298
303 private void AllocateVariable(string identifier)
304 {
305
306 if (variables.ContainsKey(identifier))
307 throw new ParserException("Die Variable '" + identifier + "' ist in einem umschliessenden Bereich bereits deklariert.");
308
309 if (localVariables.ContainsKey(identifier))
310 throw new ParserException("Die Variable '" + identifier + "' ist in diesem Bereich bereits deklariert.");
311
312 localVariables[identifier] = true;
313
314 executable.InstructionsInternal.Add(new Instruction(OpCode.DB, Operand.Variable(identifier)));
315
316 }
317
322 {
323
325
326 }
327
334 {
335
336 int index = 0;
337
338 while (true)
339 {
340
341 string identifier = "[" + functionFrameIndex + ":" + index + "]";
342
343 if (!localVariables.ContainsKey(identifier) && !variables.ContainsKey(identifier))
344 {
345
346 localVariables[identifier] = true;
347
348 return identifier;
349
350 }
351
352 ++index;
353
354 }
355
356 }
357
361 private void FreeFunctionFrame()
362 {
363
364 if (functionFrameIndex == 0)
365 return;
366
367 List<string> candidates = new List<string>();
368
369 foreach (string identifier in localVariables.Keys)
370 if (identifier.StartsWith("[" + functionFrameIndex + ":"))
371 candidates.Add(identifier);
372
373 foreach (string identifier in candidates)
374 localVariables.Remove(identifier);
375
377
378 }
379
383 private void ReadSemicolon()
384 {
385
386 Token token = ReadToken();
387
388 if (token.Type != TokenType.SemiColon)
389 throw new ParserException("Semicolon ';' erwartet.", token);
390
391 }
392
396 private void ReadComma()
397 {
398
399 Token token = ReadToken();
400
401 if (token.Type != TokenType.Comma)
402 throw new ParserException("Comma ',' erwartet.", token);
403
404 }
405
409 private void ReadLeftParenthesis()
410 {
411
412 Token token = ReadToken();
413
414 if (token.Type != TokenType.LeftParen)
415 throw new ParserException("Klammer '(' erwartet.", token);
416
417 }
418
422 private void ReadRightParenthesis()
423 {
424
425 Token token = ReadToken();
426
427 if (token.Type != TokenType.RightParen)
428 throw new ParserException("Klammer ')' erwartet.", token);
429
430 }
431
435 private void ReadLeftBrace()
436 {
437
438 Token token = ReadToken();
439
440 if (token.Type != TokenType.LeftBrace)
441 throw new ParserException("Geschwungene Klammer '{' erwartet.", token);
442
443 }
444
448 private void ReadRightBrace()
449 {
450
451 Token token = ReadToken();
452
453 if (token.Type != TokenType.RightBrace)
454 throw new ParserException("Geschwungene Klammer '}' erwartet.", token);
455
456 }
457
461 private void ReadLeftBracket()
462 {
463
464 Token token = ReadToken();
465
466 if (token.Type != TokenType.LeftBracket)
467 throw new ParserException("Eckige Klammer '[' erwartet.", token);
468
469 }
470
474 private void ReadRightBracket()
475 {
476
477 Token token = ReadToken();
478
479 if (token.Type != TokenType.RightBracket)
480 throw new ParserException("Eckige Klammer ']' erwartet.", token);
481
482 }
483
487 private void ReadPeriod()
488 {
489
490 Token token = ReadToken();
491
492 if (token.Type != TokenType.Period)
493 throw new ParserException("Punkt '.' erwartet.", token);
494
495 }
496
497 private void InsertDebugInfo(Token token)
498 {
499
500 if (!debugMode)
501 return;
502
503 string text = token.Text;
504
505 //text = text.Replace("\r", "").Replace("\n", "");
506
507 executable.InstructionsInternal.Add(new Instruction(OpCode.DBG, Operand.Literal(token.Line), Operand.Literal(text)));
508
509 }
510
515 private string ReadIdentifier()
516 {
517
518 Token token = ReadToken();
519
520 if (token.Type != TokenType.Identifier)
521 throw new ParserException("Ein Keyword oder eine Variable wurde erwartet.", token);
522
523 return token.Lexeme.ToString();
524
525 }
526
533 private string ReadQualifiedIdentifier()
534 {
535
536 string id = ReadIdentifier();
537
538 while (More() && LookAhead().Type == TokenType.Period)
539 {
540
541 ReadPeriod();
542
543 id += "." + ReadIdentifier();
544
545 }
546
547 return id;
548
549 }
550
555 private string ExpectIdentifier()
556 {
557
558 Token token = ReadToken();
559
560 if (token.Type != TokenType.Identifier)
561 throw new ParserException("Ein Keyword oder eine Variable wurde erwartet.", token);
562
563 string identifier = token.Lexeme.ToString();
564
565 if (!variables.ContainsKey(identifier) && !localVariables.ContainsKey(identifier))
566 throw new ParserException("Ein nicht vorhandener Identifier '" + identifier + "' wurde referenziert.", token);
567
568 return identifier;
569
570 }
571
575 private void VariableDeclaration()
576 {
577
578 Token token = ReadToken();
579
580 if (token.Type != TokenType.Shared && token.Type != TokenType.Var)
581 throw new ParserException( "Variablen werden mit 'shared' oder 'var' deklariert.", token);
582
583 InsertDebugInfo(token);
584
585 OpCode opcode = OpCode.DB;
586
587 if(token.Type == TokenType.Shared)
588 opcode = OpCode.DSB;
589
590 string identifier = ReadIdentifier();
591
592 while (true)
593 {
594
595 if (variables.ContainsKey(identifier))
596 throw new ParserException( "Die Variable '" + identifier + "' ist bereits vorhanden.", token);
597
598 variables[identifier] = true;
599
600 executable.InstructionsInternal.Add(new Instruction(opcode, Operand.Variable(identifier)));
601
602 if (opcode == OpCode.DSB)
603 script.Manager.SharedMemory[identifier] = NullReference.Instance;
604
605 else
606 executable.ScriptMemory[identifier] = NullReference.Instance;
607
608 token = ReadToken();
609
610 if (token.Type == TokenType.SemiColon)
611 return;
612
613 if (token.Type != TokenType.Comma)
614 throw new ParserException("Comma ',' erwartet.", token);
615
616 identifier = ReadIdentifier();
617
618 }
619
620 }
621
626 {
627
628 Token token = ReadToken();
629
630 if (token.Type != TokenType.Var)
631 throw new ParserException( "Lokale Variablen werden mit 'var' deklariert.", token);
632
633 string identifier = ReadIdentifier();
634
635 while (true)
636 {
637
638 AllocateVariable(identifier);
639
640 token = ReadToken();
641
642 if (token.Type == TokenType.SemiColon)
643 return;
644
645 if (token.Type == TokenType.Assign)
646 {
647
648 UndoToken();
649
650 UndoToken();
651
652 Assignment();
653
654 token = ReadToken();
655
656 if (token.Type == TokenType.SemiColon)
657 return;
658
659 }
660
661 if (token.Type != TokenType.Comma)
662 throw new ParserException( "Comma ',' erwartet.", token);
663
664 identifier = ReadIdentifier();
665
666 }
667
668 }
669
673 private void Run()
674 {
675
676 Token token = ReadToken();
677
678 if (token.Type != TokenType.Run)
679 throw new ParserException("Keyword 'run' erwartet.", token);
680
681 FunctionCall(true);
682
684
685 }
686
693 {
694
695 string identifier = ReadQualifiedIdentifier();
696
698
699 List<object> parameters = new List<object>();
700
701 int parameterCount = 0;
702
703 if (LookAhead().Type != TokenType.RightParen)
704 {
705
706 while (true)
707 {
708
709 Variable parameter = Expression();
710
711 executable.InstructionsInternal.Add(new Instruction( OpCode.PUSH, Operand.Variable(parameter.name)));
712
713 ++parameterCount;
714
715 if (LookAhead().Type == TokenType.RightParen)
716 break;
717
718 else
719 ReadComma();
720
721 }
722
723 }
724
726
727 Manager manager = executable.Script.Manager;
728
729 if (!manager.Routines.ContainsKey(identifier))
730 throw new ParserException("Die Routine '" + identifier + "' ist nicht vorhanden.");
731
732 Routine routine = manager.Routines[identifier];
733
734 if (routine.ParameterTypes.Count > parameterCount)
735 throw new ParserException("Der Aufruf der Routine '" + identifier + "' hat fehlende Parameter. Erwartet werden " + routine.ParameterTypes.Count + " Parameter.\nBeschreibung der Routine: " + routine.Description().ToString());
736
737 if (routine.ParameterTypes.Count < parameterCount)
738 throw new ParserException("Der Aufruf der Routine '" + identifier + "' hat zu viele Parameter. Erwartet werden " + routine.ParameterTypes.Count + " Parameter.\nBeschreibung der Routine: " + routine.Description().ToString());
739
740 executable.InstructionsInternal.Add(new Instruction(OpCode.INV, Operand.AllocateRoutinePointer(routine)));
741
742 Variable variable = new Variable
743 {
745 scope = Scope.Local,
746 derivatedType = null
747 };
748
749 executable.InstructionsInternal.Add(new Instruction(OpCode.POP, Operand.Variable(variable.name)));
750
751 return variable;
752
753 }
754
769 {
770
771 string identifier = ReadIdentifier();
772
773 string tmp = null;
774
775 while (LookAhead().Type == TokenType.LeftBracket)
776 {
777
779
780 Variable index = Expression();
781
783
785
786 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(tmp), Operand.CreatePointer(identifier, index.name)));
787
788 identifier = tmp;
789
790 }
791
792 return new Variable(tmp, Scope.Local, null);
793
794 }
795
800 private Variable Member()
801 {
802
803 string arrayIdentifier = ReadIdentifier();
804
805 string tmp = null;
806
807 while (LookAhead().Type == TokenType.Period)
808 {
809
810 ReadPeriod();
811
812 string member = ReadIdentifier();
813
815
816 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(tmp), Operand.MemberVariable(arrayIdentifier, member)));
817
818 arrayIdentifier = tmp;
819
820 }
821
822 return new Variable(tmp, Scope.Local, null);
823
824 }
825
842 {
843
844 string identifier = ReadIdentifier();
845
846 // We only get here if the next token is '[' or '.'
847 string tmp = null;
848
849 while (true)
850 {
851
852 if (LookAhead().Type == TokenType.LeftBracket)
853 {
854
856
857 Variable index = Expression();
858
860
862
863 executable.InstructionsInternal.Add(
864 new Instruction(OpCode.MOV,
865 Operand.Variable(tmp),
866 Operand.CreatePointer(identifier, index.name)));
867
868 identifier = tmp;
869
870 continue;
871
872 }
873
874 if (LookAhead().Type == TokenType.Period)
875 {
876
877 ReadPeriod();
878
879 string member = ReadIdentifier();
880
881 // Method call on CLR object: obj.Member(...)
882 // Lowered into: PUSH args..., MIV obj.Member, <argc>, POP tmp
883 if (LookAhead().Type == TokenType.LeftParen)
884 {
885
887
888 int parameterCount = 0;
889
890 if (LookAhead().Type != TokenType.RightParen)
891 {
892 while (true)
893 {
894 Variable parameter = Expression();
895 executable.InstructionsInternal.Add(new Instruction(OpCode.PUSH, Operand.Variable(parameter.name)));
896 ++parameterCount;
897
898 if (LookAhead().Type == TokenType.RightParen)
899 break;
900
901 ReadComma();
902 }
903 }
904
906
907 // invoke member
908 executable.InstructionsInternal.Add(
909 new Instruction(OpCode.MIV,
910 Operand.MemberVariable(identifier, member),
911 Operand.Literal(parameterCount)));
912
914 executable.InstructionsInternal.Add(new Instruction(OpCode.POP, Operand.Variable(tmp)));
915
916 identifier = tmp;
917 continue;
918
919 }
920
922
923 executable.InstructionsInternal.Add(
924 new Instruction(OpCode.MOV,
925 Operand.Variable(tmp),
926 Operand.MemberVariable(identifier, member)));
927
928 identifier = tmp;
929
930 continue;
931
932 }
933
934 break;
935
936 }
937
938 return new Variable(identifier, Scope.Local, null);
939
940 }
941
947 {
948
949 Token token = ReadToken();
950
951 if (token.Type != TokenType.Increment)
952 throw new ParserException( "Pre-increment '++' erwartet.", token);
953
954 string identifier = ExpectIdentifier();
955
956 executable.InstructionsInternal.Add(new Instruction(OpCode.INC, Operand.Variable(identifier)));
957
958 return new Variable(identifier, Scope.Local, null);
959
960 }
961
967 {
968
969 Token token = ReadToken();
970
971 if (token.Type != TokenType.Decrement)
972 throw new ParserException("Pre-decrement '--' erwartet.", token);
973
974 string identifier = ExpectIdentifier();
975
976 executable.InstructionsInternal.Add(new Instruction(OpCode.DEC, Operand.Variable(identifier)));
977
978 return new Variable(identifier, Scope.Local, null);
979
980 }
981
987 {
988
989 string identifier = ExpectIdentifier();
990
991 Token token = ReadToken();
992
993 if (token.Type != TokenType.Increment)
994 throw new ParserException("Post-increment '++' erwartet.", token);
995
996 string tmp = AllocateTemporaryVariable();
997
998 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(tmp), Operand.Variable(identifier)));
999
1000 executable.InstructionsInternal.Add(new Instruction(OpCode.INC, Operand.Variable(identifier)));
1001
1002 return new Variable(tmp, Scope.Local, null);
1003
1004 }
1005
1011 {
1012
1013 string identifier = ExpectIdentifier();
1014
1015 Token token = ReadToken();
1016
1017 if (token.Type != TokenType.Decrement)
1018 throw new ParserException("Post-decrement '--' erwartet.", token);
1019
1020 string tmp = AllocateTemporaryVariable();
1021
1022 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(tmp), Operand.Variable(identifier)));
1023
1024 executable.InstructionsInternal.Add(new Instruction(OpCode.DEC, Operand.Variable(identifier)));
1025
1026 return new Variable(tmp, Scope.Local, null);
1027
1028 }
1029
1035 {
1036
1037 string left = ExpectIdentifier();
1038
1039 Token token = ReadToken();
1040
1041 if (token.Type != TokenType.ShiftLeft)
1042 throw new ParserException("Shift Left '<<' erwartet.", token);
1043
1044 Variable right = Factor();
1045
1046 executable.InstructionsInternal.Add(new Instruction(OpCode.SHL, Operand.Variable(left), Operand.Variable(right.name)));
1047
1048 return right;
1049
1050 }
1051
1057 {
1058
1059 string left = ExpectIdentifier();
1060
1061 Token token = ReadToken();
1062
1063 if (token.Type != TokenType.ShiftRight)
1064 throw new ParserException("Shift Right '>>' erwartet.", token);
1065
1066 Variable right = Factor();
1067
1068 executable.InstructionsInternal.Add(new Instruction(OpCode.SHR, Operand.Variable(left), Operand.Variable(right.name)));
1069
1070 return right;
1071
1072 }
1073
1079 {
1080
1081 string left = ExpectIdentifier();
1082
1083 Token token = ReadToken();
1084
1085 if (token.Type != TokenType.AssignBinaryAnd)
1086 throw new ParserException("Binary AND '&=' erwartet.", token);
1087
1088 Variable right = Factor();
1089
1090 executable.InstructionsInternal.Add(new Instruction(OpCode.ANDB, Operand.Variable(left), Operand.Variable(right.name)));
1091
1092 return right;
1093
1094 }
1095
1101 {
1102
1103 string left = ExpectIdentifier();
1104
1105 Token token = ReadToken();
1106
1107 if (token.Type != TokenType.AssignBinaryOr)
1108 throw new ParserException("Binary OR '|=' erwartet.", token);
1109
1110 Variable right = Factor();
1111
1112 executable.InstructionsInternal.Add(new Instruction(OpCode.ORB, Operand.Variable(left), Operand.Variable(right.name)));
1113
1114 return right;
1115
1116 }
1117
1123 {
1124
1125 string left = ExpectIdentifier();
1126
1127 Token token = ReadToken();
1128
1129 if (token.Type != TokenType.AssignBinaryNot)
1130 throw new ParserException("Binary NEG '~=' erwartet.", token);
1131
1132 Variable right = Factor();
1133
1134 executable.InstructionsInternal.Add(new Instruction(OpCode.NOTB, Operand.Variable(left), Operand.Variable(right.name)));
1135
1136 return right;
1137
1138 }
1139
1145 {
1146
1147 Token token = ReadToken();
1148
1149 if (token.Type != TokenType.AssignBinaryNot)
1150 throw new ParserException("Binary NOT '~=' erwartet.", token);
1151
1152 Variable right = Factor();
1153
1154 executable.InstructionsInternal.Add(new Instruction(OpCode.NOTB, Operand.Variable(right.name)));
1155
1156 return right;
1157
1158 }
1159
1164 private Variable Xor()
1165 {
1166
1167 string left = ExpectIdentifier();
1168
1169 Token token = ReadToken();
1170
1171 if (token.Type != TokenType.AssignXor)
1172 throw new ParserException("Binary XOR '~=' erwartet.", token);
1173
1174 Variable right = Factor();
1175
1176 executable.InstructionsInternal.Add(new Instruction(OpCode.XOR, Operand.Variable(left), Operand.Variable(right.name)));
1177
1178 return right;
1179
1180 }
1181
1182 public string ToLiteral(string input)
1183 {
1184 var literal = new StringBuilder(input.Length + 2);
1185 literal.Append("\"");
1186 foreach (var c in input)
1187 {
1188 switch (c)
1189 {
1190 case '\'': literal.Append(@"\'"); break;
1191 case '\"': literal.Append("\\\""); break;
1192 case '\\': literal.Append(@"\\"); break;
1193 case '\0': literal.Append(@"\0"); break;
1194 case '\a': literal.Append(@"\a"); break;
1195 case '\b': literal.Append(@"\b"); break;
1196 case '\f': literal.Append(@"\f"); break;
1197 case '\n': literal.Append(@"\n"); break;
1198 case '\r': literal.Append(@"\r"); break;
1199 case '\t': literal.Append(@"\t"); break;
1200 case '\v': literal.Append(@"\v"); break;
1201 default:
1202 if (char.GetUnicodeCategory(c) != System.Globalization.UnicodeCategory.Control)
1203 {
1204 literal.Append(c);
1205 }
1206 else
1207 {
1208 literal.Append(@"\u");
1209 literal.Append(((ushort)c).ToString("x4"));
1210 }
1211 break;
1212 }
1213 }
1214 literal.Append("\"");
1215 return literal.ToString();
1216 }
1217
1222 private Variable Atom()
1223 {
1224
1225 Token token = ReadToken();
1226
1227 Variable variable = new Variable();
1228
1229 switch (token.Type)
1230 {
1231
1232 case TokenType.Minus:
1233
1234 variable = Atom();
1235
1236 string tmpIdentifier = AllocateTemporaryVariable();
1237
1238 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(tmpIdentifier), Operand.Variable(variable.name)));
1239
1240 executable.InstructionsInternal.Add(new Instruction(OpCode.NEG, Operand.Variable(tmpIdentifier)));
1241
1242 variable.name = tmpIdentifier;
1243
1244 return variable;
1245
1246 case TokenType.Null:
1247
1248 variable.name = AllocateTemporaryVariable();
1249
1250 variable.scope = Scope.Local;
1251
1252 variable.derivatedType = typeof(NullReference);
1253
1254 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(variable.name), Operand.Literal(NullReference.Instance)));
1255
1256 return variable;
1257
1258 case TokenType.Integer:
1259 case TokenType.Float:
1260 case TokenType.Boolean:
1261 case TokenType.String:
1262 case TokenType.Char:
1263 case TokenType.Double:
1264 case TokenType.Decimal:
1265
1266 variable.name = AllocateTemporaryVariable();
1267
1268 variable.scope = Scope.Local;
1269
1270 variable.derivatedType = Literal(token.Type);
1271
1272 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(variable.name), Operand.Literal(token.Lexeme)));
1273
1274 return variable;
1275
1276 case TokenType.Increment:
1277
1278 UndoToken();
1279
1280 variable = PreIncrement();
1281
1282 return variable;
1283
1284 case TokenType.Decrement:
1285
1286 UndoToken();
1287
1288 variable = PreDecrement();
1289
1290 return variable;
1291
1292 case TokenType.Identifier:
1293
1294 string identifier = token.Lexeme.ToString();
1295
1296 switch (LookAhead().Type)
1297 {
1298
1299 case TokenType.Increment:
1300
1301 UndoToken();
1302
1303 return PostIncrement();
1304
1305 case TokenType.Decrement:
1306
1307 UndoToken();
1308
1309 return PostDecrement();
1310
1311 case TokenType.LeftBracket:
1312
1313 // Support mixed access chains like obj.pets[0].name
1314 // by parsing consecutive member (.) and index ([...]) postfixes.
1315 UndoToken();
1316
1317 return AccessChain();
1318
1319 case TokenType.Period:
1320
1321 // Ambiguity: "std.print()" could be either a member-call on an object "std"
1322 // or a qualified routine name.
1323 //
1324 // Rule: If the dotted name is followed by '(' AND the manager has a registered
1325 // routine with that qualified name, treat it as a routine/function call.
1326 // Otherwise parse it as an access chain (supports obj.method(...)).
1327 {
1328
1329 int i = nextToken;
1330 string qualified = identifier;
1331 bool hasDot = false;
1332
1333 while (i < tokenStream.Count && tokenStream[i].Type == TokenType.Period)
1334 {
1335
1336 if (i + 1 >= tokenStream.Count || tokenStream[i + 1].Type != TokenType.Identifier)
1337 break;
1338
1339 hasDot = true;
1340 qualified += "." + tokenStream[i + 1].Lexeme.ToString();
1341 i += 2;
1342
1343 }
1344
1345 bool looksLikeQualifiedCall = hasDot && i < tokenStream.Count && tokenStream[i].Type == TokenType.LeftParen;
1346
1347 if (looksLikeQualifiedCall && executable.Script.Manager.IsRegistered(qualified))
1348 {
1349 UndoToken();
1350 return FunctionCall();
1351 }
1352
1353 UndoToken();
1354 return AccessChain();
1355
1356 }
1357
1358 case TokenType.LeftParen:
1359
1360 UndoToken();
1361
1362 return FunctionCall();
1363
1364 case TokenType.ShiftLeft:
1365
1366 UndoToken();
1367
1368 return ShiftLeft();
1369
1370 case TokenType.ShiftRight:
1371
1372 UndoToken();
1373
1374 return ShiftRight();
1375
1376 default:
1377
1378 UndoToken();
1379
1380 identifier = ExpectIdentifier();
1381
1382 variable.name = AllocateTemporaryVariable();
1383
1384 variable.scope = Scope.Local;
1385
1386 variable.derivatedType = null;
1387
1388 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(variable.name), Operand.Variable(identifier)));
1389
1390 return variable;
1391
1392 }
1393
1394 case TokenType.LeftParen:
1395
1396 variable = Expression();
1397
1399
1400 return variable;
1401
1402 default:
1403
1404 throw new ParserException( "Fehlerhafter Token '" + token + "'.", token);
1405
1406 }
1407
1408 }
1409
1415 {
1416
1417 ReadLeftBrace();
1418
1419 int index = 0;
1420
1421 string identifier = AllocateTemporaryVariable();
1422
1423 string indexIdentifier = AllocateTemporaryVariable();
1424
1425 executable.InstructionsInternal.Add(new Instruction(OpCode.DC, Operand.Variable(identifier)));
1426
1427 if (LookAhead().Type != TokenType.RightBrace)
1428 {
1429
1430 while (true)
1431 {
1432
1433 Variable tmp = Expression();
1434
1435 Token token = LookAhead();
1436
1437 if (token.Type == TokenType.Comma || token.Type == TokenType.SemiColon || token.Type == TokenType.RightBrace)
1438 {
1439
1440 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.MemberVariable(identifier, index++), Operand.Variable(tmp.name)));
1441
1442 if (token.Type == TokenType.RightBrace)
1443 break;
1444
1445 /* todo: could be comma or semicolon, depends on current cultureinfo */
1446 if (token.Type == TokenType.Comma)
1447 ReadComma();
1448
1449 if (token.Type == TokenType.SemiColon)
1450 ReadSemicolon();
1451
1452 }
1453
1454 else if (token.Type == TokenType.Colon)
1455 {
1456
1457 ReadToken();
1458
1459 Variable variableKey = tmp;
1460
1461 tmp = Expression();
1462
1463 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.CreatePointer(identifier, variableKey.name), Operand.Variable(tmp.name)));
1464
1465 if (LookAhead().Type == TokenType.RightBrace)
1466 break;
1467
1468 /* todo: could be comma or semicolon, depends on current cultureinfo */
1469 if (LookAhead().Type == TokenType.Comma)
1470 ReadComma();
1471
1472 if (LookAhead().Type == TokenType.SemiColon)
1473 ReadSemicolon();
1474
1475 }
1476
1477 else
1478 throw new ParserException( "Ein Comma ',', Semicolon ';' oder Colon ':' wurde erwartet.");
1479
1480 }
1481
1482 }
1483
1485
1486 return new Variable(identifier, Scope.Local, typeof(ArrayList));
1487
1488 }
1489
1495 {
1496
1498
1499 int index = 0;
1500
1501 string identifier = AllocateTemporaryVariable();
1502
1503 string indexIdentifier = AllocateTemporaryVariable();
1504
1505 executable.InstructionsInternal.Add(new Instruction(OpCode.DC, Operand.Variable(identifier)));
1506
1507 if (LookAhead().Type != TokenType.RightBracket)
1508 {
1509
1510 while (true)
1511 {
1512
1513 Variable tmp = Expression();
1514
1515 Token token = LookAhead();
1516
1517 if (token.Type == TokenType.Comma || token.Type == TokenType.RightBracket)
1518 {
1519
1520 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.MemberVariable(identifier, index++), Operand.Variable(tmp.name)));
1521
1522 if (token.Type == TokenType.RightBracket)
1523 break;
1524
1525 ReadComma();
1526
1527 }
1528
1529 else if (token.Type == TokenType.Colon)
1530 {
1531
1532 ReadToken();
1533
1534 Variable key = tmp;
1535
1536 tmp = Expression();
1537
1538 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.CreatePointer(identifier, key.name), Operand.Variable(tmp.name)));
1539
1540 if(LookAhead().Type == TokenType.RightBracket)
1541 break;
1542
1543 ReadComma();
1544
1545 }
1546
1547 else
1548 throw new ParserException( "Ein Comma ',' oder Colon ':' wurde erwartet.");
1549
1550 }
1551
1552 }
1553
1555
1556 return new Variable(identifier, Scope.Local, typeof(ArrayList));
1557
1558 }
1559
1565 {
1566
1567
1568 if (LookAhead().Type == TokenType.LeftBrace)
1569 return BraceArray();
1570
1571 // test
1572 if (LookAhead().Type == TokenType.LeftBracket)
1573 return BracketArray();
1574
1575 // unary bitwise NOT: "~x"
1576 if (LookAhead().Type == TokenType.BinaryNot)
1577 {
1578 ReadToken(); // "~"
1579
1580 // binds tight and is right-associative: "~~x" etc.
1581 Variable right = Factor();
1582
1583 string tmp = AllocateTemporaryVariable();
1584
1585 executable.InstructionsInternal.Add(new Instruction(
1586 OpCode.NOTB,
1587 Operand.Variable(tmp),
1588 Operand.Variable(right.name)
1589 ));
1590
1591 // type: same as right, but practically int for bitwise
1592 Variable result = new Variable(tmp, Scope.Local, typeof(int));
1593 result.derivatedType = typeof(int);
1594
1595 return result;
1596 }
1597
1598 Variable variable = Atom();
1599
1600 return variable;
1601
1602 }
1603
1608 private Variable Term()
1609 {
1610
1611 List<Instruction> listInstructions = executable.InstructionsInternal;
1612
1613 Variable first = Factor();
1614
1615 Variable second = new Variable();
1616
1617 while (true)
1618 {
1619
1620 Token token = ReadToken();
1621
1622 switch (token.Type)
1623 {
1624
1625 case TokenType.Multiply:
1626 second = Factor();
1627
1628 listInstructions.Add(new Instruction(OpCode.MUL, Operand.Variable(first.name), Operand.Variable(second.name)));
1629
1630 first.derivatedType = Derivate(token, first.derivatedType, second.derivatedType);
1631
1632 break;
1633
1634 case TokenType.Divide:
1635
1636 second = Factor();
1637
1638 listInstructions.Add(new Instruction(OpCode.DIV, Operand.Variable(first.name), Operand.Variable(second.name)));
1639
1640 first.derivatedType = Derivate(token, first.derivatedType, second.derivatedType);
1641
1642 break;
1643
1644 case TokenType.Modulo:
1645 second = Factor();
1646
1647 listInstructions.Add(new Instruction(OpCode.MOD, Operand.Variable(first.name), Operand.Variable(second.name)));
1648
1649 first.derivatedType = Derivate(token, first.derivatedType, second.derivatedType);
1650
1651 break;
1652
1653 default:
1654 UndoToken();
1655 return first;
1656
1657 }
1658
1659 }
1660
1661 }
1662
1670 {
1671
1672 List<Instruction> listInstructions = executable.InstructionsInternal;
1673
1674 Variable first = Term();
1675
1676 Variable second = new Variable();
1677
1678 while(true)
1679 {
1680
1681 Token token = ReadToken();
1682
1683 switch (token.Type)
1684 {
1685
1686 case TokenType.Plus:
1687
1688 second = Term();
1689
1690 listInstructions.Add(new Instruction(OpCode.ADD, Operand.Variable(first.name), Operand.Variable(second.name)));
1691
1692 first.derivatedType = Derivate(token, first.derivatedType, second.derivatedType);
1693
1694 break;
1695
1696 case TokenType.Minus:
1697
1698 second = Term();
1699
1700 listInstructions.Add(new Instruction(OpCode.SUB, Operand.Variable(first.name), Operand.Variable(second.name)));
1701
1702 break;
1703
1704 default:
1705
1706 UndoToken();
1707
1708 return first;
1709
1710 }
1711
1712 }
1713
1714 }
1715
1721 {
1722
1723 List<Instruction> instructions = executable.InstructionsInternal;
1724
1725 Variable first = Arithmetic();
1726
1727 Token token = ReadToken();
1728
1729 if (RelationalOperator(token.Type)) {
1730
1731 Variable second = Arithmetic();
1732
1733 instructions.Add(new Instruction(RelationalOpcode(token.Type), Operand.Variable(first.name), Operand.Variable(second.name)));
1734
1735 first.derivatedType = Derivate(token, first.derivatedType, second.derivatedType);
1736
1737 }
1738
1739 else
1740 UndoToken();
1741
1742 return first;
1743
1744 }
1745
1752 private Variable Not()
1753 {
1754
1755 Variable proposition = new Variable();
1756
1757 if (LookAhead().Type == TokenType.Not)
1758 {
1759
1760 ReadToken();
1761
1762 if (LookAhead().Type == TokenType.LeftParen)
1763 proposition = Expression();
1764
1765 else
1766 proposition = Relation();
1767
1768 executable.InstructionsInternal.Add(new Instruction(OpCode.NOT, Operand.Variable(proposition.name)));
1769
1770 return proposition;
1771
1772 }
1773
1774 else
1775 return Relation();
1776
1777 }
1778
1793 {
1794
1795 List<Instruction> instructions = executable.InstructionsInternal;
1796
1797 Variable first = Not();
1798
1799 while (true)
1800 {
1801
1802 Token token = ReadToken();
1803
1804 if (token.Type == TokenType.BinaryAnd)
1805 {
1806
1807 Variable second = Not();
1808
1809 instructions.Add(new Instruction(OpCode.ANDB, Operand.Variable(first.name), Operand.Variable(second.name)));
1810
1811 // Bitwise ops currently work on int in the runtime
1812 first.derivatedType = typeof(int);
1813
1814 continue;
1815
1816 }
1817
1818 UndoToken();
1819 return first;
1820
1821 }
1822
1823 }
1824
1829 {
1830
1831 List<Instruction> instructions = executable.InstructionsInternal;
1832
1833 Variable first = BitwiseAnd();
1834
1835 while (true)
1836 {
1837
1838 Token token = ReadToken();
1839
1840 if (token.Type == TokenType.Xor)
1841 {
1842
1843 Variable second = BitwiseAnd();
1844
1845 instructions.Add(new Instruction(OpCode.XOR, Operand.Variable(first.name), Operand.Variable(second.name)));
1846
1847 first.derivatedType = typeof(int);
1848
1849 continue;
1850
1851 }
1852
1853 UndoToken();
1854 return first;
1855
1856 }
1857
1858 }
1859
1864 {
1865
1866 List<Instruction> instructions = executable.InstructionsInternal;
1867
1868 Variable first = BitwiseXor();
1869
1870 while (true)
1871 {
1872
1873 Token token = ReadToken();
1874
1875 if (token.Type == TokenType.BinaryOr)
1876 {
1877
1878 Variable second = BitwiseXor();
1879
1880 instructions.Add(new Instruction(OpCode.ORB, Operand.Variable(first.name), Operand.Variable(second.name)));
1881
1882 first.derivatedType = typeof(int);
1883
1884 continue;
1885
1886 }
1887
1888 UndoToken();
1889 return first;
1890
1891 }
1892
1893 }
1894
1901 private Variable And()
1902 {
1903
1904 List<Instruction> instructions = executable.InstructionsInternal;
1905
1906 Variable first = BitwiseOr();
1907
1908 while (true)
1909 {
1910
1911 Token token = ReadToken();
1912
1913 if (token.Type == TokenType.And)
1914 {
1915
1916 Variable second = BitwiseOr();
1917
1918 instructions.Add(new Instruction(OpCode.AND, Operand.Variable(first.name), Operand.Variable(second.name)));
1919
1920 first.derivatedType = Derivate(token, first.derivatedType, second.derivatedType);
1921
1922 continue;
1923
1924 }
1925
1926 UndoToken();
1927 return first;
1928
1929 }
1930
1931 }
1932
1939 private Variable Or()
1940 {
1941
1942 List<Instruction> instructions = executable.InstructionsInternal;
1943
1944 Variable first = And();
1945
1946 while (true)
1947 {
1948
1949 Token token = ReadToken();
1950
1951 if (token.Type == TokenType.Or)
1952 {
1953
1954 Variable second = And();
1955
1956 instructions.Add(new Instruction(OpCode.OR, Operand.Variable(first.name), Operand.Variable(second.name)));
1957
1958 first.derivatedType = Derivate(token, first.derivatedType, second.derivatedType);
1959
1960 continue;
1961
1962 }
1963
1964 UndoToken();
1965 return first;
1966
1967 }
1968
1969 }
1970
1976 {
1977
1978 string identifier = ExpectIdentifier();
1979
1980 Token token = ReadToken();
1981
1982 if (!AssignmentOperator(token.Type))
1983 throw new ParserException("Ein Zuweisungsoperator wurde erwartet erwartet.", token);
1984
1985 Variable expression = Expression();
1986
1987 executable.InstructionsInternal.Add(new Instruction(AssignmentOpcode(token.Type), Operand.Variable(identifier), Operand.Variable(expression.name)));
1988
1989 string tmpIdentifier = AllocateTemporaryVariable();
1990
1991 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(tmpIdentifier), Operand.Variable(identifier)));
1992
1993 return new Variable(tmpIdentifier, Scope.Local, expression.derivatedType);
1994
1995 }
1996
1997 private sealed class AccessSegment
1998 {
1999 public string MemberName;
2000 public string IndexVar;
2001 public bool IsMember;
2002
2003 public static AccessSegment Member(string name) => new AccessSegment { IsMember = true, MemberName = name };
2004 public static AccessSegment Index(string indexVar) => new AccessSegment { IsMember = false, IndexVar = indexVar };
2005 }
2006
2023 {
2024
2025 string identifier = ExpectIdentifier();
2026
2027 List<Instruction> listInstructions = executable.InstructionsInternal;
2028
2029 // Parse postfix chain (.) and ([]) until we hit an assignment operator.
2030 List<AccessSegment> segments = new List<AccessSegment>();
2031
2032 while (true)
2033 {
2034 if (LookAhead().Type == TokenType.Period)
2035 {
2036 ReadPeriod();
2037 segments.Add(AccessSegment.Member(ReadIdentifier()));
2038 continue;
2039 }
2040
2041 if (LookAhead().Type == TokenType.LeftBracket)
2042 {
2044 Variable idx = Expression();
2046 segments.Add(AccessSegment.Index(idx.name));
2047 continue;
2048 }
2049
2050 break;
2051 }
2052
2053 Token tok = ReadToken();
2054
2055 if (!AssignmentOperator(tok.Type))
2056 throw new ParserException("Ein Zuweisungsoperator wurde erwartet.", tok);
2057
2058 Variable expression = Expression();
2059
2060 // Lower intermediate access steps into temporaries.
2061 string current = identifier;
2062
2063 for (int i = 0; i < segments.Count - 1; i++)
2064 {
2065 string tmp = AllocateTemporaryVariable();
2066 var seg = segments[i];
2067
2068 Operand rhs = seg.IsMember
2069 ? Operand.MemberVariable(current, seg.MemberName)
2070 : Operand.CreatePointer(current, seg.IndexVar);
2071
2072 listInstructions.Add(new Instruction(OpCode.MOV, Operand.Variable(tmp), rhs));
2073 current = tmp;
2074 }
2075
2076 // Final destination operand (last segment) or plain variable.
2077 Operand dest;
2078
2079 if (segments.Count == 0)
2080 {
2081 dest = Operand.Variable(current);
2082 }
2083 else
2084 {
2085 var last = segments[segments.Count - 1];
2086 dest = last.IsMember
2087 ? Operand.MemberVariable(current, last.MemberName)
2088 : Operand.CreatePointer(current, last.IndexVar);
2089 }
2090
2091 listInstructions.Add(new Instruction(AssignmentOpcode(tok.Type), dest, Operand.Variable(expression.name)));
2092
2093 // Return value of assignment expression
2094 string tmpIdentifier = AllocateTemporaryVariable();
2095 listInstructions.Add(new Instruction(OpCode.MOV, Operand.Variable(tmpIdentifier), dest));
2096
2097 return new Variable(tmpIdentifier, Scope.Local, expression.derivatedType);
2098
2099 }
2100
2106 {
2107
2108 string identifier = ExpectIdentifier();
2109
2110 List<Instruction> listInstructions = executable.InstructionsInternal;
2111
2112 Variable tmp = new Variable();
2113
2114 string src = identifier;
2115
2116 string dst = null;
2117
2118 while (!AssignmentOperator(LookAhead().Type))
2119 {
2120
2122
2123 tmp = Expression();
2124
2126
2127 if (!AssignmentOperator(LookAhead().Type))
2128 {
2129
2131
2132 listInstructions.Add(new Instruction(OpCode.MOV, Operand.Variable(dst), Operand.CreatePointer(src, tmp.name)));
2133
2134 src = dst;
2135
2136 }
2137
2138 }
2139
2140 Token tok = ReadToken();
2141
2142 Variable expression = Expression();
2143
2144 if (dst == null)
2145 dst = identifier;
2146
2147 listInstructions.Add(new Instruction(AssignmentOpcode(tok.Type), Operand.CreatePointer(dst, tmp.name), Operand.Variable(expression.name)));
2148
2149 string tmpIdentifier = AllocateTemporaryVariable();
2150
2151 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(tmpIdentifier), Operand.CreatePointer(dst, tmp.name)));
2152
2153 return new Variable(tmpIdentifier, Scope.Local, expression.derivatedType);
2154
2155 }
2156
2162 {
2163
2164 string identifier = ExpectIdentifier();
2165
2166 List<string> members = new List<string>();
2167
2168 while (LookAhead().Type == TokenType.Period)
2169 {
2170
2171 ReadPeriod();
2172
2173 members.Add(ReadIdentifier());
2174
2175 }
2176
2177 Token tok = ReadToken();
2178
2179 if (!AssignmentOperator(tok.Type))
2180 throw new ParserException("Ein Zuweisungsoperator wurde erwartet.", tok);
2181
2182 Variable expression = Expression();
2183
2184 string dst = null;
2185
2186 string src = identifier;
2187
2188 for (int i = 0; i < members.Count - 1; i++)
2189 {
2190
2192
2193 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(dst), Operand.MemberVariable(src, members[i])));
2194
2195 src = dst;
2196
2197 }
2198
2199 if (dst == null)
2200 dst = identifier;
2201
2202 executable.InstructionsInternal.Add(new Instruction(AssignmentOpcode(tok.Type), Operand.MemberVariable(dst, members[members.Count - 1]), Operand.Variable(expression.name)));
2203
2204 string tmpIdentifier = AllocateTemporaryVariable();
2205
2206 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(tmpIdentifier), Operand.MemberVariable(dst, members[members.Count - 1])));
2207
2208 Variable variable = new Variable(tmpIdentifier, Scope.Local, expression.derivatedType);
2209
2210 return variable;
2211
2212 }
2213
2219 {
2220 // Unified assignment parser (supports Variable, Pointer, Member and mixed chains)
2221 return AccessChainAssignment();
2222 }
2223
2230 {
2231
2232 Token start = LookAhead();
2233
2234 if (start.Type != TokenType.Identifier)
2235 return false;
2236
2237 int iInstructionCheckpoint = executable.InstructionsInternal.Count;
2238
2239 // base identifier
2241
2242 while (true)
2243 {
2244 if (LookAhead().Type == TokenType.LeftBracket)
2245 {
2247 Expression();
2249 continue;
2250 }
2251
2252 if (LookAhead().Type == TokenType.Period)
2253 {
2254 ReadPeriod();
2255
2256 Token token = ReadToken();
2257
2258 if (token.Type != TokenType.Identifier)
2259 {
2260 while (LookAhead() != start)
2261 UndoToken();
2262
2263 executable.InstructionsInternal.RemoveRange(
2264 iInstructionCheckpoint,
2265 executable.InstructionsInternal.Count - iInstructionCheckpoint);
2266
2267 return false;
2268 }
2269
2270 continue;
2271 }
2272
2273 break;
2274 }
2275
2276 Token tok = ReadToken();
2277
2278 while (LookAhead() != start)
2279 UndoToken();
2280
2281 executable.InstructionsInternal.RemoveRange(
2282 iInstructionCheckpoint,
2283 executable.InstructionsInternal.Count - iInstructionCheckpoint);
2284
2285 return AssignmentOperator(tok.Type);
2286
2287 }
2288
2293 private bool IsPointer()
2294 {
2295
2296 Token start = LookAhead();
2297
2298 if (start.Type != TokenType.Identifier)
2299 return false;
2300
2301 int iInstructionCheckpoint = executable.InstructionsInternal.Count;
2302
2303 string identifier = ReadIdentifier();
2304
2305 while (LookAhead().Type == TokenType.LeftBracket)
2306 {
2307
2309 Expression();
2311
2312 }
2313
2314 Token tok = ReadToken();
2315
2316 while (LookAhead() != start)
2317 UndoToken();
2318
2319 executable.InstructionsInternal.RemoveRange(iInstructionCheckpoint, executable.InstructionsInternal.Count - iInstructionCheckpoint);
2320
2321 return AssignmentOperator(tok.Type);
2322
2323 }
2324
2329 private bool IsMember()
2330 {
2331
2332 Token start = LookAhead();
2333
2334 if (start.Type != TokenType.Identifier)
2335 return false;
2336
2337 string identifier = ReadIdentifier();
2338
2339 while (LookAhead().Type == TokenType.Period)
2340 {
2341
2342 ReadPeriod();
2343
2344 Token token = ReadToken();
2345
2346 if (token.Type != TokenType.Identifier)
2347 {
2348
2349 while (LookAhead() != start)
2350 UndoToken();
2351
2352 return false;
2353
2354 }
2355
2356 }
2357
2358 Token tok = ReadToken();
2359
2360 while (LookAhead() != start)
2361 UndoToken();
2362
2363 return AssignmentOperator(tok.Type);
2364
2365 }
2366
2372 {
2373
2375 return Assignment();
2376
2377 else
2378 return Or();
2379
2380 }
2381
2385 private void If()
2386 {
2387
2388 Token token = ReadToken();
2389
2390 if (token.Type != TokenType.If)
2391 throw new ParserException( "Keyword 'if' erwartet.", token);
2392
2394
2395 Variable condition = Expression();
2396
2398
2399 Instruction start = new Instruction(OpCode.NOP);
2400
2401 Instruction end = new Instruction(OpCode.NOP);
2402
2403 executable.InstructionsInternal.Add(new Instruction(OpCode.JZ, Operand.Variable(condition.name), Operand.AllocateInstructionPointer(start)));
2404
2405 StatementList();
2406
2407 executable.InstructionsInternal.Add(new Instruction(OpCode.JMP, Operand.AllocateInstructionPointer(end)));
2408
2409 executable.InstructionsInternal.Add(start);
2410
2411 if (LookAhead().Type == TokenType.Else)
2412 {
2413
2414 ReadToken();
2415
2416 StatementList();
2417 }
2418
2419 executable.InstructionsInternal.Add(end);
2420
2421 }
2422
2426 private void While()
2427 {
2428
2429 Token token = ReadToken();
2430
2431 if (token.Type != TokenType.While)
2432 throw new ParserException("Keyword 'while' erwartet.", token);
2433
2434 Instruction start = new Instruction(OpCode.NOP);
2435
2436 executable.InstructionsInternal.Add(start);
2437
2439
2440 Variable condition = Expression();
2441
2442 if (condition.derivatedType != null && condition.derivatedType != typeof(bool))
2443 throw new ParserException("In While Loops wird ein logischer Ausdruck erwartet.", token);
2444
2446
2447 Instruction end = new Instruction(OpCode.NOP);
2448
2449 executable.InstructionsInternal.Add(new Instruction(OpCode.JZ, Operand.Variable(condition.name), Operand.AllocateInstructionPointer(end)));
2450
2452
2453 loopControl.Break = end;
2454
2455 loopControl.Continue = start;
2456
2457 this.loopControl.Push(loopControl);
2458
2459 StatementList();
2460
2461 this.loopControl.Pop();
2462
2463 executable.InstructionsInternal.Add(new Instruction(OpCode.JMP, Operand.AllocateInstructionPointer(start)));
2464
2465 executable.InstructionsInternal.Add(end);
2466
2467 }
2468
2472 private void For()
2473 {
2474
2475 Token token = ReadToken();
2476
2477 if (token.Type != TokenType.For)
2478 throw new ParserException("Keyword 'for' erwartet.", token);
2479
2481
2482 if (LookAhead().Type == TokenType.SemiColon)
2483 ReadSemicolon();
2484
2485 else if (LookAhead().Type == TokenType.Var)
2487
2488 else
2489 {
2490
2491 Assignment();
2492
2493 ReadSemicolon();
2494
2495 }
2496
2497 Instruction start = new Instruction(OpCode.NOP);
2498
2499 executable.InstructionsInternal.Add(start);
2500
2501 Instruction continueInstruction = new Instruction(OpCode.NOP);
2502
2503 Variable condition = new Variable();
2504
2505 if (LookAhead().Type == TokenType.SemiColon)
2506 {
2507
2508 condition = new Variable(AllocateTemporaryVariable(), Scope.Local, typeof(bool));
2509
2510 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(condition.name), Operand.Literal(true)));
2511
2512 ReadSemicolon();
2513
2514 }
2515
2516 else
2517 {
2518
2519 condition = Expression();
2520
2521 if (condition.derivatedType != null && condition.derivatedType != typeof(bool))
2522 throw new ParserException("In For Loops wird ein logischer Ausdruck oder 'null' erwartet.", token);
2523
2524 ReadSemicolon();
2525
2526 }
2527
2528 List<Instruction> expression = null;
2529
2530 if (LookAhead().Type != TokenType.RightParen)
2531 {
2532
2533 int loopStart = executable.InstructionsInternal.Count;
2534
2535 Expression();
2536
2537 int loopCount = executable.InstructionsInternal.Count - loopStart;
2538
2539 expression = executable.InstructionsInternal.GetRange(loopStart, loopCount);
2540
2541 executable.InstructionsInternal.RemoveRange(loopStart, loopCount);
2542
2543 }
2544
2545 else
2546 expression = new List<Instruction>();
2547
2549
2550 Instruction end = new Instruction(OpCode.NOP);
2551
2552 executable.InstructionsInternal.Add(new Instruction(OpCode.JZ, Operand.Variable(condition.name), Operand.AllocateInstructionPointer(end)));
2553
2555
2556 loopControl.Break = end;
2557
2558 loopControl.Continue = continueInstruction;
2559
2560 this.loopControl.Push(loopControl);
2561
2562 StatementList();
2563
2564 this.loopControl.Pop();
2565
2566 executable.InstructionsInternal.Add(continueInstruction);
2567
2568 executable.InstructionsInternal.AddRange(expression);
2569
2570 executable.InstructionsInternal.Add(new Instruction(OpCode.JMP, Operand.AllocateInstructionPointer(start)));
2571
2572 executable.InstructionsInternal.Add(end);
2573
2574 }
2575
2579 private void ForEach()
2580 {
2581
2582 Token token = ReadToken();
2583
2584 if (token.Type != TokenType.Foreach)
2585 throw new ParserException("Keyword 'foreach' erwartet.", token);
2586
2588
2589 string key = null;
2590
2591 string val = ExpectIdentifier();
2592
2593 token = ReadToken();
2594
2595 if (token.Type == TokenType.Comma)
2596 {
2597
2598 key = val;
2599
2600 val = ExpectIdentifier();
2601
2602 token = ReadToken();
2603
2604 }
2605
2606 if (token.Type != TokenType.In)
2607 throw new ParserException("Keyword 'in' erwartet.", token);
2608
2609 if (key == null)
2611
2612 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(key), Operand.Literal(NullReference.Instance)));
2613
2614 Instruction start = new Instruction(OpCode.NOP);
2615
2616 executable.InstructionsInternal.Add(start);
2617
2618 Variable array = Expression();
2619
2620 if (array.derivatedType != null
2621 && array.derivatedType != typeof(ArrayList)
2622 && array.derivatedType != typeof(string)
2623 && !typeof(System.Collections.IEnumerable).IsAssignableFrom(array.derivatedType))
2624 throw new ParserException("In ForEach Loops wird ein iterierbarer Ausdruck erwartet (Array/List/String/IEnumerable).", token);
2625
2627
2628 executable.InstructionsInternal.Add(new Instruction(OpCode.PTR, Operand.Variable(key), Operand.Variable(array.name)));
2629
2630 string identifier = AllocateTemporaryVariable();
2631
2632 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(identifier), Operand.Variable(key)));
2633
2634 //executable.InstructionsInternal.Add(new Instruction(OpCode.CEQ, Operand.Variable(identifier), Operand.Literal(true)));
2635 executable.InstructionsInternal.Add(new Instruction(OpCode.TEST, Operand.Variable(identifier)));
2636
2637 Instruction end = new Instruction(OpCode.NOP);
2638
2639 executable.InstructionsInternal.Add(new Instruction(OpCode.JNZ, Operand.Variable(identifier), Operand.AllocateInstructionPointer(end)));
2640
2641 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(val), Operand.CreatePointer(array.name, key)));
2642
2644
2645 loopControl.Break = end;
2646
2647 loopControl.Continue = start;
2648
2649 this.loopControl.Push(loopControl);
2650
2651 StatementList();
2652
2653 this.loopControl.Pop();
2654
2655 executable.InstructionsInternal.Add(new Instruction(OpCode.JMP, Operand.AllocateInstructionPointer(start)));
2656
2657 executable.InstructionsInternal.Add(end);
2658
2659 }
2660
2664 private void Break()
2665 {
2666
2667 Token token = ReadToken();
2668
2669 if (token.Type != TokenType.Break)
2670 throw new ParserException("Keyword 'break' erwartet.", token);
2671
2672 ReadSemicolon();
2673
2674 if (loopControl.Count == 0)
2675 throw new ParserException("Das Keyword 'break' kann nur innerhalb von Loops verwendet werden.", token);
2676
2677 Instruction breakInstruction = loopControl.Peek().Break;
2678
2679 executable.InstructionsInternal.Add(new Instruction(OpCode.JMP, Operand.AllocateInstructionPointer(breakInstruction)));
2680
2681 }
2682
2686 private void Continue()
2687 {
2688
2689 Token token = ReadToken();
2690
2691 if (token.Type != TokenType.Continue)
2692 throw new ParserException("Keyword 'continue' erwartet.", token);
2693
2694 ReadSemicolon();
2695
2696 if (loopControl.Count == 0)
2697 throw new ParserException("Das Keyword 'continue' kann nur innerhalb von Loops verwendet werden.", token);
2698
2699 Instruction continueInstruction = loopControl.Peek().Continue;
2700
2701 executable.InstructionsInternal.Add(new Instruction(OpCode.JMP, Operand.AllocateInstructionPointer(continueInstruction)));
2702
2703 }
2704
2708 private void Switch()
2709 {
2710
2711 Token token = ReadToken();
2712
2713 if (token.Type != TokenType.Switch)
2714 throw new ParserException("Keyword 'switch' erwartet.", token);
2715
2717
2718 string switchIdentifier = ExpectIdentifier();
2719
2721
2722 ReadLeftBrace();
2723
2724 token = LookAhead();
2725
2726 if (token.Type != TokenType.Case && token.Type != TokenType.Default)
2727 throw new ParserException("Keyword 'case' oder 'default' erwartet.", token);
2728
2729 string tmpIdentifier = AllocateTemporaryVariable();
2730
2731 string identifier = AllocateTemporaryVariable();
2732
2733 Instruction end = new Instruction(OpCode.NOP);
2734
2735 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(tmpIdentifier), Operand.Literal(false)));
2736
2737 while (LookAhead().Type != TokenType.Default && LookAhead().Type != TokenType.RightBrace)
2738 {
2739
2740 token = ReadToken();
2741
2742 if (token.Type != TokenType.Case)
2743 throw new ParserException("Keyword 'case' erwartet.", token);
2744
2745 InsertDebugInfo(token);
2746
2747 Variable expression = Expression();
2748
2749 token = ReadToken();
2750
2751 if (token.Type != TokenType.Colon)
2752 throw new ParserException("Colon ':' erwartet.", token);
2753
2754 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(identifier), Operand.Variable(switchIdentifier)));
2755
2756 executable.InstructionsInternal.Add(new Instruction(OpCode.CEQ, Operand.Variable(identifier), Operand.Variable(expression.name)));
2757
2758 executable.InstructionsInternal.Add(new Instruction(OpCode.OR, Operand.Variable(tmpIdentifier), Operand.Variable(identifier)));
2759
2760 if (LookAhead().Type != TokenType.Case)
2761 {
2762 Instruction switchInstruction = new Instruction(OpCode.NOP);
2763
2764 executable.InstructionsInternal.Add(new Instruction(OpCode.JZ, Operand.Variable(tmpIdentifier), Operand.AllocateInstructionPointer(switchInstruction)));
2765
2766 Statement();
2767
2768 executable.InstructionsInternal.Add(new Instruction(OpCode.JMP, Operand.AllocateInstructionPointer(end)));
2769
2770 executable.InstructionsInternal.Add(switchInstruction);
2771
2772 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(tmpIdentifier), Operand.Literal(false)));
2773
2774 }
2775 }
2776
2777 token = ReadToken();
2778
2779 if (token.Type == TokenType.RightBrace)
2780 {
2781
2782 executable.InstructionsInternal.Add(end);
2783
2784 return;
2785
2786 }
2787
2788 if (token.Type != TokenType.Default)
2789 throw new ParserException("Das Keyword 'default' oder eine schliessende geschwungene Klammer '}' wird am Ende einer 'switch' Anweisung erwartet.", token);
2790
2791 token = ReadToken();
2792
2793 if (token.Type != TokenType.Colon)
2794 throw new ParserException("Ein Colon ':' wurde erwartet.", token);
2795
2796 Statement();
2797
2799
2800 executable.InstructionsInternal.Add(end);
2801
2802 }
2803
2807 private void Return()
2808 {
2809
2810 Token token = ReadToken();
2811
2812 if (token.Type != TokenType.Return)
2813 throw new ParserException("Keyword 'return' erwartet.", token);
2814
2815 if (LookAhead().Type != TokenType.SemiColon)
2816 executable.InstructionsInternal.Add(new Instruction(OpCode.PUSH, Operand.Variable(Expression().name)));
2817
2818 else
2819 executable.InstructionsInternal.Add(new Instruction(OpCode.PUSH, Operand.Literal(NullReference.Instance)));
2820
2821 ReadSemicolon();
2822
2823 executable.InstructionsInternal.Add(new Instruction(OpCode.RET));
2824
2825 return;
2826
2827 }
2828
2832 private void FunctionDeclaration()
2833 {
2834
2835 Token token = ReadToken();
2836
2837 if (token.Type != TokenType.Function)
2838 throw new ParserException("Funktion werden mit 'function' deklariert.", token);
2839
2840 string functionName = ReadIdentifier();
2841
2842 if (executable.Functions.ContainsKey(functionName))
2843 throw new ParserException("Die Funktion '" + functionName + "' ist bereits vorhanden.", token);
2844
2846
2847 InsertDebugInfo(token);
2848
2849 List<string> parameters = new List<string>();
2850
2851 if (LookAhead().Type != TokenType.RightParen)
2852 {
2853
2854 while (true)
2855 {
2856
2857 token = ReadToken();
2858
2859 if (token.Type != TokenType.Identifier)
2860 throw new ParserException("Unerwarteter Token '" + token.Lexeme + "'.", token);
2861
2862 string parameter = token.Lexeme.ToString();
2863
2864 AllocateVariable(parameter);
2865
2866 parameters.Add(parameter);
2867
2868 token = LookAhead();
2869
2870 if (token.Type == TokenType.Comma)
2871 ReadComma();
2872
2873 else if (token.Type == TokenType.RightParen)
2874 break;
2875
2876 else
2877 throw new ParserException("Comma ',' oder schliessende Klammer ')' erwartet.");
2878
2879 }
2880
2881 }
2882
2884
2885 Instruction scriptInstructionFunctionEntry = new Instruction(OpCode.NOP);
2886
2887 executable.InstructionsInternal.Add(scriptInstructionFunctionEntry);
2888
2889 Function scriptFunction = new Function(executable, functionName, parameters, scriptInstructionFunctionEntry);
2890
2891 executable.Functions[functionName] = scriptFunction;
2892
2893 parameters.Reverse();
2894
2895 foreach (string parameter in parameters)
2896 {
2897
2898 Instruction scriptInstructionPop = new Instruction(OpCode.POP, Operand.Variable(parameter));
2899
2900 executable.InstructionsInternal.Add(scriptInstructionPop);
2901
2902 }
2903
2904 StatementList();
2905
2906 executable.InstructionsInternal.Add(new Instruction( OpCode.PUSH, Operand.Literal(NullReference.Instance)));
2907
2908 executable.InstructionsInternal.Add(new Instruction(OpCode.RET));
2909
2910 localVariables.Clear();
2911
2912 }
2913
2922 private Variable FunctionCall(bool background)
2923 {
2924
2925 string name = ReadQualifiedIdentifier();
2926
2927 // lets expect it is a function
2928 bool forwardDeclared = true;
2929
2930 // if it is registered to the manager it is a routine of course
2931 if (!executable.Functions.ContainsKey(name))
2932 forwardDeclared = false;
2933
2935
2936 uint parameterCount = 0;
2937
2938 if (LookAhead().Type != TokenType.RightParen)
2939 {
2940
2941 // parameters are on the stack, already set to true so we just assign them
2942 while (true)
2943 {
2944
2945 Variable parameter = Expression();
2946
2947 executable.InstructionsInternal.Add(new Instruction(OpCode.PUSH, Operand.Variable(parameter.name)));
2948
2949 ++parameterCount;
2950
2951 if (LookAhead().Type == TokenType.RightParen)
2952 break;
2953
2954 else
2955 ReadComma();
2956
2957 }
2958
2959 }
2960
2962
2963 Instruction instruction = null;
2964
2965 Function function = null;
2966
2967 if (forwardDeclared)
2968 {
2969
2970 function = executable.Functions[name];
2971
2972 if (function.ParameterCount > parameterCount)
2973 throw new ParserException("Der Aufruf der Funktion '" + name + "' hat fehlende Parameter. Erwartet werden " + function.ParameterCount + " Parameter.");
2974
2975 // \todo should we just throw the rest?
2976 if (function.ParameterCount < parameterCount)
2977 throw new ParserException("Der Aufruf der Funktion '" + name + "' hat zu viele Parameter. Erwartet werden " + function.ParameterCount + " Parameter.");
2978
2979 }
2980
2981 Variable variable = new Variable();
2982
2983 if (background)
2984 {
2985
2986 instruction = new Instruction(OpCode.RUN, Operand.AllocateFunctionPointer(function));
2987
2988 executable.InstructionsInternal.Add(instruction);
2989
2990 }
2991
2992 else
2993 {
2994
2995 instruction = new Instruction(OpCode.CALL, Operand.AllocateFunctionPointer(function));
2996
2997 executable.InstructionsInternal.Add(instruction);
2998
2999 // the result is popped onto the stack so a temp variable is created like '[0:0]'
3000 variable.name = AllocateTemporaryVariable();
3001
3002 variable.scope = Scope.Local;
3003
3004 variable.derivatedType = null;
3005
3006 executable.InstructionsInternal.Add(new Instruction(OpCode.POP, Operand.Variable(variable.name)));
3007
3008 }
3009
3010 if (!forwardDeclared)
3011 {
3012
3013 FunctionDescriptor functionDescriptor = new FunctionDescriptor();
3014
3015 functionDescriptor.name = name;
3016
3017 functionDescriptor.parameterCount = parameterCount;
3018
3019 functionDescriptor.instruction = null;
3020
3021 forwardDeclarations[instruction] = functionDescriptor;
3022
3023 }
3024
3025 return variable;
3026
3027 }
3028
3035 {
3036
3037 // We must be able to look ahead without losing how many tokens we consumed.
3038 // For qualified routine names like "std.print(...)" we consume multiple tokens.
3039 int start = nextToken;
3040
3041 string functionName = ReadQualifiedIdentifier();
3042
3043 nextToken = start;
3044
3045 if (executable.Script.Manager.IsRegistered(functionName))
3046 return RoutineCall();
3047
3048 return FunctionCall(false);
3049
3050 }
3051
3055 private void Statement()
3056 {
3057
3059
3060 Token token = LookAhead();
3061
3062 InsertDebugInfo(token);
3063
3064 switch (token.Type)
3065 {
3066
3067 case TokenType.SemiColon:
3068 ReadToken();
3069 break;
3070
3071 case TokenType.Var:
3073 break;
3074
3075 case TokenType.LeftBrace:
3076 StatementList();
3077 break;
3078
3079 case TokenType.Increment:
3080 case TokenType.Decrement:
3081 case TokenType.LeftParen:
3082 case TokenType.Identifier:
3083 case TokenType.Null:
3084 case TokenType.Integer:
3085 case TokenType.Float:
3086 case TokenType.Boolean:
3087 case TokenType.String:
3088 case TokenType.Char:
3089 Expression();
3090 if(LookAhead().Type == TokenType.SemiColon)
3091 ReadSemicolon();
3092 break;
3093
3094 case TokenType.If:
3095 If();
3096 break;
3097 case TokenType.While:
3098 While();
3099 break;
3100 case TokenType.For:
3101 For();
3102 break;
3103 case TokenType.Foreach:
3104 ForEach();
3105 break;
3106 case TokenType.Break:
3107 Break();
3108 break;
3109 case TokenType.Continue:
3110 Continue();
3111 break;
3112 case TokenType.Switch:
3113 Switch();
3114 break;
3115 case TokenType.Return:
3116 Return();
3117 break;
3118
3119 case TokenType.Run:
3120 Run();
3121 break;
3122 case TokenType.Yield:
3123 Yield();
3124 break;
3125 case TokenType.Wait:
3126 Wait();
3127 break;
3128 case TokenType.Notify:
3129 Notify();
3130 break;
3131 case TokenType.Lock:
3133 break;
3134
3135 default: throw new ParserException("ParserException::Statement: Ein unerwarteter Token '" + token.Lexeme + "' wurde gefunden.", token);
3136
3137 }
3138
3140
3141 }
3142
3147 private void StatementList()
3148 {
3149
3150 // if there are no braces, just read a single statement
3151 if (LookAhead().Type != TokenType.LeftBrace)
3152 {
3153
3154 Statement();
3155
3156 return;
3157
3158 }
3159
3160 ReadToken();
3161
3162 while (LookAhead().Type != TokenType.RightBrace)
3163 Statement();
3164
3166
3167 }
3168
3172 private void LockedStatementList()
3173 {
3174
3175 Token token = ReadToken();
3176
3177 if (token.Type != TokenType.Lock)
3178 throw new ExecutionException("ParserException: Keyword 'lock' erwartet.");
3179
3180 Variable variable = Expression();
3181
3182 executable.InstructionsInternal.Add(new Instruction(OpCode.LOCK, Operand.Variable(variable.name)));
3183
3184 Statement();
3185
3186 executable.InstructionsInternal.Add(new Instruction(OpCode.FREE, Operand.Variable(variable.name)));
3187
3188 }
3189
3193 private void Yield()
3194 {
3195
3196 Token token = ReadToken();
3197
3198 if (token.Type != TokenType.Yield)
3199 throw new ParserException("ParserException: Keyword 'yield' erwartet.", token);
3200
3201 ReadSemicolon();
3202
3203 executable.InstructionsInternal.Add(new Instruction(OpCode.INT));
3204
3205 }
3206
3212 private void Wait()
3213 {
3214
3215 Token token = ReadToken();
3216
3217 if (token.Type != TokenType.Wait)
3218 throw new ParserException("Keyword 'wailt' erwartet.", token);
3219
3220 string identifier = ExpectIdentifier();
3221
3222 ReadSemicolon();
3223
3224 List<Instruction> instructions = executable.InstructionsInternal;
3225
3226 Instruction arrived = new Instruction(OpCode.NOP);
3227
3228 Instruction waiting = new Instruction(OpCode.JZ, Operand.Variable(identifier), Operand.AllocateInstructionPointer(arrived));
3229
3230 instructions.Add(waiting);
3231
3232 instructions.Add(new Instruction(OpCode.INT));
3233
3234 instructions.Add(new Instruction(OpCode.JMP, Operand.AllocateInstructionPointer(waiting)));
3235
3236 instructions.Add(arrived);
3237
3238 }
3239
3244 private void Notify()
3245 {
3246
3247 Token token = ReadToken();
3248
3249 if (token.Type != TokenType.Notify)
3250 throw new ParserException("Keyword 'notify' erwartet.", token);
3251
3252 string identifier = ExpectIdentifier();
3253
3254 ReadSemicolon();
3255
3256 executable.InstructionsInternal.Add(new Instruction(OpCode.MOV, Operand.Variable(identifier), Operand.Literal(true)));
3257
3258 }
3259
3265 private void ParseScript()
3266 {
3267
3268 while (More())
3269 {
3270
3271 Token token = LookAhead();
3272
3273 if (token.Type == TokenType.Shared || token.Type == TokenType.Var)
3275
3276 else
3277 break;
3278
3279 }
3280
3281 if (!More())
3282 return;
3283
3284 while (More())
3285 {
3286
3287 Token token = LookAhead();
3288
3289 if (token.Type != TokenType.Function)
3290 throw new ParserException( "Ausserhalb von Funktionen sind keine Anweisungen erlaubt.", token);
3291
3293
3294 }
3295
3296 }
3297
3302 {
3303
3304 foreach (Instruction instruction in forwardDeclarations.Keys)
3305 {
3306
3307 FunctionDescriptor functionDescriptor = forwardDeclarations[instruction];
3308
3309 string name = functionDescriptor.name;
3310
3311 if (!executable.Functions.ContainsKey(name))
3312 throw new ParserException("Eine nicht deklarierte Funktion '" + name + "' wurde referenziert.");
3313
3314 Function function = executable.Functions[name];
3315
3316 instruction.First.FunctionPointer = function;
3317
3318 }
3319
3320 }
3321
3322 #endregion
3323
3324 #region Internal Properties
3325
3326 internal bool DebugMode
3327 {
3328 get { return debugMode; }
3329 set { debugMode = value; }
3330 }
3331
3332 #endregion
3333
3334 #region Public Methods
3335
3336 public Parser(Script script, List<Token> tokenStream)
3337 {
3338 this.script = script;
3339 debugMode = false;
3340 nextToken = 0;
3341 variables = new Dictionary<String, bool>();
3342 localVariables = new Dictionary<String, bool>();
3344 forwardDeclarations = new Dictionary<Instruction, FunctionDescriptor>();
3345 loopControl = new Stack<LoopControl>();
3346 this.tokenStream = new List<Token>(tokenStream);
3347 derivation = new Derivation();
3348 executable = null;
3349 }
3350
3356 {
3357 nextToken = 0;
3358 variables.Clear();
3359 localVariables.Clear();
3360 functionFrameIndex = -1;
3361 forwardDeclarations.Clear();
3362 loopControl.Clear();
3363
3365
3366 ParseScript();
3368 executable.Clean();
3369
3370 return executable;
3371 }
3372
3373 #endregion
3374
3375 }
3376
3377}
static AccessSegment Index(string indexVar)
static AccessSegment Member(string name)
void AllocateFunctionFrame()
Increase the function frame index.
Definition Parser.cs:321
void ParseScript()
After the first function declaration no more variable, struct or enum declarations are allowed anymor...
Definition Parser.cs:3265
List< Token > tokenStream
Definition Parser.cs:65
Variable Or()
Disjunction (not exclusive).
Definition Parser.cs:1939
string ReadIdentifier()
Read a new (previously NOT declared) identifier.
Definition Parser.cs:515
Variable BitwiseOr()
Bitwise OR '|'.
Definition Parser.cs:1863
void FreeFunctionFrame()
Decrease the function frame index.
Definition Parser.cs:361
bool More()
Check if there are more tokens available.
Definition Parser.cs:83
Variable AccessChainAssignment()
Mixed access chain assignment.
Definition Parser.cs:2022
void Return()
By default null is returned.
Definition Parser.cs:2807
Variable Assignment()
An assignment can be a variable assignment, an array assignment or a member assignment.
Definition Parser.cs:2218
void InsertDebugInfo(Token token)
Definition Parser.cs:497
Variable Arithmetic()
Multiplication and division before Addition and substraction.
Definition Parser.cs:1669
Variable Atom()
The smallest unit.
Definition Parser.cs:1222
string ExpectIdentifier()
Read an expected (previously declared) identifier.
Definition Parser.cs:555
Variable Not()
Proposition.
Definition Parser.cs:1752
Variable AccessChain()
Mixed access chain parser.
Definition Parser.cs:841
Variable Pointer()
Array access.
Definition Parser.cs:768
Variable VariableAssignment()
Definition Parser.cs:1975
Parser(Script script, List< Token > tokenStream)
Definition Parser.cs:3336
void VariableDeclaration()
Shared or local variable declaration.
Definition Parser.cs:575
OpCode AssignmentOpcode(TokenType tokenType)
Definition Parser.cs:179
Type Literal(TokenType tokenType)
Get the literal type of a token.
Definition Parser.cs:258
Dictionary< string, bool > variables
Definition Parser.cs:67
void Wait()
Wait for a locked secion of code to be freed.
Definition Parser.cs:3212
void ResolveForwardFunctionDeclarations()
Resolve unresolved, forward declared functions.
Definition Parser.cs:3301
Variable BracketArray()
An array enclosed in brackets.
Definition Parser.cs:1494
void AllocateVariable(string identifier)
Allocate a local variable.
Definition Parser.cs:303
Variable And()
Conjunction.
Definition Parser.cs:1901
void FunctionDeclaration()
Parameter variables are already set to true but not assigned yet. Pop all of them in reverse order on...
Definition Parser.cs:2832
string AllocateTemporaryVariable()
Add a temporary variable to the current function frames local memory.
Definition Parser.cs:333
bool RelationalOperator(TokenType tokenType)
Definition Parser.cs:207
bool IsAccessChainAssignment()
Detect whether the upcoming tokens form an assignment expression.
Definition Parser.cs:2229
Token LookAhead()
Get the next available token without actually increasing the tokenstream index.
Definition Parser.cs:106
Dictionary< string, bool > localVariables
Definition Parser.cs:68
OpCode RelationalOpcode(TokenType tokenType)
Definition Parser.cs:233
Variable BitwiseXor()
Bitwise XOR '^'.
Definition Parser.cs:1828
Token ReadToken()
Get the next available token.
Definition Parser.cs:92
Executable Parse()
Parse the token stream into an executable.
Definition Parser.cs:3355
Token LookAhead(int i)
Get the token 'n' steps forward without actually increasing the tokenstream index.
Definition Parser.cs:121
string ReadQualifiedIdentifier()
Read a dotted (qualified) identifier like: std.print or ns.io.print.
Definition Parser.cs:533
string ToLiteral(string input)
Definition Parser.cs:1182
void UndoToken()
If you read a token wrong, push it back so the stream stays intact.
Definition Parser.cs:134
void Statement()
A statement can be a local variable declaration, a statement list, an expression or a keyword.
Definition Parser.cs:3055
Type Derivate(Token token, Type first, Type second)
Get the resulting type of two computed types.
Definition Parser.cs:292
Dictionary< Instruction, FunctionDescriptor > forwardDeclarations
Definition Parser.cs:70
Variable Expression()
An expression is an assignment or a disjunction.
Definition Parser.cs:2371
Variable Member()
Member access.
Definition Parser.cs:800
Variable Term()
Factor ( [*|/|%] Factor ).
Definition Parser.cs:1608
Variable BitwiseAnd()
Bitwise AND '&'.
Definition Parser.cs:1792
Stack< LoopControl > loopControl
Definition Parser.cs:71
Variable FunctionCall()
Can be a call to a forward declared Function or a Routine. To check what it is we look if it is a reg...
Definition Parser.cs:3034
Variable BraceArray()
An array enclosed in braces.
Definition Parser.cs:1414
Variable Factor()
Atom | Array.
Definition Parser.cs:1564
void StatementList()
A list of statements. If its not a list (not in braces) just return a single statement.
Definition Parser.cs:3147
Variable FunctionCall(bool background)
Call a forward declared function Push all parameter identifier onto the stack and call the function/r...
Definition Parser.cs:2922
bool AssignmentOperator(TokenType tokenType)
Definition Parser.cs:149
A lexical token or simply token is a string with an assigned and thus identified meaning.
Definition Token.cs:101
API entry point.
Definition Manager.cs:21
ReadOnlyDictionary< String, Routine > Routines
Definition Manager.cs:307
A function, forward declared in a script.
Definition Function.cs:15
An instruction in a virtual intermediate language.
static Operand MemberVariable(string identifier, object val)
Definition Operand.cs:64
static Operand Literal(object val)
Definition Operand.cs:54
static Operand AllocateInstructionPointer(Instruction instruction)
Definition Operand.cs:74
static Operand CreatePointer(string identifier, string pointer)
Definition Operand.cs:69
static Operand AllocateFunctionPointer(Function function)
Definition Operand.cs:79
static Operand AllocateRoutinePointer(Routine routine)
Definition Operand.cs:84
static Operand Variable(string identifier)
Definition Operand.cs:59
A Routine is an abstract representation of a method.
Definition Routine.cs:70
List< Type > ParameterTypes
Definition Routine.cs:313
Internal representation of a text file (source code) which can be passed to the Interpreter to execut...
Definition Script.cs:18
TokenType
Known types of Token.
Definition Token.cs:12
Variable(string name, Scope scope, Type derivatedType)
Definition Parser.cs:33