opensim-development – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | eva | 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 | |||
28 | using System; |
||
29 | using System.IO; |
||
30 | using System.Collections.Generic; |
||
31 | using System.Reflection; |
||
32 | using log4net; |
||
33 | using Tools; |
||
34 | using OpenSim.Region.Framework.Interfaces; |
||
35 | |||
36 | namespace OpenSim.Region.ScriptEngine.Shared.CodeTools |
||
37 | { |
||
38 | public class CSCodeGenerator : ICodeConverter |
||
39 | { |
||
40 | // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
||
41 | |||
42 | private SYMBOL m_astRoot = null; |
||
43 | private Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> m_positionMap; |
||
44 | private int m_indentWidth = 4; // for indentation |
||
45 | private int m_braceCount; // for indentation |
||
46 | private int m_CSharpLine; // the current line of generated C# code |
||
47 | private int m_CSharpCol; // the current column of generated C# code |
||
48 | private List<string> m_warnings = new List<string>(); |
||
49 | private IScriptModuleComms m_comms = null; |
||
50 | |||
51 | private bool m_insertCoopTerminationChecks; |
||
52 | private static string m_coopTerminationCheck = "opensim_reserved_CheckForCoopTermination();"; |
||
53 | |||
54 | /// <summary> |
||
55 | /// Keep a record of the previous node when we do the parsing. |
||
56 | /// </summary> |
||
57 | /// <remarks> |
||
58 | /// We do this here because the parser generated by CSTools does not retain a reference to its parent node. |
||
59 | /// The previous node is required so we can correctly insert co-op termination checks when required. |
||
60 | /// </remarks> |
||
61 | // private SYMBOL m_previousNode; |
||
62 | |||
63 | /// <summary> |
||
64 | /// Creates an 'empty' CSCodeGenerator instance. |
||
65 | /// </summary> |
||
66 | public CSCodeGenerator() |
||
67 | { |
||
68 | m_comms = null; |
||
69 | ResetCounters(); |
||
70 | } |
||
71 | |||
72 | public CSCodeGenerator(IScriptModuleComms comms, bool insertCoopTerminationChecks) |
||
73 | { |
||
74 | m_comms = comms; |
||
75 | m_insertCoopTerminationChecks = insertCoopTerminationChecks; |
||
76 | ResetCounters(); |
||
77 | } |
||
78 | |||
79 | /// <summary> |
||
80 | /// Get the mapping between LSL and C# line/column number. |
||
81 | /// </summary> |
||
82 | /// <returns>Dictionary\<KeyValuePair\<int, int\>, KeyValuePair\<int, int\>\>.</returns> |
||
83 | public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> PositionMap |
||
84 | { |
||
85 | get { return m_positionMap; } |
||
86 | } |
||
87 | |||
88 | /// <summary> |
||
89 | /// Get the mapping between LSL and C# line/column number. |
||
90 | /// </summary> |
||
91 | /// <returns>SYMBOL pointing to root of the abstract syntax tree.</returns> |
||
92 | public SYMBOL ASTRoot |
||
93 | { |
||
94 | get { return m_astRoot; } |
||
95 | } |
||
96 | |||
97 | /// <summary> |
||
98 | /// Resets various counters and metadata. |
||
99 | /// </summary> |
||
100 | private void ResetCounters() |
||
101 | { |
||
102 | m_braceCount = 0; |
||
103 | m_CSharpLine = 0; |
||
104 | m_CSharpCol = 1; |
||
105 | m_positionMap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>(); |
||
106 | m_astRoot = null; |
||
107 | } |
||
108 | |||
109 | /// <summary> |
||
110 | /// Generate the code from the AST we have. |
||
111 | /// </summary> |
||
112 | /// <param name="script">The LSL source as a string.</param> |
||
113 | /// <returns>String containing the generated C# code.</returns> |
||
114 | public string Convert(string script) |
||
115 | { |
||
116 | // m_log.DebugFormat("[CS CODE GENERATOR]: Converting to C#\n{0}", script); |
||
117 | |||
118 | m_warnings.Clear(); |
||
119 | ResetCounters(); |
||
120 | Parser p = new LSLSyntax(new yyLSLSyntax(), new ErrorHandler(true)); |
||
121 | |||
122 | LSL2CSCodeTransformer codeTransformer; |
||
123 | try |
||
124 | { |
||
125 | codeTransformer = new LSL2CSCodeTransformer(p.Parse(script)); |
||
126 | } |
||
127 | catch (CSToolsException e) |
||
128 | { |
||
129 | string message; |
||
130 | |||
131 | // LL start numbering lines at 0 - geeks! |
||
132 | // Also need to subtract one line we prepend! |
||
133 | // |
||
134 | string emessage = e.Message; |
||
135 | string slinfo = e.slInfo.ToString(); |
||
136 | |||
137 | // Remove wrong line number info |
||
138 | // |
||
139 | if (emessage.StartsWith(slinfo+": ")) |
||
140 | emessage = emessage.Substring(slinfo.Length+2); |
||
141 | |||
142 | message = String.Format("({0},{1}) {2}", |
||
143 | e.slInfo.lineNumber - 1, |
||
144 | e.slInfo.charPosition - 1, emessage); |
||
145 | |||
146 | throw new Exception(message); |
||
147 | } |
||
148 | |||
149 | m_astRoot = codeTransformer.Transform(); |
||
150 | |||
151 | string retstr = String.Empty; |
||
152 | |||
153 | // standard preamble |
||
154 | //retstr = GenerateLine("using OpenSim.Region.ScriptEngine.Common;"); |
||
155 | //retstr += GenerateLine("using System.Collections.Generic;"); |
||
156 | //retstr += GenerateLine(""); |
||
157 | //retstr += GenerateLine("namespace SecondLife"); |
||
158 | //retstr += GenerateLine("{"); |
||
159 | m_braceCount++; |
||
160 | //retstr += GenerateIndentedLine("public class Script : OpenSim.Region.ScriptEngine.Common"); |
||
161 | //retstr += GenerateIndentedLine("{"); |
||
162 | m_braceCount++; |
||
163 | |||
164 | // line number |
||
165 | m_CSharpLine += 3; |
||
166 | |||
167 | // here's the payload |
||
168 | retstr += GenerateLine(); |
||
169 | foreach (SYMBOL s in m_astRoot.kids) |
||
170 | retstr += GenerateNode(m_astRoot, s); |
||
171 | |||
172 | // close braces! |
||
173 | m_braceCount--; |
||
174 | //retstr += GenerateIndentedLine("}"); |
||
175 | m_braceCount--; |
||
176 | //retstr += GenerateLine("}"); |
||
177 | |||
178 | // Removes all carriage return characters which may be generated in Windows platform. Is there |
||
179 | // cleaner way of doing this? |
||
180 | retstr = retstr.Replace("\r", ""); |
||
181 | |||
182 | return retstr; |
||
183 | } |
||
184 | |||
185 | /// <summary> |
||
186 | /// Get the set of warnings generated during compilation. |
||
187 | /// </summary> |
||
188 | /// <returns></returns> |
||
189 | public string[] GetWarnings() |
||
190 | { |
||
191 | return m_warnings.ToArray(); |
||
192 | } |
||
193 | |||
194 | private void AddWarning(string warning) |
||
195 | { |
||
196 | if (!m_warnings.Contains(warning)) |
||
197 | { |
||
198 | m_warnings.Add(warning); |
||
199 | } |
||
200 | } |
||
201 | |||
202 | /// <summary> |
||
203 | /// Recursively called to generate each type of node. Will generate this |
||
204 | /// node, then all it's children. |
||
205 | /// </summary> |
||
206 | /// <param name="previousSymbol">The parent node.</param> |
||
207 | /// <param name="s">The current node to generate code for.</param> |
||
208 | /// <returns>String containing C# code for SYMBOL s.</returns> |
||
209 | private string GenerateNode(SYMBOL previousSymbol, SYMBOL s) |
||
210 | { |
||
211 | string retstr = String.Empty; |
||
212 | |||
213 | // make sure to put type lower in the inheritance hierarchy first |
||
214 | // ie: since IdentArgument and ExpressionArgument inherit from |
||
215 | // Argument, put IdentArgument and ExpressionArgument before Argument |
||
216 | if (s is GlobalFunctionDefinition) |
||
217 | retstr += GenerateGlobalFunctionDefinition((GlobalFunctionDefinition) s); |
||
218 | else if (s is GlobalVariableDeclaration) |
||
219 | retstr += GenerateGlobalVariableDeclaration((GlobalVariableDeclaration) s); |
||
220 | else if (s is State) |
||
221 | retstr += GenerateState((State) s); |
||
222 | else if (s is CompoundStatement) |
||
223 | retstr += GenerateCompoundStatement(previousSymbol, (CompoundStatement) s); |
||
224 | else if (s is Declaration) |
||
225 | retstr += GenerateDeclaration((Declaration) s); |
||
226 | else if (s is Statement) |
||
227 | retstr += GenerateStatement(previousSymbol, (Statement) s); |
||
228 | else if (s is ReturnStatement) |
||
229 | retstr += GenerateReturnStatement((ReturnStatement) s); |
||
230 | else if (s is JumpLabel) |
||
231 | retstr += GenerateJumpLabel((JumpLabel) s); |
||
232 | else if (s is JumpStatement) |
||
233 | retstr += GenerateJumpStatement((JumpStatement) s); |
||
234 | else if (s is StateChange) |
||
235 | retstr += GenerateStateChange((StateChange) s); |
||
236 | else if (s is IfStatement) |
||
237 | retstr += GenerateIfStatement((IfStatement) s); |
||
238 | else if (s is WhileStatement) |
||
239 | retstr += GenerateWhileStatement((WhileStatement) s); |
||
240 | else if (s is DoWhileStatement) |
||
241 | retstr += GenerateDoWhileStatement((DoWhileStatement) s); |
||
242 | else if (s is ForLoop) |
||
243 | retstr += GenerateForLoop((ForLoop) s); |
||
244 | else if (s is ArgumentList) |
||
245 | retstr += GenerateArgumentList((ArgumentList) s); |
||
246 | else if (s is Assignment) |
||
247 | retstr += GenerateAssignment((Assignment) s); |
||
248 | else if (s is BinaryExpression) |
||
249 | retstr += GenerateBinaryExpression((BinaryExpression) s); |
||
250 | else if (s is ParenthesisExpression) |
||
251 | retstr += GenerateParenthesisExpression((ParenthesisExpression) s); |
||
252 | else if (s is UnaryExpression) |
||
253 | retstr += GenerateUnaryExpression((UnaryExpression) s); |
||
254 | else if (s is IncrementDecrementExpression) |
||
255 | retstr += GenerateIncrementDecrementExpression((IncrementDecrementExpression) s); |
||
256 | else if (s is TypecastExpression) |
||
257 | retstr += GenerateTypecastExpression((TypecastExpression) s); |
||
258 | else if (s is FunctionCall) |
||
259 | retstr += GenerateFunctionCall((FunctionCall) s); |
||
260 | else if (s is VectorConstant) |
||
261 | retstr += GenerateVectorConstant((VectorConstant) s); |
||
262 | else if (s is RotationConstant) |
||
263 | retstr += GenerateRotationConstant((RotationConstant) s); |
||
264 | else if (s is ListConstant) |
||
265 | retstr += GenerateListConstant((ListConstant) s); |
||
266 | else if (s is Constant) |
||
267 | retstr += GenerateConstant((Constant) s); |
||
268 | else if (s is IdentDotExpression) |
||
269 | retstr += Generate(CheckName(((IdentDotExpression) s).Name) + "." + ((IdentDotExpression) s).Member, s); |
||
270 | else if (s is IdentExpression) |
||
271 | retstr += GenerateIdentifier(((IdentExpression) s).Name, s); |
||
272 | else if (s is IDENT) |
||
273 | retstr += Generate(CheckName(((TOKEN) s).yytext), s); |
||
274 | else |
||
275 | { |
||
276 | foreach (SYMBOL kid in s.kids) |
||
277 | retstr += GenerateNode(s, kid); |
||
278 | } |
||
279 | |||
280 | return retstr; |
||
281 | } |
||
282 | |||
283 | /// <summary> |
||
284 | /// Generates the code for a GlobalFunctionDefinition node. |
||
285 | /// </summary> |
||
286 | /// <param name="gf">The GlobalFunctionDefinition node.</param> |
||
287 | /// <returns>String containing C# code for GlobalFunctionDefinition gf.</returns> |
||
288 | private string GenerateGlobalFunctionDefinition(GlobalFunctionDefinition gf) |
||
289 | { |
||
290 | string retstr = String.Empty; |
||
291 | |||
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 | |||
296 | foreach (SYMBOL kid in gf.kids) |
||
297 | if (kid is ArgumentDeclarationList) |
||
298 | argumentDeclarationListKids.Add(kid); |
||
299 | else |
||
300 | remainingKids.Add(kid); |
||
301 | |||
302 | retstr += GenerateIndented(String.Format("{0} {1}(", gf.ReturnType, CheckName(gf.Name)), gf); |
||
303 | |||
304 | // print the state arguments, if any |
||
305 | foreach (SYMBOL kid in argumentDeclarationListKids) |
||
306 | retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid); |
||
307 | |||
308 | retstr += GenerateLine(")"); |
||
309 | |||
310 | foreach (SYMBOL kid in remainingKids) |
||
311 | retstr += GenerateNode(gf, kid); |
||
312 | |||
313 | return retstr; |
||
314 | } |
||
315 | |||
316 | /// <summary> |
||
317 | /// Generates the code for a GlobalVariableDeclaration node. |
||
318 | /// </summary> |
||
319 | /// <param name="gv">The GlobalVariableDeclaration node.</param> |
||
320 | /// <returns>String containing C# code for GlobalVariableDeclaration gv.</returns> |
||
321 | private string GenerateGlobalVariableDeclaration(GlobalVariableDeclaration gv) |
||
322 | { |
||
323 | string retstr = String.Empty; |
||
324 | |||
325 | foreach (SYMBOL s in gv.kids) |
||
326 | { |
||
327 | retstr += Indent(); |
||
328 | retstr += GenerateNode(gv, s); |
||
329 | retstr += GenerateLine(";"); |
||
330 | } |
||
331 | |||
332 | return retstr; |
||
333 | } |
||
334 | |||
335 | /// <summary> |
||
336 | /// Generates the code for a State node. |
||
337 | /// </summary> |
||
338 | /// <param name="s">The State node.</param> |
||
339 | /// <returns>String containing C# code for State s.</returns> |
||
340 | private string GenerateState(State s) |
||
341 | { |
||
342 | string retstr = String.Empty; |
||
343 | |||
344 | foreach (SYMBOL kid in s.kids) |
||
345 | if (kid is StateEvent) |
||
346 | retstr += GenerateStateEvent((StateEvent) kid, s.Name); |
||
347 | |||
348 | return retstr; |
||
349 | } |
||
350 | |||
351 | /// <summary> |
||
352 | /// Generates the code for a StateEvent node. |
||
353 | /// </summary> |
||
354 | /// <param name="se">The StateEvent node.</param> |
||
355 | /// <param name="parentStateName">The name of the parent state.</param> |
||
356 | /// <returns>String containing C# code for StateEvent se.</returns> |
||
357 | private string GenerateStateEvent(StateEvent se, string parentStateName) |
||
358 | { |
||
359 | string retstr = String.Empty; |
||
360 | |||
361 | // we need to separate the argument declaration list from other kids |
||
362 | List<SYMBOL> argumentDeclarationListKids = new List<SYMBOL>(); |
||
363 | List<SYMBOL> remainingKids = new List<SYMBOL>(); |
||
364 | |||
365 | foreach (SYMBOL kid in se.kids) |
||
366 | if (kid is ArgumentDeclarationList) |
||
367 | argumentDeclarationListKids.Add(kid); |
||
368 | else |
||
369 | remainingKids.Add(kid); |
||
370 | |||
371 | // "state" (function) declaration |
||
372 | retstr += GenerateIndented(String.Format("public void {0}_event_{1}(", parentStateName, se.Name), se); |
||
373 | |||
374 | // print the state arguments, if any |
||
375 | foreach (SYMBOL kid in argumentDeclarationListKids) |
||
376 | retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid); |
||
377 | |||
378 | retstr += GenerateLine(")"); |
||
379 | |||
380 | foreach (SYMBOL kid in remainingKids) |
||
381 | retstr += GenerateNode(se, kid); |
||
382 | |||
383 | return retstr; |
||
384 | } |
||
385 | |||
386 | /// <summary> |
||
387 | /// Generates the code for an ArgumentDeclarationList node. |
||
388 | /// </summary> |
||
389 | /// <param name="adl">The ArgumentDeclarationList node.</param> |
||
390 | /// <returns>String containing C# code for ArgumentDeclarationList adl.</returns> |
||
391 | private string GenerateArgumentDeclarationList(ArgumentDeclarationList adl) |
||
392 | { |
||
393 | string retstr = String.Empty; |
||
394 | |||
395 | int comma = adl.kids.Count - 1; // tells us whether to print a comma |
||
396 | |||
397 | foreach (Declaration d in adl.kids) |
||
398 | { |
||
399 | retstr += Generate(String.Format("{0} {1}", d.Datatype, CheckName(d.Id)), d); |
||
400 | if (0 < comma--) |
||
401 | retstr += Generate(", "); |
||
402 | } |
||
403 | |||
404 | return retstr; |
||
405 | } |
||
406 | |||
407 | /// <summary> |
||
408 | /// Generates the code for an ArgumentList node. |
||
409 | /// </summary> |
||
410 | /// <param name="al">The ArgumentList node.</param> |
||
411 | /// <returns>String containing C# code for ArgumentList al.</returns> |
||
412 | private string GenerateArgumentList(ArgumentList al) |
||
413 | { |
||
414 | string retstr = String.Empty; |
||
415 | |||
416 | int comma = al.kids.Count - 1; // tells us whether to print a comma |
||
417 | |||
418 | foreach (SYMBOL s in al.kids) |
||
419 | { |
||
420 | retstr += GenerateNode(al, s); |
||
421 | if (0 < comma--) |
||
422 | retstr += Generate(", "); |
||
423 | } |
||
424 | |||
425 | return retstr; |
||
426 | } |
||
427 | |||
428 | /// <summary> |
||
429 | /// Generates the code for a CompoundStatement node. |
||
430 | /// </summary> |
||
431 | /// <param name="cs">The CompoundStatement node.</param> |
||
432 | /// <returns>String containing C# code for CompoundStatement cs.</returns> |
||
433 | private string GenerateCompoundStatement(SYMBOL previousSymbol, CompoundStatement cs) |
||
434 | { |
||
435 | string retstr = String.Empty; |
||
436 | |||
437 | // opening brace |
||
438 | retstr += GenerateIndentedLine("{"); |
||
439 | m_braceCount++; |
||
440 | |||
441 | if (m_insertCoopTerminationChecks) |
||
442 | { |
||
443 | // We have to check in event functions as well because the user can manually call these. |
||
444 | if (previousSymbol is GlobalFunctionDefinition |
||
445 | || previousSymbol is WhileStatement |
||
446 | || previousSymbol is DoWhileStatement |
||
447 | || previousSymbol is ForLoop |
||
448 | || previousSymbol is StateEvent) |
||
449 | retstr += GenerateIndentedLine(m_coopTerminationCheck); |
||
450 | } |
||
451 | |||
452 | foreach (SYMBOL kid in cs.kids) |
||
453 | retstr += GenerateNode(cs, kid); |
||
454 | |||
455 | // closing brace |
||
456 | m_braceCount--; |
||
457 | retstr += GenerateIndentedLine("}"); |
||
458 | |||
459 | return retstr; |
||
460 | } |
||
461 | |||
462 | /// <summary> |
||
463 | /// Generates the code for a Declaration node. |
||
464 | /// </summary> |
||
465 | /// <param name="d">The Declaration node.</param> |
||
466 | /// <returns>String containing C# code for Declaration d.</returns> |
||
467 | private string GenerateDeclaration(Declaration d) |
||
468 | { |
||
469 | return Generate(String.Format("{0} {1}", d.Datatype, CheckName(d.Id)), d); |
||
470 | } |
||
471 | |||
472 | /// <summary> |
||
473 | /// Generates the code for a Statement node. |
||
474 | /// </summary> |
||
475 | /// <param name="s">The Statement node.</param> |
||
476 | /// <returns>String containing C# code for Statement s.</returns> |
||
477 | private string GenerateStatement(SYMBOL previousSymbol, Statement s) |
||
478 | { |
||
479 | string retstr = String.Empty; |
||
480 | bool printSemicolon = true; |
||
481 | bool transformToBlock = false; |
||
482 | |||
483 | if (m_insertCoopTerminationChecks) |
||
484 | { |
||
485 | // A non-braced single line do while structure cannot contain multiple statements. |
||
486 | // So to insert the termination check we change this to a braced control structure instead. |
||
487 | if (previousSymbol is WhileStatement |
||
488 | || previousSymbol is DoWhileStatement |
||
489 | || previousSymbol is ForLoop) |
||
490 | { |
||
491 | transformToBlock = true; |
||
492 | |||
493 | // FIXME: This will be wrongly indented because the previous for/while/dowhile will have already indented. |
||
494 | retstr += GenerateIndentedLine("{"); |
||
495 | |||
496 | retstr += GenerateIndentedLine(m_coopTerminationCheck); |
||
497 | } |
||
498 | } |
||
499 | |||
500 | retstr += Indent(); |
||
501 | |||
502 | if (0 < s.kids.Count) |
||
503 | { |
||
504 | // Jump label prints its own colon, we don't need a semicolon. |
||
505 | printSemicolon = !(s.kids.Top is JumpLabel); |
||
506 | |||
507 | // If we encounter a lone Ident, we skip it, since that's a C# |
||
508 | // (MONO) error. |
||
509 | if (!(s.kids.Top is IdentExpression && 1 == s.kids.Count)) |
||
510 | foreach (SYMBOL kid in s.kids) |
||
511 | retstr += GenerateNode(s, kid); |
||
512 | } |
||
513 | |||
514 | if (printSemicolon) |
||
515 | retstr += GenerateLine(";"); |
||
516 | |||
517 | if (transformToBlock) |
||
518 | { |
||
519 | // FIXME: This will be wrongly indented because the for/while/dowhile is currently handling the unindent |
||
520 | retstr += GenerateIndentedLine("}"); |
||
521 | } |
||
522 | |||
523 | return retstr; |
||
524 | } |
||
525 | |||
526 | /// <summary> |
||
527 | /// Generates the code for an Assignment node. |
||
528 | /// </summary> |
||
529 | /// <param name="a">The Assignment node.</param> |
||
530 | /// <returns>String containing C# code for Assignment a.</returns> |
||
531 | private string GenerateAssignment(Assignment a) |
||
532 | { |
||
533 | string retstr = String.Empty; |
||
534 | |||
535 | List<string> identifiers = new List<string>(); |
||
536 | checkForMultipleAssignments(identifiers, a); |
||
537 | |||
538 | retstr += GenerateNode(a, (SYMBOL) a.kids.Pop()); |
||
539 | retstr += Generate(String.Format(" {0} ", a.AssignmentType), a); |
||
540 | foreach (SYMBOL kid in a.kids) |
||
541 | retstr += GenerateNode(a, kid); |
||
542 | |||
543 | return retstr; |
||
544 | } |
||
545 | |||
546 | // This code checks for LSL of the following forms, and generates a |
||
547 | // warning if it finds them. |
||
548 | // |
||
549 | // list l = [ "foo" ]; |
||
550 | // l = (l=[]) + l + ["bar"]; |
||
551 | // (produces l=["foo","bar"] in SL but l=["bar"] in OS) |
||
552 | // |
||
553 | // integer i; |
||
554 | // integer j; |
||
555 | // i = (j = 3) + (j = 4) + (j = 5); |
||
556 | // (produces j=3 in SL but j=5 in OS) |
||
557 | // |
||
558 | // Without this check, that code passes compilation, but does not do what |
||
559 | // the end user expects, because LSL in SL evaluates right to left instead |
||
560 | // of left to right. |
||
561 | // |
||
562 | // The theory here is that producing an error and alerting the end user that |
||
563 | // something needs to change is better than silently generating incorrect code. |
||
564 | private void checkForMultipleAssignments(List<string> identifiers, SYMBOL s) |
||
565 | { |
||
566 | if (s is Assignment) |
||
567 | { |
||
568 | Assignment a = (Assignment)s; |
||
569 | string newident = null; |
||
570 | |||
571 | if (a.kids[0] is Declaration) |
||
572 | { |
||
573 | newident = ((Declaration)a.kids[0]).Id; |
||
574 | } |
||
575 | else if (a.kids[0] is IDENT) |
||
576 | { |
||
577 | newident = ((IDENT)a.kids[0]).yytext; |
||
578 | } |
||
579 | else if (a.kids[0] is IdentDotExpression) |
||
580 | { |
||
581 | newident = ((IdentDotExpression)a.kids[0]).Name; // +"." + ((IdentDotExpression)a.kids[0]).Member; |
||
582 | } |
||
583 | else |
||
584 | { |
||
585 | 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)); |
||
586 | } |
||
587 | |||
588 | if (identifiers.Contains(newident)) |
||
589 | { |
||
590 | 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)); |
||
591 | } |
||
592 | identifiers.Add(newident); |
||
593 | } |
||
594 | |||
595 | int index; |
||
596 | for (index = 0; index < s.kids.Count; index++) |
||
597 | { |
||
598 | checkForMultipleAssignments(identifiers, (SYMBOL) s.kids[index]); |
||
599 | } |
||
600 | } |
||
601 | |||
602 | /// <summary> |
||
603 | /// Generates the code for a ReturnStatement node. |
||
604 | /// </summary> |
||
605 | /// <param name="rs">The ReturnStatement node.</param> |
||
606 | /// <returns>String containing C# code for ReturnStatement rs.</returns> |
||
607 | private string GenerateReturnStatement(ReturnStatement rs) |
||
608 | { |
||
609 | string retstr = String.Empty; |
||
610 | |||
611 | retstr += Generate("return ", rs); |
||
612 | |||
613 | foreach (SYMBOL kid in rs.kids) |
||
614 | retstr += GenerateNode(rs, kid); |
||
615 | |||
616 | return retstr; |
||
617 | } |
||
618 | |||
619 | /// <summary> |
||
620 | /// Generates the code for a JumpLabel node. |
||
621 | /// </summary> |
||
622 | /// <param name="jl">The JumpLabel node.</param> |
||
623 | /// <returns>String containing C# code for JumpLabel jl.</returns> |
||
624 | private string GenerateJumpLabel(JumpLabel jl) |
||
625 | { |
||
626 | string labelStatement; |
||
627 | |||
628 | if (m_insertCoopTerminationChecks) |
||
629 | labelStatement = m_coopTerminationCheck + "\n"; |
||
630 | else |
||
631 | labelStatement = "NoOp();\n"; |
||
632 | |||
633 | return Generate(String.Format("{0}: ", CheckName(jl.LabelName)), jl) + labelStatement; |
||
634 | } |
||
635 | |||
636 | /// <summary> |
||
637 | /// Generates the code for a JumpStatement node. |
||
638 | /// </summary> |
||
639 | /// <param name="js">The JumpStatement node.</param> |
||
640 | /// <returns>String containing C# code for JumpStatement js.</returns> |
||
641 | private string GenerateJumpStatement(JumpStatement js) |
||
642 | { |
||
643 | return Generate(String.Format("goto {0}", CheckName(js.TargetName)), js); |
||
644 | } |
||
645 | |||
646 | /// <summary> |
||
647 | /// Generates the code for an IfStatement node. |
||
648 | /// </summary> |
||
649 | /// <param name="ifs">The IfStatement node.</param> |
||
650 | /// <returns>String containing C# code for IfStatement ifs.</returns> |
||
651 | private string GenerateIfStatement(IfStatement ifs) |
||
652 | { |
||
653 | string retstr = String.Empty; |
||
654 | |||
655 | retstr += GenerateIndented("if (", ifs); |
||
656 | retstr += GenerateNode(ifs, (SYMBOL) ifs.kids.Pop()); |
||
657 | retstr += GenerateLine(")"); |
||
658 | |||
659 | // CompoundStatement handles indentation itself but we need to do it |
||
660 | // otherwise. |
||
661 | bool indentHere = ifs.kids.Top is Statement; |
||
662 | if (indentHere) m_braceCount++; |
||
663 | retstr += GenerateNode(ifs, (SYMBOL) ifs.kids.Pop()); |
||
664 | if (indentHere) m_braceCount--; |
||
665 | |||
666 | if (0 < ifs.kids.Count) // do it again for an else |
||
667 | { |
||
668 | retstr += GenerateIndentedLine("else", ifs); |
||
669 | |||
670 | indentHere = ifs.kids.Top is Statement; |
||
671 | if (indentHere) m_braceCount++; |
||
672 | retstr += GenerateNode(ifs, (SYMBOL) ifs.kids.Pop()); |
||
673 | if (indentHere) m_braceCount--; |
||
674 | } |
||
675 | |||
676 | return retstr; |
||
677 | } |
||
678 | |||
679 | /// <summary> |
||
680 | /// Generates the code for a StateChange node. |
||
681 | /// </summary> |
||
682 | /// <param name="sc">The StateChange node.</param> |
||
683 | /// <returns>String containing C# code for StateChange sc.</returns> |
||
684 | private string GenerateStateChange(StateChange sc) |
||
685 | { |
||
686 | return Generate(String.Format("state(\"{0}\")", sc.NewState), sc); |
||
687 | } |
||
688 | |||
689 | /// <summary> |
||
690 | /// Generates the code for a WhileStatement node. |
||
691 | /// </summary> |
||
692 | /// <param name="ws">The WhileStatement node.</param> |
||
693 | /// <returns>String containing C# code for WhileStatement ws.</returns> |
||
694 | private string GenerateWhileStatement(WhileStatement ws) |
||
695 | { |
||
696 | string retstr = String.Empty; |
||
697 | |||
698 | retstr += GenerateIndented("while (", ws); |
||
699 | retstr += GenerateNode(ws, (SYMBOL) ws.kids.Pop()); |
||
700 | retstr += GenerateLine(")"); |
||
701 | |||
702 | // CompoundStatement handles indentation itself but we need to do it |
||
703 | // otherwise. |
||
704 | bool indentHere = ws.kids.Top is Statement; |
||
705 | if (indentHere) m_braceCount++; |
||
706 | retstr += GenerateNode(ws, (SYMBOL) ws.kids.Pop()); |
||
707 | if (indentHere) m_braceCount--; |
||
708 | |||
709 | return retstr; |
||
710 | } |
||
711 | |||
712 | /// <summary> |
||
713 | /// Generates the code for a DoWhileStatement node. |
||
714 | /// </summary> |
||
715 | /// <param name="dws">The DoWhileStatement node.</param> |
||
716 | /// <returns>String containing C# code for DoWhileStatement dws.</returns> |
||
717 | private string GenerateDoWhileStatement(DoWhileStatement dws) |
||
718 | { |
||
719 | string retstr = String.Empty; |
||
720 | |||
721 | retstr += GenerateIndentedLine("do", dws); |
||
722 | |||
723 | // CompoundStatement handles indentation itself but we need to do it |
||
724 | // otherwise. |
||
725 | bool indentHere = dws.kids.Top is Statement; |
||
726 | if (indentHere) m_braceCount++; |
||
727 | retstr += GenerateNode(dws, (SYMBOL) dws.kids.Pop()); |
||
728 | if (indentHere) m_braceCount--; |
||
729 | |||
730 | retstr += GenerateIndented("while (", dws); |
||
731 | retstr += GenerateNode(dws, (SYMBOL) dws.kids.Pop()); |
||
732 | retstr += GenerateLine(");"); |
||
733 | |||
734 | return retstr; |
||
735 | } |
||
736 | |||
737 | /// <summary> |
||
738 | /// Generates the code for a ForLoop node. |
||
739 | /// </summary> |
||
740 | /// <param name="fl">The ForLoop node.</param> |
||
741 | /// <returns>String containing C# code for ForLoop fl.</returns> |
||
742 | private string GenerateForLoop(ForLoop fl) |
||
743 | { |
||
744 | string retstr = String.Empty; |
||
745 | |||
746 | retstr += GenerateIndented("for (", fl); |
||
747 | |||
748 | // It's possible that we don't have an assignment, in which case |
||
749 | // the child will be null and we only print the semicolon. |
||
750 | // for (x = 0; x < 10; x++) |
||
751 | // ^^^^^ |
||
752 | ForLoopStatement s = (ForLoopStatement) fl.kids.Pop(); |
||
753 | if (null != s) |
||
754 | { |
||
755 | retstr += GenerateForLoopStatement(s); |
||
756 | } |
||
757 | retstr += Generate("; "); |
||
758 | // for (x = 0; x < 10; x++) |
||
759 | // ^^^^^^ |
||
760 | retstr += GenerateNode(fl, (SYMBOL) fl.kids.Pop()); |
||
761 | retstr += Generate("; "); |
||
762 | // for (x = 0; x < 10; x++) |
||
763 | // ^^^ |
||
764 | retstr += GenerateForLoopStatement((ForLoopStatement) fl.kids.Pop()); |
||
765 | retstr += GenerateLine(")"); |
||
766 | |||
767 | // CompoundStatement handles indentation itself but we need to do it |
||
768 | // otherwise. |
||
769 | bool indentHere = fl.kids.Top is Statement; |
||
770 | if (indentHere) m_braceCount++; |
||
771 | retstr += GenerateNode(fl, (SYMBOL) fl.kids.Pop()); |
||
772 | if (indentHere) m_braceCount--; |
||
773 | |||
774 | return retstr; |
||
775 | } |
||
776 | |||
777 | /// <summary> |
||
778 | /// Generates the code for a ForLoopStatement node. |
||
779 | /// </summary> |
||
780 | /// <param name="fls">The ForLoopStatement node.</param> |
||
781 | /// <returns>String containing C# code for ForLoopStatement fls.</returns> |
||
782 | private string GenerateForLoopStatement(ForLoopStatement fls) |
||
783 | { |
||
784 | string retstr = String.Empty; |
||
785 | |||
786 | int comma = fls.kids.Count - 1; // tells us whether to print a comma |
||
787 | |||
788 | // It's possible that all we have is an empty Ident, for example: |
||
789 | // |
||
790 | // for (x; x < 10; x++) { ... } |
||
791 | // |
||
792 | // Which is illegal in C# (MONO). We'll skip it. |
||
793 | if (fls.kids.Top is IdentExpression && 1 == fls.kids.Count) |
||
794 | return retstr; |
||
795 | |||
796 | for (int i = 0; i < fls.kids.Count; i++) |
||
797 | { |
||
798 | SYMBOL s = (SYMBOL)fls.kids[i]; |
||
799 | |||
800 | // Statements surrounded by parentheses in for loops |
||
801 | // |
||
802 | // e.g. for ((i = 0), (j = 7); (i < 10); (++i)) |
||
803 | // |
||
804 | // are legal in LSL but not in C# so we need to discard the parentheses |
||
805 | // |
||
806 | // The following, however, does not appear to be legal in LLS |
||
807 | // |
||
808 | // for ((i = 0, j = 7); (i < 10); (++i)) |
||
809 | // |
||
810 | // As of Friday 20th November 2009, the Linden Lab simulators appear simply never to compile or run this |
||
811 | // script but with no debug or warnings at all! Therefore, we won't deal with this yet (which looks |
||
812 | // like it would be considerably more complicated to handle). |
||
813 | while (s is ParenthesisExpression) |
||
814 | s = (SYMBOL)s.kids.Pop(); |
||
815 | |||
816 | retstr += GenerateNode(fls, s); |
||
817 | if (0 < comma--) |
||
818 | retstr += Generate(", "); |
||
819 | } |
||
820 | |||
821 | return retstr; |
||
822 | } |
||
823 | |||
824 | /// <summary> |
||
825 | /// Generates the code for a BinaryExpression node. |
||
826 | /// </summary> |
||
827 | /// <param name="be">The BinaryExpression node.</param> |
||
828 | /// <returns>String containing C# code for BinaryExpression be.</returns> |
||
829 | private string GenerateBinaryExpression(BinaryExpression be) |
||
830 | { |
||
831 | string retstr = String.Empty; |
||
832 | |||
833 | if (be.ExpressionSymbol.Equals("&&") || be.ExpressionSymbol.Equals("||")) |
||
834 | { |
||
835 | // special case handling for logical and/or, see Mantis 3174 |
||
836 | retstr += "((bool)("; |
||
837 | retstr += GenerateNode(be, (SYMBOL)be.kids.Pop()); |
||
838 | retstr += "))"; |
||
839 | retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol.Substring(0,1)), be); |
||
840 | retstr += "((bool)("; |
||
841 | foreach (SYMBOL kid in be.kids) |
||
842 | retstr += GenerateNode(be, kid); |
||
843 | retstr += "))"; |
||
844 | } |
||
845 | else |
||
846 | { |
||
847 | retstr += GenerateNode(be, (SYMBOL)be.kids.Pop()); |
||
848 | retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol), be); |
||
849 | foreach (SYMBOL kid in be.kids) |
||
850 | retstr += GenerateNode(be, kid); |
||
851 | } |
||
852 | |||
853 | return retstr; |
||
854 | } |
||
855 | |||
856 | /// <summary> |
||
857 | /// Generates the code for a UnaryExpression node. |
||
858 | /// </summary> |
||
859 | /// <param name="ue">The UnaryExpression node.</param> |
||
860 | /// <returns>String containing C# code for UnaryExpression ue.</returns> |
||
861 | private string GenerateUnaryExpression(UnaryExpression ue) |
||
862 | { |
||
863 | string retstr = String.Empty; |
||
864 | |||
865 | retstr += Generate(ue.UnarySymbol, ue); |
||
866 | retstr += GenerateNode(ue, (SYMBOL) ue.kids.Pop()); |
||
867 | |||
868 | return retstr; |
||
869 | } |
||
870 | |||
871 | /// <summary> |
||
872 | /// Generates the code for a ParenthesisExpression node. |
||
873 | /// </summary> |
||
874 | /// <param name="pe">The ParenthesisExpression node.</param> |
||
875 | /// <returns>String containing C# code for ParenthesisExpression pe.</returns> |
||
876 | private string GenerateParenthesisExpression(ParenthesisExpression pe) |
||
877 | { |
||
878 | string retstr = String.Empty; |
||
879 | |||
880 | retstr += Generate("("); |
||
881 | foreach (SYMBOL kid in pe.kids) |
||
882 | retstr += GenerateNode(pe, kid); |
||
883 | retstr += Generate(")"); |
||
884 | |||
885 | return retstr; |
||
886 | } |
||
887 | |||
888 | /// <summary> |
||
889 | /// Generates the code for a IncrementDecrementExpression node. |
||
890 | /// </summary> |
||
891 | /// <param name="ide">The IncrementDecrementExpression node.</param> |
||
892 | /// <returns>String containing C# code for IncrementDecrementExpression ide.</returns> |
||
893 | private string GenerateIncrementDecrementExpression(IncrementDecrementExpression ide) |
||
894 | { |
||
895 | string retstr = String.Empty; |
||
896 | |||
897 | if (0 < ide.kids.Count) |
||
898 | { |
||
899 | IdentDotExpression dot = (IdentDotExpression) ide.kids.Top; |
||
900 | retstr += Generate(String.Format("{0}", ide.PostOperation ? CheckName(dot.Name) + "." + dot.Member + ide.Operation : ide.Operation + CheckName(dot.Name) + "." + dot.Member), ide); |
||
901 | } |
||
902 | else |
||
903 | retstr += Generate(String.Format("{0}", ide.PostOperation ? CheckName(ide.Name) + ide.Operation : ide.Operation + CheckName(ide.Name)), ide); |
||
904 | |||
905 | return retstr; |
||
906 | } |
||
907 | |||
908 | /// <summary> |
||
909 | /// Generates the code for a TypecastExpression node. |
||
910 | /// </summary> |
||
911 | /// <param name="te">The TypecastExpression node.</param> |
||
912 | /// <returns>String containing C# code for TypecastExpression te.</returns> |
||
913 | private string GenerateTypecastExpression(TypecastExpression te) |
||
914 | { |
||
915 | string retstr = String.Empty; |
||
916 | |||
917 | // we wrap all typecasted statements in parentheses |
||
918 | retstr += Generate(String.Format("({0}) (", te.TypecastType), te); |
||
919 | retstr += GenerateNode(te, (SYMBOL) te.kids.Pop()); |
||
920 | retstr += Generate(")"); |
||
921 | |||
922 | return retstr; |
||
923 | } |
||
924 | |||
925 | /// <summary> |
||
926 | /// Generates the code for an identifier |
||
927 | /// </summary> |
||
928 | /// <param name="id">The symbol name</param> |
||
929 | /// <param name="s">The Symbol node.</param> |
||
930 | /// <returns>String containing C# code for identifier reference.</returns> |
||
931 | private string GenerateIdentifier(string id, SYMBOL s) |
||
932 | { |
||
933 | if (m_comms != null) |
||
934 | { |
||
935 | object value = m_comms.LookupModConstant(id); |
||
936 | if (value != null) |
||
937 | { |
||
938 | string retval = null; |
||
939 | if (value is int) |
||
940 | retval = String.Format("new LSL_Types.LSLInteger({0})",((int)value).ToString()); |
||
941 | else if (value is float) |
||
942 | retval = String.Format("new LSL_Types.LSLFloat({0})",((float)value).ToString()); |
||
943 | else if (value is string) |
||
944 | retval = String.Format("new LSL_Types.LSLString(\"{0}\")",((string)value)); |
||
945 | else if (value is OpenMetaverse.UUID) |
||
946 | retval = String.Format("new LSL_Types.key(\"{0}\")",((OpenMetaverse.UUID)value).ToString()); |
||
947 | else if (value is OpenMetaverse.Vector3) |
||
948 | retval = String.Format("new LSL_Types.Vector3(\"{0}\")",((OpenMetaverse.Vector3)value).ToString()); |
||
949 | else if (value is OpenMetaverse.Quaternion) |
||
950 | retval = String.Format("new LSL_Types.Quaternion(\"{0}\")",((OpenMetaverse.Quaternion)value).ToString()); |
||
951 | else retval = id; |
||
952 | |||
953 | return Generate(retval, s); |
||
954 | } |
||
955 | } |
||
956 | |||
957 | return Generate(CheckName(id), s); |
||
958 | } |
||
959 | |||
960 | /// <summary> |
||
961 | /// Generates the code for a FunctionCall node. |
||
962 | /// </summary> |
||
963 | /// <param name="fc">The FunctionCall node.</param> |
||
964 | /// <returns>String containing C# code for FunctionCall fc.</returns> |
||
965 | private string GenerateFunctionCall(FunctionCall fc) |
||
966 | { |
||
967 | string retstr = String.Empty; |
||
968 | |||
969 | string modinvoke = null; |
||
970 | if (m_comms != null) |
||
971 | modinvoke = m_comms.LookupModInvocation(fc.Id); |
||
972 | |||
973 | if (modinvoke != null) |
||
974 | { |
||
975 | if (fc.kids[0] is ArgumentList) |
||
976 | { |
||
977 | if ((fc.kids[0] as ArgumentList).kids.Count == 0) |
||
978 | retstr += Generate(String.Format("{0}(\"{1}\"",modinvoke,fc.Id), fc); |
||
979 | else |
||
980 | retstr += Generate(String.Format("{0}(\"{1}\",",modinvoke,fc.Id), fc); |
||
981 | } |
||
982 | } |
||
983 | else |
||
984 | { |
||
985 | retstr += Generate(String.Format("{0}(", CheckName(fc.Id)), fc); |
||
986 | } |
||
987 | |||
988 | foreach (SYMBOL kid in fc.kids) |
||
989 | retstr += GenerateNode(fc, kid); |
||
990 | |||
991 | retstr += Generate(")"); |
||
992 | |||
993 | return retstr; |
||
994 | } |
||
995 | |||
996 | /// <summary> |
||
997 | /// Generates the code for a Constant node. |
||
998 | /// </summary> |
||
999 | /// <param name="c">The Constant node.</param> |
||
1000 | /// <returns>String containing C# code for Constant c.</returns> |
||
1001 | private string GenerateConstant(Constant c) |
||
1002 | { |
||
1003 | string retstr = String.Empty; |
||
1004 | |||
1005 | // Supprt LSL's weird acceptance of floats with no trailing digits |
||
1006 | // after the period. Turn float x = 10.; into float x = 10.0; |
||
1007 | if ("LSL_Types.LSLFloat" == c.Type) |
||
1008 | { |
||
1009 | int dotIndex = c.Value.IndexOf('.') + 1; |
||
1010 | if (0 < dotIndex && (dotIndex == c.Value.Length || !Char.IsDigit(c.Value[dotIndex]))) |
||
1011 | c.Value = c.Value.Insert(dotIndex, "0"); |
||
1012 | c.Value = "new LSL_Types.LSLFloat("+c.Value+")"; |
||
1013 | } |
||
1014 | else if ("LSL_Types.LSLInteger" == c.Type) |
||
1015 | { |
||
1016 | c.Value = "new LSL_Types.LSLInteger("+c.Value+")"; |
||
1017 | } |
||
1018 | else if ("LSL_Types.LSLString" == c.Type) |
||
1019 | { |
||
1020 | c.Value = "new LSL_Types.LSLString(\""+c.Value+"\")"; |
||
1021 | } |
||
1022 | |||
1023 | retstr += Generate(c.Value, c); |
||
1024 | |||
1025 | return retstr; |
||
1026 | } |
||
1027 | |||
1028 | /// <summary> |
||
1029 | /// Generates the code for a VectorConstant node. |
||
1030 | /// </summary> |
||
1031 | /// <param name="vc">The VectorConstant node.</param> |
||
1032 | /// <returns>String containing C# code for VectorConstant vc.</returns> |
||
1033 | private string GenerateVectorConstant(VectorConstant vc) |
||
1034 | { |
||
1035 | string retstr = String.Empty; |
||
1036 | |||
1037 | retstr += Generate(String.Format("new {0}(", vc.Type), vc); |
||
1038 | retstr += GenerateNode(vc, (SYMBOL) vc.kids.Pop()); |
||
1039 | retstr += Generate(", "); |
||
1040 | retstr += GenerateNode(vc, (SYMBOL) vc.kids.Pop()); |
||
1041 | retstr += Generate(", "); |
||
1042 | retstr += GenerateNode(vc, (SYMBOL) vc.kids.Pop()); |
||
1043 | retstr += Generate(")"); |
||
1044 | |||
1045 | return retstr; |
||
1046 | } |
||
1047 | |||
1048 | /// <summary> |
||
1049 | /// Generates the code for a RotationConstant node. |
||
1050 | /// </summary> |
||
1051 | /// <param name="rc">The RotationConstant node.</param> |
||
1052 | /// <returns>String containing C# code for RotationConstant rc.</returns> |
||
1053 | private string GenerateRotationConstant(RotationConstant rc) |
||
1054 | { |
||
1055 | string retstr = String.Empty; |
||
1056 | |||
1057 | retstr += Generate(String.Format("new {0}(", rc.Type), rc); |
||
1058 | retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop()); |
||
1059 | retstr += Generate(", "); |
||
1060 | retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop()); |
||
1061 | retstr += Generate(", "); |
||
1062 | retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop()); |
||
1063 | retstr += Generate(", "); |
||
1064 | retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop()); |
||
1065 | retstr += Generate(")"); |
||
1066 | |||
1067 | return retstr; |
||
1068 | } |
||
1069 | |||
1070 | /// <summary> |
||
1071 | /// Generates the code for a ListConstant node. |
||
1072 | /// </summary> |
||
1073 | /// <param name="lc">The ListConstant node.</param> |
||
1074 | /// <returns>String containing C# code for ListConstant lc.</returns> |
||
1075 | private string GenerateListConstant(ListConstant lc) |
||
1076 | { |
||
1077 | string retstr = String.Empty; |
||
1078 | |||
1079 | retstr += Generate(String.Format("new {0}(", lc.Type), lc); |
||
1080 | |||
1081 | foreach (SYMBOL kid in lc.kids) |
||
1082 | retstr += GenerateNode(lc, kid); |
||
1083 | |||
1084 | retstr += Generate(")"); |
||
1085 | |||
1086 | return retstr; |
||
1087 | } |
||
1088 | |||
1089 | /// <summary> |
||
1090 | /// Prints a newline. |
||
1091 | /// </summary> |
||
1092 | /// <returns>A newline.</returns> |
||
1093 | private string GenerateLine() |
||
1094 | { |
||
1095 | return GenerateLine(""); |
||
1096 | } |
||
1097 | |||
1098 | /// <summary> |
||
1099 | /// Prints text, followed by a newline. |
||
1100 | /// </summary> |
||
1101 | /// <param name="s">String of text to print.</param> |
||
1102 | /// <returns>String s followed by newline.</returns> |
||
1103 | private string GenerateLine(string s) |
||
1104 | { |
||
1105 | return GenerateLine(s, null); |
||
1106 | } |
||
1107 | |||
1108 | /// <summary> |
||
1109 | /// Prints text, followed by a newline. |
||
1110 | /// </summary> |
||
1111 | /// <param name="s">String of text to print.</param> |
||
1112 | /// <param name="sym">Symbol being generated to extract original line |
||
1113 | /// number and column from.</param> |
||
1114 | /// <returns>String s followed by newline.</returns> |
||
1115 | private string GenerateLine(string s, SYMBOL sym) |
||
1116 | { |
||
1117 | string retstr = Generate(s, sym) + "\n"; |
||
1118 | |||
1119 | m_CSharpLine++; |
||
1120 | m_CSharpCol = 1; |
||
1121 | |||
1122 | return retstr; |
||
1123 | } |
||
1124 | |||
1125 | /// <summary> |
||
1126 | /// Prints text. |
||
1127 | /// </summary> |
||
1128 | /// <param name="s">String of text to print.</param> |
||
1129 | /// <returns>String s.</returns> |
||
1130 | private string Generate(string s) |
||
1131 | { |
||
1132 | return Generate(s, null); |
||
1133 | } |
||
1134 | |||
1135 | /// <summary> |
||
1136 | /// Prints text. |
||
1137 | /// </summary> |
||
1138 | /// <param name="s">String of text to print.</param> |
||
1139 | /// <param name="sym">Symbol being generated to extract original line |
||
1140 | /// number and column from.</param> |
||
1141 | /// <returns>String s.</returns> |
||
1142 | private string Generate(string s, SYMBOL sym) |
||
1143 | { |
||
1144 | if (null != sym) |
||
1145 | m_positionMap.Add(new KeyValuePair<int, int>(m_CSharpLine, m_CSharpCol), new KeyValuePair<int, int>(sym.Line, sym.Position)); |
||
1146 | |||
1147 | m_CSharpCol += s.Length; |
||
1148 | |||
1149 | return s; |
||
1150 | } |
||
1151 | |||
1152 | /// <summary> |
||
1153 | /// Prints text correctly indented, followed by a newline. |
||
1154 | /// </summary> |
||
1155 | /// <param name="s">String of text to print.</param> |
||
1156 | /// <returns>Properly indented string s followed by newline.</returns> |
||
1157 | private string GenerateIndentedLine(string s) |
||
1158 | { |
||
1159 | return GenerateIndentedLine(s, null); |
||
1160 | } |
||
1161 | |||
1162 | /// <summary> |
||
1163 | /// Prints text correctly indented, followed by a newline. |
||
1164 | /// </summary> |
||
1165 | /// <param name="s">String of text to print.</param> |
||
1166 | /// <param name="sym">Symbol being generated to extract original line |
||
1167 | /// number and column from.</param> |
||
1168 | /// <returns>Properly indented string s followed by newline.</returns> |
||
1169 | private string GenerateIndentedLine(string s, SYMBOL sym) |
||
1170 | { |
||
1171 | string retstr = GenerateIndented(s, sym) + "\n"; |
||
1172 | |||
1173 | m_CSharpLine++; |
||
1174 | m_CSharpCol = 1; |
||
1175 | |||
1176 | return retstr; |
||
1177 | } |
||
1178 | |||
1179 | /// <summary> |
||
1180 | /// Prints text correctly indented. |
||
1181 | /// </summary> |
||
1182 | /// <param name="s">String of text to print.</param> |
||
1183 | /// <returns>Properly indented string s.</returns> |
||
1184 | //private string GenerateIndented(string s) |
||
1185 | //{ |
||
1186 | // return GenerateIndented(s, null); |
||
1187 | //} |
||
1188 | // THIS FUNCTION IS COMMENTED OUT TO SUPPRESS WARNINGS |
||
1189 | |||
1190 | /// <summary> |
||
1191 | /// Prints text correctly indented. |
||
1192 | /// </summary> |
||
1193 | /// <param name="s">String of text to print.</param> |
||
1194 | /// <param name="sym">Symbol being generated to extract original line |
||
1195 | /// number and column from.</param> |
||
1196 | /// <returns>Properly indented string s.</returns> |
||
1197 | private string GenerateIndented(string s, SYMBOL sym) |
||
1198 | { |
||
1199 | string retstr = Indent() + s; |
||
1200 | |||
1201 | if (null != sym) |
||
1202 | m_positionMap.Add(new KeyValuePair<int, int>(m_CSharpLine, m_CSharpCol), new KeyValuePair<int, int>(sym.Line, sym.Position)); |
||
1203 | |||
1204 | m_CSharpCol += s.Length; |
||
1205 | |||
1206 | return retstr; |
||
1207 | } |
||
1208 | |||
1209 | /// <summary> |
||
1210 | /// Prints correct indentation. |
||
1211 | /// </summary> |
||
1212 | /// <returns>Indentation based on brace count.</returns> |
||
1213 | private string Indent() |
||
1214 | { |
||
1215 | string retstr = String.Empty; |
||
1216 | |||
1217 | for (int i = 0; i < m_braceCount; i++) |
||
1218 | for (int j = 0; j < m_indentWidth; j++) |
||
1219 | { |
||
1220 | retstr += " "; |
||
1221 | m_CSharpCol++; |
||
1222 | } |
||
1223 | |||
1224 | return retstr; |
||
1225 | } |
||
1226 | |||
1227 | /// <summary> |
||
1228 | /// Returns the passed name with an underscore prepended if that name is a reserved word in C# |
||
1229 | /// and not resevered in LSL otherwise it just returns the passed name. |
||
1230 | /// |
||
1231 | /// This makes no attempt to cache the results to minimise future lookups. For a non trivial |
||
1232 | /// scripts the number of unique identifiers could easily grow to the size of the reserved word |
||
1233 | /// list so maintaining a list or dictionary and doing the lookup there firstwould probably not |
||
1234 | /// give any real speed advantage. |
||
1235 | /// |
||
1236 | /// I believe there is a class Microsoft.CSharp.CSharpCodeProvider that has a function |
||
1237 | /// CreateValidIdentifier(str) that will return either the value of str if it is not a C# |
||
1238 | /// key word or "_"+str if it is. But availability under Mono? |
||
1239 | /// </summary> |
||
1240 | private string CheckName(string s) |
||
1241 | { |
||
1242 | if (CSReservedWords.IsReservedWord(s)) |
||
1243 | return "@" + s; |
||
1244 | else |
||
1245 | return s; |
||
1246 | } |
||
1247 | } |
||
1248 | } |