CSCodeGenerator.cs 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.IO;
  29. using System.Collections.Generic;
  30. using System.Reflection;
  31. using log4net;
  32. using Tools;
  33. using OpenSim.Region.Framework.Interfaces;
  34. namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
  35. {
  36. public class CSCodeGenerator : ICodeConverter
  37. {
  38. // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  39. private SYMBOL m_astRoot = null;
  40. private Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> m_positionMap;
  41. private int m_indentWidth = 4; // for indentation
  42. private int m_braceCount; // for indentation
  43. private int m_CSharpLine; // the current line of generated C# code
  44. private int m_CSharpCol; // the current column of generated C# code
  45. private List<string> m_warnings = new List<string>();
  46. private IScriptModuleComms m_comms = null;
  47. /// <summary>
  48. /// Creates an 'empty' CSCodeGenerator instance.
  49. /// </summary>
  50. public CSCodeGenerator()
  51. {
  52. m_comms = null;
  53. ResetCounters();
  54. }
  55. public CSCodeGenerator(IScriptModuleComms comms)
  56. {
  57. m_comms = comms;
  58. ResetCounters();
  59. }
  60. /// <summary>
  61. /// Get the mapping between LSL and C# line/column number.
  62. /// </summary>
  63. /// <returns>Dictionary\<KeyValuePair\<int, int\>, KeyValuePair\<int, int\>\>.</returns>
  64. public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> PositionMap
  65. {
  66. get { return m_positionMap; }
  67. }
  68. /// <summary>
  69. /// Get the mapping between LSL and C# line/column number.
  70. /// </summary>
  71. /// <returns>SYMBOL pointing to root of the abstract syntax tree.</returns>
  72. public SYMBOL ASTRoot
  73. {
  74. get { return m_astRoot; }
  75. }
  76. /// <summary>
  77. /// Resets various counters and metadata.
  78. /// </summary>
  79. private void ResetCounters()
  80. {
  81. m_braceCount = 0;
  82. m_CSharpLine = 0;
  83. m_CSharpCol = 1;
  84. m_positionMap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
  85. m_astRoot = null;
  86. }
  87. /// <summary>
  88. /// Generate the code from the AST we have.
  89. /// </summary>
  90. /// <param name="script">The LSL source as a string.</param>
  91. /// <returns>String containing the generated C# code.</returns>
  92. public string Convert(string script)
  93. {
  94. // m_log.DebugFormat("[CS CODE GENERATOR]: Converting to C#\n{0}", script);
  95. m_warnings.Clear();
  96. ResetCounters();
  97. Parser p = new LSLSyntax(new yyLSLSyntax(), new ErrorHandler(true));
  98. LSL2CSCodeTransformer codeTransformer;
  99. try
  100. {
  101. codeTransformer = new LSL2CSCodeTransformer(p.Parse(script));
  102. }
  103. catch (CSToolsException e)
  104. {
  105. string message;
  106. // LL start numbering lines at 0 - geeks!
  107. // Also need to subtract one line we prepend!
  108. //
  109. string emessage = e.Message;
  110. string slinfo = e.slInfo.ToString();
  111. // Remove wrong line number info
  112. //
  113. if (emessage.StartsWith(slinfo+": "))
  114. emessage = emessage.Substring(slinfo.Length+2);
  115. message = String.Format("({0},{1}) {2}",
  116. e.slInfo.lineNumber - 1,
  117. e.slInfo.charPosition - 1, emessage);
  118. throw new Exception(message);
  119. }
  120. m_astRoot = codeTransformer.Transform();
  121. string retstr = String.Empty;
  122. // standard preamble
  123. //retstr = GenerateLine("using OpenSim.Region.ScriptEngine.Common;");
  124. //retstr += GenerateLine("using System.Collections.Generic;");
  125. //retstr += GenerateLine("");
  126. //retstr += GenerateLine("namespace SecondLife");
  127. //retstr += GenerateLine("{");
  128. m_braceCount++;
  129. //retstr += GenerateIndentedLine("public class Script : OpenSim.Region.ScriptEngine.Common");
  130. //retstr += GenerateIndentedLine("{");
  131. m_braceCount++;
  132. // line number
  133. m_CSharpLine += 3;
  134. // here's the payload
  135. retstr += GenerateLine();
  136. foreach (SYMBOL s in m_astRoot.kids)
  137. retstr += GenerateNode(s);
  138. // close braces!
  139. m_braceCount--;
  140. //retstr += GenerateIndentedLine("}");
  141. m_braceCount--;
  142. //retstr += GenerateLine("}");
  143. // Removes all carriage return characters which may be generated in Windows platform. Is there
  144. // cleaner way of doing this?
  145. retstr=retstr.Replace("\r", "");
  146. return retstr;
  147. }
  148. /// <summary>
  149. /// Get the set of warnings generated during compilation.
  150. /// </summary>
  151. /// <returns></returns>
  152. public string[] GetWarnings()
  153. {
  154. return m_warnings.ToArray();
  155. }
  156. private void AddWarning(string warning)
  157. {
  158. if (!m_warnings.Contains(warning))
  159. {
  160. m_warnings.Add(warning);
  161. }
  162. }
  163. /// <summary>
  164. /// Recursively called to generate each type of node. Will generate this
  165. /// node, then all it's children.
  166. /// </summary>
  167. /// <param name="s">The current node to generate code for.</param>
  168. /// <returns>String containing C# code for SYMBOL s.</returns>
  169. private string GenerateNode(SYMBOL s)
  170. {
  171. string retstr = String.Empty;
  172. // make sure to put type lower in the inheritance hierarchy first
  173. // ie: since IdentArgument and ExpressionArgument inherit from
  174. // Argument, put IdentArgument and ExpressionArgument before Argument
  175. if (s is GlobalFunctionDefinition)
  176. retstr += GenerateGlobalFunctionDefinition((GlobalFunctionDefinition) s);
  177. else if (s is GlobalVariableDeclaration)
  178. retstr += GenerateGlobalVariableDeclaration((GlobalVariableDeclaration) s);
  179. else if (s is State)
  180. retstr += GenerateState((State) s);
  181. else if (s is CompoundStatement)
  182. retstr += GenerateCompoundStatement((CompoundStatement) s);
  183. else if (s is Declaration)
  184. retstr += GenerateDeclaration((Declaration) s);
  185. else if (s is Statement)
  186. retstr += GenerateStatement((Statement) s);
  187. else if (s is ReturnStatement)
  188. retstr += GenerateReturnStatement((ReturnStatement) s);
  189. else if (s is JumpLabel)
  190. retstr += GenerateJumpLabel((JumpLabel) s);
  191. else if (s is JumpStatement)
  192. retstr += GenerateJumpStatement((JumpStatement) s);
  193. else if (s is StateChange)
  194. retstr += GenerateStateChange((StateChange) s);
  195. else if (s is IfStatement)
  196. retstr += GenerateIfStatement((IfStatement) s);
  197. else if (s is WhileStatement)
  198. retstr += GenerateWhileStatement((WhileStatement) s);
  199. else if (s is DoWhileStatement)
  200. retstr += GenerateDoWhileStatement((DoWhileStatement) s);
  201. else if (s is ForLoop)
  202. retstr += GenerateForLoop((ForLoop) s);
  203. else if (s is ArgumentList)
  204. retstr += GenerateArgumentList((ArgumentList) s);
  205. else if (s is Assignment)
  206. retstr += GenerateAssignment((Assignment) s);
  207. else if (s is BinaryExpression)
  208. retstr += GenerateBinaryExpression((BinaryExpression) s);
  209. else if (s is ParenthesisExpression)
  210. retstr += GenerateParenthesisExpression((ParenthesisExpression) s);
  211. else if (s is UnaryExpression)
  212. retstr += GenerateUnaryExpression((UnaryExpression) s);
  213. else if (s is IncrementDecrementExpression)
  214. retstr += GenerateIncrementDecrementExpression((IncrementDecrementExpression) s);
  215. else if (s is TypecastExpression)
  216. retstr += GenerateTypecastExpression((TypecastExpression) s);
  217. else if (s is FunctionCall)
  218. retstr += GenerateFunctionCall((FunctionCall) s);
  219. else if (s is VectorConstant)
  220. retstr += GenerateVectorConstant((VectorConstant) s);
  221. else if (s is RotationConstant)
  222. retstr += GenerateRotationConstant((RotationConstant) s);
  223. else if (s is ListConstant)
  224. retstr += GenerateListConstant((ListConstant) s);
  225. else if (s is Constant)
  226. retstr += GenerateConstant((Constant) s);
  227. else if (s is IdentDotExpression)
  228. retstr += Generate(CheckName(((IdentDotExpression) s).Name) + "." + ((IdentDotExpression) s).Member, s);
  229. else if (s is IdentExpression)
  230. retstr += GenerateIdentifier(((IdentExpression) s).Name, s);
  231. else if (s is IDENT)
  232. retstr += Generate(CheckName(((TOKEN) s).yytext), s);
  233. else
  234. {
  235. foreach (SYMBOL kid in s.kids)
  236. retstr += GenerateNode(kid);
  237. }
  238. return retstr;
  239. }
  240. /// <summary>
  241. /// Generates the code for a GlobalFunctionDefinition node.
  242. /// </summary>
  243. /// <param name="gf">The GlobalFunctionDefinition node.</param>
  244. /// <returns>String containing C# code for GlobalFunctionDefinition gf.</returns>
  245. private string GenerateGlobalFunctionDefinition(GlobalFunctionDefinition gf)
  246. {
  247. string retstr = String.Empty;
  248. // we need to separate the argument declaration list from other kids
  249. List<SYMBOL> argumentDeclarationListKids = new List<SYMBOL>();
  250. List<SYMBOL> remainingKids = new List<SYMBOL>();
  251. foreach (SYMBOL kid in gf.kids)
  252. if (kid is ArgumentDeclarationList)
  253. argumentDeclarationListKids.Add(kid);
  254. else
  255. remainingKids.Add(kid);
  256. retstr += GenerateIndented(String.Format("{0} {1}(", gf.ReturnType, CheckName(gf.Name)), gf);
  257. // print the state arguments, if any
  258. foreach (SYMBOL kid in argumentDeclarationListKids)
  259. retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid);
  260. retstr += GenerateLine(")");
  261. foreach (SYMBOL kid in remainingKids)
  262. retstr += GenerateNode(kid);
  263. return retstr;
  264. }
  265. /// <summary>
  266. /// Generates the code for a GlobalVariableDeclaration node.
  267. /// </summary>
  268. /// <param name="gv">The GlobalVariableDeclaration node.</param>
  269. /// <returns>String containing C# code for GlobalVariableDeclaration gv.</returns>
  270. private string GenerateGlobalVariableDeclaration(GlobalVariableDeclaration gv)
  271. {
  272. string retstr = String.Empty;
  273. foreach (SYMBOL s in gv.kids)
  274. {
  275. retstr += Indent();
  276. retstr += GenerateNode(s);
  277. retstr += GenerateLine(";");
  278. }
  279. return retstr;
  280. }
  281. /// <summary>
  282. /// Generates the code for a State node.
  283. /// </summary>
  284. /// <param name="s">The State node.</param>
  285. /// <returns>String containing C# code for State s.</returns>
  286. private string GenerateState(State s)
  287. {
  288. string retstr = String.Empty;
  289. foreach (SYMBOL kid in s.kids)
  290. if (kid is StateEvent)
  291. retstr += GenerateStateEvent((StateEvent) kid, s.Name);
  292. return retstr;
  293. }
  294. /// <summary>
  295. /// Generates the code for a StateEvent node.
  296. /// </summary>
  297. /// <param name="se">The StateEvent node.</param>
  298. /// <param name="parentStateName">The name of the parent state.</param>
  299. /// <returns>String containing C# code for StateEvent se.</returns>
  300. private string GenerateStateEvent(StateEvent se, string parentStateName)
  301. {
  302. string retstr = String.Empty;
  303. // we need to separate the argument declaration list from other kids
  304. List<SYMBOL> argumentDeclarationListKids = new List<SYMBOL>();
  305. List<SYMBOL> remainingKids = new List<SYMBOL>();
  306. foreach (SYMBOL kid in se.kids)
  307. if (kid is ArgumentDeclarationList)
  308. argumentDeclarationListKids.Add(kid);
  309. else
  310. remainingKids.Add(kid);
  311. // "state" (function) declaration
  312. retstr += GenerateIndented(String.Format("public void {0}_event_{1}(", parentStateName, se.Name), se);
  313. // print the state arguments, if any
  314. foreach (SYMBOL kid in argumentDeclarationListKids)
  315. retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid);
  316. retstr += GenerateLine(")");
  317. foreach (SYMBOL kid in remainingKids)
  318. retstr += GenerateNode(kid);
  319. return retstr;
  320. }
  321. /// <summary>
  322. /// Generates the code for an ArgumentDeclarationList node.
  323. /// </summary>
  324. /// <param name="adl">The ArgumentDeclarationList node.</param>
  325. /// <returns>String containing C# code for ArgumentDeclarationList adl.</returns>
  326. private string GenerateArgumentDeclarationList(ArgumentDeclarationList adl)
  327. {
  328. string retstr = String.Empty;
  329. int comma = adl.kids.Count - 1; // tells us whether to print a comma
  330. foreach (Declaration d in adl.kids)
  331. {
  332. retstr += Generate(String.Format("{0} {1}", d.Datatype, CheckName(d.Id)), d);
  333. if (0 < comma--)
  334. retstr += Generate(", ");
  335. }
  336. return retstr;
  337. }
  338. /// <summary>
  339. /// Generates the code for an ArgumentList node.
  340. /// </summary>
  341. /// <param name="al">The ArgumentList node.</param>
  342. /// <returns>String containing C# code for ArgumentList al.</returns>
  343. private string GenerateArgumentList(ArgumentList al)
  344. {
  345. string retstr = String.Empty;
  346. int comma = al.kids.Count - 1; // tells us whether to print a comma
  347. foreach (SYMBOL s in al.kids)
  348. {
  349. retstr += GenerateNode(s);
  350. if (0 < comma--)
  351. retstr += Generate(", ");
  352. }
  353. return retstr;
  354. }
  355. /// <summary>
  356. /// Generates the code for a CompoundStatement node.
  357. /// </summary>
  358. /// <param name="cs">The CompoundStatement node.</param>
  359. /// <returns>String containing C# code for CompoundStatement cs.</returns>
  360. private string GenerateCompoundStatement(CompoundStatement cs)
  361. {
  362. string retstr = String.Empty;
  363. // opening brace
  364. retstr += GenerateIndentedLine("{");
  365. m_braceCount++;
  366. foreach (SYMBOL kid in cs.kids)
  367. retstr += GenerateNode(kid);
  368. // closing brace
  369. m_braceCount--;
  370. retstr += GenerateIndentedLine("}");
  371. return retstr;
  372. }
  373. /// <summary>
  374. /// Generates the code for a Declaration node.
  375. /// </summary>
  376. /// <param name="d">The Declaration node.</param>
  377. /// <returns>String containing C# code for Declaration d.</returns>
  378. private string GenerateDeclaration(Declaration d)
  379. {
  380. return Generate(String.Format("{0} {1}", d.Datatype, CheckName(d.Id)), d);
  381. }
  382. /// <summary>
  383. /// Generates the code for a Statement node.
  384. /// </summary>
  385. /// <param name="s">The Statement node.</param>
  386. /// <returns>String containing C# code for Statement s.</returns>
  387. private string GenerateStatement(Statement s)
  388. {
  389. string retstr = String.Empty;
  390. bool printSemicolon = true;
  391. retstr += Indent();
  392. if (0 < s.kids.Count)
  393. {
  394. // Jump label prints its own colon, we don't need a semicolon.
  395. printSemicolon = !(s.kids.Top is JumpLabel);
  396. // If we encounter a lone Ident, we skip it, since that's a C#
  397. // (MONO) error.
  398. if (!(s.kids.Top is IdentExpression && 1 == s.kids.Count))
  399. foreach (SYMBOL kid in s.kids)
  400. retstr += GenerateNode(kid);
  401. }
  402. if (printSemicolon)
  403. retstr += GenerateLine(";");
  404. return retstr;
  405. }
  406. /// <summary>
  407. /// Generates the code for an Assignment node.
  408. /// </summary>
  409. /// <param name="a">The Assignment node.</param>
  410. /// <returns>String containing C# code for Assignment a.</returns>
  411. private string GenerateAssignment(Assignment a)
  412. {
  413. string retstr = String.Empty;
  414. List<string> identifiers = new List<string>();
  415. checkForMultipleAssignments(identifiers, a);
  416. retstr += GenerateNode((SYMBOL) a.kids.Pop());
  417. retstr += Generate(String.Format(" {0} ", a.AssignmentType), a);
  418. foreach (SYMBOL kid in a.kids)
  419. retstr += GenerateNode(kid);
  420. return retstr;
  421. }
  422. // This code checks for LSL of the following forms, and generates a
  423. // warning if it finds them.
  424. //
  425. // list l = [ "foo" ];
  426. // l = (l=[]) + l + ["bar"];
  427. // (produces l=["foo","bar"] in SL but l=["bar"] in OS)
  428. //
  429. // integer i;
  430. // integer j;
  431. // i = (j = 3) + (j = 4) + (j = 5);
  432. // (produces j=3 in SL but j=5 in OS)
  433. //
  434. // Without this check, that code passes compilation, but does not do what
  435. // the end user expects, because LSL in SL evaluates right to left instead
  436. // of left to right.
  437. //
  438. // The theory here is that producing an error and alerting the end user that
  439. // something needs to change is better than silently generating incorrect code.
  440. private void checkForMultipleAssignments(List<string> identifiers, SYMBOL s)
  441. {
  442. if (s is Assignment)
  443. {
  444. Assignment a = (Assignment)s;
  445. string newident = null;
  446. if (a.kids[0] is Declaration)
  447. {
  448. newident = ((Declaration)a.kids[0]).Id;
  449. }
  450. else if (a.kids[0] is IDENT)
  451. {
  452. newident = ((IDENT)a.kids[0]).yytext;
  453. }
  454. else if (a.kids[0] is IdentDotExpression)
  455. {
  456. newident = ((IdentDotExpression)a.kids[0]).Name; // +"." + ((IdentDotExpression)a.kids[0]).Member;
  457. }
  458. else
  459. {
  460. 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));
  461. }
  462. if (identifiers.Contains(newident))
  463. {
  464. 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));
  465. }
  466. identifiers.Add(newident);
  467. }
  468. int index;
  469. for (index = 0; index < s.kids.Count; index++)
  470. {
  471. checkForMultipleAssignments(identifiers, (SYMBOL) s.kids[index]);
  472. }
  473. }
  474. /// <summary>
  475. /// Generates the code for a ReturnStatement node.
  476. /// </summary>
  477. /// <param name="rs">The ReturnStatement node.</param>
  478. /// <returns>String containing C# code for ReturnStatement rs.</returns>
  479. private string GenerateReturnStatement(ReturnStatement rs)
  480. {
  481. string retstr = String.Empty;
  482. retstr += Generate("return ", rs);
  483. foreach (SYMBOL kid in rs.kids)
  484. retstr += GenerateNode(kid);
  485. return retstr;
  486. }
  487. /// <summary>
  488. /// Generates the code for a JumpLabel node.
  489. /// </summary>
  490. /// <param name="jl">The JumpLabel node.</param>
  491. /// <returns>String containing C# code for JumpLabel jl.</returns>
  492. private string GenerateJumpLabel(JumpLabel jl)
  493. {
  494. return Generate(String.Format("{0}:", CheckName(jl.LabelName)), jl) + " NoOp();\n";
  495. }
  496. /// <summary>
  497. /// Generates the code for a JumpStatement node.
  498. /// </summary>
  499. /// <param name="js">The JumpStatement node.</param>
  500. /// <returns>String containing C# code for JumpStatement js.</returns>
  501. private string GenerateJumpStatement(JumpStatement js)
  502. {
  503. return Generate(String.Format("goto {0}", CheckName(js.TargetName)), js);
  504. }
  505. /// <summary>
  506. /// Generates the code for an IfStatement node.
  507. /// </summary>
  508. /// <param name="ifs">The IfStatement node.</param>
  509. /// <returns>String containing C# code for IfStatement ifs.</returns>
  510. private string GenerateIfStatement(IfStatement ifs)
  511. {
  512. string retstr = String.Empty;
  513. retstr += GenerateIndented("if (", ifs);
  514. retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
  515. retstr += GenerateLine(")");
  516. // CompoundStatement handles indentation itself but we need to do it
  517. // otherwise.
  518. bool indentHere = ifs.kids.Top is Statement;
  519. if (indentHere) m_braceCount++;
  520. retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
  521. if (indentHere) m_braceCount--;
  522. if (0 < ifs.kids.Count) // do it again for an else
  523. {
  524. retstr += GenerateIndentedLine("else", ifs);
  525. indentHere = ifs.kids.Top is Statement;
  526. if (indentHere) m_braceCount++;
  527. retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
  528. if (indentHere) m_braceCount--;
  529. }
  530. return retstr;
  531. }
  532. /// <summary>
  533. /// Generates the code for a StateChange node.
  534. /// </summary>
  535. /// <param name="sc">The StateChange node.</param>
  536. /// <returns>String containing C# code for StateChange sc.</returns>
  537. private string GenerateStateChange(StateChange sc)
  538. {
  539. return Generate(String.Format("state(\"{0}\")", sc.NewState), sc);
  540. }
  541. /// <summary>
  542. /// Generates the code for a WhileStatement node.
  543. /// </summary>
  544. /// <param name="ws">The WhileStatement node.</param>
  545. /// <returns>String containing C# code for WhileStatement ws.</returns>
  546. private string GenerateWhileStatement(WhileStatement ws)
  547. {
  548. string retstr = String.Empty;
  549. retstr += GenerateIndented("while (", ws);
  550. retstr += GenerateNode((SYMBOL) ws.kids.Pop());
  551. retstr += GenerateLine(")");
  552. // CompoundStatement handles indentation itself but we need to do it
  553. // otherwise.
  554. bool indentHere = ws.kids.Top is Statement;
  555. if (indentHere) m_braceCount++;
  556. retstr += GenerateNode((SYMBOL) ws.kids.Pop());
  557. if (indentHere) m_braceCount--;
  558. return retstr;
  559. }
  560. /// <summary>
  561. /// Generates the code for a DoWhileStatement node.
  562. /// </summary>
  563. /// <param name="dws">The DoWhileStatement node.</param>
  564. /// <returns>String containing C# code for DoWhileStatement dws.</returns>
  565. private string GenerateDoWhileStatement(DoWhileStatement dws)
  566. {
  567. string retstr = String.Empty;
  568. retstr += GenerateIndentedLine("do", dws);
  569. // CompoundStatement handles indentation itself but we need to do it
  570. // otherwise.
  571. bool indentHere = dws.kids.Top is Statement;
  572. if (indentHere) m_braceCount++;
  573. retstr += GenerateNode((SYMBOL) dws.kids.Pop());
  574. if (indentHere) m_braceCount--;
  575. retstr += GenerateIndented("while (", dws);
  576. retstr += GenerateNode((SYMBOL) dws.kids.Pop());
  577. retstr += GenerateLine(");");
  578. return retstr;
  579. }
  580. /// <summary>
  581. /// Generates the code for a ForLoop node.
  582. /// </summary>
  583. /// <param name="fl">The ForLoop node.</param>
  584. /// <returns>String containing C# code for ForLoop fl.</returns>
  585. private string GenerateForLoop(ForLoop fl)
  586. {
  587. string retstr = String.Empty;
  588. retstr += GenerateIndented("for (", fl);
  589. // It's possible that we don't have an assignment, in which case
  590. // the child will be null and we only print the semicolon.
  591. // for (x = 0; x < 10; x++)
  592. // ^^^^^
  593. ForLoopStatement s = (ForLoopStatement) fl.kids.Pop();
  594. if (null != s)
  595. {
  596. retstr += GenerateForLoopStatement(s);
  597. }
  598. retstr += Generate("; ");
  599. // for (x = 0; x < 10; x++)
  600. // ^^^^^^
  601. retstr += GenerateNode((SYMBOL) fl.kids.Pop());
  602. retstr += Generate("; ");
  603. // for (x = 0; x < 10; x++)
  604. // ^^^
  605. retstr += GenerateForLoopStatement((ForLoopStatement) fl.kids.Pop());
  606. retstr += GenerateLine(")");
  607. // CompoundStatement handles indentation itself but we need to do it
  608. // otherwise.
  609. bool indentHere = fl.kids.Top is Statement;
  610. if (indentHere) m_braceCount++;
  611. retstr += GenerateNode((SYMBOL) fl.kids.Pop());
  612. if (indentHere) m_braceCount--;
  613. return retstr;
  614. }
  615. /// <summary>
  616. /// Generates the code for a ForLoopStatement node.
  617. /// </summary>
  618. /// <param name="fls">The ForLoopStatement node.</param>
  619. /// <returns>String containing C# code for ForLoopStatement fls.</returns>
  620. private string GenerateForLoopStatement(ForLoopStatement fls)
  621. {
  622. string retstr = String.Empty;
  623. int comma = fls.kids.Count - 1; // tells us whether to print a comma
  624. // It's possible that all we have is an empty Ident, for example:
  625. //
  626. // for (x; x < 10; x++) { ... }
  627. //
  628. // Which is illegal in C# (MONO). We'll skip it.
  629. if (fls.kids.Top is IdentExpression && 1 == fls.kids.Count)
  630. return retstr;
  631. for (int i = 0; i < fls.kids.Count; i++)
  632. {
  633. SYMBOL s = (SYMBOL)fls.kids[i];
  634. // Statements surrounded by parentheses in for loops
  635. //
  636. // e.g. for ((i = 0), (j = 7); (i < 10); (++i))
  637. //
  638. // are legal in LSL but not in C# so we need to discard the parentheses
  639. //
  640. // The following, however, does not appear to be legal in LLS
  641. //
  642. // for ((i = 0, j = 7); (i < 10); (++i))
  643. //
  644. // As of Friday 20th November 2009, the Linden Lab simulators appear simply never to compile or run this
  645. // script but with no debug or warnings at all! Therefore, we won't deal with this yet (which looks
  646. // like it would be considerably more complicated to handle).
  647. while (s is ParenthesisExpression)
  648. s = (SYMBOL)s.kids.Pop();
  649. retstr += GenerateNode(s);
  650. if (0 < comma--)
  651. retstr += Generate(", ");
  652. }
  653. return retstr;
  654. }
  655. /// <summary>
  656. /// Generates the code for a BinaryExpression node.
  657. /// </summary>
  658. /// <param name="be">The BinaryExpression node.</param>
  659. /// <returns>String containing C# code for BinaryExpression be.</returns>
  660. private string GenerateBinaryExpression(BinaryExpression be)
  661. {
  662. string retstr = String.Empty;
  663. if (be.ExpressionSymbol.Equals("&&") || be.ExpressionSymbol.Equals("||"))
  664. {
  665. // special case handling for logical and/or, see Mantis 3174
  666. retstr += "((bool)(";
  667. retstr += GenerateNode((SYMBOL)be.kids.Pop());
  668. retstr += "))";
  669. retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol.Substring(0,1)), be);
  670. retstr += "((bool)(";
  671. foreach (SYMBOL kid in be.kids)
  672. retstr += GenerateNode(kid);
  673. retstr += "))";
  674. }
  675. else
  676. {
  677. retstr += GenerateNode((SYMBOL)be.kids.Pop());
  678. retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol), be);
  679. foreach (SYMBOL kid in be.kids)
  680. retstr += GenerateNode(kid);
  681. }
  682. return retstr;
  683. }
  684. /// <summary>
  685. /// Generates the code for a UnaryExpression node.
  686. /// </summary>
  687. /// <param name="ue">The UnaryExpression node.</param>
  688. /// <returns>String containing C# code for UnaryExpression ue.</returns>
  689. private string GenerateUnaryExpression(UnaryExpression ue)
  690. {
  691. string retstr = String.Empty;
  692. retstr += Generate(ue.UnarySymbol, ue);
  693. retstr += GenerateNode((SYMBOL) ue.kids.Pop());
  694. return retstr;
  695. }
  696. /// <summary>
  697. /// Generates the code for a ParenthesisExpression node.
  698. /// </summary>
  699. /// <param name="pe">The ParenthesisExpression node.</param>
  700. /// <returns>String containing C# code for ParenthesisExpression pe.</returns>
  701. private string GenerateParenthesisExpression(ParenthesisExpression pe)
  702. {
  703. string retstr = String.Empty;
  704. retstr += Generate("(");
  705. foreach (SYMBOL kid in pe.kids)
  706. retstr += GenerateNode(kid);
  707. retstr += Generate(")");
  708. return retstr;
  709. }
  710. /// <summary>
  711. /// Generates the code for a IncrementDecrementExpression node.
  712. /// </summary>
  713. /// <param name="ide">The IncrementDecrementExpression node.</param>
  714. /// <returns>String containing C# code for IncrementDecrementExpression ide.</returns>
  715. private string GenerateIncrementDecrementExpression(IncrementDecrementExpression ide)
  716. {
  717. string retstr = String.Empty;
  718. if (0 < ide.kids.Count)
  719. {
  720. IdentDotExpression dot = (IdentDotExpression) ide.kids.Top;
  721. retstr += Generate(String.Format("{0}", ide.PostOperation ? CheckName(dot.Name) + "." + dot.Member + ide.Operation : ide.Operation + CheckName(dot.Name) + "." + dot.Member), ide);
  722. }
  723. else
  724. retstr += Generate(String.Format("{0}", ide.PostOperation ? CheckName(ide.Name) + ide.Operation : ide.Operation + CheckName(ide.Name)), ide);
  725. return retstr;
  726. }
  727. /// <summary>
  728. /// Generates the code for a TypecastExpression node.
  729. /// </summary>
  730. /// <param name="te">The TypecastExpression node.</param>
  731. /// <returns>String containing C# code for TypecastExpression te.</returns>
  732. private string GenerateTypecastExpression(TypecastExpression te)
  733. {
  734. string retstr = String.Empty;
  735. // we wrap all typecasted statements in parentheses
  736. retstr += Generate(String.Format("({0}) (", te.TypecastType), te);
  737. retstr += GenerateNode((SYMBOL) te.kids.Pop());
  738. retstr += Generate(")");
  739. return retstr;
  740. }
  741. /// <summary>
  742. /// Generates the code for an identifier
  743. /// </summary>
  744. /// <param name="id">The symbol name</param>
  745. /// <param name="s">The Symbol node.</param>
  746. /// <returns>String containing C# code for identifier reference.</returns>
  747. private string GenerateIdentifier(string id, SYMBOL s)
  748. {
  749. if (m_comms != null)
  750. {
  751. object value = m_comms.LookupModConstant(id);
  752. if (value != null)
  753. {
  754. string retval = null;
  755. if (value is int)
  756. retval = ((int)value).ToString();
  757. else if (value is float)
  758. retval = String.Format("new LSL_Types.LSLFloat({0})",((float)value).ToString());
  759. else if (value is string)
  760. retval = String.Format("new LSL_Types.LSLString(\"{0}\")",((string)value));
  761. else if (value is OpenMetaverse.UUID)
  762. retval = String.Format("new LSL_Types.key(\"{0}\")",((OpenMetaverse.UUID)value).ToString());
  763. else if (value is OpenMetaverse.Vector3)
  764. retval = String.Format("new LSL_Types.Vector3(\"{0}\")",((OpenMetaverse.Vector3)value).ToString());
  765. else if (value is OpenMetaverse.Quaternion)
  766. retval = String.Format("new LSL_Types.Quaternion(\"{0}\")",((OpenMetaverse.Quaternion)value).ToString());
  767. else retval = id;
  768. return Generate(retval, s);
  769. }
  770. }
  771. return Generate(CheckName(id), s);
  772. }
  773. /// <summary>
  774. /// Generates the code for a FunctionCall node.
  775. /// </summary>
  776. /// <param name="fc">The FunctionCall node.</param>
  777. /// <returns>String containing C# code for FunctionCall fc.</returns>
  778. private string GenerateFunctionCall(FunctionCall fc)
  779. {
  780. string retstr = String.Empty;
  781. string modinvoke = null;
  782. if (m_comms != null)
  783. modinvoke = m_comms.LookupModInvocation(fc.Id);
  784. if (modinvoke != null)
  785. {
  786. if (fc.kids[0] is ArgumentList)
  787. {
  788. if ((fc.kids[0] as ArgumentList).kids.Count == 0)
  789. retstr += Generate(String.Format("{0}(\"{1}\"",modinvoke,fc.Id), fc);
  790. else
  791. retstr += Generate(String.Format("{0}(\"{1}\",",modinvoke,fc.Id), fc);
  792. }
  793. }
  794. else
  795. {
  796. retstr += Generate(String.Format("{0}(", CheckName(fc.Id)), fc);
  797. }
  798. foreach (SYMBOL kid in fc.kids)
  799. retstr += GenerateNode(kid);
  800. retstr += Generate(")");
  801. return retstr;
  802. }
  803. /// <summary>
  804. /// Generates the code for a Constant node.
  805. /// </summary>
  806. /// <param name="c">The Constant node.</param>
  807. /// <returns>String containing C# code for Constant c.</returns>
  808. private string GenerateConstant(Constant c)
  809. {
  810. string retstr = String.Empty;
  811. // Supprt LSL's weird acceptance of floats with no trailing digits
  812. // after the period. Turn float x = 10.; into float x = 10.0;
  813. if ("LSL_Types.LSLFloat" == c.Type)
  814. {
  815. int dotIndex = c.Value.IndexOf('.') + 1;
  816. if (0 < dotIndex && (dotIndex == c.Value.Length || !Char.IsDigit(c.Value[dotIndex])))
  817. c.Value = c.Value.Insert(dotIndex, "0");
  818. c.Value = "new LSL_Types.LSLFloat("+c.Value+")";
  819. }
  820. else if ("LSL_Types.LSLInteger" == c.Type)
  821. {
  822. c.Value = "new LSL_Types.LSLInteger("+c.Value+")";
  823. }
  824. else if ("LSL_Types.LSLString" == c.Type)
  825. {
  826. c.Value = "new LSL_Types.LSLString(\""+c.Value+"\")";
  827. }
  828. retstr += Generate(c.Value, c);
  829. return retstr;
  830. }
  831. /// <summary>
  832. /// Generates the code for a VectorConstant node.
  833. /// </summary>
  834. /// <param name="vc">The VectorConstant node.</param>
  835. /// <returns>String containing C# code for VectorConstant vc.</returns>
  836. private string GenerateVectorConstant(VectorConstant vc)
  837. {
  838. string retstr = String.Empty;
  839. retstr += Generate(String.Format("new {0}(", vc.Type), vc);
  840. retstr += GenerateNode((SYMBOL) vc.kids.Pop());
  841. retstr += Generate(", ");
  842. retstr += GenerateNode((SYMBOL) vc.kids.Pop());
  843. retstr += Generate(", ");
  844. retstr += GenerateNode((SYMBOL) vc.kids.Pop());
  845. retstr += Generate(")");
  846. return retstr;
  847. }
  848. /// <summary>
  849. /// Generates the code for a RotationConstant node.
  850. /// </summary>
  851. /// <param name="rc">The RotationConstant node.</param>
  852. /// <returns>String containing C# code for RotationConstant rc.</returns>
  853. private string GenerateRotationConstant(RotationConstant rc)
  854. {
  855. string retstr = String.Empty;
  856. retstr += Generate(String.Format("new {0}(", rc.Type), rc);
  857. retstr += GenerateNode((SYMBOL) rc.kids.Pop());
  858. retstr += Generate(", ");
  859. retstr += GenerateNode((SYMBOL) rc.kids.Pop());
  860. retstr += Generate(", ");
  861. retstr += GenerateNode((SYMBOL) rc.kids.Pop());
  862. retstr += Generate(", ");
  863. retstr += GenerateNode((SYMBOL) rc.kids.Pop());
  864. retstr += Generate(")");
  865. return retstr;
  866. }
  867. /// <summary>
  868. /// Generates the code for a ListConstant node.
  869. /// </summary>
  870. /// <param name="lc">The ListConstant node.</param>
  871. /// <returns>String containing C# code for ListConstant lc.</returns>
  872. private string GenerateListConstant(ListConstant lc)
  873. {
  874. string retstr = String.Empty;
  875. retstr += Generate(String.Format("new {0}(", lc.Type), lc);
  876. foreach (SYMBOL kid in lc.kids)
  877. retstr += GenerateNode(kid);
  878. retstr += Generate(")");
  879. return retstr;
  880. }
  881. /// <summary>
  882. /// Prints a newline.
  883. /// </summary>
  884. /// <returns>A newline.</returns>
  885. private string GenerateLine()
  886. {
  887. return GenerateLine("");
  888. }
  889. /// <summary>
  890. /// Prints text, followed by a newline.
  891. /// </summary>
  892. /// <param name="s">String of text to print.</param>
  893. /// <returns>String s followed by newline.</returns>
  894. private string GenerateLine(string s)
  895. {
  896. return GenerateLine(s, null);
  897. }
  898. /// <summary>
  899. /// Prints text, followed by a newline.
  900. /// </summary>
  901. /// <param name="s">String of text to print.</param>
  902. /// <param name="sym">Symbol being generated to extract original line
  903. /// number and column from.</param>
  904. /// <returns>String s followed by newline.</returns>
  905. private string GenerateLine(string s, SYMBOL sym)
  906. {
  907. string retstr = Generate(s, sym) + "\n";
  908. m_CSharpLine++;
  909. m_CSharpCol = 1;
  910. return retstr;
  911. }
  912. /// <summary>
  913. /// Prints text.
  914. /// </summary>
  915. /// <param name="s">String of text to print.</param>
  916. /// <returns>String s.</returns>
  917. private string Generate(string s)
  918. {
  919. return Generate(s, null);
  920. }
  921. /// <summary>
  922. /// Prints text.
  923. /// </summary>
  924. /// <param name="s">String of text to print.</param>
  925. /// <param name="sym">Symbol being generated to extract original line
  926. /// number and column from.</param>
  927. /// <returns>String s.</returns>
  928. private string Generate(string s, SYMBOL sym)
  929. {
  930. if (null != sym)
  931. m_positionMap.Add(new KeyValuePair<int, int>(m_CSharpLine, m_CSharpCol), new KeyValuePair<int, int>(sym.Line, sym.Position));
  932. m_CSharpCol += s.Length;
  933. return s;
  934. }
  935. /// <summary>
  936. /// Prints text correctly indented, followed by a newline.
  937. /// </summary>
  938. /// <param name="s">String of text to print.</param>
  939. /// <returns>Properly indented string s followed by newline.</returns>
  940. private string GenerateIndentedLine(string s)
  941. {
  942. return GenerateIndentedLine(s, null);
  943. }
  944. /// <summary>
  945. /// Prints text correctly indented, followed by a newline.
  946. /// </summary>
  947. /// <param name="s">String of text to print.</param>
  948. /// <param name="sym">Symbol being generated to extract original line
  949. /// number and column from.</param>
  950. /// <returns>Properly indented string s followed by newline.</returns>
  951. private string GenerateIndentedLine(string s, SYMBOL sym)
  952. {
  953. string retstr = GenerateIndented(s, sym) + "\n";
  954. m_CSharpLine++;
  955. m_CSharpCol = 1;
  956. return retstr;
  957. }
  958. /// <summary>
  959. /// Prints text correctly indented.
  960. /// </summary>
  961. /// <param name="s">String of text to print.</param>
  962. /// <returns>Properly indented string s.</returns>
  963. //private string GenerateIndented(string s)
  964. //{
  965. // return GenerateIndented(s, null);
  966. //}
  967. // THIS FUNCTION IS COMMENTED OUT TO SUPPRESS WARNINGS
  968. /// <summary>
  969. /// Prints text correctly indented.
  970. /// </summary>
  971. /// <param name="s">String of text to print.</param>
  972. /// <param name="sym">Symbol being generated to extract original line
  973. /// number and column from.</param>
  974. /// <returns>Properly indented string s.</returns>
  975. private string GenerateIndented(string s, SYMBOL sym)
  976. {
  977. string retstr = Indent() + s;
  978. if (null != sym)
  979. m_positionMap.Add(new KeyValuePair<int, int>(m_CSharpLine, m_CSharpCol), new KeyValuePair<int, int>(sym.Line, sym.Position));
  980. m_CSharpCol += s.Length;
  981. return retstr;
  982. }
  983. /// <summary>
  984. /// Prints correct indentation.
  985. /// </summary>
  986. /// <returns>Indentation based on brace count.</returns>
  987. private string Indent()
  988. {
  989. string retstr = String.Empty;
  990. for (int i = 0; i < m_braceCount; i++)
  991. for (int j = 0; j < m_indentWidth; j++)
  992. {
  993. retstr += " ";
  994. m_CSharpCol++;
  995. }
  996. return retstr;
  997. }
  998. /// <summary>
  999. /// Returns the passed name with an underscore prepended if that name is a reserved word in C#
  1000. /// and not resevered in LSL otherwise it just returns the passed name.
  1001. ///
  1002. /// This makes no attempt to cache the results to minimise future lookups. For a non trivial
  1003. /// scripts the number of unique identifiers could easily grow to the size of the reserved word
  1004. /// list so maintaining a list or dictionary and doing the lookup there firstwould probably not
  1005. /// give any real speed advantage.
  1006. ///
  1007. /// I believe there is a class Microsoft.CSharp.CSharpCodeProvider that has a function
  1008. /// CreateValidIdentifier(str) that will return either the value of str if it is not a C#
  1009. /// key word or "_"+str if it is. But availability under Mono?
  1010. /// </summary>
  1011. private string CheckName(string s)
  1012. {
  1013. if (CSReservedWords.IsReservedWord(s))
  1014. return "@" + s;
  1015. else
  1016. return s;
  1017. }
  1018. }
  1019. }