12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193 |
- /*
- * 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.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 SYMBOL m_astRoot = null;
- private Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> m_positionMap;
- private int m_indentWidth = 4; // for indentation
- 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<string> m_warnings = new List<string>();
- private IScriptModuleComms m_comms = null;
- /// <summary>
- /// Creates an 'empty' CSCodeGenerator instance.
- /// </summary>
- public CSCodeGenerator()
- {
- m_comms = null;
- ResetCounters();
- }
- public CSCodeGenerator(IScriptModuleComms comms)
- {
- m_comms = comms;
- ResetCounters();
- }
- /// <summary>
- /// Get the mapping between LSL and C# line/column number.
- /// </summary>
- /// <returns>Dictionary\<KeyValuePair\<int, int\>, KeyValuePair\<int, int\>\>.</returns>
- public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> PositionMap
- {
- get { return m_positionMap; }
- }
- /// <summary>
- /// Get the mapping between LSL and C# line/column number.
- /// </summary>
- /// <returns>SYMBOL pointing to root of the abstract syntax tree.</returns>
- public SYMBOL ASTRoot
- {
- get { return m_astRoot; }
- }
- /// <summary>
- /// Resets various counters and metadata.
- /// </summary>
- private void ResetCounters()
- {
- m_braceCount = 0;
- m_CSharpLine = 0;
- m_CSharpCol = 1;
- m_positionMap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
- m_astRoot = null;
- }
- /// <summary>
- /// Generate the code from the AST we have.
- /// </summary>
- /// <param name="script">The LSL source as a string.</param>
- /// <returns>String containing the generated C# code.</returns>
- public string Convert(string script)
- {
- // m_log.DebugFormat("[CS CODE GENERATOR]: Converting to C#\n{0}", script);
- m_warnings.Clear();
- ResetCounters();
- Parser p = new LSLSyntax(new yyLSLSyntax(), new ErrorHandler(true));
- 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();
- string retstr = String.Empty;
- // 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 += 3;
- // here's the payload
- retstr += GenerateLine();
- foreach (SYMBOL s in m_astRoot.kids)
- retstr += GenerateNode(s);
- // 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?
- retstr=retstr.Replace("\r", "");
- return retstr;
- }
- /// <summary>
- /// Get the set of warnings generated during compilation.
- /// </summary>
- /// <returns></returns>
- public string[] GetWarnings()
- {
- return m_warnings.ToArray();
- }
- private void AddWarning(string warning)
- {
- if (!m_warnings.Contains(warning))
- {
- m_warnings.Add(warning);
- }
- }
- /// <summary>
- /// Recursively called to generate each type of node. Will generate this
- /// node, then all it's children.
- /// </summary>
- /// <param name="s">The current node to generate code for.</param>
- /// <returns>String containing C# code for SYMBOL s.</returns>
- 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 += Generate(CheckName(((IdentDotExpression) s).Name) + "." + ((IdentDotExpression) s).Member, s);
- else if (s is IdentExpression)
- retstr += GenerateIdentifier(((IdentExpression) s).Name, s);
- else if (s is IDENT)
- retstr += Generate(CheckName(((TOKEN) s).yytext), s);
- else
- {
- foreach (SYMBOL kid in s.kids)
- retstr += GenerateNode(kid);
- }
- return retstr;
- }
- /// <summary>
- /// Generates the code for a GlobalFunctionDefinition node.
- /// </summary>
- /// <param name="gf">The GlobalFunctionDefinition node.</param>
- /// <returns>String containing C# code for GlobalFunctionDefinition gf.</returns>
- private string GenerateGlobalFunctionDefinition(GlobalFunctionDefinition gf)
- {
- string retstr = String.Empty;
- // we need to separate the argument declaration list from other kids
- List<SYMBOL> argumentDeclarationListKids = new List<SYMBOL>();
- List<SYMBOL> remainingKids = new List<SYMBOL>();
- foreach (SYMBOL kid in gf.kids)
- if (kid is ArgumentDeclarationList)
- argumentDeclarationListKids.Add(kid);
- else
- remainingKids.Add(kid);
- retstr += GenerateIndented(String.Format("{0} {1}(", gf.ReturnType, CheckName(gf.Name)), gf);
- // print the state arguments, if any
- foreach (SYMBOL kid in argumentDeclarationListKids)
- retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid);
- retstr += GenerateLine(")");
- foreach (SYMBOL kid in remainingKids)
- retstr += GenerateNode(kid);
- return retstr;
- }
- /// <summary>
- /// Generates the code for a GlobalVariableDeclaration node.
- /// </summary>
- /// <param name="gv">The GlobalVariableDeclaration node.</param>
- /// <returns>String containing C# code for GlobalVariableDeclaration gv.</returns>
- private string GenerateGlobalVariableDeclaration(GlobalVariableDeclaration gv)
- {
- string retstr = String.Empty;
- foreach (SYMBOL s in gv.kids)
- {
- retstr += Indent();
- retstr += GenerateNode(s);
- retstr += GenerateLine(";");
- }
- return retstr;
- }
- /// <summary>
- /// Generates the code for a State node.
- /// </summary>
- /// <param name="s">The State node.</param>
- /// <returns>String containing C# code for State s.</returns>
- 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);
- return retstr;
- }
- /// <summary>
- /// Generates the code for a StateEvent node.
- /// </summary>
- /// <param name="se">The StateEvent node.</param>
- /// <param name="parentStateName">The name of the parent state.</param>
- /// <returns>String containing C# code for StateEvent se.</returns>
- private string GenerateStateEvent(StateEvent se, string parentStateName)
- {
- string retstr = String.Empty;
- // we need to separate the argument declaration list from other kids
- List<SYMBOL> argumentDeclarationListKids = new List<SYMBOL>();
- List<SYMBOL> remainingKids = new List<SYMBOL>();
- foreach (SYMBOL kid in se.kids)
- if (kid is ArgumentDeclarationList)
- argumentDeclarationListKids.Add(kid);
- else
- remainingKids.Add(kid);
- // "state" (function) declaration
- retstr += GenerateIndented(String.Format("public void {0}_event_{1}(", parentStateName, se.Name), se);
- // print the state arguments, if any
- foreach (SYMBOL kid in argumentDeclarationListKids)
- retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid);
- retstr += GenerateLine(")");
- foreach (SYMBOL kid in remainingKids)
- retstr += GenerateNode(kid);
- return retstr;
- }
- /// <summary>
- /// Generates the code for an ArgumentDeclarationList node.
- /// </summary>
- /// <param name="adl">The ArgumentDeclarationList node.</param>
- /// <returns>String containing C# code for ArgumentDeclarationList adl.</returns>
- 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 += Generate(String.Format("{0} {1}", d.Datatype, CheckName(d.Id)), d);
- if (0 < comma--)
- retstr += Generate(", ");
- }
- return retstr;
- }
- /// <summary>
- /// Generates the code for an ArgumentList node.
- /// </summary>
- /// <param name="al">The ArgumentList node.</param>
- /// <returns>String containing C# code for ArgumentList al.</returns>
- 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 += Generate(", ");
- }
- return retstr;
- }
- /// <summary>
- /// Generates the code for a CompoundStatement node.
- /// </summary>
- /// <param name="cs">The CompoundStatement node.</param>
- /// <returns>String containing C# code for CompoundStatement cs.</returns>
- private string GenerateCompoundStatement(CompoundStatement cs)
- {
- string retstr = String.Empty;
- // opening brace
- retstr += GenerateIndentedLine("{");
- m_braceCount++;
- foreach (SYMBOL kid in cs.kids)
- retstr += GenerateNode(kid);
- // closing brace
- m_braceCount--;
- retstr += GenerateIndentedLine("}");
- return retstr;
- }
- /// <summary>
- /// Generates the code for a Declaration node.
- /// </summary>
- /// <param name="d">The Declaration node.</param>
- /// <returns>String containing C# code for Declaration d.</returns>
- private string GenerateDeclaration(Declaration d)
- {
- return Generate(String.Format("{0} {1}", d.Datatype, CheckName(d.Id)), d);
- }
- /// <summary>
- /// Generates the code for a Statement node.
- /// </summary>
- /// <param name="s">The Statement node.</param>
- /// <returns>String containing C# code for Statement s.</returns>
- private string GenerateStatement(Statement s)
- {
- string retstr = String.Empty;
- bool printSemicolon = true;
- retstr += Indent();
- 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)
- retstr += GenerateNode(kid);
- }
- if (printSemicolon)
- retstr += GenerateLine(";");
- return retstr;
- }
- /// <summary>
- /// Generates the code for an Assignment node.
- /// </summary>
- /// <param name="a">The Assignment node.</param>
- /// <returns>String containing C# code for Assignment a.</returns>
- private string GenerateAssignment(Assignment a)
- {
- string retstr = String.Empty;
- List<string> identifiers = new List<string>();
- checkForMultipleAssignments(identifiers, a);
- retstr += GenerateNode((SYMBOL) a.kids.Pop());
- retstr += Generate(String.Format(" {0} ", a.AssignmentType), a);
- foreach (SYMBOL kid in a.kids)
- retstr += GenerateNode(kid);
- return retstr;
- }
- // 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<string> 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]);
- }
- }
- /// <summary>
- /// Generates the code for a ReturnStatement node.
- /// </summary>
- /// <param name="rs">The ReturnStatement node.</param>
- /// <returns>String containing C# code for ReturnStatement rs.</returns>
- private string GenerateReturnStatement(ReturnStatement rs)
- {
- string retstr = String.Empty;
- retstr += Generate("return ", rs);
- foreach (SYMBOL kid in rs.kids)
- retstr += GenerateNode(kid);
- return retstr;
- }
- /// <summary>
- /// Generates the code for a JumpLabel node.
- /// </summary>
- /// <param name="jl">The JumpLabel node.</param>
- /// <returns>String containing C# code for JumpLabel jl.</returns>
- private string GenerateJumpLabel(JumpLabel jl)
- {
- return Generate(String.Format("{0}:", CheckName(jl.LabelName)), jl) + " NoOp();\n";
- }
- /// <summary>
- /// Generates the code for a JumpStatement node.
- /// </summary>
- /// <param name="js">The JumpStatement node.</param>
- /// <returns>String containing C# code for JumpStatement js.</returns>
- private string GenerateJumpStatement(JumpStatement js)
- {
- return Generate(String.Format("goto {0}", CheckName(js.TargetName)), js);
- }
- /// <summary>
- /// Generates the code for an IfStatement node.
- /// </summary>
- /// <param name="ifs">The IfStatement node.</param>
- /// <returns>String containing C# code for IfStatement ifs.</returns>
- private string GenerateIfStatement(IfStatement ifs)
- {
- string retstr = String.Empty;
- retstr += GenerateIndented("if (", ifs);
- retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
- retstr += GenerateLine(")");
- // 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 += GenerateIndentedLine("else", ifs);
- indentHere = ifs.kids.Top is Statement;
- if (indentHere) m_braceCount++;
- retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
- if (indentHere) m_braceCount--;
- }
- return retstr;
- }
- /// <summary>
- /// Generates the code for a StateChange node.
- /// </summary>
- /// <param name="sc">The StateChange node.</param>
- /// <returns>String containing C# code for StateChange sc.</returns>
- private string GenerateStateChange(StateChange sc)
- {
- return Generate(String.Format("state(\"{0}\")", sc.NewState), sc);
- }
- /// <summary>
- /// Generates the code for a WhileStatement node.
- /// </summary>
- /// <param name="ws">The WhileStatement node.</param>
- /// <returns>String containing C# code for WhileStatement ws.</returns>
- private string GenerateWhileStatement(WhileStatement ws)
- {
- string retstr = String.Empty;
- retstr += GenerateIndented("while (", ws);
- retstr += GenerateNode((SYMBOL) ws.kids.Pop());
- retstr += GenerateLine(")");
- // 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;
- }
- /// <summary>
- /// Generates the code for a DoWhileStatement node.
- /// </summary>
- /// <param name="dws">The DoWhileStatement node.</param>
- /// <returns>String containing C# code for DoWhileStatement dws.</returns>
- private string GenerateDoWhileStatement(DoWhileStatement dws)
- {
- string retstr = String.Empty;
- retstr += GenerateIndentedLine("do", dws);
- // 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 += GenerateIndented("while (", dws);
- retstr += GenerateNode((SYMBOL) dws.kids.Pop());
- retstr += GenerateLine(");");
- return retstr;
- }
- /// <summary>
- /// Generates the code for a ForLoop node.
- /// </summary>
- /// <param name="fl">The ForLoop node.</param>
- /// <returns>String containing C# code for ForLoop fl.</returns>
- private string GenerateForLoop(ForLoop fl)
- {
- string retstr = String.Empty;
- retstr += GenerateIndented("for (", fl);
- // 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)
- {
- retstr += GenerateForLoopStatement(s);
- }
- retstr += Generate("; ");
- // for (x = 0; x < 10; x++)
- // ^^^^^^
- retstr += GenerateNode((SYMBOL) fl.kids.Pop());
- retstr += Generate("; ");
- // for (x = 0; x < 10; x++)
- // ^^^
- retstr += GenerateForLoopStatement((ForLoopStatement) fl.kids.Pop());
- retstr += GenerateLine(")");
- // 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;
- }
- /// <summary>
- /// Generates the code for a ForLoopStatement node.
- /// </summary>
- /// <param name="fls">The ForLoopStatement node.</param>
- /// <returns>String containing C# code for ForLoopStatement fls.</returns>
- private string GenerateForLoopStatement(ForLoopStatement fls)
- {
- string retstr = String.Empty;
- 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 retstr;
- 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();
-
- retstr += GenerateNode(s);
- if (0 < comma--)
- retstr += Generate(", ");
- }
- return retstr;
- }
- /// <summary>
- /// Generates the code for a BinaryExpression node.
- /// </summary>
- /// <param name="be">The BinaryExpression node.</param>
- /// <returns>String containing C# code for BinaryExpression be.</returns>
- private string GenerateBinaryExpression(BinaryExpression be)
- {
- string retstr = String.Empty;
- if (be.ExpressionSymbol.Equals("&&") || be.ExpressionSymbol.Equals("||"))
- {
- // special case handling for logical and/or, see Mantis 3174
- retstr += "((bool)(";
- retstr += GenerateNode((SYMBOL)be.kids.Pop());
- retstr += "))";
- retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol.Substring(0,1)), be);
- retstr += "((bool)(";
- foreach (SYMBOL kid in be.kids)
- retstr += GenerateNode(kid);
- retstr += "))";
- }
- else
- {
- retstr += GenerateNode((SYMBOL)be.kids.Pop());
- retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol), be);
- foreach (SYMBOL kid in be.kids)
- retstr += GenerateNode(kid);
- }
- return retstr;
- }
- /// <summary>
- /// Generates the code for a UnaryExpression node.
- /// </summary>
- /// <param name="ue">The UnaryExpression node.</param>
- /// <returns>String containing C# code for UnaryExpression ue.</returns>
- private string GenerateUnaryExpression(UnaryExpression ue)
- {
- string retstr = String.Empty;
- retstr += Generate(ue.UnarySymbol, ue);
- retstr += GenerateNode((SYMBOL) ue.kids.Pop());
- return retstr;
- }
- /// <summary>
- /// Generates the code for a ParenthesisExpression node.
- /// </summary>
- /// <param name="pe">The ParenthesisExpression node.</param>
- /// <returns>String containing C# code for ParenthesisExpression pe.</returns>
- private string GenerateParenthesisExpression(ParenthesisExpression pe)
- {
- string retstr = String.Empty;
- retstr += Generate("(");
- foreach (SYMBOL kid in pe.kids)
- retstr += GenerateNode(kid);
- retstr += Generate(")");
- return retstr;
- }
- /// <summary>
- /// Generates the code for a IncrementDecrementExpression node.
- /// </summary>
- /// <param name="ide">The IncrementDecrementExpression node.</param>
- /// <returns>String containing C# code for IncrementDecrementExpression ide.</returns>
- private string GenerateIncrementDecrementExpression(IncrementDecrementExpression ide)
- {
- string retstr = String.Empty;
- if (0 < ide.kids.Count)
- {
- IdentDotExpression dot = (IdentDotExpression) ide.kids.Top;
- retstr += Generate(String.Format("{0}", ide.PostOperation ? CheckName(dot.Name) + "." + dot.Member + ide.Operation : ide.Operation + CheckName(dot.Name) + "." + dot.Member), ide);
- }
- else
- retstr += Generate(String.Format("{0}", ide.PostOperation ? CheckName(ide.Name) + ide.Operation : ide.Operation + CheckName(ide.Name)), ide);
- return retstr;
- }
- /// <summary>
- /// Generates the code for a TypecastExpression node.
- /// </summary>
- /// <param name="te">The TypecastExpression node.</param>
- /// <returns>String containing C# code for TypecastExpression te.</returns>
- private string GenerateTypecastExpression(TypecastExpression te)
- {
- string retstr = String.Empty;
- // we wrap all typecasted statements in parentheses
- retstr += Generate(String.Format("({0}) (", te.TypecastType), te);
- retstr += GenerateNode((SYMBOL) te.kids.Pop());
- retstr += Generate(")");
- return retstr;
- }
- /// <summary>
- /// Generates the code for an identifier
- /// </summary>
- /// <param name="id">The symbol name</param>
- /// <param name="s">The Symbol node.</param>
- /// <returns>String containing C# code for identifier reference.</returns>
- private string GenerateIdentifier(string id, SYMBOL s)
- {
- if (m_comms != null)
- {
- object value = m_comms.LookupModConstant(id);
- if (value != null)
- {
- string retval = null;
- if (value is int)
- retval = ((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;
-
- return Generate(retval, s);
- }
- }
- return Generate(CheckName(id), s);
- }
- /// <summary>
- /// Generates the code for a FunctionCall node.
- /// </summary>
- /// <param name="fc">The FunctionCall node.</param>
- /// <returns>String containing C# code for FunctionCall fc.</returns>
- private string GenerateFunctionCall(FunctionCall fc)
- {
- string retstr = String.Empty;
- 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)
- retstr += Generate(String.Format("{0}(\"{1}\"",modinvoke,fc.Id), fc);
- else
- retstr += Generate(String.Format("{0}(\"{1}\",",modinvoke,fc.Id), fc);
- }
- }
- else
- {
- retstr += Generate(String.Format("{0}(", CheckName(fc.Id)), fc);
- }
-
- foreach (SYMBOL kid in fc.kids)
- retstr += GenerateNode(kid);
- retstr += Generate(")");
- return retstr;
- }
- /// <summary>
- /// Generates the code for a Constant node.
- /// </summary>
- /// <param name="c">The Constant node.</param>
- /// <returns>String containing C# code for Constant c.</returns>
- 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");
- 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+"\")";
- }
- retstr += Generate(c.Value, c);
- return retstr;
- }
- /// <summary>
- /// Generates the code for a VectorConstant node.
- /// </summary>
- /// <param name="vc">The VectorConstant node.</param>
- /// <returns>String containing C# code for VectorConstant vc.</returns>
- private string GenerateVectorConstant(VectorConstant vc)
- {
- string retstr = String.Empty;
- retstr += Generate(String.Format("new {0}(", vc.Type), vc);
- retstr += GenerateNode((SYMBOL) vc.kids.Pop());
- retstr += Generate(", ");
- retstr += GenerateNode((SYMBOL) vc.kids.Pop());
- retstr += Generate(", ");
- retstr += GenerateNode((SYMBOL) vc.kids.Pop());
- retstr += Generate(")");
- return retstr;
- }
- /// <summary>
- /// Generates the code for a RotationConstant node.
- /// </summary>
- /// <param name="rc">The RotationConstant node.</param>
- /// <returns>String containing C# code for RotationConstant rc.</returns>
- private string GenerateRotationConstant(RotationConstant rc)
- {
- string retstr = String.Empty;
- retstr += Generate(String.Format("new {0}(", rc.Type), rc);
- retstr += GenerateNode((SYMBOL) rc.kids.Pop());
- retstr += Generate(", ");
- retstr += GenerateNode((SYMBOL) rc.kids.Pop());
- retstr += Generate(", ");
- retstr += GenerateNode((SYMBOL) rc.kids.Pop());
- retstr += Generate(", ");
- retstr += GenerateNode((SYMBOL) rc.kids.Pop());
- retstr += Generate(")");
- return retstr;
- }
- /// <summary>
- /// Generates the code for a ListConstant node.
- /// </summary>
- /// <param name="lc">The ListConstant node.</param>
- /// <returns>String containing C# code for ListConstant lc.</returns>
- private string GenerateListConstant(ListConstant lc)
- {
- string retstr = String.Empty;
- retstr += Generate(String.Format("new {0}(", lc.Type), lc);
- foreach (SYMBOL kid in lc.kids)
- retstr += GenerateNode(kid);
- retstr += Generate(")");
- return retstr;
- }
- /// <summary>
- /// Prints a newline.
- /// </summary>
- /// <returns>A newline.</returns>
- private string GenerateLine()
- {
- return GenerateLine("");
- }
- /// <summary>
- /// Prints text, followed by a newline.
- /// </summary>
- /// <param name="s">String of text to print.</param>
- /// <returns>String s followed by newline.</returns>
- private string GenerateLine(string s)
- {
- return GenerateLine(s, null);
- }
- /// <summary>
- /// Prints text, followed by a newline.
- /// </summary>
- /// <param name="s">String of text to print.</param>
- /// <param name="sym">Symbol being generated to extract original line
- /// number and column from.</param>
- /// <returns>String s followed by newline.</returns>
- private string GenerateLine(string s, SYMBOL sym)
- {
- string retstr = Generate(s, sym) + "\n";
- m_CSharpLine++;
- m_CSharpCol = 1;
- return retstr;
- }
- /// <summary>
- /// Prints text.
- /// </summary>
- /// <param name="s">String of text to print.</param>
- /// <returns>String s.</returns>
- private string Generate(string s)
- {
- return Generate(s, null);
- }
- /// <summary>
- /// Prints text.
- /// </summary>
- /// <param name="s">String of text to print.</param>
- /// <param name="sym">Symbol being generated to extract original line
- /// number and column from.</param>
- /// <returns>String s.</returns>
- private string Generate(string s, SYMBOL sym)
- {
- if (null != sym)
- m_positionMap.Add(new KeyValuePair<int, int>(m_CSharpLine, m_CSharpCol), new KeyValuePair<int, int>(sym.Line, sym.Position));
- m_CSharpCol += s.Length;
- return s;
- }
- /// <summary>
- /// Prints text correctly indented, followed by a newline.
- /// </summary>
- /// <param name="s">String of text to print.</param>
- /// <returns>Properly indented string s followed by newline.</returns>
- private string GenerateIndentedLine(string s)
- {
- return GenerateIndentedLine(s, null);
- }
- /// <summary>
- /// Prints text correctly indented, followed by a newline.
- /// </summary>
- /// <param name="s">String of text to print.</param>
- /// <param name="sym">Symbol being generated to extract original line
- /// number and column from.</param>
- /// <returns>Properly indented string s followed by newline.</returns>
- private string GenerateIndentedLine(string s, SYMBOL sym)
- {
- string retstr = GenerateIndented(s, sym) + "\n";
- m_CSharpLine++;
- m_CSharpCol = 1;
- return retstr;
- }
- /// <summary>
- /// Prints text correctly indented.
- /// </summary>
- /// <param name="s">String of text to print.</param>
- /// <returns>Properly indented string s.</returns>
- //private string GenerateIndented(string s)
- //{
- // return GenerateIndented(s, null);
- //}
- // THIS FUNCTION IS COMMENTED OUT TO SUPPRESS WARNINGS
- /// <summary>
- /// Prints text correctly indented.
- /// </summary>
- /// <param name="s">String of text to print.</param>
- /// <param name="sym">Symbol being generated to extract original line
- /// number and column from.</param>
- /// <returns>Properly indented string s.</returns>
- private string GenerateIndented(string s, SYMBOL sym)
- {
- string retstr = Indent() + s;
- if (null != sym)
- m_positionMap.Add(new KeyValuePair<int, int>(m_CSharpLine, m_CSharpCol), new KeyValuePair<int, int>(sym.Line, sym.Position));
- m_CSharpCol += s.Length;
- return retstr;
- }
- /// <summary>
- /// Prints correct indentation.
- /// </summary>
- /// <returns>Indentation based on brace count.</returns>
- private string Indent()
- {
- string retstr = String.Empty;
- for (int i = 0; i < m_braceCount; i++)
- for (int j = 0; j < m_indentWidth; j++)
- {
- retstr += " ";
- m_CSharpCol++;
- }
- return retstr;
- }
- /// <summary>
- /// 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?
- /// </summary>
- private string CheckName(string s)
- {
- if (CSReservedWords.IsReservedWord(s))
- return "@" + s;
- else
- return s;
- }
- }
- }
|