/// <summary> /// Creates an eval expression given template replacement info /// </summary> /// <param name="context">The context that contains information about the document being rendered</param> /// <returns></returns> public IDocumentExpression CreateExpression(DocumentExpressionContext context) { if (context.Body.Count > 0) { throw new DocumentRenderException("eval tags can't have a body", context.ReplacementKeyToken); } TokenReader <DocumentToken> reader = new TokenReader <DocumentToken>(context.Parameters); DocumentToken fgToken, bgToken; var ret = new EvalExpression(context.ReplacementKeyToken); if (reader.TryAdvance(out fgToken, skipWhitespace: true) == false) { return(ret); } else { ret.ForegroundColorToken = fgToken; } if (reader.TryAdvance(out bgToken, skipWhitespace: true) == false) { return(ret); } else { ret.BackgroundColorToken = bgToken; } return(ret); }
/// <summary> /// Creates a template expression given a replacement token and parameters. /// </summary> /// <param name="replacementKeyToken">The token whose value should be 'template'</param> /// <param name="parameters">There should be 2 parameters. One representing the name of the template to render and one representing the data to bind to the template.</param> /// <param name="body">Should be empty. Template expressions do not support a body.</param> /// <returns>a template expression</returns> public IDocumentExpression CreateExpression(DocumentToken replacementKeyToken, List <DocumentToken> parameters, List <DocumentToken> body) { if (body.Count > 0) { throw new DocumentRenderException("template tags can't have a body", replacementKeyToken); } TokenReader <DocumentToken> reader = new TokenReader <DocumentToken>(parameters); DocumentToken idToken; if (reader.TryAdvance(out idToken, skipWhitespace: true) == false) { throw new DocumentRenderException("missing template Id", replacementKeyToken); } DocumentToken evalToken; if (reader.TryAdvance(out evalToken, skipWhitespace: true) == false) { throw new DocumentRenderException("missing eval token", idToken); } var ret = new TemplateExpression(idToken, evalToken); return(ret); }
/// <summary> /// Creates a variable expression given replacement info /// </summary> /// <param name="replacementKeyToken">The replacement key token that should have a value of 'var'</param> /// <param name="parameters">There should be 2 parameters. The name of the variable and the initial value.</param> /// <param name="body">There should be no body</param> /// <returns>A variable expression</returns> public IDocumentExpression CreateExpression(DocumentToken replacementKeyToken, List <DocumentToken> parameters, List <DocumentToken> body) { if (body.Count > 0) { throw new DocumentRenderException("var tags can't have a body", body.First()); } TokenReader <DocumentToken> reader = new TokenReader <DocumentToken>(parameters); DocumentToken variableName; if (reader.TryAdvance(out variableName, skipWhitespace: true) == false) { throw new DocumentRenderException("Expected variable name after var tag", replacementKeyToken); } DocumentToken variableValue; if (reader.TryAdvance(out variableValue, skipWhitespace: true) == false) { throw new DocumentRenderException("Expected variable value expression", variableName); } return(new VarExpression(variableName, variableValue)); }
/// <summary> /// Takes in document replacement info and converts it into a document expression that represents an each loop. /// </summary> /// <param name="replacementKeyToken">The token that is expected to have a value of 'each'</param> /// <param name="parameters">The parameters which should follow the format ITERATION_VARIABLE_NAME in COLLECTION_EXPRESSION</param> /// <param name="body">The body of the each loop</param> /// <returns>The parsed each expression</returns> public IDocumentExpression CreateExpression(DocumentToken replacementKeyToken, List <DocumentToken> parameters, List <DocumentToken> body) { if (body.Count == 0) { throw new DocumentRenderException("Each tag has no body", replacementKeyToken); } TokenReader <DocumentToken> reader = new TokenReader <DocumentToken>(parameters); DocumentToken variableName; if (reader.TryAdvance(out variableName, skipWhitespace: true) == false) { throw new DocumentRenderException("missing variable name in each tag", replacementKeyToken); } DocumentToken inToken; if (reader.TryAdvance(out inToken, skipWhitespace: true) == false) { throw new DocumentRenderException("Expected 'in' after iteration variable", variableName); } if (inToken.Value != "in") { throw new DocumentRenderException("Expected 'in', got '" + inToken.Value + "'", inToken); } DocumentToken collectionExpressionToken; if (reader.TryAdvance(out collectionExpressionToken, skipWhitespace: true) == false) { throw new DocumentRenderException("Expected collection expression after 'in' ", inToken); } DocumentToken unexpected; if (reader.TryAdvance(out unexpected, skipWhitespace: true)) { throw new DocumentRenderException("Unexpected parameter '" + unexpected.Value + "' after collection", unexpected); } return(new EachExpression(variableName, collectionExpressionToken, body)); }
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); }
/// <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 }); }
private DocumentToken AdvanceAndExpectConstantType(TokenReader <DocumentToken> reader, DocumentTokenType expectedType) { DocumentToken read; if (reader.TryAdvance(out read, skipWhitespace: true) == false) { throw Unexpected(DocumentToken.GetTokenTypeValue(expectedType)); } if (read.TokenType != expectedType) { throw Unexpected(DocumentToken.GetTokenTypeValue(expectedType), read); } return(read); }
private DocumentToken AdvanceAndExpect(TokenReader <DocumentToken> reader, DocumentTokenType expectedType, string expectedText, bool skipWhitespace = false) { DocumentToken read; if (reader.TryAdvance(out read, skipWhitespace: skipWhitespace) == false) { throw Unexpected(expectedText); } if (read.TokenType != expectedType) { throw Unexpected(expectedText, read); } return(read); }
/// <summary> /// Creates either an if or an ifnot expression, based on its configuration, using the given document info. /// </summary> /// <param name="replacementKeyToken">The replacement key token whose value is either 'if' or 'ifnot'</param> /// <param name="parameters">There should be no parameters to an if or ifnot expression</param> /// <param name="body">The body which will be conditionally rendered.</param> /// <returns>The expression, either an if or an ifnot expression</returns> public IDocumentExpression CreateExpression(DocumentToken replacementKeyToken, List <DocumentToken> parameters, List <DocumentToken> body) { TokenReader <DocumentToken> reader = new TokenReader <DocumentToken>(parameters); DocumentToken ifExpressionToken; if (reader.TryAdvance(out ifExpressionToken, skipWhitespace: true) == false) { throw new DocumentRenderException("missing if expression", replacementKeyToken); } if (reader.CanAdvance(skipWhitespace: true)) { throw new DocumentRenderException("unexpected parameters after if expression", reader.Advance(skipWhitespace: true)); } return(not ? new IfNotExpression(ifExpressionToken, body) : new IfExpression(ifExpressionToken, body)); }
/// <summary> /// Creates a clear variable expression given replacement info /// </summary> /// <param name="context">The context that contains information about the document being rendered</param> /// <returns>a clear variable expression</returns> public IDocumentExpression CreateExpression(DocumentExpressionContext context) { if (context.Body.Count > 0) { throw new DocumentRenderException("clearvar tags can't have a body", context.ReplacementKeyToken); } TokenReader <DocumentToken> reader = new TokenReader <DocumentToken>(context.Parameters); DocumentToken variableName; if (reader.TryAdvance(out variableName, skipWhitespace: true) == false) { throw new DocumentRenderException("Expected variable name after clearvar tag", context.ReplacementKeyToken); } return(new ClearVarExpression(variableName)); }
/// <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)); }