aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs7719
1 files changed, 7719 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs
new file mode 100644
index 0000000..a8af740
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptReduce.cs
@@ -0,0 +1,7719 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/**
29 * @brief Reduce parser tokens to abstract syntax tree tokens.
30 *
31 * Usage:
32 *
33 * tokenBegin = returned by TokenBegin.Analyze ()
34 * representing the whole script source
35 * as a flat list of tokens
36 *
37 * TokenScript tokenScript = Reduce.Analyze (TokenBegin tokenBegin);
38 *
39 * tokenScript = represents the whole script source
40 * as a tree of tokens
41 */
42
43using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
44using System;
45using System.Collections.Generic;
46using System.IO;
47using System.Reflection;
48using System.Reflection.Emit;
49using System.Text;
50
51using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
52using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
53using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
54using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
55using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
56using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
57using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
58
59namespace OpenSim.Region.ScriptEngine.XMREngine {
60
61 public class ScriptReduce {
62 public const uint SDT_PRIVATE = 1;
63 public const uint SDT_PROTECTED = 2;
64 public const uint SDT_PUBLIC = 4;
65 public const uint SDT_ABSTRACT = 8;
66 public const uint SDT_FINAL = 16;
67 public const uint SDT_NEW = 32;
68 public const uint SDT_OVERRIDE = 64;
69 public const uint SDT_STATIC = 128;
70 public const uint SDT_VIRTUAL = 256;
71
72 private const int ASNPR = 50;
73
74 private static Dictionary<Type, int> precedence = PrecedenceInit ();
75
76 private static readonly Type[] brkCloseOnly = new Type[] { typeof (TokenKwBrkClose) };
77 private static readonly Type[] cmpGTOnly = new Type[] { typeof (TokenKwCmpGT) };
78 private static readonly Type[] colonOnly = new Type[] { typeof (TokenKwColon) };
79 private static readonly Type[] commaOrBrcClose = new Type[] { typeof (TokenKwComma), typeof (TokenKwBrcClose) };
80 private static readonly Type[] colonOrDotDotDot = new Type[] { typeof (TokenKwColon), typeof (TokenKwDotDotDot) };
81 private static readonly Type[] parCloseOnly = new Type[] { typeof (TokenKwParClose) };
82 private static readonly Type[] semiOnly = new Type[] { typeof (TokenKwSemi) };
83
84 /**
85 * @brief Initialize operator precedence table
86 * @returns with precedence table pointer
87 */
88 private static Dictionary<Type, int> PrecedenceInit ()
89 {
90 Dictionary<Type, int> p = new Dictionary<Type, int> ();
91
92 // http://www.lslwiki.net/lslwiki/wakka.php?wakka=operators
93
94 p.Add (typeof (TokenKwComma), 30);
95
96 p.Add (typeof (TokenKwAsnLSh), ASNPR); // all assignment operators of equal precedence
97 p.Add (typeof (TokenKwAsnRSh), ASNPR); // ... so they get processed strictly right-to-left
98 p.Add (typeof (TokenKwAsnAdd), ASNPR);
99 p.Add (typeof (TokenKwAsnAnd), ASNPR);
100 p.Add (typeof (TokenKwAsnSub), ASNPR);
101 p.Add (typeof (TokenKwAsnMul), ASNPR);
102 p.Add (typeof (TokenKwAsnDiv), ASNPR);
103 p.Add (typeof (TokenKwAsnMod), ASNPR);
104 p.Add (typeof (TokenKwAsnOr), ASNPR);
105 p.Add (typeof (TokenKwAsnXor), ASNPR);
106 p.Add (typeof (TokenKwAssign), ASNPR);
107
108 p.Add (typeof (TokenKwQMark), 60);
109
110 p.Add (typeof (TokenKwOrOrOr), 70);
111 p.Add (typeof (TokenKwAndAndAnd), 80);
112
113 p.Add (typeof (TokenKwOrOr), 100);
114
115 p.Add (typeof (TokenKwAndAnd), 120);
116
117 p.Add (typeof (TokenKwOr), 140);
118
119 p.Add (typeof (TokenKwXor), 160);
120
121 p.Add (typeof (TokenKwAnd), 180);
122
123 p.Add (typeof (TokenKwCmpEQ), 200);
124 p.Add (typeof (TokenKwCmpNE), 200);
125
126 p.Add (typeof (TokenKwCmpLT), 240);
127 p.Add (typeof (TokenKwCmpLE), 240);
128 p.Add (typeof (TokenKwCmpGT), 240);
129 p.Add (typeof (TokenKwCmpGE), 240);
130
131 p.Add (typeof (TokenKwRSh), 260);
132 p.Add (typeof (TokenKwLSh), 260);
133
134 p.Add (typeof (TokenKwAdd), 280);
135 p.Add (typeof (TokenKwSub), 280);
136
137 p.Add (typeof (TokenKwMul), 320);
138 p.Add (typeof (TokenKwDiv), 320);
139 p.Add (typeof (TokenKwMod), 320);
140
141 return p;
142 }
143
144 /**
145 * @brief Reduce raw token stream to a single script token.
146 * Performs a little semantic testing, ie, undefined variables, etc.
147 * @param tokenBegin = points to a TokenBegin
148 * followed by raw tokens
149 * and last token is a TokenEnd
150 * @returns null: not a valid script, error messages have been output
151 * else: valid script top token
152 */
153 public static TokenScript Reduce (TokenBegin tokenBegin)
154 {
155 return new ScriptReduce (tokenBegin).tokenScript;
156 }
157
158 /*
159 * Instance variables.
160 */
161 private bool errors = false;
162 private string lastErrorFile = "";
163 private int lastErrorLine = 0;
164 private int numTypedefs = 0;
165 private TokenDeclVar currentDeclFunc = null;
166 private TokenDeclSDType currentDeclSDType = null;
167 private TokenScript tokenScript;
168 private TokenStmtBlock currentStmtBlock = null;
169
170 /**
171 * @brief the constructor does all the processing.
172 * @param token = first token of script after the TokenBegin token
173 * @returns tokenScript = null: there were errors
174 * else: successful
175 */
176 private ScriptReduce (TokenBegin tokenBegin)
177 {
178 /*
179 * Create a place to put the top-level script components,
180 * eg, state bodies, functions, global variables.
181 */
182 tokenScript = new TokenScript (tokenBegin.nextToken);
183 tokenScript.expiryDays = tokenBegin.expiryDays;
184
185 /*
186 * 'class', 'delegate', 'instance' all define types.
187 * So we pre-scan the source tokens for those keywords
188 * to build a script-defined type table and substitute
189 * type tokens for those names in the source. This is
190 * done as a separate scan so they can cross-reference
191 * each other. Also does likewise for fixed array types.
192 *
193 * Also, all 'typedef's are processed here. Their definitions
194 * remain in the source token stream after this, but they can
195 * be skipped over, because their bodies have been substituted
196 * in the source for any references.
197 */
198 ParseSDTypePreScanPassOne (tokenBegin); // catalog definitions
199 ParseSDTypePreScanPassTwo (tokenBegin); // substitute references
200
201 /*
202 int braces = 0;
203 Token prevTok = null;
204 for (Token token = tokenBegin; token != null; token = token.nextToken) {
205 if (token is TokenKwParClose) braces -= 2;
206 if (token is TokenKwBrcClose) braces -= 4;
207 StringBuilder sb = new StringBuilder ("ScriptReduce*: ");
208 sb.Append (token.GetHashCode ().ToString ("X8"));
209 sb.Append (" ");
210 sb.Append (token.line.ToString ().PadLeft (3));
211 sb.Append (".");
212 sb.Append (token.posn.ToString ().PadLeft (3));
213 sb.Append (" ");
214 sb.Append (token.GetType ().Name.PadRight (24));
215 sb.Append (" : ");
216 for (int i = 0; i < braces; i ++) sb.Append (' ');
217 token.DebString (sb);
218 Console.WriteLine (sb.ToString ());
219 if (token.prevToken != prevTok) {
220 Console.WriteLine ("ScriptReduce*: -- prevToken link bad => " + token.prevToken.GetHashCode ().ToString ("X8"));
221 }
222 if (token is TokenKwBrcOpen) braces += 4;
223 if (token is TokenKwParOpen) braces += 2;
224 prevTok = token;
225 }
226 */
227
228 /*
229 * Create a function $globalvarinit to hold all explicit
230 * global variable initializations.
231 */
232 TokenDeclVar gviFunc = new TokenDeclVar (tokenBegin, null, tokenScript);
233 gviFunc.name = new TokenName (gviFunc, "$globalvarinit");
234 gviFunc.retType = new TokenTypeVoid (gviFunc);
235 gviFunc.argDecl = new TokenArgDecl (gviFunc);
236 TokenStmtBlock gviBody = new TokenStmtBlock (gviFunc);
237 gviBody.function = gviFunc;
238 gviFunc.body = gviBody;
239 tokenScript.globalVarInit = gviFunc;
240 tokenScript.AddVarEntry (gviFunc);
241
242 /*
243 * Scan through the tokens until we reach the end.
244 */
245 for (Token token = tokenBegin.nextToken; !(token is TokenEnd);) {
246 if (token is TokenKwSemi) {
247 token = token.nextToken;
248 continue;
249 }
250
251 /*
252 * Script-defined type declarations.
253 */
254 if (ParseDeclSDTypes (ref token, null, SDT_PUBLIC)) continue;
255
256 /*
257 * constant <name> = <rval> ;
258 */
259 if (token is TokenKwConst) {
260 ParseDeclVar (ref token, null);
261 continue;
262 }
263
264 /*
265 * <type> <name> ;
266 * <type> <name> = <rval> ;
267 */
268 if ((token is TokenType) &&
269 (token.nextToken is TokenName) &&
270 ((token.nextToken.nextToken is TokenKwSemi) ||
271 (token.nextToken.nextToken is TokenKwAssign))) {
272 TokenDeclVar var = ParseDeclVar (ref token, gviFunc);
273 if (var != null) {
274 // <name> = <init>;
275 TokenLValName left = new TokenLValName (var.name, tokenScript.variablesStack);
276 DoVarInit (gviFunc, left, var.init);
277 }
278 continue;
279 }
280
281 /*
282 * <type> <name> { [ get { <body> } ] [ set { <body> } ] }
283 */
284 if ((token is TokenType) &&
285 (token.nextToken is TokenName) &&
286 (token.nextToken.nextToken is TokenKwBrcOpen)) {
287 ParseProperty (ref token, false, true);
288 continue;
289 }
290
291 /*
292 * <type> <name> <funcargs> <funcbody>
293 * global function returning specified type
294 */
295 if (token is TokenType) {
296 TokenType tokenType = (TokenType)token;
297
298 token = token.nextToken;
299 if (!(token is TokenName)) {
300 ErrorMsg (token, "expecting variable/function name");
301 token = SkipPastSemi (token);
302 continue;
303 }
304 TokenName tokenName = (TokenName)token;
305 token = token.nextToken;
306 if (!(token is TokenKwParOpen)) {
307 ErrorMsg (token, "<type> <name> must be followed by ; = or (");
308 token = SkipPastSemi (token);
309 continue;
310 }
311 token = tokenType;
312 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, false, false, false);
313 if (tokenDeclFunc == null) continue;
314 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
315 ErrorMsg (tokenName, "duplicate function " + tokenDeclFunc.funcNameSig.val);
316 }
317 continue;
318 }
319
320 /*
321 * <name> <funcargs> <funcbody>
322 * global function returning void
323 */
324 if (token is TokenName) {
325 TokenName tokenName = (TokenName)token;
326 token = token.nextToken;
327 if (!(token is TokenKwParOpen)) {
328 ErrorMsg (token, "looking for open paren after assuming " +
329 tokenName.val + " is a function name");
330 token = SkipPastSemi (token);
331 continue;
332 }
333 token = tokenName;
334 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, false, false, false);
335 if (tokenDeclFunc == null) continue;
336 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
337 ErrorMsg (tokenName, "duplicate function " + tokenDeclFunc.funcNameSig.val);
338 }
339 continue;
340 }
341
342 /*
343 * default <statebody>
344 */
345 if (token is TokenKwDefault) {
346 TokenDeclState tokenDeclState = new TokenDeclState (token);
347 token = token.nextToken;
348 tokenDeclState.body = ParseStateBody (ref token);
349 if (tokenDeclState.body == null) continue;
350 if (tokenScript.defaultState != null) {
351 ErrorMsg (tokenDeclState, "default state already declared");
352 continue;
353 }
354 tokenScript.defaultState = tokenDeclState;
355 continue;
356 }
357
358 /*
359 * state <name> <statebody>
360 */
361 if (token is TokenKwState) {
362 TokenDeclState tokenDeclState = new TokenDeclState (token);
363 token = token.nextToken;
364 if (!(token is TokenName)) {
365 ErrorMsg (token, "state must be followed by state name");
366 token = SkipPastSemi (token);
367 continue;
368 }
369 tokenDeclState.name = (TokenName)token;
370 token = token.nextToken;
371 tokenDeclState.body = ParseStateBody (ref token);
372 if (tokenDeclState.body == null) continue;
373 if (tokenScript.states.ContainsKey (tokenDeclState.name.val)) {
374 ErrorMsg (tokenDeclState.name, "duplicate state definition");
375 continue;
376 }
377 tokenScript.states.Add (tokenDeclState.name.val, tokenDeclState);
378 continue;
379 }
380
381 /*
382 * Doesn't fit any of those forms, output message and skip to next statement.
383 */
384 ErrorMsg (token, "looking for var name, type, state or default, script-defined type declaration");
385 token = SkipPastSemi (token);
386 continue;
387 }
388
389 /*
390 * Must have a default state to start in.
391 */
392 if (!errors && (tokenScript.defaultState == null)) {
393 ErrorMsg (tokenScript, "no default state defined");
394 }
395
396 /*
397 * If any error messages were written out, set return value to null.
398 */
399 if (errors) tokenScript = null;
400 }
401
402 /**
403 * @brief Pre-scan the source for class, delegate, interface, typedef definition keywords.
404 * Clump the keywords and name being defined together, but leave the body intact.
405 * In the case of a delegate with an explicit return type, it reverses the name and return type.
406 * After this completes there shouldn't be any TokenKw{Class,Delegate,Interface,Typedef}
407 * keywords in the source, they are all replaced by TokenDeclSDType{Class,Delegate,Interface,
408 * Typedef} tokens which also encapsulate the name of the type being defined and any generic
409 * parameter names. The body remains intact in the source token stream following the
410 * TokenDeclSDType* token.
411 */
412 private void ParseSDTypePreScanPassOne (Token tokenBegin)
413 {
414 Stack<int> braceLevels = new Stack<int> ();
415 Stack<TokenDeclSDType> outerLevels = new Stack<TokenDeclSDType> ();
416 int openBraceLevel = 0;
417 braceLevels.Push (-1);
418 outerLevels.Push (null);
419
420 for (Token t = tokenBegin; !((t = t.nextToken) is TokenEnd);) {
421
422 /*
423 * Keep track of nested definitions so we can link them up.
424 * We also need to detect the end of class and interface definitions.
425 */
426 if (t is TokenKwBrcOpen) {
427 openBraceLevel ++;
428 continue;
429 }
430 if (t is TokenKwBrcClose) {
431 if (-- openBraceLevel < 0) {
432 ErrorMsg (t, "{ } mismatch");
433 return;
434 }
435 if (braceLevels.Peek () == openBraceLevel) {
436 braceLevels.Pop ();
437 outerLevels.Pop ().endToken = t;
438 }
439 continue;
440 }
441
442 /*
443 * Check for 'class' or 'interface'.
444 * They always define a new class or interface.
445 * They can contain nested script-defined type definitions.
446 */
447 if ((t is TokenKwClass) || (t is TokenKwInterface)) {
448 Token kw = t;
449 t = t.nextToken;
450 if (!(t is TokenName)) {
451 ErrorMsg (t, "expecting class or interface name");
452 t = SkipPastSemi (t).prevToken;
453 continue;
454 }
455 TokenName name = (TokenName)t;
456 t = t.nextToken;
457
458 /*
459 * Malloc the script-defined type object.
460 */
461 TokenDeclSDType decl;
462 if (kw is TokenKwClass) decl = new TokenDeclSDTypeClass (name, kw.prevToken is TokenKwPartial);
463 else decl = new TokenDeclSDTypeInterface (name);
464 decl.outerSDType = outerLevels.Peek ();
465
466 /*
467 * Check for generic parameter list.
468 */
469 if (!ParseGenProtoParamList (ref t, decl)) continue;
470
471 /*
472 * Splice in a TokenDeclSDType token that replaces the keyword and the name tokens
473 * and any generic parameters including the '<', ','s and '>'.
474 * kw = points to 'class' or 'interface' keyword.
475 * t = points to just past last part of class name parsed, hopefully a ':' or '{'.
476 */
477 decl.prevToken = decl.isPartial ? kw.prevToken.prevToken : kw.prevToken;
478 decl.nextToken = t;
479 decl.prevToken.nextToken = decl;
480 decl.nextToken.prevToken = decl;
481
482 /*
483 * Enter it in name lists so it can be seen by others.
484 */
485 Token partialNewBody = CatalogSDTypeDecl (decl);
486
487 /*
488 * Start inner type definitions.
489 */
490 braceLevels.Push (openBraceLevel);
491 outerLevels.Push (decl);
492
493 /*
494 * Scan the body starting on for before the '{'.
495 *
496 * If this body had an old partial merged into it,
497 * resume scanning at the beginning of the new body,
498 * ie, what used to be the first token after the '{'
499 * before the old body was spliced in.
500 */
501 if (partialNewBody != null) {
502
503 /*
504 * We have a partial that has had old partial body merged
505 * into new partial body. So resume scanning at the beginning
506 * of the new partial body so we don't get any duplicate scanning
507 * of the old partial body.
508 *
509 * <decl> ... { <oldbody> <newbody> }
510 * ^- resume scanning here
511 * but inc openBraceLevel because
512 * we skipped scanning the '{'
513 */
514 openBraceLevel ++;
515 t = partialNewBody;
516 }
517 t = t.prevToken;
518 continue;
519 }
520
521 /*
522 * Check for 'delegate'.
523 * It always defines a new delegate.
524 * Delegates never define nested types.
525 */
526 if (t is TokenKwDelegate) {
527 Token kw = t;
528 t = t.nextToken;
529
530 /*
531 * Next thing might be an explicit return type or the delegate's name.
532 * If it's a type token, then it's the return type, simple enough.
533 * But if it's a name token, it might be the name of some other script-defined type.
534 * The way to tell is that the delegate name is followed by a '(', whereas an
535 * explicit return type is followed by the delegate name.
536 */
537 Token retType = t;
538 TokenName delName = null;
539 Token u;
540 int angles = 0;
541 for (u = t; !(u is TokenKwParOpen); u = u.nextToken) {
542 if ((u is TokenKwSemi) || (u is TokenEnd)) break;
543 if (u is TokenKwCmpLT) angles ++;
544 if (u is TokenKwCmpGT) angles --;
545 if (u is TokenKwRSh) angles -= 2; // idiot >>
546 if ((angles == 0) && (u is TokenName)) delName = (TokenName)u;
547 }
548 if (!(u is TokenKwParOpen)) {
549 ErrorMsg (u, "expecting ( for delegate parameter list");
550 t = SkipPastSemi (t).prevToken;
551 continue;
552 }
553 if (delName == null) {
554 ErrorMsg (u, "expecting delegate name");
555 t = SkipPastSemi (t).prevToken;
556 continue;
557 }
558 if (retType == delName) retType = null;
559
560 /*
561 * Malloc the script-defined type object.
562 */
563 TokenDeclSDTypeDelegate decl = new TokenDeclSDTypeDelegate (delName);
564 decl.outerSDType = outerLevels.Peek ();
565
566 /*
567 * Check for generic parameter list.
568 */
569 t = delName.nextToken;
570 if (!ParseGenProtoParamList (ref t, decl)) continue;
571
572 /*
573 * Enter it in name lists so it can be seen by others.
574 */
575 CatalogSDTypeDecl (decl);
576
577 /*
578 * Splice in the token that replaces the 'delegate' keyword and the whole name
579 * (including the '<' name ... '>' parts). The return type token(s), if any,
580 * follow the splice token and come before the '('.
581 */
582 decl.prevToken = kw.prevToken;
583 kw.prevToken.nextToken = decl;
584
585 if (retType == null) {
586 decl.nextToken = t;
587 t.prevToken = decl;
588 } else {
589 decl.nextToken = retType;
590 retType.prevToken = decl;
591 retType.nextToken = t;
592 t.prevToken = retType;
593 }
594
595 /*
596 * Scan for terminating ';'.
597 * There cannot be an intervening class, delegate, interfate, typedef, { or }.
598 */
599 for (t = decl; !(t is TokenKwSemi); t = u) {
600 u = t.nextToken;
601 if ((u is TokenEnd) ||
602 (u is TokenKwClass) ||
603 (u is TokenKwDelegate) ||
604 (u is TokenKwInterface) ||
605 (u is TokenKwTypedef) ||
606 (u is TokenKwBrcOpen) ||
607 (u is TokenKwBrcClose)) {
608 ErrorMsg (t, "delegate missing terminating ;");
609 break;
610 }
611 }
612 decl.endToken = t;
613 continue;
614 }
615
616 /*
617 * Check for 'typedef'.
618 * It always defines a new macro.
619 * Typedefs never define nested types.
620 */
621 if (t is TokenKwTypedef) {
622 Token kw = t;
623 t = t.nextToken;
624
625 if (!(t is TokenName)) {
626 ErrorMsg (t, "expecting typedef name");
627 t = SkipPastSemi (t).prevToken;
628 continue;
629 }
630 TokenName tdName = (TokenName)t;
631 t = t.nextToken;
632
633 /*
634 * Malloc the script-defined type object.
635 */
636 TokenDeclSDTypeTypedef decl = new TokenDeclSDTypeTypedef (tdName);
637 decl.outerSDType = outerLevels.Peek ();
638
639 /*
640 * Check for generic parameter list.
641 */
642 if (!ParseGenProtoParamList (ref t, decl)) continue;
643
644 /*
645 * Enter it in name lists so it can be seen by others.
646 */
647 CatalogSDTypeDecl (decl);
648 numTypedefs ++;
649
650 /*
651 * Splice in the token that replaces the 'typedef' keyword and the whole name
652 * (including the '<' name ... '>' parts).
653 */
654 decl.prevToken = kw.prevToken;
655 kw.prevToken.nextToken = decl;
656 decl.nextToken = t;
657 t.prevToken = decl;
658
659 /*
660 * Scan for terminating ';'.
661 * There cannot be an intervening class, delegate, interfate, typedef, { or }.
662 */
663 Token u;
664 for (t = decl; !(t is TokenKwSemi); t = u) {
665 u = t.nextToken;
666 if ((u is TokenEnd) ||
667 (u is TokenKwClass) ||
668 (u is TokenKwDelegate) ||
669 (u is TokenKwInterface) ||
670 (u is TokenKwTypedef) ||
671 (u is TokenKwBrcOpen) ||
672 (u is TokenKwBrcClose)) {
673 ErrorMsg (t, "typedef missing terminating ;");
674 break;
675 }
676 }
677 decl.endToken = t;
678 continue;
679 }
680 }
681 }
682
683 /**
684 * @brief Parse a possibly generic type definition's parameter list.
685 * @param t = points to the possible opening '<' on entry
686 * points just past the closing '>' on return
687 * @param decl = the generic type being declared
688 * @returns false: parse error
689 * true: decl.genParams = filled in with parameter list
690 * decl.innerSDTypes = filled in with parameter list
691 */
692 private bool ParseGenProtoParamList (ref Token t, TokenDeclSDType decl)
693 {
694 /*
695 * Maybe there aren't any generic parameters.
696 * If so, leave decl.genParams = null.
697 */
698 if (!(t is TokenKwCmpLT)) return true;
699
700 /*
701 * Build list of generic parameter names.
702 */
703 Dictionary<string, int> parms = new Dictionary<string, int> ();
704 do {
705 t = t.nextToken;
706 if (!(t is TokenName)) {
707 ErrorMsg (t, "expecting generic parameter name");
708 break;
709 }
710 TokenName tn = (TokenName)t;
711 if (parms.ContainsKey (tn.val)) {
712 ErrorMsg (tn, "duplicate use of generic parameter name");
713 } else {
714 parms.Add (tn.val, parms.Count);
715 }
716 t = t.nextToken;
717 } while (t is TokenKwComma);
718 if (!(t is TokenKwCmpGT)) {
719 ErrorMsg (t, "expecting , for more params or > to end param list");
720 return false;
721 }
722 t = t.nextToken;
723 decl.genParams = parms;
724
725 return true;
726 }
727
728 /**
729 * @brief Catalog a script-defined type.
730 * Its short name (eg, 'Node') gets put in the next outer level (eg, 'List')'s inner type definition table.
731 * Its long name (eg, 'List.Node') gets put in the global type definition table.
732 */
733 public Token CatalogSDTypeDecl (TokenDeclSDType decl)
734 {
735 string longName = decl.longName.val;
736 TokenDeclSDType dupDecl;
737 if (!tokenScript.sdSrcTypesTryGetValue (longName, out dupDecl)) {
738 tokenScript.sdSrcTypesAdd (longName, decl);
739 if (decl.outerSDType != null) {
740 decl.outerSDType.innerSDTypes.Add (decl.shortName.val, decl);
741 }
742 return null;
743 }
744
745 if (!dupDecl.isPartial || !decl.isPartial) {
746 ErrorMsg (decl, "duplicate definition of type " + longName);
747 ErrorMsg (dupDecl, "previous definition here");
748 return null;
749 }
750
751 if (!GenericParametersMatch (decl, dupDecl)) {
752 ErrorMsg (decl, "all partial class generic parameters must match");
753 }
754
755 /*
756 * Have new declaration be the cataloged one because body is going to get
757 * snipped out of old declaration and pasted into new declaration.
758 */
759 tokenScript.sdSrcTypesRep (longName, decl);
760 if (decl.outerSDType != null) {
761 decl.outerSDType.innerSDTypes[decl.shortName.val] = decl;
762 }
763
764 /*
765 * Find old partial definition's opening brace.
766 */
767 Token dupBrcOpen;
768 for (dupBrcOpen = dupDecl; !(dupBrcOpen is TokenKwBrcOpen); dupBrcOpen = dupBrcOpen.nextToken) {
769 if (dupBrcOpen == dupDecl.endToken) {
770 ErrorMsg (dupDecl, "missing {");
771 return null;
772 }
773 }
774
775 /*
776 * Find new partial definition's opening brace.
777 */
778 Token brcOpen;
779 for (brcOpen = decl; !(brcOpen is TokenKwBrcOpen); brcOpen = brcOpen.nextToken) {
780 if (brcOpen is TokenEnd) {
781 ErrorMsg (decl, "missing {");
782 return null;
783 }
784 }
785 Token body = brcOpen.nextToken;
786
787 /*
788 * Stick old partial definition's extends/implementeds list just
789 * in front of new partial definition's extends/implementeds list.
790 *
791 * class oldextimp { oldbody } ...
792 * dupDecl dupBrcOpen dupDecl.endToken
793 *
794 * class newextimp { newbody } ...
795 * decl brcOpen body decl.endToken
796 *
797 * becomes
798 *
799 * class ...
800 * dupDecl
801 * dupDecl.endToken
802 *
803 * class oldextimp newextimp { oldbody newbody } ...
804 * decl brcOpen body decl.endToken
805 */
806 if (dupBrcOpen != dupDecl.nextToken) {
807 dupBrcOpen.prevToken.nextToken = decl.nextToken;
808 dupDecl.nextToken.prevToken = decl;
809 decl.nextToken.prevToken = dupBrcOpen.prevToken;
810 decl.nextToken = dupDecl.nextToken;
811 }
812
813 /*
814 * Stick old partial definition's body just
815 * in front of new partial definition's body.
816 */
817 if (dupBrcOpen.nextToken != dupDecl.endToken) {
818 dupBrcOpen.nextToken.prevToken = brcOpen;
819 dupDecl.endToken.prevToken.nextToken = body;
820 body.prevToken = dupDecl.endToken.prevToken;
821 brcOpen.nextToken = dupBrcOpen.nextToken;
822 }
823
824 /*
825 * Null out old definition's extends/implementeds list and body
826 * by having the declaration token be the only thing left.
827 */
828 dupDecl.nextToken = dupDecl.endToken.nextToken;
829 dupDecl.nextToken.prevToken = dupDecl;
830 dupDecl.endToken = dupDecl;
831
832 return body;
833 }
834
835 /**
836 * @brief Determine whether or not the generic parameters of two class declarations match exactly.
837 */
838 private static bool GenericParametersMatch (TokenDeclSDType c1, TokenDeclSDType c2)
839 {
840 if ((c1.genParams == null) && (c2.genParams == null)) return true;
841 if ((c1.genParams == null) || (c2.genParams == null)) return false;
842 Dictionary<string, int> gp1 = c1.genParams;
843 Dictionary<string, int> gp2 = c2.genParams;
844 if (gp1.Count != gp2.Count) return false;
845 foreach (KeyValuePair<string, int> kvp1 in gp1) {
846 int v2;
847 if (!gp2.TryGetValue (kvp1.Key, out v2)) return false;
848 if (v2 != kvp1.Value) return false;
849 }
850 return true;
851 }
852
853 /**
854 * @brief Replace all TokenName tokens that refer to the script-defined types with
855 * corresponding TokenTypeSDType{Class,Delegate,GenParam,Interface} tokens.
856 * Also handle generic references, ie, recognize that 'List<integer>' is an
857 * instantiation of 'List<>' and instantiate the generic.
858 */
859 private const uint REPEAT_NOTYPE = 1;
860 private const uint REPEAT_INSTGEN = 2;
861 private const uint REPEAT_SUBST = 4;
862
863 private void ParseSDTypePreScanPassTwo (Token tokenBegin)
864 {
865 List<Token> noTypes = new List<Token> ();
866 TokenDeclSDType outerSDType;
867 uint repeat;
868
869 do {
870 repeat = 0;
871 outerSDType = null;
872 noTypes.Clear ();
873
874 for (Token t = tokenBegin; !((t = t.nextToken) is TokenEnd);) {
875
876 /*
877 * Maybe it's time to pop out of an outer class definition.
878 */
879 if ((outerSDType != null) && (outerSDType.endToken == t)) {
880 outerSDType = outerSDType.outerSDType;
881 continue;
882 }
883
884 /*
885 * Skip completely over any script-defined generic prototypes.
886 * We only need to process their instantiations which are non-
887 * generic versions of the generics.
888 */
889 if ((t is TokenDeclSDType) && (((TokenDeclSDType)t).genParams != null)) {
890 t = ((TokenDeclSDType)t).endToken;
891 continue;
892 }
893
894 /*
895 * Check for beginning of non-generic script-defined type definitions.
896 * They can have nested definitions in their innerSDTypes[] that match
897 * name tokens, so add them to the stack.
898 *
899 * But just ignore any preliminary partial definitions as they have had
900 * their entire contents spliced out and spliced into a subsequent partial
901 * definition. So if we originally had:
902 * partial class Abc { public intenger one; }
903 * partial class Abc { public intenger two; }
904 * We now have:
905 * partial_class_Abc <== if we are here, just ignore the partial_class_Abc token
906 * partial_class_Abc { public intenger one; public intenger two; }
907 */
908 if (t is TokenDeclSDType) {
909 if (((TokenDeclSDType)t).endToken != t) {
910 outerSDType = (TokenDeclSDType)t;
911 }
912 continue;
913 }
914
915 /*
916 * For names not preceded by a '.', scan the script-defined type definition
917 * stack for that name. Splice the name out and replace with equivalent token.
918 */
919 if ((t is TokenName) && !(t.prevToken is TokenKwDot)) {
920 t = TrySpliceTypeRef (t, outerSDType, ref repeat, noTypes);
921 }
922
923 /*
924 * This handles types such as integer[,][], List<string>[], etc.
925 * They are an instantiation of an internally generated type of the same name, brackets and all.
926 * Note that to malloc an array, use something like 'new float[,][](3,5)', not 'new float[3,5][]'.
927 *
928 * Note that we must not get confused by $idxprop property declarations such as:
929 * float [string kee] { get { ... } }
930 * ... and try to convert 'float' '[' to an array type.
931 */
932 if ((t is TokenType) && (t.nextToken is TokenKwBrkOpen)) {
933 if ((t.nextToken.nextToken is TokenKwBrkClose) ||
934 (t.nextToken.nextToken is TokenKwComma)) {
935 t = InstantiateJaggedArray (t, tokenBegin, ref repeat);
936 }
937 }
938 }
939
940 /*
941 * If we instantiated a generic, loop back to process its contents
942 * just as if the source code had the instantiated code to begin with.
943 * Also repeat if we found a non-type inside the <> of a generic reference
944 * provided we have made at least one name->type substitution.
945 */
946 } while (((repeat & REPEAT_INSTGEN) != 0) ||
947 ((repeat & (REPEAT_NOTYPE | REPEAT_SUBST)) == (REPEAT_NOTYPE | REPEAT_SUBST)));
948
949 /*
950 * These are places where we required a type be present,
951 * eg, a generic type argument or the body of a typedef.
952 */
953 foreach (Token t in noTypes) {
954 ErrorMsg (t, "looking for type");
955 }
956 }
957
958 /**
959 * @brief Try to convert the source token string to a type reference
960 * and splice the type reference into the source token string
961 * replacing the original token(s).
962 * @param t = points to the initial TokenName token
963 * @param outerSDType = null: this is a top-level code reference
964 * else: this code is within outerSDType
965 * @returns pointer to last token parsed
966 * possibly with spliced-in type token
967 * repeat = possibly set true if need to do another pass
968 */
969 private Token TrySpliceTypeRef (Token t, TokenDeclSDType outerSDType, ref uint repeat, List<Token> noTypes)
970 {
971 Token start = t;
972 string tnamestr = ((TokenName)t).val;
973
974 /*
975 * Look for the name as a type declared by outerSDType or anything
976 * even farther out than that. If not found, simply return
977 * without updating t, meaning that t isn't the name of a type.
978 */
979 TokenDeclSDType decl = null;
980 while (outerSDType != null) {
981 if (outerSDType.innerSDTypes.TryGetValue (tnamestr, out decl)) break;
982 outerSDType = outerSDType.outerSDType;
983 }
984 if ((outerSDType == null) && !tokenScript.sdSrcTypesTryGetValue (tnamestr, out decl)) return t;
985
986 TokenDeclSDType instdecl;
987 while (true) {
988
989 /*
990 * If it is a generic type, it must be followed by instantiation arguments.
991 */
992 instdecl = decl;
993 if (decl.genParams != null) {
994 t = t.nextToken;
995 if (!(t is TokenKwCmpLT)) {
996 ErrorMsg (t, "expecting < for generic argument list");
997 return t;
998 }
999 tnamestr += "<";
1000 int nArgs = decl.genParams.Count;
1001 TokenType[] genArgs = new TokenType[nArgs];
1002 for (int i = 0; i < nArgs;) {
1003 t = t.nextToken;
1004 if (!(t is TokenType)) {
1005 repeat |= REPEAT_NOTYPE;
1006 noTypes.Add (t);
1007 return t.prevToken; // make sure name gets processed
1008 // so substitution can occur on it
1009 }
1010 TokenType ga = (TokenType)t;
1011 genArgs[i] = ga;
1012 tnamestr += ga.ToString ();
1013 t = t.nextToken;
1014 if (++ i < nArgs) {
1015 if (!(t is TokenKwComma)) {
1016 ErrorMsg (t, "expecting , for more generic arguments");
1017 return t;
1018 }
1019 tnamestr += ",";
1020 }
1021 }
1022 if (t is TokenKwRSh) { // idiot >>
1023 Token u = new TokenKwCmpGT (t);
1024 Token v = new TokenKwCmpGT (t);
1025 v.posn ++;
1026 u.prevToken = t.prevToken;
1027 u.nextToken = v;
1028 v.nextToken = t.nextToken;
1029 v.prevToken = u;
1030 u.prevToken.nextToken = u;
1031 v.nextToken.prevToken = v;
1032 t = u;
1033 }
1034 if (!(t is TokenKwCmpGT)) {
1035 ErrorMsg (t, "expecting > at end of generic argument list");
1036 return t;
1037 }
1038 tnamestr += ">";
1039 if (outerSDType != null) {
1040 outerSDType.innerSDTypes.TryGetValue (tnamestr, out instdecl);
1041 } else {
1042 tokenScript.sdSrcTypesTryGetValue (tnamestr, out instdecl);
1043 }
1044
1045 /*
1046 * Couldn't find 'List<string>' but found 'List' and we have genArgs = 'string'.
1047 * Instantiate the generic to create 'List<string>'. This splices the definition
1048 * of 'List<string>' into the source token stream just as if it had been there all
1049 * along. We have to then repeat the scan to process the instance's contents.
1050 */
1051 if (instdecl == null) {
1052 instdecl = decl.InstantiateGeneric (tnamestr, genArgs, this);
1053 CatalogSDTypeDecl (instdecl);
1054 repeat |= REPEAT_INSTGEN;
1055 }
1056 }
1057
1058 /*
1059 * Maybe caller wants a subtype by putting a '.' following all that.
1060 */
1061 if (!(t.nextToken is TokenKwDot)) break;
1062 if (!(t.nextToken.nextToken is TokenName)) break;
1063 tnamestr = ((TokenName)t.nextToken.nextToken).val;
1064 if (!instdecl.innerSDTypes.TryGetValue (tnamestr, out decl)) break;
1065 t = t.nextToken.nextToken;
1066 outerSDType = instdecl;
1067 }
1068
1069 /*
1070 * Create a reference in the source to the definition
1071 * that encapsulates the long dotted type name given in
1072 * the source, and replace the long dotted type name in
1073 * the source with the reference token, eg, replace
1074 * 'Dictionary' '<' 'string' ',' 'integer' '>' '.' 'ValueList'
1075 * with 'Dictionary<string,integer>.ValueList'.
1076 */
1077 TokenType refer = instdecl.MakeRefToken (start);
1078 if (refer == null) {
1079 // typedef body is not yet a type
1080 noTypes.Add (start);
1081 repeat |= REPEAT_NOTYPE;
1082 return start;
1083 }
1084 refer.prevToken = start.prevToken; // start points right at the first TokenName
1085 refer.nextToken = t.nextToken; // t points at the last TokenName or TokenKwCmpGT
1086 refer.prevToken.nextToken = refer;
1087 refer.nextToken.prevToken = refer;
1088 repeat |= REPEAT_SUBST;
1089
1090 return refer;
1091 }
1092
1093 /**
1094 * @brief We are known to have <type>'[' so make an equivalent array type.
1095 * @param t = points to the TokenType
1096 * @param tokenBegin = where we can safely splice in new array class definitions
1097 * @param repeat = set REPEAT_INSTGEN if new type created
1098 * @returns pointer to last token parsed
1099 * possibly with spliced-in type token
1100 * repeat = possibly set true if need to do another pass
1101 */
1102 private Token InstantiateJaggedArray (Token t, Token tokenBegin, ref uint repeat)
1103 {
1104 Token start = t;
1105 TokenType ofType = (TokenType)t;
1106
1107 Stack<int> ranks = new Stack<int> ();
1108
1109 /*
1110 * When script specifies 'float[,][]' it means a two-dimensional matrix
1111 * that points to one-dimensional vectors of floats. So we would push
1112 * a 2 then a 1 in this parsing code...
1113 */
1114 do {
1115 t = t.nextToken; // point at '['
1116 int rank = 0;
1117 do {
1118 rank ++; // count '[' and ','s
1119 t = t.nextToken; // point at ',' or ']'
1120 } while (t is TokenKwComma);
1121 if (!(t is TokenKwBrkClose)) {
1122 ErrorMsg (t, "expecting only [ , or ] for array type specification");
1123 return t;
1124 }
1125 ranks.Push (rank);
1126 } while (t.nextToken is TokenKwBrkOpen);
1127
1128 /*
1129 * Now we build the types in reverse order. For the example above we will:
1130 * first, create a type that is a one-dimensional vector of floats, float[]
1131 * second, create a type that is a two-dimensional matrix of that.
1132 * This keeps declaration and referencing similar, eg,
1133 * float[,][] jag = new float[,][] (3,4);
1134 * jag[i,j][k] ... is used to access the elements
1135 */
1136 do {
1137 int rank = ranks.Pop ();
1138 TokenDeclSDType decl = InstantiateFixedArray (rank, ofType, tokenBegin, ref repeat);
1139 ofType = decl.MakeRefToken (ofType);
1140 } while (ranks.Count > 0);
1141
1142 /*
1143 * Finally splice in the resultant array type to replace the original tokens.
1144 */
1145 ofType.prevToken = start.prevToken;
1146 ofType.nextToken = t.nextToken;
1147 ofType.prevToken.nextToken = ofType;
1148 ofType.nextToken.prevToken = ofType;
1149
1150 /*
1151 * Resume parsing just after the spliced-in array type token.
1152 */
1153 return ofType;
1154 }
1155
1156 /**
1157 * @brief Instantiate a script-defined class type to handle fixed-dimension arrays.
1158 * @param rank = number of dimensions for the array
1159 * @param ofType = type of each element of the array
1160 * @returns script-defined class declaration created to handle the array
1161 */
1162 private TokenDeclSDType InstantiateFixedArray (int rank, TokenType ofType, Token tokenBegin, ref uint repeat)
1163 {
1164 /*
1165 * Create the array type's name.
1166 * If starting with a non-array type, just append the rank to it, eg, float + rank=1 -> float[]
1167 * If starting with an array type, slip this rank in front of existing array, eg, float[] + rank=2 -> float[,][].
1168 * This makes it consistent with what the script-writer sees for both a type specification and when
1169 * referencing elements in a jagged array.
1170 */
1171 string name = ofType.ToString ();
1172 StringBuilder sb = new StringBuilder (name);
1173 int ix = name.IndexOf ('[');
1174 if (ix < 0) ix = name.Length;
1175 sb.Insert (ix ++, '[');
1176 for (int i = 0; ++ i < rank;) {
1177 sb.Insert (ix ++, ',');
1178 }
1179 sb.Insert (ix, ']');
1180 name = sb.ToString ();
1181
1182 TokenDeclSDType fa;
1183 if (!tokenScript.sdSrcTypesTryGetValue (name, out fa)) {
1184 char suffix = 'O';
1185 if (ofType is TokenTypeChar) suffix = 'C';
1186 if (ofType is TokenTypeFloat) suffix = 'F';
1187 if (ofType is TokenTypeInt) suffix = 'I';
1188
1189 /*
1190 * Don't already have one, create a new skeleton struct.
1191 * Splice in a definition for the class at beginning of source file.
1192 *
1193 * class <arraytypename> {
1194 */
1195 fa = new TokenDeclSDTypeClass (new TokenName (tokenScript, name), false);
1196 CatalogSDTypeDecl (fa);
1197 repeat |= REPEAT_INSTGEN;
1198 ((TokenDeclSDTypeClass)fa).arrayOfType = ofType;
1199 ((TokenDeclSDTypeClass)fa).arrayOfRank = rank;
1200
1201 Token t = SpliceAfter (tokenBegin, fa);
1202 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1203
1204 /*
1205 * public integer len0;
1206 * public integer len1;
1207 * ...
1208 * public object obj;
1209 */
1210 for (int i = 0; i < rank; i ++) {
1211 t = SpliceAfter (t, new TokenKwPublic (t));
1212 t = SpliceAfter (t, new TokenTypeInt (t));
1213 t = SpliceAfter (t, new TokenName (t, "len" + i));
1214 t = SpliceAfter (t, new TokenKwSemi (t));
1215 }
1216
1217 t = SpliceAfter (t, new TokenKwPublic (t));
1218 t = SpliceAfter (t, new TokenTypeObject (t));
1219 t = SpliceAfter (t, new TokenName (t, "obj"));
1220 t = SpliceAfter (t, new TokenKwSemi (t));
1221
1222 /*
1223 * public constructor (integer len0, integer len1, ...) {
1224 * this.len0 = len0;
1225 * this.len1 = len1;
1226 * ...
1227 * this.obj = xmrFixedArrayAlloc<suffix> (len0 * len1 * ...);
1228 * }
1229 */
1230 t = SpliceAfter (t, new TokenKwPublic (t));
1231 t = SpliceAfter (t, new TokenKwConstructor (t));
1232 t = SpliceAfter (t, new TokenKwParOpen (t));
1233 for (int i = 0; i < rank; i ++) {
1234 if (i > 0) t = SpliceAfter (t, new TokenKwComma (t));
1235 t = SpliceAfter (t, new TokenTypeInt (t));
1236 t = SpliceAfter (t, new TokenName (t, "len" + i));
1237 }
1238 t = SpliceAfter (t, new TokenKwParClose (t));
1239 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1240
1241 for (int i = 0; i < rank; i ++) {
1242 t = SpliceAfter (t, new TokenKwThis (t));
1243 t = SpliceAfter (t, new TokenKwDot (t));
1244 t = SpliceAfter (t, new TokenName (t, "len" + i));
1245 t = SpliceAfter (t, new TokenKwAssign (t));
1246 t = SpliceAfter (t, new TokenName (t, "len" + i));
1247 t = SpliceAfter (t, new TokenKwSemi (t));
1248 }
1249
1250 t = SpliceAfter (t, new TokenKwThis (t));
1251 t = SpliceAfter (t, new TokenKwDot (t));
1252 t = SpliceAfter (t, new TokenName (t, "obj"));
1253 t = SpliceAfter (t, new TokenKwAssign (t));
1254 t = SpliceAfter (t, new TokenName (t, "xmrFixedArrayAlloc" + suffix));
1255 t = SpliceAfter (t, new TokenKwParOpen (t));
1256 for (int i = 0; i < rank; i ++) {
1257 if (i > 0) t = SpliceAfter (t, new TokenKwMul (t));
1258 t = SpliceAfter (t, new TokenName (t, "len" + i));
1259 }
1260 t = SpliceAfter (t, new TokenKwParClose (t));
1261 t = SpliceAfter (t, new TokenKwSemi (t));
1262 t = SpliceAfter (t, new TokenKwBrcClose (t));
1263
1264 /*
1265 * public integer Length { get {
1266 * return this.len0 * this.len1 * ... ;
1267 * } }
1268 */
1269 t = SpliceAfter (t, new TokenKwPublic (t));
1270 t = SpliceAfter (t, new TokenTypeInt (t));
1271 t = SpliceAfter (t, new TokenName (t, "Length"));
1272 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1273 t = SpliceAfter (t, new TokenKwGet (t));
1274 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1275
1276 t = SpliceAfter (t, new TokenKwRet (t));
1277 for (int i = 0; i < rank; i ++) {
1278 if (i > 0) t = SpliceAfter (t, new TokenKwMul (t));
1279 t = SpliceAfter (t, new TokenKwThis (t));
1280 t = SpliceAfter (t, new TokenKwDot (t));
1281 t = SpliceAfter (t, new TokenName (t, "len" + i));
1282 }
1283 t = SpliceAfter (t, new TokenKwSemi (t));
1284
1285 t = SpliceAfter (t, new TokenKwBrcClose (t));
1286 t = SpliceAfter (t, new TokenKwBrcClose (t));
1287
1288 /*
1289 * public integer Length (integer dim) {
1290 * switch (dim) {
1291 * case 0: return this.len0;
1292 * case 1: return this.len1;
1293 * ...
1294 * }
1295 * return 0;
1296 * }
1297 */
1298 t = SpliceAfter (t, new TokenKwPublic (t));
1299 t = SpliceAfter (t, new TokenTypeInt (t));
1300 t = SpliceAfter (t, new TokenName (t, "Length"));
1301 t = SpliceAfter (t, new TokenKwParOpen (t));
1302 t = SpliceAfter (t, new TokenTypeInt (t));
1303 t = SpliceAfter (t, new TokenName (t, "dim"));
1304 t = SpliceAfter (t, new TokenKwParClose (t));
1305 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1306
1307 t = SpliceAfter (t, new TokenKwSwitch (t));
1308 t = SpliceAfter (t, new TokenKwParOpen (t));
1309 t = SpliceAfter (t, new TokenName (t, "dim"));
1310 t = SpliceAfter (t, new TokenKwParClose (t));
1311 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1312
1313 for (int i = 0; i < rank; i ++) {
1314 t = SpliceAfter (t, new TokenKwCase (t));
1315 t = SpliceAfter (t, new TokenInt (t, i));
1316 t = SpliceAfter (t, new TokenKwColon (t));
1317 t = SpliceAfter (t, new TokenKwRet (t));
1318 t = SpliceAfter (t, new TokenKwThis (t));
1319 t = SpliceAfter (t, new TokenKwDot (t));
1320 t = SpliceAfter (t, new TokenName (t, "len" + i));
1321 t = SpliceAfter (t, new TokenKwSemi (t));
1322 }
1323 t = SpliceAfter (t, new TokenKwBrcClose (t));
1324
1325 t = SpliceAfter (t, new TokenKwRet (t));
1326 t = SpliceAfter (t, new TokenInt (t, 0));
1327 t = SpliceAfter (t, new TokenKwSemi (t));
1328 t = SpliceAfter (t, new TokenKwBrcClose (t));
1329
1330 /*
1331 * public integer Index (integer idx0, integet idx1, ...) {
1332 * integer idx = idx0;
1333 * idx *= this.len1; idx += idx1;
1334 * idx *= this.len2; idx += idx2;
1335 * ...
1336 * return idx;
1337 * }
1338 */
1339 t = SpliceAfter (t, new TokenKwPublic (t));
1340 t = SpliceAfter (t, new TokenTypeInt (t));
1341 t = SpliceAfter (t, new TokenName (t, "Index"));
1342 t = SpliceAfter (t, new TokenKwParOpen (t));
1343 for (int i = 0; i < rank; i ++) {
1344 if (i > 0) t = SpliceAfter (t, new TokenKwComma (t));
1345 t = SpliceAfter (t, new TokenTypeInt (t));
1346 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1347 }
1348 t = SpliceAfter (t, new TokenKwParClose (t));
1349 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1350
1351 t = SpliceAfter (t, new TokenTypeInt (t));
1352 t = SpliceAfter (t, new TokenName (t, "idx"));
1353 t = SpliceAfter (t, new TokenKwAssign (t));
1354 t = SpliceAfter (t, new TokenName (t, "idx0"));
1355 t = SpliceAfter (t, new TokenKwSemi (t));
1356
1357 for (int i = 1; i < rank; i ++) {
1358 t = SpliceAfter (t, new TokenName (t, "idx"));
1359 t = SpliceAfter (t, new TokenKwAsnMul (t));
1360 t = SpliceAfter (t, new TokenKwThis (t));
1361 t = SpliceAfter (t, new TokenKwDot (t));
1362 t = SpliceAfter (t, new TokenName (t, "len" + i));
1363 t = SpliceAfter (t, new TokenKwSemi (t));
1364 t = SpliceAfter (t, new TokenName (t, "idx"));
1365 t = SpliceAfter (t, new TokenKwAsnAdd (t));
1366 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1367 t = SpliceAfter (t, new TokenKwSemi (t));
1368 }
1369
1370 t = SpliceAfter (t, new TokenKwRet (t));
1371 t = SpliceAfter (t, new TokenName (t, "idx"));
1372 t = SpliceAfter (t, new TokenKwSemi (t));
1373 t = SpliceAfter (t, new TokenKwBrcClose (t));
1374
1375 /*
1376 * public <oftype> Get (integer idx0, integet idx1, ...) {
1377 * integer idx = idx0;
1378 * idx *= this.len1; idx += idx1;
1379 * idx *= this.len2; idx += idx2;
1380 * ...
1381 * return (<oftype>) xmrFixedArrayGet<suffix> (this.obj, idx);
1382 * }
1383 */
1384 t = SpliceAfter (t, new TokenKwPublic (t));
1385 t = SpliceAfter (t, ofType.CopyToken (t));
1386 t = SpliceAfter (t, new TokenName (t, "Get"));
1387 t = SpliceAfter (t, new TokenKwParOpen (t));
1388 for (int i = 0; i < rank; i ++) {
1389 if (i > 0) t = SpliceAfter (t, new TokenKwComma (t));
1390 t = SpliceAfter (t, new TokenTypeInt (t));
1391 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1392 }
1393 t = SpliceAfter (t, new TokenKwParClose (t));
1394 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1395
1396 t = SpliceAfter (t, new TokenTypeInt (t));
1397 t = SpliceAfter (t, new TokenName (t, "idx"));
1398 t = SpliceAfter (t, new TokenKwAssign (t));
1399 t = SpliceAfter (t, new TokenName (t, "idx0"));
1400 t = SpliceAfter (t, new TokenKwSemi (t));
1401
1402 for (int i = 1; i < rank; i ++) {
1403 t = SpliceAfter (t, new TokenName (t, "idx"));
1404 t = SpliceAfter (t, new TokenKwAsnMul (t));
1405 t = SpliceAfter (t, new TokenKwThis (t));
1406 t = SpliceAfter (t, new TokenKwDot (t));
1407 t = SpliceAfter (t, new TokenName (t, "len" + i));
1408 t = SpliceAfter (t, new TokenKwSemi (t));
1409 t = SpliceAfter (t, new TokenName (t, "idx"));
1410 t = SpliceAfter (t, new TokenKwAsnAdd (t));
1411 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1412 t = SpliceAfter (t, new TokenKwSemi (t));
1413 }
1414
1415 t = SpliceAfter (t, new TokenKwRet (t));
1416 if (suffix == 'O') {
1417 t = SpliceAfter (t, new TokenKwParOpen (t));
1418 t = SpliceAfter (t, ofType.CopyToken (t));
1419 t = SpliceAfter (t, new TokenKwParClose (t));
1420 }
1421 t = SpliceAfter (t, new TokenName (t, "xmrFixedArrayGet" + suffix));
1422 t = SpliceAfter (t, new TokenKwParOpen (t));
1423 t = SpliceAfter (t, new TokenKwThis (t));
1424 t = SpliceAfter (t, new TokenKwDot (t));
1425 t = SpliceAfter (t, new TokenName (t, "obj"));
1426 t = SpliceAfter (t, new TokenKwComma (t));
1427 t = SpliceAfter (t, new TokenName (t, "idx"));
1428 t = SpliceAfter (t, new TokenKwParClose (t));
1429 t = SpliceAfter (t, new TokenKwSemi (t));
1430 t = SpliceAfter (t, new TokenKwBrcClose (t));
1431
1432 /*
1433 * public void Set (integer idx0, integer idx1, ..., <oftype> val) {
1434 * integer idx = idx0;
1435 * idx *= this.len1; idx += idx1;
1436 * idx *= this.len2; idx += idx2;
1437 * ...
1438 * xmrFixedArraySet<suffix> (this.obj, idx, val);
1439 * }
1440 */
1441 t = SpliceAfter (t, new TokenKwPublic (t));
1442 t = SpliceAfter (t, new TokenTypeVoid (t));
1443 t = SpliceAfter (t, new TokenName (t, "Set"));
1444 t = SpliceAfter (t, new TokenKwParOpen (t));
1445 for (int i = 0; i < rank; i ++) {
1446 t = SpliceAfter (t, new TokenTypeInt (t));
1447 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1448 t = SpliceAfter (t, new TokenKwComma (t));
1449 }
1450 t = SpliceAfter (t, ofType.CopyToken (t));
1451 t = SpliceAfter (t, new TokenName (t, "val"));
1452 t = SpliceAfter (t, new TokenKwParClose (t));
1453 t = SpliceAfter (t, new TokenKwBrcOpen (t));
1454
1455 t = SpliceAfter (t, new TokenTypeInt (t));
1456 t = SpliceAfter (t, new TokenName (t, "idx"));
1457 t = SpliceAfter (t, new TokenKwAssign (t));
1458 t = SpliceAfter (t, new TokenName (t, "idx0"));
1459 t = SpliceAfter (t, new TokenKwSemi (t));
1460 for (int i = 1; i < rank; i ++) {
1461 t = SpliceAfter (t, new TokenName (t, "idx"));
1462 t = SpliceAfter (t, new TokenKwAsnMul (t));
1463 t = SpliceAfter (t, new TokenKwThis (t));
1464 t = SpliceAfter (t, new TokenKwDot (t));
1465 t = SpliceAfter (t, new TokenName (t, "len" + i));
1466 t = SpliceAfter (t, new TokenKwSemi (t));
1467 t = SpliceAfter (t, new TokenName (t, "idx"));
1468 t = SpliceAfter (t, new TokenKwAsnAdd (t));
1469 t = SpliceAfter (t, new TokenName (t, "idx" + i));
1470 t = SpliceAfter (t, new TokenKwSemi (t));
1471 }
1472
1473 t = SpliceAfter (t, new TokenName (t, "xmrFixedArraySet" + suffix));
1474 t = SpliceAfter (t, new TokenKwParOpen (t));
1475 t = SpliceAfter (t, new TokenKwThis (t));
1476 t = SpliceAfter (t, new TokenKwDot (t));
1477 t = SpliceAfter (t, new TokenName (t, "obj"));
1478 t = SpliceAfter (t, new TokenKwComma (t));
1479 t = SpliceAfter (t, new TokenName (t, "idx"));
1480 t = SpliceAfter (t, new TokenKwComma (t));
1481 t = SpliceAfter (t, new TokenName (t, "val"));
1482 t = SpliceAfter (t, new TokenKwParClose (t));
1483 t = SpliceAfter (t, new TokenKwSemi (t));
1484
1485 t = SpliceAfter (t, new TokenKwBrcClose (t));
1486 t = SpliceAfter (t, new TokenKwBrcClose (t));
1487 }
1488 return fa;
1489 }
1490 private Token SpliceAfter (Token before, Token after)
1491 {
1492 after.nextToken = before.nextToken;
1493 after.prevToken = before;
1494 before.nextToken = after;
1495 after.nextToken.prevToken = after;
1496 return after;
1497 }
1498
1499 /**
1500 * @brief Parse script-defined type declarations.
1501 * @param token = points to possible script-defined type keyword
1502 * @param outerSDType = null: top-level type
1503 * else: sub-type of this type
1504 * @param flags = access level (SDT_{PRIVATE,PROTECTED,PUBLIC})
1505 * @returns true: something defined; else: not a sd type def
1506 */
1507 private bool ParseDeclSDTypes (ref Token token, TokenDeclSDType outerSDType, uint flags)
1508 {
1509 if (!(token is TokenDeclSDType)) return false;
1510
1511 TokenDeclSDType decl = (TokenDeclSDType)token;
1512
1513 /*
1514 * If declaration of generic type, skip it.
1515 * The instantiations get parsed (ie, when we know the concrete types)
1516 * below because they appear as non-generic types.
1517 */
1518 if (decl.genParams != null) {
1519 token = decl.endToken.nextToken;
1520 return true;
1521 }
1522
1523 /*
1524 * Also skip over any typedefs. They were all processed in
1525 * ParseSDTypePreScanPassTwo().
1526 */
1527 if (decl is TokenDeclSDTypeTypedef) {
1528 token = decl.endToken.nextToken;
1529 return true;
1530 }
1531
1532 /*
1533 * Non-generic types get parsed inline because we know all their types.
1534 */
1535 if (decl is TokenDeclSDTypeClass) {
1536 ParseDeclClass (ref token, outerSDType, flags);
1537 return true;
1538 }
1539 if (decl is TokenDeclSDTypeDelegate) {
1540 ParseDeclDelegate (ref token, outerSDType, flags);
1541 return true;
1542 }
1543 if (decl is TokenDeclSDTypeInterface) {
1544 ParseDeclInterface (ref token, outerSDType, flags);
1545 return true;
1546 }
1547
1548 throw new Exception ("unhandled token " + token.GetType ().ToString ());
1549 }
1550
1551 /**
1552 * @brief Parse a class declaration.
1553 * @param token = points to TokenDeclSDTypeClass token
1554 * points just past closing '}' on return
1555 * @param outerSDType = null: this is a top-level class
1556 * else: this class is being defined inside this type
1557 * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
1558 */
1559 private void ParseDeclClass (ref Token token, TokenDeclSDType outerSDType, uint flags)
1560 {
1561 bool haveExplicitConstructor = false;
1562 Token u = token;
1563 TokenDeclSDTypeClass tokdeclcl;
1564
1565 tokdeclcl = (TokenDeclSDTypeClass)u;
1566 tokdeclcl.outerSDType = outerSDType;
1567 tokdeclcl.accessLevel = flags;
1568 u = u.nextToken;
1569
1570 // maybe it is a partial class that had its body snipped out
1571 // by a later partial class declaration of the same class
1572 if (tokdeclcl.endToken == tokdeclcl) {
1573 token = u;
1574 return;
1575 }
1576
1577 // make this class the currently compiled class
1578 // used for retrieving stuff like 'this' possibly
1579 // in field initialization code
1580 TokenDeclSDType saveCurSDType = currentDeclSDType;
1581 currentDeclSDType = tokdeclcl;
1582
1583 // next can be ':' followed by list of implemented
1584 // interfaces and one extended class
1585 if (u is TokenKwColon) {
1586 u = u.nextToken;
1587 while (true) {
1588 if (u is TokenTypeSDTypeClass) {
1589 TokenDeclSDTypeClass c = ((TokenTypeSDTypeClass)u).decl;
1590 if (tokdeclcl.extends == null) {
1591 tokdeclcl.extends = c;
1592 } else if (tokdeclcl.extends != c) {
1593 ErrorMsg (u, "can extend from only one class");
1594 }
1595 } else if (u is TokenTypeSDTypeInterface) {
1596 TokenDeclSDTypeInterface i = ((TokenTypeSDTypeInterface)u).decl;
1597 i.AddToClassDecl (tokdeclcl);
1598 } else {
1599 ErrorMsg (u, "expecting class or interface name");
1600 if (u is TokenKwBrcOpen) break;
1601 }
1602 u = u.nextToken;
1603
1604 // allow : in case it is spliced from multiple partial class definitions
1605 if (!(u is TokenKwComma) && !(u is TokenKwColon)) break;
1606 u = u.nextToken;
1607 }
1608 }
1609
1610 // next must be '{' to open class declaration body
1611 if (!(u is TokenKwBrcOpen)) {
1612 ErrorMsg (u, "expecting { to open class declaration body");
1613 token = SkipPastSemi (token);
1614 goto ret;
1615 }
1616 token = u.nextToken;
1617
1618 // push a var frame to put all the class members in
1619 tokdeclcl.members.thisClass = tokdeclcl;
1620 tokenScript.PushVarFrame (tokdeclcl.members);
1621
1622 /*
1623 * Create a function $instfieldnit to hold all explicit
1624 * instance field initializations.
1625 */
1626 TokenDeclVar ifiFunc = new TokenDeclVar (tokdeclcl, null, tokenScript);
1627 ifiFunc.name = new TokenName (ifiFunc, "$instfieldinit");
1628 ifiFunc.retType = new TokenTypeVoid (ifiFunc);
1629 ifiFunc.argDecl = new TokenArgDecl (ifiFunc);
1630 ifiFunc.sdtClass = tokdeclcl;
1631 ifiFunc.sdtFlags = SDT_PUBLIC | SDT_NEW;
1632 TokenStmtBlock ifiBody = new TokenStmtBlock (ifiFunc);
1633 ifiBody.function = ifiFunc;
1634 ifiFunc.body = ifiBody;
1635 tokdeclcl.instFieldInit = ifiFunc;
1636 tokenScript.AddVarEntry (ifiFunc);
1637
1638 /*
1639 * Create a function $staticfieldnit to hold all explicit
1640 * static field initializations.
1641 */
1642 TokenDeclVar sfiFunc = new TokenDeclVar (tokdeclcl, null, tokenScript);
1643 sfiFunc.name = new TokenName (sfiFunc, "$staticfieldinit");
1644 sfiFunc.retType = new TokenTypeVoid (sfiFunc);
1645 sfiFunc.argDecl = new TokenArgDecl (sfiFunc);
1646 sfiFunc.sdtClass = tokdeclcl;
1647 sfiFunc.sdtFlags = SDT_PUBLIC | SDT_STATIC | SDT_NEW;
1648 TokenStmtBlock sfiBody = new TokenStmtBlock (sfiFunc);
1649 sfiBody.function = sfiFunc;
1650 sfiFunc.body = sfiBody;
1651 tokdeclcl.staticFieldInit = sfiFunc;
1652 tokenScript.AddVarEntry (sfiFunc);
1653
1654 // process declaration statements until '}'
1655 while (!(token is TokenKwBrcClose)) {
1656 if (token is TokenKwSemi) {
1657 token = token.nextToken;
1658 continue;
1659 }
1660
1661 /*
1662 * Check for all qualifiers.
1663 * typedef has an implied 'public' qualifier.
1664 */
1665 flags = SDT_PUBLIC;
1666 if (!(token is TokenDeclSDTypeTypedef)) {
1667 flags = ParseQualifierFlags (ref token);
1668 }
1669
1670 /*
1671 * Parse nested script-defined type definitions.
1672 */
1673 if (ParseDeclSDTypes (ref token, tokdeclcl, flags)) continue;
1674
1675 /*
1676 * constant <name> = <rval> ;
1677 */
1678 if (token is TokenKwConst) {
1679 if ((flags & (SDT_ABSTRACT | SDT_NEW | SDT_OVERRIDE | SDT_VIRTUAL)) != 0) {
1680 ErrorMsg (token, "cannot have abstract, new, override or virtual field");
1681 }
1682 TokenDeclVar var = ParseDeclVar (ref token, null);
1683 if (var != null) {
1684 var.sdtClass = tokdeclcl;
1685 var.sdtFlags = flags | SDT_STATIC;
1686 }
1687 continue;
1688 }
1689
1690 /*
1691 * <type> <name> ;
1692 * <type> <name> = <rval> ;
1693 */
1694 if ((token is TokenType) &&
1695 (token.nextToken is TokenName) &&
1696 ((token.nextToken.nextToken is TokenKwSemi) ||
1697 (token.nextToken.nextToken is TokenKwAssign))) {
1698 if ((flags & (SDT_ABSTRACT | SDT_FINAL | SDT_NEW | SDT_OVERRIDE | SDT_VIRTUAL)) != 0) {
1699 ErrorMsg (token, "cannot have abstract, final, new, override or virtual field");
1700 }
1701 TokenDeclVar var = ParseDeclVar (ref token, ifiFunc);
1702 if (var != null) {
1703 var.sdtClass = tokdeclcl;
1704 var.sdtFlags = flags;
1705 if ((flags & SDT_STATIC) != 0) {
1706 // <type>.<name> = <init>;
1707 TokenLValSField left = new TokenLValSField (var.init);
1708 left.baseType = tokdeclcl.MakeRefToken (var);
1709 left.fieldName = var.name;
1710 DoVarInit (sfiFunc, left, var.init);
1711 } else if (var.init != null) {
1712 // this.<name> = <init>;
1713 TokenLValIField left = new TokenLValIField (var.init);
1714 left.baseRVal = new TokenRValThis (var.init, tokdeclcl);
1715 left.fieldName = var.name;
1716 DoVarInit (ifiFunc, left, var.init);
1717 }
1718 }
1719 continue;
1720 }
1721
1722 /*
1723 * <type> <name> [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
1724 * <type> '[' ... ']' [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
1725 */
1726 bool prop = (token is TokenType) &&
1727 (token.nextToken is TokenName) &&
1728 (token.nextToken.nextToken is TokenKwBrcOpen ||
1729 token.nextToken.nextToken is TokenKwColon);
1730 prop |= (token is TokenType) && (token.nextToken is TokenKwBrkOpen);
1731 if (prop) {
1732 TokenDeclVar var = ParseProperty (ref token, (flags & SDT_ABSTRACT) != 0, true);
1733 if (var != null) {
1734 var.sdtClass = tokdeclcl;
1735 var.sdtFlags = flags;
1736 if (var.getProp != null) {
1737 var.getProp.sdtClass = tokdeclcl;
1738 var.getProp.sdtFlags = flags;
1739 }
1740 if (var.setProp != null) {
1741 var.setProp.sdtClass = tokdeclcl;
1742 var.setProp.sdtFlags = flags;
1743 }
1744 }
1745 continue;
1746 }
1747
1748 /*
1749 * 'constructor' '(' arglist ')' [ ':' [ 'base' ] '(' baseconstructorcall ')' ] '{' body '}'
1750 */
1751 if (token is TokenKwConstructor) {
1752 ParseSDTClassCtorDecl (ref token, flags, tokdeclcl);
1753 haveExplicitConstructor = true;
1754 continue;
1755 }
1756
1757 /*
1758 * <type> <name> <funcargs> <funcbody>
1759 * method with explicit return type
1760 */
1761 if (token is TokenType) {
1762 ParseSDTClassMethodDecl (ref token, flags, tokdeclcl);
1763 continue;
1764 }
1765
1766 /*
1767 * <name> <funcargs> <funcbody>
1768 * method returning void
1769 */
1770 if ((token is TokenName) || ((token is TokenKw) && ((TokenKw)token).sdtClassOp)) {
1771 ParseSDTClassMethodDecl (ref token, flags, tokdeclcl);
1772 continue;
1773 }
1774
1775 /*
1776 * That's all we support in a class declaration.
1777 */
1778 ErrorMsg (token, "expecting field or method declaration");
1779 token = SkipPastSemi (token);
1780 }
1781
1782 /*
1783 * If script didn't specify any constructor, create a default no-argument one.
1784 */
1785 if (!haveExplicitConstructor) {
1786 TokenDeclVar tokenDeclFunc = new TokenDeclVar (token, null, tokenScript);
1787 tokenDeclFunc.name = new TokenName (token, "$ctor");
1788 tokenDeclFunc.retType = new TokenTypeVoid (token);
1789 tokenDeclFunc.argDecl = new TokenArgDecl (token);
1790 tokenDeclFunc.sdtClass = tokdeclcl;
1791 tokenDeclFunc.sdtFlags = SDT_PUBLIC | SDT_NEW;
1792 tokenDeclFunc.body = new TokenStmtBlock (token);
1793 tokenDeclFunc.body.function = tokenDeclFunc;
1794
1795 if (tokdeclcl.extends != null) {
1796 SetUpDefaultBaseCtorCall (tokenDeclFunc);
1797 } else {
1798 // default constructor that doesn't do anything is trivial
1799 tokenDeclFunc.triviality = Triviality.trivial;
1800 }
1801
1802 tokenScript.AddVarEntry (tokenDeclFunc);
1803 }
1804
1805 /*
1806 * Skip over the closing brace and pop corresponding var frame.
1807 */
1808 token = token.nextToken;
1809 tokenScript.PopVarFrame ();
1810 ret:
1811 currentDeclSDType = saveCurSDType;
1812 }
1813
1814 /**
1815 * @brief Parse out abstract/override/private/protected/public/static/virtual keywords.
1816 * @param token = first token to evaluate
1817 * @returns flags found; token = unprocessed token
1818 */
1819 private Dictionary<uint, Token> foundFlags = new Dictionary<uint, Token> ();
1820 private uint ParseQualifierFlags (ref Token token)
1821 {
1822 foundFlags.Clear ();
1823 while (true) {
1824 if (token is TokenKwPrivate) {
1825 token = AddQualifierFlag (token, SDT_PRIVATE, SDT_PROTECTED | SDT_PUBLIC);
1826 continue;
1827 }
1828 if (token is TokenKwProtected) {
1829 token = AddQualifierFlag (token, SDT_PROTECTED, SDT_PRIVATE | SDT_PUBLIC);
1830 continue;
1831 }
1832 if (token is TokenKwPublic) {
1833 token = AddQualifierFlag (token, SDT_PUBLIC, SDT_PRIVATE | SDT_PROTECTED);
1834 continue;
1835 }
1836
1837 if (token is TokenKwAbstract) {
1838 token = AddQualifierFlag (token, SDT_ABSTRACT, SDT_FINAL | SDT_STATIC | SDT_VIRTUAL);
1839 continue;
1840 }
1841 if (token is TokenKwFinal) {
1842 token = AddQualifierFlag (token, SDT_FINAL, SDT_ABSTRACT | SDT_VIRTUAL);
1843 continue;
1844 }
1845 if (token is TokenKwNew) {
1846 token = AddQualifierFlag (token, SDT_NEW, SDT_OVERRIDE);
1847 continue;
1848 }
1849 if (token is TokenKwOverride) {
1850 token = AddQualifierFlag (token, SDT_OVERRIDE, SDT_NEW | SDT_STATIC);
1851 continue;
1852 }
1853 if (token is TokenKwStatic) {
1854 token = AddQualifierFlag (token, SDT_STATIC, SDT_ABSTRACT | SDT_OVERRIDE | SDT_VIRTUAL);
1855 continue;
1856 }
1857 if (token is TokenKwVirtual) {
1858 token = AddQualifierFlag (token, SDT_VIRTUAL, SDT_ABSTRACT | SDT_STATIC);
1859 continue;
1860 }
1861 break;
1862 }
1863
1864 uint flags = 0;
1865 foreach (uint flag in foundFlags.Keys) flags |= flag;
1866 if ((flags & (SDT_PRIVATE | SDT_PROTECTED | SDT_PUBLIC)) == 0) {
1867 ErrorMsg (token, "must specify exactly one of private, protected or public");
1868 }
1869 return flags;
1870 }
1871 private Token AddQualifierFlag (Token token, uint add, uint confs)
1872 {
1873 while (confs != 0) {
1874 uint conf = (uint)(confs & -confs);
1875 Token confToken;
1876 if (foundFlags.TryGetValue (conf, out confToken)) {
1877 ErrorMsg (token, "conflicts with " + confToken.ToString ());
1878 }
1879 confs -= conf;
1880 }
1881 foundFlags[add] = token;
1882 return token.nextToken;
1883 }
1884
1885 /**
1886 * @brief Parse a property declaration.
1887 * @param token = points to the property type token on entry
1888 * points just past the closing brace on return
1889 * @param abs = true: property is abstract
1890 * false: property is concrete
1891 * @param imp = allow implemented interface specs
1892 * @returns null: parse failure
1893 * else: property
1894 *
1895 * <type> <name> [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
1896 * <type> '[' ... ']' [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
1897 */
1898 private TokenDeclVar ParseProperty (ref Token token, bool abs, bool imp)
1899 {
1900 /*
1901 * Parse out the property's type and name.
1902 * <type> <name>
1903 */
1904 TokenType type = (TokenType)token;
1905 TokenName name;
1906 TokenArgDecl args;
1907 Token argTokens = null;
1908 token = token.nextToken;
1909 if (token is TokenKwBrkOpen) {
1910 argTokens = token;
1911 name = new TokenName (token, "$idxprop");
1912 args = ParseFuncArgs (ref token, typeof (TokenKwBrkClose));
1913 } else {
1914 name = (TokenName)token;
1915 token = token.nextToken;
1916 args = new TokenArgDecl (token);
1917 }
1918
1919 /*
1920 * Maybe it claims to implement some interface properties.
1921 * [ ':' <ifacetype>[.<propname>] ',' ... ]
1922 */
1923 TokenIntfImpl implements = null;
1924 if (token is TokenKwColon) {
1925 implements = ParseImplements (ref token, name);
1926 if (implements == null) return null;
1927 if (!imp) {
1928 ErrorMsg (token, "cannot implement interface property");
1929 }
1930 }
1931
1932 /*
1933 * Should have an opening brace.
1934 */
1935 if (!(token is TokenKwBrcOpen)) {
1936 ErrorMsg (token, "expect { to open property definition");
1937 token = SkipPastSemi (token);
1938 return null;
1939 }
1940 token = token.nextToken;
1941
1942 /*
1943 * Parse out the getter and/or setter.
1944 * 'get' { <body> | ';' }
1945 * 'set' { <body> | ';' }
1946 */
1947 TokenDeclVar getFunc = null;
1948 TokenDeclVar setFunc = null;
1949 while (!(token is TokenKwBrcClose)) {
1950
1951 /*
1952 * Maybe create a getter function.
1953 */
1954 if (token is TokenKwGet) {
1955 getFunc = new TokenDeclVar (token, null, tokenScript);
1956 getFunc.name = new TokenName (token, name.val + "$get");
1957 getFunc.retType = type;
1958 getFunc.argDecl = args;
1959 getFunc.implements = MakePropertyImplements (implements, "$get");
1960
1961 token = token.nextToken;
1962 if (!ParseFunctionBody (ref token, getFunc, abs)) {
1963 getFunc = null;
1964 } else if (!tokenScript.AddVarEntry (getFunc)) {
1965 ErrorMsg (getFunc, "duplicate getter");
1966 }
1967 continue;
1968 }
1969
1970 /*
1971 * Maybe create a setter function.
1972 */
1973 if (token is TokenKwSet) {
1974 TokenArgDecl argDecl = args;
1975 if (getFunc != null) {
1976 argDecl = (argTokens == null) ? new TokenArgDecl (token) :
1977 ParseFuncArgs (ref argTokens, typeof (TokenKwBrkClose));
1978 }
1979 argDecl.AddArg (type, new TokenName (token, "value"));
1980
1981 setFunc = new TokenDeclVar (token, null, tokenScript);
1982 setFunc.name = new TokenName (token, name.val + "$set");
1983 setFunc.retType = new TokenTypeVoid (token);
1984 setFunc.argDecl = argDecl;
1985 setFunc.implements = MakePropertyImplements (implements, "$set");
1986
1987 token = token.nextToken;
1988 if (!ParseFunctionBody (ref token, setFunc, abs)) {
1989 setFunc = null;
1990 } else if (!tokenScript.AddVarEntry (setFunc)) {
1991 ErrorMsg (setFunc, "duplicate setter");
1992 }
1993 continue;
1994 }
1995
1996 ErrorMsg (token, "expecting get or set");
1997 token = SkipPastSemi (token);
1998 return null;
1999 }
2000 token = token.nextToken;
2001
2002 if ((getFunc == null) && (setFunc == null)) {
2003 ErrorMsg (name, "must specify at least one of get, set");
2004 return null;
2005 }
2006
2007 /*
2008 * Set up a variable for the property.
2009 */
2010 TokenDeclVar tokenDeclVar = new TokenDeclVar (name, null, tokenScript);
2011 tokenDeclVar.type = type;
2012 tokenDeclVar.name = name;
2013 tokenDeclVar.getProp = getFunc;
2014 tokenDeclVar.setProp = setFunc;
2015
2016 /*
2017 * Can't be same name already in block.
2018 */
2019 if (!tokenScript.AddVarEntry (tokenDeclVar)) {
2020 ErrorMsg (tokenDeclVar, "duplicate member " + name.val);
2021 return null;
2022 }
2023 return tokenDeclVar;
2024 }
2025
2026 /**
2027 * @brief Given a list of implemented interface methods, create a similar list with suffix added to all the names
2028 * @param implements = original list of implemented interface methods
2029 * @param suffix = string to be added to end of implemented interface method names
2030 * @returns list similar to implements with suffix added to end of implemented interface method names
2031 */
2032 private TokenIntfImpl MakePropertyImplements (TokenIntfImpl implements, string suffix)
2033 {
2034 TokenIntfImpl gsimpls = null;
2035 for (TokenIntfImpl impl = implements; impl != null; impl = (TokenIntfImpl)impl.nextToken) {
2036 TokenIntfImpl gsimpl = new TokenIntfImpl (impl.intfType,
2037 new TokenName (impl.methName, impl.methName.val + suffix));
2038 gsimpl.nextToken = gsimpls;
2039 gsimpls = gsimpl;
2040 }
2041 return gsimpls;
2042 }
2043
2044 /**
2045 * @brief Parse a constructor definition for a script-defined type class.
2046 * @param token = points to 'constructor' keyword
2047 * @param flags = abstract/override/static/virtual flags
2048 * @param tokdeclcl = which script-defined type class this method is in
2049 * @returns with method parsed and cataloged (or error message(s) printed)
2050 */
2051 private void ParseSDTClassCtorDecl (ref Token token, uint flags, TokenDeclSDTypeClass tokdeclcl)
2052 {
2053 if ((flags & (SDT_ABSTRACT | SDT_OVERRIDE | SDT_STATIC | SDT_VIRTUAL)) != 0) {
2054 ErrorMsg (token, "cannot have abstract, override, static or virtual constructor");
2055 }
2056
2057 TokenDeclVar tokenDeclFunc = new TokenDeclVar (token, null, tokenScript);
2058 tokenDeclFunc.name = new TokenName (tokenDeclFunc, "$ctor");
2059 tokenDeclFunc.retType = new TokenTypeVoid (token);
2060 tokenDeclFunc.sdtClass = tokdeclcl;
2061 tokenDeclFunc.sdtFlags = flags | SDT_NEW;
2062
2063 token = token.nextToken;
2064 if (!(token is TokenKwParOpen)) {
2065 ErrorMsg (token, "expecting ( for constructor argument list");
2066 token = SkipPastSemi (token);
2067 return;
2068 }
2069 tokenDeclFunc.argDecl = ParseFuncArgs (ref token, typeof (TokenKwParClose));
2070 if (tokenDeclFunc.argDecl == null) return;
2071
2072 TokenDeclVar saveDeclFunc = currentDeclFunc;
2073 currentDeclFunc = tokenDeclFunc;
2074 tokenScript.PushVarFrame (tokenDeclFunc.argDecl.varDict);
2075 try {
2076 /*
2077 * Set up reference to base constructor.
2078 */
2079 TokenLValBaseField baseCtor = new TokenLValBaseField (token,
2080 new TokenName (token, "$ctor"),
2081 tokdeclcl);
2082
2083 /*
2084 * Parse any base constructor call as if it were the first statement of the
2085 * constructor itself.
2086 */
2087 if (token is TokenKwColon) {
2088 token = token.nextToken;
2089 if (token is TokenKwBase) {
2090 token = token.nextToken;
2091 }
2092 if (!(token is TokenKwParOpen)) {
2093 ErrorMsg (token, "expecting ( for base constructor call arguments");
2094 token = SkipPastSemi (token);
2095 return;
2096 }
2097 TokenRValCall rvc = ParseRValCall (ref token, baseCtor);
2098 if (rvc == null) return;
2099 if (tokdeclcl.extends != null) {
2100 tokenDeclFunc.baseCtorCall = rvc;
2101 tokenDeclFunc.unknownTrivialityCalls.AddLast (rvc);
2102 } else {
2103 ErrorMsg (rvc, "base constructor call cannot be specified if not extending anything");
2104 }
2105 } else if (tokdeclcl.extends != null) {
2106
2107 /*
2108 * Caller didn't specify a constructor but we are extending, so we will
2109 * call the extended class's default constructor.
2110 */
2111 SetUpDefaultBaseCtorCall (tokenDeclFunc);
2112 }
2113
2114 /*
2115 * Parse the constructor body.
2116 */
2117 tokenDeclFunc.body = ParseStmtBlock (ref token);
2118 if (tokenDeclFunc.body == null) return;
2119 if (tokenDeclFunc.argDecl == null) return;
2120 } finally {
2121 tokenScript.PopVarFrame ();
2122 currentDeclFunc = saveDeclFunc;
2123 }
2124
2125 /*
2126 * Add to list of methods defined by this class.
2127 * It has the name "$ctor(argsig)".
2128 */
2129 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
2130 ErrorMsg (tokenDeclFunc, "duplicate constructor definition");
2131 }
2132 }
2133
2134 /**
2135 * @brief Set up a call from a constructor to its default base constructor.
2136 */
2137 private void SetUpDefaultBaseCtorCall (TokenDeclVar thisCtor)
2138 {
2139 TokenLValBaseField baseCtor = new TokenLValBaseField (thisCtor,
2140 new TokenName (thisCtor, "$ctor"),
2141 (TokenDeclSDTypeClass)thisCtor.sdtClass);
2142 TokenRValCall rvc = new TokenRValCall (thisCtor);
2143 rvc.meth = baseCtor;
2144 thisCtor.baseCtorCall = rvc;
2145 thisCtor.unknownTrivialityCalls.AddLast (rvc);
2146 }
2147
2148 /**
2149 * @brief Parse a method definition for a script-defined type class.
2150 * @param token = points to return type (or method name for implicit return type of void)
2151 * @param flags = abstract/override/static/virtual flags
2152 * @param tokdeclcl = which script-defined type class this method is in
2153 * @returns with method parsed and cataloged (or error message(s) printed)
2154 */
2155 private void ParseSDTClassMethodDecl (ref Token token, uint flags, TokenDeclSDTypeClass tokdeclcl)
2156 {
2157 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token,
2158 (flags & SDT_ABSTRACT) != 0,
2159 (flags & SDT_STATIC) == 0,
2160 (flags & SDT_STATIC) == 0);
2161 if (tokenDeclFunc != null) {
2162 tokenDeclFunc.sdtClass = tokdeclcl;
2163 tokenDeclFunc.sdtFlags = flags;
2164 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
2165 string funcNameSig = tokenDeclFunc.funcNameSig.val;
2166 ErrorMsg (tokenDeclFunc.funcNameSig, "duplicate method name " + funcNameSig);
2167 }
2168 }
2169 }
2170
2171 /**
2172 * @brief Parse a delegate declaration statement.
2173 * @param token = points to TokenDeclSDTypeDelegate token on entry
2174 * points just past ';' on return
2175 * @param outerSDType = null: this is a top-level delegate
2176 * else: this delegate is being defined inside this type
2177 * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
2178 */
2179 private void ParseDeclDelegate (ref Token token, TokenDeclSDType outerSDType, uint flags)
2180 {
2181 Token u = token;
2182 TokenDeclSDTypeDelegate tokdecldel;
2183 TokenType retType;
2184
2185 tokdecldel = (TokenDeclSDTypeDelegate)u;
2186 tokdecldel.outerSDType = outerSDType;
2187 tokdecldel.accessLevel = flags;
2188
2189 // first thing following that should be return type
2190 // but we will fill in 'void' if it is missing
2191 u = u.nextToken;
2192 if (u is TokenType) {
2193 retType = (TokenType)u;
2194 u = u.nextToken;
2195 } else {
2196 retType = new TokenTypeVoid (u);
2197 }
2198
2199 // get list of argument types until we see a ')'
2200 List<TokenType> args = new List<TokenType> ();
2201 bool first = true;
2202 do {
2203 if (first) {
2204
2205 // first time should have '(' ')' or '(' <type>
2206 if (!(u is TokenKwParOpen)) {
2207 ErrorMsg (u, "expecting ( after delegate name");
2208 token = SkipPastSemi (token);
2209 return;
2210 }
2211 first = false;
2212 u = u.nextToken;
2213 if (u is TokenKwParClose) break;
2214 } else {
2215
2216 // other times should have ',' <type>
2217 if (!(u is TokenKwComma)) {
2218 ErrorMsg (u, "expecting , separating arg types");
2219 token = SkipPastSemi (token);
2220 return;
2221 }
2222 u = u.nextToken;
2223 }
2224 if (!(u is TokenType)) {
2225 ErrorMsg (u, "expecting argument type");
2226 token = SkipPastSemi (token);
2227 return;
2228 }
2229 args.Add ((TokenType)u);
2230 u = u.nextToken;
2231
2232 // they can put in a dummy name that we toss out
2233 if (u is TokenName) u = u.nextToken;
2234
2235 // scanning ends on a ')'
2236 } while (!(u is TokenKwParClose));
2237
2238 // fill in the return type and argment type array
2239 tokdecldel.SetRetArgTypes (retType, args.ToArray ());
2240
2241 // and finally must have ';' to finish the delegate declaration statement
2242 u = u.nextToken;
2243 if (!(u is TokenKwSemi)) {
2244 ErrorMsg (u, "expecting ; after ) in delegate");
2245 token = SkipPastSemi (token);
2246 return;
2247 }
2248 token = u.nextToken;
2249 }
2250
2251 /**
2252 * @brief Parse an interface declaration.
2253 * @param token = points to TokenDeclSDTypeInterface token on entry
2254 * points just past closing '}' on return
2255 * @param outerSDType = null: this is a top-level interface
2256 * else: this interface is being defined inside this type
2257 * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
2258 */
2259 private void ParseDeclInterface (ref Token token, TokenDeclSDType outerSDType, uint flags)
2260 {
2261 Token u = token;
2262 TokenDeclSDTypeInterface tokdeclin;
2263
2264 tokdeclin = (TokenDeclSDTypeInterface)u;
2265 tokdeclin.outerSDType = outerSDType;
2266 tokdeclin.accessLevel = flags;
2267 u = u.nextToken;
2268
2269 // next can be ':' followed by list of implemented interfaces
2270 if (u is TokenKwColon) {
2271 u = u.nextToken;
2272 while (true) {
2273 if (u is TokenTypeSDTypeInterface) {
2274 TokenDeclSDTypeInterface i = ((TokenTypeSDTypeInterface)u).decl;
2275 if (!tokdeclin.implements.Contains (i)) {
2276 tokdeclin.implements.Add (i);
2277 }
2278 } else {
2279 ErrorMsg (u, "expecting interface name");
2280 if (u is TokenKwBrcOpen) break;
2281 }
2282 u = u.nextToken;
2283 if (!(u is TokenKwComma)) break;
2284 u = u.nextToken;
2285 }
2286 }
2287
2288 // next must be '{' to open interface declaration body
2289 if (!(u is TokenKwBrcOpen)) {
2290 ErrorMsg (u, "expecting { to open interface declaration body");
2291 token = SkipPastSemi (token);
2292 return;
2293 }
2294 token = u.nextToken;
2295
2296 // start a var definition frame to collect the interface members
2297 tokenScript.PushVarFrame (false);
2298 tokdeclin.methsNProps = tokenScript.variablesStack;
2299
2300 // process declaration statements until '}'
2301 while (!(token is TokenKwBrcClose)) {
2302 if (token is TokenKwSemi) {
2303 token = token.nextToken;
2304 continue;
2305 }
2306
2307 /*
2308 * Parse nested script-defined type definitions.
2309 */
2310 if (ParseDeclSDTypes (ref token, tokdeclin, SDT_PUBLIC)) continue;
2311
2312 /*
2313 * <type> <name> <funcargs> ;
2314 * abstract method with explicit return type
2315 */
2316 if ((token is TokenType) &&
2317 (token.nextToken is TokenName) &&
2318 (token.nextToken.nextToken is TokenKwParOpen)) {
2319 Token name = token.nextToken;
2320 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, true, false, false);
2321 if (tokenDeclFunc == null) continue;
2322 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
2323 ErrorMsg (name, "duplicate method name");
2324 continue;
2325 }
2326 continue;
2327 }
2328
2329 /*
2330 * <name> <funcargs> ;
2331 * abstract method returning void
2332 */
2333 if ((token is TokenName) &&
2334 (token.nextToken is TokenKwParOpen)) {
2335 Token name = token;
2336 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, true, false, false);
2337 if (tokenDeclFunc == null) continue;
2338 if (!tokenScript.AddVarEntry (tokenDeclFunc)) {
2339 ErrorMsg (name, "duplicate method name");
2340 }
2341 continue;
2342 }
2343
2344 /*
2345 * <type> <name> { [ get ; ] [ set ; ] }
2346 * <type> '[' ... ']' { [ get ; ] [ set ; ] }
2347 * abstract property
2348 */
2349 bool prop = (token is TokenType) &&
2350 (token.nextToken is TokenName) &&
2351 (token.nextToken.nextToken is TokenKwBrcOpen ||
2352 token.nextToken.nextToken is TokenKwColon);
2353 prop |= (token is TokenType) && (token.nextToken is TokenKwBrkOpen);
2354 if (prop) {
2355 ParseProperty (ref token, true, false);
2356 continue;
2357 }
2358
2359 /*
2360 * That's all we support in an interface declaration.
2361 */
2362 ErrorMsg (token, "expecting method or property prototype");
2363 token = SkipPastSemi (token);
2364 }
2365
2366 /*
2367 * Skip over the closing brace and pop the corresponding var frame.
2368 */
2369 token = token.nextToken;
2370 tokenScript.PopVarFrame ();
2371 }
2372
2373 /**
2374 * @brief parse state body (including all its event handlers)
2375 * @param token = points to TokenKwBrcOpen
2376 * @returns null: state body parse error
2377 * else: token representing state
2378 * token = points past close brace
2379 */
2380 private TokenStateBody ParseStateBody (ref Token token)
2381 {
2382 TokenStateBody tokenStateBody = new TokenStateBody (token);
2383
2384 if (!(token is TokenKwBrcOpen)) {
2385 ErrorMsg (token, "expecting { at beg of state");
2386 token = SkipPastSemi (token);
2387 return null;
2388 }
2389
2390 token = token.nextToken;
2391 while (!(token is TokenKwBrcClose)) {
2392 if (token is TokenEnd) {
2393 ErrorMsg (tokenStateBody, "eof parsing state body");
2394 return null;
2395 }
2396 TokenDeclVar tokenDeclFunc = ParseDeclFunc (ref token, false, false, false);
2397 if (tokenDeclFunc == null) return null;
2398 if (!(tokenDeclFunc.retType is TokenTypeVoid)) {
2399 ErrorMsg (tokenDeclFunc.retType, "event handlers don't have return types");
2400 return null;
2401 }
2402 tokenDeclFunc.nextToken = tokenStateBody.eventFuncs;
2403 tokenStateBody.eventFuncs = tokenDeclFunc;
2404 }
2405 token = token.nextToken;
2406 return tokenStateBody;
2407 }
2408
2409 /**
2410 * @brief Parse a function declaration, including its arg list and body
2411 * @param token = points to function return type token (or function name token if return type void)
2412 * @param abs = false: concrete function; true: abstract declaration
2413 * @param imp = allow implemented interface specs
2414 * @param ops = accept operators (==, +, etc) for function name
2415 * @returns null: error parsing function definition
2416 * else: function declaration
2417 * token = advanced just past function, ie, just past the closing brace
2418 */
2419 private TokenDeclVar ParseDeclFunc (ref Token token, bool abs, bool imp, bool ops)
2420 {
2421 TokenType retType;
2422 if (token is TokenType) {
2423 retType = (TokenType)token;
2424 token = token.nextToken;
2425 } else {
2426 retType = new TokenTypeVoid (token);
2427 }
2428
2429 TokenName simpleName;
2430 if ((token is TokenKw) && ((TokenKw)token).sdtClassOp) {
2431 if (!ops) ErrorMsg (token, "operator functions disallowed in static contexts");
2432 simpleName = new TokenName (token, "$op" + token.ToString ());
2433 } else if (!(token is TokenName)) {
2434 ErrorMsg (token, "expecting function name");
2435 token = SkipPastSemi (token);
2436 return null;
2437 } else {
2438 simpleName = (TokenName)token;
2439 }
2440 token = token.nextToken;
2441
2442 return ParseDeclFunc (ref token, abs, imp, retType, simpleName);
2443 }
2444
2445 /**
2446 * @brief Parse a function declaration, including its arg list and body
2447 * This version enters with token pointing to the '(' at beginning of arg list
2448 * @param token = points to the '(' of the arg list
2449 * @param abs = false: concrete function; true: abstract declaration
2450 * @param imp = allow implemented interface specs
2451 * @param retType = return type (TokenTypeVoid if void, never null)
2452 * @param simpleName = function name without any signature
2453 * @returns null: error parsing remainder of function definition
2454 * else: function declaration
2455 * token = advanced just past function, ie, just past the closing brace
2456 */
2457 private TokenDeclVar ParseDeclFunc (ref Token token, bool abs, bool imp, TokenType retType, TokenName simpleName)
2458 {
2459 TokenDeclVar tokenDeclFunc = new TokenDeclVar (simpleName, null, tokenScript);
2460 tokenDeclFunc.name = simpleName;
2461 tokenDeclFunc.retType = retType;
2462 tokenDeclFunc.argDecl = ParseFuncArgs (ref token, typeof (TokenKwParClose));
2463 if (tokenDeclFunc.argDecl == null) return null;
2464
2465 if (token is TokenKwColon) {
2466 tokenDeclFunc.implements = ParseImplements (ref token, simpleName);
2467 if (tokenDeclFunc.implements == null) return null;
2468 if (!imp) {
2469 ErrorMsg (tokenDeclFunc.implements, "cannot implement interface method");
2470 tokenDeclFunc.implements = null;
2471 }
2472 }
2473
2474 if (!ParseFunctionBody (ref token, tokenDeclFunc, abs)) return null;
2475 if (tokenDeclFunc.argDecl == null) return null;
2476 return tokenDeclFunc;
2477 }
2478
2479 /**
2480 * @brief Parse interface implementation list.
2481 * @param token = points to ':' on entry
2482 * points just past list on return
2483 * @param simpleName = simple name (no arg signature) of method/property that
2484 * is implementing the interface method/property
2485 * @returns list of implemented interface methods/properties
2486 */
2487 private TokenIntfImpl ParseImplements (ref Token token, TokenName simpleName)
2488 {
2489 TokenIntfImpl implements = null;
2490 do {
2491 token = token.nextToken;
2492 if (!(token is TokenTypeSDTypeInterface)) {
2493 ErrorMsg (token, "expecting interface type");
2494 token = SkipPastSemi (token);
2495 return null;
2496 }
2497 TokenTypeSDTypeInterface intfType = (TokenTypeSDTypeInterface)token;
2498 token = token.nextToken;
2499 TokenName methName = simpleName;
2500 if ((token is TokenKwDot) && (token.nextToken is TokenName)) {
2501 methName = (TokenName)token.nextToken;
2502 token = token.nextToken.nextToken;
2503 }
2504 TokenIntfImpl intfImpl = new TokenIntfImpl (intfType, methName);
2505 intfImpl.nextToken = implements;
2506 implements = intfImpl;
2507 } while (token is TokenKwComma);
2508 return implements;
2509 }
2510
2511
2512 /**
2513 * @brief Parse function declaration's body
2514 * @param token = points to body, ie, ';' or '{'
2515 * @param tokenDeclFunc = function being declared
2516 * @param abs = false: concrete function; true: abstract declaration
2517 * @returns whether or not the function definition parsed correctly
2518 */
2519 private bool ParseFunctionBody (ref Token token, TokenDeclVar tokenDeclFunc, bool abs)
2520 {
2521 if (token is TokenKwSemi) {
2522 if (!abs) {
2523 ErrorMsg (token, "concrete function must have body");
2524 token = SkipPastSemi (token);
2525 return false;
2526 }
2527 token = token.nextToken;
2528 return true;
2529 }
2530
2531 /*
2532 * Declare this function as being the one currently being processed
2533 * for anything that cares. We also start a variable frame that
2534 * includes all the declared parameters.
2535 */
2536 TokenDeclVar saveDeclFunc = currentDeclFunc;
2537 currentDeclFunc = tokenDeclFunc;
2538 tokenScript.PushVarFrame (tokenDeclFunc.argDecl.varDict);
2539
2540 /*
2541 * Now parse the function statement block.
2542 */
2543 tokenDeclFunc.body = ParseStmtBlock (ref token);
2544
2545 /*
2546 * Pop the var frame that contains the arguments.
2547 */
2548 tokenScript.PopVarFrame ();
2549 currentDeclFunc = saveDeclFunc;
2550
2551 /*
2552 * Check final errors.
2553 */
2554 if (tokenDeclFunc.body == null) return false;
2555 if (abs) {
2556 ErrorMsg (tokenDeclFunc.body, "abstract function must not have body");
2557 tokenDeclFunc.body = null;
2558 return false;
2559 }
2560 return true;
2561 }
2562
2563
2564 /**
2565 * @brief Parse statement
2566 * @param token = first token of statement
2567 * @returns null: parse error
2568 * else: token representing whole statement
2569 * token = points past statement
2570 */
2571 private TokenStmt ParseStmt (ref Token token)
2572 {
2573 /*
2574 * Statements that begin with a specific keyword.
2575 */
2576 if (token is TokenKwAt) return ParseStmtLabel (ref token);
2577 if (token is TokenKwBrcOpen) return ParseStmtBlock (ref token);
2578 if (token is TokenKwBreak) return ParseStmtBreak (ref token);
2579 if (token is TokenKwCont) return ParseStmtCont (ref token);
2580 if (token is TokenKwDo) return ParseStmtDo (ref token);
2581 if (token is TokenKwFor) return ParseStmtFor (ref token);
2582 if (token is TokenKwForEach) return ParseStmtForEach (ref token);
2583 if (token is TokenKwIf) return ParseStmtIf (ref token);
2584 if (token is TokenKwJump) return ParseStmtJump (ref token);
2585 if (token is TokenKwRet) return ParseStmtRet (ref token);
2586 if (token is TokenKwSemi) return ParseStmtNull (ref token);
2587 if (token is TokenKwState) return ParseStmtState (ref token);
2588 if (token is TokenKwSwitch) return ParseStmtSwitch (ref token);
2589 if (token is TokenKwThrow) return ParseStmtThrow (ref token);
2590 if (token is TokenKwTry) return ParseStmtTry (ref token);
2591 if (token is TokenKwWhile) return ParseStmtWhile (ref token);
2592
2593 /*
2594 * Try to parse anything else as an expression, possibly calling
2595 * something and/or writing to a variable.
2596 */
2597 TokenRVal tokenRVal = ParseRVal (ref token, semiOnly);
2598 if (tokenRVal != null) {
2599 TokenStmtRVal tokenStmtRVal = new TokenStmtRVal (tokenRVal);
2600 tokenStmtRVal.rVal = tokenRVal;
2601 return tokenStmtRVal;
2602 }
2603
2604 /*
2605 * Who knows what it is...
2606 */
2607 ErrorMsg (token, "unknown statement");
2608 token = SkipPastSemi (token);
2609 return null;
2610 }
2611
2612 /**
2613 * @brief parse a statement block, ie, group of statements between braces
2614 * @param token = points to { token
2615 * @returns null: error parsing
2616 * else: statements bundled in this token
2617 * token = advanced just past the } token
2618 */
2619 private TokenStmtBlock ParseStmtBlock (ref Token token)
2620 {
2621 if (!(token is TokenKwBrcOpen)) {
2622 ErrorMsg (token, "statement block body must begin with a {");
2623 token = SkipPastSemi (token);
2624 return null;
2625 }
2626 TokenStmtBlock tokenStmtBlock = new TokenStmtBlock (token);
2627 tokenStmtBlock.function = currentDeclFunc;
2628 tokenStmtBlock.outerStmtBlock = currentStmtBlock;
2629 currentStmtBlock = tokenStmtBlock;
2630 VarDict outerVariablesStack = tokenScript.variablesStack;
2631 try {
2632 Token prevStmt = null;
2633 token = token.nextToken;
2634 while (!(token is TokenKwBrcClose)) {
2635 if (token is TokenEnd) {
2636 ErrorMsg (tokenStmtBlock, "missing }");
2637 return null;
2638 }
2639 Token thisStmt;
2640 if (((token is TokenType) && (token.nextToken is TokenName)) ||
2641 (token is TokenKwConst)) {
2642 thisStmt = ParseDeclVar (ref token, null);
2643 } else {
2644 thisStmt = ParseStmt (ref token);
2645 }
2646 if (thisStmt == null) return null;
2647 if (prevStmt == null) tokenStmtBlock.statements = thisStmt;
2648 else prevStmt.nextToken = thisStmt;
2649 prevStmt = thisStmt;
2650 }
2651 token = token.nextToken;
2652 } finally {
2653 tokenScript.variablesStack = outerVariablesStack;
2654 currentStmtBlock = tokenStmtBlock.outerStmtBlock;
2655 }
2656 return tokenStmtBlock;
2657 }
2658
2659 /**
2660 * @brief parse a 'break' statement
2661 * @param token = points to break keyword token
2662 * @returns null: error parsing
2663 * else: statements bundled in this token
2664 * token = advanced just past the ; token
2665 */
2666 private TokenStmtBreak ParseStmtBreak (ref Token token)
2667 {
2668 TokenStmtBreak tokenStmtBreak = new TokenStmtBreak (token);
2669 token = token.nextToken;
2670 if (!(token is TokenKwSemi)) {
2671 ErrorMsg (token, "expecting ;");
2672 token = SkipPastSemi (token);
2673 return null;
2674 }
2675 token = token.nextToken;
2676 return tokenStmtBreak;
2677 }
2678
2679 /**
2680 * @brief parse a 'continue' statement
2681 * @param token = points to continue keyword token
2682 * @returns null: error parsing
2683 * else: statements bundled in this token
2684 * token = advanced just past the ; token
2685 */
2686 private TokenStmtCont ParseStmtCont (ref Token token)
2687 {
2688 TokenStmtCont tokenStmtCont = new TokenStmtCont (token);
2689 token = token.nextToken;
2690 if (!(token is TokenKwSemi)) {
2691 ErrorMsg (token, "expecting ;");
2692 token = SkipPastSemi (token);
2693 return null;
2694 }
2695 token = token.nextToken;
2696 return tokenStmtCont;
2697 }
2698
2699 /**
2700 * @brief parse a 'do' statement
2701 * @params token = points to 'do' keyword token
2702 * @returns null: parse error
2703 * else: pointer to token encapsulating the do statement, including body
2704 * token = advanced just past the body statement
2705 */
2706 private TokenStmtDo ParseStmtDo (ref Token token)
2707 {
2708 currentDeclFunc.triviality = Triviality.complex;
2709 TokenStmtDo tokenStmtDo = new TokenStmtDo (token);
2710 token = token.nextToken;
2711 tokenStmtDo.bodyStmt = ParseStmt (ref token);
2712 if (tokenStmtDo.bodyStmt == null) return null;
2713 if (!(token is TokenKwWhile)) {
2714 ErrorMsg (token, "expecting while clause");
2715 return null;
2716 }
2717 token = token.nextToken;
2718 tokenStmtDo.testRVal = ParseRValParen (ref token);
2719 if (tokenStmtDo.testRVal == null) return null;
2720 if (!(token is TokenKwSemi)) {
2721 ErrorMsg (token, "while clause must terminate on semicolon");
2722 token = SkipPastSemi (token);
2723 return null;
2724 }
2725 token = token.nextToken;
2726 return tokenStmtDo;
2727 }
2728
2729 /**
2730 * @brief parse a for statement
2731 * @param token = points to 'for' keyword token
2732 * @returns null: parse error
2733 * else: pointer to encapsulated for statement token
2734 * token = advanced just past for body statement
2735 */
2736 private TokenStmt ParseStmtFor (ref Token token)
2737 {
2738 currentDeclFunc.triviality = Triviality.complex;
2739
2740 /*
2741 * Create encapsulating token and skip past 'for ('
2742 */
2743 TokenStmtFor tokenStmtFor = new TokenStmtFor (token);
2744 token = token.nextToken;
2745 if (!(token is TokenKwParOpen)) {
2746 ErrorMsg (token, "for must be followed by (");
2747 return null;
2748 }
2749 token = token.nextToken;
2750
2751 /*
2752 * If a plain for, ie, not declaring a variable, it's straightforward.
2753 */
2754 if (!(token is TokenType)) {
2755 tokenStmtFor.initStmt = ParseStmt (ref token);
2756 if (tokenStmtFor.initStmt == null) return null;
2757 return ParseStmtFor2 (tokenStmtFor, ref token) ? tokenStmtFor : null;
2758 }
2759
2760 /*
2761 * Initialization declares a variable, so encapsulate it in a block so
2762 * variable has scope only in the for statement, including its body.
2763 */
2764 TokenStmtBlock forStmtBlock = new TokenStmtBlock (tokenStmtFor);
2765 forStmtBlock.outerStmtBlock = currentStmtBlock;
2766 forStmtBlock.function = currentDeclFunc;
2767 currentStmtBlock = forStmtBlock;
2768 tokenScript.PushVarFrame (true);
2769
2770 TokenDeclVar tokenDeclVar = ParseDeclVar (ref token, null);
2771 if (tokenDeclVar == null) {
2772 tokenScript.PopVarFrame ();
2773 currentStmtBlock = forStmtBlock.outerStmtBlock;
2774 return null;
2775 }
2776
2777 forStmtBlock.statements = tokenDeclVar;
2778 tokenDeclVar.nextToken = tokenStmtFor;
2779
2780 bool ok = ParseStmtFor2 (tokenStmtFor, ref token);
2781 tokenScript.PopVarFrame ();
2782 currentStmtBlock = forStmtBlock.outerStmtBlock;
2783 return ok ? forStmtBlock : null;
2784 }
2785
2786 /**
2787 * @brief parse rest of 'for' statement starting with the test expression.
2788 * @param tokenStmtFor = token encapsulating the for statement
2789 * @param token = points to test expression
2790 * @returns false: parse error
2791 * true: successful
2792 * token = points just past body statement
2793 */
2794 private bool ParseStmtFor2 (TokenStmtFor tokenStmtFor, ref Token token)
2795 {
2796 if (token is TokenKwSemi) {
2797 token = token.nextToken;
2798 } else {
2799 tokenStmtFor.testRVal = ParseRVal (ref token, semiOnly);
2800 if (tokenStmtFor.testRVal == null) return false;
2801 }
2802 if (token is TokenKwParClose) {
2803 token = token.nextToken;
2804 } else {
2805 tokenStmtFor.incrRVal = ParseRVal (ref token, parCloseOnly);
2806 if (tokenStmtFor.incrRVal == null) return false;
2807 }
2808 tokenStmtFor.bodyStmt = ParseStmt (ref token);
2809 return tokenStmtFor.bodyStmt != null;
2810 }
2811
2812 /**
2813 * @brief parse a foreach statement
2814 * @param token = points to 'foreach' keyword token
2815 * @returns null: parse error
2816 * else: pointer to encapsulated foreach statement token
2817 * token = advanced just past for body statement
2818 */
2819 private TokenStmt ParseStmtForEach (ref Token token)
2820 {
2821 currentDeclFunc.triviality = Triviality.complex;
2822
2823 /*
2824 * Create encapsulating token and skip past 'foreach ('
2825 */
2826 TokenStmtForEach tokenStmtForEach = new TokenStmtForEach (token);
2827 token = token.nextToken;
2828 if (!(token is TokenKwParOpen)) {
2829 ErrorMsg (token, "foreach must be followed by (");
2830 return null;
2831 }
2832 token = token.nextToken;
2833
2834 if (token is TokenName) {
2835 tokenStmtForEach.keyLVal = new TokenLValName ((TokenName)token, tokenScript.variablesStack);
2836 token = token.nextToken;
2837 }
2838 if (!(token is TokenKwComma)) {
2839 ErrorMsg (token, "expecting comma");
2840 token = SkipPastSemi (token);
2841 return null;
2842 }
2843 token = token.nextToken;
2844 if (token is TokenName) {
2845 tokenStmtForEach.valLVal = new TokenLValName ((TokenName)token, tokenScript.variablesStack);
2846 token = token.nextToken;
2847 }
2848 if (!(token is TokenKwIn)) {
2849 ErrorMsg (token, "expecting 'in'");
2850 token = SkipPastSemi (token);
2851 return null;
2852 }
2853 token = token.nextToken;
2854 tokenStmtForEach.arrayRVal = GetOperand (ref token);
2855 if (tokenStmtForEach.arrayRVal == null) return null;
2856 if (!(token is TokenKwParClose)) {
2857 ErrorMsg (token, "expecting )");
2858 token = SkipPastSemi (token);
2859 return null;
2860 }
2861 token = token.nextToken;
2862 tokenStmtForEach.bodyStmt = ParseStmt (ref token);
2863 if (tokenStmtForEach.bodyStmt == null) return null;
2864 return tokenStmtForEach;
2865 }
2866
2867 private TokenStmtIf ParseStmtIf (ref Token token)
2868 {
2869 TokenStmtIf tokenStmtIf = new TokenStmtIf (token);
2870 token = token.nextToken;
2871 tokenStmtIf.testRVal = ParseRValParen (ref token);
2872 if (tokenStmtIf.testRVal == null) return null;
2873 tokenStmtIf.trueStmt = ParseStmt (ref token);
2874 if (tokenStmtIf.trueStmt == null) return null;
2875 if (token is TokenKwElse) {
2876 token = token.nextToken;
2877 tokenStmtIf.elseStmt = ParseStmt (ref token);
2878 if (tokenStmtIf.elseStmt == null) return null;
2879 }
2880 return tokenStmtIf;
2881 }
2882
2883 private TokenStmtJump ParseStmtJump (ref Token token)
2884 {
2885 /*
2886 * Create jump statement token to encapsulate the whole statement.
2887 */
2888 TokenStmtJump tokenStmtJump = new TokenStmtJump (token);
2889 token = token.nextToken;
2890 if (!(token is TokenName) || !(token.nextToken is TokenKwSemi)) {
2891 ErrorMsg (token, "expecting label;");
2892 token = SkipPastSemi (token);
2893 return null;
2894 }
2895 tokenStmtJump.label = (TokenName)token;
2896 token = token.nextToken.nextToken;
2897
2898 /*
2899 * If label is already defined, it means this is a backward (looping)
2900 * jump, so remember the label has backward jump references.
2901 * We also then assume the function is complex, ie, it has a loop.
2902 */
2903 if (currentDeclFunc.labels.ContainsKey (tokenStmtJump.label.val)) {
2904 currentDeclFunc.labels[tokenStmtJump.label.val].hasBkwdRefs = true;
2905 currentDeclFunc.triviality = Triviality.complex;
2906 }
2907
2908 return tokenStmtJump;
2909 }
2910
2911 /**
2912 * @brief parse a jump target label statement
2913 * @param token = points to the '@' token
2914 * @returns null: error parsing
2915 * else: the label
2916 * token = advanced just past the ;
2917 */
2918 private TokenStmtLabel ParseStmtLabel (ref Token token)
2919 {
2920 if (!(token.nextToken is TokenName) ||
2921 !(token.nextToken.nextToken is TokenKwSemi)) {
2922 ErrorMsg (token, "invalid label");
2923 token = SkipPastSemi (token);
2924 return null;
2925 }
2926 TokenStmtLabel stmtLabel = new TokenStmtLabel (token);
2927 stmtLabel.name = (TokenName)token.nextToken;
2928 stmtLabel.block = currentStmtBlock;
2929 if (currentDeclFunc.labels.ContainsKey (stmtLabel.name.val)) {
2930 ErrorMsg (token.nextToken, "duplicate label");
2931 ErrorMsg (currentDeclFunc.labels[stmtLabel.name.val], "previously defined here");
2932 token = SkipPastSemi (token);
2933 return null;
2934 }
2935 currentDeclFunc.labels.Add (stmtLabel.name.val, stmtLabel);
2936 token = token.nextToken.nextToken.nextToken;
2937 return stmtLabel;
2938 }
2939
2940 private TokenStmtNull ParseStmtNull (ref Token token)
2941 {
2942 TokenStmtNull tokenStmtNull = new TokenStmtNull (token);
2943 token = token.nextToken;
2944 return tokenStmtNull;
2945 }
2946
2947 private TokenStmtRet ParseStmtRet (ref Token token)
2948 {
2949 TokenStmtRet tokenStmtRet = new TokenStmtRet (token);
2950 token = token.nextToken;
2951 if (token is TokenKwSemi) {
2952 token = token.nextToken;
2953 } else {
2954 tokenStmtRet.rVal = ParseRVal (ref token, semiOnly);
2955 if (tokenStmtRet.rVal == null) return null;
2956 }
2957 return tokenStmtRet;
2958 }
2959
2960 private TokenStmtSwitch ParseStmtSwitch (ref Token token)
2961 {
2962 TokenStmtSwitch tokenStmtSwitch = new TokenStmtSwitch (token);
2963 token = token.nextToken;
2964 tokenStmtSwitch.testRVal = ParseRValParen (ref token);
2965 if (tokenStmtSwitch.testRVal == null) return null;
2966 if (!(token is TokenKwBrcOpen)) {
2967 ErrorMsg (token, "expecting open brace");
2968 token = SkipPastSemi (token);
2969 return null;
2970 }
2971 token = token.nextToken;
2972 TokenSwitchCase tokenSwitchCase = null;
2973 bool haveComplained = false;
2974 while (!(token is TokenKwBrcClose)) {
2975 if (token is TokenKwCase) {
2976 tokenSwitchCase = new TokenSwitchCase (token);
2977 if (tokenStmtSwitch.lastCase == null) {
2978 tokenStmtSwitch.cases = tokenSwitchCase;
2979 } else {
2980 tokenStmtSwitch.lastCase.nextCase = tokenSwitchCase;
2981 }
2982 tokenStmtSwitch.lastCase = tokenSwitchCase;
2983
2984 token = token.nextToken;
2985 tokenSwitchCase.rVal1 = ParseRVal (ref token, colonOrDotDotDot);
2986 if (tokenSwitchCase.rVal1 == null) return null;
2987 if (token is TokenKwDotDotDot) {
2988 token = token.nextToken;
2989 tokenSwitchCase.rVal2 = ParseRVal (ref token, colonOnly);
2990 if (tokenSwitchCase.rVal2 == null) return null;
2991 } else {
2992 if (!(token is TokenKwColon)) {
2993 ErrorMsg (token, "expecting : or ...");
2994 token = SkipPastSemi (token);
2995 return null;
2996 }
2997 token = token.nextToken;
2998 }
2999 } else if (token is TokenKwDefault) {
3000 tokenSwitchCase = new TokenSwitchCase (token);
3001 if (tokenStmtSwitch.lastCase == null) {
3002 tokenStmtSwitch.cases = tokenSwitchCase;
3003 } else {
3004 tokenStmtSwitch.lastCase.nextCase = tokenSwitchCase;
3005 }
3006 tokenStmtSwitch.lastCase = tokenSwitchCase;
3007
3008 token = token.nextToken;
3009 if (!(token is TokenKwColon)) {
3010 ErrorMsg (token, "expecting :");
3011 token = SkipPastSemi (token);
3012 return null;
3013 }
3014 token = token.nextToken;
3015 } else if (tokenSwitchCase != null) {
3016 TokenStmt bodyStmt = ParseStmt (ref token);
3017 if (bodyStmt == null) return null;
3018 if (tokenSwitchCase.lastStmt == null) {
3019 tokenSwitchCase.stmts = bodyStmt;
3020 } else {
3021 tokenSwitchCase.lastStmt.nextToken = bodyStmt;
3022 }
3023 tokenSwitchCase.lastStmt = bodyStmt;
3024 bodyStmt.nextToken = null;
3025 } else if (!haveComplained) {
3026 ErrorMsg (token, "expecting case or default label");
3027 token = SkipPastSemi (token);
3028 haveComplained = true;
3029 }
3030 }
3031 token = token.nextToken;
3032 return tokenStmtSwitch;
3033 }
3034
3035 private TokenStmtState ParseStmtState (ref Token token)
3036 {
3037 TokenStmtState tokenStmtState = new TokenStmtState (token);
3038 token = token.nextToken;
3039 if ((!(token is TokenName) && !(token is TokenKwDefault)) || !(token.nextToken is TokenKwSemi)) {
3040 ErrorMsg (token, "expecting state;");
3041 token = SkipPastSemi (token);
3042 return null;
3043 }
3044 if (token is TokenName) {
3045 tokenStmtState.state = (TokenName)token;
3046 }
3047 token = token.nextToken.nextToken;
3048 return tokenStmtState;
3049 }
3050
3051 private TokenStmtThrow ParseStmtThrow (ref Token token)
3052 {
3053 TokenStmtThrow tokenStmtThrow = new TokenStmtThrow (token);
3054 token = token.nextToken;
3055 if (token is TokenKwSemi) {
3056 token = token.nextToken;
3057 } else {
3058 tokenStmtThrow.rVal = ParseRVal (ref token, semiOnly);
3059 if (tokenStmtThrow.rVal == null) return null;
3060 }
3061 return tokenStmtThrow;
3062 }
3063
3064 /**
3065 * @brief Parse a try { ... } catch { ... } finally { ... } statement block
3066 * @param token = point to 'try' keyword on entry
3067 * points past last '}' processed on return
3068 * @returns encapsulated try/catch/finally or null if parsing error
3069 */
3070 private TokenStmtTry ParseStmtTry (ref Token token)
3071 {
3072 /*
3073 * Parse out the 'try { ... }' part
3074 */
3075 Token tryKw = token;
3076 token = token.nextToken;
3077 TokenStmt body = ParseStmtBlock (ref token);
3078
3079 while (true) {
3080 TokenStmtTry tokenStmtTry;
3081 if (token is TokenKwCatch) {
3082 if (!(token.nextToken is TokenKwParOpen) ||
3083 !(token.nextToken.nextToken is TokenType) ||
3084 !(token.nextToken.nextToken.nextToken is TokenName) ||
3085 !(token.nextToken.nextToken.nextToken.nextToken is TokenKwParClose)) {
3086 ErrorMsg (token, "catch must be followed by ( <type> <varname> ) { <statement>... }");
3087 return null;
3088 }
3089 token = token.nextToken.nextToken; // skip over 'catch' '('
3090 TokenDeclVar tag = new TokenDeclVar (token.nextToken, currentDeclFunc, tokenScript);
3091 tag.type = (TokenType)token;
3092 token = token.nextToken; // skip over <type>
3093 tag.name = (TokenName)token;
3094 token = token.nextToken.nextToken; // skip over <varname> ')'
3095
3096 if ((!(tag.type is TokenTypeExc)) && (!(tag.type is TokenTypeStr))) {
3097 ErrorMsg (tag.type, "must be type 'exception' or 'string'");
3098 }
3099
3100 tokenStmtTry = new TokenStmtTry (tryKw);
3101 tokenStmtTry.tryStmt = WrapTryCatFinInBlock (body);
3102 tokenStmtTry.catchVar = tag;
3103 tokenScript.PushVarFrame (false);
3104 tokenScript.AddVarEntry (tag);
3105 tokenStmtTry.catchStmt = ParseStmtBlock (ref token);
3106 tokenScript.PopVarFrame ();
3107 if (tokenStmtTry.catchStmt == null) return null;
3108 tokenStmtTry.tryStmt.isTry = true;
3109 tokenStmtTry.tryStmt.tryStmt = tokenStmtTry;
3110 tokenStmtTry.catchStmt.isCatch = true;
3111 tokenStmtTry.catchStmt.tryStmt = tokenStmtTry;
3112 }
3113 else if (token is TokenKwFinally) {
3114 token = token.nextToken;
3115
3116 tokenStmtTry = new TokenStmtTry (tryKw);
3117 tokenStmtTry.tryStmt = WrapTryCatFinInBlock (body);
3118 tokenStmtTry.finallyStmt = ParseStmtBlock (ref token);
3119 if (tokenStmtTry.finallyStmt == null) return null;
3120 tokenStmtTry.tryStmt.isTry = true;
3121 tokenStmtTry.tryStmt.tryStmt = tokenStmtTry;
3122 tokenStmtTry.finallyStmt.isFinally = true;
3123 tokenStmtTry.finallyStmt.tryStmt = tokenStmtTry;
3124 }
3125 else break;
3126
3127 body = tokenStmtTry;
3128 }
3129
3130 if (!(body is TokenStmtTry)) {
3131 ErrorMsg (body, "try must have a matching catch and/or finally");
3132 return null;
3133 }
3134 return (TokenStmtTry)body;
3135 }
3136
3137 /**
3138 * @brief Wrap a possible try/catch/finally statement block in a block statement.
3139 *
3140 * Given body = try { } catch (string s) { }
3141 *
3142 * we return { try { } catch (string s) { } }
3143 *
3144 * @param body = a TokenStmtTry or a TokenStmtBlock
3145 * @returns a TokenStmtBlock
3146 */
3147 private TokenStmtBlock WrapTryCatFinInBlock (TokenStmt body)
3148 {
3149 if (body is TokenStmtBlock) return (TokenStmtBlock)body;
3150
3151 TokenStmtTry innerTry = (TokenStmtTry)body;
3152
3153 TokenStmtBlock wrapper = new TokenStmtBlock (body);
3154 wrapper.statements = innerTry;
3155 wrapper.outerStmtBlock = currentStmtBlock;
3156 wrapper.function = currentDeclFunc;
3157
3158 innerTry.tryStmt.outerStmtBlock = wrapper;
3159 if (innerTry.catchStmt != null) innerTry.catchStmt.outerStmtBlock = wrapper;
3160 if (innerTry.finallyStmt != null) innerTry.finallyStmt.outerStmtBlock = wrapper;
3161
3162 return wrapper;
3163 }
3164
3165 private TokenStmtWhile ParseStmtWhile (ref Token token)
3166 {
3167 currentDeclFunc.triviality = Triviality.complex;
3168 TokenStmtWhile tokenStmtWhile = new TokenStmtWhile (token);
3169 token = token.nextToken;
3170 tokenStmtWhile.testRVal = ParseRValParen (ref token);
3171 if (tokenStmtWhile.testRVal == null) return null;
3172 tokenStmtWhile.bodyStmt = ParseStmt (ref token);
3173 if (tokenStmtWhile.bodyStmt == null) return null;
3174 return tokenStmtWhile;
3175 }
3176
3177 /**
3178 * @brief parse a variable declaration statement, including init value if any.
3179 * @param token = points to type or 'constant' token
3180 * @param initFunc = null: parsing a local var declaration
3181 * put initialization code in .init
3182 * else: parsing a global var or field var declaration
3183 * put initialization code in initFunc.body
3184 * @returns null: parsing error
3185 * else: variable declaration encapulating token
3186 * token = advanced just past semi-colon
3187 * variables = modified to include the new variable
3188 */
3189 private TokenDeclVar ParseDeclVar (ref Token token, TokenDeclVar initFunc)
3190 {
3191 TokenDeclVar tokenDeclVar = new TokenDeclVar (token.nextToken, currentDeclFunc, tokenScript);
3192
3193 /*
3194 * Handle constant declaration.
3195 * It ends up in the declared variables list for the statement block just like
3196 * any other variable, except it has .constant = true.
3197 * The code generator will test that the initialization expression is constant.
3198 *
3199 * constant <name> = <value> ;
3200 */
3201 if (token is TokenKwConst) {
3202 token = token.nextToken;
3203 if (!(token is TokenName)) {
3204 ErrorMsg (token, "expecting constant name");
3205 token = SkipPastSemi (token);
3206 return null;
3207 }
3208 tokenDeclVar.name = (TokenName)token;
3209 token = token.nextToken;
3210 if (!(token is TokenKwAssign)) {
3211 ErrorMsg (token, "expecting =");
3212 token = SkipPastSemi (token);
3213 return null;
3214 }
3215 token = token.nextToken;
3216 TokenRVal rVal = ParseRVal (ref token, semiOnly);
3217 if (rVal == null) return null;
3218 tokenDeclVar.init = rVal;
3219 tokenDeclVar.constant = true;
3220 }
3221
3222 /*
3223 * Otherwise, normal variable declaration with optional initialization value.
3224 */
3225 else {
3226 /*
3227 * Build basic encapsulating token with type and name.
3228 */
3229 tokenDeclVar.type = (TokenType)token;
3230 token = token.nextToken;
3231 if (!(token is TokenName)) {
3232 ErrorMsg (token, "expecting variable name");
3233 token = SkipPastSemi (token);
3234 return null;
3235 }
3236 tokenDeclVar.name = (TokenName)token;
3237 token = token.nextToken;
3238
3239 /*
3240 * If just a ;, there is no explicit initialization value.
3241 * Otherwise, look for an =RVal; expression that has init value.
3242 */
3243 if (token is TokenKwSemi) {
3244 token = token.nextToken;
3245 if (initFunc != null) {
3246 tokenDeclVar.init = TokenRValInitDef.Construct (tokenDeclVar);
3247 }
3248 } else if (token is TokenKwAssign) {
3249 token = token.nextToken;
3250 if (initFunc != null) {
3251 currentDeclFunc = initFunc;
3252 tokenDeclVar.init = ParseRVal (ref token, semiOnly);
3253 currentDeclFunc = null;
3254 } else {
3255 tokenDeclVar.init = ParseRVal (ref token, semiOnly);
3256 }
3257 if (tokenDeclVar.init == null) return null;
3258 } else {
3259 ErrorMsg (token, "expecting = or ;");
3260 token = SkipPastSemi (token);
3261 return null;
3262 }
3263 }
3264
3265 /*
3266 * If doing local vars, each var goes in its own var frame,
3267 * to make sure no code before this point can reference it.
3268 */
3269 if (currentStmtBlock != null) {
3270 tokenScript.PushVarFrame (true);
3271 }
3272
3273 /*
3274 * Can't be same name already in block.
3275 */
3276 if (!tokenScript.AddVarEntry (tokenDeclVar)) {
3277 ErrorMsg (tokenDeclVar, "duplicate variable " + tokenDeclVar.name.val);
3278 return null;
3279 }
3280 return tokenDeclVar;
3281 }
3282
3283 /**
3284 * @brief Add variable initialization to $globalvarinit, $staticfieldinit or $instfieldinit function.
3285 * @param initFunc = $globalvarinit, $staticfieldinit or $instfieldinit function
3286 * @param left = variable being initialized
3287 * @param init = null: initialize to default value
3288 * else: initialize to this value
3289 */
3290 private void DoVarInit (TokenDeclVar initFunc, TokenLVal left, TokenRVal init)
3291 {
3292 /*
3293 * Make a statement that assigns the initialization value to the variable.
3294 */
3295 TokenStmt stmt;
3296 if (init == null) {
3297 TokenStmtVarIniDef tsvid = new TokenStmtVarIniDef (left);
3298 tsvid.var = left;
3299 stmt = tsvid;
3300 } else {
3301 TokenKw op = new TokenKwAssign (left);
3302 TokenStmtRVal tsrv = new TokenStmtRVal (init);
3303 tsrv.rVal = new TokenRValOpBin (left, op, init);
3304 stmt = tsrv;
3305 }
3306
3307 /*
3308 * Add statement to end of initialization function.
3309 * Be sure to execute them in same order as in source
3310 * as some doofus scripts depend on it.
3311 */
3312 Token lastStmt = initFunc.body.statements;
3313 if (lastStmt == null) {
3314 initFunc.body.statements = stmt;
3315 } else {
3316 Token nextStmt;
3317 while ((nextStmt = lastStmt.nextToken) != null) {
3318 lastStmt = nextStmt;
3319 }
3320 lastStmt.nextToken = stmt;
3321 }
3322 }
3323
3324 /**
3325 * @brief parse function declaration argument list
3326 * @param token = points to TokenKwParOpen
3327 * @returns null: parse error
3328 * else: points to token with types and names
3329 * token = updated past the TokenKw{Brk,Par}Close
3330 */
3331 private TokenArgDecl ParseFuncArgs (ref Token token, Type end)
3332 {
3333 TokenArgDecl tokenArgDecl = new TokenArgDecl (token);
3334
3335 bool first = true;
3336 do {
3337 token = token.nextToken;
3338 if ((token.GetType () == end) && first) break;
3339 if (!(token is TokenType)) {
3340 ErrorMsg (token, "expecting arg type");
3341 token = SkipPastSemi (token);
3342 return null;
3343 }
3344 TokenType type = (TokenType)token;
3345 token = token.nextToken;
3346 if (!(token is TokenName)) {
3347 ErrorMsg (token, "expecting arg name");
3348 token = SkipPastSemi (token);
3349 return null;
3350 }
3351 TokenName name = (TokenName)token;
3352 token = token.nextToken;
3353
3354 if (!tokenArgDecl.AddArg (type, name)) {
3355 ErrorMsg (name, "duplicate arg name");
3356 }
3357 first = false;
3358 } while (token is TokenKwComma);
3359
3360 if (token.GetType () != end) {
3361 ErrorMsg (token, "expecting comma or close bracket/paren");
3362 token = SkipPastSemi (token);
3363 return null;
3364 }
3365 token = token.nextToken;
3366
3367 return tokenArgDecl;
3368 }
3369
3370 /**
3371 * @brief parse right-hand value expression
3372 * this is where arithmetic-like expressions are processed
3373 * @param token = points to first token expression
3374 * @param termTokenType = expression termination token type
3375 * @returns null: not an RVal
3376 * else: single token representing whole expression
3377 * token = if termTokenType.Length == 1, points just past terminating token
3378 * else, points right at terminating token
3379 */
3380 public TokenRVal ParseRVal (ref Token token, Type[] termTokenTypes)
3381 {
3382 /*
3383 * Start with pushing the first operand on operand stack.
3384 */
3385 BinOp binOps = null;
3386 TokenRVal operands = GetOperand (ref token);
3387 if (operands == null) return null;
3388
3389 /*
3390 * Keep scanning until we hit the termination token.
3391 */
3392 while (true) {
3393 Type tokType = token.GetType ();
3394 for (int i = termTokenTypes.Length; -- i >= 0;) {
3395 if (tokType == termTokenTypes[i]) goto done;
3396 }
3397
3398 /*
3399 * Special form:
3400 * <operand> is <typeexp>
3401 */
3402 if (token is TokenKwIs) {
3403 TokenRValIsType tokenRValIsType = new TokenRValIsType (token);
3404 token = token.nextToken;
3405
3406 /*
3407 * Parse the <typeexp>.
3408 */
3409 tokenRValIsType.typeExp = ParseTypeExp (ref token);
3410 if (tokenRValIsType.typeExp == null) return null;
3411
3412 /*
3413 * Replace top operand with result of <operand> is <typeexp>
3414 */
3415 tokenRValIsType.rValExp = operands;
3416 tokenRValIsType.nextToken = operands.nextToken;
3417 operands = tokenRValIsType;
3418
3419 /*
3420 * token points just past <typeexp> so see if it is another operator.
3421 */
3422 continue;
3423 }
3424
3425 /*
3426 * Peek at next operator.
3427 */
3428 BinOp binOp = GetOperator (ref token);
3429 if (binOp == null) return null;
3430
3431 /*
3432 * If there are stacked operators of higher or same precedence than new one,
3433 * perform their computation then push result back on operand stack.
3434 *
3435 * higher or same = left-to-right application of operators
3436 * eg, a - b - c becomes (a - b) - c
3437 *
3438 * higher precedence = right-to-left application of operators
3439 * eg, a - b - c becomes a - (b - c)
3440 *
3441 * Now of course, there is some ugliness necessary:
3442 * we want: a - b - c => (a - b) - c so we do 'higher or same'
3443 * but we want: a += b = c => a += (b = c) so we do 'higher only'
3444 *
3445 * binOps is the first operator (or null if only one)
3446 * binOp is the second operator (or first if only one)
3447 */
3448 while (binOps != null) {
3449 if (binOps.preced < binOp.preced) break; // 1st operator lower than 2nd, so leave 1st on stack to do later
3450 if (binOps.preced > binOp.preced) goto do1st; // 1st op higher than 2nd, so we always do 1st op first
3451 if (binOps.preced == ASNPR) break; // equal preced, if assignment type, leave 1st on stack to do later
3452 // if non-asn type, do 1st op first (ie left-to-right)
3453 do1st:
3454 TokenRVal result = PerformBinOp ((TokenRVal)operands.prevToken, binOps, (TokenRVal)operands);
3455 result.prevToken = operands.prevToken.prevToken;
3456 operands = result;
3457 binOps = binOps.pop;
3458 }
3459
3460 /*
3461 * Handle conditional expression as a special form:
3462 * <condexp> ? <trueexp> : <falseexp>
3463 */
3464 if (binOp.token is TokenKwQMark) {
3465 TokenRValCondExpr condExpr = new TokenRValCondExpr (binOp.token);
3466 condExpr.condExpr = operands;
3467 condExpr.trueExpr = ParseRVal (ref token, new Type[] { typeof (TokenKwColon) });
3468 condExpr.falseExpr = ParseRVal (ref token, termTokenTypes);
3469 condExpr.prevToken = operands.prevToken;
3470 operands = condExpr;
3471 termTokenTypes = new Type[0];
3472 goto done;
3473 }
3474
3475 /*
3476 * Push new operator on its stack.
3477 */
3478 binOp.pop = binOps;
3479 binOps = binOp;
3480
3481 /*
3482 * Push next operand on its stack.
3483 */
3484 TokenRVal operand = GetOperand (ref token);
3485 if (operand == null) return null;
3486 operand.prevToken = operands;
3487 operands = operand;
3488 }
3489 done:
3490
3491 /*
3492 * At end of expression, perform any stacked computations.
3493 */
3494 while (binOps != null) {
3495 TokenRVal result = PerformBinOp ((TokenRVal)operands.prevToken, binOps, (TokenRVal)operands);
3496 result.prevToken = operands.prevToken.prevToken;
3497 operands = result;
3498 binOps = binOps.pop;
3499 }
3500
3501 /*
3502 * There should be exactly one remaining operand on the stack which is our final result.
3503 */
3504 if (operands.prevToken != null) throw new Exception ("too many operands");
3505
3506 /*
3507 * If only one terminator type possible, advance past the terminator.
3508 */
3509 if (termTokenTypes.Length == 1) token = token.nextToken;
3510
3511 return operands;
3512 }
3513
3514 private TokenTypeExp ParseTypeExp (ref Token token)
3515 {
3516 TokenTypeExp leftOperand = GetTypeExp (ref token);
3517 if (leftOperand == null) return null;
3518
3519 while ((token is TokenKwAnd) || (token is TokenKwOr)) {
3520 Token typeBinOp = token;
3521 token = token.nextToken;
3522 TokenTypeExp rightOperand = GetTypeExp (ref token);
3523 if (rightOperand == null) return null;
3524 TokenTypeExpBinOp typeExpBinOp = new TokenTypeExpBinOp (typeBinOp);
3525 typeExpBinOp.leftOp = leftOperand;
3526 typeExpBinOp.binOp = typeBinOp;
3527 typeExpBinOp.rightOp = rightOperand;
3528 leftOperand = typeExpBinOp;
3529 }
3530 return leftOperand;
3531 }
3532
3533 private TokenTypeExp GetTypeExp (ref Token token)
3534 {
3535 if (token is TokenKwTilde) {
3536 TokenTypeExpNot typeExpNot = new TokenTypeExpNot (token);
3537 token = token.nextToken;
3538 typeExpNot.typeExp = GetTypeExp (ref token);
3539 if (typeExpNot.typeExp == null) return null;
3540 return typeExpNot;
3541 }
3542 if (token is TokenKwParOpen) {
3543 TokenTypeExpPar typeExpPar = new TokenTypeExpPar (token);
3544 token = token.nextToken;
3545 typeExpPar.typeExp = GetTypeExp (ref token);
3546 if (typeExpPar.typeExp == null) return null;
3547 if (!(token is TokenKwParClose)) {
3548 ErrorMsg (token, "expected close parenthesis");
3549 token = SkipPastSemi (token);
3550 return null;
3551 }
3552 return typeExpPar;
3553 }
3554 if (token is TokenKwUndef) {
3555 TokenTypeExpUndef typeExpUndef = new TokenTypeExpUndef (token);
3556 token = token.nextToken;
3557 return typeExpUndef;
3558 }
3559 if (token is TokenType) {
3560 TokenTypeExpType typeExpType = new TokenTypeExpType (token);
3561 typeExpType.typeToken = (TokenType)token;
3562 token = token.nextToken;
3563 return typeExpType;
3564 }
3565 ErrorMsg (token, "expected type");
3566 token = SkipPastSemi (token);
3567 return null;
3568 }
3569
3570 /**
3571 * @brief get a right-hand operand expression token
3572 * @param token = first token of operand to parse
3573 * @returns null: invalid operand
3574 * else: token that bundles or wraps the operand
3575 * token = points to token following last operand token
3576 */
3577 private TokenRVal GetOperand (ref Token token)
3578 {
3579 /*
3580 * Prefix unary operators (eg ++, --) requiring an L-value.
3581 */
3582 if ((token is TokenKwIncr) || (token is TokenKwDecr)) {
3583 TokenRValAsnPre asnPre = new TokenRValAsnPre (token);
3584 asnPre.prefix = token;
3585 token = token.nextToken;
3586 TokenRVal op = GetOperand (ref token);
3587 if (op == null) return null;
3588 if (!(op is TokenLVal)) {
3589 ErrorMsg (op, "can pre{in,de}crement only an L-value");
3590 return null;
3591 }
3592 asnPre.lVal = (TokenLVal)op;
3593 return asnPre;
3594 }
3595
3596 /*
3597 * Get the bulk of the operand, ie, without any of the below suffixes.
3598 */
3599 TokenRVal operand = GetOperandNoMods (ref token);
3600 if (operand == null) return null;
3601 modifiers:
3602
3603 /*
3604 * If followed by '++' or '--', it is post-{in,de}cremented.
3605 */
3606 if ((token is TokenKwIncr) || (token is TokenKwDecr)) {
3607 TokenRValAsnPost asnPost = new TokenRValAsnPost (token);
3608 asnPost.postfix = token;
3609 token = token.nextToken;
3610 if (!(operand is TokenLVal)) {
3611 ErrorMsg (operand, "can post{in,de}crement only an L-value");
3612 return null;
3613 }
3614 asnPost.lVal = (TokenLVal)operand;
3615 return asnPost;
3616 }
3617
3618 /*
3619 * If followed by a '.', it is an instance field or instance method reference.
3620 */
3621 if (token is TokenKwDot) {
3622 token = token.nextToken;
3623 if (!(token is TokenName)) {
3624 ErrorMsg (token, ". must be followed by field/method name");
3625 return null;
3626 }
3627 TokenLValIField field = new TokenLValIField (token);
3628 field.baseRVal = operand;
3629 field.fieldName = (TokenName)token;
3630 operand = field;
3631 token = token.nextToken;
3632 goto modifiers;
3633 }
3634
3635 /*
3636 * If followed by a '[', it is an array subscript.
3637 */
3638 if (token is TokenKwBrkOpen) {
3639 TokenLValArEle tokenLValArEle = new TokenLValArEle (token);
3640 token = token.nextToken;
3641
3642 /*
3643 * Parse subscript(s) expression.
3644 */
3645 tokenLValArEle.subRVal = ParseRVal (ref token, brkCloseOnly);
3646 if (tokenLValArEle.subRVal == null) {
3647 ErrorMsg (tokenLValArEle, "invalid subscript");
3648 return null;
3649 }
3650
3651 /*
3652 * See if comma-separated list of values.
3653 */
3654 TokenRVal subscriptRVals;
3655 int numSubscripts = SplitCommaRVals (tokenLValArEle.subRVal, out subscriptRVals);
3656 if (numSubscripts > 1) {
3657
3658 /*
3659 * If so, put the values in an LSL_List object.
3660 */
3661 TokenRValList rValList = new TokenRValList (tokenLValArEle);
3662 rValList.rVal = subscriptRVals;
3663 rValList.nItems = numSubscripts;
3664 tokenLValArEle.subRVal = rValList;
3665 }
3666
3667 /*
3668 * Either way, save array variable name
3669 * and substitute whole reference for L-value
3670 */
3671 tokenLValArEle.baseRVal = operand;
3672 operand = tokenLValArEle;
3673 goto modifiers;
3674 }
3675
3676 /*
3677 * If followed by a '(', it is a function/method call.
3678 */
3679 if (token is TokenKwParOpen) {
3680 operand = ParseRValCall (ref token, operand);
3681 goto modifiers;
3682 }
3683
3684 /*
3685 * If 'new' arraytipe '{', it is an array initializer.
3686 */
3687 if ((token is TokenKwBrcOpen) && (operand is TokenLValSField) &&
3688 (((TokenLValSField)operand).fieldName.val == "$new") &&
3689 ((TokenLValSField)operand).baseType.ToString ().EndsWith ("]")) {
3690 operand = ParseRValNewArIni (ref token, (TokenLValSField)operand);
3691 if (operand != null) goto modifiers;
3692 }
3693
3694 return operand;
3695 }
3696
3697 /**
3698 * @brief same as GetOperand() except doesn't check for any modifiers
3699 */
3700 private TokenRVal GetOperandNoMods (ref Token token)
3701 {
3702 /*
3703 * Simple unary operators.
3704 */
3705 if ((token is TokenKwSub) ||
3706 (token is TokenKwTilde) ||
3707 (token is TokenKwExclam)) {
3708 Token uop = token;
3709 token = token.nextToken;
3710 TokenRVal rVal = GetOperand (ref token);
3711 if (rVal == null) return null;
3712 return PerformUnOp (uop, rVal);
3713 }
3714
3715 /*
3716 * Type casting.
3717 */
3718 if ((token is TokenKwParOpen) &&
3719 (token.nextToken is TokenType) &&
3720 (token.nextToken.nextToken is TokenKwParClose)) {
3721 TokenType type = (TokenType)token.nextToken;
3722 token = token.nextToken.nextToken.nextToken;
3723 TokenRVal rVal = GetOperand (ref token);
3724 if (rVal == null) return null;
3725 return new TokenRValCast (type, rVal);
3726 }
3727
3728 /*
3729 * Parenthesized expression.
3730 */
3731 if (token is TokenKwParOpen) {
3732 return ParseRValParen (ref token);
3733 }
3734
3735 /*
3736 * Constants.
3737 */
3738 if (token is TokenChar) {
3739 TokenRValConst rValConst = new TokenRValConst (token, ((TokenChar)token).val);
3740 token = token.nextToken;
3741 return rValConst;
3742 }
3743 if (token is TokenFloat) {
3744 TokenRValConst rValConst = new TokenRValConst (token, ((TokenFloat)token).val);
3745 token = token.nextToken;
3746 return rValConst;
3747 }
3748 if (token is TokenInt) {
3749 TokenRValConst rValConst = new TokenRValConst (token, ((TokenInt)token).val);
3750 token = token.nextToken;
3751 return rValConst;
3752 }
3753 if (token is TokenStr) {
3754 TokenRValConst rValConst = new TokenRValConst (token, ((TokenStr)token).val);
3755 token = token.nextToken;
3756 return rValConst;
3757 }
3758 if (token is TokenKwUndef) {
3759 TokenRValUndef rValUndef = new TokenRValUndef ((TokenKwUndef)token);
3760 token = token.nextToken;
3761 return rValUndef;
3762 }
3763
3764 /*
3765 * '<'value,...'>', ie, rotation or vector
3766 */
3767 if (token is TokenKwCmpLT) {
3768 Token openBkt = token;
3769 token = token.nextToken;
3770 TokenRVal rValAll = ParseRVal (ref token, cmpGTOnly);
3771 if (rValAll == null) return null;
3772 TokenRVal rVals;
3773 int nVals = SplitCommaRVals (rValAll, out rVals);
3774 switch (nVals) {
3775 case 3: {
3776 TokenRValVec rValVec = new TokenRValVec (openBkt);
3777 rValVec.xRVal = rVals;
3778 rValVec.yRVal = (TokenRVal)rVals.nextToken;
3779 rValVec.zRVal = (TokenRVal)rVals.nextToken.nextToken;
3780 return rValVec;
3781 }
3782 case 4: {
3783 TokenRValRot rValRot = new TokenRValRot (openBkt);
3784 rValRot.xRVal = rVals;
3785 rValRot.yRVal = (TokenRVal)rVals.nextToken;
3786 rValRot.zRVal = (TokenRVal)rVals.nextToken.nextToken;
3787 rValRot.wRVal = (TokenRVal)rVals.nextToken.nextToken.nextToken;
3788 return rValRot;
3789 }
3790 default: {
3791 ErrorMsg (openBkt, "bad rotation/vector");
3792 token = SkipPastSemi (token);
3793 return null;
3794 }
3795 }
3796 }
3797
3798 /*
3799 * '['value,...']', ie, list
3800 */
3801 if (token is TokenKwBrkOpen) {
3802 TokenRValList rValList = new TokenRValList (token);
3803 token = token.nextToken;
3804 if (token is TokenKwBrkClose) {
3805 token = token.nextToken; // empty list
3806 } else {
3807 TokenRVal rValAll = ParseRVal (ref token, brkCloseOnly);
3808 if (rValAll == null) return null;
3809 rValList.nItems = SplitCommaRVals (rValAll, out rValList.rVal);
3810 }
3811 return rValList;
3812 }
3813
3814 /*
3815 * Maybe we have <type>.<name> referencing a static field or method of some type.
3816 */
3817 if ((token is TokenType) && (token.nextToken is TokenKwDot) && (token.nextToken.nextToken is TokenName)) {
3818 TokenLValSField field = new TokenLValSField (token.nextToken.nextToken);
3819 field.baseType = (TokenType)token;
3820 field.fieldName = (TokenName)token.nextToken.nextToken;
3821 token = token.nextToken.nextToken.nextToken;
3822 return field;
3823 }
3824
3825 /*
3826 * Maybe we have 'this' referring to the object of the instance method.
3827 */
3828 if (token is TokenKwThis) {
3829 if ((currentDeclSDType == null) || !(currentDeclSDType is TokenDeclSDTypeClass)) {
3830 ErrorMsg (token, "using 'this' outside class definition");
3831 token = SkipPastSemi (token);
3832 return null;
3833 }
3834 TokenRValThis zhis = new TokenRValThis (token, (TokenDeclSDTypeClass)currentDeclSDType);
3835 token = token.nextToken;
3836 return zhis;
3837 }
3838
3839 /*
3840 * Maybe we have 'base' referring to a field/method of the extended class.
3841 */
3842 if (token is TokenKwBase) {
3843 if ((currentDeclFunc == null) || (currentDeclFunc.sdtClass == null) || !(currentDeclFunc.sdtClass is TokenDeclSDTypeClass)) {
3844 ErrorMsg (token, "using 'base' outside method");
3845 token = SkipPastSemi (token);
3846 return null;
3847 }
3848 if (!(token.nextToken is TokenKwDot) || !(token.nextToken.nextToken is TokenName)) {
3849 ErrorMsg (token, "base must be followed by . then field or method name");
3850 TokenRValThis zhis = new TokenRValThis (token, (TokenDeclSDTypeClass)currentDeclFunc.sdtClass);
3851 token = token.nextToken;
3852 return zhis;
3853 }
3854 TokenLValBaseField baseField = new TokenLValBaseField (token,
3855 (TokenName)token.nextToken.nextToken,
3856 (TokenDeclSDTypeClass)currentDeclFunc.sdtClass);
3857 token = token.nextToken.nextToken.nextToken;
3858 return baseField;
3859 }
3860
3861 /*
3862 * Maybe we have 'new <script-defined-type>' saying to create an object instance.
3863 * This ends up generating a call to static function <script-defined-type>.$new(...)
3864 * whose CIL code is generated by GenerateNewobjBody().
3865 */
3866 if (token is TokenKwNew) {
3867 if (!(token.nextToken is TokenType)) {
3868 ErrorMsg (token.nextToken, "new must be followed by type");
3869 token = SkipPastSemi (token);
3870 return null;
3871 }
3872 TokenLValSField field = new TokenLValSField (token.nextToken.nextToken);
3873 field.baseType = (TokenType)token.nextToken;
3874 field.fieldName = new TokenName (token, "$new");
3875 token = token.nextToken.nextToken;
3876 return field;
3877 }
3878
3879 /*
3880 * All we got left is <name>, eg, arg, function, global or local variable reference
3881 */
3882 if (token is TokenName) {
3883 TokenLValName name = new TokenLValName ((TokenName)token, tokenScript.variablesStack);
3884 token = token.nextToken;
3885 return name;
3886 }
3887
3888 /*
3889 * Who knows what it is supposed to be?
3890 */
3891 ErrorMsg (token, "invalid operand token");
3892 token = SkipPastSemi (token);
3893 return null;
3894 }
3895
3896 /**
3897 * @brief Parse a call expression
3898 * @param token = points to arg list '('
3899 * @param meth = points to method name being called
3900 * @returns call expression value
3901 * token = points just past arg list ')'
3902 */
3903 private TokenRValCall ParseRValCall (ref Token token, TokenRVal meth)
3904 {
3905 /*
3906 * Set up basic function call struct with function name.
3907 */
3908 TokenRValCall rValCall = new TokenRValCall (token);
3909 rValCall.meth = meth;
3910
3911 /*
3912 * Parse the call parameters, if any.
3913 */
3914 token = token.nextToken;
3915 if (token is TokenKwParClose) {
3916 token = token.nextToken;
3917 } else {
3918 rValCall.args = ParseRVal (ref token, parCloseOnly);
3919 if (rValCall.args == null) return null;
3920 rValCall.nArgs = SplitCommaRVals (rValCall.args, out rValCall.args);
3921 }
3922
3923 currentDeclFunc.unknownTrivialityCalls.AddLast (rValCall);
3924
3925 return rValCall;
3926 }
3927
3928 /**
3929 * @brief decode binary operator token
3930 * @param token = points to token to decode
3931 * @returns null: invalid operator token
3932 * else: operator token and precedence
3933 */
3934 private BinOp GetOperator (ref Token token)
3935 {
3936 BinOp binOp = new BinOp ();
3937 if (precedence.TryGetValue (token.GetType (), out binOp.preced)) {
3938 binOp.token = (TokenKw)token;
3939 token = token.nextToken;
3940 return binOp;
3941 }
3942
3943 if ((token is TokenKwSemi) || (token is TokenKwBrcOpen) || (token is TokenKwBrcClose)) {
3944 ErrorMsg (token, "premature expression end");
3945 } else {
3946 ErrorMsg (token, "invalid operator");
3947 }
3948 token = SkipPastSemi (token);
3949 return null;
3950 }
3951
3952 private class BinOp {
3953 public BinOp pop;
3954 public TokenKw token;
3955 public int preced;
3956 }
3957
3958 /**
3959 * @brief Return an R-value expression token that will be used to
3960 * generate code to perform the operation at runtime.
3961 * @param left = left-hand operand
3962 * @param binOp = operator
3963 * @param right = right-hand operand
3964 * @returns resultant expression
3965 */
3966 private TokenRVal PerformBinOp (TokenRVal left, BinOp binOp, TokenRVal right)
3967 {
3968 return new TokenRValOpBin (left, binOp.token, right);
3969 }
3970
3971 /**
3972 * @brief Return an R-value expression token that will be used to
3973 * generate code to perform the operation at runtime.
3974 * @param unOp = operator
3975 * @param right = right-hand operand
3976 * @returns resultant constant or expression
3977 */
3978 private TokenRVal PerformUnOp (Token unOp, TokenRVal right)
3979 {
3980 return new TokenRValOpUn ((TokenKw)unOp, right);
3981 }
3982
3983 /**
3984 * @brief Parse an array initialization expression.
3985 * @param token = points to '{' on entry
3986 * @param newCall = encapsulates a '$new' call
3987 * @return resultant operand encapsulating '$new' call and initializers
3988 * token = points just past terminating '}'
3989 * ...or null if parse error
3990 */
3991 private TokenRVal ParseRValNewArIni (ref Token token, TokenLValSField newCall)
3992 {
3993 Stack<TokenList> stack = new Stack<TokenList> ();
3994 TokenRValNewArIni arini = new TokenRValNewArIni (token);
3995 arini.arrayType = newCall.baseType;
3996 TokenList values = null;
3997 while (true) {
3998
3999 // open brace means start a (sub-)list
4000 if (token is TokenKwBrcOpen) {
4001 stack.Push (values);
4002 values = new TokenList (token);
4003 token = token.nextToken;
4004 continue;
4005 }
4006
4007 // close brace means end of (sub-)list
4008 // if final '}' all done parsing
4009 if (token is TokenKwBrcClose) {
4010 token = token.nextToken; // skip over the '}'
4011 TokenList innerds = values; // save the list just closed
4012 arini.valueList = innerds; // it's the top list if it's the last closed
4013 values = stack.Pop (); // pop to next outer list
4014 if (values == null) return arini; // final '}', we are done
4015 values.tl.Add (innerds); // put the inner list on end of outer list
4016 if (token is TokenKwComma) { // should have a ',' or '}' next
4017 token = token.nextToken; // skip over the ','
4018 } else if (!(token is TokenKwBrcClose)) {
4019 ErrorMsg (token, "expecting , or } after sublist");
4020 }
4021 continue;
4022 }
4023
4024 // this is a comma that doesn't have a value expression before it
4025 // so we take it to mean skip initializing element (leave it zeroes/null etc)
4026 if (token is TokenKwComma) {
4027 values.tl.Add (token);
4028 token = token.nextToken;
4029 continue;
4030 }
4031
4032 // parse value expression and skip terminating ',' if any
4033 TokenRVal append = ParseRVal (ref token, commaOrBrcClose);
4034 if (append == null) return null;
4035 values.tl.Add (append);
4036 if (token is TokenKwComma) {
4037 token = token.nextToken;
4038 }
4039 }
4040 }
4041
4042 /**
4043 * @brief parse out a parenthesized expression.
4044 * @param token = points to open parenthesis
4045 * @returns null: invalid expression
4046 * else: parenthesized expression token or constant token
4047 * token = points past the close parenthesis
4048 */
4049 private TokenRValParen ParseRValParen (ref Token token)
4050 {
4051 if (!(token is TokenKwParOpen)) {
4052 ErrorMsg (token, "expecting (");
4053 token = SkipPastSemi (token);
4054 return null;
4055 }
4056 TokenRValParen tokenRValParen = new TokenRValParen (token);
4057 token = token.nextToken;
4058 tokenRValParen.rVal = ParseRVal (ref token, parCloseOnly);
4059 if (tokenRValParen.rVal == null) return null;
4060 return tokenRValParen;
4061 }
4062
4063 /**
4064 * @brief Split a comma'd RVal into separate expressions
4065 * @param rValAll = expression containing commas
4066 * @returns number of comma separated values
4067 * rVals = values in a null-terminated list linked by rVals.nextToken
4068 */
4069 private int SplitCommaRVals (TokenRVal rValAll, out TokenRVal rVals)
4070 {
4071 if (!(rValAll is TokenRValOpBin) || !(((TokenRValOpBin)rValAll).opcode is TokenKwComma)) {
4072 rVals = rValAll;
4073 if (rVals.nextToken != null) throw new Exception ("expected null");
4074 return 1;
4075 }
4076 TokenRValOpBin opBin = (TokenRValOpBin)rValAll;
4077 TokenRVal rValLeft, rValRight;
4078 int leftCount = SplitCommaRVals (opBin.rValLeft, out rValLeft);
4079 int rightCount = SplitCommaRVals (opBin.rValRight, out rValRight);
4080 rVals = rValLeft;
4081 while (rValLeft.nextToken != null) rValLeft = (TokenRVal)rValLeft.nextToken;
4082 rValLeft.nextToken = rValRight;
4083 return leftCount + rightCount;
4084 }
4085
4086 /**
4087 * @brief output error message and remember that there is an error.
4088 * @param token = what token is associated with the error
4089 * @param message = error message string
4090 */
4091 private void ErrorMsg (Token token, string message)
4092 {
4093 if (!errors || (token.file != lastErrorFile) || (token.line > lastErrorLine)) {
4094 errors = true;
4095 lastErrorFile = token.file;
4096 lastErrorLine = token.line;
4097 token.ErrorMsg (message);
4098 }
4099 }
4100
4101 /**
4102 * @brief Skip past the next semicolon (or matched braces)
4103 * @param token = points to token to skip over
4104 * @returns token just after the semicolon or close brace
4105 */
4106 private Token SkipPastSemi (Token token)
4107 {
4108 int braceLevel = 0;
4109
4110 while (!(token is TokenEnd)) {
4111 if ((token is TokenKwSemi) && (braceLevel == 0)) {
4112 return token.nextToken;
4113 }
4114 if (token is TokenKwBrcOpen) {
4115 braceLevel ++;
4116 }
4117 if ((token is TokenKwBrcClose) && (-- braceLevel <= 0)) {
4118 return token.nextToken;
4119 }
4120 token = token.nextToken;
4121 }
4122 return token;
4123 }
4124 }
4125
4126 /**
4127 * @brief Script-defined type declarations
4128 */
4129 public abstract class TokenDeclSDType : Token {
4130 protected const byte CLASS = 0;
4131 protected const byte DELEGATE = 1;
4132 protected const byte INTERFACE = 2;
4133 protected const byte TYPEDEF = 3;
4134
4135 // stuff that gets cloned/copied/transformed when instantiating a generic
4136 // see InstantiateGeneric() below
4137 public TokenDeclSDType outerSDType; // null if top-level
4138 // else points to defining script-defined type
4139 public Dictionary<string, TokenDeclSDType> innerSDTypes = new Dictionary<string, TokenDeclSDType> ();
4140 // indexed by shortName
4141 public Token begToken; // token that begins the definition (might be this or something like 'public')
4142 public Token endToken; // the '}' or ';' that ends the definition
4143
4144 // generic instantiation assumes none of the rest needs to be cloned (well except for the shortName)
4145 public int sdTypeIndex = -1; // index in scriptObjCode.sdObjTypesIndx[] array
4146 public TokenDeclSDTypeClass extends; // only non-null for TokenDeclSDTypeClass's
4147 public uint accessLevel; // SDT_PRIVATE, SDT_PROTECTED or SDT_PUBLIC
4148 // ... all top-level types are SDT_PUBLIC
4149 public VarDict members = new VarDict (false); // declared fields, methods, properties if any
4150
4151 public Dictionary<string, int> genParams; // list of parameters for generic prototypes
4152 // null for non-generic prototypes
4153 // eg, for 'Dictionary<K,V>'
4154 // ...genParams gives K->0; V->1
4155
4156 public bool isPartial; // was declared with 'partial' keyword
4157 // classes only, all others always false
4158
4159 /*
4160 * Name of the type.
4161 * shortName = doesn't include outer class type names
4162 * eg, 'Engine' for non-generic
4163 * 'Dictionary<,>' for generic prototype
4164 * 'Dictionary<string,integer>' for generic instantiation
4165 * longName = includes all outer class type names if any
4166 */
4167 private TokenName _shortName;
4168 private TokenName _longName;
4169
4170 public TokenName shortName {
4171 get {
4172 return _shortName;
4173 }
4174 set {
4175 _shortName = value;
4176 _longName = null;
4177 }
4178 }
4179
4180 public TokenName longName {
4181 get {
4182 if (_longName == null) {
4183 _longName = _shortName;
4184 if (outerSDType != null) {
4185 _longName = new TokenName (_shortName, outerSDType.longName.val + "." + _shortName.val);
4186 }
4187 }
4188 return _longName;
4189 }
4190 }
4191
4192 /*
4193 * Dictionary used when reading from object file that holds all script-defined types.
4194 * Not complete though until all types have been read from the object file.
4195 */
4196 private Dictionary<string, TokenDeclSDType> sdTypes;
4197
4198 public TokenDeclSDType (Token t) : base (t) { }
4199 protected abstract TokenDeclSDType MakeBlank (TokenName shortName);
4200 public abstract TokenType MakeRefToken (Token t);
4201 public abstract Type GetSysType ();
4202 public abstract void WriteToFile (BinaryWriter objFileWriter);
4203 public abstract void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter);
4204
4205 /**
4206 * @brief Given that this is a generic prototype, apply the supplied genArgs
4207 * to create an equivalent instantiated non-generic. This basically
4208 * makes a copy replacing all the parameter types with the actual
4209 * argument types.
4210 * @param this = the prototype to be instantiated, eg, 'Dictionary<string,integer>.Converter'
4211 * @param name = short name with arguments, eg, 'Converter<float>'.
4212 * @param genArgs = argument types of just this level, eg, 'float'.
4213 * @returns clone of this but with arguments applied and spliced in source token stream
4214 */
4215 public TokenDeclSDType InstantiateGeneric (string name, TokenType[] genArgs, ScriptReduce reduce)
4216 {
4217 /*
4218 * Malloc the struct and give it a name.
4219 */
4220 TokenDeclSDType instdecl = this.MakeBlank (new TokenName (this, name));
4221
4222 /*
4223 * If the original had an outer type, then so does the new one.
4224 * The outer type will never be a generic prototype, eg, if this
4225 * is 'ValueList' it will always be inside 'Dictionary<string,integer>'
4226 * not 'Dictionary' at this point.
4227 */
4228 if ((this.outerSDType != null) && (this.outerSDType.genParams != null)) throw new Exception ();
4229 instdecl.outerSDType = this.outerSDType;
4230
4231 /*
4232 * The generic prototype may have stuff like 'public' just before it and we need to copy that too.
4233 */
4234 Token prefix;
4235 for (prefix = this; (prefix = prefix.prevToken) != null;) {
4236 if (!(prefix is TokenKwPublic) && !(prefix is TokenKwProtected) && !(prefix is TokenKwPrivate)) break;
4237 }
4238 this.begToken = prefix.nextToken;
4239
4240 /*
4241 * Splice in a copy of the prefix tokens, just before the beginning token of prototype (this.begToken).
4242 */
4243 while ((prefix = prefix.nextToken) != this) {
4244 SpliceSourceToken (prefix.CopyToken (prefix));
4245 }
4246
4247 /*
4248 * Splice instantiation (instdecl) in just before the beginning token of prototype (this.begToken).
4249 */
4250 SpliceSourceToken (instdecl);
4251
4252 /*
4253 * Now for the fun part... Copy the rest of the prototype body to the
4254 * instantiated body, replacing all generic parameter type tokens with
4255 * the corresponding generic argument types. Note that the parameters
4256 * are numbered starting with the outermost so we need the full genArgs
4257 * array. Eg if we are doing 'Converter<V=float>' from
4258 * 'Dictionary<T=string,U=integer>.Converter<V=float>', any V's are
4259 * numbered [2]. Any [0]s or [1]s should be gone by now but it doesn't
4260 * matter.
4261 */
4262 int index;
4263 Token it, pt;
4264 TokenDeclSDType innerProto = this;
4265 TokenDeclSDType innerInst = instdecl;
4266 for (pt = this; (pt = pt.nextToken) != this.endToken;) {
4267
4268 /*
4269 * Coming across a sub-type's declaration involves a deep copy of the
4270 * declaration token. Fortunately we are early on in parsing, so there
4271 * really isn't much to copy:
4272 * 1) short name is the same, eg, doing List of Dictionary<string,integer>.List is same short name as Dictionary<T,U>.List
4273 * if generic, eg doing Converter<W> of Dictionary<T,U>.Converter<W>, we have to manually copy the W as well.
4274 * 2) outerSDType is transformed from Dictionary<T,U> to Dictionary<string,integer>.
4275 * 3) innerSDTypes is rebuilt when/if we find classes that are inner to this one.
4276 */
4277 if (pt is TokenDeclSDType) {
4278
4279 /*
4280 * Make a new TokenDeclSDType{Class,Delegate,Interface}.
4281 */
4282 TokenDeclSDType ptSDType = (TokenDeclSDType)pt;
4283 TokenDeclSDType itSDType = ptSDType.MakeBlank (new TokenName (ptSDType.shortName, ptSDType.shortName.val));
4284
4285 /*
4286 * Set up the transformed outerSDType.
4287 * Eg, if we are creating Enumerator of Dictionary<string,integer>.Enumerator,
4288 * innerProto = Dictionary<T,U> and innerInst = Dictionary<string,integer>.
4289 */
4290 itSDType.outerSDType = innerInst;
4291
4292 /*
4293 * This clone is an inner type of its next outer level.
4294 */
4295 reduce.CatalogSDTypeDecl (itSDType);
4296
4297 /*
4298 * We need to manually copy any generic parameters of the class declaration being cloned.
4299 * eg, if we are cloning Converter<W>, this is where the W gets copied.
4300 * Since it is an immutable array of strings, just copy the array pointer, if any.
4301 */
4302 itSDType.genParams = ptSDType.genParams;
4303
4304 /*
4305 * We are now processing tokens for this cloned type declaration.
4306 */
4307 innerProto = ptSDType;
4308 innerInst = itSDType;
4309
4310 /*
4311 * Splice this clone token in.
4312 */
4313 it = itSDType;
4314 }
4315
4316 /*
4317 * Check for an generic parameter to substitute out.
4318 */
4319 else if ((pt is TokenName) && this.genParams.TryGetValue (((TokenName)pt).val, out index)) {
4320 it = genArgs[index].CopyToken (pt);
4321 }
4322
4323 /*
4324 * Everything else is a simple copy.
4325 */
4326 else it = pt.CopyToken (pt);
4327
4328 /*
4329 * Whatever we came up with, splice it into the source token stream.
4330 */
4331 SpliceSourceToken (it);
4332
4333 /*
4334 * Maybe we just finished copying an inner type definition.
4335 * If so, remember where it ends and pop it from the stack.
4336 */
4337 if (innerProto.endToken == pt) {
4338 innerInst.endToken = it;
4339 innerProto = innerProto.outerSDType;
4340 innerInst = innerInst.outerSDType;
4341 }
4342 }
4343
4344 /*
4345 * Clone and insert the terminator, either '}' or ';'
4346 */
4347 it = pt.CopyToken (pt);
4348 SpliceSourceToken (it);
4349 instdecl.endToken = it;
4350
4351 return instdecl;
4352 }
4353
4354 /**
4355 * @brief Splice a source token in just before the type's beginning keyword.
4356 */
4357 private void SpliceSourceToken (Token it)
4358 {
4359 it.nextToken = this.begToken;
4360 (it.prevToken = this.begToken.prevToken).nextToken = it;
4361 this.begToken.prevToken = it;
4362 }
4363
4364 /**
4365 * @brief Read one of these in from the object file.
4366 * @param sdTypes = dictionary of script-defined types, not yet complete
4367 * @param name = script-visible name of this type
4368 * @param objFileReader = reads from the object file
4369 * @param asmFileWriter = writes to the disassembly file (might be null)
4370 */
4371 public static TokenDeclSDType ReadFromFile (Dictionary<string, TokenDeclSDType> sdTypes, string name,
4372 BinaryReader objFileReader, TextWriter asmFileWriter)
4373 {
4374 string file = objFileReader.ReadString ();
4375 int line = objFileReader.ReadInt32 ();
4376 int posn = objFileReader.ReadInt32 ();
4377 byte code = objFileReader.ReadByte ();
4378 TokenName n = new TokenName (null, file, line, posn, name);
4379 TokenDeclSDType sdt;
4380 switch (code) {
4381 case CLASS: {
4382 sdt = new TokenDeclSDTypeClass (n, false);
4383 break;
4384 }
4385 case DELEGATE: {
4386 sdt = new TokenDeclSDTypeDelegate (n);
4387 break;
4388 }
4389 case INTERFACE: {
4390 sdt = new TokenDeclSDTypeInterface (n);
4391 break;
4392 }
4393 case TYPEDEF: {
4394 sdt = new TokenDeclSDTypeTypedef (n);
4395 break;
4396 }
4397 default: throw new Exception ();
4398 }
4399 sdt.sdTypes = sdTypes;
4400 sdt.sdTypeIndex = objFileReader.ReadInt32 ();
4401 sdt.ReadFromFile (objFileReader, asmFileWriter);
4402 return sdt;
4403 }
4404
4405 /**
4406 * @brief Convert a typename string to a type token
4407 * @param name = script-visible name of token to create,
4408 * either a script-defined type or an LSL-defined type
4409 * @returns type token
4410 */
4411 protected TokenType MakeTypeToken (string name)
4412 {
4413 TokenDeclSDType sdtdecl;
4414 if (sdTypes.TryGetValue (name, out sdtdecl)) return sdtdecl.MakeRefToken (this);
4415 return TokenType.FromLSLType (this, name);
4416 }
4417
4418 // debugging - returns, eg, 'Dictionary<T,U>.Enumerator.Node'
4419 public override void DebString (StringBuilder sb)
4420 {
4421 // get long name broken down into segments from outermost to this
4422 Stack<TokenDeclSDType> declStack = new Stack<TokenDeclSDType> ();
4423 for (TokenDeclSDType decl = this; decl != null; decl = decl.outerSDType) {
4424 declStack.Push (decl);
4425 }
4426
4427 // output each segment's name followed by our args for it
4428 // starting with outermost and ending with this
4429 while (declStack.Count > 0) {
4430 TokenDeclSDType decl = declStack.Pop ();
4431 sb.Append (decl.shortName.val);
4432 if (decl.genParams != null) {
4433 sb.Append ('<');
4434 string[] parms = new string[decl.genParams.Count];
4435 foreach (KeyValuePair<string, int> kvp in decl.genParams) {
4436 parms[kvp.Value] = kvp.Key;
4437 }
4438 for (int j = 0; j < parms.Length;) {
4439 sb.Append (parms[j]);
4440 if (++ j < parms.Length) sb.Append (',');
4441 }
4442 sb.Append ('>');
4443 }
4444 if (declStack.Count > 0) sb.Append ('.');
4445 }
4446 }
4447 }
4448
4449 public class TokenDeclSDTypeClass : TokenDeclSDType {
4450 public List<TokenDeclSDTypeInterface> implements = new List<TokenDeclSDTypeInterface> ();
4451 public TokenDeclVar instFieldInit; // $instfieldinit function to do instance field initializations
4452 public TokenDeclVar staticFieldInit; // $staticfieldinit function to do static field initializations
4453
4454 public Dictionary<string, int> intfIndices = new Dictionary<string, int> (); // longname => this.iFaces index
4455 public TokenDeclSDTypeInterface[] iFaces; // array of implemented interfaces
4456 // low-end entries copied from rootward classes
4457 public TokenDeclVar[][] iImplFunc; // iImplFunc[i][j]:
4458 // low-end [i] entries copied from rootward classes
4459 // i = interface number from this.intfIndices[name]
4460 // j = method of interface from iface.methods[name].vTableIndex
4461
4462 public TokenType arrayOfType; // if array, it's an array of this type, else null
4463 public int arrayOfRank; // if array, it has this number of dimensions, else zero
4464
4465 public bool slotsAssigned; // set true when slots have been assigned...
4466 public XMRInstArSizes instSizes = new XMRInstArSizes ();
4467 // number of instance fields of various types
4468 public int numVirtFuncs; // number of virtual functions
4469 public int numInterfaces; // number of implemented interfaces
4470
4471 private string extendsStr;
4472 private string arrayOfTypeStr;
4473 private List<StackedMethod> stackedMethods;
4474 private List<StackedIFace> stackedIFaces;
4475
4476 public DynamicMethod[] vDynMeths; // virtual method entrypoints
4477 public Type[] vMethTypes; // virtual method delegate types
4478 public DynamicMethod[][] iDynMeths; // interface method entrypoints
4479 public Type[][] iMethTypes; // interface method types
4480 // low-end [i] entries copied from rootward classes
4481 // i = interface number from this.intfIndices[name]
4482 // j = method of interface from iface.methods[name].vTableIndex
4483
4484 public TokenDeclSDTypeClass (TokenName shortName, bool isPartial) : base (shortName)
4485 {
4486 this.shortName = shortName;
4487 this.isPartial = isPartial;
4488 }
4489
4490 protected override TokenDeclSDType MakeBlank (TokenName shortName)
4491 {
4492 return new TokenDeclSDTypeClass (shortName, false);
4493 }
4494
4495 public override TokenType MakeRefToken (Token t)
4496 {
4497 return new TokenTypeSDTypeClass (t, this);
4498 }
4499
4500 public override Type GetSysType ()
4501 {
4502 return typeof (XMRSDTypeClObj);
4503 }
4504
4505 /**
4506 * @brief See if the class implements the interface.
4507 * Do a recursive (deep) check in all rootward classes.
4508 */
4509 public bool CanCastToIntf (TokenDeclSDTypeInterface intf)
4510 {
4511 if (this.implements.Contains (intf)) return true;
4512 if (this.extends == null) return false;
4513 return this.extends.CanCastToIntf (intf);
4514 }
4515
4516 /**
4517 * @brief Write enough out so we can reconstruct with ReadFromFile.
4518 */
4519 public override void WriteToFile (BinaryWriter objFileWriter)
4520 {
4521 objFileWriter.Write (this.file);
4522 objFileWriter.Write (this.line);
4523 objFileWriter.Write (this.posn);
4524 objFileWriter.Write ((byte)CLASS);
4525 objFileWriter.Write (this.sdTypeIndex);
4526
4527 this.instSizes.WriteToFile (objFileWriter);
4528 objFileWriter.Write (numVirtFuncs);
4529
4530 if (extends == null) {
4531 objFileWriter.Write ("");
4532 } else {
4533 objFileWriter.Write (extends.longName.val);
4534 }
4535
4536 objFileWriter.Write (arrayOfRank);
4537 if (arrayOfRank > 0) objFileWriter.Write (arrayOfType.ToString ());
4538
4539 foreach (TokenDeclVar meth in members) {
4540 if ((meth.retType != null) && (meth.vTableIndex >= 0)) {
4541 objFileWriter.Write (meth.vTableIndex);
4542 objFileWriter.Write (meth.GetObjCodeName ());
4543 objFileWriter.Write (meth.GetDelType ().decl.GetWholeSig ());
4544 }
4545 }
4546 objFileWriter.Write (-1);
4547
4548 int numIFaces = iImplFunc.Length;
4549 objFileWriter.Write (numIFaces);
4550 for (int i = 0; i < numIFaces; i ++) {
4551 objFileWriter.Write (iFaces[i].longName.val);
4552 TokenDeclVar[] meths = iImplFunc[i];
4553 int numMeths = 0;
4554 if (meths != null) numMeths = meths.Length;
4555 objFileWriter.Write (numMeths);
4556 for (int j = 0; j < numMeths; j ++) {
4557 TokenDeclVar meth = meths[j];
4558 objFileWriter.Write (meth.vTableIndex);
4559 objFileWriter.Write (meth.GetObjCodeName ());
4560 objFileWriter.Write (meth.GetDelType ().decl.GetWholeSig ());
4561 }
4562 }
4563 }
4564
4565 /**
4566 * @brief Reconstruct from the file.
4567 */
4568 public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
4569 {
4570 instSizes.ReadFromFile (objFileReader);
4571 numVirtFuncs = objFileReader.ReadInt32 ();
4572
4573 extendsStr = objFileReader.ReadString ();
4574 arrayOfRank = objFileReader.ReadInt32 ();
4575 if (arrayOfRank > 0) arrayOfTypeStr = objFileReader.ReadString ();
4576
4577 if (asmFileWriter != null) {
4578 instSizes.WriteAsmFile (asmFileWriter, extendsStr + "." + shortName.val + ".numInst");
4579 }
4580
4581 stackedMethods = new List<StackedMethod> ();
4582 int vTableIndex;
4583 while ((vTableIndex = objFileReader.ReadInt32 ()) >= 0) {
4584 StackedMethod sm;
4585 sm.methVTI = vTableIndex;
4586 sm.methName = objFileReader.ReadString ();
4587 sm.methSig = objFileReader.ReadString ();
4588 stackedMethods.Add (sm);
4589 }
4590
4591 int numIFaces = objFileReader.ReadInt32 ();
4592 if (numIFaces > 0) {
4593 iDynMeths = new DynamicMethod[numIFaces][];
4594 iMethTypes = new Type[numIFaces][];
4595 stackedIFaces = new List<StackedIFace> ();
4596 for (int i = 0; i < numIFaces; i ++) {
4597 string iFaceName = objFileReader.ReadString ();
4598 intfIndices[iFaceName] = i;
4599 int numMeths = objFileReader.ReadInt32 ();
4600 iDynMeths[i] = new DynamicMethod[numMeths];
4601 iMethTypes[i] = new Type[numMeths];
4602 for (int j = 0; j < numMeths; j ++) {
4603 StackedIFace si;
4604 si.iFaceIndex = i;
4605 si.methIndex = j;
4606 si.vTableIndex = objFileReader.ReadInt32 ();
4607 si.methName = objFileReader.ReadString ();
4608 si.methSig = objFileReader.ReadString ();
4609 stackedIFaces.Add (si);
4610 }
4611 }
4612 }
4613 }
4614
4615 private struct StackedMethod {
4616 public int methVTI;
4617 public string methName;
4618 public string methSig;
4619 }
4620
4621 private struct StackedIFace {
4622 public int iFaceIndex; // which implemented interface
4623 public int methIndex; // which method of that interface
4624 public int vTableIndex; // <0: implemented by non-virtual; else: implemented by virtual
4625 public string methName; // object code name of implementing method (GetObjCodeName)
4626 public string methSig; // method signature incl return type (GetWholeSig)
4627 }
4628
4629 /**
4630 * @brief Called after all dynamic method code has been generated to fill in vDynMeths and vMethTypes
4631 * Also fills in iDynMeths, iMethTypes.
4632 */
4633 public void FillVTables (ScriptObjCode scriptObjCode)
4634 {
4635 if (extendsStr != null) {
4636 if (extendsStr != "") {
4637 extends = (TokenDeclSDTypeClass)scriptObjCode.sdObjTypesName[extendsStr];
4638 extends.FillVTables (scriptObjCode);
4639 }
4640 extendsStr = null;
4641 }
4642 if (arrayOfTypeStr != null) {
4643 arrayOfType = MakeTypeToken (arrayOfTypeStr);
4644 arrayOfTypeStr = null;
4645 }
4646
4647 if ((numVirtFuncs > 0) && (stackedMethods != null)) {
4648
4649 /*
4650 * Allocate arrays big enough for mine plus type we are extending.
4651 */
4652 vDynMeths = new DynamicMethod[numVirtFuncs];
4653 vMethTypes = new Type[numVirtFuncs];
4654
4655 /*
4656 * Fill in low parts from type we are extending.
4657 */
4658 if (extends != null) {
4659 int n = extends.numVirtFuncs;
4660 for (int i = 0; i < n; i ++) {
4661 vDynMeths[i] = extends.vDynMeths[i];
4662 vMethTypes[i] = extends.vMethTypes[i];
4663 }
4664 }
4665
4666 /*
4667 * Fill in high parts with my own methods.
4668 * Might also overwrite lower ones with 'override' methods.
4669 */
4670 foreach (StackedMethod sm in stackedMethods) {
4671 int i = sm.methVTI;
4672 string methName = sm.methName;
4673 DynamicMethod dm;
4674 if (scriptObjCode.dynamicMethods.TryGetValue (methName, out dm)) {
4675 // method is not abstract
4676 vDynMeths[i] = dm;
4677 vMethTypes[i] = GetDynamicMethodDelegateType (dm, sm.methSig);
4678 }
4679 }
4680 stackedMethods = null;
4681 }
4682
4683 if (stackedIFaces != null) {
4684 foreach (StackedIFace si in stackedIFaces) {
4685 int i = si.iFaceIndex;
4686 int j = si.methIndex;
4687 int vti = si.vTableIndex;
4688 string methName = si.methName;
4689 DynamicMethod dm = scriptObjCode.dynamicMethods[methName];
4690 iDynMeths[i][j] = (vti < 0) ? dm : vDynMeths[vti];
4691 iMethTypes[i][j] = GetDynamicMethodDelegateType (dm, si.methSig);
4692 }
4693 stackedIFaces = null;
4694 }
4695 }
4696
4697 private Type GetDynamicMethodDelegateType (DynamicMethod dm, string methSig)
4698 {
4699 Type retType = dm.ReturnType;
4700 ParameterInfo[] pi = dm.GetParameters ();
4701 Type[] argTypes = new Type[pi.Length];
4702 for (int j = 0; j < pi.Length; j ++) {
4703 argTypes[j] = pi[j].ParameterType;
4704 }
4705 return DelegateCommon.GetType (retType, argTypes, methSig);
4706 }
4707
4708 public override void DebString (StringBuilder sb)
4709 {
4710 /*
4711 * Don't output if array of some type.
4712 * They will be re-instantiated as referenced by rest of script.
4713 */
4714 if (arrayOfType != null) return;
4715
4716 /*
4717 * This class name and extended/implemented type declaration.
4718 */
4719 sb.Append ("class ");
4720 sb.Append (shortName.val);
4721 bool first = true;
4722 if (extends != null) {
4723 sb.Append (" : ");
4724 sb.Append (extends.longName);
4725 first = false;
4726 }
4727 foreach (TokenDeclSDType impld in implements) {
4728 sb.Append (first ? " : " : ", ");
4729 sb.Append (impld.longName);
4730 first = false;
4731 }
4732 sb.Append (" {");
4733
4734 /*
4735 * Inner type definitions.
4736 */
4737 foreach (TokenDeclSDType subs in innerSDTypes.Values) {
4738 subs.DebString (sb);
4739 }
4740
4741 /*
4742 * Members (fields, methods, properties).
4743 */
4744 foreach (TokenDeclVar memb in members) {
4745 if ((memb == instFieldInit) || (memb == staticFieldInit)) {
4746 memb.DebStringInitFields (sb);
4747 } else if (memb.retType != null) {
4748 memb.DebString (sb);
4749 }
4750 }
4751
4752 sb.Append ('}');
4753 }
4754 }
4755
4756 public class TokenDeclSDTypeDelegate : TokenDeclSDType {
4757 private TokenType retType;
4758 private TokenType[] argTypes;
4759
4760 private string argSig;
4761 private string wholeSig;
4762 private Type sysType;
4763 private Type retSysType;
4764 private Type[] argSysTypes;
4765
4766 private string retStr;
4767 private string[] argStrs;
4768
4769 private static Dictionary<string, TokenDeclSDTypeDelegate> inlines = new Dictionary<string, TokenDeclSDTypeDelegate> ();
4770 private static Dictionary<Type, string> inlrevs = new Dictionary<Type, string> ();
4771
4772 public TokenDeclSDTypeDelegate (TokenName shortName) : base (shortName)
4773 {
4774 this.shortName = shortName;
4775 }
4776 public void SetRetArgTypes (TokenType retType, TokenType[] argTypes)
4777 {
4778 this.retType = retType;
4779 this.argTypes = argTypes;
4780 }
4781
4782 protected override TokenDeclSDType MakeBlank (TokenName shortName)
4783 {
4784 return new TokenDeclSDTypeDelegate (shortName);
4785 }
4786
4787 public override TokenType MakeRefToken (Token t)
4788 {
4789 return new TokenTypeSDTypeDelegate (t, this);
4790 }
4791
4792 /**
4793 * @brief Get system type for the whole delegate.
4794 */
4795 public override Type GetSysType ()
4796 {
4797 if (sysType == null) FillInStuff ();
4798 return sysType;
4799 }
4800
4801 /**
4802 * @brief Get the function's return value type (TokenTypeVoid if void, never null)
4803 */
4804 public TokenType GetRetType ()
4805 {
4806 if (retType == null) FillInStuff ();
4807 return retType;
4808 }
4809
4810 /**
4811 * @brief Get the function's argument types
4812 */
4813 public TokenType[] GetArgTypes ()
4814 {
4815 if (argTypes == null) FillInStuff ();
4816 return argTypes;
4817 }
4818
4819 /**
4820 * @brief Get signature for the whole delegate, eg, "void(integer,list)"
4821 */
4822 public string GetWholeSig ()
4823 {
4824 if (wholeSig == null) FillInStuff ();
4825 return wholeSig;
4826 }
4827
4828 /**
4829 * @brief Get signature for the arguments, eg, "(integer,list)"
4830 */
4831 public string GetArgSig ()
4832 {
4833 if (argSig == null) FillInStuff ();
4834 return argSig;
4835 }
4836
4837 /**
4838 * @brief Find out how to create one of these delegates.
4839 */
4840 public ConstructorInfo GetConstructorInfo ()
4841 {
4842 if (sysType == null) FillInStuff ();
4843 return sysType.GetConstructor (DelegateCommon.constructorArgTypes);
4844 }
4845
4846 /**
4847 * @brief Find out how to call what one of these delegates points to.
4848 */
4849 public MethodInfo GetInvokerInfo ()
4850 {
4851 if (sysType == null) FillInStuff ();
4852 return sysType.GetMethod ("Invoke", argSysTypes);
4853 }
4854
4855 /**
4856 * @brief Write enough out to a file so delegate can be reconstructed in ReadFromFile().
4857 */
4858 public override void WriteToFile (BinaryWriter objFileWriter)
4859 {
4860 objFileWriter.Write (this.file);
4861 objFileWriter.Write (this.line);
4862 objFileWriter.Write (this.posn);
4863 objFileWriter.Write ((byte)DELEGATE);
4864 objFileWriter.Write (this.sdTypeIndex);
4865
4866 objFileWriter.Write (retType.ToString ());
4867 int nArgs = argTypes.Length;
4868 objFileWriter.Write (nArgs);
4869 for (int i = 0; i < nArgs; i ++) {
4870 objFileWriter.Write (argTypes[i].ToString ());
4871 }
4872 }
4873
4874 /**
4875 * @brief Read that data from file so we can reconstruct.
4876 * Don't actually reconstruct yet in case any forward-referenced types are undefined.
4877 */
4878 public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
4879 {
4880 retStr = objFileReader.ReadString ();
4881 int nArgs = objFileReader.ReadInt32 ();
4882 if (asmFileWriter != null) {
4883 asmFileWriter.Write (" delegate " + retStr + " " + longName.val + "(");
4884 }
4885 argStrs = new string[nArgs];
4886 for (int i = 0; i < nArgs; i ++) {
4887 argStrs[i] = objFileReader.ReadString ();
4888 if (asmFileWriter != null) {
4889 if (i > 0) asmFileWriter.Write (",");
4890 asmFileWriter.Write (argStrs[i]);
4891 }
4892 }
4893 if (asmFileWriter != null) {
4894 asmFileWriter.WriteLine (");");
4895 }
4896 }
4897
4898 /**
4899 * @brief Fill in missing internal data.
4900 */
4901 private void FillInStuff ()
4902 {
4903 int nArgs;
4904
4905 /*
4906 * This happens when the node was restored via ReadFromFile().
4907 * It leaves the types in retStr/argStrs for resolution after
4908 * all definitions have been read from the object file in case
4909 * there are forward references.
4910 */
4911 if (retType == null) {
4912 retType = MakeTypeToken (retStr);
4913 }
4914 if (argTypes == null) {
4915 nArgs = argStrs.Length;
4916 argTypes = new TokenType[nArgs];
4917 for (int i = 0; i < nArgs; i ++) {
4918 argTypes[i] = MakeTypeToken (argStrs[i]);
4919 }
4920 }
4921
4922 /*
4923 * Fill in system types from token types.
4924 * Might as well build the signature strings too from token types.
4925 */
4926 retSysType = retType.ToSysType();
4927
4928 nArgs = argTypes.Length;
4929 StringBuilder sb = new StringBuilder ();
4930 argSysTypes = new Type[nArgs];
4931 sb.Append ('(');
4932 for (int i = 0; i < nArgs; i ++) {
4933 if (i > 0) sb.Append (',');
4934 sb.Append (argTypes[i].ToString ());
4935 argSysTypes[i] = argTypes[i].ToSysType ();
4936 }
4937 sb.Append (')');
4938 argSig = sb.ToString ();
4939 wholeSig = retType.ToString () + argSig;
4940
4941 /*
4942 * Now we can create a system delegate type from the given
4943 * return and argument types. Give it an unique name using
4944 * the whole signature string.
4945 */
4946 sysType = DelegateCommon.GetType (retSysType, argSysTypes, wholeSig);
4947 }
4948
4949 /**
4950 * @brief create delegate reference token for inline functions.
4951 * there is just one instance of these per inline function
4952 * shared by all scripts, and it is just used when the
4953 * script engine is loaded.
4954 */
4955 public static TokenDeclSDTypeDelegate CreateInline (TokenType retType, TokenType[] argTypes)
4956 {
4957 TokenDeclSDTypeDelegate decldel;
4958
4959 /*
4960 * Name it after the whole signature string.
4961 */
4962 StringBuilder sb = new StringBuilder ("$inline");
4963 sb.Append (retType.ToString ());
4964 sb.Append ("(");
4965 bool first = true;
4966 foreach (TokenType at in argTypes) {
4967 if (!first) sb.Append (",");
4968 sb.Append (at.ToString ());
4969 first = false;
4970 }
4971 sb.Append (")");
4972 string inlname = sb.ToString ();
4973 if (!inlines.TryGetValue (inlname, out decldel)) {
4974
4975 /*
4976 * Create the corresponding declaration and link to it
4977 */
4978 TokenName name = new TokenName (null, inlname);
4979 decldel = new TokenDeclSDTypeDelegate (name);
4980 decldel.retType = retType;
4981 decldel.argTypes = argTypes;
4982 inlines.Add (inlname, decldel);
4983 inlrevs.Add (decldel.GetSysType (), inlname);
4984 }
4985 return decldel;
4986 }
4987
4988 public static string TryGetInlineName (Type sysType)
4989 {
4990 string name;
4991 if (!inlrevs.TryGetValue (sysType, out name)) return null;
4992 return name;
4993 }
4994
4995 public static Type TryGetInlineSysType (string name)
4996 {
4997 TokenDeclSDTypeDelegate decl;
4998 if (!inlines.TryGetValue (name, out decl)) return null;
4999 return decl.GetSysType ();
5000 }
5001 }
5002
5003 public class TokenDeclSDTypeInterface : TokenDeclSDType {
5004 public VarDict methsNProps = new VarDict (false);
5005 // any class that implements this interface
5006 // must implement all of these methods & properties
5007
5008 public List<TokenDeclSDTypeInterface> implements = new List<TokenDeclSDTypeInterface> ();
5009 // any class that implements this interface
5010 // must also implement all of the methods & properties
5011 // of all of these interfaces
5012
5013 public TokenDeclSDTypeInterface (TokenName shortName) : base (shortName)
5014 {
5015 this.shortName = shortName;
5016 }
5017
5018 protected override TokenDeclSDType MakeBlank (TokenName shortName)
5019 {
5020 return new TokenDeclSDTypeInterface (shortName);
5021 }
5022
5023 public override TokenType MakeRefToken (Token t)
5024 {
5025 return new TokenTypeSDTypeInterface (t, this);
5026 }
5027
5028 public override Type GetSysType ()
5029 {
5030 // interfaces are implemented as arrays of delegates
5031 // they are taken from iDynMeths[interfaceIndex] of a script-defined class object
5032 return typeof (Delegate[]);
5033 }
5034
5035 public override void WriteToFile (BinaryWriter objFileWriter)
5036 {
5037 objFileWriter.Write (this.file);
5038 objFileWriter.Write (this.line);
5039 objFileWriter.Write (this.posn);
5040 objFileWriter.Write ((byte)INTERFACE);
5041 objFileWriter.Write (this.sdTypeIndex);
5042 }
5043
5044 public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
5045 { }
5046
5047 /**
5048 * @brief Add this interface to the list of interfaces implemented by a class if not already.
5049 * And also add this interface's implemented interfaces to the class for those not already there,
5050 * just as if the class itself had declared to implement those interfaces.
5051 */
5052 public void AddToClassDecl (TokenDeclSDTypeClass tokdeclcl)
5053 {
5054 if (!tokdeclcl.implements.Contains (this)) {
5055 tokdeclcl.implements.Add (this);
5056 foreach (TokenDeclSDTypeInterface subimpl in this.implements) {
5057 subimpl.AddToClassDecl (tokdeclcl);
5058 }
5059 }
5060 }
5061
5062 /**
5063 * @brief See if the 'this' interface implements the new interface.
5064 * Do a recursive (deep) check.
5065 */
5066 public bool Implements (TokenDeclSDTypeInterface newDecl)
5067 {
5068 foreach (TokenDeclSDTypeInterface ii in this.implements) {
5069 if (ii == newDecl) return true;
5070 if (ii.Implements (newDecl)) return true;
5071 }
5072 return false;
5073 }
5074
5075 /**
5076 * @brief Scan an interface and all its implemented interfaces for a method or property
5077 * @param scg = script code generator (ie, which script is being compiled)
5078 * @param fieldName = name of the member being looked for
5079 * @param argsig = the method's argument signature
5080 * @returns null: no such member; intf = undefined
5081 * else: member; intf = which interface actually found in
5082 */
5083 public TokenDeclVar FindIFaceMember (ScriptCodeGen scg, TokenName fieldName, TokenType[] argsig, out TokenDeclSDTypeInterface intf)
5084 {
5085 intf = this;
5086 TokenDeclVar var = scg.FindSingleMember (this.methsNProps, fieldName, argsig);
5087 if (var == null) {
5088 foreach (TokenDeclSDTypeInterface ii in this.implements) {
5089 var = ii.FindIFaceMember (scg, fieldName, argsig, out intf);
5090 if (var != null) break;
5091 }
5092 }
5093 return var;
5094 }
5095 }
5096
5097 public class TokenDeclSDTypeTypedef : TokenDeclSDType {
5098
5099 public TokenDeclSDTypeTypedef (TokenName shortName) : base (shortName)
5100 {
5101 this.shortName = shortName;
5102 }
5103
5104 protected override TokenDeclSDType MakeBlank (TokenName shortName)
5105 {
5106 return new TokenDeclSDTypeTypedef (shortName);
5107 }
5108
5109 public override TokenType MakeRefToken (Token t)
5110 {
5111 // if our body is a single type token, that is what we return
5112 // otherwise return null saying maybe our body needs some substitutions
5113 if (!(this.nextToken is TokenType)) return null;
5114 if (this.nextToken.nextToken != this.endToken) {
5115 this.nextToken.nextToken.ErrorMsg ("extra tokens for typedef");
5116 return null;
5117 }
5118 return (TokenType)this.nextToken.CopyToken (t);
5119 }
5120
5121 public override Type GetSysType ()
5122 {
5123 // we are just a macro
5124 // we are asked for system type because we are cataloged
5125 // but we don't really have one so return null
5126 return null;
5127 }
5128
5129 public override void WriteToFile (BinaryWriter objFileWriter)
5130 {
5131 objFileWriter.Write (this.file);
5132 objFileWriter.Write (this.line);
5133 objFileWriter.Write (this.posn);
5134 objFileWriter.Write ((byte)TYPEDEF);
5135 objFileWriter.Write (this.sdTypeIndex);
5136 }
5137
5138 public override void ReadFromFile (BinaryReader objFileReader, TextWriter asmFileWriter)
5139 { }
5140 }
5141
5142 /**
5143 * @brief Script-defined type references.
5144 * These occur in the source code wherever it specifies (eg, variable declaration) a script-defined type.
5145 * These must be copyable via CopyToken().
5146 */
5147 public abstract class TokenTypeSDType : TokenType {
5148 public TokenTypeSDType (TokenErrorMessage emsg, string file, int line, int posn) : base (emsg, file, line, posn) { }
5149 public TokenTypeSDType (Token t) : base (t) { }
5150 public abstract TokenDeclSDType GetDecl ();
5151 public abstract void SetDecl (TokenDeclSDType decl);
5152 }
5153
5154 public class TokenTypeSDTypeClass : TokenTypeSDType {
5155 private static readonly FieldInfo iarSDTClObjsFieldInfo = typeof (XMRInstArrays).GetField ("iarSDTClObjs");
5156
5157 public TokenDeclSDTypeClass decl;
5158
5159 public TokenTypeSDTypeClass (Token t, TokenDeclSDTypeClass decl) : base (t)
5160 {
5161 this.decl = decl;
5162 }
5163 public override TokenDeclSDType GetDecl ()
5164 {
5165 return decl;
5166 }
5167 public override void SetDecl (TokenDeclSDType decl)
5168 {
5169 this.decl = (TokenDeclSDTypeClass)decl;
5170 }
5171 public override string ToString ()
5172 {
5173 return decl.longName.val;
5174 }
5175 public override Type ToSysType ()
5176 {
5177 return typeof (XMRSDTypeClObj);
5178 }
5179
5180 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias)
5181 {
5182 declVar.vTableArray = iarSDTClObjsFieldInfo;
5183 declVar.vTableIndex = ias.iasSDTClObjs ++;
5184 }
5185
5186 // debugging
5187 public override void DebString (StringBuilder sb)
5188 {
5189 sb.Append (decl.longName);
5190 }
5191 }
5192
5193 public class TokenTypeSDTypeDelegate : TokenTypeSDType {
5194 private static readonly FieldInfo iarObjectsFieldInfo = typeof (XMRInstArrays).GetField ("iarObjects");
5195
5196 public TokenDeclSDTypeDelegate decl;
5197
5198 /**
5199 * @brief create a reference to an explicitly declared delegate
5200 * @param t = where the reference is being made in the source file
5201 * @param decl = the explicit delegate declaration
5202 */
5203 public TokenTypeSDTypeDelegate (Token t, TokenDeclSDTypeDelegate decl) : base (t)
5204 {
5205 this.decl = decl;
5206 }
5207 public override TokenDeclSDType GetDecl ()
5208 {
5209 return decl;
5210 }
5211 public override void SetDecl (TokenDeclSDType decl)
5212 {
5213 this.decl = (TokenDeclSDTypeDelegate)decl;
5214 }
5215
5216 /**
5217 * @brief create a reference to a possibly anonymous delegate
5218 * @param t = where the reference is being made in the source file
5219 * @param retType = return type (TokenTypeVoid if void, never null)
5220 * @param argTypes = script-visible argument types
5221 * @param tokenScript = what script this is part of
5222 */
5223 public TokenTypeSDTypeDelegate (Token t, TokenType retType, TokenType[] argTypes, TokenScript tokenScript) : base (t)
5224 {
5225 TokenDeclSDTypeDelegate decldel;
5226
5227 /*
5228 * See if we already have a matching declared one cataloged.
5229 */
5230 int nArgs = argTypes.Length;
5231 foreach (TokenDeclSDType decl in tokenScript.sdSrcTypesValues) {
5232 if (decl is TokenDeclSDTypeDelegate) {
5233 decldel = (TokenDeclSDTypeDelegate)decl;
5234 TokenType rt = decldel.GetRetType ();
5235 TokenType[] ats = decldel.GetArgTypes ();
5236 if ((rt.ToString () == retType.ToString ()) && (ats.Length == nArgs)) {
5237 for (int i = 0; i < nArgs; i ++) {
5238 if (ats[i].ToString () != argTypes[i].ToString ()) goto nomatch;
5239 }
5240 this.decl = decldel;
5241 return;
5242 }
5243 }
5244 nomatch:;
5245 }
5246
5247 /*
5248 * No such luck, create a new anonymous declaration.
5249 */
5250 StringBuilder sb = new StringBuilder ("$anondel$");
5251 sb.Append (retType.ToString ());
5252 sb.Append ("(");
5253 bool first = true;
5254 foreach (TokenType at in argTypes) {
5255 if (!first) sb.Append (",");
5256 sb.Append (at.ToString ());
5257 first = false;
5258 }
5259 sb.Append (")");
5260 TokenName name = new TokenName (t, sb.ToString ());
5261 decldel = new TokenDeclSDTypeDelegate (name);
5262 decldel.SetRetArgTypes (retType, argTypes);
5263 tokenScript.sdSrcTypesAdd (name.val, decldel);
5264 this.decl = decldel;
5265 }
5266
5267 public override Type ToSysType ()
5268 {
5269 return decl.GetSysType ();
5270 }
5271
5272 public override string ToString ()
5273 {
5274 return decl.longName.val;
5275 }
5276
5277 /**
5278 * @brief Assign slots in the gblObjects[] array because we have to typecast out in any case.
5279 * Likewise with the sdtcObjects[] array.
5280 */
5281 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias)
5282 {
5283 declVar.vTableArray = iarObjectsFieldInfo;
5284 declVar.vTableIndex = ias.iasObjects ++;
5285 }
5286
5287 /**
5288 * @brief create delegate reference token for inline functions.
5289 */
5290 public TokenTypeSDTypeDelegate (TokenType retType, TokenType[] argTypes) : base (null)
5291 {
5292 this.decl = TokenDeclSDTypeDelegate.CreateInline (retType, argTypes);
5293 }
5294
5295 // debugging
5296 public override void DebString (StringBuilder sb)
5297 {
5298 sb.Append (decl.longName);
5299 }
5300 }
5301
5302 public class TokenTypeSDTypeInterface : TokenTypeSDType {
5303 private static readonly FieldInfo iarSDTIntfObjsFieldInfo = typeof (XMRInstArrays).GetField ("iarSDTIntfObjs");
5304
5305 public TokenDeclSDTypeInterface decl;
5306
5307 public TokenTypeSDTypeInterface (Token t, TokenDeclSDTypeInterface decl) : base (t)
5308 {
5309 this.decl = decl;
5310 }
5311 public override TokenDeclSDType GetDecl ()
5312 {
5313 return decl;
5314 }
5315 public override void SetDecl (TokenDeclSDType decl)
5316 {
5317 this.decl = (TokenDeclSDTypeInterface)decl;
5318 }
5319
5320 public override string ToString ()
5321 {
5322 return decl.longName.val;
5323 }
5324 public override Type ToSysType ()
5325 {
5326 return typeof (Delegate[]);
5327 }
5328
5329 /**
5330 * @brief Assign slots in the gblSDTIntfObjs[] array
5331 * Likewise with the sdtcSDTIntfObjs[] array.
5332 */
5333 public override void AssignVarSlot (TokenDeclVar declVar, XMRInstArSizes ias)
5334 {
5335 declVar.vTableArray = iarSDTIntfObjsFieldInfo;
5336 declVar.vTableIndex = ias.iasSDTIntfObjs ++;
5337 }
5338
5339 // debugging
5340 public override void DebString (StringBuilder sb)
5341 {
5342 sb.Append (decl.longName);
5343 }
5344 }
5345
5346 /**
5347 * @brief function argument list declaration
5348 */
5349 public class TokenArgDecl : Token
5350 {
5351 public VarDict varDict = new VarDict (false);
5352
5353 public TokenArgDecl (Token original) : base (original) { }
5354
5355 public bool AddArg (TokenType type, TokenName name)
5356 {
5357 TokenDeclVar var = new TokenDeclVar (name, null, null);
5358 var.name = name;
5359 var.type = type;
5360 var.vTableIndex = varDict.Count;
5361 return varDict.AddEntry (var);
5362 }
5363
5364 /**
5365 * @brief Get an array of the argument types.
5366 */
5367 private TokenType[] _types;
5368 public TokenType[] types {
5369 get {
5370 if (_types == null) {
5371 _types = new TokenType[varDict.Count];
5372 foreach (TokenDeclVar var in varDict) {
5373 _types[var.vTableIndex] = var.type;
5374 }
5375 }
5376 return _types;
5377 }
5378 }
5379
5380 /**
5381 * @brief Access the arguments as an array of variables.
5382 */
5383 private TokenDeclVar[] _vars;
5384 public TokenDeclVar[] vars {
5385 get {
5386 if (_vars == null) {
5387 _vars = new TokenDeclVar[varDict.Count];
5388 foreach (TokenDeclVar var in varDict) {
5389 _vars[var.vTableIndex] = var;
5390 }
5391 }
5392 return _vars;
5393 }
5394 }
5395
5396 /**
5397 * @brief Get argument signature string, eg, "(list,vector,integer)"
5398 */
5399 private string argSig = null;
5400 public string GetArgSig ()
5401 {
5402 if (argSig == null) {
5403 argSig = ScriptCodeGen.ArgSigString (types);
5404 }
5405 return argSig;
5406 }
5407 }
5408
5409 /**
5410 * @brief encapsulate a state declaration in a single token
5411 */
5412 public class TokenDeclState : Token {
5413
5414 public TokenName name; // null for default state
5415 public TokenStateBody body;
5416
5417 public TokenDeclState (Token original) : base (original) { }
5418
5419 public override void DebString (StringBuilder sb)
5420 {
5421 if (name == null) {
5422 sb.Append ("default");
5423 } else {
5424 sb.Append ("state ");
5425 sb.Append (name);
5426 }
5427 body.DebString (sb);
5428 }
5429 }
5430
5431 /**
5432 * @brief encapsulate the declaration of a field/function/method/property/variable.
5433 */
5434
5435 public enum Triviality { // function triviality: has no loops and doesn't call anything that has loops
5436 // such a function does not need all the CheckRun() and stack serialization stuff
5437 unknown, // function's Triviality unknown as of yet
5438 // - it does not have any loops or backward gotos
5439 // - nothing it calls is known to be complex
5440 trivial, // function known to be trivial
5441 // - it does not have any loops or backward gotos
5442 // - everything it calls is known to be trivial
5443 complex, // function known to be complex
5444 // - it has loops or backward gotos
5445 // - something it calls is known to be complex
5446 analyzing // triviality is being analyzed (used to detect recursive loops)
5447 };
5448
5449 public class TokenDeclVar : TokenStmt {
5450 public TokenName name; // vars: name; funcs: bare name, ie, no signature
5451 public TokenRVal init; // vars: null if none; funcs: null
5452 public bool constant; // vars: 'constant'; funcs: false
5453 public uint sdtFlags; // SDT_<*> flags
5454
5455 public CompValu location; // used by codegen to keep track of location
5456 public FieldInfo vTableArray;
5457 public int vTableIndex = -1; // local vars: not used (-1)
5458 // arg vars: index in the arg list
5459 // global vars: which slot in gbl<Type>s[] array it is stored
5460 // instance vars: which slot in inst<Types>s[] array it is stored
5461 // static vars: which slot in gbl<Type>s[] array it is stored
5462 // global funcs: not used (-1)
5463 // virt funcs: which slot in vTable[] array it is stored
5464 // instance func: not used (-1)
5465 public TokenDeclVar getProp; // if property, function that reads value
5466 public TokenDeclVar setProp; // if property, function that writes value
5467
5468 public TokenScript tokenScript; // what script this function is part of
5469 public TokenDeclSDType sdtClass; // null: script global member
5470 // else: member is part of this script-defined type
5471
5472 // function-only data:
5473
5474 public TokenType retType; // vars: null; funcs: TokenTypeVoid if void
5475 public TokenArgDecl argDecl; // vars: null; funcs: argument list prototypes
5476 public TokenStmtBlock body; // vars: null; funcs: statements (null iff abstract)
5477 public Dictionary<string, TokenStmtLabel> labels = new Dictionary<string, TokenStmtLabel> ();
5478 // all labels defined in the function
5479 public LinkedList<TokenDeclVar> localVars = new LinkedList<TokenDeclVar> ();
5480 // all local variables declared by this function
5481 // - doesn't include argument variables
5482 public TokenIntfImpl implements; // if script-defined type method, what interface method(s) this func implements
5483 public TokenRValCall baseCtorCall; // if script-defined type constructor, call to base constructor, if any
5484 public Triviality triviality = Triviality.unknown;
5485 // vars: unknown (not used for any thing); funcs: unknown/trivial/complex
5486 public LinkedList<TokenRValCall> unknownTrivialityCalls = new LinkedList<TokenRValCall> ();
5487 // reduction puts all calls here
5488 // compilation sorts it all out
5489
5490 public ScriptObjWriter ilGen; // codegen stores emitted code here
5491
5492 /**
5493 * @brief Set up a variable declaration token.
5494 * @param original = original source token that triggered definition
5495 * (for error messages)
5496 * @param func = null: global variable
5497 * else: local to the given function
5498 */
5499 public TokenDeclVar (Token original, TokenDeclVar func, TokenScript ts) : base (original)
5500 {
5501 if (func != null) {
5502 func.localVars.AddLast (this);
5503 }
5504 tokenScript = ts;
5505 }
5506
5507 /**
5508 * @brief Get/Set overall type
5509 * For vars, this is the type of the location
5510 * For funcs, this is the delegate type
5511 */
5512 private TokenType _type;
5513 public TokenType type {
5514 get {
5515 if (_type == null) {
5516 GetDelType ();
5517 }
5518 return _type;
5519 }
5520 set {
5521 _type = value;
5522 }
5523 }
5524
5525 /**
5526 * @brief Full name: <fulltype>.<name>(<argsig>)
5527 * (<argsig>) missing for fields/variables
5528 * <fulltype>. missing for top-level functions/variables
5529 */
5530 public string fullName {
5531 get {
5532 if (sdtClass == null) {
5533 if (retType == null) return name.val;
5534 return funcNameSig.val;
5535 }
5536 string ln = sdtClass.longName.val;
5537 if (retType == null) return ln + "." + name.val;
5538 return ln + "." + funcNameSig.val;
5539 }
5540 }
5541
5542 /**
5543 * @brief See if reading or writing the variable is trivial.
5544 * Note that for functions, this is reading the function itself,
5545 * as in 'someDelegate = SomeFunction;', not calling it as such.
5546 * The triviality of actually calling the function is IsFuncTrivial().
5547 */
5548 public bool IsVarTrivial (ScriptCodeGen scg)
5549 {
5550 // reading or writing a property involves a function call however
5551 // so we need to check the triviality of the property functions
5552 if ((getProp != null) && !getProp.IsFuncTrivial (scg)) return false;
5553 if ((setProp != null) && !setProp.IsFuncTrivial (scg)) return false;
5554
5555 // otherwise for variables it is a trivial access
5556 // and likewise for getting a delegate that points to a function
5557 return true;
5558 }
5559
5560 /***************************\
5561 * FUNCTION-only methods *
5562 \***************************/
5563
5564 private TokenName _funcNameSig; // vars: null; funcs: function name including argumet signature, eg, "PrintStuff(list,string)"
5565 public TokenName funcNameSig {
5566 get {
5567 if (_funcNameSig == null) {
5568 if (argDecl == null) return null;
5569 _funcNameSig = new TokenName (name, name.val + argDecl.GetArgSig ());
5570 }
5571 return _funcNameSig;
5572 }
5573 }
5574
5575 /**
5576 * @brief The bare function name, ie, without any signature info
5577 */
5578 public string GetSimpleName ()
5579 {
5580 return name.val;
5581 }
5582
5583 /**
5584 * @brief The function name as it appears in the object code,
5585 * ie, script-defined type name if any,
5586 * bare function name and argument signature,
5587 * eg, "MyClass.PrintStuff(string)"
5588 */
5589 public string GetObjCodeName ()
5590 {
5591 string objCodeName = "";
5592 if (sdtClass != null) {
5593 objCodeName += sdtClass.longName.val + ".";
5594 }
5595 objCodeName += funcNameSig.val;
5596 return objCodeName;
5597 }
5598
5599 /**
5600 * @brief Get delegate type.
5601 * This is the function's script-visible type,
5602 * It includes return type and all script-visible argument types.
5603 * @returns null for vars; else delegate type for funcs
5604 */
5605 public TokenTypeSDTypeDelegate GetDelType ()
5606 {
5607 if (argDecl == null) return null;
5608 if (_type == null) {
5609 if (tokenScript == null) {
5610 // used during startup to define inline function delegate types
5611 _type = new TokenTypeSDTypeDelegate (retType, argDecl.types);
5612 } else {
5613 // used for normal script processing
5614 _type = new TokenTypeSDTypeDelegate (this, retType, argDecl.types, tokenScript);
5615 }
5616 }
5617 if (!(_type is TokenTypeSDTypeDelegate)) return null;
5618 return (TokenTypeSDTypeDelegate)_type;
5619 }
5620
5621 /**
5622 * @brief See if the function's code itself is trivial or not.
5623 * If it contains any loops (calls to CheckRun()), it is not trivial.
5624 * If it calls anything that is not trivial, it is not trivial.
5625 * Otherwise it is trivial.
5626 */
5627 public bool IsFuncTrivial (ScriptCodeGen scg)
5628 {
5629 /*
5630 * If not really a function, assume it's a delegate.
5631 * And since we don't really know what functions it can point to,
5632 * assume it can point to a non-trivial one.
5633 */
5634 if (retType == null) return false;
5635
5636 /*
5637 * All virtual functions are non-trivial because although a particular
5638 * one might be trivial, it might be overidden with a non-trivial one.
5639 */
5640 if ((sdtFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE |
5641 ScriptReduce.SDT_VIRTUAL)) != 0) {
5642 return false;
5643 }
5644
5645 /*
5646 * Check the triviality status of the function.
5647 */
5648 switch (triviality) {
5649
5650 /*
5651 * Don't yet know if it is trivial.
5652 * We know at this point it doesn't have any direct looping.
5653 * But if it calls something that has loops, it isn't trivial.
5654 * Otherwise it is trivial.
5655 */
5656 case Triviality.unknown: {
5657
5658 /*
5659 * Mark that we are analyzing this function now. So if there are
5660 * any recursive call loops, that will show that the function is
5661 * non-trivial and the analysis will terminate at that point.
5662 */
5663 triviality = Triviality.analyzing;
5664
5665 /*
5666 * Check out everything else this function calls. If any say they
5667 * aren't trivial, then we say this function isn't trivial.
5668 */
5669 foreach (TokenRValCall call in unknownTrivialityCalls) {
5670 if (!call.IsRValTrivial (scg, null)) {
5671 triviality = Triviality.complex;
5672 return false;
5673 }
5674 }
5675
5676 /*
5677 * All functions called by this function are trivial, and this
5678 * function's code doesn't have any loops, so we can mark this
5679 * function as being trivial.
5680 */
5681 triviality = Triviality.trivial;
5682 return true;
5683 }
5684
5685 /*
5686 * We already know that it is trivial.
5687 */
5688 case Triviality.trivial: {
5689 return true;
5690 }
5691
5692 /*
5693 * We either know it is complex or are trying to analyze it already.
5694 * If we are already analyzing it, it means it has a recursive loop
5695 * and we assume those are non-trivial.
5696 */
5697 default: return false;
5698 }
5699 }
5700
5701 // debugging
5702 public override void DebString (StringBuilder sb)
5703 {
5704 DebStringSDTFlags (sb);
5705
5706 if (retType == null) {
5707 sb.Append (constant ? "constant" : type.ToString ());
5708 sb.Append (' ');
5709 sb.Append (name.val);
5710 if (init != null) {
5711 sb.Append (" = ");
5712 init.DebString (sb);
5713 }
5714 sb.Append (';');
5715 } else {
5716 if (!(retType is TokenTypeVoid)) {
5717 sb.Append (retType.ToString ());
5718 sb.Append (' ');
5719 }
5720 string namestr = name.val;
5721 if (namestr == "$ctor") namestr = "constructor";
5722 sb.Append (namestr);
5723 sb.Append (" (");
5724 for (int i = 0; i < argDecl.vars.Length; i ++) {
5725 if (i > 0) sb.Append (", ");
5726 sb.Append (argDecl.vars[i].type.ToString ());
5727 sb.Append (' ');
5728 sb.Append (argDecl.vars[i].name.val);
5729 }
5730 sb.Append (')');
5731 if (body == null) sb.Append (';');
5732 else {
5733 sb.Append (' ');
5734 body.DebString (sb);
5735 }
5736 }
5737 }
5738
5739 // debugging
5740 // - used to output contents of a $globalvarinit(), $instfieldinit() or $statisfieldinit() function
5741 // as a series of variable declaration statements with initial value assignments
5742 // so we get the initial value assignments done in same order as specified in script
5743 public void DebStringInitFields (StringBuilder sb)
5744 {
5745 if ((retType == null) || !(retType is TokenTypeVoid)) throw new Exception ("bad return type " + retType.GetType ().Name);
5746 if (argDecl.vars.Length != 0) throw new Exception ("has " + argDecl.vars.Length + " arg(s)");
5747
5748 for (Token stmt = body.statements; stmt != null; stmt = stmt.nextToken) {
5749
5750 /*
5751 * Body of the function should all be arithmetic statements (not eg for loops, if statements etc).
5752 */
5753 TokenRVal rval = ((TokenStmtRVal)stmt).rVal;
5754
5755 /*
5756 * And the opcode should be a simple assignment operator.
5757 */
5758 TokenRValOpBin rvob = (TokenRValOpBin)rval;
5759 if (!(rvob.opcode is TokenKwAssign)) throw new Exception ("bad op type " + rvob.opcode.GetType ().Name);
5760
5761 /*
5762 * Get field or variable being assigned to.
5763 */
5764 TokenDeclVar var = null;
5765 TokenRVal left = rvob.rValLeft;
5766 if (left is TokenLValIField) {
5767 TokenLValIField ifield = (TokenLValIField)left;
5768 TokenRValThis zhis = (TokenRValThis)ifield.baseRVal;
5769 TokenDeclSDTypeClass sdt = zhis.sdtClass;
5770 var = sdt.members.FindExact (ifield.fieldName.val, null);
5771 }
5772 if (left is TokenLValName) {
5773 TokenLValName global = (TokenLValName)left;
5774 var = global.stack.FindExact (global.name.val, null);
5775 }
5776 if (left is TokenLValSField) {
5777 TokenLValSField sfield = (TokenLValSField)left;
5778 TokenTypeSDTypeClass sdtc = (TokenTypeSDTypeClass)sfield.baseType;
5779 TokenDeclSDTypeClass decl = sdtc.decl;
5780 var = decl.members.FindExact (sfield.fieldName.val, null);
5781 }
5782 if (var == null) throw new Exception ("unknown var type " + left.GetType ().Name);
5783
5784 /*
5785 * Output flags, type name and bare variable name.
5786 * This should look like a declaration in the 'sb'
5787 * as it is not enclosed in a function.
5788 */
5789 var.DebStringSDTFlags (sb);
5790 var.type.DebString (sb);
5791 sb.Append (' ');
5792 sb.Append (var.name.val);
5793
5794 /*
5795 * Maybe it has a non-default initialization value.
5796 */
5797 if ((var.init != null) && !(var.init is TokenRValInitDef)) {
5798 sb.Append (" = ");
5799 var.init.DebString (sb);
5800 }
5801
5802 /*
5803 * End of declaration statement.
5804 */
5805 sb.Append (';');
5806 }
5807 }
5808
5809 private void DebStringSDTFlags (StringBuilder sb)
5810 {
5811 if ((sdtFlags & ScriptReduce.SDT_PRIVATE) != 0) sb.Append ("private ");
5812 if ((sdtFlags & ScriptReduce.SDT_PROTECTED) != 0) sb.Append ("protected ");
5813 if ((sdtFlags & ScriptReduce.SDT_PUBLIC) != 0) sb.Append ("public ");
5814 if ((sdtFlags & ScriptReduce.SDT_ABSTRACT) != 0) sb.Append ("abstract ");
5815 if ((sdtFlags & ScriptReduce.SDT_FINAL) != 0) sb.Append ("final ");
5816 if ((sdtFlags & ScriptReduce.SDT_NEW) != 0) sb.Append ("new ");
5817 if ((sdtFlags & ScriptReduce.SDT_OVERRIDE) != 0) sb.Append ("override ");
5818 if ((sdtFlags & ScriptReduce.SDT_STATIC) != 0) sb.Append ("static ");
5819 if ((sdtFlags & ScriptReduce.SDT_VIRTUAL) != 0) sb.Append ("virtual ");
5820 }
5821 }
5822
5823 /**
5824 * @brief Indicates an interface type.method that is implemented by the function
5825 */
5826 public class TokenIntfImpl : Token {
5827 public TokenTypeSDTypeInterface intfType;
5828 public TokenName methName; // simple name, no arg signature
5829
5830 public TokenIntfImpl (TokenTypeSDTypeInterface intfType, TokenName methName) : base (intfType)
5831 {
5832 this.intfType = intfType;
5833 this.methName = methName;
5834 }
5835 }
5836
5837 /**
5838 * @brief any expression that can go on left side of an "="
5839 */
5840 public abstract class TokenLVal : TokenRVal {
5841 public TokenLVal (Token original) : base (original) { }
5842 public abstract override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig);
5843 public abstract override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig);
5844 }
5845
5846 /**
5847 * @brief an element of an array is an L-value
5848 */
5849 public class TokenLValArEle : TokenLVal {
5850 public TokenRVal baseRVal;
5851 public TokenRVal subRVal;
5852
5853 public TokenLValArEle (Token original) : base (original) { }
5854
5855 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
5856 {
5857 TokenType baseType = baseRVal.GetRValType (scg, null);
5858
5859 /*
5860 * Maybe referencing element of a fixed-dimension array.
5861 */
5862 if ((baseType is TokenTypeSDTypeClass) && (((TokenTypeSDTypeClass)baseType).decl.arrayOfType != null)) {
5863 return ((TokenTypeSDTypeClass)baseType).decl.arrayOfType;
5864 }
5865
5866 /*
5867 * Maybe referencing $idxprop property of script-defined class or interface.
5868 */
5869 if (baseType is TokenTypeSDTypeClass) {
5870 TokenDeclSDTypeClass sdtDecl = ((TokenTypeSDTypeClass)baseType).decl;
5871 TokenDeclVar idxProp = scg.FindSingleMember (sdtDecl.members, new TokenName (this, "$idxprop"), null);
5872 if (idxProp != null) return idxProp.type;
5873 }
5874 if (baseType is TokenTypeSDTypeInterface) {
5875 TokenDeclSDTypeInterface sdtDecl = ((TokenTypeSDTypeInterface)baseType).decl;
5876 TokenDeclVar idxProp = sdtDecl.FindIFaceMember (scg, new TokenName (this, "$idxprop"), null, out sdtDecl);
5877 if (idxProp != null) return idxProp.type;
5878 }
5879
5880 /*
5881 * Maybe referencing single character of a string.
5882 */
5883 if ((baseType is TokenTypeKey) || (baseType is TokenTypeStr)) {
5884 return new TokenTypeChar (this);
5885 }
5886
5887 /*
5888 * Assume XMR_Array element or extracting element from list.
5889 */
5890 if ((baseType is TokenTypeArray) || (baseType is TokenTypeList)) {
5891 return new TokenTypeObject (this);
5892 }
5893
5894 scg.ErrorMsg (this, "unknown array reference");
5895 return new TokenTypeVoid (this);
5896 }
5897
5898 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
5899 {
5900 return baseRVal.IsRValTrivial (scg, null) && subRVal.IsRValTrivial (scg, null);
5901 }
5902
5903 public override void DebString (StringBuilder sb)
5904 {
5905 baseRVal.DebString (sb);
5906 sb.Append ('[');
5907 subRVal.DebString (sb);
5908 sb.Append (']');
5909 }
5910 }
5911
5912 /**
5913 * @brief 'base.' being used to reference a field/method of the extended class.
5914 */
5915 public class TokenLValBaseField : TokenLVal {
5916 public TokenName fieldName;
5917 private TokenDeclSDTypeClass thisClass;
5918
5919 public TokenLValBaseField (Token original, TokenName fieldName, TokenDeclSDTypeClass thisClass) : base (original)
5920 {
5921 this.fieldName = fieldName;
5922 this.thisClass = thisClass;
5923 }
5924
5925 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
5926 {
5927 TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig);
5928 if (var != null) return var.type;
5929 scg.ErrorMsg (fieldName, "unknown member of " + thisClass.extends.ToString ());
5930 return new TokenTypeVoid (fieldName);
5931 }
5932
5933 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
5934 {
5935 TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig);
5936 return (var != null) && var.IsVarTrivial (scg);
5937 }
5938
5939 public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
5940 {
5941 TokenDeclVar var = scg.FindThisMember (thisClass.extends, fieldName, argsig);
5942 return (var != null) && var.IsFuncTrivial (scg);
5943 }
5944 }
5945
5946 /**
5947 * @brief a field within an struct is an L-value
5948 */
5949 public class TokenLValIField : TokenLVal {
5950 public TokenRVal baseRVal;
5951 public TokenName fieldName;
5952
5953 public TokenLValIField (Token original) : base (original) { }
5954
5955 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
5956 {
5957 TokenType baseType = baseRVal.GetRValType (scg, null);
5958 if (baseType is TokenTypeSDTypeClass) {
5959 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
5960 if (var != null) return var.type;
5961 }
5962 if (baseType is TokenTypeSDTypeInterface) {
5963 TokenDeclSDTypeInterface baseIntfDecl = ((TokenTypeSDTypeInterface)baseType).decl;
5964 TokenDeclVar var = baseIntfDecl.FindIFaceMember (scg, fieldName, argsig, out baseIntfDecl);
5965 if (var != null) return var.type;
5966 }
5967 if (baseType is TokenTypeArray) {
5968 return XMR_Array.GetRValType (fieldName);
5969 }
5970 if ((baseType is TokenTypeRot) || (baseType is TokenTypeVec)) {
5971 return new TokenTypeFloat (fieldName);
5972 }
5973 scg.ErrorMsg (fieldName, "unknown member of " + baseType.ToString ());
5974 return new TokenTypeVoid (fieldName);
5975 }
5976
5977 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
5978 {
5979 /*
5980 * If getting pointer to instance isn't trivial, then accessing the member isn't trivial either.
5981 */
5982 if (!baseRVal.IsRValTrivial (scg, null)) return false;
5983
5984 /*
5985 * Accessing a member of a class depends on the member.
5986 * In the case of a method, this is accessing it as a delegate, not calling it, and
5987 * argsig simply serves as selecting which of possibly overloaded methods to select.
5988 * The case of accessing a property, however, depends on the property implementation,
5989 * as there could be looping inside the property code.
5990 */
5991 TokenType baseType = baseRVal.GetRValType (scg, null);
5992 if (baseType is TokenTypeSDTypeClass) {
5993 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
5994 return (var != null) && var.IsVarTrivial (scg);
5995 }
5996
5997 /*
5998 * Accessing the members of anything else (arrays, rotations, vectors) is always trivial.
5999 */
6000 return true;
6001 }
6002
6003 /**
6004 * @brief Check to see if the case of calling an instance method of some object is trivial.
6005 * @param scg = script making the call
6006 * @param argsig = argument types of the call (used to select among overloads)
6007 * @returns true iff we can tell at compile time that the call will always call a trivial method
6008 */
6009 public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
6010 {
6011 /*
6012 * If getting pointer to instance isn't trivial, then calling the method isn't trivial either.
6013 */
6014 if (!baseRVal.IsRValTrivial (scg, null)) return false;
6015
6016 /*
6017 * Calling a method of a class depends on the method.
6018 */
6019 TokenType baseType = baseRVal.GetRValType (scg, null);
6020 if (baseType is TokenTypeSDTypeClass) {
6021 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
6022 return (var != null) && var.IsFuncTrivial (scg);
6023 }
6024
6025 /*
6026 * Calling via a pointer to an interface instance is never trivial.
6027 * (It is really a pointer to an array of delegates).
6028 * We can't tell for this call site whether the actual method being called is trivial or not,
6029 * so we have to assume it isn't.
6030 * ??? We could theoretically check to see if *all* implementations of this method of
6031 * this interface are trivial, then we could conclude that this call is trivial.
6032 */
6033 if (baseType is TokenTypeSDTypeInterface) return false;
6034
6035 /*
6036 * Calling a method of anything else (arrays, rotations, vectors) is always trivial.
6037 * Even methods of delegates, such as ".GetArgTypes()" that we may someday do is trivial.
6038 */
6039 return true;
6040 }
6041
6042 // debugging
6043 public override void DebString (StringBuilder sb)
6044 {
6045 baseRVal.DebString (sb);
6046 sb.Append ('.');
6047 sb.Append (fieldName.val);
6048 }
6049 }
6050
6051 /**
6052 * @brief a name is being used as an L-value
6053 */
6054 public class TokenLValName : TokenLVal {
6055 public TokenName name;
6056 public VarDict stack;
6057
6058 public TokenLValName (TokenName name, VarDict stack) : base (name)
6059 {
6060 /*
6061 * Save name of variable/method/function/field.
6062 */
6063 this.name = name;
6064
6065 /*
6066 * Save where in the stack it can be looked up.
6067 * If the current stack is for locals, do not allow forward references.
6068 * this allows idiocy like:
6069 * list buttons = [ 1, 2, 3 ];
6070 * x () {
6071 * list buttons = llList2List (buttons, 0, 1);
6072 * llOwnerSay (llList2CSV (buttons));
6073 * }
6074 * If it is not locals, allow forward references.
6075 * this allows function X() to call Y() and Y() to call X().
6076 */
6077 this.stack = stack.FreezeLocals ();
6078 }
6079
6080 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6081 {
6082 TokenDeclVar var = scg.FindNamedVar (this, argsig);
6083 if (var != null) return var.type;
6084 scg.ErrorMsg (name, "undefined name " + name.val + ScriptCodeGen.ArgSigString (argsig));
6085 return new TokenTypeVoid (name);
6086 }
6087
6088 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6089 {
6090 TokenDeclVar var = scg.FindNamedVar (this, argsig);
6091 return (var != null) && var.IsVarTrivial (scg);
6092 }
6093
6094 /**
6095 * @brief Check to see if the case of calling a global method is trivial.
6096 * @param scg = script making the call
6097 * @param argsig = argument types of the call (used to select among overloads)
6098 * @returns true iff we can tell at compile time that the call will always call a trivial method
6099 */
6100 public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
6101 {
6102 TokenDeclVar var = scg.FindNamedVar (this, argsig);
6103 return (var != null) && var.IsFuncTrivial (scg);
6104 }
6105
6106 // debugging
6107 public override void DebString (StringBuilder sb)
6108 {
6109 sb.Append (name.val);
6110 }
6111 }
6112
6113 /**
6114 * @brief a static field within a struct is an L-value
6115 */
6116 public class TokenLValSField : TokenLVal {
6117 public TokenType baseType;
6118 public TokenName fieldName;
6119
6120 public TokenLValSField (Token original) : base (original) { }
6121
6122 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6123 {
6124 if (baseType is TokenTypeSDTypeClass) {
6125 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
6126 if (var != null) return var.type;
6127 }
6128 scg.ErrorMsg (fieldName, "unknown member of " + baseType.ToString ());
6129 return new TokenTypeVoid (fieldName);
6130 }
6131
6132 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6133 {
6134 /*
6135 * Accessing a member of a class depends on the member.
6136 * In the case of a method, this is accessing it as a delegate, not calling it, and
6137 * argsig simply serves as selecting which of possibly overloaded methods to select.
6138 * The case of accessing a property, however, depends on the property implementation,
6139 * as there could be looping inside the property code.
6140 */
6141 if (baseType is TokenTypeSDTypeClass) {
6142 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
6143 return (var != null) && var.IsVarTrivial (scg);
6144 }
6145
6146 /*
6147 * Accessing the fields/methods/properties of anything else (arrays, rotations, vectors) is always trivial.
6148 */
6149 return true;
6150 }
6151
6152 /**
6153 * @brief Check to see if the case of calling a class' static method is trivial.
6154 * @param scg = script making the call
6155 * @param argsig = argument types of the call (used to select among overloads)
6156 * @returns true iff we can tell at compile time that the call will always call a trivial method
6157 */
6158 public override bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
6159 {
6160 /*
6161 * Calling a static method of a class depends on the method.
6162 */
6163 if (baseType is TokenTypeSDTypeClass) {
6164 TokenDeclVar var = scg.FindThisMember ((TokenTypeSDTypeClass)baseType, fieldName, argsig);
6165 return (var != null) && var.IsFuncTrivial (scg);
6166 }
6167
6168 /*
6169 * Calling a static method of anything else (arrays, rotations, vectors) is always trivial.
6170 */
6171 return true;
6172 }
6173
6174 public override void DebString (StringBuilder sb)
6175 {
6176 if (fieldName.val == "$new") {
6177 sb.Append ("new ");
6178 baseType.DebString (sb);
6179 } else {
6180 baseType.DebString (sb);
6181 sb.Append ('.');
6182 fieldName.DebString (sb);
6183 }
6184 }
6185 }
6186
6187 /**
6188 * @brief any expression that can go on right side of "="
6189 */
6190 public delegate TokenRVal TCCLookup (TokenRVal rVal, ref bool didOne);
6191 public abstract class TokenRVal : Token {
6192 public TokenRVal (Token original) : base (original) { }
6193
6194 /**
6195 * @brief Tell us the type of the expression.
6196 */
6197 public abstract TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig);
6198
6199 /**
6200 * @brief Tell us if reading and writing the value is trivial.
6201 *
6202 * @param scg = script code generator of script making the access
6203 * @param argsig = argument types of the call (used to select among overloads)
6204 * @returns true: we can tell at compile time that reading/writing this location
6205 * will always be trivial (no looping or CheckRun() calls possible).
6206 * false: otherwise
6207 */
6208 public abstract bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig);
6209
6210 /**
6211 * @brief Tell us if calling the method is trivial.
6212 *
6213 * This is the default implementation that returns false.
6214 * It is only used if the location is holding a delegate
6215 * and the method that the delegate is pointing to is being
6216 * called. Since we can't tell if the actual runtime method
6217 * is trivial or not, we assume it isn't.
6218 *
6219 * For the more usual ways of calling functions, see the
6220 * various overrides of IsCallTrivial().
6221 *
6222 * @param scg = script code generator of script making the call
6223 * @param argsig = argument types of the call (used to select among overloads)
6224 * @returns true: we can tell at compile time that this call will always
6225 * be to a trivial function/method (no looping or CheckRun()
6226 * calls possible).
6227 * false: otherwise
6228 */
6229 public virtual bool IsCallTrivial (ScriptCodeGen scg, TokenType[] argsig)
6230 {
6231 return false;
6232 }
6233
6234 /**
6235 * @brief If the result of the expression is a constant,
6236 * create a TokenRValConst equivalent, set didOne, and return that.
6237 * Otherwise, just return the original without changing didOne.
6238 */
6239 public virtual TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6240 {
6241 return lookup (this, ref didOne);
6242 }
6243 }
6244
6245 /**
6246 * @brief a postfix operator and corresponding L-value
6247 */
6248 public class TokenRValAsnPost : TokenRVal {
6249 public TokenLVal lVal;
6250 public Token postfix;
6251
6252 public TokenRValAsnPost (Token original) : base (original) { }
6253
6254 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6255 {
6256 return lVal.GetRValType (scg, argsig);
6257 }
6258
6259 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6260 {
6261 return lVal.IsRValTrivial (scg, null);
6262 }
6263
6264 public override void DebString (StringBuilder sb)
6265 {
6266 lVal.DebString (sb);
6267 sb.Append (' ');
6268 postfix.DebString (sb);
6269 }
6270 }
6271
6272 /**
6273 * @brief a prefix operator and corresponding L-value
6274 */
6275 public class TokenRValAsnPre : TokenRVal {
6276 public Token prefix;
6277 public TokenLVal lVal;
6278
6279 public TokenRValAsnPre (Token original) : base (original) { }
6280
6281 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6282 {
6283 return lVal.GetRValType (scg, argsig);
6284 }
6285
6286 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6287 {
6288 return lVal.IsRValTrivial (scg, null);
6289 }
6290
6291 public override void DebString (StringBuilder sb)
6292 {
6293 prefix.DebString (sb);
6294 sb.Append (' ');
6295 lVal.DebString (sb);
6296 }
6297 }
6298
6299 /**
6300 * @brief calling a function or method, ie, may have side-effects
6301 */
6302 public class TokenRValCall : TokenRVal {
6303
6304 public TokenRVal meth; // points to the function to be called
6305 // - might be a reference to a global function (TokenLValName)
6306 // - or an instance method of a class (TokenLValIField)
6307 // - or a static method of a class (TokenLValSField)
6308 // - or a delegate stored in a variable (assumption for anything else)
6309 public TokenRVal args; // null-terminated TokenRVal list
6310 public int nArgs; // number of elements in args
6311
6312 public TokenRValCall (Token original) : base (original) { }
6313
6314 private TokenType[] myArgSig;
6315
6316 /**
6317 * @brief The type of a call is the type of the return value.
6318 */
6319 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6320 {
6321 /*
6322 * Build type signature so we select correct overloaded function.
6323 */
6324 if (myArgSig == null) {
6325 myArgSig = new TokenType[nArgs];
6326 int i = 0;
6327 for (Token t = args; t != null; t = t.nextToken) {
6328 myArgSig[i++] = ((TokenRVal)t).GetRValType (scg, null);
6329 }
6330 }
6331
6332 /*
6333 * Get the type of the method itself. This should get us a delegate type.
6334 */
6335 TokenType delType = meth.GetRValType (scg, myArgSig);
6336 if (!(delType is TokenTypeSDTypeDelegate)) {
6337 scg.ErrorMsg (meth, "must be function or method");
6338 return new TokenTypeVoid (meth);
6339 }
6340
6341 /*
6342 * Get the return type from the delegate type.
6343 */
6344 return ((TokenTypeSDTypeDelegate)delType).decl.GetRetType ();
6345 }
6346
6347 /**
6348 * @brief See if the call to the function/method is trivial.
6349 * It is trivial if all the argument computations are trivial and
6350 * the function is not being called via virtual table or delegate
6351 * and the function body is trivial.
6352 */
6353 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6354 {
6355 /*
6356 * Build type signature so we select correct overloaded function.
6357 */
6358 if (myArgSig == null) {
6359 myArgSig = new TokenType[nArgs];
6360 int i = 0;
6361 for (Token t = args; t != null; t = t.nextToken) {
6362 myArgSig[i++] = ((TokenRVal)t).GetRValType (scg, null);
6363 }
6364 }
6365
6366 /*
6367 * Make sure all arguments can be computed trivially.
6368 */
6369 for (Token t = args; t != null; t = t.nextToken) {
6370 if (!((TokenRVal)t).IsRValTrivial (scg, null)) return false;
6371 }
6372
6373 /*
6374 * See if the function call itself and the function body are trivial.
6375 */
6376 return meth.IsCallTrivial (scg, myArgSig);
6377 }
6378
6379 // debugging
6380 public override void DebString (StringBuilder sb)
6381 {
6382 meth.DebString (sb);
6383 sb.Append (" (");
6384 bool first = true;
6385 for (Token t = args; t != null; t = t.nextToken) {
6386 if (!first) sb.Append (", ");
6387 t.DebString (sb);
6388 first = false;
6389 }
6390 sb.Append (")");
6391 }
6392 }
6393
6394 /**
6395 * @brief encapsulates a typecast, ie, (type)
6396 */
6397 public class TokenRValCast : TokenRVal {
6398 public TokenType castTo;
6399 public TokenRVal rVal;
6400
6401 public TokenRValCast (TokenType type, TokenRVal value) : base (type)
6402 {
6403 castTo = type;
6404 rVal = value;
6405 }
6406
6407 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6408 {
6409 return castTo;
6410 }
6411
6412 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6413 {
6414 argsig = null;
6415 if (castTo is TokenTypeSDTypeDelegate) {
6416 argsig = ((TokenTypeSDTypeDelegate)castTo).decl.GetArgTypes ();
6417 }
6418 return rVal.IsRValTrivial (scg, argsig);
6419 }
6420
6421 /**
6422 * @brief If operand is constant, maybe we can say the whole thing is a constant.
6423 */
6424 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6425 {
6426 rVal = rVal.TryComputeConstant (lookup, ref didOne);
6427 if (rVal is TokenRValConst) {
6428 try {
6429 object val = ((TokenRValConst)rVal).val;
6430 object nval = null;
6431 if (castTo is TokenTypeChar) {
6432 if (val is char) return rVal;
6433 if (val is int) nval = (char)(int)val;
6434 }
6435 if (castTo is TokenTypeFloat) {
6436 if (val is double) return rVal;
6437 if (val is int) nval = (double)(int)val;
6438 if (val is string) nval = new LSL_Float ((string)val).value;
6439 }
6440 if (castTo is TokenTypeInt) {
6441 if (val is int) return rVal;
6442 if (val is char) nval = (int)(char)val;
6443 if (val is double) nval = (int)(double)val;
6444 if (val is string) nval = new LSL_Integer ((string)val).value;
6445 }
6446 if (castTo is TokenTypeRot) {
6447 if (val is LSL_Rotation) return rVal;
6448 if (val is string) nval = new LSL_Rotation ((string)val);
6449 }
6450 if ((castTo is TokenTypeKey) || (castTo is TokenTypeStr)) {
6451 if (val is string) nval = val; // in case of key/string conversion
6452 if (val is char) nval = TypeCast.CharToString ((char)val);
6453 if (val is double) nval = TypeCast.FloatToString ((double)val);
6454 if (val is int) nval = TypeCast.IntegerToString ((int)val);
6455 if (val is LSL_Rotation) nval = TypeCast.RotationToString ((LSL_Rotation)val);
6456 if (val is LSL_Vector) nval = TypeCast.VectorToString ((LSL_Vector)val);
6457 }
6458 if (castTo is TokenTypeVec) {
6459 if (val is LSL_Vector) return rVal;
6460 if (val is string) nval = new LSL_Vector ((string)val);
6461 }
6462 if (nval != null) {
6463 TokenRVal rValConst = new TokenRValConst (castTo, nval);
6464 didOne = true;
6465 return rValConst;
6466 }
6467 } catch {
6468 }
6469 }
6470 return this;
6471 }
6472
6473 public override void DebString (StringBuilder sb)
6474 {
6475 sb.Append ('(');
6476 castTo.DebString (sb);
6477 sb.Append (')');
6478 rVal.DebString (sb);
6479 }
6480 }
6481
6482 /**
6483 * @brief Encapsulate a conditional expression:
6484 * <condExpr> ? <trueExpr> : <falseExpr>
6485 */
6486 public class TokenRValCondExpr : TokenRVal {
6487 public TokenRVal condExpr;
6488 public TokenRVal trueExpr;
6489 public TokenRVal falseExpr;
6490
6491 public TokenRValCondExpr (Token original) : base (original)
6492 { }
6493
6494 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6495 {
6496 TokenType trueType = trueExpr.GetRValType (scg, argsig);
6497 TokenType falseType = falseExpr.GetRValType (scg, argsig);
6498 if (trueType.ToString () != falseType.ToString ()) {
6499 scg.ErrorMsg (condExpr, "true & false expr types don't match");
6500 }
6501 return trueType;
6502 }
6503
6504 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6505 {
6506 return condExpr.IsRValTrivial (scg, null) &&
6507 trueExpr.IsRValTrivial (scg, argsig) &&
6508 falseExpr.IsRValTrivial (scg, argsig);
6509 }
6510
6511 /**
6512 * @brief If condition is constant, then the whole expression is constant
6513 * iff the corresponding trueExpr or falseExpr is constant.
6514 */
6515 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6516 {
6517 TokenRVal rValCond = condExpr.TryComputeConstant (lookup, ref didOne);
6518 if (rValCond is TokenRValConst) {
6519 didOne = true;
6520 bool isTrue = ((TokenRValConst)rValCond).IsConstBoolTrue ();
6521 return (isTrue ? trueExpr : falseExpr).TryComputeConstant (lookup, ref didOne);
6522 }
6523 return this;
6524 }
6525
6526 // debugging
6527 public override void DebString (StringBuilder sb)
6528 {
6529 condExpr.DebString (sb);
6530 sb.Append (" ? ");
6531 trueExpr.DebString (sb);
6532 sb.Append (" : ");
6533 falseExpr.DebString (sb);
6534 }
6535 }
6536
6537 /**
6538 * @brief all constants supposed to end up here
6539 */
6540 public enum TokenRValConstType : byte { CHAR = 0, FLOAT = 1, INT = 2, KEY = 3, STRING = 4 };
6541 public class TokenRValConst : TokenRVal {
6542 public object val; // always a system type (char, int, double, string), never LSL-wrapped
6543 public TokenRValConstType type;
6544 public TokenType tokType;
6545
6546 public TokenRValConst (Token original, object value) : base (original)
6547 {
6548 val = value;
6549
6550 TokenType tt = null;
6551 if (val is char) {
6552 type = TokenRValConstType.CHAR;
6553 tt = new TokenTypeChar (this);
6554 } else if (val is int) {
6555 type = TokenRValConstType.INT;
6556 tt = new TokenTypeInt (this);
6557 } else if (val is double) {
6558 type = TokenRValConstType.FLOAT;
6559 tt = new TokenTypeFloat (this);
6560 } else if (val is string) {
6561 type = TokenRValConstType.STRING;
6562 tt = new TokenTypeStr (this);
6563 } else {
6564 throw new Exception ("invalid constant type " + val.GetType ());
6565 }
6566
6567 tokType = (original is TokenType) ? (TokenType)original : tt;
6568 if (tokType is TokenTypeKey) {
6569 type = TokenRValConstType.KEY;
6570 }
6571 }
6572
6573 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6574 {
6575 return tokType;
6576 }
6577
6578 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6579 {
6580 return true;
6581 }
6582
6583 public CompValu GetCompValu ()
6584 {
6585 switch (type) {
6586 case TokenRValConstType.CHAR: { return new CompValuChar (tokType, (char)val); }
6587 case TokenRValConstType.FLOAT: { return new CompValuFloat (tokType, (double)val); }
6588 case TokenRValConstType.INT: { return new CompValuInteger (tokType, (int)val); }
6589 case TokenRValConstType.KEY:
6590 case TokenRValConstType.STRING: { return new CompValuString (tokType, (string)val); }
6591 default: throw new Exception ("unknown type");
6592 }
6593 }
6594
6595 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6596 {
6597 // gotta end somewhere
6598 return this;
6599 }
6600
6601 public bool IsConstBoolTrue ()
6602 {
6603 switch (type) {
6604 case TokenRValConstType.CHAR: { return (char)val != 0; }
6605 case TokenRValConstType.FLOAT: { return (double)val != 0; }
6606 case TokenRValConstType.INT: { return (int)val != 0; }
6607 case TokenRValConstType.KEY: { return (string)val != "" && (string)val != ScriptBaseClass.NULL_KEY; }
6608 case TokenRValConstType.STRING: { return (string)val != ""; }
6609 default: throw new Exception ("unknown type");
6610 }
6611 }
6612
6613 public override void DebString (StringBuilder sb)
6614 {
6615 if (val is char) {
6616 sb.Append ('\'');
6617 EscapeQuotes (sb, new string (new char [] { (char)val }));
6618 sb.Append ('\'');
6619 } else if (val is int) {
6620 sb.Append ((int)val);
6621 } else if (val is double) {
6622 string str = ((double)val).ToString ();
6623 sb.Append (str);
6624 if ((str.IndexOf ('.') < 0) &&
6625 (str.IndexOf ('E') < 0) &&
6626 (str.IndexOf ('e') < 0)) {
6627 sb.Append (".0");
6628 }
6629 } else if (val is string) {
6630 sb.Append ('"');
6631 EscapeQuotes (sb, (string)val);
6632 sb.Append ('"');
6633 } else {
6634 throw new Exception ("invalid constant type " + val.GetType ());
6635 }
6636 }
6637 private static void EscapeQuotes (StringBuilder sb, string s)
6638 {
6639 foreach (char c in s) {
6640 switch (c) {
6641 case '\n': {
6642 sb.Append ("\\n");
6643 break;
6644 }
6645 case '\t': {
6646 sb.Append ("\\t");
6647 break;
6648 }
6649 case '\\': {
6650 sb.Append ("\\\\");
6651 break;
6652 }
6653 case '\'': {
6654 sb.Append ("\\'");
6655 break;
6656 }
6657 case '\"': {
6658 sb.Append ("\\\"");
6659 break;
6660 }
6661 default: {
6662 sb.Append (c);
6663 break;
6664 }
6665 }
6666 }
6667 }
6668 }
6669
6670 /**
6671 * @brief Default initialization value for the corresponding variable.
6672 */
6673 public class TokenRValInitDef : TokenRVal {
6674 public TokenType type;
6675
6676 public static TokenRValInitDef Construct (TokenDeclVar tokenDeclVar)
6677 {
6678 TokenRValInitDef zhis = new TokenRValInitDef (tokenDeclVar);
6679 zhis.type = tokenDeclVar.type;
6680 return zhis;
6681 }
6682 private TokenRValInitDef (Token original) : base (original) { }
6683
6684 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6685 {
6686 return type;
6687 }
6688
6689 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6690 {
6691 // it's always just a constant so it's always very trivial
6692 return true;
6693 }
6694
6695 public override void DebString (StringBuilder sb)
6696 {
6697 sb.Append ("<default ");
6698 sb.Append (type.ToString ());
6699 sb.Append ('>');
6700 }
6701 }
6702
6703 /**
6704 * @brief encapsulation of <rval> is <typeexp>
6705 */
6706 public class TokenRValIsType : TokenRVal {
6707 public TokenRVal rValExp;
6708 public TokenTypeExp typeExp;
6709
6710 public TokenRValIsType (Token original) : base (original) { }
6711
6712 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6713 {
6714 return new TokenTypeBool (rValExp);
6715 }
6716
6717 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6718 {
6719 return rValExp.IsRValTrivial (scg, argsig);
6720 }
6721 }
6722
6723 /**
6724 * @brief an R-value enclosed in brackets is an LSLList
6725 */
6726 public class TokenRValList : TokenRVal {
6727
6728 public TokenRVal rVal; // null-terminated list of TokenRVal objects
6729 public int nItems;
6730
6731 public TokenRValList (Token original) : base (original) { }
6732
6733 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6734 {
6735 return new TokenTypeList (rVal);
6736 }
6737
6738 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6739 {
6740 for (Token t = rVal; t != null; t = t.nextToken) {
6741 if (!((TokenRVal)t).IsRValTrivial (scg, null)) return false;
6742 }
6743 return true;
6744 }
6745
6746 public override void DebString (StringBuilder sb)
6747 {
6748 bool first = true;
6749 sb.Append ('[');
6750 for (Token t = rVal; t != null; t = t.nextToken) {
6751 if (!first) sb.Append (',');
6752 sb.Append (' ');
6753 t.DebString (sb);
6754 first = false;
6755 }
6756 sb.Append (" ]");
6757 }
6758 }
6759
6760 /**
6761 * @brief encapsulates '$new' arraytype '{' ... '}'
6762 */
6763 public class TokenRValNewArIni : TokenRVal {
6764 public TokenType arrayType;
6765 public TokenList valueList; // TokenList : a sub-list
6766 // TokenKwComma : a default value
6767 // TokenRVal : init expression
6768
6769 public TokenRValNewArIni (Token original) : base (original)
6770 {
6771 valueList = new TokenList (original);
6772 }
6773
6774 // type of the expression = the array type allocated by $new()
6775 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6776 {
6777 return arrayType;
6778 }
6779
6780 // The expression is trivial if all the initializers are trivial.
6781 // An array's constructor is always trivial (no CheckRun() calls).
6782 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6783 {
6784 return ListIsTrivial (scg, valueList);
6785 }
6786 private bool ListIsTrivial (ScriptCodeGen scg, TokenList valList)
6787 {
6788 foreach (Token val in valList.tl) {
6789 if (val is TokenRVal) {
6790 if (!((TokenRVal)val).IsRValTrivial (scg, null)) return false;
6791 }
6792 if (val is TokenList) {
6793 if (!ListIsTrivial (scg, (TokenList)val)) return false;
6794 }
6795 }
6796 return true;
6797 }
6798
6799 public override void DebString (StringBuilder sb)
6800 {
6801 sb.Append ("new ");
6802 arrayType.DebString (sb);
6803 sb.Append (' ');
6804 valueList.DebString (sb);
6805 }
6806 }
6807 public class TokenList : Token {
6808 public List<Token> tl = new List<Token> ();
6809 public TokenList (Token original) : base (original) { }
6810
6811 public override void DebString (StringBuilder sb)
6812 {
6813 sb.Append ('{');
6814 bool first = true;
6815 foreach (Token t in tl) {
6816 if (!first) sb.Append (", ");
6817 t.DebString (sb);
6818 first = false;
6819 }
6820 sb.Append ('}');
6821 }
6822 }
6823
6824 /**
6825 * @brief a binary operator and its two operands
6826 */
6827 public class TokenRValOpBin : TokenRVal {
6828 public TokenRVal rValLeft;
6829 public TokenKw opcode;
6830 public TokenRVal rValRight;
6831
6832 public TokenRValOpBin (TokenRVal left, TokenKw op, TokenRVal right) : base (op)
6833 {
6834 rValLeft = left;
6835 opcode = op;
6836 rValRight = right;
6837 }
6838
6839 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6840 {
6841 /*
6842 * Comparisons and the like always return bool.
6843 */
6844 string opstr = opcode.ToString ();
6845 if ((opstr == "==") || (opstr == "!=") || (opstr == ">=") || (opstr == ">") ||
6846 (opstr == "&&") || (opstr == "||") || (opstr == "<=") || (opstr == "<") ||
6847 (opstr == "&&&") || (opstr == "|||")) {
6848 return new TokenTypeBool (opcode);
6849 }
6850
6851 /*
6852 * Comma is always type of right-hand operand.
6853 */
6854 if (opstr == ",") return rValRight.GetRValType (scg, argsig);
6855
6856 /*
6857 * Assignments are always the type of the left-hand operand,
6858 * including stuff like "+=".
6859 */
6860 if (opstr.EndsWith ("=")) {
6861 return rValLeft.GetRValType (scg, argsig);
6862 }
6863
6864 /*
6865 * string+something or something+string is always string.
6866 * except list+something or something+list is always a list.
6867 */
6868 string lType = rValLeft.GetRValType (scg, argsig).ToString ();
6869 string rType = rValRight.GetRValType (scg, argsig).ToString ();
6870 if ((opstr == "+") && ((lType == "list") || (rType == "list"))) {
6871 return new TokenTypeList (opcode);
6872 }
6873 if ((opstr == "+") && ((lType == "key") || (lType == "string") ||
6874 (rType == "key") || (rType == "string"))) {
6875 return new TokenTypeStr (opcode);
6876 }
6877
6878 /*
6879 * Everything else depends on both operands.
6880 */
6881 string key = lType + opstr + rType;
6882 BinOpStr binOpStr;
6883 if (BinOpStr.defined.TryGetValue (key, out binOpStr)) {
6884 return TokenType.FromSysType (opcode, binOpStr.outtype);
6885 }
6886
6887 scg.ErrorMsg (opcode, "undefined operation " + key);
6888 return new TokenTypeVoid (opcode);
6889 }
6890
6891 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6892 {
6893 return rValLeft.IsRValTrivial (scg, null) && rValRight.IsRValTrivial (scg, null);
6894 }
6895
6896 /**
6897 * @brief If both operands are constants, maybe we can say the whole thing is a constant.
6898 */
6899 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6900 {
6901 rValLeft = rValLeft.TryComputeConstant (lookup, ref didOne);
6902 rValRight = rValRight.TryComputeConstant (lookup, ref didOne);
6903 if ((rValLeft is TokenRValConst) && (rValRight is TokenRValConst)) {
6904 try {
6905 object val = opcode.binOpConst (((TokenRValConst)rValLeft).val,
6906 ((TokenRValConst)rValRight).val);
6907 TokenRVal rValConst = new TokenRValConst (opcode, val);
6908 didOne = true;
6909 return rValConst;
6910 } catch {
6911 }
6912 }
6913 return this;
6914 }
6915
6916 // debugging
6917 public override void DebString (StringBuilder sb)
6918 {
6919 rValLeft.DebString (sb);
6920 sb.Append (' ');
6921 sb.Append (opcode.ToString ());
6922 sb.Append (' ');
6923 rValRight.DebString (sb);
6924 }
6925 }
6926
6927 /**
6928 * @brief an unary operator and its one operand
6929 */
6930 public class TokenRValOpUn : TokenRVal {
6931 public TokenKw opcode;
6932 public TokenRVal rVal;
6933
6934 public TokenRValOpUn (TokenKw op, TokenRVal right) : base (op)
6935 {
6936 opcode = op;
6937 rVal = right;
6938 }
6939
6940 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6941 {
6942 if (opcode is TokenKwExclam) return new TokenTypeInt (opcode);
6943 return rVal.GetRValType (scg, null);
6944 }
6945
6946 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6947 {
6948 return rVal.IsRValTrivial (scg, null);
6949 }
6950
6951 /**
6952 * @brief If operand is constant, maybe we can say the whole thing is a constant.
6953 */
6954 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
6955 {
6956 rVal = rVal.TryComputeConstant (lookup, ref didOne);
6957 if (rVal is TokenRValConst) {
6958 try {
6959 object val = opcode.unOpConst (((TokenRValConst)rVal).val);
6960 TokenRVal rValConst = new TokenRValConst (opcode, val);
6961 didOne = true;
6962 return rValConst;
6963 } catch {
6964 }
6965 }
6966 return this;
6967 }
6968
6969 /**
6970 * @brief Serialization/Deserialization.
6971 */
6972 public TokenRValOpUn (Token original) : base (original) { }
6973
6974 // debugging
6975 public override void DebString (StringBuilder sb)
6976 {
6977 sb.Append (opcode.ToString ());
6978 rVal.DebString (sb);
6979 }
6980 }
6981
6982 /**
6983 * @brief an R-value enclosed in parentheses
6984 */
6985 public class TokenRValParen : TokenRVal {
6986
6987 public TokenRVal rVal;
6988
6989 public TokenRValParen (Token original) : base (original) { }
6990
6991 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
6992 {
6993 // pass argsig through in this simple case, ie, let
6994 // them do something like (llOwnerSay)("blabla...");
6995 return rVal.GetRValType (scg, argsig);
6996 }
6997
6998 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
6999 {
7000 // pass argsig through in this simple case, ie, let
7001 // them do something like (llOwnerSay)("blabla...");
7002 return rVal.IsRValTrivial (scg, argsig);
7003 }
7004
7005 /**
7006 * @brief If operand is constant, we can say the whole thing is a constant.
7007 */
7008 public override TokenRVal TryComputeConstant (TCCLookup lookup, ref bool didOne)
7009 {
7010 rVal = rVal.TryComputeConstant (lookup, ref didOne);
7011 if (rVal is TokenRValConst) {
7012 didOne = true;
7013 return rVal;
7014 }
7015 return this;
7016 }
7017
7018 public override void DebString (StringBuilder sb)
7019 {
7020 sb.Append ('(');
7021 rVal.DebString (sb);
7022 sb.Append (')');
7023 }
7024 }
7025
7026 public class TokenRValRot : TokenRVal {
7027
7028 public TokenRVal xRVal;
7029 public TokenRVal yRVal;
7030 public TokenRVal zRVal;
7031 public TokenRVal wRVal;
7032
7033 public TokenRValRot (Token original) : base (original) { }
7034
7035 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
7036 {
7037 return new TokenTypeRot (xRVal);
7038 }
7039
7040 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
7041 {
7042 return xRVal.IsRValTrivial (scg, null) &&
7043 yRVal.IsRValTrivial (scg, null) &&
7044 zRVal.IsRValTrivial (scg, null) &&
7045 wRVal.IsRValTrivial (scg, null);
7046 }
7047
7048 public override void DebString (StringBuilder sb)
7049 {
7050 sb.Append ('<');
7051 xRVal.DebString (sb);
7052 sb.Append (',');
7053 yRVal.DebString (sb);
7054 sb.Append (',');
7055 zRVal.DebString (sb);
7056 sb.Append (',');
7057 wRVal.DebString (sb);
7058 sb.Append ('>');
7059 }
7060 }
7061
7062 /**
7063 * @brief 'this' is being used as an rval inside an instance method.
7064 */
7065 public class TokenRValThis : TokenRVal {
7066 public Token original;
7067 public TokenDeclSDTypeClass sdtClass;
7068
7069 public TokenRValThis (Token original, TokenDeclSDTypeClass sdtClass) : base (original)
7070 {
7071 this.original = original;
7072 this.sdtClass = sdtClass;
7073 }
7074
7075 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
7076 {
7077 return sdtClass.MakeRefToken (original);
7078 }
7079
7080 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
7081 {
7082 return true; // ldarg.0/starg.0 can't possibly loop
7083 }
7084
7085 // debugging
7086 public override void DebString (StringBuilder sb)
7087 {
7088 sb.Append ("this");
7089 }
7090 }
7091
7092 /**
7093 * @brief the 'undef' keyword is being used as a value in an expression.
7094 * It is the null object pointer and has type TokenTypeUndef.
7095 */
7096 public class TokenRValUndef : TokenRVal {
7097 Token original;
7098
7099 public TokenRValUndef (Token original) : base (original)
7100 {
7101 this.original = original;
7102 }
7103
7104 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
7105 {
7106 return new TokenTypeUndef (original);
7107 }
7108
7109 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
7110 {
7111 return true;
7112 }
7113
7114 public override void DebString (StringBuilder sb)
7115 {
7116 sb.Append ("undef");
7117 }
7118 }
7119
7120 /**
7121 * @brief put 3 RVals together as a Vector value.
7122 */
7123 public class TokenRValVec : TokenRVal {
7124
7125 public TokenRVal xRVal;
7126 public TokenRVal yRVal;
7127 public TokenRVal zRVal;
7128
7129 public TokenRValVec (Token original) : base (original) { }
7130
7131 public override TokenType GetRValType (ScriptCodeGen scg, TokenType[] argsig)
7132 {
7133 return new TokenTypeVec (xRVal);
7134 }
7135
7136 public override bool IsRValTrivial (ScriptCodeGen scg, TokenType[] argsig)
7137 {
7138 return xRVal.IsRValTrivial (scg, null) &&
7139 yRVal.IsRValTrivial (scg, null) &&
7140 zRVal.IsRValTrivial (scg, null);
7141 }
7142
7143 public override void DebString (StringBuilder sb)
7144 {
7145 sb.Append ('<');
7146 xRVal.DebString (sb);
7147 sb.Append (',');
7148 yRVal.DebString (sb);
7149 sb.Append (',');
7150 zRVal.DebString (sb);
7151 sb.Append ('>');
7152 }
7153 }
7154
7155 /**
7156 * @brief encapsulates the whole script in a single token
7157 */
7158 public class TokenScript : Token {
7159 public int expiryDays = Int32.MaxValue;
7160 public TokenDeclState defaultState;
7161 public Dictionary<string, TokenDeclState> states = new Dictionary<string, TokenDeclState> ();
7162 public VarDict variablesStack = new VarDict (false); // initial one is used for global functions and variables
7163 public TokenDeclVar globalVarInit; // $globalvarinit function
7164 // - performs explicit global var and static field inits
7165
7166 private Dictionary<string, TokenDeclSDType> sdSrcTypes = new Dictionary<string, TokenDeclSDType> ();
7167 private bool sdSrcTypesSealed = false;
7168
7169 public TokenScript (Token original) : base (original) { }
7170
7171 /*
7172 * Handle variable definition stack.
7173 * Generally a '{' pushes a new frame and a '}' pops the frame.
7174 * Function parameters are pushed in an additional frame (just outside the body's { ... } block)
7175 */
7176 public void PushVarFrame (bool locals)
7177 {
7178 PushVarFrame (new VarDict (locals));
7179 }
7180 public void PushVarFrame (VarDict newFrame)
7181 {
7182 newFrame.outerVarDict = variablesStack;
7183 variablesStack = newFrame;
7184 }
7185 public void PopVarFrame ()
7186 {
7187 variablesStack = variablesStack.outerVarDict;
7188 }
7189 public bool AddVarEntry (TokenDeclVar var)
7190 {
7191 return variablesStack.AddEntry (var);
7192 }
7193
7194 /*
7195 * Handle list of script-defined types.
7196 */
7197 public void sdSrcTypesSeal ()
7198 {
7199 sdSrcTypesSealed = true;
7200 }
7201 public bool sdSrcTypesContainsKey (string key)
7202 {
7203 return sdSrcTypes.ContainsKey (key);
7204 }
7205 public bool sdSrcTypesTryGetValue (string key, out TokenDeclSDType value)
7206 {
7207 return sdSrcTypes.TryGetValue (key, out value);
7208 }
7209 public void sdSrcTypesAdd (string key, TokenDeclSDType value)
7210 {
7211 if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed");
7212 value.sdTypeIndex = sdSrcTypes.Count;
7213 sdSrcTypes.Add (key, value);
7214 }
7215 public void sdSrcTypesRep (string key, TokenDeclSDType value)
7216 {
7217 if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed");
7218 value.sdTypeIndex = sdSrcTypes[key].sdTypeIndex;
7219 sdSrcTypes[key] = value;
7220 }
7221 public void sdSrcTypesReplace (string key, TokenDeclSDType value)
7222 {
7223 if (sdSrcTypesSealed) throw new Exception ("sdSrcTypes is sealed");
7224 sdSrcTypes[key] = value;
7225 }
7226 public Dictionary<string, TokenDeclSDType>.ValueCollection sdSrcTypesValues
7227 {
7228 get {
7229 return sdSrcTypes.Values;
7230 }
7231 }
7232 public int sdSrcTypesCount
7233 {
7234 get {
7235 return sdSrcTypes.Count;
7236 }
7237 }
7238
7239 /**
7240 * @brief Debug output.
7241 */
7242 public override void DebString (StringBuilder sb)
7243 {
7244 /*
7245 * Script-defined types.
7246 */
7247 foreach (TokenDeclSDType srcType in sdSrcTypes.Values) {
7248 srcType.DebString (sb);
7249 }
7250
7251 /*
7252 * Global constants.
7253 * Variables are handled by outputting the $globalvarinit function.
7254 */
7255 foreach (TokenDeclVar var in variablesStack) {
7256 if (var.constant) {
7257 var.DebString (sb);
7258 }
7259 }
7260
7261 /*
7262 * Global functions.
7263 */
7264 foreach (TokenDeclVar var in variablesStack) {
7265 if (var == globalVarInit) {
7266 var.DebStringInitFields (sb);
7267 } else if (var.retType != null) {
7268 var.DebString (sb);
7269 }
7270 }
7271
7272 /*
7273 * States and their event handler functions.
7274 */
7275 defaultState.DebString (sb);
7276 foreach (TokenDeclState st in states.Values) {
7277 st.DebString (sb);
7278 }
7279 }
7280 }
7281
7282 /**
7283 * @brief state body declaration
7284 */
7285 public class TokenStateBody : Token {
7286
7287 public TokenDeclVar eventFuncs;
7288
7289 public int index = -1; // (codegen) row in ScriptHandlerEventTable (0=default)
7290
7291 public TokenStateBody (Token original) : base (original) { }
7292
7293 public override void DebString (StringBuilder sb)
7294 {
7295 sb.Append (" { ");
7296 for (Token t = eventFuncs; t != null; t = t.nextToken) {
7297 t.DebString (sb);
7298 }
7299 sb.Append (" } ");
7300 }
7301 }
7302
7303 /**
7304 * @brief a single statement, such as ending on a semicolon or enclosed in braces
7305 * TokenStmt includes the terminating semicolon or the enclosing braces
7306 * Also includes @label; for jump targets.
7307 * Also includes stray ; null statements.
7308 * Also includes local variable declarations with or without initialization value.
7309 */
7310 public class TokenStmt : Token {
7311 public TokenStmt (Token original) : base (original) { }
7312 }
7313
7314 /**
7315 * @brief a group of statements enclosed in braces
7316 */
7317 public class TokenStmtBlock : TokenStmt {
7318
7319 public Token statements; // null-terminated list of statements, can also have TokenDeclVar's in here
7320 public TokenStmtBlock outerStmtBlock; // next outer stmtBlock or null if top-level, ie, function definition
7321 public TokenDeclVar function; // function it is part of
7322 public bool isTry; // true iff it's a try statement block
7323 public bool isCatch; // true iff it's a catch statement block
7324 public bool isFinally; // true iff it's a finally statement block
7325 public TokenStmtTry tryStmt; // set iff isTry|isCatch|isFinally is set
7326
7327 public TokenStmtBlock (Token original) : base (original) { }
7328
7329 // debugging
7330 public override void DebString (StringBuilder sb)
7331 {
7332 sb.Append ("{ ");
7333 for (Token stmt = statements; stmt != null; stmt = stmt.nextToken) {
7334 stmt.DebString (sb);
7335 }
7336 sb.Append ("} ");
7337 }
7338 }
7339
7340 /**
7341 * @brief definition of branch target name
7342 */
7343 public class TokenStmtLabel : TokenStmt {
7344
7345 public TokenName name; // the label's name
7346 public TokenStmtBlock block; // which block it is defined in
7347 public bool hasBkwdRefs = false;
7348
7349 public bool labelTagged; // code gen: location of label
7350 public ScriptMyLabel labelStruct;
7351
7352 public TokenStmtLabel (Token original) : base (original) { }
7353
7354 public override void DebString (StringBuilder sb)
7355 {
7356 sb.Append ('@');
7357 name.DebString (sb);
7358 sb.Append (';');
7359 }
7360 }
7361
7362 /**
7363 * @brief those types of RVals with a semi-colon on the end
7364 * that are allowed to stand alone as statements
7365 */
7366 public class TokenStmtRVal : TokenStmt {
7367 public TokenRVal rVal;
7368
7369 public TokenStmtRVal (Token original) : base (original) { }
7370
7371 // debugging
7372 public override void DebString (StringBuilder sb)
7373 {
7374 rVal.DebString (sb);
7375 sb.Append ("; ");
7376 }
7377 }
7378
7379 public class TokenStmtBreak : TokenStmt {
7380 public TokenStmtBreak (Token original) : base (original) { }
7381
7382 public override void DebString (StringBuilder sb)
7383 {
7384 sb.Append ("break;");
7385 }
7386 }
7387
7388 public class TokenStmtCont : TokenStmt {
7389 public TokenStmtCont (Token original) : base (original) { }
7390
7391 public override void DebString (StringBuilder sb)
7392 {
7393 sb.Append ("continue;");
7394 }
7395 }
7396
7397 /**
7398 * @brief "do" statement
7399 */
7400 public class TokenStmtDo : TokenStmt {
7401
7402 public TokenStmt bodyStmt;
7403 public TokenRValParen testRVal;
7404
7405 public TokenStmtDo (Token original) : base (original) { }
7406
7407 public override void DebString (StringBuilder sb)
7408 {
7409 sb.Append ("do ");
7410 bodyStmt.DebString (sb);
7411 sb.Append (" while ");
7412 testRVal.DebString (sb);
7413 sb.Append (';');
7414 }
7415 }
7416
7417 /**
7418 * @brief "for" statement
7419 */
7420 public class TokenStmtFor : TokenStmt {
7421
7422 public TokenStmt initStmt; // there is always an init statement, though it may be a null statement
7423 public TokenRVal testRVal; // there may or may not be a test (null if not)
7424 public TokenRVal incrRVal; // there may or may not be an increment (null if not)
7425 public TokenStmt bodyStmt; // there is always a body statement, though it may be a null statement
7426
7427 public TokenStmtFor (Token original) : base (original) { }
7428
7429 public override void DebString (StringBuilder sb)
7430 {
7431 sb.Append ("for (");
7432 if (initStmt != null) initStmt.DebString (sb);
7433 else sb.Append (';');
7434 if (testRVal != null) testRVal.DebString (sb);
7435 sb.Append (';');
7436 if (incrRVal != null) incrRVal.DebString (sb);
7437 sb.Append (") ");
7438 bodyStmt.DebString (sb);
7439 }
7440 }
7441
7442 /**
7443 * @brief "foreach" statement
7444 */
7445 public class TokenStmtForEach : TokenStmt {
7446
7447 public TokenLVal keyLVal;
7448 public TokenLVal valLVal;
7449 public TokenRVal arrayRVal;
7450 public TokenStmt bodyStmt; // there is always a body statement, though it may be a null statement
7451
7452 public TokenStmtForEach (Token original) : base (original) { }
7453
7454 public override void DebString (StringBuilder sb)
7455 {
7456 sb.Append ("foreach (");
7457 if (keyLVal != null) keyLVal.DebString (sb);
7458 sb.Append (',');
7459 if (valLVal != null) valLVal.DebString (sb);
7460 sb.Append (" in ");
7461 arrayRVal.DebString (sb);
7462 sb.Append (')');
7463 bodyStmt.DebString (sb);
7464 }
7465 }
7466
7467 public class TokenStmtIf : TokenStmt {
7468
7469 public TokenRValParen testRVal;
7470 public TokenStmt trueStmt;
7471 public TokenStmt elseStmt;
7472
7473 public TokenStmtIf (Token original) : base (original) { }
7474
7475 public override void DebString (StringBuilder sb)
7476 {
7477 sb.Append ("if ");
7478 testRVal.DebString (sb);
7479 sb.Append (" ");
7480 trueStmt.DebString (sb);
7481 if (elseStmt != null) {
7482 sb.Append (" else ");
7483 elseStmt.DebString (sb);
7484 }
7485 }
7486 }
7487
7488 public class TokenStmtJump : TokenStmt {
7489
7490 public TokenName label;
7491
7492 public TokenStmtJump (Token original) : base (original) { }
7493
7494 public override void DebString (StringBuilder sb)
7495 {
7496 sb.Append ("jump ");
7497 label.DebString (sb);
7498 sb.Append (';');
7499 }
7500 }
7501
7502 public class TokenStmtNull : TokenStmt {
7503
7504 public TokenStmtNull (Token original) : base (original) { }
7505
7506 public override void DebString (StringBuilder sb)
7507 {
7508 sb.Append (';');
7509 }
7510 }
7511
7512 public class TokenStmtRet : TokenStmt {
7513
7514 public TokenRVal rVal; // null if void
7515
7516 public TokenStmtRet (Token original) : base (original) { }
7517
7518 public override void DebString (StringBuilder sb)
7519 {
7520 sb.Append ("return");
7521 if (rVal != null) {
7522 sb.Append (' ');
7523 rVal.DebString (sb);
7524 }
7525 sb.Append (';');
7526 }
7527 }
7528
7529 /**
7530 * @brief statement that changes the current state.
7531 */
7532 public class TokenStmtState : TokenStmt {
7533
7534 public TokenName state; // null for default
7535
7536 public TokenStmtState (Token original) : base (original) { }
7537
7538 public override void DebString (StringBuilder sb)
7539 {
7540 sb.Append ("state ");
7541 sb.Append ((state == null) ? "default" : state.val);
7542 sb.Append (';');
7543 }
7544 }
7545
7546 /**
7547 * @brief Encapsulates a whole switch statement including the body and all cases.
7548 */
7549 public class TokenStmtSwitch : TokenStmt {
7550
7551 public TokenRValParen testRVal; // the integer index expression
7552 public TokenSwitchCase cases = null; // list of all cases, linked by .nextCase
7553 public TokenSwitchCase lastCase = null; // used during reduce to point to last in 'cases' list
7554
7555 public TokenStmtSwitch (Token original) : base (original) { }
7556
7557 public override void DebString (StringBuilder sb)
7558 {
7559 sb.Append ("switch ");
7560 testRVal.DebString (sb);
7561 sb.Append ('{');
7562 for (TokenSwitchCase kase = cases; kase != null; kase = kase.nextCase) {
7563 kase.DebString (sb);
7564 }
7565 sb.Append ('}');
7566 }
7567 }
7568
7569 /**
7570 * @brief Encapsulates a case/default clause from a switch statement including the
7571 * two values and the corresponding body statements.
7572 */
7573 public class TokenSwitchCase : Token {
7574 public TokenSwitchCase nextCase; // next case in source-code order
7575 public TokenRVal rVal1; // null means 'default', else 'case'
7576 public TokenRVal rVal2; // null means 'case expr:', else 'case expr ... expr:'
7577 public TokenStmt stmts; // statements associated with the case
7578 public TokenStmt lastStmt; // used during reduce for building statement list
7579
7580 public int val1; // codegen: value of rVal1 here
7581 public int val2; // codegen: value of rVal2 here
7582 public ScriptMyLabel label; // codegen: target label here
7583 public TokenSwitchCase nextSortedCase; // codegen: next case in ascending val order
7584
7585 public string str1;
7586 public string str2;
7587 public TokenSwitchCase lowerCase;
7588 public TokenSwitchCase higherCase;
7589
7590 public TokenSwitchCase (Token original) : base (original) { }
7591
7592 public override void DebString (StringBuilder sb)
7593 {
7594 if (rVal1 == null) {
7595 sb.Append ("default: ");
7596 } else {
7597 sb.Append ("case ");
7598 rVal1.DebString (sb);
7599 if (rVal2 != null) {
7600 sb.Append (" ... ");
7601 rVal2.DebString (sb);
7602 }
7603 sb.Append (": ");
7604 }
7605 for (Token t = stmts; t != null; t = t.nextToken) {
7606 t.DebString (sb);
7607 }
7608 }
7609 }
7610
7611 public class TokenStmtThrow : TokenStmt {
7612
7613 public TokenRVal rVal; // null if rethrow style
7614
7615 public TokenStmtThrow (Token original) : base (original) { }
7616
7617 public override void DebString (StringBuilder sb)
7618 {
7619 sb.Append ("throw ");
7620 rVal.DebString (sb);
7621 sb.Append (';');
7622 }
7623 }
7624
7625 /**
7626 * @brief Encapsulates related try, catch and finally statements.
7627 */
7628 public class TokenStmtTry : TokenStmt {
7629
7630 public TokenStmtBlock tryStmt;
7631 public TokenDeclVar catchVar; // null iff catchStmt is null
7632 public TokenStmtBlock catchStmt; // can be null
7633 public TokenStmtBlock finallyStmt; // can be null
7634 public Dictionary<string, IntermediateLeave> iLeaves = new Dictionary<string, IntermediateLeave> ();
7635
7636 public TokenStmtTry (Token original) : base (original) { }
7637
7638 public override void DebString (StringBuilder sb)
7639 {
7640 sb.Append ("try ");
7641 tryStmt.DebString (sb);
7642 if (catchStmt != null) {
7643 sb.Append ("catch (");
7644 sb.Append (catchVar.type.ToString ());
7645 sb.Append (' ');
7646 sb.Append (catchVar.name.val);
7647 sb.Append (") ");
7648 catchStmt.DebString (sb);
7649 }
7650 if (finallyStmt != null) {
7651 sb.Append ("finally ");
7652 finallyStmt.DebString (sb);
7653 }
7654 }
7655 }
7656
7657 public class IntermediateLeave {
7658 public ScriptMyLabel jumpIntoLabel;
7659 public ScriptMyLabel jumpAwayLabel;
7660 }
7661
7662 public class TokenStmtVarIniDef : TokenStmt {
7663 public TokenLVal var;
7664 public TokenStmtVarIniDef (Token original) : base (original) { }
7665 }
7666
7667 public class TokenStmtWhile : TokenStmt {
7668
7669 public TokenRValParen testRVal;
7670 public TokenStmt bodyStmt;
7671
7672 public TokenStmtWhile (Token original) : base (original) { }
7673
7674 public override void DebString (StringBuilder sb)
7675 {
7676 sb.Append ("while ");
7677 testRVal.DebString (sb);
7678 sb.Append (' ');
7679 bodyStmt.DebString (sb);
7680 }
7681 }
7682
7683 /**
7684 * @brief type expressions (right-hand of 'is' keyword).
7685 */
7686 public class TokenTypeExp : Token {
7687 public TokenTypeExp (Token original) : base (original) { }
7688 }
7689
7690 public class TokenTypeExpBinOp : TokenTypeExp {
7691 public TokenTypeExp leftOp;
7692 public Token binOp;
7693 public TokenTypeExp rightOp;
7694
7695 public TokenTypeExpBinOp (Token original) : base (original) { }
7696 }
7697
7698 public class TokenTypeExpNot : TokenTypeExp {
7699 public TokenTypeExp typeExp;
7700
7701 public TokenTypeExpNot (Token original) : base (original) { }
7702 }
7703
7704 public class TokenTypeExpPar : TokenTypeExp {
7705 public TokenTypeExp typeExp;
7706
7707 public TokenTypeExpPar (Token original) : base (original) { }
7708 }
7709
7710 public class TokenTypeExpType : TokenTypeExp {
7711 public TokenType typeToken;
7712
7713 public TokenTypeExpType (Token original) : base (original) { }
7714 }
7715
7716 public class TokenTypeExpUndef : TokenTypeExp {
7717 public TokenTypeExpUndef (Token original) : base (original) { }
7718 }
7719}