CSCodeGenerator.cs 41 KB

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