/* * 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 OpenSim 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.IO; using System.Collections.Generic; using Tools; namespace OpenSim.Region.ScriptEngine.Shared.CodeTools { public class CSCodeGenerator { private SYMBOL m_astRoot = null; private int m_braceCount; // for indentation /// /// Pass the new CodeGenerator a string containing the LSL source. /// /// String containing LSL source. public CSCodeGenerator(string script) { Parser p = new LSLSyntax(new yyLSLSyntax(), new ErrorHandler(true)); // Obviously this needs to be in a try/except block. LSL2CSCodeTransformer codeTransformer = new LSL2CSCodeTransformer(p.Parse(script)); m_astRoot = codeTransformer.Transform(); } /// /// Pass the new CodeGenerator an abstract syntax tree. /// /// The root node of the AST. public CSCodeGenerator(SYMBOL astRoot) { m_braceCount = 0; m_astRoot = astRoot; } /// /// Generate the code from the AST we have. /// /// String containing the generated C# code. public string Generate() { string retstr = String.Empty; // standard preamble //retstr = "using OpenSim.Region.ScriptEngine.Common;\n"; //retstr += "using System.Collections.Generic;\n\n"; //retstr += "namespace SecondLife\n"; //retstr += "{\n"; //retstr += " public class Script : OpenSim.Region.ScriptEngine.Common\n"; //retstr += " {\n"; // here's the payload m_braceCount += 2; retstr += "\n"; foreach (SYMBOL s in m_astRoot.kids) retstr += GenerateNode(s); // close braces! //retstr += " }\n"; //retstr += "}\n"; m_braceCount -= 2; return retstr; } /// /// Recursively called to generate each type of node. Will generate this /// node, then all it's children. /// /// The current node to generate code for. /// String containing C# code for SYMBOL s. private string GenerateNode(SYMBOL s) { string retstr = String.Empty; // 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) retstr += GenerateGlobalFunctionDefinition((GlobalFunctionDefinition) s); else if (s is GlobalVariableDeclaration) retstr += GenerateGlobalVariableDeclaration((GlobalVariableDeclaration) s); else if (s is State) retstr += GenerateState((State) s); else if (s is CompoundStatement) retstr += GenerateCompoundStatement((CompoundStatement) s); else if (s is Declaration) retstr += GenerateDeclaration((Declaration) s); else if (s is Statement) retstr += GenerateStatement((Statement) s); else if (s is ReturnStatement) retstr += GenerateReturnStatement((ReturnStatement) s); else if (s is JumpLabel) retstr += GenerateJumpLabel((JumpLabel) s); else if (s is JumpStatement) retstr += GenerateJumpStatement((JumpStatement) s); else if (s is StateChange) retstr += GenerateStateChange((StateChange) s); else if (s is IfStatement) retstr += GenerateIfStatement((IfStatement) s); else if (s is WhileStatement) retstr += GenerateWhileStatement((WhileStatement) s); else if (s is DoWhileStatement) retstr += GenerateDoWhileStatement((DoWhileStatement) s); else if (s is ForLoop) retstr += GenerateForLoop((ForLoop) s); else if (s is ArgumentList) retstr += GenerateArgumentList((ArgumentList) s); else if (s is Assignment) retstr += GenerateAssignment((Assignment) s); else if (s is BinaryExpression) retstr += GenerateBinaryExpression((BinaryExpression) s); else if (s is ParenthesisExpression) retstr += GenerateParenthesisExpression((ParenthesisExpression) s); else if (s is UnaryExpression) retstr += GenerateUnaryExpression((UnaryExpression) s); else if (s is IncrementDecrementExpression) retstr += GenerateIncrementDecrementExpression((IncrementDecrementExpression) s); else if (s is TypecastExpression) retstr += GenerateTypecastExpression((TypecastExpression) s); else if (s is FunctionCall) retstr += GenerateFunctionCall((FunctionCall) s); else if (s is VectorConstant) retstr += GenerateVectorConstant((VectorConstant) s); else if (s is RotationConstant) retstr += GenerateRotationConstant((RotationConstant) s); else if (s is ListConstant) retstr += GenerateListConstant((ListConstant) s); else if (s is Constant) retstr += GenerateConstant((Constant) s); else if (s is IdentDotExpression) retstr += ((IdentDotExpression) s).Name + "." + ((IdentDotExpression) s).Member; else if (s is IdentExpression) retstr += ((IdentExpression) s).Name; else if (s is IDENT) retstr += ((TOKEN) s).yytext; else { foreach (SYMBOL kid in s.kids) retstr += GenerateNode(kid); } return retstr; } /// /// Generates the code for a GlobalFunctionDefinition node. /// /// The GlobalFunctionDefinition node. /// String containing C# code for GlobalFunctionDefinition gf. private string GenerateGlobalFunctionDefinition(GlobalFunctionDefinition gf) { string retstr = String.Empty; // 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); retstr += WriteIndented(String.Format("{0} {1}(", gf.ReturnType, gf.Name)); // print the state arguments, if any foreach (SYMBOL kid in argumentDeclarationListKids) retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid); retstr += ")\n"; foreach (SYMBOL kid in remainingKids) retstr += GenerateNode(kid); return retstr; } /// /// Generates the code for a GlobalVariableDeclaration node. /// /// The GlobalVariableDeclaration node. /// String containing C# code for GlobalVariableDeclaration gv. private string GenerateGlobalVariableDeclaration(GlobalVariableDeclaration gv) { string retstr = String.Empty; foreach (SYMBOL s in gv.kids) { retstr += Indent(); retstr += GenerateNode(s); retstr += ";\n"; } return retstr; } /// /// Generates the code for a State node. /// /// The State node. /// String containing C# code for State s. private string GenerateState(State s) { string retstr = String.Empty; foreach (SYMBOL kid in s.kids) if (kid is StateEvent) retstr += GenerateStateEvent((StateEvent) kid, s.Name); else retstr += String.Format("ERROR: State '{0}' contains a '{1}\n", s.Name, kid.GetType()); return retstr; } /// /// Generates the code for a StateEvent node. /// /// The StateEvent node. /// The name of the parent state. /// String containing C# code for StateEvent se. private string GenerateStateEvent(StateEvent se, string parentStateName) { string retstr = String.Empty; // 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 retstr += WriteIndented(String.Format("public void {0}_event_{1}(", parentStateName, se.Name)); // print the state arguments, if any foreach (SYMBOL kid in argumentDeclarationListKids) retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid); retstr += ")\n"; foreach (SYMBOL kid in remainingKids) retstr += GenerateNode(kid); return retstr; } /// /// Generates the code for an ArgumentDeclarationList node. /// /// The ArgumentDeclarationList node. /// String containing C# code for SYMBOL s. private string GenerateArgumentDeclarationList(ArgumentDeclarationList adl) { string retstr = String.Empty; int comma = adl.kids.Count - 1; // tells us whether to print a comma foreach (Declaration d in adl.kids) { retstr += String.Format("{0} {1}", d.Datatype, d.Id); if (0 < comma--) retstr += ", "; } return retstr; } /// /// Generates the code for an ArgumentList node. /// /// The ArgumentList node. /// String containing C# code for SYMBOL s. private string GenerateArgumentList(ArgumentList al) { string retstr = String.Empty; int comma = al.kids.Count - 1; // tells us whether to print a comma foreach (SYMBOL s in al.kids) { retstr += GenerateNode(s); if (0 < comma--) retstr += ", "; } return retstr; } /// /// Generates the code for a CompoundStatement node. /// /// The CompoundStatement node. /// String containing C# code for SYMBOL s. private string GenerateCompoundStatement(CompoundStatement cs) { string retstr = String.Empty; // opening brace retstr += WriteIndentedLine("{"); m_braceCount++; foreach (SYMBOL kid in cs.kids) retstr += GenerateNode(kid); // closing brace m_braceCount--; retstr += WriteIndentedLine("}"); return retstr; } /// /// Generates the code for a Declaration node. /// /// The Declaration node. /// String containing C# code for SYMBOL s. private string GenerateDeclaration(Declaration d) { return String.Format("{0} {1}", d.Datatype, d.Id); } /// /// Generates the code for a Statement node. /// /// The Statement node. /// String containing C# code for SYMBOL s. private string GenerateStatement(Statement s) { string retstr = String.Empty; // Jump label prints its own colon, we don't need a semicolon. bool printSemicolon = !(s.kids.Top is JumpLabel); retstr += Indent(); foreach (SYMBOL kid in s.kids) retstr += GenerateNode(kid); if (printSemicolon) retstr += ";\n"; return retstr; } /// /// Generates the code for an Assignment node. /// /// The Assignment node. /// String containing C# code for SYMBOL s. private string GenerateAssignment(Assignment a) { string retstr = String.Empty; retstr += GenerateNode((SYMBOL) a.kids.Pop()); retstr +=String.Format(" {0} ", a.AssignmentType); foreach (SYMBOL kid in a.kids) retstr += GenerateNode(kid); return retstr; } /// /// Generates the code for a ReturnStatement node. /// /// The ReturnStatement node. /// String containing C# code for SYMBOL s. private string GenerateReturnStatement(ReturnStatement rs) { string retstr = String.Empty; retstr += "return "; foreach (SYMBOL kid in rs.kids) retstr += GenerateNode(kid); return retstr; } /// /// Generates the code for a JumpLabel node. /// /// The JumpLabel node. /// String containing C# code for SYMBOL s. private string GenerateJumpLabel(JumpLabel jl) { return String.Format("{0}:\n", jl.LabelName); } /// /// Generates the code for a JumpStatement node. /// /// The JumpStatement node. /// String containing C# code for SYMBOL s. private string GenerateJumpStatement(JumpStatement js) { return String.Format("goto {0}", js.TargetName); } /// /// Generates the code for a IfStatement node. /// /// The IfStatement node. /// String containing C# code for SYMBOL s. private string GenerateIfStatement(IfStatement ifs) { string retstr = String.Empty; retstr += WriteIndented("if ("); retstr += GenerateNode((SYMBOL) ifs.kids.Pop()); retstr += ")\n"; // CompoundStatement handles indentation itself but we need to do it // otherwise. bool indentHere = ifs.kids.Top is Statement; if (indentHere) m_braceCount++; retstr += GenerateNode((SYMBOL) ifs.kids.Pop()); if (indentHere) m_braceCount--; if (0 < ifs.kids.Count) // do it again for an else { retstr += WriteIndentedLine("else"); indentHere = ifs.kids.Top is Statement; if (indentHere) m_braceCount++; retstr += GenerateNode((SYMBOL) ifs.kids.Pop()); if (indentHere) m_braceCount--; } return retstr; } /// /// Generates the code for a StateChange node. /// /// The StateChange node. /// String containing C# code for SYMBOL s. private string GenerateStateChange(StateChange sc) { return String.Format("state(\"{0}\")", sc.NewState); } /// /// Generates the code for a WhileStatement node. /// /// The WhileStatement node. /// String containing C# code for SYMBOL s. private string GenerateWhileStatement(WhileStatement ws) { string retstr = String.Empty; retstr += WriteIndented("while ("); retstr += GenerateNode((SYMBOL) ws.kids.Pop()); retstr += ")\n"; // CompoundStatement handles indentation itself but we need to do it // otherwise. bool indentHere = ws.kids.Top is Statement; if (indentHere) m_braceCount++; retstr += GenerateNode((SYMBOL) ws.kids.Pop()); if (indentHere) m_braceCount--; return retstr; } /// /// Generates the code for a DoWhileStatement node. /// /// The DoWhileStatement node. /// String containing C# code for SYMBOL s. private string GenerateDoWhileStatement(DoWhileStatement dws) { string retstr = String.Empty; retstr += WriteIndentedLine("do"); // CompoundStatement handles indentation itself but we need to do it // otherwise. bool indentHere = dws.kids.Top is Statement; if (indentHere) m_braceCount++; retstr += GenerateNode((SYMBOL) dws.kids.Pop()); if (indentHere) m_braceCount--; retstr += WriteIndented("while ("); retstr += GenerateNode((SYMBOL) dws.kids.Pop()); retstr += ");\n"; return retstr; } /// /// Generates the code for a ForLoop node. /// /// The ForLoop node. /// String containing C# code for SYMBOL s. private string GenerateForLoop(ForLoop fl) { string retstr = String.Empty; retstr += WriteIndented("for ("); // for ( x = 0 ; x < 10 ; x++ ) // ^^^^^^^ retstr += GenerateForLoopStatement((ForLoopStatement) fl.kids.Pop()); retstr += "; "; // for ( x = 0 ; x < 10 ; x++ ) // ^^^^^^^^ retstr += GenerateNode((SYMBOL) fl.kids.Pop()); retstr += "; "; // for ( x = 0 ; x < 10 ; x++ ) // ^^^^^ retstr += GenerateForLoopStatement((ForLoopStatement) fl.kids.Pop()); retstr += ")\n"; // CompoundStatement handles indentation itself but we need to do it // otherwise. bool indentHere = fl.kids.Top is Statement; if (indentHere) m_braceCount++; retstr += GenerateNode((SYMBOL) fl.kids.Pop()); if (indentHere) m_braceCount--; return retstr; } /// /// Generates the code for a ForLoopStatement node. /// /// The ForLoopStatement node. /// String containing C# code for SYMBOL s. private string GenerateForLoopStatement(ForLoopStatement fls) { string retstr = String.Empty; int comma = fls.kids.Count - 1; // tells us whether to print a comma foreach (SYMBOL s in fls.kids) { retstr += GenerateNode(s); if (0 < comma--) retstr += ", "; } return retstr; } /// /// Generates the code for a BinaryExpression node. /// /// The BinaryExpression node. /// String containing C# code for SYMBOL s. private string GenerateBinaryExpression(BinaryExpression be) { string retstr = String.Empty; retstr += GenerateNode((SYMBOL) be.kids.Pop()); retstr += String.Format(" {0} ", be.ExpressionSymbol); foreach (SYMBOL kid in be.kids) retstr += GenerateNode(kid); return retstr; } /// /// Generates the code for a UnaryExpression node. /// /// The UnaryExpression node. /// String containing C# code for SYMBOL s. private string GenerateUnaryExpression(UnaryExpression ue) { string retstr = String.Empty; retstr += ue.UnarySymbol; retstr += GenerateNode((SYMBOL) ue.kids.Pop()); return retstr; } /// /// Generates the code for a ParenthesisExpression node. /// /// The ParenthesisExpression node. /// String containing C# code for SYMBOL s. private string GenerateParenthesisExpression(ParenthesisExpression pe) { string retstr = String.Empty; retstr += "("; foreach (SYMBOL kid in pe.kids) retstr += GenerateNode(kid); retstr += ")"; return retstr; } /// /// Generates the code for a IncrementDecrementExpression node. /// /// The IncrementDecrementExpression node. /// String containing C# code for SYMBOL s. private string GenerateIncrementDecrementExpression(IncrementDecrementExpression ide) { string retstr = String.Empty; if (0 < ide.kids.Count) { IdentDotExpression dot = (IdentDotExpression) ide.kids.Top; retstr += String.Format("{0}", ide.PostOperation ? dot.Name + "." + dot.Member + ide.Operation : ide.Operation + dot.Name + "." + dot.Member); } else retstr += String.Format("{0}", ide.PostOperation ? ide.Name + ide.Operation : ide.Operation + ide.Name); return retstr; } /// /// Generates the code for a TypecastExpression node. /// /// The TypecastExpression node. /// String containing C# code for SYMBOL s. private string GenerateTypecastExpression(TypecastExpression te) { string retstr = String.Empty; // we wrap all typecasted statements in parentheses retstr += String.Format("({0}) (", te.TypecastType); retstr += GenerateNode((SYMBOL) te.kids.Pop()); retstr += ")"; return retstr; } /// /// Generates the code for a FunctionCall node. /// /// The FunctionCall node. /// String containing C# code for SYMBOL s. private string GenerateFunctionCall(FunctionCall fc) { string retstr = String.Empty; retstr += String.Format("{0}(", fc.Id); foreach (SYMBOL kid in fc.kids) retstr += GenerateNode(kid); retstr += ")"; return retstr; } /// /// Generates the code for a Constant node. /// /// The Constant node. /// String containing C# code for SYMBOL s. private string GenerateConstant(Constant c) { string retstr = String.Empty; // 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"); } // need to quote strings if ("LSL_Types.LSLString" == c.Type) retstr += "\""; retstr += c.Value; if ("LSL_Types.LSLString" == c.Type) retstr += "\""; return retstr; } /// /// Generates the code for a VectorConstant node. /// /// The VectorConstant node. /// String containing C# code for SYMBOL s. private string GenerateVectorConstant(VectorConstant vc) { string retstr = String.Empty; retstr += String.Format("new {0}(", vc.Type); retstr += GenerateNode((SYMBOL) vc.kids.Pop()); retstr += ", "; retstr += GenerateNode((SYMBOL) vc.kids.Pop()); retstr += ", "; retstr += GenerateNode((SYMBOL) vc.kids.Pop()); retstr += ")"; return retstr; } /// /// Generates the code for a RotationConstant node. /// /// The RotationConstant node. /// String containing C# code for SYMBOL s. private string GenerateRotationConstant(RotationConstant rc) { string retstr = String.Empty; retstr += String.Format("new {0}(", rc.Type); retstr += GenerateNode((SYMBOL) rc.kids.Pop()); retstr += ", "; retstr += GenerateNode((SYMBOL) rc.kids.Pop()); retstr += ", "; retstr += GenerateNode((SYMBOL) rc.kids.Pop()); retstr += ", "; retstr += GenerateNode((SYMBOL) rc.kids.Pop()); retstr += ")"; return retstr; } /// /// Generates the code for a ListConstant node. /// /// The ListConstant node. /// String containing C# code for SYMBOL s. private string GenerateListConstant(ListConstant lc) { string retstr = String.Empty; retstr += String.Format("new {0}(", lc.Type); foreach (SYMBOL kid in lc.kids) retstr += GenerateNode(kid); retstr += ")"; return retstr; } /// /// Prints text correctly indented, followed by a newline. /// /// String of text to print. /// String containing C# code for SYMBOL s. private string WriteIndentedLine(string s) { return WriteIndented(s) + "\n"; } /// /// Prints text correctly indented. /// /// String of text to print. /// String containing C# code for SYMBOL s. private string WriteIndented(string s) { return Indent() + s; } /// /// Prints correct indentation. /// /// String containing C# code for SYMBOL s. private string Indent() { string retstr = String.Empty; for (int i = 0; i < m_braceCount; i++) retstr += " "; return retstr; } } }