public override void VisitBlock(Block block) { if (CanRewrite(block)) { var newNode = RewriteBlock(_blocks.Peek(), block); if (newNode != null) { _blocks.Peek().Children.Add(newNode); } } else { // Not rewritable. var builder = new BlockBuilder(block); builder.Children.Clear(); _blocks.Push(builder); base.VisitBlock(block); Debug.Assert(ReferenceEquals(builder, _blocks.Peek())); if (_blocks.Count > 1) { _blocks.Pop(); _blocks.Peek().Children.Add(builder.Build()); } } }
/// <summary> /// Instantiates a new <see cref="ParserResults"/> instance. /// </summary> /// <param name="success"><c>true</c> if parsing was successful, <c>false</c> otherwise.</param> /// <param name="document">The <see cref="Block"/> for the syntax tree.</param> /// <param name="tagHelperDescriptors"> /// The <see cref="TagHelperDescriptor"/>s that apply to the current Razor document. /// </param> /// <param name="errorSink"> /// The <see cref="ErrorSink"/> used to collect <see cref="RazorError"/>s encountered when parsing the /// current Razor document. /// </param> protected ParserResults(bool success, Block document, IEnumerable<TagHelperDescriptor> tagHelperDescriptors, ErrorSink errorSink) { if (document == null) { throw new ArgumentNullException(nameof(document)); } if (tagHelperDescriptors == null) { throw new ArgumentNullException(nameof(tagHelperDescriptors)); } if (errorSink == null) { throw new ArgumentNullException(nameof(errorSink)); } Success = success; Document = document; TagHelperDescriptors = tagHelperDescriptors; ErrorSink = errorSink; ParserErrors = errorSink.Errors; Prefix = tagHelperDescriptors.FirstOrDefault()?.Prefix; }
public static void EvaluateParseTree(Block actualRoot, Block expectedRoot) { // Evaluate the result var collector = new ErrorCollector(); if (expectedRoot == null) { Assert.Null(actualRoot); } else { // Link all the nodes expectedRoot.LinkNodes(); Assert.NotNull(actualRoot); EvaluateSyntaxTreeNode(collector, actualRoot, expectedRoot); if (collector.Success) { WriteTraceLine("Parse Tree Validation Succeeded:" + Environment.NewLine + collector.Message); } else { Assert.True(false, Environment.NewLine + collector.Message); } } }
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context) { var chunk = context.ChunkTreeBuilder.StartParentChunk<CodeAttributeChunk>(target); chunk.Attribute = Name; chunk.Prefix = Prefix; chunk.Suffix = Suffix; }
protected override SyntaxTreeNode RewriteBlock(BlockBuilder parent, Block block) { // Collect the content of this node var content = string.Concat(block.Children.Cast<Span>().Select(s => s.Content)); // Create a new span containing this content var span = new SpanBuilder(); span.EditHandler = new SpanEditHandler(HtmlTokenizer.Tokenize); FillSpan(span, block.Children.Cast<Span>().First().Start, content); return span.Build(); }
public virtual void VisitBlock(Block block) { VisitStartBlock(block); // Perf: Avoid allocating an enumerator. for (var i = 0; i < block.Children.Count; i++) { block.Children[i].Accept(this); } VisitEndBlock(block); }
public static TagHelperBlockBuilder Rewrite( string tagName, bool validStructure, Block tag, IEnumerable<TagHelperDescriptor> descriptors, ErrorSink errorSink) { // There will always be at least one child for the '<'. var start = tag.Children.First().Start; var attributes = GetTagAttributes(tagName, validStructure, tag, descriptors, errorSink); var tagMode = GetTagMode(tagName, tag, descriptors, errorSink); return new TagHelperBlockBuilder(tagName, tagMode, start, attributes, descriptors); }
public IEnumerable<TagHelperDescriptor> GetDescriptors(Block root) { if (root == null) { throw new ArgumentNullException(nameof(root)); } _directiveDescriptors = new List<TagHelperDirectiveDescriptor>(); // This will recurse through the syntax tree. VisitBlock(root); var resolutionContext = GetTagHelperDescriptorResolutionContext(_directiveDescriptors, _errorSink); var descriptors = _descriptorResolver.Resolve(resolutionContext); return descriptors; }
protected override bool CanRewrite(Block block) { var gen = block.ChunkGenerator as AttributeBlockChunkGenerator; if (gen != null && block.Children.Count > 0) { // Perf: Avoid allocating an enumerator. for (var i = 0; i < block.Children.Count; i++) { if (!IsLiteralAttributeValue(block.Children[i])) { return false; } } return true; } return false; }
private static bool IsTypeAttribute(Block block) { var span = block.Children.First() as Span; if (span == null) { return false; } var trimmedStartContent = span.Content.TrimStart(); if (trimmedStartContent.StartsWith("type", StringComparison.OrdinalIgnoreCase) && (trimmedStartContent.Length == 4 || ValidAfterTypeAttributeNameCharacters.Contains(trimmedStartContent[4]))) { return true; } return false; }
/// <summary> /// Instantiates a new <see cref="ParserResults"/> instance. /// </summary> /// <param name="document">The <see cref="Block"/> for the syntax tree.</param> /// <param name="tagHelperDescriptors"> /// The <see cref="TagHelperDescriptor"/>s that apply to the current Razor document. /// </param> /// <param name="errorSink"> /// The <see cref="ErrorSink"/> used to collect <see cref="RazorError"/>s encountered when parsing the /// current Razor document. /// </param> public ParserResults(Block document, IEnumerable<TagHelperDescriptor> tagHelperDescriptors, ErrorSink errorSink) : this(!errorSink.Errors.Any(), document, tagHelperDescriptors, errorSink) { if (document == null) { throw new ArgumentNullException(nameof(document)); } if (tagHelperDescriptors == null) { throw new ArgumentNullException(nameof(tagHelperDescriptors)); } if (errorSink == null) { throw new ArgumentNullException(nameof(errorSink)); } }
public Block TagHelperBlock( string tagName, TagMode tagMode, SourceLocation start, Block startTag, SyntaxTreeNode[] children, Block endTag) { var builder = new TagHelperBlockBuilder( tagName, tagMode, attributes: new List<TagHelperAttributeNode>(), children: children) { Start = start, SourceStartTag = startTag, SourceEndTag = endTag }; return builder.Build(); }
/// <summary> /// Instantiates a new <see cref="GeneratorResults"/> instance. /// </summary> /// <param name="document">The <see cref="Block"/> for the syntax tree.</param> /// <param name="tagHelperDescriptors"> /// The <see cref="TagHelperDescriptor"/>s that apply to the current Razor document. /// </param> /// <param name="errorSink"> /// The <see cref="ErrorSink"/> used to collect <see cref="RazorError"/>s encountered when parsing the /// current Razor document. /// </param> /// <param name="codeGeneratorResult">The results of generating code for the document.</param> /// <param name="chunkTree">A <see cref="ChunkTree"/> for the document.</param> public GeneratorResults(Block document, IEnumerable<TagHelperDescriptor> tagHelperDescriptors, ErrorSink errorSink, CodeGeneratorResult codeGeneratorResult, ChunkTree chunkTree) : base(document, tagHelperDescriptors, errorSink) { if (document == null) { throw new ArgumentNullException(nameof(document)); } if (tagHelperDescriptors == null) { throw new ArgumentNullException(nameof(tagHelperDescriptors)); } if (errorSink == null) { throw new ArgumentNullException(nameof(errorSink)); } if (codeGeneratorResult == null) { throw new ArgumentNullException(nameof(codeGeneratorResult)); } if (chunkTree == null) { throw new ArgumentNullException(nameof(chunkTree)); } GeneratedCode = codeGeneratorResult.Code; DesignTimeLineMappings = codeGeneratorResult.DesignTimeLineMappings; ChunkTree = chunkTree; }
protected override SyntaxTreeNode RewriteBlock(BlockBuilder parent, Block block) { var newBlock = new BlockBuilder(block); newBlock.Children.Clear(); var ws = block.Children.FirstOrDefault() as Span; IEnumerable<SyntaxTreeNode> newNodes = block.Children; if (ws.Content.All(Char.IsWhiteSpace)) { // Add this node to the parent var builder = new SpanBuilder(ws); builder.ClearSymbols(); FillSpan(builder, ws.Start, ws.Content); parent.Children.Add(builder.Build()); // Remove the old whitespace node newNodes = block.Children.Skip(1); } foreach (SyntaxTreeNode node in newNodes) { newBlock.Children.Add(node); } return newBlock.Build(); }
private static void EvaluateBlock(ErrorCollector collector, Block actual, Block expected) { if (actual.Type != expected.Type || !expected.ChunkGenerator.Equals(actual.ChunkGenerator)) { AddMismatchError(collector, actual, expected); } else { if (actual is TagHelperBlock) { EvaluateTagHelperBlock(collector, actual as TagHelperBlock, expected as TagHelperBlock); } AddPassedMessage(collector, expected); using (collector.Indent()) { var expectedNodes = expected.Children.GetEnumerator(); var actualNodes = actual.Children.GetEnumerator(); while (expectedNodes.MoveNext()) { if (!actualNodes.MoveNext()) { collector.AddError("{0} - FAILED :: No more elements at this node", expectedNodes.Current); } else { EvaluateSyntaxTreeNode(collector, actualNodes.Current, expectedNodes.Current); } } while (actualNodes.MoveNext()) { collector.AddError("End of Node - FAILED :: Found Node: {0}", actualNodes.Current); } } } }
public static void EvaluateResults(ParserResults results, Block expectedRoot, IList<RazorError> expectedErrors) { EvaluateParseTree(results.Document, expectedRoot); EvaluateRazorErrors(results.ParserErrors, expectedErrors); }
public static void EvaluateResults(ParserResults results, Block expectedRoot) { EvaluateResults(results, expectedRoot, null); }
protected virtual void RunParseTest(string document, Func<ParserBase, Action> parserActionSelector, Block expectedRoot, IList<RazorError> expectedErrors, bool designTimeParser, Func<ParserContext, ParserBase> parserSelector = null) { // Create the source var results = RunParse(document, parserActionSelector, designTimeParser, parserSelector); // Evaluate the results if (!ReferenceEquals(expectedRoot, IgnoreOutput)) { EvaluateResults(results, expectedRoot, expectedErrors); } }
protected virtual void ParseDocumentTest(string document, Block expectedRoot, bool designTimeParser, params RazorError[] expectedErrors) { RunParseTest(document, parser => parser.ParseDocument, expectedRoot, expectedErrors, designTimeParser, parserSelector: c => c.MarkupParser); }
private void CodeBlock(bool acceptTerminatingBrace, Block block) { EnsureCurrent(); while (!EndOfFile && !At(CSharpSymbolType.RightBrace)) { // Parse a statement, then return here Statement(); EnsureCurrent(); } if (EndOfFile) { Context.OnError( block.Start, RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF(block.Name, '}', '{'), length: 1 /* { OR } */); } else if (acceptTerminatingBrace) { Assert(CSharpSymbolType.RightBrace); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; AcceptAndMoveNext(); } }
private void CodeBlock(Block block) { CodeBlock(true, block); }
private void HandleStatement(Block block, CSharpSymbolType type) { switch (type) { case CSharpSymbolType.RazorCommentTransition: Output(SpanKind.Code); RazorComment(); Statement(block); break; case CSharpSymbolType.LeftBrace: // Verbatim Block block = block ?? new Block(RazorResources.BlockName_Code, CurrentLocation); AcceptAndMoveNext(); CodeBlock(block); break; case CSharpSymbolType.Keyword: // Keyword block HandleKeyword(false, StandardStatement); break; case CSharpSymbolType.Transition: // Embedded Expression block EmbeddedExpression(); break; case CSharpSymbolType.RightBrace: // Possible end of Code Block, just run the continuation break; case CSharpSymbolType.Comment: AcceptAndMoveNext(); break; default: // Other statement StandardStatement(); break; } }
private void Statement(Block block) { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.Any; // Accept whitespace but always keep the last whitespace node so we can put it back if necessary var lastWhitespace = AcceptWhiteSpaceInLines(); Debug.Assert(lastWhitespace == null || (lastWhitespace.Start.AbsoluteIndex + lastWhitespace.Content.Length == CurrentLocation.AbsoluteIndex)); if (EndOfFile) { if (lastWhitespace != null) { Accept(lastWhitespace); } return; } var type = CurrentSymbol.Type; var loc = CurrentLocation; // Both cases @: and @:: are triggered as markup, second colon in second case will be triggered as a plain text var isSingleLineMarkup = type == CSharpSymbolType.Transition && (NextIs(CSharpSymbolType.Colon, CSharpSymbolType.DoubleColon)); var isMarkup = isSingleLineMarkup || type == CSharpSymbolType.LessThan || (type == CSharpSymbolType.Transition && NextIs(CSharpSymbolType.LessThan)); if (Context.DesignTimeMode || !isMarkup) { // CODE owns whitespace, MARKUP owns it ONLY in DesignTimeMode. if (lastWhitespace != null) { Accept(lastWhitespace); } } else { var nextSymbol = Lookahead(1); // MARKUP owns whitespace EXCEPT in DesignTimeMode. PutCurrentBack(); // Put back the whitespace unless it precedes a '<text>' tag. if (nextSymbol != null && !string.Equals(nextSymbol.Content, SyntaxConstants.TextTagName, StringComparison.Ordinal)) { PutBack(lastWhitespace); } } if (isMarkup) { if (type == CSharpSymbolType.Transition && !isSingleLineMarkup) { Context.OnError( loc, RazorResources.ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start, length: 1 /* @ */); } // Markup block Output(SpanKind.Code); if (Context.DesignTimeMode && CurrentSymbol != null && (CurrentSymbol.Type == CSharpSymbolType.LessThan || CurrentSymbol.Type == CSharpSymbolType.Transition)) { PutCurrentBack(); } OtherParserBlock(); } else { // What kind of statement is this? HandleStatement(block, type); } }
private void ConditionalBlock(Block block) { AcceptAndMoveNext(); AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); // Parse the condition, if present (if not present, we'll let the C# compiler complain) if (AcceptCondition()) { AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); ExpectCodeBlock(block); } }
protected virtual void ParseBlockTest(string document, Block expectedRoot, params RazorError[] expectedErrors) { ParseBlockTest(document, expectedRoot, false, expectedErrors); }
protected virtual void ParseBlockTest(string document, Block expectedRoot) { ParseBlockTest(document, expectedRoot, false, null); }
protected virtual void ParseBlockTest(string document, Block expectedRoot, bool designTimeParser) { ParseBlockTest(document, expectedRoot, designTimeParser, null); }
private void FilterableCatchBlock() { Assert(CSharpKeyword.Catch); var block = new Block(CurrentSymbol); // Accept "catch" AcceptAndMoveNext(); AcceptWhile(IsValidStatementSpacingSymbol); // Parse the catch condition if present. If not present, let the C# compiler complain. if (AcceptCondition()) { AcceptWhile(IsValidStatementSpacingSymbol); if (At(CSharpKeyword.When)) { // Accept "when". AcceptAndMoveNext(); AcceptWhile(IsValidStatementSpacingSymbol); // Parse the filter condition if present. If not present, let the C# compiler complain. if (!AcceptCondition()) { // Incomplete condition. return; } AcceptWhile(IsValidStatementSpacingSymbol); } ExpectCodeBlock(block); } }
protected virtual void ParseBlockTest(string document, Block expectedRoot, bool designTimeParser, params RazorError[] expectedErrors) { RunParseTest(document, parser => parser.ParseBlock, expectedRoot, (expectedErrors ?? new RazorError[0]).ToList(), designTimeParser); }
private void ConditionalBlock(bool topLevel) { Assert(CSharpSymbolType.Keyword); var block = new Block(CurrentSymbol); ConditionalBlock(block); if (topLevel) { CompleteBlock(); } }