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