| File: System\Xml\Xsl\Xslt\XPathPatternParser.cs | Web Access |
| Project: src\runtime\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Xml; using System.Xml.Xsl.Qil; using System.Xml.Xsl.XPath; using XPathNodeType = System.Xml.XPath.XPathNodeType; using XPathParser = System.Xml.Xsl.XPath.XPathParser<System.Xml.Xsl.Qil.QilNode>; namespace System.Xml.Xsl.Xslt { internal sealed class XPathPatternParser { public interface IPatternBuilder : IXPathBuilder<QilNode> { IXPathBuilder<QilNode> GetPredicateBuilder(QilNode context); } private XPathScanner? _scanner; private IPatternBuilder? _ptrnBuilder; private readonly XPathParser _predicateParser = new XPathParser(); public QilNode Parse(XPathScanner scanner, IPatternBuilder ptrnBuilder) { Debug.Assert(_scanner == null && _ptrnBuilder == null); Debug.Assert(scanner != null && ptrnBuilder != null); QilNode? result = null; ptrnBuilder.StartBuild(); try { _scanner = scanner; _ptrnBuilder = ptrnBuilder; result = this.ParsePattern(); _scanner.CheckToken(LexKind.Eof); } finally { result = ptrnBuilder.EndBuild(result); #if DEBUG _ptrnBuilder = null; _scanner = null; #endif } return result!; } /* * Pattern ::= LocationPathPattern ('|' LocationPathPattern)* */ private QilNode ParsePattern() { QilNode opnd = ParseLocationPathPattern(); while (_scanner!.Kind == LexKind.Union) { _scanner.NextLex(); opnd = _ptrnBuilder!.Operator(XPathOperator.Union, opnd, ParseLocationPathPattern()); } return opnd; } /* * LocationPathPattern ::= '/' RelativePathPattern? | '//'? RelativePathPattern | IdKeyPattern (('/' | '//') RelativePathPattern)? */ private QilNode ParseLocationPathPattern() { QilNode opnd; switch (_scanner!.Kind) { case LexKind.Slash: _scanner.NextLex(); opnd = _ptrnBuilder!.Axis(XPathAxis.Root, XPathNodeType.All, null, null); if (XPathParser.IsStep(_scanner.Kind)) { opnd = _ptrnBuilder.JoinStep(opnd, ParseRelativePathPattern()); } return opnd; case LexKind.SlashSlash: _scanner.NextLex(); return _ptrnBuilder!.JoinStep( _ptrnBuilder.Axis(XPathAxis.Root, XPathNodeType.All, null, null), _ptrnBuilder.JoinStep( _ptrnBuilder.Axis(XPathAxis.DescendantOrSelf, XPathNodeType.All, null, null), ParseRelativePathPattern() ) ); case LexKind.Name: if (_scanner.CanBeFunction && _scanner.Prefix.Length == 0 && (_scanner.Name == "id" || _scanner.Name == "key")) { opnd = ParseIdKeyPattern(); switch (_scanner.Kind) { case LexKind.Slash: _scanner.NextLex(); opnd = _ptrnBuilder!.JoinStep(opnd, ParseRelativePathPattern()); break; case LexKind.SlashSlash: _scanner.NextLex(); opnd = _ptrnBuilder!.JoinStep(opnd, _ptrnBuilder.JoinStep( _ptrnBuilder.Axis(XPathAxis.DescendantOrSelf, XPathNodeType.All, null, null), ParseRelativePathPattern() ) ); break; } return opnd; } break; } opnd = ParseRelativePathPattern(); return opnd; } /* * IdKeyPattern ::= 'id' '(' Literal ')' | 'key' '(' Literal ',' Literal ')' */ private QilNode ParseIdKeyPattern() { Debug.Assert(_scanner!.CanBeFunction); Debug.Assert(_scanner.Prefix.Length == 0); Debug.Assert(_scanner.Name == "id" || _scanner.Name == "key"); List<QilNode> args = new List<QilNode>(2); if (_scanner.Name == "id") { _scanner.NextLex(); _scanner.PassToken(LexKind.LParens); _scanner.CheckToken(LexKind.String); args.Add(_ptrnBuilder!.String(_scanner.StringValue)); _scanner.NextLex(); _scanner.PassToken(LexKind.RParens); return _ptrnBuilder.Function("", "id", args); } else { _scanner.NextLex(); _scanner.PassToken(LexKind.LParens); _scanner.CheckToken(LexKind.String); args.Add(_ptrnBuilder!.String(_scanner.StringValue)); _scanner.NextLex(); _scanner.PassToken(LexKind.Comma); _scanner.CheckToken(LexKind.String); args.Add(_ptrnBuilder.String(_scanner.StringValue)); _scanner.NextLex(); _scanner.PassToken(LexKind.RParens); return _ptrnBuilder.Function("", "key", args); } } /* * RelativePathPattern ::= StepPattern (('/' | '//') StepPattern)* */ //Max depth to avoid StackOverflow private const int MaxParseRelativePathDepth = 1024; private int _parseRelativePath; private QilNode ParseRelativePathPattern() { if (++_parseRelativePath > MaxParseRelativePathDepth) { if (LocalAppContextSwitches.LimitXPathComplexity) { throw _scanner!.CreateException(SR.Xslt_InputTooComplex); } } QilNode opnd = ParseStepPattern(); if (_scanner!.Kind == LexKind.Slash) { _scanner.NextLex(); opnd = _ptrnBuilder!.JoinStep(opnd, ParseRelativePathPattern()); } else if (_scanner.Kind == LexKind.SlashSlash) { _scanner.NextLex(); opnd = _ptrnBuilder!.JoinStep(opnd, _ptrnBuilder.JoinStep( _ptrnBuilder.Axis(XPathAxis.DescendantOrSelf, XPathNodeType.All, null, null), ParseRelativePathPattern() ) ); } --_parseRelativePath; return opnd; } /* * StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* * ChildOrAttributeAxisSpecifier ::= @ ? | ('child' | 'attribute') '::' */ private QilNode ParseStepPattern() { QilNode opnd; XPathAxis axis; switch (_scanner!.Kind) { case LexKind.Dot: case LexKind.DotDot: throw _scanner.CreateException(SR.XPath_InvalidAxisInPattern); case LexKind.At: axis = XPathAxis.Attribute; _scanner.NextLex(); break; case LexKind.Axis: axis = _scanner.Axis; if (axis != XPathAxis.Child && axis != XPathAxis.Attribute) { throw _scanner.CreateException(SR.XPath_InvalidAxisInPattern); } _scanner.NextLex(); // Skip '::' _scanner.NextLex(); break; case LexKind.Name: case LexKind.Star: // NodeTest must start with Name or '*' axis = XPathAxis.Child; break; default: throw _scanner.CreateException(SR.XPath_UnexpectedToken, _scanner.RawValue); } XPathNodeType nodeType; string? nodePrefix, nodeName; XPathParser.InternalParseNodeTest(_scanner, axis, out nodeType, out nodePrefix, out nodeName); opnd = _ptrnBuilder!.Axis(axis, nodeType, nodePrefix, nodeName); XPathPatternBuilder? xpathPatternBuilder = _ptrnBuilder as XPathPatternBuilder; if (xpathPatternBuilder != null) { //for XPathPatternBuilder, get all predicates and then build them List<QilNode> predicates = new List<QilNode>(); while (_scanner.Kind == LexKind.LBracket) { predicates.Add(ParsePredicate(opnd)); } if (predicates.Count > 0) opnd = xpathPatternBuilder.BuildPredicates(opnd, predicates); } else { while (_scanner.Kind == LexKind.LBracket) { opnd = _ptrnBuilder.Predicate(opnd, ParsePredicate(opnd), /*reverseStep:*/false); } } return opnd; } /* * Predicate ::= '[' Expr ']' */ private QilNode ParsePredicate(QilNode context) { Debug.Assert(_scanner!.Kind == LexKind.LBracket); _scanner.NextLex(); QilNode result = _predicateParser.Parse(_scanner, _ptrnBuilder!.GetPredicateBuilder(context), LexKind.RBracket); Debug.Assert(_scanner.Kind == LexKind.RBracket); _scanner.NextLex(); return result; } } }