opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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 using System;
29 using System.IO;
30 using System.Collections.Generic;
31 using System.Reflection;
32 using log4net;
33 using Tools;
34 using OpenSim.Region.Framework.Interfaces;
35  
36 namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
37 {
38 public class CSCodeGenerator : ICodeConverter
39 {
40 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
41  
42 private SYMBOL m_astRoot = null;
43 private Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> m_positionMap;
44 private int m_indentWidth = 4; // for indentation
45 private int m_braceCount; // for indentation
46 private int m_CSharpLine; // the current line of generated C# code
47 private int m_CSharpCol; // the current column of generated C# code
48 private List<string> m_warnings = new List<string>();
49 private IScriptModuleComms m_comms = null;
50  
51 private bool m_insertCoopTerminationChecks;
52 private static string m_coopTerminationCheck = "opensim_reserved_CheckForCoopTermination();";
53  
54 /// <summary>
55 /// Keep a record of the previous node when we do the parsing.
56 /// </summary>
57 /// <remarks>
58 /// We do this here because the parser generated by CSTools does not retain a reference to its parent node.
59 /// The previous node is required so we can correctly insert co-op termination checks when required.
60 /// </remarks>
61 // private SYMBOL m_previousNode;
62  
63 /// <summary>
64 /// Creates an 'empty' CSCodeGenerator instance.
65 /// </summary>
66 public CSCodeGenerator()
67 {
68 m_comms = null;
69 ResetCounters();
70 }
71  
72 public CSCodeGenerator(IScriptModuleComms comms, bool insertCoopTerminationChecks)
73 {
74 m_comms = comms;
75 m_insertCoopTerminationChecks = insertCoopTerminationChecks;
76 ResetCounters();
77 }
78  
79 /// <summary>
80 /// Get the mapping between LSL and C# line/column number.
81 /// </summary>
82 /// <returns>Dictionary\<KeyValuePair\<int, int\>, KeyValuePair\<int, int\>\>.</returns>
83 public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> PositionMap
84 {
85 get { return m_positionMap; }
86 }
87  
88 /// <summary>
89 /// Get the mapping between LSL and C# line/column number.
90 /// </summary>
91 /// <returns>SYMBOL pointing to root of the abstract syntax tree.</returns>
92 public SYMBOL ASTRoot
93 {
94 get { return m_astRoot; }
95 }
96  
97 /// <summary>
98 /// Resets various counters and metadata.
99 /// </summary>
100 private void ResetCounters()
101 {
102 m_braceCount = 0;
103 m_CSharpLine = 0;
104 m_CSharpCol = 1;
105 m_positionMap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
106 m_astRoot = null;
107 }
108  
109 /// <summary>
110 /// Generate the code from the AST we have.
111 /// </summary>
112 /// <param name="script">The LSL source as a string.</param>
113 /// <returns>String containing the generated C# code.</returns>
114 public string Convert(string script)
115 {
116 // m_log.DebugFormat("[CS CODE GENERATOR]: Converting to C#\n{0}", script);
117  
118 m_warnings.Clear();
119 ResetCounters();
120 Parser p = new LSLSyntax(new yyLSLSyntax(), new ErrorHandler(true));
121  
122 LSL2CSCodeTransformer codeTransformer;
123 try
124 {
125 codeTransformer = new LSL2CSCodeTransformer(p.Parse(script));
126 }
127 catch (CSToolsException e)
128 {
129 string message;
130  
131 // LL start numbering lines at 0 - geeks!
132 // Also need to subtract one line we prepend!
133 //
134 string emessage = e.Message;
135 string slinfo = e.slInfo.ToString();
136  
137 // Remove wrong line number info
138 //
139 if (emessage.StartsWith(slinfo+": "))
140 emessage = emessage.Substring(slinfo.Length+2);
141  
142 message = String.Format("({0},{1}) {2}",
143 e.slInfo.lineNumber - 1,
144 e.slInfo.charPosition - 1, emessage);
145  
146 throw new Exception(message);
147 }
148  
149 m_astRoot = codeTransformer.Transform();
150  
151 string retstr = String.Empty;
152  
153 // standard preamble
154 //retstr = GenerateLine("using OpenSim.Region.ScriptEngine.Common;");
155 //retstr += GenerateLine("using System.Collections.Generic;");
156 //retstr += GenerateLine("");
157 //retstr += GenerateLine("namespace SecondLife");
158 //retstr += GenerateLine("{");
159 m_braceCount++;
160 //retstr += GenerateIndentedLine("public class Script : OpenSim.Region.ScriptEngine.Common");
161 //retstr += GenerateIndentedLine("{");
162 m_braceCount++;
163  
164 // line number
165 m_CSharpLine += 3;
166  
167 // here's the payload
168 retstr += GenerateLine();
169 foreach (SYMBOL s in m_astRoot.kids)
170 retstr += GenerateNode(m_astRoot, s);
171  
172 // close braces!
173 m_braceCount--;
174 //retstr += GenerateIndentedLine("}");
175 m_braceCount--;
176 //retstr += GenerateLine("}");
177  
178 // Removes all carriage return characters which may be generated in Windows platform. Is there
179 // cleaner way of doing this?
180 retstr = retstr.Replace("\r", "");
181  
182 return retstr;
183 }
184  
185 /// <summary>
186 /// Get the set of warnings generated during compilation.
187 /// </summary>
188 /// <returns></returns>
189 public string[] GetWarnings()
190 {
191 return m_warnings.ToArray();
192 }
193  
194 private void AddWarning(string warning)
195 {
196 if (!m_warnings.Contains(warning))
197 {
198 m_warnings.Add(warning);
199 }
200 }
201  
202 /// <summary>
203 /// Recursively called to generate each type of node. Will generate this
204 /// node, then all it's children.
205 /// </summary>
206 /// <param name="previousSymbol">The parent node.</param>
207 /// <param name="s">The current node to generate code for.</param>
208 /// <returns>String containing C# code for SYMBOL s.</returns>
209 private string GenerateNode(SYMBOL previousSymbol, SYMBOL s)
210 {
211 string retstr = String.Empty;
212  
213 // make sure to put type lower in the inheritance hierarchy first
214 // ie: since IdentArgument and ExpressionArgument inherit from
215 // Argument, put IdentArgument and ExpressionArgument before Argument
216 if (s is GlobalFunctionDefinition)
217 retstr += GenerateGlobalFunctionDefinition((GlobalFunctionDefinition) s);
218 else if (s is GlobalVariableDeclaration)
219 retstr += GenerateGlobalVariableDeclaration((GlobalVariableDeclaration) s);
220 else if (s is State)
221 retstr += GenerateState((State) s);
222 else if (s is CompoundStatement)
223 retstr += GenerateCompoundStatement(previousSymbol, (CompoundStatement) s);
224 else if (s is Declaration)
225 retstr += GenerateDeclaration((Declaration) s);
226 else if (s is Statement)
227 retstr += GenerateStatement(previousSymbol, (Statement) s);
228 else if (s is ReturnStatement)
229 retstr += GenerateReturnStatement((ReturnStatement) s);
230 else if (s is JumpLabel)
231 retstr += GenerateJumpLabel((JumpLabel) s);
232 else if (s is JumpStatement)
233 retstr += GenerateJumpStatement((JumpStatement) s);
234 else if (s is StateChange)
235 retstr += GenerateStateChange((StateChange) s);
236 else if (s is IfStatement)
237 retstr += GenerateIfStatement((IfStatement) s);
238 else if (s is WhileStatement)
239 retstr += GenerateWhileStatement((WhileStatement) s);
240 else if (s is DoWhileStatement)
241 retstr += GenerateDoWhileStatement((DoWhileStatement) s);
242 else if (s is ForLoop)
243 retstr += GenerateForLoop((ForLoop) s);
244 else if (s is ArgumentList)
245 retstr += GenerateArgumentList((ArgumentList) s);
246 else if (s is Assignment)
247 retstr += GenerateAssignment((Assignment) s);
248 else if (s is BinaryExpression)
249 retstr += GenerateBinaryExpression((BinaryExpression) s);
250 else if (s is ParenthesisExpression)
251 retstr += GenerateParenthesisExpression((ParenthesisExpression) s);
252 else if (s is UnaryExpression)
253 retstr += GenerateUnaryExpression((UnaryExpression) s);
254 else if (s is IncrementDecrementExpression)
255 retstr += GenerateIncrementDecrementExpression((IncrementDecrementExpression) s);
256 else if (s is TypecastExpression)
257 retstr += GenerateTypecastExpression((TypecastExpression) s);
258 else if (s is FunctionCall)
259 retstr += GenerateFunctionCall((FunctionCall) s);
260 else if (s is VectorConstant)
261 retstr += GenerateVectorConstant((VectorConstant) s);
262 else if (s is RotationConstant)
263 retstr += GenerateRotationConstant((RotationConstant) s);
264 else if (s is ListConstant)
265 retstr += GenerateListConstant((ListConstant) s);
266 else if (s is Constant)
267 retstr += GenerateConstant((Constant) s);
268 else if (s is IdentDotExpression)
269 retstr += Generate(CheckName(((IdentDotExpression) s).Name) + "." + ((IdentDotExpression) s).Member, s);
270 else if (s is IdentExpression)
271 retstr += GenerateIdentifier(((IdentExpression) s).Name, s);
272 else if (s is IDENT)
273 retstr += Generate(CheckName(((TOKEN) s).yytext), s);
274 else
275 {
276 foreach (SYMBOL kid in s.kids)
277 retstr += GenerateNode(s, kid);
278 }
279  
280 return retstr;
281 }
282  
283 /// <summary>
284 /// Generates the code for a GlobalFunctionDefinition node.
285 /// </summary>
286 /// <param name="gf">The GlobalFunctionDefinition node.</param>
287 /// <returns>String containing C# code for GlobalFunctionDefinition gf.</returns>
288 private string GenerateGlobalFunctionDefinition(GlobalFunctionDefinition gf)
289 {
290 string retstr = String.Empty;
291  
292 // we need to separate the argument declaration list from other kids
293 List<SYMBOL> argumentDeclarationListKids = new List<SYMBOL>();
294 List<SYMBOL> remainingKids = new List<SYMBOL>();
295  
296 foreach (SYMBOL kid in gf.kids)
297 if (kid is ArgumentDeclarationList)
298 argumentDeclarationListKids.Add(kid);
299 else
300 remainingKids.Add(kid);
301  
302 retstr += GenerateIndented(String.Format("{0} {1}(", gf.ReturnType, CheckName(gf.Name)), gf);
303  
304 // print the state arguments, if any
305 foreach (SYMBOL kid in argumentDeclarationListKids)
306 retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid);
307  
308 retstr += GenerateLine(")");
309  
310 foreach (SYMBOL kid in remainingKids)
311 retstr += GenerateNode(gf, kid);
312  
313 return retstr;
314 }
315  
316 /// <summary>
317 /// Generates the code for a GlobalVariableDeclaration node.
318 /// </summary>
319 /// <param name="gv">The GlobalVariableDeclaration node.</param>
320 /// <returns>String containing C# code for GlobalVariableDeclaration gv.</returns>
321 private string GenerateGlobalVariableDeclaration(GlobalVariableDeclaration gv)
322 {
323 string retstr = String.Empty;
324  
325 foreach (SYMBOL s in gv.kids)
326 {
327 retstr += Indent();
328 retstr += GenerateNode(gv, s);
329 retstr += GenerateLine(";");
330 }
331  
332 return retstr;
333 }
334  
335 /// <summary>
336 /// Generates the code for a State node.
337 /// </summary>
338 /// <param name="s">The State node.</param>
339 /// <returns>String containing C# code for State s.</returns>
340 private string GenerateState(State s)
341 {
342 string retstr = String.Empty;
343  
344 foreach (SYMBOL kid in s.kids)
345 if (kid is StateEvent)
346 retstr += GenerateStateEvent((StateEvent) kid, s.Name);
347  
348 return retstr;
349 }
350  
351 /// <summary>
352 /// Generates the code for a StateEvent node.
353 /// </summary>
354 /// <param name="se">The StateEvent node.</param>
355 /// <param name="parentStateName">The name of the parent state.</param>
356 /// <returns>String containing C# code for StateEvent se.</returns>
357 private string GenerateStateEvent(StateEvent se, string parentStateName)
358 {
359 string retstr = String.Empty;
360  
361 // we need to separate the argument declaration list from other kids
362 List<SYMBOL> argumentDeclarationListKids = new List<SYMBOL>();
363 List<SYMBOL> remainingKids = new List<SYMBOL>();
364  
365 foreach (SYMBOL kid in se.kids)
366 if (kid is ArgumentDeclarationList)
367 argumentDeclarationListKids.Add(kid);
368 else
369 remainingKids.Add(kid);
370  
371 // "state" (function) declaration
372 retstr += GenerateIndented(String.Format("public void {0}_event_{1}(", parentStateName, se.Name), se);
373  
374 // print the state arguments, if any
375 foreach (SYMBOL kid in argumentDeclarationListKids)
376 retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid);
377  
378 retstr += GenerateLine(")");
379  
380 foreach (SYMBOL kid in remainingKids)
381 retstr += GenerateNode(se, kid);
382  
383 return retstr;
384 }
385  
386 /// <summary>
387 /// Generates the code for an ArgumentDeclarationList node.
388 /// </summary>
389 /// <param name="adl">The ArgumentDeclarationList node.</param>
390 /// <returns>String containing C# code for ArgumentDeclarationList adl.</returns>
391 private string GenerateArgumentDeclarationList(ArgumentDeclarationList adl)
392 {
393 string retstr = String.Empty;
394  
395 int comma = adl.kids.Count - 1; // tells us whether to print a comma
396  
397 foreach (Declaration d in adl.kids)
398 {
399 retstr += Generate(String.Format("{0} {1}", d.Datatype, CheckName(d.Id)), d);
400 if (0 < comma--)
401 retstr += Generate(", ");
402 }
403  
404 return retstr;
405 }
406  
407 /// <summary>
408 /// Generates the code for an ArgumentList node.
409 /// </summary>
410 /// <param name="al">The ArgumentList node.</param>
411 /// <returns>String containing C# code for ArgumentList al.</returns>
412 private string GenerateArgumentList(ArgumentList al)
413 {
414 string retstr = String.Empty;
415  
416 int comma = al.kids.Count - 1; // tells us whether to print a comma
417  
418 foreach (SYMBOL s in al.kids)
419 {
420 retstr += GenerateNode(al, s);
421 if (0 < comma--)
422 retstr += Generate(", ");
423 }
424  
425 return retstr;
426 }
427  
428 /// <summary>
429 /// Generates the code for a CompoundStatement node.
430 /// </summary>
431 /// <param name="cs">The CompoundStatement node.</param>
432 /// <returns>String containing C# code for CompoundStatement cs.</returns>
433 private string GenerateCompoundStatement(SYMBOL previousSymbol, CompoundStatement cs)
434 {
435 string retstr = String.Empty;
436  
437 // opening brace
438 retstr += GenerateIndentedLine("{");
439 m_braceCount++;
440  
441 if (m_insertCoopTerminationChecks)
442 {
443 // We have to check in event functions as well because the user can manually call these.
444 if (previousSymbol is GlobalFunctionDefinition
445 || previousSymbol is WhileStatement
446 || previousSymbol is DoWhileStatement
447 || previousSymbol is ForLoop
448 || previousSymbol is StateEvent)
449 retstr += GenerateIndentedLine(m_coopTerminationCheck);
450 }
451  
452 foreach (SYMBOL kid in cs.kids)
453 retstr += GenerateNode(cs, kid);
454  
455 // closing brace
456 m_braceCount--;
457 retstr += GenerateIndentedLine("}");
458  
459 return retstr;
460 }
461  
462 /// <summary>
463 /// Generates the code for a Declaration node.
464 /// </summary>
465 /// <param name="d">The Declaration node.</param>
466 /// <returns>String containing C# code for Declaration d.</returns>
467 private string GenerateDeclaration(Declaration d)
468 {
469 return Generate(String.Format("{0} {1}", d.Datatype, CheckName(d.Id)), d);
470 }
471  
472 /// <summary>
473 /// Generates the code for a Statement node.
474 /// </summary>
475 /// <param name="s">The Statement node.</param>
476 /// <returns>String containing C# code for Statement s.</returns>
477 private string GenerateStatement(SYMBOL previousSymbol, Statement s)
478 {
479 string retstr = String.Empty;
480 bool printSemicolon = true;
481 bool transformToBlock = false;
482  
483 if (m_insertCoopTerminationChecks)
484 {
485 // A non-braced single line do while structure cannot contain multiple statements.
486 // So to insert the termination check we change this to a braced control structure instead.
487 if (previousSymbol is WhileStatement
488 || previousSymbol is DoWhileStatement
489 || previousSymbol is ForLoop)
490 {
491 transformToBlock = true;
492  
493 // FIXME: This will be wrongly indented because the previous for/while/dowhile will have already indented.
494 retstr += GenerateIndentedLine("{");
495  
496 retstr += GenerateIndentedLine(m_coopTerminationCheck);
497 }
498 }
499  
500 retstr += Indent();
501  
502 if (0 < s.kids.Count)
503 {
504 // Jump label prints its own colon, we don't need a semicolon.
505 printSemicolon = !(s.kids.Top is JumpLabel);
506  
507 // If we encounter a lone Ident, we skip it, since that's a C#
508 // (MONO) error.
509 if (!(s.kids.Top is IdentExpression && 1 == s.kids.Count))
510 foreach (SYMBOL kid in s.kids)
511 retstr += GenerateNode(s, kid);
512 }
513  
514 if (printSemicolon)
515 retstr += GenerateLine(";");
516  
517 if (transformToBlock)
518 {
519 // FIXME: This will be wrongly indented because the for/while/dowhile is currently handling the unindent
520 retstr += GenerateIndentedLine("}");
521 }
522  
523 return retstr;
524 }
525  
526 /// <summary>
527 /// Generates the code for an Assignment node.
528 /// </summary>
529 /// <param name="a">The Assignment node.</param>
530 /// <returns>String containing C# code for Assignment a.</returns>
531 private string GenerateAssignment(Assignment a)
532 {
533 string retstr = String.Empty;
534  
535 List<string> identifiers = new List<string>();
536 checkForMultipleAssignments(identifiers, a);
537  
538 retstr += GenerateNode(a, (SYMBOL) a.kids.Pop());
539 retstr += Generate(String.Format(" {0} ", a.AssignmentType), a);
540 foreach (SYMBOL kid in a.kids)
541 retstr += GenerateNode(a, kid);
542  
543 return retstr;
544 }
545  
546 // This code checks for LSL of the following forms, and generates a
547 // warning if it finds them.
548 //
549 // list l = [ "foo" ];
550 // l = (l=[]) + l + ["bar"];
551 // (produces l=["foo","bar"] in SL but l=["bar"] in OS)
552 //
553 // integer i;
554 // integer j;
555 // i = (j = 3) + (j = 4) + (j = 5);
556 // (produces j=3 in SL but j=5 in OS)
557 //
558 // Without this check, that code passes compilation, but does not do what
559 // the end user expects, because LSL in SL evaluates right to left instead
560 // of left to right.
561 //
562 // The theory here is that producing an error and alerting the end user that
563 // something needs to change is better than silently generating incorrect code.
564 private void checkForMultipleAssignments(List<string> identifiers, SYMBOL s)
565 {
566 if (s is Assignment)
567 {
568 Assignment a = (Assignment)s;
569 string newident = null;
570  
571 if (a.kids[0] is Declaration)
572 {
573 newident = ((Declaration)a.kids[0]).Id;
574 }
575 else if (a.kids[0] is IDENT)
576 {
577 newident = ((IDENT)a.kids[0]).yytext;
578 }
579 else if (a.kids[0] is IdentDotExpression)
580 {
581 newident = ((IdentDotExpression)a.kids[0]).Name; // +"." + ((IdentDotExpression)a.kids[0]).Member;
582 }
583 else
584 {
585 AddWarning(String.Format("Multiple assignments checker internal error '{0}' at line {1} column {2}.", a.kids[0].GetType(), ((SYMBOL)a.kids[0]).Line - 1, ((SYMBOL)a.kids[0]).Position));
586 }
587  
588 if (identifiers.Contains(newident))
589 {
590 AddWarning(String.Format("Multiple assignments to '{0}' at line {1} column {2}; results may differ between LSL and OSSL.", newident, ((SYMBOL)a.kids[0]).Line - 1, ((SYMBOL)a.kids[0]).Position));
591 }
592 identifiers.Add(newident);
593 }
594  
595 int index;
596 for (index = 0; index < s.kids.Count; index++)
597 {
598 checkForMultipleAssignments(identifiers, (SYMBOL) s.kids[index]);
599 }
600 }
601  
602 /// <summary>
603 /// Generates the code for a ReturnStatement node.
604 /// </summary>
605 /// <param name="rs">The ReturnStatement node.</param>
606 /// <returns>String containing C# code for ReturnStatement rs.</returns>
607 private string GenerateReturnStatement(ReturnStatement rs)
608 {
609 string retstr = String.Empty;
610  
611 retstr += Generate("return ", rs);
612  
613 foreach (SYMBOL kid in rs.kids)
614 retstr += GenerateNode(rs, kid);
615  
616 return retstr;
617 }
618  
619 /// <summary>
620 /// Generates the code for a JumpLabel node.
621 /// </summary>
622 /// <param name="jl">The JumpLabel node.</param>
623 /// <returns>String containing C# code for JumpLabel jl.</returns>
624 private string GenerateJumpLabel(JumpLabel jl)
625 {
626 string labelStatement;
627  
628 if (m_insertCoopTerminationChecks)
629 labelStatement = m_coopTerminationCheck + "\n";
630 else
631 labelStatement = "NoOp();\n";
632  
633 return Generate(String.Format("{0}: ", CheckName(jl.LabelName)), jl) + labelStatement;
634 }
635  
636 /// <summary>
637 /// Generates the code for a JumpStatement node.
638 /// </summary>
639 /// <param name="js">The JumpStatement node.</param>
640 /// <returns>String containing C# code for JumpStatement js.</returns>
641 private string GenerateJumpStatement(JumpStatement js)
642 {
643 return Generate(String.Format("goto {0}", CheckName(js.TargetName)), js);
644 }
645  
646 /// <summary>
647 /// Generates the code for an IfStatement node.
648 /// </summary>
649 /// <param name="ifs">The IfStatement node.</param>
650 /// <returns>String containing C# code for IfStatement ifs.</returns>
651 private string GenerateIfStatement(IfStatement ifs)
652 {
653 string retstr = String.Empty;
654  
655 retstr += GenerateIndented("if (", ifs);
656 retstr += GenerateNode(ifs, (SYMBOL) ifs.kids.Pop());
657 retstr += GenerateLine(")");
658  
659 // CompoundStatement handles indentation itself but we need to do it
660 // otherwise.
661 bool indentHere = ifs.kids.Top is Statement;
662 if (indentHere) m_braceCount++;
663 retstr += GenerateNode(ifs, (SYMBOL) ifs.kids.Pop());
664 if (indentHere) m_braceCount--;
665  
666 if (0 < ifs.kids.Count) // do it again for an else
667 {
668 retstr += GenerateIndentedLine("else", ifs);
669  
670 indentHere = ifs.kids.Top is Statement;
671 if (indentHere) m_braceCount++;
672 retstr += GenerateNode(ifs, (SYMBOL) ifs.kids.Pop());
673 if (indentHere) m_braceCount--;
674 }
675  
676 return retstr;
677 }
678  
679 /// <summary>
680 /// Generates the code for a StateChange node.
681 /// </summary>
682 /// <param name="sc">The StateChange node.</param>
683 /// <returns>String containing C# code for StateChange sc.</returns>
684 private string GenerateStateChange(StateChange sc)
685 {
686 return Generate(String.Format("state(\"{0}\")", sc.NewState), sc);
687 }
688  
689 /// <summary>
690 /// Generates the code for a WhileStatement node.
691 /// </summary>
692 /// <param name="ws">The WhileStatement node.</param>
693 /// <returns>String containing C# code for WhileStatement ws.</returns>
694 private string GenerateWhileStatement(WhileStatement ws)
695 {
696 string retstr = String.Empty;
697  
698 retstr += GenerateIndented("while (", ws);
699 retstr += GenerateNode(ws, (SYMBOL) ws.kids.Pop());
700 retstr += GenerateLine(")");
701  
702 // CompoundStatement handles indentation itself but we need to do it
703 // otherwise.
704 bool indentHere = ws.kids.Top is Statement;
705 if (indentHere) m_braceCount++;
706 retstr += GenerateNode(ws, (SYMBOL) ws.kids.Pop());
707 if (indentHere) m_braceCount--;
708  
709 return retstr;
710 }
711  
712 /// <summary>
713 /// Generates the code for a DoWhileStatement node.
714 /// </summary>
715 /// <param name="dws">The DoWhileStatement node.</param>
716 /// <returns>String containing C# code for DoWhileStatement dws.</returns>
717 private string GenerateDoWhileStatement(DoWhileStatement dws)
718 {
719 string retstr = String.Empty;
720  
721 retstr += GenerateIndentedLine("do", dws);
722  
723 // CompoundStatement handles indentation itself but we need to do it
724 // otherwise.
725 bool indentHere = dws.kids.Top is Statement;
726 if (indentHere) m_braceCount++;
727 retstr += GenerateNode(dws, (SYMBOL) dws.kids.Pop());
728 if (indentHere) m_braceCount--;
729  
730 retstr += GenerateIndented("while (", dws);
731 retstr += GenerateNode(dws, (SYMBOL) dws.kids.Pop());
732 retstr += GenerateLine(");");
733  
734 return retstr;
735 }
736  
737 /// <summary>
738 /// Generates the code for a ForLoop node.
739 /// </summary>
740 /// <param name="fl">The ForLoop node.</param>
741 /// <returns>String containing C# code for ForLoop fl.</returns>
742 private string GenerateForLoop(ForLoop fl)
743 {
744 string retstr = String.Empty;
745  
746 retstr += GenerateIndented("for (", fl);
747  
748 // It's possible that we don't have an assignment, in which case
749 // the child will be null and we only print the semicolon.
750 // for (x = 0; x < 10; x++)
751 // ^^^^^
752 ForLoopStatement s = (ForLoopStatement) fl.kids.Pop();
753 if (null != s)
754 {
755 retstr += GenerateForLoopStatement(s);
756 }
757 retstr += Generate("; ");
758 // for (x = 0; x < 10; x++)
759 // ^^^^^^
760 retstr += GenerateNode(fl, (SYMBOL) fl.kids.Pop());
761 retstr += Generate("; ");
762 // for (x = 0; x < 10; x++)
763 // ^^^
764 retstr += GenerateForLoopStatement((ForLoopStatement) fl.kids.Pop());
765 retstr += GenerateLine(")");
766  
767 // CompoundStatement handles indentation itself but we need to do it
768 // otherwise.
769 bool indentHere = fl.kids.Top is Statement;
770 if (indentHere) m_braceCount++;
771 retstr += GenerateNode(fl, (SYMBOL) fl.kids.Pop());
772 if (indentHere) m_braceCount--;
773  
774 return retstr;
775 }
776  
777 /// <summary>
778 /// Generates the code for a ForLoopStatement node.
779 /// </summary>
780 /// <param name="fls">The ForLoopStatement node.</param>
781 /// <returns>String containing C# code for ForLoopStatement fls.</returns>
782 private string GenerateForLoopStatement(ForLoopStatement fls)
783 {
784 string retstr = String.Empty;
785  
786 int comma = fls.kids.Count - 1; // tells us whether to print a comma
787  
788 // It's possible that all we have is an empty Ident, for example:
789 //
790 // for (x; x < 10; x++) { ... }
791 //
792 // Which is illegal in C# (MONO). We'll skip it.
793 if (fls.kids.Top is IdentExpression && 1 == fls.kids.Count)
794 return retstr;
795  
796 for (int i = 0; i < fls.kids.Count; i++)
797 {
798 SYMBOL s = (SYMBOL)fls.kids[i];
799  
800 // Statements surrounded by parentheses in for loops
801 //
802 // e.g. for ((i = 0), (j = 7); (i < 10); (++i))
803 //
804 // are legal in LSL but not in C# so we need to discard the parentheses
805 //
806 // The following, however, does not appear to be legal in LLS
807 //
808 // for ((i = 0, j = 7); (i < 10); (++i))
809 //
810 // As of Friday 20th November 2009, the Linden Lab simulators appear simply never to compile or run this
811 // script but with no debug or warnings at all! Therefore, we won't deal with this yet (which looks
812 // like it would be considerably more complicated to handle).
813 while (s is ParenthesisExpression)
814 s = (SYMBOL)s.kids.Pop();
815  
816 retstr += GenerateNode(fls, s);
817 if (0 < comma--)
818 retstr += Generate(", ");
819 }
820  
821 return retstr;
822 }
823  
824 /// <summary>
825 /// Generates the code for a BinaryExpression node.
826 /// </summary>
827 /// <param name="be">The BinaryExpression node.</param>
828 /// <returns>String containing C# code for BinaryExpression be.</returns>
829 private string GenerateBinaryExpression(BinaryExpression be)
830 {
831 string retstr = String.Empty;
832  
833 if (be.ExpressionSymbol.Equals("&&") || be.ExpressionSymbol.Equals("||"))
834 {
835 // special case handling for logical and/or, see Mantis 3174
836 retstr += "((bool)(";
837 retstr += GenerateNode(be, (SYMBOL)be.kids.Pop());
838 retstr += "))";
839 retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol.Substring(0,1)), be);
840 retstr += "((bool)(";
841 foreach (SYMBOL kid in be.kids)
842 retstr += GenerateNode(be, kid);
843 retstr += "))";
844 }
845 else
846 {
847 retstr += GenerateNode(be, (SYMBOL)be.kids.Pop());
848 retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol), be);
849 foreach (SYMBOL kid in be.kids)
850 retstr += GenerateNode(be, kid);
851 }
852  
853 return retstr;
854 }
855  
856 /// <summary>
857 /// Generates the code for a UnaryExpression node.
858 /// </summary>
859 /// <param name="ue">The UnaryExpression node.</param>
860 /// <returns>String containing C# code for UnaryExpression ue.</returns>
861 private string GenerateUnaryExpression(UnaryExpression ue)
862 {
863 string retstr = String.Empty;
864  
865 retstr += Generate(ue.UnarySymbol, ue);
866 retstr += GenerateNode(ue, (SYMBOL) ue.kids.Pop());
867  
868 return retstr;
869 }
870  
871 /// <summary>
872 /// Generates the code for a ParenthesisExpression node.
873 /// </summary>
874 /// <param name="pe">The ParenthesisExpression node.</param>
875 /// <returns>String containing C# code for ParenthesisExpression pe.</returns>
876 private string GenerateParenthesisExpression(ParenthesisExpression pe)
877 {
878 string retstr = String.Empty;
879  
880 retstr += Generate("(");
881 foreach (SYMBOL kid in pe.kids)
882 retstr += GenerateNode(pe, kid);
883 retstr += Generate(")");
884  
885 return retstr;
886 }
887  
888 /// <summary>
889 /// Generates the code for a IncrementDecrementExpression node.
890 /// </summary>
891 /// <param name="ide">The IncrementDecrementExpression node.</param>
892 /// <returns>String containing C# code for IncrementDecrementExpression ide.</returns>
893 private string GenerateIncrementDecrementExpression(IncrementDecrementExpression ide)
894 {
895 string retstr = String.Empty;
896  
897 if (0 < ide.kids.Count)
898 {
899 IdentDotExpression dot = (IdentDotExpression) ide.kids.Top;
900 retstr += Generate(String.Format("{0}", ide.PostOperation ? CheckName(dot.Name) + "." + dot.Member + ide.Operation : ide.Operation + CheckName(dot.Name) + "." + dot.Member), ide);
901 }
902 else
903 retstr += Generate(String.Format("{0}", ide.PostOperation ? CheckName(ide.Name) + ide.Operation : ide.Operation + CheckName(ide.Name)), ide);
904  
905 return retstr;
906 }
907  
908 /// <summary>
909 /// Generates the code for a TypecastExpression node.
910 /// </summary>
911 /// <param name="te">The TypecastExpression node.</param>
912 /// <returns>String containing C# code for TypecastExpression te.</returns>
913 private string GenerateTypecastExpression(TypecastExpression te)
914 {
915 string retstr = String.Empty;
916  
917 // we wrap all typecasted statements in parentheses
918 retstr += Generate(String.Format("({0}) (", te.TypecastType), te);
919 retstr += GenerateNode(te, (SYMBOL) te.kids.Pop());
920 retstr += Generate(")");
921  
922 return retstr;
923 }
924  
925 /// <summary>
926 /// Generates the code for an identifier
927 /// </summary>
928 /// <param name="id">The symbol name</param>
929 /// <param name="s">The Symbol node.</param>
930 /// <returns>String containing C# code for identifier reference.</returns>
931 private string GenerateIdentifier(string id, SYMBOL s)
932 {
933 if (m_comms != null)
934 {
935 object value = m_comms.LookupModConstant(id);
936 if (value != null)
937 {
938 string retval = null;
939 if (value is int)
940 retval = String.Format("new LSL_Types.LSLInteger({0})",((int)value).ToString());
941 else if (value is float)
942 retval = String.Format("new LSL_Types.LSLFloat({0})",((float)value).ToString());
943 else if (value is string)
944 retval = String.Format("new LSL_Types.LSLString(\"{0}\")",((string)value));
945 else if (value is OpenMetaverse.UUID)
946 retval = String.Format("new LSL_Types.key(\"{0}\")",((OpenMetaverse.UUID)value).ToString());
947 else if (value is OpenMetaverse.Vector3)
948 retval = String.Format("new LSL_Types.Vector3(\"{0}\")",((OpenMetaverse.Vector3)value).ToString());
949 else if (value is OpenMetaverse.Quaternion)
950 retval = String.Format("new LSL_Types.Quaternion(\"{0}\")",((OpenMetaverse.Quaternion)value).ToString());
951 else retval = id;
952  
953 return Generate(retval, s);
954 }
955 }
956  
957 return Generate(CheckName(id), s);
958 }
959  
960 /// <summary>
961 /// Generates the code for a FunctionCall node.
962 /// </summary>
963 /// <param name="fc">The FunctionCall node.</param>
964 /// <returns>String containing C# code for FunctionCall fc.</returns>
965 private string GenerateFunctionCall(FunctionCall fc)
966 {
967 string retstr = String.Empty;
968  
969 string modinvoke = null;
970 if (m_comms != null)
971 modinvoke = m_comms.LookupModInvocation(fc.Id);
972  
973 if (modinvoke != null)
974 {
975 if (fc.kids[0] is ArgumentList)
976 {
977 if ((fc.kids[0] as ArgumentList).kids.Count == 0)
978 retstr += Generate(String.Format("{0}(\"{1}\"",modinvoke,fc.Id), fc);
979 else
980 retstr += Generate(String.Format("{0}(\"{1}\",",modinvoke,fc.Id), fc);
981 }
982 }
983 else
984 {
985 retstr += Generate(String.Format("{0}(", CheckName(fc.Id)), fc);
986 }
987  
988 foreach (SYMBOL kid in fc.kids)
989 retstr += GenerateNode(fc, kid);
990  
991 retstr += Generate(")");
992  
993 return retstr;
994 }
995  
996 /// <summary>
997 /// Generates the code for a Constant node.
998 /// </summary>
999 /// <param name="c">The Constant node.</param>
1000 /// <returns>String containing C# code for Constant c.</returns>
1001 private string GenerateConstant(Constant c)
1002 {
1003 string retstr = String.Empty;
1004  
1005 // Supprt LSL's weird acceptance of floats with no trailing digits
1006 // after the period. Turn float x = 10.; into float x = 10.0;
1007 if ("LSL_Types.LSLFloat" == c.Type)
1008 {
1009 int dotIndex = c.Value.IndexOf('.') + 1;
1010 if (0 < dotIndex && (dotIndex == c.Value.Length || !Char.IsDigit(c.Value[dotIndex])))
1011 c.Value = c.Value.Insert(dotIndex, "0");
1012 c.Value = "new LSL_Types.LSLFloat("+c.Value+")";
1013 }
1014 else if ("LSL_Types.LSLInteger" == c.Type)
1015 {
1016 c.Value = "new LSL_Types.LSLInteger("+c.Value+")";
1017 }
1018 else if ("LSL_Types.LSLString" == c.Type)
1019 {
1020 c.Value = "new LSL_Types.LSLString(\""+c.Value+"\")";
1021 }
1022  
1023 retstr += Generate(c.Value, c);
1024  
1025 return retstr;
1026 }
1027  
1028 /// <summary>
1029 /// Generates the code for a VectorConstant node.
1030 /// </summary>
1031 /// <param name="vc">The VectorConstant node.</param>
1032 /// <returns>String containing C# code for VectorConstant vc.</returns>
1033 private string GenerateVectorConstant(VectorConstant vc)
1034 {
1035 string retstr = String.Empty;
1036  
1037 retstr += Generate(String.Format("new {0}(", vc.Type), vc);
1038 retstr += GenerateNode(vc, (SYMBOL) vc.kids.Pop());
1039 retstr += Generate(", ");
1040 retstr += GenerateNode(vc, (SYMBOL) vc.kids.Pop());
1041 retstr += Generate(", ");
1042 retstr += GenerateNode(vc, (SYMBOL) vc.kids.Pop());
1043 retstr += Generate(")");
1044  
1045 return retstr;
1046 }
1047  
1048 /// <summary>
1049 /// Generates the code for a RotationConstant node.
1050 /// </summary>
1051 /// <param name="rc">The RotationConstant node.</param>
1052 /// <returns>String containing C# code for RotationConstant rc.</returns>
1053 private string GenerateRotationConstant(RotationConstant rc)
1054 {
1055 string retstr = String.Empty;
1056  
1057 retstr += Generate(String.Format("new {0}(", rc.Type), rc);
1058 retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop());
1059 retstr += Generate(", ");
1060 retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop());
1061 retstr += Generate(", ");
1062 retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop());
1063 retstr += Generate(", ");
1064 retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop());
1065 retstr += Generate(")");
1066  
1067 return retstr;
1068 }
1069  
1070 /// <summary>
1071 /// Generates the code for a ListConstant node.
1072 /// </summary>
1073 /// <param name="lc">The ListConstant node.</param>
1074 /// <returns>String containing C# code for ListConstant lc.</returns>
1075 private string GenerateListConstant(ListConstant lc)
1076 {
1077 string retstr = String.Empty;
1078  
1079 retstr += Generate(String.Format("new {0}(", lc.Type), lc);
1080  
1081 foreach (SYMBOL kid in lc.kids)
1082 retstr += GenerateNode(lc, kid);
1083  
1084 retstr += Generate(")");
1085  
1086 return retstr;
1087 }
1088  
1089 /// <summary>
1090 /// Prints a newline.
1091 /// </summary>
1092 /// <returns>A newline.</returns>
1093 private string GenerateLine()
1094 {
1095 return GenerateLine("");
1096 }
1097  
1098 /// <summary>
1099 /// Prints text, followed by a newline.
1100 /// </summary>
1101 /// <param name="s">String of text to print.</param>
1102 /// <returns>String s followed by newline.</returns>
1103 private string GenerateLine(string s)
1104 {
1105 return GenerateLine(s, null);
1106 }
1107  
1108 /// <summary>
1109 /// Prints text, followed by a newline.
1110 /// </summary>
1111 /// <param name="s">String of text to print.</param>
1112 /// <param name="sym">Symbol being generated to extract original line
1113 /// number and column from.</param>
1114 /// <returns>String s followed by newline.</returns>
1115 private string GenerateLine(string s, SYMBOL sym)
1116 {
1117 string retstr = Generate(s, sym) + "\n";
1118  
1119 m_CSharpLine++;
1120 m_CSharpCol = 1;
1121  
1122 return retstr;
1123 }
1124  
1125 /// <summary>
1126 /// Prints text.
1127 /// </summary>
1128 /// <param name="s">String of text to print.</param>
1129 /// <returns>String s.</returns>
1130 private string Generate(string s)
1131 {
1132 return Generate(s, null);
1133 }
1134  
1135 /// <summary>
1136 /// Prints text.
1137 /// </summary>
1138 /// <param name="s">String of text to print.</param>
1139 /// <param name="sym">Symbol being generated to extract original line
1140 /// number and column from.</param>
1141 /// <returns>String s.</returns>
1142 private string Generate(string s, SYMBOL sym)
1143 {
1144 if (null != sym)
1145 m_positionMap.Add(new KeyValuePair<int, int>(m_CSharpLine, m_CSharpCol), new KeyValuePair<int, int>(sym.Line, sym.Position));
1146  
1147 m_CSharpCol += s.Length;
1148  
1149 return s;
1150 }
1151  
1152 /// <summary>
1153 /// Prints text correctly indented, followed by a newline.
1154 /// </summary>
1155 /// <param name="s">String of text to print.</param>
1156 /// <returns>Properly indented string s followed by newline.</returns>
1157 private string GenerateIndentedLine(string s)
1158 {
1159 return GenerateIndentedLine(s, null);
1160 }
1161  
1162 /// <summary>
1163 /// Prints text correctly indented, followed by a newline.
1164 /// </summary>
1165 /// <param name="s">String of text to print.</param>
1166 /// <param name="sym">Symbol being generated to extract original line
1167 /// number and column from.</param>
1168 /// <returns>Properly indented string s followed by newline.</returns>
1169 private string GenerateIndentedLine(string s, SYMBOL sym)
1170 {
1171 string retstr = GenerateIndented(s, sym) + "\n";
1172  
1173 m_CSharpLine++;
1174 m_CSharpCol = 1;
1175  
1176 return retstr;
1177 }
1178  
1179 /// <summary>
1180 /// Prints text correctly indented.
1181 /// </summary>
1182 /// <param name="s">String of text to print.</param>
1183 /// <returns>Properly indented string s.</returns>
1184 //private string GenerateIndented(string s)
1185 //{
1186 // return GenerateIndented(s, null);
1187 //}
1188 // THIS FUNCTION IS COMMENTED OUT TO SUPPRESS WARNINGS
1189  
1190 /// <summary>
1191 /// Prints text correctly indented.
1192 /// </summary>
1193 /// <param name="s">String of text to print.</param>
1194 /// <param name="sym">Symbol being generated to extract original line
1195 /// number and column from.</param>
1196 /// <returns>Properly indented string s.</returns>
1197 private string GenerateIndented(string s, SYMBOL sym)
1198 {
1199 string retstr = Indent() + s;
1200  
1201 if (null != sym)
1202 m_positionMap.Add(new KeyValuePair<int, int>(m_CSharpLine, m_CSharpCol), new KeyValuePair<int, int>(sym.Line, sym.Position));
1203  
1204 m_CSharpCol += s.Length;
1205  
1206 return retstr;
1207 }
1208  
1209 /// <summary>
1210 /// Prints correct indentation.
1211 /// </summary>
1212 /// <returns>Indentation based on brace count.</returns>
1213 private string Indent()
1214 {
1215 string retstr = String.Empty;
1216  
1217 for (int i = 0; i < m_braceCount; i++)
1218 for (int j = 0; j < m_indentWidth; j++)
1219 {
1220 retstr += " ";
1221 m_CSharpCol++;
1222 }
1223  
1224 return retstr;
1225 }
1226  
1227 /// <summary>
1228 /// Returns the passed name with an underscore prepended if that name is a reserved word in C#
1229 /// and not resevered in LSL otherwise it just returns the passed name.
1230 ///
1231 /// This makes no attempt to cache the results to minimise future lookups. For a non trivial
1232 /// scripts the number of unique identifiers could easily grow to the size of the reserved word
1233 /// list so maintaining a list or dictionary and doing the lookup there firstwould probably not
1234 /// give any real speed advantage.
1235 ///
1236 /// I believe there is a class Microsoft.CSharp.CSharpCodeProvider that has a function
1237 /// CreateValidIdentifier(str) that will return either the value of str if it is not a C#
1238 /// key word or "_"+str if it is. But availability under Mono?
1239 /// </summary>
1240 private string CheckName(string s)
1241 {
1242 if (CSReservedWords.IsReservedWord(s))
1243 return "@" + s;
1244 else
1245 return s;
1246 }
1247 }
1248 }