/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Text; using System.IO; using System.Collections.Generic; using System.Reflection; using log4net; using Tools; using OpenSim.Region.Framework.Interfaces; namespace OpenSim.Region.ScriptEngine.Shared.CodeTools { public class CSCodeGenerator : ICodeConverter { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static yyLSLSyntax yyLSL = new yyLSLSyntax(); private SYMBOL m_astRoot = null; private Dictionary, KeyValuePair> m_positionMap; private int m_braceCount; // for indentation private int m_CSharpLine; // the current line of generated C# code private int m_CSharpCol; // the current column of generated C# code private List m_warnings = new List(); private IScriptModuleComms m_comms = null; private bool m_insertCoopTerminationChecks; private static string m_coopTerminationCheck = "opensim_reserved_CheckForCoopTermination();"; /// /// Keep a record of the previous node when we do the parsing. /// /// /// We do this here because the parser generated by CSTools does not retain a reference to its parent node. /// The previous node is required so we can correctly insert co-op termination checks when required. /// // private SYMBOL m_previousNode; /// /// Creates an 'empty' CSCodeGenerator instance. /// public CSCodeGenerator() { m_comms = null; ResetCounters(); } public CSCodeGenerator(IScriptModuleComms comms, bool insertCoopTerminationChecks) { m_comms = comms; m_insertCoopTerminationChecks = insertCoopTerminationChecks; ResetCounters(); } /// /// Get the mapping between LSL and C# line/column number. /// /// Dictionary\, KeyValuePair\\>. public Dictionary, KeyValuePair> PositionMap { get { return m_positionMap; } } /// /// Get the mapping between LSL and C# line/column number. /// /// SYMBOL pointing to root of the abstract syntax tree. public SYMBOL ASTRoot { get { return m_astRoot; } } public void Clear() { m_astRoot.kids = null; m_astRoot.yylx = null; m_astRoot.yyps = null; m_astRoot = null; m_positionMap = null; m_warnings.Clear(); m_comms = null; } /// /// Resets various counters and metadata. /// private void ResetCounters() { m_braceCount = 0; m_CSharpLine = 0; m_CSharpCol = 1; m_positionMap = new Dictionary, KeyValuePair>(); m_astRoot = null; } public string Convert(string script) { StringBuilder sb = new StringBuilder(4096); Convert(script, sb); return sb.ToString(); } /// /// Generate the code from the AST we have. /// /// The LSL source as a string. /// String containing the generated C# code. public void Convert(string script, StringBuilder sb) { // m_log.DebugFormat("[CS CODE GENERATOR]: Converting to C#\n{0}", script); m_warnings.Clear(); ResetCounters(); ErrorHandler errorHandler = new ErrorHandler(true); Parser p = new LSLSyntax(yyLSL, errorHandler); LSL2CSCodeTransformer codeTransformer; try { codeTransformer = new LSL2CSCodeTransformer(p.Parse(script)); } catch (CSToolsException e) { string message; // LL start numbering lines at 0 - geeks! // Also need to subtract one line we prepend! // string emessage = e.Message; string slinfo = e.slInfo.ToString(); // Remove wrong line number info // if (emessage.StartsWith(slinfo+": ")) emessage = emessage.Substring(slinfo.Length+2); message = String.Format("({0},{1}) {2}", e.slInfo.lineNumber - 1, e.slInfo.charPosition - 1, emessage); throw new Exception(message); } m_astRoot = codeTransformer.Transform(); // standard preamble //retstr = GenerateLine("using OpenSim.Region.ScriptEngine.Common;"); //retstr += GenerateLine("using System.Collections.Generic;"); //retstr += GenerateLine(""); //retstr += GenerateLine("namespace SecondLife"); //retstr += GenerateLine("{"); m_braceCount++; //retstr += GenerateIndentedLine("public class Script : OpenSim.Region.ScriptEngine.Common"); //retstr += GenerateIndentedLine("{"); m_braceCount++; // line number m_CSharpLine += 9; // here's the payload sb.Append("\n"); foreach (SYMBOL s in m_astRoot.kids) GenerateNodeToSB(m_astRoot, s, sb); codeTransformer = null; p.m_lexer.m_buf=null; p.m_lexer.yytext = null; p.m_lexer = null; p.m_symbols = null; p = null; errorHandler = null; // close braces! // m_braceCount--; //retstr += GenerateIndentedLine("}"); // m_braceCount--; //retstr += GenerateLine("}"); // Removes all carriage return characters which may be generated in Windows platform. Is there // cleaner way of doing this? // sb.Replace("\r", ""); } /// /// Get the set of warnings generated during compilation. /// /// public string[] GetWarnings() { return m_warnings.ToArray(); } private void AddWarning(string warning) { if (!m_warnings.Contains(warning)) { m_warnings.Add(warning); } } /// /// Recursively called to generate each type of node. Will generate this /// node, then all it's children. /// /// The parent node. /// The current node to generate code for. /// String containing C# code for SYMBOL s. private void GenerateNodeToSB(SYMBOL previousSymbol, SYMBOL s, StringBuilder sb) { // make sure to put type lower in the inheritance hierarchy first // ie: since IdentArgument and ExpressionArgument inherit from // Argument, put IdentArgument and ExpressionArgument before Argument if (s is GlobalFunctionDefinition) GenerateGlobalFunctionDefinition((GlobalFunctionDefinition) s, sb); else if (s is GlobalVariableDeclaration) GenerateGlobalVariableDeclaration((GlobalVariableDeclaration) s , sb); else if (s is State) GenerateState((State) s, sb); else if (s is CompoundStatement) GenerateCompoundStatement(previousSymbol, (CompoundStatement) s, sb); else if (s is Declaration) GenerateDeclaration((Declaration) s, sb); else if (s is Statement) GenerateStatement(previousSymbol, (Statement) s, sb); else if (s is ReturnStatement) GenerateReturnStatement((ReturnStatement) s, sb); else if (s is JumpLabel) GenerateJumpLabel((JumpLabel) s, sb); else if (s is JumpStatement) GenerateJumpStatement((JumpStatement) s, sb); else if (s is StateChange) GenerateStateChange((StateChange) s, sb); else if (s is IfStatement) GenerateIfStatement((IfStatement) s, sb); else if (s is WhileStatement) GenerateWhileStatement((WhileStatement) s, sb); else if (s is DoWhileStatement) GenerateDoWhileStatement((DoWhileStatement) s, sb); else if (s is ForLoop) GenerateForLoop((ForLoop) s, sb); else if (s is ArgumentList) GenerateArgumentList((ArgumentList) s, sb); else if (s is Assignment) GenerateAssignment((Assignment) s, sb); else if (s is BinaryExpression) GenerateBinaryExpression((BinaryExpression) s, sb); else if (s is ParenthesisExpression) GenerateParenthesisExpression((ParenthesisExpression) s, sb); else if (s is UnaryExpression) GenerateUnaryExpression((UnaryExpression) s, sb); else if (s is IncrementDecrementExpression) GenerateIncrementDecrementExpression((IncrementDecrementExpression) s, sb); else if (s is TypecastExpression) GenerateTypecastExpression((TypecastExpression) s, sb); else if (s is FunctionCall) GenerateFunctionCall((FunctionCall) s, sb); else if (s is VectorConstant) GenerateVectorConstant((VectorConstant) s, sb); else if (s is RotationConstant) GenerateRotationConstant((RotationConstant) s, sb); else if (s is ListConstant) GenerateListConstant((ListConstant) s, sb); else if (s is Constant) GenerateConstant((Constant) s, sb); else if (s is IdentDotExpression) Generate(CheckName(((IdentDotExpression) s).Name) + "." + ((IdentDotExpression) s).Member, s, sb); else if (s is IdentExpression) GenerateIdentifier(((IdentExpression) s).Name, s, sb); else if (s is IDENT) Generate(CheckName(((TOKEN) s).yytext), s, sb); else { foreach (SYMBOL kid in s.kids) GenerateNodeToSB(s, kid,sb); } return; } /// /// Generates the code for a GlobalFunctionDefinition node. /// /// The GlobalFunctionDefinition node. /// String containing C# code for GlobalFunctionDefinition gf. private void GenerateGlobalFunctionDefinition(GlobalFunctionDefinition gf, StringBuilder sb) { // we need to separate the argument declaration list from other kids List argumentDeclarationListKids = new List(); List remainingKids = new List(); foreach (SYMBOL kid in gf.kids) if (kid is ArgumentDeclarationList) argumentDeclarationListKids.Add(kid); else remainingKids.Add(kid); GenerateIndented(String.Format("{0} {1}(", gf.ReturnType, CheckName(gf.Name)), gf, sb); // print the state arguments, if any foreach (SYMBOL kid in argumentDeclarationListKids) GenerateArgumentDeclarationList((ArgumentDeclarationList) kid, sb); GenerateLine(")", sb); foreach (SYMBOL kid in remainingKids) GenerateNodeToSB(gf, kid,sb); } /// /// Generates the code for a GlobalVariableDeclaration node. /// /// The GlobalVariableDeclaration node. /// String containing C# code for GlobalVariableDeclaration gv. private void GenerateGlobalVariableDeclaration(GlobalVariableDeclaration gv, StringBuilder sb) { foreach (SYMBOL s in gv.kids) { Indent(sb); GenerateNodeToSB(gv, s ,sb); GenerateLine(";", sb); } } /// /// Generates the code for a State node. /// /// The State node. /// String containing C# code for State s. private void GenerateState(State s, StringBuilder sb) { foreach (SYMBOL kid in s.kids) if (kid is StateEvent) GenerateStateEvent((StateEvent) kid, s.Name, sb); } /// /// Generates the code for a StateEvent node. /// /// The StateEvent node. /// The name of the parent state. /// String containing C# code for StateEvent se. private void GenerateStateEvent(StateEvent se, string parentStateName, StringBuilder sb) { // we need to separate the argument declaration list from other kids List argumentDeclarationListKids = new List(); List remainingKids = new List(); foreach (SYMBOL kid in se.kids) if (kid is ArgumentDeclarationList) argumentDeclarationListKids.Add(kid); else remainingKids.Add(kid); // "state" (function) declaration GenerateIndented(String.Format("public void {0}_event_{1}(", parentStateName, se.Name), se , sb); // print the state arguments, if any foreach (SYMBOL kid in argumentDeclarationListKids) GenerateArgumentDeclarationList((ArgumentDeclarationList) kid, sb); GenerateLine(")", sb); foreach (SYMBOL kid in remainingKids) GenerateNodeToSB(se, kid, sb); } /// /// Generates the code for an ArgumentDeclarationList node. /// /// The ArgumentDeclarationList node. /// String containing C# code for ArgumentDeclarationList adl. private void GenerateArgumentDeclarationList(ArgumentDeclarationList adl, StringBuilder sb) { int comma = adl.kids.Count - 1; // tells us whether to print a comma foreach (Declaration d in adl.kids) { Generate(String.Format("{0} {1}", d.Datatype, CheckName(d.Id)), d, sb); if (0 < comma--) Generate(", ", sb); } } /// /// Generates the code for an ArgumentList node. /// /// The ArgumentList node. /// String containing C# code for ArgumentList al. private void GenerateArgumentList(ArgumentList al, StringBuilder sb) { int comma = al.kids.Count - 1; // tells us whether to print a comma foreach (SYMBOL s in al.kids) { GenerateNodeToSB(al, s, sb); if (0 < comma--) Generate(", ", sb); } } /// /// Generates the code for a CompoundStatement node. /// /// The CompoundStatement node. /// String containing C# code for CompoundStatement cs. private void GenerateCompoundStatement(SYMBOL previousSymbol, CompoundStatement cs, StringBuilder sb) { // opening brace GenerateIndentedLine("{", sb); m_braceCount++; if (m_insertCoopTerminationChecks) { // We have to check in event functions as well because the user can manually call these. if (previousSymbol is GlobalFunctionDefinition || previousSymbol is WhileStatement || previousSymbol is DoWhileStatement || previousSymbol is ForLoop || previousSymbol is StateEvent) GenerateIndentedLine(m_coopTerminationCheck, sb); } foreach (SYMBOL kid in cs.kids) GenerateNodeToSB(cs, kid, sb); // closing brace m_braceCount--; GenerateIndentedLine("}", sb); } /// /// Generates the code for a Declaration node. /// /// The Declaration node. /// String containing C# code for Declaration d. private void GenerateDeclaration(Declaration d, StringBuilder sb) { Generate(String.Format("{0} {1}", d.Datatype, CheckName(d.Id)), d, sb); } /// /// Generates the code for a Statement node. /// /// The Statement node. /// String containing C# code for Statement s. private void GenerateStatement(SYMBOL previousSymbol, Statement s, StringBuilder sb) { string retstr = String.Empty; bool printSemicolon = true; bool transformToBlock = false; if (m_insertCoopTerminationChecks) { // A non-braced single line do while structure cannot contain multiple statements. // So to insert the termination check we change this to a braced control structure instead. if (previousSymbol is WhileStatement || previousSymbol is DoWhileStatement || previousSymbol is ForLoop) { transformToBlock = true; // FIXME: This will be wrongly indented because the previous for/while/dowhile will have already indented. GenerateIndentedLine("{", sb); GenerateIndentedLine(m_coopTerminationCheck, sb); } } Indent(sb); if (0 < s.kids.Count) { // Jump label prints its own colon, we don't need a semicolon. printSemicolon = !(s.kids.Top is JumpLabel); // If we encounter a lone Ident, we skip it, since that's a C# // (MONO) error. if (!(s.kids.Top is IdentExpression && 1 == s.kids.Count)) foreach (SYMBOL kid in s.kids) GenerateNodeToSB(s, kid, sb); } if (printSemicolon) GenerateLine(";", sb); if (transformToBlock) { // FIXME: This will be wrongly indented because the for/while/dowhile is currently handling the unindent GenerateIndentedLine("}", sb); } } /// /// Generates the code for an Assignment node. /// /// The Assignment node. /// String containing C# code for Assignment a. private void GenerateAssignment(Assignment a, StringBuilder sb) { List identifiers = new List(); checkForMultipleAssignments(identifiers, a); GenerateNodeToSB(a, (SYMBOL) a.kids.Pop(), sb); Generate(String.Format(" {0} ", a.AssignmentType), a, sb); foreach (SYMBOL kid in a.kids) GenerateNodeToSB(a, kid, sb); } // This code checks for LSL of the following forms, and generates a // warning if it finds them. // // list l = [ "foo" ]; // l = (l=[]) + l + ["bar"]; // (produces l=["foo","bar"] in SL but l=["bar"] in OS) // // integer i; // integer j; // i = (j = 3) + (j = 4) + (j = 5); // (produces j=3 in SL but j=5 in OS) // // Without this check, that code passes compilation, but does not do what // the end user expects, because LSL in SL evaluates right to left instead // of left to right. // // The theory here is that producing an error and alerting the end user that // something needs to change is better than silently generating incorrect code. private void checkForMultipleAssignments(List identifiers, SYMBOL s) { if (s is Assignment) { Assignment a = (Assignment)s; string newident = null; if (a.kids[0] is Declaration) { newident = ((Declaration)a.kids[0]).Id; } else if (a.kids[0] is IDENT) { newident = ((IDENT)a.kids[0]).yytext; } else if (a.kids[0] is IdentDotExpression) { newident = ((IdentDotExpression)a.kids[0]).Name; // +"." + ((IdentDotExpression)a.kids[0]).Member; } else { 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)); } if (identifiers.Contains(newident)) { 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)); } identifiers.Add(newident); } int index; for (index = 0; index < s.kids.Count; index++) { checkForMultipleAssignments(identifiers, (SYMBOL) s.kids[index]); } } /// /// Generates the code for a ReturnStatement node. /// /// The ReturnStatement node. /// String containing C# code for ReturnStatement rs. private void GenerateReturnStatement(ReturnStatement rs, StringBuilder sb) { Generate("return ", rs, sb); foreach (SYMBOL kid in rs.kids) GenerateNodeToSB(rs, kid, sb); } /// /// Generates the code for a JumpLabel node. /// /// The JumpLabel node. /// String containing C# code for JumpLabel jl. private void GenerateJumpLabel(JumpLabel jl, StringBuilder sb) { string labelStatement; if (m_insertCoopTerminationChecks) labelStatement = m_coopTerminationCheck; else labelStatement = "NoOp();"; GenerateLine(String.Format("{0}: {1}", CheckName(jl.LabelName), labelStatement), jl, sb); } /// /// Generates the code for a JumpStatement node. /// /// The JumpStatement node. /// String containing C# code for JumpStatement js. private void GenerateJumpStatement(JumpStatement js, StringBuilder sb) { Generate(String.Format("goto {0}", CheckName(js.TargetName)), js, sb); } /// /// Generates the code for an IfStatement node. /// /// The IfStatement node. /// String containing C# code for IfStatement ifs. private void GenerateIfStatement(IfStatement ifs, StringBuilder sb) { GenerateIndented("if (", ifs, sb); GenerateNodeToSB(ifs, (SYMBOL) ifs.kids.Pop(), sb); GenerateLine(")", sb); // CompoundStatement handles indentation itself but we need to do it // otherwise. bool indentHere = ifs.kids.Top is Statement; if (indentHere) m_braceCount++; GenerateNodeToSB(ifs, (SYMBOL) ifs.kids.Pop(), sb); if (indentHere) m_braceCount--; if (0 < ifs.kids.Count) // do it again for an else { GenerateIndentedLine("else", ifs, sb); indentHere = ifs.kids.Top is Statement; if (indentHere) m_braceCount++; GenerateNodeToSB(ifs, (SYMBOL) ifs.kids.Pop(), sb); if (indentHere) m_braceCount--; } } /// /// Generates the code for a StateChange node. /// /// The StateChange node. /// String containing C# code for StateChange sc. private void GenerateStateChange(StateChange sc, StringBuilder sb) { Generate(String.Format("state(\"{0}\")", sc.NewState), sc, sb); } /// /// Generates the code for a WhileStatement node. /// /// The WhileStatement node. /// String containing C# code for WhileStatement ws. private void GenerateWhileStatement(WhileStatement ws, StringBuilder sb) { GenerateIndented("while (", ws, sb); GenerateNodeToSB(ws, (SYMBOL) ws.kids.Pop(), sb); GenerateLine(")", sb); // CompoundStatement handles indentation itself but we need to do it // otherwise. bool indentHere = ws.kids.Top is Statement; if (indentHere) m_braceCount++; GenerateNodeToSB(ws, (SYMBOL) ws.kids.Pop(), sb); if (indentHere) m_braceCount--; } /// /// Generates the code for a DoWhileStatement node. /// /// The DoWhileStatement node. /// String containing C# code for DoWhileStatement dws. private void GenerateDoWhileStatement(DoWhileStatement dws, StringBuilder sb) { GenerateIndentedLine("do", dws, sb); // CompoundStatement handles indentation itself but we need to do it // otherwise. bool indentHere = dws.kids.Top is Statement; if (indentHere) m_braceCount++; GenerateNodeToSB(dws, (SYMBOL) dws.kids.Pop(), sb); if (indentHere) m_braceCount--; GenerateIndented("while (", dws ,sb); GenerateNodeToSB(dws, (SYMBOL) dws.kids.Pop(), sb); GenerateLine(");", sb); } /// /// Generates the code for a ForLoop node. /// /// The ForLoop node. /// String containing C# code for ForLoop fl. private void GenerateForLoop(ForLoop fl, StringBuilder sb) { GenerateIndented("for (", fl, sb); // It's possible that we don't have an assignment, in which case // the child will be null and we only print the semicolon. // for (x = 0; x < 10; x++) // ^^^^^ ForLoopStatement s = (ForLoopStatement) fl.kids.Pop(); if (null != s) { GenerateForLoopStatement(s, sb); } Generate("; ", sb); // for (x = 0; x < 10; x++) // ^^^^^^ GenerateNodeToSB(fl, (SYMBOL) fl.kids.Pop(), sb); Generate("; ", sb); // for (x = 0; x < 10; x++) // ^^^ GenerateForLoopStatement((ForLoopStatement) fl.kids.Pop(), sb); GenerateLine(")", sb); // CompoundStatement handles indentation itself but we need to do it // otherwise. bool indentHere = fl.kids.Top is Statement; if (indentHere) m_braceCount++; GenerateNodeToSB(fl, (SYMBOL) fl.kids.Pop(), sb); if (indentHere) m_braceCount--; } /// /// Generates the code for a ForLoopStatement node. /// /// The ForLoopStatement node. /// String containing C# code for ForLoopStatement fls. private void GenerateForLoopStatement(ForLoopStatement fls, StringBuilder sb) { int comma = fls.kids.Count - 1; // tells us whether to print a comma // It's possible that all we have is an empty Ident, for example: // // for (x; x < 10; x++) { ... } // // Which is illegal in C# (MONO). We'll skip it. if (fls.kids.Top is IdentExpression && 1 == fls.kids.Count) return; for (int i = 0; i < fls.kids.Count; i++) { SYMBOL s = (SYMBOL)fls.kids[i]; // Statements surrounded by parentheses in for loops // // e.g. for ((i = 0), (j = 7); (i < 10); (++i)) // // are legal in LSL but not in C# so we need to discard the parentheses // // The following, however, does not appear to be legal in LLS // // for ((i = 0, j = 7); (i < 10); (++i)) // // As of Friday 20th November 2009, the Linden Lab simulators appear simply never to compile or run this // script but with no debug or warnings at all! Therefore, we won't deal with this yet (which looks // like it would be considerably more complicated to handle). while (s is ParenthesisExpression) s = (SYMBOL)s.kids.Pop(); GenerateNodeToSB(fls, s, sb); if (0 < comma--) Generate(", ", sb); } } /// /// Generates the code for a BinaryExpression node. /// /// The BinaryExpression node. /// String containing C# code for BinaryExpression be. private void GenerateBinaryExpression(BinaryExpression be, StringBuilder sb) { if (be.ExpressionSymbol.Equals("&&") || be.ExpressionSymbol.Equals("||")) { // special case handling for logical and/or, see Mantis 3174 sb.Append("((bool)("); GenerateNodeToSB(be, (SYMBOL)be.kids.Pop(), sb); sb.Append("))"); Generate(String.Format(" {0} ", be.ExpressionSymbol.Substring(0,1)), be, sb); sb.Append("((bool)("); foreach (SYMBOL kid in be.kids) GenerateNodeToSB(be, kid, sb); sb.Append("))"); } else { GenerateNodeToSB(be, (SYMBOL)be.kids.Pop(), sb); Generate(String.Format(" {0} ", be.ExpressionSymbol), be, sb); foreach (SYMBOL kid in be.kids) GenerateNodeToSB(be, kid, sb); } } /// /// Generates the code for a UnaryExpression node. /// /// The UnaryExpression node. /// String containing C# code for UnaryExpression ue. private void GenerateUnaryExpression(UnaryExpression ue, StringBuilder sb) { Generate(ue.UnarySymbol, ue, sb); GenerateNodeToSB(ue, (SYMBOL) ue.kids.Pop(), sb); } /// /// Generates the code for a ParenthesisExpression node. /// /// The ParenthesisExpression node. /// String containing C# code for ParenthesisExpression pe. private void GenerateParenthesisExpression(ParenthesisExpression pe, StringBuilder sb) { string retstr = String.Empty; Generate("(", sb); foreach (SYMBOL kid in pe.kids) GenerateNodeToSB(pe, kid, sb); Generate(")", sb); } /// /// Generates the code for a IncrementDecrementExpression node. /// /// The IncrementDecrementExpression node. /// String containing C# code for IncrementDecrementExpression ide. private void GenerateIncrementDecrementExpression(IncrementDecrementExpression ide, StringBuilder sb) { if (0 < ide.kids.Count) { IdentDotExpression dot = (IdentDotExpression) ide.kids.Top; Generate(String.Format("{0}", ide.PostOperation ? CheckName(dot.Name) + "." + dot.Member + ide.Operation : ide.Operation + CheckName(dot.Name) + "." + dot.Member), ide, sb); } else Generate(String.Format("{0}", ide.PostOperation ? CheckName(ide.Name) + ide.Operation : ide.Operation + CheckName(ide.Name)), ide, sb); } /// /// Generates the code for a TypecastExpression node. /// /// The TypecastExpression node. /// String containing C# code for TypecastExpression te. private void GenerateTypecastExpression(TypecastExpression te, StringBuilder sb) { // we wrap all typecasted statements in parentheses Generate(String.Format("({0}) (", te.TypecastType), te, sb); GenerateNodeToSB(te, (SYMBOL) te.kids.Pop(), sb); Generate(")", sb); } /// /// Generates the code for an identifier /// /// The symbol name /// The Symbol node. /// String containing C# code for identifier reference. private void GenerateIdentifier(string id, SYMBOL s, StringBuilder sb) { if (m_comms != null) { object value = m_comms.LookupModConstant(id); if (value != null) { string retval = null; if (value is int) retval = String.Format("new LSL_Types.LSLInteger({0})",((int)value).ToString()); else if (value is float) retval = String.Format("new LSL_Types.LSLFloat({0})",((float)value).ToString()); else if (value is string) retval = String.Format("new LSL_Types.LSLString(\"{0}\")",((string)value)); else if (value is OpenMetaverse.UUID) retval = String.Format("new LSL_Types.key(\"{0}\")",((OpenMetaverse.UUID)value).ToString()); else if (value is OpenMetaverse.Vector3) retval = String.Format("new LSL_Types.Vector3(\"{0}\")",((OpenMetaverse.Vector3)value).ToString()); else if (value is OpenMetaverse.Quaternion) retval = String.Format("new LSL_Types.Quaternion(\"{0}\")",((OpenMetaverse.Quaternion)value).ToString()); else retval = id; Generate(retval, s, sb); return; } } Generate(CheckName(id), s, sb); return; } /// /// Generates the code for a FunctionCall node. /// /// The FunctionCall node. /// String containing C# code for FunctionCall fc. private void GenerateFunctionCall(FunctionCall fc, StringBuilder sb) { string modinvoke = null; if (m_comms != null) modinvoke = m_comms.LookupModInvocation(fc.Id); if (modinvoke != null) { if (fc.kids[0] is ArgumentList) { if ((fc.kids[0] as ArgumentList).kids.Count == 0) Generate(String.Format("{0}(\"{1}\"",modinvoke,fc.Id), fc, sb); else Generate(String.Format("{0}(\"{1}\",",modinvoke,fc.Id), fc, sb); } } else { Generate(String.Format("{0}(", CheckName(fc.Id)), fc, sb); } foreach (SYMBOL kid in fc.kids) GenerateNodeToSB(fc, kid, sb); Generate(")", sb); } /// /// Generates the code for a Constant node. /// /// The Constant node. /// String containing C# code for Constant c. private void GenerateConstant(Constant c, StringBuilder sb) { // Supprt LSL's weird acceptance of floats with no trailing digits // after the period. Turn float x = 10.; into float x = 10.0; if ("LSL_Types.LSLFloat" == c.Type) { int dotIndex = c.Value.IndexOf('.') + 1; if (0 < dotIndex && (dotIndex == c.Value.Length || !Char.IsDigit(c.Value[dotIndex]))) c.Value = c.Value.Insert(dotIndex, "0"); c.Value = "new LSL_Types.LSLFloat("+c.Value+")"; } else if ("LSL_Types.LSLInteger" == c.Type) { c.Value = "new LSL_Types.LSLInteger("+c.Value+")"; } else if ("LSL_Types.LSLString" == c.Type) { c.Value = "new LSL_Types.LSLString(\""+c.Value+"\")"; } Generate(c.Value, c, sb); } /// /// Generates the code for a VectorConstant node. /// /// The VectorConstant node. /// String containing C# code for VectorConstant vc. private void GenerateVectorConstant(VectorConstant vc, StringBuilder sb) { Generate(String.Format("new {0}(", vc.Type), vc, sb); GenerateNodeToSB(vc, (SYMBOL) vc.kids.Pop(), sb); Generate(", ", sb); GenerateNodeToSB(vc, (SYMBOL) vc.kids.Pop(), sb); Generate(", ", sb); GenerateNodeToSB(vc, (SYMBOL) vc.kids.Pop(), sb); Generate(")", sb); } /// /// Generates the code for a RotationConstant node. /// /// The RotationConstant node. /// String containing C# code for RotationConstant rc. private void GenerateRotationConstant(RotationConstant rc, StringBuilder sb) { Generate(String.Format("new {0}(", rc.Type), rc, sb); GenerateNodeToSB(rc, (SYMBOL) rc.kids.Pop(), sb); Generate(", ", sb); GenerateNodeToSB(rc, (SYMBOL) rc.kids.Pop(), sb); Generate(", ", sb); GenerateNodeToSB(rc, (SYMBOL) rc.kids.Pop(), sb); Generate(", ", sb); GenerateNodeToSB(rc, (SYMBOL) rc.kids.Pop(), sb); Generate(")", sb); } /// /// Generates the code for a ListConstant node. /// /// The ListConstant node. /// String containing C# code for ListConstant lc. private void GenerateListConstant(ListConstant lc, StringBuilder sb) { Generate(String.Format("new {0}(", lc.Type), lc, sb); foreach (SYMBOL kid in lc.kids) GenerateNodeToSB(lc, kid, sb); Generate(")", sb); } /// /// Prints a newline. /// /// A newline. private void GenerateLine(StringBuilder sb) { sb.Append("\n"); } /// /// Prints text, followed by a newline. /// /// String of text to print. /// String s followed by newline. private void GenerateLine(string s, StringBuilder sb) { sb.Append(s); sb.Append("\n"); } /// /// Prints text, followed by a newline. /// /// String of text to print. /// Symbol being generated to extract original line /// number and column from. /// String s followed by newline. private void GenerateLine(string s, SYMBOL sym, StringBuilder sb) { Generate(s, sym, sb); sb.Append("\n"); m_CSharpLine++; m_CSharpCol = 1; } /// /// Prints text. /// /// String of text to print. /// String s. private void Generate(string s, StringBuilder sb) { sb.Append(s); m_CSharpCol += s.Length; } /// /// Prints text. /// /// String of text to print. /// Symbol being generated to extract original line /// number and column from. /// String s. private void Generate(string s, SYMBOL sym, StringBuilder sb) { sb.Append(s); if (null != sym) m_positionMap.Add(new KeyValuePair(m_CSharpLine, m_CSharpCol), new KeyValuePair(sym.Line, sym.Position)); m_CSharpCol += s.Length; } /// /// Prints text correctly indented, followed by a newline. /// /// String of text to print. /// Properly indented string s followed by newline. private void GenerateIndentedLine(string s, StringBuilder sb) { GenerateIndentedLine(s, null, sb); } /// /// Prints text correctly indented, followed by a newline. /// /// String of text to print. /// Symbol being generated to extract original line /// number and column from. /// Properly indented string s followed by newline. private void GenerateIndentedLine(string s, SYMBOL sym, StringBuilder sb) { GenerateIndented(s, sym , sb ); sb.Append("\n"); m_CSharpLine++; m_CSharpCol = 1; } /// /// Prints text correctly indented. /// /// String of text to print. /// Properly indented string s. //private string GenerateIndented(string s) //{ // return GenerateIndented(s, null); //} // THIS FUNCTION IS COMMENTED OUT TO SUPPRESS WARNINGS /// /// Prints text correctly indented. /// /// String of text to print. /// Symbol being generated to extract original line /// number and column from. /// Properly indented string s. private void GenerateIndented(string s, SYMBOL sym, StringBuilder sb) { Indent(sb); sb.Append(s); if (null != sym) m_positionMap.Add(new KeyValuePair(m_CSharpLine, m_CSharpCol), new KeyValuePair(sym.Line, sym.Position)); m_CSharpCol += s.Length; } /// /// Prints correct indentation. /// /// Indentation based on brace count. private void Indent(StringBuilder sb) { sb.Append(" "); } /// /// Returns the passed name with an underscore prepended if that name is a reserved word in C# /// and not resevered in LSL otherwise it just returns the passed name. /// /// This makes no attempt to cache the results to minimise future lookups. For a non trivial /// scripts the number of unique identifiers could easily grow to the size of the reserved word /// list so maintaining a list or dictionary and doing the lookup there firstwould probably not /// give any real speed advantage. /// /// I believe there is a class Microsoft.CSharp.CSharpCodeProvider that has a function /// CreateValidIdentifier(str) that will return either the value of str if it is not a C# /// key word or "_"+str if it is. But availability under Mono? /// private string CheckName(string s) { if (CSReservedWords.IsReservedWord(s)) return "@" + s; else return s; } } }