public MetalUseMacro(Tag tag, string expression, Dictionary<string, ProgramSlot> slots, List<MetalDefineParam> parameters) : base(tag, CommandType.MetalUseMacro) { Expression = expression; Slots = slots; Parameters = parameters; }
public MetalDefineParam(Tag tag, string type, string name, string expression) : base(tag, CommandType.MetalDefineParam) { Type = type; Name = name; Expression = expression; }
public TalDefine(Tag tag, VariableScope scope, string name, string expression) : base(tag, CommandType.TalDefine) { Scope = scope; Name = name; Expression = expression; }
public Tag(Tag tag) { Name = tag.Name; Suffix = tag.Suffix; Singleton = tag.Singleton; if (tag.Attributes != null) Attributes = new List<TagAttribute>(tag.Attributes); SourcePath = tag.SourcePath; LineNumber = tag.LineNumber; LinePosition = tag.LinePosition; }
public TalRepeat(Tag tag, string name, string expression) : base(tag, CommandType.TalRepeat) { Name = name; Expression = expression; }
public TalReplace(Tag tag, string expression, bool structure) : base(tag, CommandType.TalReplace) { Structure = structure; Expression = expression; }
TagStackItem AddTagToStack(Tag tag, List<TagAttribute> cleanAttributes) { // Set tag attributes to contain only normal HTML/XML attributes (TAL/METAL attributes are removed) tag.Attributes = cleanAttributes; // Add tag to tag stack var tagStackItem = new TagStackItem(tag); _tagStack.Add(tagStackItem); return tagStackItem; }
protected override void HandleEndTag(Tag tag) { while (_tagStack.Count > 0) { TagStackItem tagStackItem = _tagStack.Last(); _tagStack.RemoveAt(_tagStack.Count - 1); Tag oldTag = tagStackItem.Tag; int? endTagCommandLocation = tagStackItem.EndTagCommandLocation; List<Action> popFunctionList = tagStackItem.PopFunctionList; if (popFunctionList != null) { foreach (Action func in popFunctionList) func(); } if (oldTag.Name == tag.Name) { // We've found the right tag, now check to see if we have any TAL commands on it if (endTagCommandLocation != null) { // We have a command (it's a TAL tag) // Note where the end tag command location should point (i.e. the next command) _endTagsCommandMap[(int)endTagCommandLocation] = _programCommands.Count; // We need a "close scope and tag" command _programCommands.Add(new CmdEndTagEndScope(tag)); return; } if (!tag.Singleton) { // We are popping off an un-interesting tag, just add the close as text _programCommands.Add(new CmdOutput(tag, "</" + tag.Name + ">")); return; } // We are suppressing the output of this tag, so just return return; } // We have a different tag, which means something like <br> which never closes is in // between us and the real tag. // If the tag that we did pop off has a command though it means un-balanced TAL tags! if (endTagCommandLocation != null) { // ERROR string msg = string.Format("TAL/METAL Elements must be balanced - found close tag {0} expecting {1}", tag.Name, oldTag.Name); throw new TemplateParseException(oldTag, msg); } } throw new TemplateParseException(null, string.Format("</{0}> {1}", tag.Name, "Close tag encountered with no corresponding open tag.")); }
public MetalDefineSlot(Tag tag, string slotName) : base(tag, CommandType.MetalDefineSlot) { SlotName = slotName; }
public TalAttributes(Tag tag, List<TagAttribute> attributes) : base(tag, CommandType.TalAttributes) { Attributes = attributes; }
public MetaInterpolation(Tag tag, bool enabled) : base(tag, CommandType.MetaInterpolation) { Enabled = enabled; }
public CmdStartScope(Tag tag) : base(tag, CommandType.CmdStartScope) { }
public TalCondition(Tag tag, string expression) : base(tag, CommandType.TalCondition) { Expression = expression; }
public CmdOutput(Tag tag, string data) : base(tag, CommandType.CmdOutput) { Data = data; }
public TalOmitTag(Tag tag, string expression) : base(tag, CommandType.TalOmittag) { Expression = expression; }
public CmdEndTagEndScope(Tag tag) : base(tag, CommandType.CmdEndtagEndscope) { }
public TalContent(Tag tag, string expression, bool structure) : base(tag, CommandType.TalContent) { Structure = structure; Expression = expression; }
public TagStackItem(Tag tag) { Tag = tag; EndTagCommandLocation = null; PopFunctionList = null; UseMacroCommandLocation = -1; }
protected abstract void HandleEndTag(Tag tag);
protected override void HandleStartTag(Tag tag) { // Note down the tag we are handling, it will be used for error handling during compilation _currentStartTag = new Tag(tag); // Expand HTML entity references in attribute values foreach (TagAttribute att in _currentStartTag.Attributes) att.Value = att.UnescapedValue; // Sorted dictionary of TAL attributes grouped by attribute type. The dictionary is sorted by the attribute type. SortedDictionary<CommandType, List<TagAttribute>> talAttributesDictionary = new SortedDictionary<CommandType, List<TagAttribute>>(new CommandTypeComparer()); // Clean HTML/XML attributes var cleanAttributes = new List<TagAttribute>(); var popFunctionList = new List<Action>(); bool isTalElementNameSpace = false; string prefixToAdd = ""; // Resolve TAL/METAL namespace declarations from attributes foreach (var att in _currentStartTag.Attributes) { if (att.Name.Length > 4 && att.Name.Substring(0, 5) == "xmlns") { // We have a namespace declaration. string prefix = att.Name.Length > 5 ? att.Name.Substring(6) : ""; if (att.Value == Namespaces.MetaNs) { // It's a META namespace declaration if (prefix.Length > 0) { _metaNamespacePrefixStack.Add(_metaNamespacePrefix); SetMetaPrefix(prefix); // We want this function called when the scope ends popFunctionList.Add(PopMetaNamespace); } else { // We don't allow META/METAL/TAL to be declared as a default const string msg = "Can not use META name space by default, a prefix must be provided."; throw new TemplateParseException(_currentStartTag, msg); } } else if (att.Value == Namespaces.MetalNs) { // It's a METAL namespace declaration if (prefix.Length > 0) { _metalNamespacePrefixStack.Add(_metalNamespacePrefix); SetMetalPrefix(prefix); // We want this function called when the scope ends popFunctionList.Add(PopMetalNamespace); } else { // We don't allow META/METAL/TAL to be declared as a default const string msg = "Can not use METAL name space by default, a prefix must be provided."; throw new TemplateParseException(_currentStartTag, msg); } } else if (att.Value == Namespaces.TalNs) { // TAL this time if (prefix.Length > 0) { _talNamespacePrefixStack.Add(_talNamespacePrefix); SetTalPrefix(prefix); // We want this function called when the scope ends popFunctionList.Add(PopTalNamespace); } else { // We don't allow META/METAL/TAL to be declared as a default const string msg = "Can not use TAL name space by default, a prefix must be provided."; throw new TemplateParseException(_currentStartTag, msg); } } else { // It's nothing special, just an ordinary namespace declaration cleanAttributes.Add(att); } } } // Determine whether this element is in either the METAL or TAL namespace if (tag.Name.IndexOf(':') > 0) { // We have a namespace involved, so let's look to see if its one of ours string _namespace = tag.Name.Substring(0, tag.Name.IndexOf(':')); if (_namespace == _metalNamespacePrefix) { isTalElementNameSpace = true; prefixToAdd = _metalNamespacePrefix + ":"; } else if (_namespace == _talNamespacePrefix) { isTalElementNameSpace = true; prefixToAdd = _talNamespacePrefix + ":"; } if (isTalElementNameSpace) { // We should treat this an implicit omit-tag // Will go to default, i.e. yes talAttributesDictionary[CommandType.TalOmittag] = new List<TagAttribute> { new TalTagAttribute { Value = "", CommandType = CommandType.TalOmittag } }; } } // Look for TAL/METAL attributes foreach (var att in _currentStartTag.Attributes) { if (att.Name.Length > 4 && att.Name.Substring(0, 5) == "xmlns") // We have a namespace declaration. continue; string talCommandName; if (isTalElementNameSpace && att.Name.IndexOf(':') < 0) // This means that the attribute name does not have a namespace, so use the prefix for this tag. talCommandName = prefixToAdd + att.Name; else talCommandName = att.Name; if (_talAttributeMap.ContainsKey(talCommandName)) { // It's a TAL attribute CommandType cmdType = _talAttributeMap[talCommandName]; if (cmdType == CommandType.TalOmittag && isTalElementNameSpace) { // Supressing omit-tag command present on TAL or METAL element } else { if (!talAttributesDictionary.ContainsKey(cmdType)) talAttributesDictionary.Add(cmdType, new List<TagAttribute>()); talAttributesDictionary[cmdType].Add(new TalTagAttribute(att) { CommandType = cmdType }); } } else if (_metalAttributeMap.ContainsKey(talCommandName)) { // It's a METAL attribute CommandType cmdType = _metalAttributeMap[talCommandName]; if (!talAttributesDictionary.ContainsKey(cmdType)) talAttributesDictionary.Add(cmdType, new List<TagAttribute>()); talAttributesDictionary[cmdType].Add(new TalTagAttribute(att) { CommandType = cmdType }); } else if (_metaAttributeMap.ContainsKey(talCommandName)) { // It's a META attribute CommandType cmdType = _metaAttributeMap[talCommandName]; if (!talAttributesDictionary.ContainsKey(cmdType)) talAttributesDictionary.Add(cmdType, new List<TagAttribute>()); talAttributesDictionary[cmdType].Add(new TalTagAttribute(att) { CommandType = cmdType }); } else { // It's normal HTML/XML attribute cleanAttributes.Add(att); } } if (cleanAttributes.Count > 0) { // Insert normal HTML/XML attributes BEFORE other TAL/METAL TAL_ATTRIBUTES commands // as fake TAL_ATTRIBUTES commands to enable string expressions interpolation on normal HTML/XML attributes. if (!talAttributesDictionary.ContainsKey(CommandType.TalAttributes)) talAttributesDictionary.Add(CommandType.TalAttributes, new List<TagAttribute>()); talAttributesDictionary[CommandType.TalAttributes].InsertRange(0, cleanAttributes); } // Create a symbol for the end of the tag - we don't know what the offset is yet _endTagCommandLocationCounter++; TagStackItem tagStackItem = null; foreach (CommandType cmdType in talAttributesDictionary.Keys) { // Resolve program commands from tal attributes var commands = _talAttributeHandlers[cmdType](talAttributesDictionary[cmdType]); if (commands != null) foreach (Command cmd in commands) { if (tagStackItem == null) { // The first command needs to add the tag to the tag stack tagStackItem = AddTagToStack(tag, cleanAttributes); // Save metal:use-macro command position if (cmd.CommandType == CommandType.MetalUseMacro) tagStackItem.UseMacroCommandLocation = _programCommands.Count + 1; // Append command to create new scope for the tag Command startScopeCmd = new CmdStartScope(_currentStartTag); _programCommands.Add(startScopeCmd); } // All others just append _programCommands.Add(cmd); } } if (tagStackItem == null) { tagStackItem = AddTagToStack(tag, cleanAttributes); // Append command to create new scope for the tag _programCommands.Add(new CmdStartScope(_currentStartTag)); } // Save pop functions and end tag command location for this tag tagStackItem.PopFunctionList = popFunctionList; tagStackItem.EndTagCommandLocation = _endTagCommandLocationCounter; // Finally, append start tag command _programCommands.Add(new CmdStartTag(_currentStartTag)); }
protected abstract void HandleStartTag(Tag tag);
Program GetTemplateProgram(string templateBody, string templatePath) { // Init per-template-body compiling state _importMacroCommands = new HashSet<string>(); // Try to get template program from cache string bodyHash = Utils.ComputeHash(templateBody); Program program; lock (TemplateProgramCacheLock) { if (TemplateProgramCache.TryGetValue(bodyHash, out program)) return program; } // Per-template-body compiling state _programCommands = new List<ICommand>(); _endTagsCommandMap = new Dictionary<int, int>(); _macroMap = new Dictionary<string, IProgram>(); _tagStack = new List<TagStackItem>(); _endTagCommandLocationCounter = 0; _currentStartTag = null; // Parse template ParseTemplate(templateBody, templatePath, DefaultNamespaces); // Create template program instance program = new Program(templateBody, templatePath, bodyHash, _programCommands, _endTagsCommandMap, _macroMap, _importMacroCommands); // Put template program to cache lock (TemplateProgramCacheLock) { if (!TemplateProgramCache.ContainsKey(bodyHash)) TemplateProgramCache.Add(bodyHash, program); } return program; }
void HandleElement(Element e) { if (e.Kind == ElementKind.Element || e.Kind == ElementKind.StartTag) { // Start tag var name = e.StartTagTokens["name"] as Token; var suffix = e.StartTagTokens["suffix"] as Token; Location loc = name.Location; var tag = new Tag { Name = name.ToString(), Suffix = suffix.ToString(), SourcePath = name.Filename, LineNumber = loc.Line, LinePosition = loc.Position, Attributes = new List<TagAttribute>() }; var attrs = e.StartTagTokens["attrs"] as List<Dictionary<string, object>>; foreach (var attr in attrs) { var attrName = attr["name"] as Token; var attrValue = attr["value"] as Token; var attrEq = attr["eq"] as Token; var attrQuote = attr["quote"] as Token; var a = new TagAttribute { Name = attrName.ToString(), Value = attrValue.ToString(), Eq = attrEq.ToString(), Quote = attrQuote.ToString(), QuoteEntity = Utils.Char2Entity(attrQuote.ToString()) }; tag.Attributes.Add(a); } if ((e.Children.Count == 0 && suffix.ToString() == "/>") || e.EndTagTokens.Count == 0) { // Singleton element tag.Singleton = true; HandleStartTag(tag); HandleEndTag(tag); } else { tag.Singleton = false; HandleStartTag(tag); } // Children foreach (var item in e.Children) HandleElement(item); // End tag if (e.EndTagTokens.Count > 0) { var endName = e.EndTagTokens["name"] as Token; var endSuffix = e.EndTagTokens["suffix"] as Token; Location endLoc = endName.Location; var endTag = new Tag { Name = endName.ToString(), Suffix = endSuffix.ToString(), SourcePath = endName.Filename, LineNumber = endLoc.Line, LinePosition = endLoc.Position }; HandleEndTag(endTag); } } else if (e.Kind == ElementKind.Text) { foreach (Token token in e.StartTagTokens.Values) HandleData(token.ToString()); } else if (e.Kind == ElementKind.Comment) { foreach (Token token in e.StartTagTokens.Values) HandleComment(token.ToString()); } else if (e.Kind == ElementKind.CData) { foreach (Token token in e.StartTagTokens.Values) HandleCData(token.ToString()); } else if (e.Kind == ElementKind.ProcessingInstruction) { HandleProcessingInstruction(e); } else if (e.Kind == ElementKind.Default) { foreach (Token token in e.StartTagTokens.Values) HandleDefault(token.ToString()); } }
public Command(Tag tag, CommandType commandType) { if (tag != null) Tag = new Tag(tag); CommandType = commandType; }
public CmdStartTag(Tag tag) : base(tag, CommandType.CmdStartTag) { }