CSCodeGenerator.cs 42 KB

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