示例#1
0
        /// <summary>
        /// Finds the sub snippet with the given tagname in the parenting tag node.
        /// </summary>
        /// <param name="tagNode">The parenting tag node</param>
        /// <param name="snippetName">The subtag to search</param>
        /// <returns>The sub snippet if it is found or <c>null</c> otherwise</returns>
        /// <exception cref="FormatException">Thrown if the subtag has more than just a single <see cref="TMLTerminalNode"/>.</exception>
        private SubSnippet FindSubSnippet(TMLTagNode tagNode, string snippetName)
        {
            var subnode = tagNode.InnerNodes.Where(n => n is TMLTagNode).Select(n => (TMLTagNode)n).Where(n => n.TagName == snippetName).FirstOrDefault();

            if (subnode != null && subnode.InnerNodes.Count > 0)
            {
                //only single inner node allowed
                if (subnode.InnerNodes.Count > 1 || !(subnode.InnerNodes.First() is TMLTerminalNode))
                {
                    throw new FormatException("The solution tag may not have nested tags.");
                }
                return(ParseTrimmedContent((TMLTerminalNode)subnode.InnerNodes.First()));
            }
            return(null);
        }
示例#2
0
        /// <summary>
        /// Constructs the abstract syntax tree from a given text.
        /// </summary>
        /// <param name="text">The original text to analyze</param>
        /// <exception cref="FormatException">Thrown if the number of opening and closing tags to not match or if an unexpected closing tag is encountered.</exception>
        public TMLSyntaxTree(string text)
        {
            //Setup the regex patterns to match opening and closing tags
            var tagStartRegex   = new Regex(@"//<(?<tag>\w+)(?<attributes>[^>]*?)(?<!/)>");
            var tagEndRegex     = new Regex(@"//</(?<tag>\w+)>");
            var attributesRegex = new Regex("\\b(?<key>\\w+)\\b=\"(?<value>.*?)\"");

            //Find the opening and closing tags
            var tagStartMatches = tagStartRegex.Matches(text);
            var tagEndMatches   = tagEndRegex.Matches(text);

            if (tagStartMatches.Count != tagEndMatches.Count)
            {
                throw new FormatException($"There are {tagStartMatches.Count} opening tags but {tagEndMatches.Count} closing tags.");
            }

            //the character index where the last text portion (i.e. non-tag text) started
            var lastTextPosition = 0;
            //the index of the next tag start within tagStartMatches
            int nextTagStart = 0;
            //the index of the next tag end within tagEndMatches
            int nextTagEnd = 0;

            //stack of currently open tags
            var tagStack = new Stack <TMLTagNode>();

            //go through the matched opening and closing tags in order
            while (nextTagStart < tagStartMatches.Count || nextTagEnd < tagEndMatches.Count)
            {
                //character index of the next opening tag or text.Length if there are no more opening tags
                int nextStartPosition = (nextTagStart < tagStartMatches.Count ? tagStartMatches[nextTagStart].Index : text.Length);

                //character index of the next closing tag or text.length if there are no more closing tags
                int nextEndPosition = (nextTagEnd < tagEndMatches.Count ? tagEndMatches[nextTagEnd].Index : text.Length);

                //character index of the next opening or closing tag
                int nextPosition = Math.Min(nextStartPosition, nextEndPosition);

                //list of children of the tag we are currently in (either the global list or the child list of a node)
                var childrenOfParentNode = (tagStack.Count == 0 ? Nodes : tagStack.Peek().InnerNodes);

                //Add text nodes if the next tag is not at the current position
                if (nextPosition > lastTextPosition)
                {
                    childrenOfParentNode.Add(new TMLTerminalNode()
                    {
                        BeginIndex = lastTextPosition, Length = nextPosition - lastTextPosition
                    });
                }

                if (nextStartPosition < nextEndPosition)
                {
                    //The next tag is an opening one
                    var match = tagStartMatches[nextTagStart++];
                    var tag   = new TMLTagNode()
                    {
                        TagName = match.Groups["tag"].Value, OpeningTagBeginIndex = match.Index, OpeningTagLength = match.Length
                    };

                    //Gather the attributes
                    var attributesString = match.Groups["attributes"].Value;
                    var attributeMatches = attributesRegex.Matches(attributesString);
                    foreach (Match a in attributeMatches)
                    {
                        tag.Attributes[a.Groups["key"].Value] = a.Groups["value"].Value;
                    }

                    //Add the new tag to the parent
                    childrenOfParentNode.Add(tag);
                    //move into this tag
                    tagStack.Push(tag);

                    lastTextPosition = match.Index + match.Length;
                }
                else
                {
                    //The next tag is a closing one
                    var match   = tagEndMatches[nextTagEnd++];
                    var tagname = match.Groups["tag"].Value;
                    if (tagStack.Count == 0)
                    {
                        throw new FormatException($"Encountered a closing tag of {tagname} but expected no closing tag.");
                    }

                    if (tagStack.Peek().TagName != tagname)
                    {
                        throw new FormatException($"Encountered a closing tag of {tagname} but expected a closing tag of {tagStack.Peek().TagName}.");
                    }

                    //leave the tag
                    var tag = tagStack.Pop();
                    tag.ClosingTagBeginIndex = match.Index;
                    tag.ClosingTagLength     = match.Length;
                    lastTextPosition         = match.Index + match.Length;
                }
            }
            //Add a text node for everything after the last tag
            if (text.Length > lastTextPosition)
            {
                Nodes.Add(new TMLTerminalNode()
                {
                    BeginIndex = lastTextPosition, Length = text.Length - lastTextPosition
                });
            }
        }