Beispiel #1
0
        internal static IMorestachioExpression ParseExpressionFromKind(this XmlReader reader)
        {
            IMorestachioExpression exp = null;

            switch (reader.GetAttribute(ExpressionKindNodeName))
            {
            case "Expression":
                exp = new MorestachioExpression();
                break;

            case "ExpressionList":
                exp = new MorestachioExpressionList();
                break;

            case "ExpressionString":
                exp = new MorestachioExpressionString();
                break;
            }
            exp.ReadXml(reader);
            return(exp);
        }
Beispiel #2
0
        /// <inheritdoc />
        protected bool Equals(MorestachioExpressionList other)
        {
            if (!Location.Equals(other.Location))
            {
                return(false);
            }

            if (other.Expressions.Count != Expressions.Count)
            {
                return(false);
            }

            for (var index = 0; index < Expressions.Count; index++)
            {
                var expression = Expressions[index];
                var otherExp   = other.Expressions[index];
                if (!expression.Equals(otherExp))
                {
                    return(false);
                }
            }
            return(true);
        }
Beispiel #3
0
        /// <summary>
        ///		Parses the text into one or more expressions
        /// </summary>
        /// <param name="text">the path to parse excluding {{ and }}</param>
        /// <param name="context">The context used to tokenize the text</param>
        /// <param name="indexVar">the index of where the parsing stoped</param>
        /// <returns></returns>
        public static IMorestachioExpression ParseFrom(string text,
                                                       TokenzierContext context,
                                                       out int indexVar)
        {
            var index = 0;

            text = text.Trim();
            if (!text.Contains("("))
            {
                var expression = new MorestachioExpression(context.CurrentLocation);
                for (; index < text.Length; index++)
                {
                    var c = text[index];
                    if (!expression.PathTokenizer.Add(c, context, index))
                    {
                        indexVar = 0;
                        return(null);
                    }
                }
                expression.CompilePath(context, 0);
                indexVar = text.Length;
                context.AdvanceLocation(text.Length);
                return(expression);
            }

            IMorestachioExpression morestachioExpressions = null;
            HeaderTokenMatch       currentScope           = null;
            //this COULD be made with regexes, i have made it and rejected it as it was no longer readable in any way.
            var tokenScopes = new Stack <HeaderTokenMatch>();

            tokenScopes.Push(new HeaderTokenMatch
            {
                State         = TokenState.DecideArgumentType,
                TokenLocation = context.CurrentLocation
            });
            //var currentPathPart = new StringBuilder();
            char formatChar;

            int SkipWhitespaces()
            {
                if (Tokenizer.IsWhiteSpaceDelimiter(formatChar))
                {
                    return(Seek(f => !Tokenizer.IsWhiteSpaceDelimiter(f), true));
                }

                return(index);
            }

            int Seek(Func <char, bool> condition, bool includeCurrent)
            {
                var idx = index;

                if (!includeCurrent)
                {
                    if (idx + 1 >= text.Length)
                    {
                        return(idx);
                    }

                    idx++;
                }

                for (; idx < text.Length; idx++)
                {
                    formatChar = text[idx];
                    if (condition(formatChar))
                    {
                        return(idx);
                    }
                }
                return(idx);
            }

            bool Eoex()
            {
                index = SkipWhitespaces();
                return(index + 1 == text.Length);
            }

            char?SeekNext(out int nIndex)
            {
                nIndex = Seek(f => Tokenizer.IsExpressionChar(f) || Tokenizer.IsPathDelimiterChar(f), false);
                if (nIndex != -1)
                {
                    return(text[nIndex]);
                }

                return(null);
            }

            char[] Take(Func <char, bool> condition, out int idx)
            {
                idx = index;
                var chrs = new List <char>();

                for (int i = idx; i < text.Length; i++)
                {
                    var c = text[i];
                    idx = i;
                    if (!condition(c))
                    {
                        break;
                    }
                    chrs.Add(c);
                }

                return(chrs.ToArray());
            }

            void TerminateCurrentScope(bool tryTerminate = false)
            {
                if ((tryTerminate && tokenScopes.Any()) || !tryTerminate)
                {
                    tokenScopes.Pop();
                }
            }

            int EndParameterBracket()
            {
                var  parent = currentScope.Parent?.Parent;
                char?seekNext;

                while (_closingChars.Contains(seekNext = SeekNext(out var seekIndex)))
                {
                    index = seekIndex;
                    if (seekNext == ')')                     //the next char is also an closing bracket so there is no next parameter nor an followup expression
                    {
                        //there is nothing after this expression so close it
                        TerminateCurrentScope();
                        HeaderTokenMatch scope = null;
                        if (tokenScopes.Any())
                        {
                            scope = tokenScopes.Peek();
                        }

                        if (scope?.Value is MorestachioExpressionList)                        //if the parent expression is a list close that list too
                        {
                            TerminateCurrentScope();
                            parent = parent?.Parent;
                        }
                        parent = parent?.Parent;                        //set the new parent for followup expression
                    }
                    else
                    {
                        //there is something after this expression
                        if (seekNext == '.')                        //the next char indicates a followup expression
                        {
                            HeaderTokenMatch scope = null;
                            if (tokenScopes.Any())
                            {
                                scope = tokenScopes.Peek();
                            }
                            if (scope != null && scope.Parent != null)                             //this is a nested expression
                            {
                                if ((scope.Parent?.Value is MorestachioExpressionList))            //the parents parent expression is already a list so close the parent
                                {
                                    scope = scope.Parent;
                                    TerminateCurrentScope();
                                }
                                else if (!(scope.Value is MorestachioExpressionList))                                //the parent is not an list expression so replace the parent with a list expression
                                {
                                    var oldValue = scope.Value as MorestachioExpression;
                                    scope.Value = new MorestachioExpressionList(new List <IMorestachioExpression>
                                    {
                                        oldValue
                                    });
                                    var parValue  = (scope.Parent.Value as MorestachioExpression);
                                    var hasFormat = parValue.Formats.FirstOrDefault(f => f.MorestachioExpression == oldValue);
                                    if (hasFormat != null)
                                    {
                                        hasFormat.MorestachioExpression = scope.Value;
                                    }
                                }
                                parent = scope;
                            }
                            else
                            {
                                //we are at root level no need to do anything as the root is already a list expression
                                TerminateCurrentScope(true);
                            }
                        }
                        else
                        {
                            //the next char indicates a new parameter so close this expression and allow next
                            TerminateCurrentScope(true);
                        }

                        if (!Eoex())
                        {
                            HeaderTokenMatch item;
                            if (parent != null)
                            {
                                //if there is a parent set then this indicates a new argument
                                item = new HeaderTokenMatch
                                {
                                    State         = TokenState.ArgumentStart,
                                    Parent        = parent,
                                    TokenLocation = context.CurrentLocation.Offset(index + 1)
                                };
                            }
                            else
                            {
                                index++;
                                index = SkipWhitespaces();
                                if (!Tokenizer.IsStartOfExpressionPathChar(formatChar) && formatChar != ')')
                                {
                                    context.Errors.Add(new InvalidPathSyntaxError(
                                                           context.CurrentLocation.Offset(index)
                                                           .AddWindow(new CharacterSnippedLocation(1, index, text)),
                                                           currentScope.Value.ToString()));
                                    return(text.Length);
                                }

                                if (morestachioExpressions != null)
                                {
                                    if (formatChar != '.')
                                    {
                                        context.Errors.Add(new InvalidPathSyntaxError(
                                                               context.CurrentLocation.Offset(index)
                                                               .AddWindow(new CharacterSnippedLocation(1, index, text)),
                                                               currentScope.Value.ToString()));
                                        return(text.Length);
                                    }
                                }

                                index--;
                                //currentScope.State = TokenState.DecideArgumentType;

                                //if there is no parent set this indicates a followup expression
                                item = new HeaderTokenMatch
                                {
                                    State         = TokenState.DecideArgumentType,
                                    Parent        = parent,
                                    Value         = new MorestachioExpression(context.CurrentLocation.Offset(index)),
                                    TokenLocation = context.CurrentLocation.Offset(index + 1)
                                };
                            }

                            tokenScopes.Push(item);
                        }

                        if (seekNext == '.')
                        {
                            index--;
                        }

                        break;
                    }

                    if (Eoex())
                    {
                        TerminateCurrentScope(true);
                        break;
                    }
                }

                return(index);
            }

            for (index = 0; index < text.Length; index++)
            {
                currentScope = tokenScopes.Peek();
                formatChar   = text[index];
                switch (currentScope.State)
                {
                case TokenState.ArgumentStart:
                {
                    //we are at the start of an argument
                    index = SkipWhitespaces();

                    if (formatChar == '[')
                    {
                        index++;
                        currentScope.ArgumentName = new string(Take(f => f != ']', out var idxa));
                        index = idxa + 1;
                    }

                    index--;                                     //reprocess the char
                    currentScope.State = TokenState.DecideArgumentType;
                }
                break;

                case TokenState.DecideArgumentType:
                {
                    //we are at the start of an argument
                    index = SkipWhitespaces();

                    var idx = index;
                    if (Tokenizer.IsStringDelimiter(formatChar))
                    {
                        //this is an string
                        var cidx = context.Character;
                        currentScope.Value = MorestachioExpressionString.ParseFrom(text, index, context, out index);
                        context.SetLocation(cidx);
                        currentScope.State = TokenState.Expression;
                    }
                    else if (Tokenizer.IsExpressionChar(formatChar))
                    {
                        currentScope.State = TokenState.Expression;
                        //this is the first char of an expression.
                        index--;
                        currentScope.Value = new MorestachioExpression(context.CurrentLocation.Offset(index));
                    }
                    else
                    {
                        //this is not the start of an expression and not a string
                        context.Errors.Add(new InvalidPathSyntaxError(
                                               context.CurrentLocation.Offset(index)
                                               .AddWindow(new CharacterSnippedLocation(1, index, text)),
                                               currentScope.Value.ToString()));
                        indexVar = 0;
                        return(null);
                    }

                    if (currentScope.Parent == null)
                    {
                        if (morestachioExpressions == null)
                        {
                            morestachioExpressions = currentScope.Value;
                        }
                        else
                        {
                            if (morestachioExpressions is MorestachioExpressionList expList)
                            {
                                expList.Add(currentScope.Value);
                            }
                            else
                            {
                                var oldExp = morestachioExpressions;
                                morestachioExpressions = new MorestachioExpressionList(new List <IMorestachioExpression>()
                                    {
                                        oldExp,
                                        currentScope.Value
                                    })
                                {
                                    Location = oldExp.Location
                                };
                            }
                        }
                    }
                    else
                    {
                        if (currentScope.Parent?.Value is MorestachioExpression exp)
                        {
                            exp.Formats.Add(
                                new ExpressionArgument(context.CurrentLocation.Offset(idx))
                                {
                                    MorestachioExpression = currentScope.Value,
                                    Name = currentScope.ArgumentName
                                });
                        }

                        if (currentScope.Parent?.Value is MorestachioExpressionList expList)
                        {
                            expList.Add(currentScope.Value);
                        }
                    }
                }
                break;

                case TokenState.Expression:
                {
                    index = SkipWhitespaces();
                    if (formatChar == '(')
                    {
                        //in this case the current path has ended and we must prepare for arguments

                        //if this scope was opened multible times, set an error
                        if (currentScope.BracketsCounter > 1)
                        {
                            context.Errors.Add(new MorestachioSyntaxError(context.CurrentLocation.Offset(index)
                                                                          .AddWindow(new CharacterSnippedLocation(1, index, text)),
                                                                          "Format", "(", "Name of Formatter",
                                                                          "Did expect to find the name of a formatter but found single path. Did you forgot to put an . before the 2nd formatter?"));
                            indexVar = 0;
                            return(null);
                        }

                        var currentExpression = currentScope.Value as MorestachioExpression;
                        currentExpression.CompilePath(context, index);
                        if (currentExpression.PathParts.Count == 0)
                        {
                            //in this case there are no parts in the path that indicates ether {{(}} or {{data.(())}}
                            context.Errors.Add(new MorestachioSyntaxError(context.CurrentLocation.Offset(index)
                                                                          .AddWindow(new CharacterSnippedLocation(1, index, text)),
                                                                          "Format", "(", "Name of Formatter",
                                                                          "Did expect to find the name of a formatter but found single path. Did you forgot to put an . before the 2nd formatter?"));
                            indexVar = 0;
                            return(null);
                        }

                        //get the last part of the path as the name of the formatter
                        currentExpression.FormatterName = currentExpression.PathTokenizer.GetFormatterName();
                        currentScope.BracketsCounter++;
                        //seek the next non whitespace char. That should be ether " or an expression char
                        index = Seek(f => !Tokenizer.IsWhiteSpaceDelimiter(f), false);
                        if (formatChar == ')')
                        {
                            //the only next char is the closing bracket so no arguments
                            currentScope.BracketsCounter--;
                            index = EndParameterBracket();
                        }
                        else
                        {
                            //indicates the start of an argument
                            index--;
                            tokenScopes.Push(new HeaderTokenMatch
                                {
                                    State  = TokenState.ArgumentStart,
                                    Parent = currentScope
                                });
                        }
                    }
                    else if (formatChar == ')')
                    {
                        ////close the current scope. This scope is an parameter expression
                        //TerminateCurrentScope();

                        var parentExpression = currentScope.Parent?.Value as MorestachioExpression;
                        currentScope.Parent.BracketsCounter--;
                        if (currentScope.Value is MorestachioExpression currentScopeValue)
                        {
                            currentScopeValue.CompilePath(context, index);
                            if (currentScopeValue != null &&
                                !currentScopeValue.PathParts.Any() && parentExpression?.Formats.Any() == true)
                            {
                                context.Errors.Add(new InvalidPathSyntaxError(
                                                       context.CurrentLocation.Offset(index)
                                                       .AddWindow(new CharacterSnippedLocation(1, index, text)),
                                                       currentScope.Value.ToString()));
                            }
                        }

                        TerminateCurrentScope();
                        index = EndParameterBracket();
                    }
                    else if (formatChar == ',')
                    {
                        if (currentScope.Value is MorestachioExpression currentScopeValue)
                        {
                            currentScopeValue.CompilePath(context, index);
                            if (currentScopeValue != null &&
                                !currentScopeValue.PathParts.Any())
                            {
                                context.Errors.Add(
                                    new InvalidPathSyntaxError(currentScopeValue.Location
                                                               .AddWindow(new CharacterSnippedLocation(1, index, text)),
                                                               ","));
                            }
                        }

                        TerminateCurrentScope();
                        //add a new one into the stack as , indicates a new argument
                        tokenScopes.Push(new HeaderTokenMatch
                            {
                                State  = TokenState.ArgumentStart,
                                Parent = currentScope.Parent
                            });
                    }
                    else if (currentScope.BracketsCounter == 0)
                    {
                        //we are in an path expression
                        //like data.data.data.data

                        if ((currentScope.Value as MorestachioExpression)?.PathTokenizer.Add(formatChar, context, index) == false)
                        {
                            indexVar = 0;
                            return(null);
                        }

                        if (Eoex())
                        {
                            //an expression can be ended just at any time
                            //it just should not end with an .

                            if (formatChar == '.')
                            {
                                context.Errors.Add(new MorestachioSyntaxError(context.CurrentLocation.Offset(index)
                                                                              .AddWindow(new CharacterSnippedLocation(1, index, text)),
                                                                              "Format", "(", "Name of Formatter",
                                                                              "Did not expect a . at the end of an expression without an formatter"));
                            }

                            (currentScope.Value as MorestachioExpression).CompilePath(context, index);
                            TerminateCurrentScope();
                        }
                    }
                }
                break;
                }
            }

            if (tokenScopes.Any())
            {
                context.Errors.Add(new InvalidPathSyntaxError(context.CurrentLocation.Offset(index)
                                                              .AddWindow(new CharacterSnippedLocation(1, index, text)),
                                                              text));
            }

            context.AdvanceLocation(index);
            indexVar = index;
            return(morestachioExpressions);
        }