PageRenderTime 59ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/SharpTAL/CodeGenerator.cs

https://bitbucket.org/rlacko/sharptal
C# | 1232 lines | 1067 code | 100 blank | 65 comment | 39 complexity | 7d24cc7a2a74e0b723c9cff5a0c08b64 MD5 | raw file
Possible License(s): Apache-2.0
  1. //
  2. // CodeGenerator.cs
  3. //
  4. // Author:
  5. // Roman Lacko (backup.rlacko@gmail.com)
  6. //
  7. // Copyright (c) 2010 - 2013 Roman Lacko
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. namespace SharpTAL
  29. {
  30. using System;
  31. using System.Linq;
  32. using System.Collections.Generic;
  33. using System.IO;
  34. using System.Reflection;
  35. using System.Text;
  36. using System.Text.RegularExpressions;
  37. using System.Diagnostics;
  38. using System.Runtime.Serialization;
  39. using ICSharpCode.NRefactory;
  40. using ICSharpCode.NRefactory.CSharp;
  41. using SharpTAL.TemplateParser;
  42. using SharpTAL.TemplateProgram;
  43. using SharpTAL.TemplateProgram.Commands;
  44. public class CodeGenerator : AbstractProgramInterpreter, ICodeGenerator
  45. {
  46. protected static readonly string FileBodyTemplate =
  47. @"${usings}
  48. namespace Templates
  49. {
  50. [SecurityPermission(SecurityAction.PermitOnly, Execution = true)]
  51. public class Template_${template_hash}
  52. {
  53. public static void Render(StreamWriter output, SharpTAL.IRenderContext context)
  54. {
  55. Stack<List<object>> __programStack = new Stack<List<object>>();
  56. Stack<List<object>> __scopeStack = new Stack<List<object>>();
  57. bool __moveToEndTag = false;
  58. int __outputTag = 1;
  59. object __tagContent = null;
  60. int __tagContentType = 1;
  61. Dictionary<string, string> __macros = new Dictionary<string, string>();
  62. Dictionary<string, Attr> __currentAttributes = new Dictionary<string, Attr>();
  63. Dictionary<string, Attr> __repeatAttributesCopy = new Dictionary<string, Attr>();
  64. Dictionary<string, MacroDelegate> __slotMap = new Dictionary<string, MacroDelegate>();
  65. Dictionary<string, MacroDelegate> __currentSlots = new Dictionary<string, MacroDelegate>();
  66. Dictionary<string, object> __paramMap = new Dictionary<string, object>();
  67. Dictionary<string, object> __currentParams = new Dictionary<string, object>();
  68. CommandInfo __currentCmdInfo = new CommandInfo();
  69. // Template globals
  70. var repeat = (SharpTAL.IRepeatDictionary)context[""repeat""];
  71. // TODO: get macros from context
  72. Dictionary<string, MacroDelegate> macros = new Dictionary<string, MacroDelegate>();
  73. FormatResult = (Func<object, string>)context[""__FormatResult""];
  74. IsFalseResult = (Func<object, bool>)context[""__IsFalseResult""];
  75. try
  76. {
  77. // Globals
  78. ${globals}
  79. // Delegates
  80. MacroDelegate __CleanProgram = delegate()
  81. {
  82. // Clean state
  83. __scopeStack = new Stack<List<object>>();
  84. __moveToEndTag = false;
  85. __outputTag = 1;
  86. __tagContent = null;
  87. __tagContentType = 0;
  88. __currentAttributes = new Dictionary<string, Attr>();
  89. __currentCmdInfo = null;
  90. // Used in repeat only.
  91. __repeatAttributesCopy = new Dictionary<string, Attr>();
  92. // Pass in the macro slots and params
  93. __currentSlots = __slotMap;
  94. __currentParams = __paramMap;
  95. };
  96. MacroDelegate __PushProgram = delegate()
  97. {
  98. List<object> vars = new List<object>() {
  99. __scopeStack,
  100. __slotMap,
  101. __paramMap,
  102. __currentSlots,
  103. __moveToEndTag,
  104. __outputTag,
  105. __tagContent,
  106. __tagContentType,
  107. __currentAttributes,
  108. __repeatAttributesCopy,
  109. __currentCmdInfo
  110. };
  111. __programStack.Push(vars);
  112. };
  113. MacroDelegate __PopProgram = delegate()
  114. {
  115. List<object> vars = __programStack.Pop();
  116. __scopeStack = (Stack<List<object>>)vars[0];
  117. __slotMap = (Dictionary<string, MacroDelegate>)vars[1];
  118. __paramMap = (Dictionary<string, object>)vars[2];
  119. __currentSlots = (Dictionary<string, MacroDelegate>)vars[3];
  120. __moveToEndTag = (bool)vars[4];
  121. __outputTag = (int)vars[5];
  122. __tagContent = (object)vars[6];
  123. __tagContentType = (int)vars[7];
  124. __currentAttributes = (Dictionary<string, Attr>)vars[8];
  125. __repeatAttributesCopy = (Dictionary<string, Attr>)vars[9];
  126. __currentCmdInfo = (CommandInfo)vars[10];
  127. };
  128. ${body}
  129. }
  130. catch (Exception ex)
  131. {
  132. string msg = string.Format(""Render method failed with following error:{0} {1}"", Environment.NewLine, ex.Message);
  133. // Current Command Info
  134. msg = string.Format(""{0}{1}{1}Current Command Info:"", msg, Environment.NewLine);
  135. if (__currentCmdInfo != null)
  136. {
  137. msg = string.Format(@""{0}{1} Command: {2}"", msg, Environment.NewLine, __currentCmdInfo.CommandName);
  138. msg = string.Format(@""{0}{1} Tag: {2}"", msg, Environment.NewLine, __currentCmdInfo.Tag);
  139. msg = string.Format(@""{0}{1} Line: {2}"", msg, Environment.NewLine, __currentCmdInfo.Line);
  140. msg = string.Format(@""{0}{1} Position: {2}"", msg, Environment.NewLine, __currentCmdInfo.Position);
  141. msg = string.Format(@""{0}{1} Source: {2}"", msg, Environment.NewLine, __currentCmdInfo.Source);
  142. }
  143. // Macros
  144. msg = string.Format(""{0}{1}{1}Macros:"", msg, Environment.NewLine);
  145. foreach (string key in __macros.Keys)
  146. {
  147. string importInfo = """";
  148. if (!string.IsNullOrEmpty(__macros[key]))
  149. {
  150. importInfo = string.Format(@"" imported from """"{0}"""""", __macros[key]);
  151. }
  152. msg = string.Format(@""{0}{1} """"{2}""""{3}"", msg, Environment.NewLine, key, importInfo);
  153. }
  154. throw new Exception(msg, ex);
  155. }
  156. }
  157. private static Func<object, string> FormatResult { get; set; }
  158. private static Func<object, bool> IsFalseResult { get; set; }
  159. private const string DEFAULT_VALUE = ""${defaultvalue}"";
  160. private static readonly Regex _re_needs_escape = new Regex(@""[&<>""""\']"");
  161. private static readonly Regex _re_amp = new Regex(@""&(?!([A-Za-z]+|#[0-9]+);)"");
  162. private delegate void MacroDelegate();
  163. private class ProgramNamespace
  164. {
  165. public Dictionary<string, MacroDelegate> macros = new Dictionary<string, MacroDelegate>();
  166. }
  167. private class CommandInfo
  168. {
  169. public CommandInfo()
  170. {
  171. }
  172. public CommandInfo(string commandName)
  173. {
  174. CommandName = commandName;
  175. }
  176. public CommandInfo(string commandName, string tag, int line, int position, string source)
  177. {
  178. CommandName = commandName;
  179. Tag = tag;
  180. Line = line;
  181. Position = position;
  182. Source = source;
  183. }
  184. public string CommandName;
  185. public string Tag;
  186. public int Line;
  187. public int Position;
  188. public string Source;
  189. }
  190. private class Attr
  191. {
  192. public string Name;
  193. public string Value;
  194. public string Eq;
  195. public string Quote;
  196. public string QuoteEntity;
  197. }
  198. private static string Escape(string str)
  199. {
  200. if (string.IsNullOrEmpty(str))
  201. return str;
  202. if (!_re_needs_escape.IsMatch(str))
  203. return str;
  204. if (str.IndexOf('&') >= 0)
  205. {
  206. if (str.IndexOf(';') >= 0)
  207. str = _re_amp.Replace(str, ""&amp;"");
  208. else
  209. str = str.Replace(""&"", ""&amp;"");
  210. }
  211. if (str.IndexOf('>') >= 0)
  212. str = str.Replace(""<"", ""&lt;"");
  213. if (str.IndexOf('>') >= 0)
  214. str = str.Replace("">"", ""&gt;"");
  215. return str;
  216. }
  217. private static string EscapeAttrValue(string str, string quote, string quoteEntity)
  218. {
  219. if (string.IsNullOrEmpty(str))
  220. return str;
  221. str = Escape(str);
  222. if (!string.IsNullOrEmpty(quote) && str.IndexOf(quote) >= 0)
  223. str = str.Replace(quote, quoteEntity);
  224. return str;
  225. }
  226. private static bool IsDefaultValue(object obj)
  227. {
  228. if ((obj is string) && ((string)obj) == DEFAULT_VALUE)
  229. {
  230. return true;
  231. }
  232. return false;
  233. }
  234. }
  235. }
  236. ";
  237. const string MAIN_PROGRAM_NAMESPACE = "template";
  238. const string DEFAULT_VALUE_EXPRESSION = "default";
  239. protected class Scope
  240. {
  241. public string ID;
  242. public Stack<SubScope> SubScope = new Stack<SubScope>();
  243. public bool Interpolation { get; set; }
  244. }
  245. protected class SubScope
  246. {
  247. public string ID;
  248. }
  249. protected class RepeatScope : SubScope
  250. {
  251. public string VarName;
  252. }
  253. protected Dictionary<string, string> typeNamesCache;
  254. protected List<string> globalNames;
  255. protected StringBuilder globalsBody;
  256. protected string globalsBodyTabs;
  257. protected StringBuilder rendererBody;
  258. protected string rendererBodyTabs;
  259. protected Scope currentScope;
  260. protected Stack<Scope> scopeStack;
  261. // TODO: use this instead of generating guids
  262. protected ObjectIDGenerator idGen = new ObjectIDGenerator();
  263. public CodeGenerator()
  264. {
  265. typeNamesCache = new Dictionary<string, string>();
  266. globalNames = new List<string>();
  267. globalsBody = new StringBuilder();
  268. globalsBodyTabs = " ";
  269. rendererBody = new StringBuilder();
  270. rendererBodyTabs = " ";
  271. scopeStack = new Stack<Scope>();
  272. }
  273. public string GenerateCode(TemplateInfo ti)
  274. {
  275. scopeStack = new Stack<Scope>();
  276. //----------------
  277. // Process globals
  278. //----------------
  279. if (ti.GlobalsTypes != null)
  280. {
  281. foreach (string varName in ti.GlobalsTypes.Keys)
  282. {
  283. Type type = ti.GlobalsTypes[varName];
  284. if (type != null)
  285. {
  286. string typeName = Utils.GetFullTypeName(type, typeNamesCache);
  287. globalNames.Add(varName);
  288. WriteToGlobals(@"{0} {1} = ({0})context[""{1}""];", typeName, varName);
  289. }
  290. }
  291. }
  292. //-----------------------
  293. // Process macro commands
  294. //-----------------------
  295. List<string> programNamespaces = new List<string>();
  296. // Process main program macro commands
  297. ProcessProgramMacros(ti.MainProgram, MAIN_PROGRAM_NAMESPACE, programNamespaces);
  298. // Process imported programs macro commands
  299. foreach (string destNs in ti.ImportedNamespaces.Keys)
  300. {
  301. foreach (string templatePath in ti.ImportedNamespaces[destNs])
  302. {
  303. Program importedProgram = ti.ImportedPrograms[templatePath];
  304. ProcessProgramMacros(importedProgram, destNs, programNamespaces);
  305. }
  306. }
  307. // Main template macros are also in the global namespace
  308. WriteToGlobals(@"macros = {0}.macros;", MAIN_PROGRAM_NAMESPACE);
  309. //--------------------------------------------------------
  310. // Process main program commands (ignoring macro commands)
  311. //--------------------------------------------------------
  312. WriteToBody(@"//=======================");
  313. WriteToBody(@"// Main template program:");
  314. WriteToBody(@"//=======================");
  315. WriteToBody(@"");
  316. WriteToBody(@"__CleanProgram();");
  317. WriteToBody(@"");
  318. HandleCommands(ti.MainProgram.ProgramCommands);
  319. //----------------------------
  320. // Resolve required namespaces
  321. //----------------------------
  322. // Default namespaces
  323. List<string> namespacesList = new List<string>()
  324. {
  325. "System",
  326. "System.IO",
  327. "System.Linq",
  328. "System.Text",
  329. "System.Text.RegularExpressions",
  330. "System.Collections",
  331. "System.Collections.Generic",
  332. "System.Security.Permissions",
  333. "System.Security",
  334. "System.Globalization",
  335. "SharpTAL",
  336. };
  337. // Find all namespaces with extension methods in assemblies where global types are defined
  338. List<string> assemblies = new List<string>();
  339. if (ti.GlobalsTypes != null)
  340. {
  341. foreach (string varName in ti.GlobalsTypes.Keys)
  342. {
  343. Type type = ti.GlobalsTypes[varName];
  344. if (type != null)
  345. {
  346. if (!assemblies.Contains(type.Assembly.Location) &&
  347. !assemblies.Contains(Path.GetFileName(type.Assembly.Location)))
  348. {
  349. // Check if assembly has defined "ExtensionAttribute"
  350. Utils.GetExtensionMethodNamespaces(type.Assembly, namespacesList);
  351. assemblies.Add(type.Assembly.Location);
  352. // Referenced assemblies
  353. foreach (AssemblyName assemblyName in type.Assembly.GetReferencedAssemblies())
  354. {
  355. Assembly assembly = AppDomain.CurrentDomain.Load(assemblyName);
  356. if (!assemblies.Contains(assembly.Location) &&
  357. !assemblies.Contains(Path.GetFileName(assembly.Location)))
  358. {
  359. Utils.GetExtensionMethodNamespaces(assembly, namespacesList);
  360. assemblies.Add(assembly.Location);
  361. }
  362. }
  363. }
  364. }
  365. }
  366. }
  367. // Find all namespaces with extension methods in referenced assemblies
  368. if (ti.ReferencedAssemblies != null)
  369. {
  370. foreach (Assembly refAsm in ti.ReferencedAssemblies)
  371. {
  372. if (!assemblies.Contains(refAsm.Location) &&
  373. !assemblies.Contains(Path.GetFileName(refAsm.Location)))
  374. {
  375. Utils.GetExtensionMethodNamespaces(refAsm, namespacesList);
  376. assemblies.Add(refAsm.Location);
  377. }
  378. }
  379. }
  380. // Create list of "usings"
  381. string usings = "";
  382. foreach (string ns in namespacesList)
  383. {
  384. usings = string.Format("{0}using {1};{2}", usings, ns, Environment.NewLine);
  385. }
  386. //-------------------------
  387. // Generate template source
  388. //-------------------------
  389. string templateSource = FileBodyTemplate.
  390. Replace("${defaultvalue}", Constants.DEFAULT_VALUE).
  391. Replace("${usings}", usings).
  392. Replace("${template_hash}", ti.TemplateKey).
  393. Replace("${globals}", globalsBody.ToString()).
  394. Replace("${body}", rendererBody.ToString());
  395. return templateSource;
  396. }
  397. private void ProcessProgramMacros(Program templateProgram, string programNamespace, List<string> programNamespaces)
  398. {
  399. if (templateProgram.Macros != null)
  400. {
  401. if (!programNamespaces.Contains(programNamespace))
  402. {
  403. // Check if other global var is defined with the name as this template
  404. if (globalNames.Contains(programNamespace))
  405. {
  406. throw new InvalidOperationException(string.Format(@"Failed to process macros in namespace ""{0}"".
  407. Global variable with namespace name allready exists.", programNamespace));
  408. }
  409. globalNames.Add(programNamespace);
  410. programNamespaces.Add(programNamespace);
  411. WriteToGlobals(@"ProgramNamespace {0} = new ProgramNamespace();", programNamespace);
  412. }
  413. foreach (IProgram macro in templateProgram.Macros.Values)
  414. {
  415. // Create macro delegate
  416. WriteToBody(@"//====================");
  417. WriteToBody(@"// Macro: ""{0}.{1}""", programNamespace, macro.Name);
  418. WriteToBody(@"// Source: ""{0}""", macro.TemplatePath);
  419. WriteToBody(@"//====================");
  420. WriteToBody(@"MacroDelegate macro_{0}_{1} = delegate()", programNamespace, macro.Name);
  421. WriteToBody(@"{{");
  422. rendererBodyTabs += " ";
  423. WriteToBody(@"__CleanProgram();");
  424. // Process METAL_DEFINE_PARAM commands
  425. HandleCommands(macro.ProgramCommands.Where(c => c.CommandType == CommandType.METAL_DEFINE_PARAM));
  426. // Process macro commands and ignore METAL_DEFINE_PARAM commands
  427. HandleCommands(macro.ProgramCommands.Where(c => c.CommandType != CommandType.METAL_DEFINE_PARAM));
  428. // Finalize macro delegate
  429. rendererBodyTabs = rendererBodyTabs.Remove(rendererBodyTabs.Length - 5, 4);
  430. WriteToBody(@"}};");
  431. WriteToBody(@"{0}.macros.Add(""{1}"", macro_{0}_{1});", programNamespace, macro.Name);
  432. WriteToBody(@"__macros.Add(@""{0}.{1}"", @""{2}"");", programNamespace, macro.Name, macro.TemplatePath);
  433. WriteToBody(@"");
  434. }
  435. }
  436. }
  437. private void WriteCmdInfo(ICommand command)
  438. {
  439. WriteToBody("");
  440. if (command.Tag != null)
  441. {
  442. WriteToBody(@"__currentCmdInfo = new CommandInfo(""{0}"", @""{1}"", {2}, {3}, @""{4}"");",
  443. Enum.GetName(typeof(CommandType), command.CommandType),
  444. command.Tag.ToString().Replace(Environment.NewLine, "").Replace(@"""", @""""""),
  445. command.Tag.LineNumber,
  446. command.Tag.LinePosition,
  447. command.Tag.SourcePath);
  448. }
  449. else
  450. {
  451. WriteToBody(@"__currentCmdInfo = new CommandInfo(""{0}"");",
  452. Enum.GetName(typeof(CommandType), command.CommandType));
  453. }
  454. WriteToBody("");
  455. }
  456. protected string FormatCodeBlock(CMDCodeBlock codeBlock)
  457. {
  458. return FormatCSharpStatements(codeBlock.Code);
  459. }
  460. protected string FormatCSharpStatements(string code)
  461. {
  462. var parser = new CSharpParser();
  463. var stmts = parser.ParseStatements(code);
  464. if (parser.HasErrors)
  465. {
  466. var errors = string.Join(Environment.NewLine, parser.Errors.Select(err => err.Message));
  467. throw new TemplateParseException(null, string.Format("{0}{1}{2}", code, Environment.NewLine, errors));
  468. }
  469. return code;
  470. }
  471. static readonly Regex _str_expr_regex = new Regex(@"(?<!\\)\$({(?<expression>.*)})", RegexOptions.Singleline);
  472. protected string FormatStringExpression(string expression)
  473. {
  474. List<string> formatNodes = new List<string>();
  475. List<string> formatArgs = new List<string>();
  476. string text = expression;
  477. while (!string.IsNullOrEmpty(text))
  478. {
  479. string matched = text;
  480. var m = _str_expr_regex.Match(matched);
  481. if (!m.Success)
  482. {
  483. formatNodes.Add(text.Replace(@"""", @"""""").Replace(@"{", @"{{").Replace(@"}", @"}}"));
  484. break;
  485. }
  486. string part = text.Substring(0, m.Index);
  487. text = text.Substring(m.Index);
  488. if (!string.IsNullOrEmpty(part))
  489. {
  490. formatNodes.Add(part.Replace(@"""", @"""""").Replace(@"{", @"{{").Replace(@"}", @"}}"));
  491. }
  492. while (true)
  493. {
  494. string str = m.Groups["expression"].Value;
  495. try
  496. {
  497. string s = FormatExpression(str);
  498. formatNodes.Add(string.Format("{{{0}}}", formatArgs.Count));
  499. formatArgs.Add(s);
  500. break;
  501. }
  502. catch (Exception ex)
  503. {
  504. if (m.Length == 0)
  505. throw;
  506. matched = matched.Substring(m.Index, m.Length - 1);
  507. m = _str_expr_regex.Match(matched);
  508. if (!m.Success)
  509. throw;
  510. }
  511. }
  512. text = text.Substring(m.Length);
  513. }
  514. string result = string.Format(@"string.Format(@""{0}""{1}{2})",
  515. string.Join("", formatNodes.ToArray()),
  516. formatArgs.Count > 0 ? ", " : "",
  517. string.Join(", ", formatArgs.ToArray()));
  518. return result;
  519. }
  520. protected string FormatCSharpExpression(string expression)
  521. {
  522. var parser = new CSharpParser();
  523. var expr = parser.ParseExpression(expression + ";");
  524. if (parser.HasErrors)
  525. {
  526. var errors = string.Join(Environment.NewLine, parser.Errors.Select(err => err.Message));
  527. throw new TemplateParseException(null, string.Format("{0}{1}{2}", expression, Environment.NewLine, errors));
  528. }
  529. return expression;
  530. }
  531. protected string FormatExpression(string expression)
  532. {
  533. // Expression: "default"
  534. if (expression.Trim(' ') == DEFAULT_VALUE_EXPRESSION)
  535. {
  536. return "DEFAULT_VALUE";
  537. }
  538. // Expression: "string:"
  539. if (expression.TrimStart(' ').StartsWith("string:"))
  540. {
  541. return FormatStringExpression(expression.TrimStart(' ').Substring("string:".Length));
  542. }
  543. // Expression: "csharp:"
  544. if (expression.TrimStart(' ').StartsWith("csharp:"))
  545. {
  546. expression = expression.TrimStart(' ').Substring("csharp:".Length);
  547. }
  548. return FormatCSharpExpression(expression);
  549. }
  550. protected void WriteToGlobals(string format, params object[] args)
  551. {
  552. if (args != null)
  553. format = string.Format(format, args);
  554. globalsBody.AppendFormat(
  555. @"{0}{1}{2}",
  556. Environment.NewLine, globalsBodyTabs, format);
  557. }
  558. protected void WriteToBody(string format, params object[] args)
  559. {
  560. if (args != null)
  561. format = string.Format(format, args);
  562. rendererBody.AppendFormat(
  563. @"{0}{1}{2}",
  564. Environment.NewLine, rendererBodyTabs, format);
  565. }
  566. protected void WriteToBodyNoFormat(string text)
  567. {
  568. rendererBody.AppendFormat(
  569. @"{0}{1}{2}",
  570. Environment.NewLine, rendererBodyTabs, text);
  571. }
  572. static string SafeVariableName(string str)
  573. {
  574. string name = "";
  575. for (int i = 0; i < str.Length; i++)
  576. {
  577. if (char.IsLetterOrDigit(str, i))
  578. name += str[i];
  579. else
  580. name += '_';
  581. }
  582. return name;
  583. }
  584. #region AbstractProgramInterpreter implementation
  585. protected override void Handle_META_INTERPOLATION(ICommand command)
  586. {
  587. METAInterpolation interpolationCmd = (METAInterpolation)command;
  588. currentScope.Interpolation = interpolationCmd.Enabled;
  589. }
  590. protected override void Handle_METAL_USE_MACRO(ICommand command)
  591. {
  592. // Evaluates the expression, if it resolves to a SubTemplate it then places
  593. // the slotMap into currentSlots and then jumps to the end tag
  594. METALUseMacro useMacroCmd = (METALUseMacro)command;
  595. string macroExpression = useMacroCmd.Expression;
  596. Dictionary<string, ProgramSlot> slots = useMacroCmd.Slots;
  597. List<METALDefineParam> parameters = useMacroCmd.Parameters;
  598. string scopeID = currentScope.ID;
  599. // Start SubScope
  600. string subScopeID = Guid.NewGuid().ToString().Replace("-", "");
  601. currentScope.SubScope.Push(new SubScope() { ID = subScopeID });
  602. WriteCmdInfo(command);
  603. string expression = FormatExpression(macroExpression);
  604. WriteToBody(@"object use_macro_delegate_{0} = {1};", subScopeID, expression);
  605. WriteToBody(@"if (use_macro_delegate_{0} != null && use_macro_delegate_{0} is MacroDelegate)", subScopeID);
  606. WriteToBody(@"{{");
  607. rendererBodyTabs += " ";
  608. WriteToBody(@"__outputTag = 0;");
  609. WriteToBody(@"__tagContent = use_macro_delegate_{0};", subScopeID);
  610. WriteToBody(@"__tagContentType = 1;");
  611. WriteToBody(@"__slotMap = new Dictionary<string, MacroDelegate>();");
  612. // Set macro params
  613. foreach (METALDefineParam param in parameters)
  614. {
  615. WriteToBody(@"__paramMap[""{0}""] = {1};", param.Name, FormatExpression(param.Expression));
  616. }
  617. // Expand slots (SubTemplates)
  618. foreach (string slotName in slots.Keys)
  619. {
  620. ProgramSlot slot = slots[slotName];
  621. string slotID = Guid.NewGuid().ToString().Replace("-", "");
  622. // Create slot delegate
  623. WriteToBody(@"");
  624. WriteToBody(@"//====================");
  625. WriteToBody(@"// Slot: ""{0}""", slotName);
  626. WriteToBody(@"//====================");
  627. WriteToBody(@"MacroDelegate slot_{0}_delegate_{1} = delegate()", slotName, slotID);
  628. WriteToBody(@"{{");
  629. rendererBodyTabs += " ";
  630. // Process slot commands
  631. HandleCommands(slot.ProgramCommands);
  632. // Finalize slot delegate
  633. rendererBodyTabs = rendererBodyTabs.Remove(rendererBodyTabs.Length - 5, 4);
  634. WriteToBody(@"}};");
  635. WriteToBody(@"__slotMap[""{0}""] = slot_{0}_delegate_{1};", slotName, slotID);
  636. WriteToBody(@"");
  637. }
  638. // Go to end tag
  639. WriteToBody(@"");
  640. WriteToBody(@"// NOTE: WE JUMP STRAIGHT TO THE END TAG, NO OTHER TAL/METAL COMMANDS ARE EVALUATED");
  641. WriteToBody(@"goto TAL_ENDTAG_ENDSCOPE_{0};", scopeID);
  642. }
  643. protected override void Handle_METAL_DEFINE_SLOT(ICommand command)
  644. {
  645. // If the slotName is filled then that is used, otherwise the original content is used.
  646. METALDefineSlot defineSlotCmd = (METALDefineSlot)command;
  647. string slotName = defineSlotCmd.SlotName;
  648. string scopeID = currentScope.ID;
  649. WriteCmdInfo(command);
  650. WriteToBody(@"if (__currentSlots.ContainsKey(""{0}""))", slotName);
  651. WriteToBody(@"{{");
  652. WriteToBody(" // This slot is filled, so replace us with that content");
  653. WriteToBody(@" __outputTag = 0;");
  654. WriteToBody(@" __tagContent = __currentSlots[""{0}""];", slotName);
  655. WriteToBody(@" __tagContentType = 1;");
  656. WriteToBody(@" ");
  657. WriteToBody(@" // Output none of our content or the existing content");
  658. WriteToBody(@" // NOTE: NO FURTHER TAL/METAL COMMANDS ARE EVALUATED");
  659. WriteToBody(@" goto TAL_ENDTAG_ENDSCOPE_{0};", scopeID);
  660. WriteToBody(@"}}");
  661. }
  662. protected override void Handle_METAL_DEFINE_PARAM(ICommand command)
  663. {
  664. METALDefineParam defineParamCmd = (METALDefineParam)command;
  665. WriteCmdInfo(defineParamCmd);
  666. string expression = FormatExpression(defineParamCmd.Expression);
  667. // Create param variable
  668. WriteToBody(@"{0} {1} = {2};", defineParamCmd.Type, defineParamCmd.Name, expression);
  669. WriteToBody(@"if (__currentParams.ContainsKey(""{0}""))", defineParamCmd.Name);
  670. WriteToBody(@"{{");
  671. WriteToBody(" // This param is filled");
  672. WriteToBody(@" {0} = ({1})__currentParams[""{0}""];", defineParamCmd.Name, defineParamCmd.Type);
  673. WriteToBody(@"}}");
  674. }
  675. protected override void Handle_TAL_DEFINE(ICommand command)
  676. {
  677. TALDefine defineCmd = (TALDefine)command;
  678. WriteCmdInfo(defineCmd);
  679. string expression = FormatExpression(defineCmd.Expression);
  680. if (defineCmd.Scope == TALDefine.VariableScope.Local)
  681. {
  682. // Create new local variable
  683. string body = string.Format(@"var {0} = {1};", defineCmd.Name, expression);
  684. WriteToBody(body);
  685. }
  686. else if (defineCmd.Scope == TALDefine.VariableScope.NonLocal)
  687. {
  688. // Set existing variable
  689. string body = string.Format(@"{0} = {1};", defineCmd.Name, expression);
  690. WriteToBody(body);
  691. }
  692. else
  693. {
  694. if (globalNames.Contains(defineCmd.Name))
  695. {
  696. // Set existing global variable
  697. string body = string.Format(@"{0} = {1};", defineCmd.Name, expression);
  698. WriteToBody(body);
  699. }
  700. else
  701. {
  702. // Create new global variable
  703. string body = string.Format(@"var {0} = {1};", defineCmd.Name, expression);
  704. globalNames.Add(defineCmd.Name);
  705. WriteToGlobals(body);
  706. }
  707. }
  708. }
  709. protected override void Handle_TAL_CONDITION(ICommand command)
  710. {
  711. // Conditionally continues with execution of all content contained by it.
  712. TALCondition conditionCmd = (TALCondition)command;
  713. string expression = conditionCmd.Expression;
  714. string scopeID = currentScope.ID;
  715. WriteCmdInfo(command);
  716. // Start SubScope
  717. expression = FormatExpression(expression);
  718. WriteToBody(@"if (IsFalseResult({0}))", expression);
  719. WriteToBody(@"{{");
  720. WriteToBody(@" // Nothing to output - evaluated to false.");
  721. WriteToBody(@" __outputTag = 0;");
  722. WriteToBody(@" __tagContent = null;");
  723. WriteToBody(@" goto TAL_ENDTAG_ENDSCOPE_{0};", scopeID);
  724. WriteToBody(@"}}");
  725. }
  726. protected override void Handle_TAL_REPEAT(ICommand command)
  727. {
  728. // Repeats anything in the cmndList
  729. TALRepeat repeatCmd = (TALRepeat)command;
  730. string varName = repeatCmd.Name;
  731. string expression = repeatCmd.Expression;
  732. // Start Repeat SubScope
  733. string repeatSubScopeID = Guid.NewGuid().ToString().Replace("-", "");
  734. currentScope.SubScope.Push(new RepeatScope() { ID = repeatSubScopeID, VarName = varName });
  735. WriteCmdInfo(command);
  736. expression = FormatExpression(expression);
  737. WriteToBody(@"// Backup the current attributes for this tag");
  738. WriteToBody(@"Dictionary<string, Attr> __currentAttributesCopy_{0} = new Dictionary<string, Attr>(__currentAttributes);", repeatSubScopeID);
  739. WriteToBody(@"");
  740. WriteToBody(@"var enumerable_{0}_{1} = {2};", varName, repeatSubScopeID, expression);
  741. WriteToBody(@"var enumerator_{0}_{1} = enumerable_{0}_{1}.GetEnumerator();", varName, repeatSubScopeID);
  742. WriteToBody(@"bool isfirst_{0}_{1} = true;", varName, repeatSubScopeID);
  743. WriteToBody(@"bool islast_{0}_{1} = !enumerator_{0}_{1}.MoveNext();", varName, repeatSubScopeID);
  744. WriteToBody(@"bool isdefault_{0}_{1} = false;", varName, repeatSubScopeID);
  745. WriteToBody(@"if (IsDefaultValue(enumerable_{0}_{1}))", varName, repeatSubScopeID);
  746. WriteToBody(@"{{");
  747. WriteToBody(@" // Stop after first enumeration, so only default content is rendered");
  748. WriteToBody(@" islast_{0}_{1} = true;", varName, repeatSubScopeID);
  749. WriteToBody(@" isdefault_{0}_{1} = true;", varName, repeatSubScopeID);
  750. WriteToBody(@"}}");
  751. WriteToBody(@"else");
  752. WriteToBody(@" repeat[""{0}""] = new SharpTAL.RepeatItem(enumerable_{0}_{1});", varName, repeatSubScopeID);
  753. WriteToBody(@"do");
  754. WriteToBody(@"{{");
  755. WriteToBody(@" __outputTag = 1;");
  756. WriteToBody(@" __tagContent = null;");
  757. WriteToBody(@" __tagContentType = 0;");
  758. WriteToBody(@" __moveToEndTag = false;");
  759. WriteToBody(@" ");
  760. WriteToBody(@" // Skip repeat, if there is nothing to enumerate");
  761. WriteToBody(@" if (isfirst_{0}_{1} &&", varName, repeatSubScopeID);
  762. WriteToBody(@" islast_{0}_{1} &&", varName, repeatSubScopeID);
  763. WriteToBody(@" isdefault_{0}_{1} == false)", varName, repeatSubScopeID);
  764. WriteToBody(@" {{");
  765. WriteToBody(@" goto END_REPEAT_{0};", repeatSubScopeID);
  766. WriteToBody(@" }}");
  767. WriteToBody(@" isfirst_{0}_{1} = false;", varName, repeatSubScopeID);
  768. WriteToBody(@" ");
  769. WriteToBody(@" var {0} = enumerator_{0}_{1}.Current;", varName, repeatSubScopeID);
  770. WriteToBody(@" if (!isdefault_{0}_{1})", varName, repeatSubScopeID);
  771. WriteToBody(@" {{");
  772. WriteToBody(@" islast_{0}_{1} = !enumerator_{0}_{1}.MoveNext();", varName, repeatSubScopeID);
  773. WriteToBody(@" repeat[""{0}""].next(islast_{0}_{1});", varName, repeatSubScopeID);
  774. WriteToBody(@" }}");
  775. rendererBodyTabs += " ";
  776. }
  777. protected override void Handle_TAL_CONTENT(ICommand command)
  778. {
  779. TALContent contentCmd = (TALContent)command;
  780. string expression = contentCmd.Expression;
  781. bool structure = contentCmd.Structure;
  782. string scopeID = currentScope.ID;
  783. WriteCmdInfo(command);
  784. expression = FormatExpression(expression);
  785. WriteToBody(@"object content_expression_result_{0} = {1};", scopeID, expression);
  786. WriteToBody(@"");
  787. WriteToBody(@"if (content_expression_result_{0} == null)", scopeID);
  788. WriteToBody(@"{{");
  789. WriteToBody(@" // Output none of our content or the existing content, but potentially the tags");
  790. WriteToBody(@" __moveToEndTag = true;", scopeID);
  791. WriteToBody(@"}}");
  792. WriteToBody(@"else if (!IsDefaultValue(content_expression_result_{0}))", scopeID);
  793. WriteToBody(@"{{");
  794. WriteToBody(@" // We have content, so let's suppress the natural content and output this!");
  795. WriteToBody(@" __tagContent = {0};", expression);
  796. WriteToBody(@" __tagContentType = {0};", structure ? 1 : 0);
  797. WriteToBody(@" __moveToEndTag = true;");
  798. WriteToBody(@"}}");
  799. }
  800. protected override void Handle_TAL_REPLACE(ICommand command)
  801. {
  802. TALReplace replaceCmd = (TALReplace)command;
  803. string expression = replaceCmd.Expression;
  804. bool structure = replaceCmd.Structure;
  805. string scopeID = currentScope.ID;
  806. WriteCmdInfo(command);
  807. expression = FormatExpression(expression);
  808. WriteToBody(@"object content_expression_result_{0} = {1};", scopeID, expression);
  809. WriteToBody(@"");
  810. WriteToBody(@"if (content_expression_result_{0} == null)", scopeID);
  811. WriteToBody(@"{{");
  812. WriteToBody(@" // Only output tags if this is a content not a replace");
  813. WriteToBody(@" __outputTag = 0;");
  814. WriteToBody(@" // Output none of our content or the existing content, but potentially the tags");
  815. WriteToBody(@" __moveToEndTag = true;", scopeID);
  816. WriteToBody(@"}}");
  817. WriteToBody(@"else if (!IsDefaultValue(content_expression_result_{0}))", scopeID);
  818. WriteToBody(@"{{");
  819. WriteToBody(@" // Replace content - do not output tags");
  820. WriteToBody(@" __outputTag = 0;");
  821. WriteToBody(@" __tagContent = {0};", expression);
  822. WriteToBody(@" __tagContentType = {0};", structure ? 1 : 0);
  823. WriteToBody(@" __moveToEndTag = true;");
  824. WriteToBody(@"}}");
  825. }
  826. protected override void Handle_CMD_OUTPUT(ICommand command)
  827. {
  828. CMDOutput outputCmd = (CMDOutput)command;
  829. string data = outputCmd.Data;
  830. WriteCmdInfo(command);
  831. if (currentScope == null || (currentScope != null && currentScope.Interpolation))
  832. {
  833. string expression = FormatStringExpression(data);
  834. WriteToBody(@"output.Write({0});", expression);
  835. }
  836. else
  837. WriteToBody(@"output.Write(@""{0}"");", data.Replace(@"""", @""""""));
  838. }
  839. protected override void Handle_CMD_CODE_BLOCK(ICommand command)
  840. {
  841. CMDCodeBlock codeCmd = (CMDCodeBlock)command;
  842. WriteCmdInfo(command);
  843. string code = FormatCodeBlock(codeCmd);
  844. WriteToBodyNoFormat(code);
  845. }
  846. protected override void Handle_TAL_OMITTAG(ICommand command)
  847. {
  848. // Conditionally turn off tag output
  849. TALOmitTag omitTagCmd = (TALOmitTag)command;
  850. string expression = omitTagCmd.Expression;
  851. WriteCmdInfo(command);
  852. expression = FormatExpression(expression);
  853. WriteToBody(@"if (!IsFalseResult({0}))", expression);
  854. WriteToBody(@"{{");
  855. WriteToBody(@" // Turn tag output off");
  856. WriteToBody(@" __outputTag = 0;");
  857. WriteToBody(@"}}");
  858. }
  859. protected override void Handle_CMD_START_SCOPE(ICommand command)
  860. {
  861. // Pushes the current state onto the stack, and sets up the new state
  862. CMDStartScope startScopeCmd = (CMDStartScope)command;
  863. WriteCmdInfo(command);
  864. scopeStack.Push(currentScope);
  865. currentScope = new Scope()
  866. {
  867. ID = Guid.NewGuid().ToString().Replace("-", ""),
  868. Interpolation = true,
  869. SubScope = new Stack<SubScope>()
  870. };
  871. WriteToBody("");
  872. WriteToBody("// Start scope: {0}", currentScope.ID);
  873. WriteToBody("{{");
  874. rendererBodyTabs += " ";
  875. string scopeID = currentScope.ID;
  876. WriteToBody("");
  877. WriteToBody(@"Dictionary<string, Attr> __currentAttributes_{0} = new Dictionary<string, Attr>();", scopeID);
  878. WriteToBody(@"");
  879. WriteToBody(@"List<object> push_scope_{0} = new List<object>()", scopeID);
  880. WriteToBody(@"{{");
  881. WriteToBody(@" __moveToEndTag,");
  882. WriteToBody(@" __outputTag,");
  883. WriteToBody(@" __currentAttributes,");
  884. WriteToBody(@" __tagContent,");
  885. WriteToBody(@" __tagContentType");
  886. WriteToBody(@"}};");
  887. WriteToBody(@"");
  888. WriteToBody(@"__scopeStack.Push(push_scope_{0});", scopeID);
  889. WriteToBody(@"");
  890. WriteToBody(@"__moveToEndTag = false;");
  891. WriteToBody(@"__outputTag = 1;");
  892. WriteToBody(@"__currentAttributes = __currentAttributes_{0};", scopeID);
  893. WriteToBody(@"__tagContent = null;");
  894. WriteToBody(@"__tagContentType = 1;");
  895. }
  896. protected override void Handle_TAL_ATTRIBUTES(ICommand command)
  897. {
  898. TALAttributes attributesCmd = (TALAttributes)command;
  899. List<TagAttribute> attributes = attributesCmd.Attributes;
  900. string scopeID = currentScope.ID;
  901. WriteCmdInfo(command);
  902. HashSet<string> attVarNames = new HashSet<string>();
  903. foreach (TagAttribute att in attributes)
  904. {
  905. WriteToBody("// Attribute: {0}", att.Name);
  906. string attVarName = SafeVariableName(att.Name);
  907. if (!attVarNames.Contains(attVarName))
  908. {
  909. attVarNames.Add(attVarName);
  910. WriteToBody(@"object attribute_{0}_{1} = null;", attVarName, scopeID);
  911. }
  912. if (string.IsNullOrEmpty(att.Value))
  913. {
  914. WriteToBody(@"if (!__currentAttributes.ContainsKey(""{0}""))", att.Name);
  915. WriteToBody(@" __currentAttributes.Add(""{0}"", new Attr {{ Name = @""{0}"", Value = @"""", Eq = @""{1}"", Quote = @""{2}"", QuoteEntity = @""{3}"" }});",
  916. att.Name, att.Eq, att.Quote.Replace(@"""", @""""""), att.QuoteEntity);
  917. }
  918. else
  919. {
  920. string expression = "";
  921. if (att is TALTagAttribute)
  922. // This is TAL command attribute
  923. expression = FormatExpression(att.Value);
  924. else
  925. {
  926. // This is clean attribute (no TAL command)
  927. if (currentScope.Interpolation)
  928. // Interpolate attribute value
  929. expression = FormatStringExpression(att.Value);
  930. else
  931. // Get attribute value as raw string
  932. expression = string.Format(@"@""{0}""", att.Value.Replace(@"""", @""""""));
  933. }
  934. WriteToBody(@"try");
  935. WriteToBody(@"{{");
  936. WriteToBody(@" attribute_{0}_{1} = {2};", attVarName, scopeID, expression);
  937. WriteToBody(@"}}");
  938. WriteToBody(@"catch (Exception ex)");
  939. WriteToBody(@"{{");
  940. WriteToBody(@" attribute_{0}_{1} = null;", attVarName, scopeID);
  941. WriteToBody(@"}}");
  942. WriteToBody(@"if (attribute_{0}_{1} == null)", attVarName, scopeID);
  943. WriteToBody(@" __currentAttributes.Remove(""{0}"");", att.Name);
  944. WriteToBody(@"else if (!IsDefaultValue(attribute_{0}_{1}))", attVarName, scopeID);
  945. WriteToBody(@"{{");
  946. WriteToBody(@" if (!__currentAttributes.ContainsKey(""{0}""))", att.Name);
  947. WriteToBody(@" __currentAttributes.Add(""{0}"", new Attr {{ Name = @""{0}"", Value = @"""", Eq = @""{1}"", Quote = @""{2}"", QuoteEntity = @""{3}"" }});",
  948. att.Name, att.Eq, att.Quote.Replace(@"""", @""""""), att.QuoteEntity);
  949. WriteToBody(@" __currentAttributes[""{0}""].Value = FormatResult(attribute_{1}_{2});", att.Name, attVarName, scopeID);
  950. WriteToBody(@"}}");
  951. }
  952. }
  953. }
  954. protected override void Handle_CMD_START_TAG(ICommand command)
  955. {
  956. CMDStartTag startTagCmd = (CMDStartTag)command;
  957. string tagName = startTagCmd.Tag.Name;
  958. string tagSuffix = startTagCmd.Tag.Suffix;
  959. string scopeID = currentScope.ID;
  960. WriteCmdInfo(command);
  961. WriteToBody(@"if (__outputTag == 1)");
  962. WriteToBody(@"{{");
  963. WriteToBody(@" output.Write(@""<"");");
  964. WriteToBody(@" output.Write(@""{0}"");", tagName);
  965. WriteToBody(@" foreach (var att in __currentAttributes.Values)");
  966. WriteToBody(@" {{");
  967. WriteToBody(@" output.Write(@"" {{0}}{{1}}{{2}}{{3}}{{2}}"", att.Name, att.Eq, att.Quote, EscapeAttrValue(att.Value, att.Quote, att.QuoteEntity));");
  968. WriteToBody(@" }}");
  969. WriteToBody(@" output.Write(@""{0}"");", tagSuffix);
  970. WriteToBody(@"}}");
  971. WriteToBody(@"");
  972. WriteToBody(@"if (__moveToEndTag == true)");
  973. WriteToBody(@"{{");
  974. WriteToBody(@" goto TAL_ENDTAG_ENDSCOPE_{0};", scopeID);
  975. WriteToBody(@"}}");
  976. }
  977. protected override void Handle_CMD_ENDTAG_ENDSCOPE(ICommand command)
  978. {
  979. CMDEntTagEndScope entTagEndScopeCmd = (CMDEntTagEndScope)command;
  980. string tagName = entTagEndScopeCmd.Tag.Name;
  981. bool singletonTag = entTagEndScopeCmd.Tag.Singleton;
  982. WriteCmdInfo(command);
  983. string scopeID = currentScope.ID;
  984. WriteToBody("TAL_ENDTAG_ENDSCOPE_{0}:", scopeID);
  985. WriteToBody("");
  986. WriteToBody("// End tag: <{0}>", tagName);
  987. WriteToBody(@"if (__tagContent != null)");
  988. WriteToBody(@"{{");
  989. WriteToBody(@" if (__tagContentType == 1)");
  990. WriteToBody(@" {{");
  991. WriteToBody(@" if (__tagContent is MacroDelegate)");
  992. WriteToBody(@" {{");
  993. WriteToBody(@" // Save our state!");
  994. WriteToBody(@" __PushProgram();");
  995. WriteToBody(@" // Execute macro or slot delegate");
  996. WriteToBody(@" ((MacroDelegate)__tagContent)();");
  997. WriteToBody(@" // Restore state");
  998. WriteToBody(@" __PopProgram();");
  999. WriteToBody(@" // End of the macro expansion (if any) so clear the slots and params");
  1000. WriteToBody(@" __slotMap = new Dictionary<string, MacroDelegate>();");
  1001. WriteToBody(@" __paramMap = new Dictionary<string, object>();");
  1002. WriteToBody(@" }}");
  1003. WriteToBody(@" else");
  1004. WriteToBody(@" {{");
  1005. WriteToBody(@" output.Write((string)__tagContent);");
  1006. WriteToBody(@" }}");
  1007. WriteToBody(@" }}");
  1008. WriteToBody(@" else");
  1009. WriteToBody(@" {{");
  1010. WriteToBody(@" output.Write(Escape(FormatResult(__tagContent)));");
  1011. WriteToBody(@" }}");
  1012. WriteToBody(@"}}");
  1013. WriteToBody(@"if (__outputTag == 1)");
  1014. WriteToBody(@"{{");
  1015. WriteToBody(@" // Do NOT output end tag if a singleton with no content");
  1016. WriteToBody(@" if (({0} == 1 && __tagContent == null) == false)", singletonTag ? 1 : 0);
  1017. WriteToBody(@" {{");
  1018. WriteToBody(@" output.Write(@""</{0}>"");", tagName);
  1019. WriteToBody(@" }}");
  1020. WriteToBody(@"}}");
  1021. while (currentScope.SubScope.Count > 0)
  1022. {
  1023. SubScope subScope = currentScope.SubScope.Pop();
  1024. if (subScope is RepeatScope)
  1025. {
  1026. RepeatScope repeatScope = (RepeatScope)subScope;
  1027. WriteToBody("");
  1028. WriteToBody("// End sub-scope repeat: {0}", subScope.ID);
  1029. WriteToBody(@"");
  1030. WriteToBody(@"// Restore the current attributes");
  1031. WriteToBody(@"__currentAttributes = new Dictionary<string, Attr>(__currentAttributesCopy_{1});", scopeID, repeatScope.ID);
  1032. WriteToBody(@"");
  1033. rendererBodyTabs = rendererBodyTabs.Remove(rendererBodyTabs.Length - 5, 4);
  1034. WriteToBody("}}");
  1035. WriteToBody(@"while (!islast_{0}_{1});", repeatScope.VarName, repeatScope.ID);
  1036. WriteToBody(@"END_REPEAT_{0}:", repeatScope.ID);
  1037. WriteToBody(@"repeat[""{0}""] = null;", repeatScope.VarName);
  1038. }
  1039. else
  1040. {
  1041. WriteToBody("");
  1042. WriteToBody("// End sub-scope: {0}", subScope.ID);
  1043. rendererBodyTabs = rendererBodyTabs.Remove(rendererBodyTabs.Length - 5, 4);
  1044. WriteToBody("}}");
  1045. }
  1046. }
  1047. WriteToBody("");
  1048. WriteToBody(@"List<object> pop_scope_{0} = __scopeStack.Pop();", scopeID);
  1049. WriteToBody(@"__moveToEndTag = (bool)pop_scope_{0}[0];", scopeID);
  1050. WriteToBody(@"__outputTag = (int)pop_scope_{0}[1];", scopeID);
  1051. WriteToBody(@"__currentAttributes = (Dictionary<string, Attr>)pop_scope_{0}[2];", scopeID);
  1052. WriteToBody(@"__tagContent = (object)pop_scope_{0}[3];", scopeID);
  1053. WriteToBody(@"__tagContentType = (int)pop_scope_{0}[4];", scopeID);
  1054. WriteToBody("");
  1055. WriteToBody("// End scope: {0}", scopeID);
  1056. rendererBodyTabs = rendererBodyTabs.Remove(rendererBodyTabs.Length - 5, 4);
  1057. WriteToBody("}}");
  1058. currentScope = scopeStack.Pop();
  1059. }
  1060. protected override void Handle_METAL_FILL_SLOT(ICommand command)
  1061. {
  1062. throw new NotImplementedException();
  1063. }
  1064. protected override void Handle_METAL_DEFINE_MACRO(ICommand command)
  1065. {
  1066. throw new NotImplementedException();
  1067. }
  1068. protected override void Handle_METAL_FILL_PARAM(ICommand command)
  1069. {
  1070. throw new NotImplementedException();
  1071. }
  1072. protected override void Handle_METAL_IMPORT(ICommand command)
  1073. {
  1074. throw new NotImplementedException();
  1075. }
  1076. #endregion
  1077. }
  1078. }