private void ParseReplacement(TokenReader<DocumentToken> reader, List<IDocumentExpression> ret)
        {
            var openToken = AdvanceAndExpectConstantType(reader, DocumentTokenType.BeginReplacementSegment);
            var replacementKeyToken = AdvanceAndExpect(reader, DocumentTokenType.ReplacementKey, "replacement key", skipWhitespace: true);

            List<DocumentToken> parameters = new List<DocumentToken>();
            List<DocumentToken> body = new List<DocumentToken>();
            while (reader.CanAdvance(skipWhitespace: true) && reader.Peek(skipWhitespace: true).TokenType == DocumentTokenType.ReplacementParameter)
            {
                var paramToken = reader.Advance(skipWhitespace: true);
                parameters.Add(paramToken);
            }

            DocumentToken closeReplacementToken;
            if(reader.TryAdvance(out closeReplacementToken, skipWhitespace: true) == false)
            {
                throw Unexpected(string.Format("'{0}' or '{1}'", DocumentToken.GetTokenTypeValue(DocumentTokenType.EndReplacementSegment), DocumentToken.GetTokenTypeValue(DocumentTokenType.QuickTerminateReplacementSegment)));
            }

            if (closeReplacementToken.TokenType == DocumentTokenType.EndReplacementSegment)
            {
                body.AddRange(ReadReplacementBody(reader, replacementKeyToken));
            }
            else if (closeReplacementToken.TokenType == DocumentTokenType.QuickTerminateReplacementSegment)
            {
                // do nothing, there is no body when the quick termination replacement segment is used
            }
            else
            {
                throw Unexpected(string.Format("'{0}' or '{1}'", DocumentToken.GetTokenTypeValue(DocumentTokenType.EndReplacementSegment), DocumentToken.GetTokenTypeValue(DocumentTokenType.QuickTerminateReplacementSegment)), closeReplacementToken);
            }

            IDocumentExpressionProvider provider;
            if (this.expressionProviders.TryGetValue(replacementKeyToken.Value, out provider) == false)
            {
                provider = new EvalExpressionProvider();
            }

            var context = new DocumentExpressionContext
            {
                OpenToken = openToken,
                CloseToken = closeReplacementToken,
                Parameters = parameters.AsReadOnly(),
                Body = body.AsReadOnly(),
                ReplacementKeyToken = replacementKeyToken,
            };

            var expression = provider.CreateExpression(context);
            ret.Add(expression);
        }
        private List<DocumentToken> ReadReplacementBody(TokenReader<DocumentToken> reader, DocumentToken replacementKeyToken)
        {
            List<DocumentToken> replacementContents = new List<DocumentToken>();

            int numOpenReplacements = 1;

            while (reader.CanAdvance())
            {
                if (reader.Peek().TokenType == DocumentTokenType.BeginReplacementSegment)
                {
                    numOpenReplacements++;
                }
                else if (reader.Peek().TokenType == DocumentTokenType.QuickTerminateReplacementSegment)
                {
                    numOpenReplacements--;

                    if(numOpenReplacements == 0)
                    {
                        throw Unexpected(reader.Peek());
                    }

                }
                else if (reader.Peek().TokenType == DocumentTokenType.BeginTerminateReplacementSegment)
                {
                    numOpenReplacements--;

                    if(numOpenReplacements == 0)
                    {
                        AdvanceAndExpectConstantType(reader, DocumentTokenType.BeginTerminateReplacementSegment);
                        AdvanceAndExpect(reader, DocumentTokenType.ReplacementKey, replacementKeyToken.Value, skipWhitespace: true);
                        AdvanceAndExpectConstantType(reader, DocumentTokenType.EndReplacementSegment);
                        break;
                    }
                }

                replacementContents.Add(reader.Advance());
            }

            if(numOpenReplacements != 0)
            {
                throw Unexpected("end of '" + replacementKeyToken.Value + "' replacement");
            }

            return replacementContents;
        }
        /// <summary>
        /// Parses the given tokens into document expressions that can then be evaluated against a data context.
        /// </summary>
        /// <param name="tokens">The tokens to parse</param>
        /// <returns>a list of document expressions</returns>
        public List<IDocumentExpression> Parse(IEnumerable<DocumentToken> tokens)
        {
            List<IDocumentExpression> ret = new List<IDocumentExpression>();

            TokenReader<DocumentToken> reader = new TokenReader<DocumentToken>(tokens);

            while(reader.CanAdvance())
            {
                if(reader.Peek().TokenType == DocumentTokenType.BeginReplacementSegment)
                {
                    ParseReplacement(reader, ret);
                }
                else
                {
                    var plain = new PlainTextDocumentExpression(reader.Advance());
                    ret.Add(plain);
                }
            }

            return ret;
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Deserializes the given frame given a known width and height.
        /// </summary>
        /// <param name="serializedFrame">the frame data</param>
        /// <param name="width">the known width of the frame</param>
        /// <param name="height">the known height of the frame</param>
        /// <returns>a deserialized frame that's either a raw frame or a diff frame, depending on what was in the serialized string</returns>
        public ConsoleBitmapFrame DeserializeFrame(string serializedFrame, int width, int height)
        {
            var tokens = tokenizer.Tokenize(serializedFrame);
            var reader = new TokenReader<Token>(tokens);

            reader.Expect("[");
            var timestampToken = reader.Advance();
            var timestamp = new TimeSpan(long.Parse(timestampToken.Value));
            reader.Expect("]");

            reader.Expect("[");
            reader.Advance();
            var isDiff = reader.Current.Value == "Diff";
            reader.Expect("]");

            if (isDiff)
            {
                var diffFrame = new ConsoleBitmapDiffFrame()
                {
                    Timestamp = timestamp,
                    Diffs = new System.Collections.Generic.List<ConsoleBitmapPixelDiff>()
                };

                var lastBackground = ConsoleString.DefaultBackgroundColor;
                var lastForeground = ConsoleString.DefaultForegroundColor;
                while (reader.CanAdvance(skipWhitespace: true))
                {
                    reader.Expect("[", skipWhiteSpace: true);
                    if (reader.Peek().Value.StartsWith("F=") || reader.Peek().Value.StartsWith("B="))
                    {
                        reader.Advance();
                        var match = ColorSpecifierRegex.Match(reader.Current.Value);
                        if (match.Success == false) throw new FormatException($"Unexpected token {reader.Current.Value} at position {reader.Current.Position} ");

                        var isForeground = match.Groups["ForB"].Value == "F";

                        if (isForeground)
                        {
                            if (Enum.TryParse(match.Groups["color"].Value, out ConsoleColor c))
                            {
                                lastForeground = (RGB)c;
                            }
                            else if (RGB.TryParse(match.Groups["color"].Value, out lastForeground) == false)
                            {
                                throw new ArgumentException($"Expected a color @ {reader.Position}");
                            }
                        }
                        else
                        {
                            if (Enum.TryParse(match.Groups["color"].Value, out ConsoleColor c))
                            {
                                lastBackground = (RGB)c;
                            }
                            else if (RGB.TryParse(match.Groups["color"].Value, out lastBackground) == false)
                            {
                                throw new ArgumentException($"Expected a color @ {reader.Position}");
                            }
                        }

                        reader.Expect("]");
                    }
                    else
                    {
                        var match = PixelDiffRegex.Match(reader.Advance().Value);
                        if (match.Success == false) throw new FormatException("Could not parse pixel diff");

                        var valGroup = match.Groups["val"].Value;

                        char? nextChar = valGroup.Length == 1 ? valGroup[0] : valGroup == "OB" ? '[' : valGroup == "CB" ? ']' : new char?();
                        if (nextChar.HasValue == false) throw new FormatException($"Unexpected token {nextChar} @ {reader.Position}");

                        diffFrame.Diffs.Add(new ConsoleBitmapPixelDiff()
                        {
                            X = int.Parse(match.Groups["x"].Value),
                            Y = int.Parse(match.Groups["y"].Value),
                            Value = new ConsoleCharacter(nextChar.Value, lastForeground, lastBackground),
                        });

                        reader.Expect("]");
                    }
                }

                return diffFrame;
            }
            else
            {
                var rawFrame = new ConsoleBitmapRawFrame()
                {
                    Timestamp = timestamp,
                    Pixels = new ConsoleCharacter[width][]
                };

                for (var i = 0; i < width; i++)
                {
                    rawFrame.Pixels[i] = new ConsoleCharacter[height];
                }

                var x = 0;
                var y = 0;
                var lastFg = ConsoleString.DefaultForegroundColor;
                var lastBg = ConsoleString.DefaultBackgroundColor;
                while (reader.CanAdvance(skipWhitespace:true))
                {
                    reader.Expect("[", skipWhiteSpace:true);
                    var next = reader.Advance();
                    var match = ColorSpecifierRegex.Match(next.Value);
                    if (match.Success)
                    {  
                        var isForeground = match.Groups["ForB"].Value == "F";

                        if (isForeground)
                        {

                            if(Enum.TryParse<ConsoleColor>(match.Groups["color"].Value, out ConsoleColor c))
                            {
                                lastFg = c;
                            }
                            else if(RGB.TryParse(match.Groups["color"].Value, out lastFg) == false)
                            {
                                throw new ArgumentException($"Expected a color @ {reader.Position}");
                            }
                        }
                        else
                        {
                            if (Enum.TryParse<ConsoleColor>(match.Groups["color"].Value, out ConsoleColor c))
                            {
                                lastBg = c;
                            }
                            else if (RGB.TryParse(match.Groups["color"].Value, out lastBg) == false)
                            {
                                throw new ArgumentException($"Expected a color @ {reader.Position}");
                            }
                        }
                    }
                    else
                    {
                        char? nextChar = next.Value.Length == 1 ? next.Value[0] : next.Value == "OB" ? '[' : next.Value == "CB" ? ']' : new char?();
                        if (nextChar.HasValue == false) throw new FormatException($"Unexpected token {nextChar} @ {next.Position}");
                        rawFrame.Pixels[x][y++] = new ConsoleCharacter(nextChar.Value, lastFg, lastBg);
                        if (y == height)
                        {
                            y = 0;
                            x++;
                        }
                    }

                    reader.Expect("]");
                }

                return rawFrame;
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Creates either an if or an ifnot expression, based on its configuration, using the given document info.
        /// </summary>
        /// <param name="context">The context that contains information about the document being rendered</param>
        /// <returns>The expression, either an if or an ifnot expression</returns>
        public IDocumentExpression CreateExpression(DocumentExpressionContext context)
        {
            TokenReader<DocumentToken> reader = new TokenReader<DocumentToken>(context.Parameters);

            DocumentToken ifExpressionToken;
            if(reader.TryAdvance(out ifExpressionToken, skipWhitespace: true) == false)
            {
                throw new DocumentRenderException("missing if expression", context.ReplacementKeyToken);
            }

            if (reader.CanAdvance(skipWhitespace: true))
            {
                throw new DocumentRenderException("unexpected parameters after if expression", reader.Advance(skipWhitespace: true));
            }

            return not ? new IfNotExpression(ifExpressionToken, context.Body) : new IfExpression(ifExpressionToken, context.Body);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Parses an object path expression from a string.
        /// </summary>
        /// <param name="expression">The expression text to parse</param>
        /// <returns>The parsed expression</returns>
        public static ObjectPathExpression Parse(string expression)
        {
            if (expression == null) throw new ArgumentNullException("path cannot be null");
            if (expression.Length == 0) throw new FormatException("Cannot parse empty string");

            Tokenizer<ObjectPathToken> tokenizer = new Tokenizer<ObjectPathToken>();
            tokenizer.TokenFactory = ObjectPathToken.TokenFactoryImpl;
            tokenizer.WhitespaceBehavior = WhitespaceBehavior.DelimitAndInclude;
            tokenizer.DoubleQuoteBehavior = DoubleQuoteBehavior.IncludeQuotedTokensAsStringLiterals;
            tokenizer.Delimiters.Add("[");
            tokenizer.Delimiters.Add("]");
            tokenizer.Delimiters.Add(".");
            List<ObjectPathToken> tokens = tokenizer.Tokenize(expression);

            TokenReader<ObjectPathToken> reader = new TokenReader<ObjectPathToken>(tokens);

            List<IObjectPathElement> pathElements = new List<IObjectPathElement>();

            bool lastTokenWasNavigation = false;
            while (reader.CanAdvance(skipWhitespace: true))
            {
                var currentToken = reader.Advance(skipWhitespace: true);

                if (lastTokenWasNavigation == true && currentToken.TokenType == ObjectPathTokenType.IndexerOpen)
                {
                    throw new FormatException("Expected property, got '['" + " at " + currentToken.Position);
                }

                lastTokenWasNavigation = false;

                if(pathElements.Count == 0 && currentToken.TokenType == ObjectPathTokenType.NavigationElement)
                {
                    throw new FormatException("Expected property or index, got '" + currentToken.Value + "'" + " at " + currentToken.Position);
                }

                if (currentToken.TokenType == ObjectPathTokenType.IndexerClose ||
                    currentToken.TokenType == ObjectPathTokenType.StringLiteral)
                {
                    throw new FormatException("Expected property or index, got '" + currentToken.Value + "'" + " at " + currentToken.Position);
                }

                if (currentToken.TokenType == ObjectPathTokenType.IndexerOpen)
                {
                    // read index value
                    if (reader.TryAdvance(out currentToken,skipWhitespace: true) == false) throw new FormatException("Expected index value, got end of string");

                    if (currentToken.TokenType == ObjectPathTokenType.Identifier || currentToken.TokenType == ObjectPathTokenType.StringLiteral)
                    {
                        string indexValueText = currentToken.Value;

                        if(currentToken.TokenType == ObjectPathTokenType.StringLiteral)
                        {
                            indexValueText = indexValueText.Substring(1, indexValueText.Length - 2);
                        }

                        object indexValue;
                        int indexValueInt;
                        if (int.TryParse(indexValueText, out indexValueInt) == false)
                        {
                            indexValue = indexValueText;
                        }
                        else
                        {
                            indexValue = indexValueInt;
                        }

                        // read index close
                        if (reader.TryAdvance(out currentToken, skipWhitespace: true) == false) throw new FormatException("Expected ']', got end of string");
                        if (currentToken.TokenType != ObjectPathTokenType.IndexerClose) throw new FormatException("Expected ']', got '" + currentToken.Value + "' at " + currentToken.Position);

                        IndexerPathElement el = new IndexerPathElement(indexValue);
                        pathElements.Add(el);

                        if (reader.TryAdvance(out currentToken, skipWhitespace: true))
                        {
                            if (currentToken.TokenType != ObjectPathTokenType.NavigationElement) throw new FormatException("Expected '.', got '" + currentToken.Value + "' at " + currentToken.Position);
                            if (reader.CanAdvance(skipWhitespace: true) == false) throw new FormatException("Expected property, got end of string");
                            lastTokenWasNavigation = true;
                        }
                    }
                    else
                    {
                        throw new ArgumentException("Unexpected token '" + currentToken.Value + "' at " + currentToken.Position);
                    }
                }
                else if(currentToken.TokenType == ObjectPathTokenType.Identifier)
                {
                    PropertyPathElement el = new PropertyPathElement(currentToken.Value);
                    pathElements.Add(el);
                }
                else if(currentToken.TokenType == ObjectPathTokenType.NavigationElement)
                {
                    // do nothing
                }
                else
                {
                    throw new ArgumentException("Unexpected token '" + currentToken.Value + "' at " + currentToken.Position);
                }
            }

            return new ObjectPathExpression(pathElements);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Creates a table expression from the given document info.
        /// </summary>
        /// <param name="context">The context that contains information about the document being rendered</param>
        /// <returns>The created document expression</returns>
        public IDocumentExpression CreateExpression(DocumentExpressionContext context)
        {
            if (context.Body.Count > 0)
            {
                throw new DocumentRenderException("table tags can't have a body", context.ReplacementKeyToken);
            }

            TokenReader<DocumentToken> reader = new TokenReader<DocumentToken>(context.Parameters);

            DocumentToken variableExpressionToken;

            if (reader.TryAdvance(out variableExpressionToken, skipWhitespace: true) == false)
            {
                throw new DocumentRenderException("missing collection expression after table tag", context.ReplacementKeyToken);
            }

            List<DocumentToken> columns = new List<DocumentToken>();

            bool showDefaults = true;
            bool showPossibilities = true;

            while(reader.CanAdvance(skipWhitespace: true))
            {
                var nextToken = reader.Advance(skipWhitespace: true);

                if (nextToken.Value == "-HideDefaults")
                {
                    showDefaults = false;
                }
                else if(nextToken.Value == "-HideEnumValues")
                {
                    showPossibilities = false;
                }
                else
                {
                    columns.Add(nextToken);
                }
            }

            if (columns.Count == 0)
            {
                throw new DocumentRenderException("table elements need to have at least one column parameter", context.ReplacementKeyToken);
            }

            return new TableExpression(variableExpressionToken, columns, context) { ShowDefaultValuesForArguments = showDefaults, ShowPossibleValuesForArguments = showPossibilities };
        }