XPathParser.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / Xml / System / Xml / XPath / Internal / XPathParser.cs / 1 / XPathParser.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace MS.Internal.Xml.XPath { 
    using System;
    using System.Xml; 
    using System.Xml.XPath;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.InteropServices; 
    using System.Collections;
 
    internal class XPathParser { 
        XPathScanner scanner;
 
 		private XPathParser(XPathScanner scanner) {
            this.scanner = scanner;
		}
 
		public static AstNode ParseXPathExpresion(string xpathExpresion) {
			XPathScanner scanner = new XPathScanner(xpathExpresion); 
 			XPathParser  parser  = new XPathParser(scanner); 
            AstNode result = parser.ParseExpresion(null);
            if (scanner.Kind != XPathScanner.LexKind.Eof) { 
                throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
            }
			return result;
 		} 

 		public static AstNode ParseXPathPattern(string xpathPattern) { 
			XPathScanner scanner = new XPathScanner(xpathPattern); 
 			XPathParser  parser  = new XPathParser(scanner);
            AstNode result = parser.ParsePattern(null); 
            if (scanner.Kind != XPathScanner.LexKind.Eof) {
                throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
            }
			return result; 
		}
 
        // --------------- Expresion Parsing ---------------------- 

        private AstNode ParseExpresion(AstNode qyInput) { 
            return ParseOrExpr(qyInput);
        }

        //>> OrExpr ::= ( OrExpr 'or' )? AndExpr 
        private AstNode ParseOrExpr(AstNode qyInput) {
            AstNode opnd = ParseAndExpr(qyInput); 
 
            do {
                if (! TestOp("or")) { 
                    return opnd;
                }
                NextLex();
                opnd = new Operator(Operator.Op.OR, opnd, ParseAndExpr(qyInput)); 
            }while (true);
        } 
 
        //>> AndExpr ::= ( AndExpr 'and' )? EqualityExpr
        private AstNode ParseAndExpr(AstNode qyInput) { 
            AstNode opnd = ParseEqualityExpr(qyInput);

            do {
                if (! TestOp("and")) { 
                    return opnd;
                } 
                NextLex(); 
                opnd = new Operator(Operator.Op.AND, opnd, ParseEqualityExpr(qyInput));
            }while (true); 
        }

        //>> EqualityOp ::= '=' | '!='
        //>> EqualityExpr    ::= ( EqualityExpr EqualityOp )? RelationalExpr 
        private AstNode ParseEqualityExpr(AstNode  qyInput) {
            AstNode opnd = ParseRelationalExpr(qyInput); 
 
            do {
                Operator.Op op = ( 
                    this.scanner.Kind == XPathScanner.LexKind.Eq ? Operator.Op.EQ :
                    this.scanner.Kind == XPathScanner.LexKind.Ne ? Operator.Op.NE :
                    /*default :*/                                  Operator.Op.INVALID
                ); 
                if (op == Operator.Op.INVALID) {
                    return opnd; 
                } 
                NextLex();
                opnd = new Operator(op, opnd, ParseRelationalExpr(qyInput)); 
            }while (true);
        }

        //>> RelationalOp ::= '<' | '>' | '<=' | '>=' 
        //>> RelationalExpr    ::= ( RelationalExpr RelationalOp )? AdditiveExpr
        private AstNode ParseRelationalExpr(AstNode  qyInput) { 
            AstNode  opnd = ParseAdditiveExpr(qyInput); 

            do { 
                Operator.Op op = (
                    this.scanner.Kind == XPathScanner.LexKind.Lt ? Operator.Op.LT :
                    this.scanner.Kind == XPathScanner.LexKind.Le ? Operator.Op.LE :
                    this.scanner.Kind == XPathScanner.LexKind.Gt ? Operator.Op.GT : 
                    this.scanner.Kind == XPathScanner.LexKind.Ge ? Operator.Op.GE :
                    /*default :*/                                  Operator.Op.INVALID 
                ); 
                if (op == Operator.Op.INVALID) {
                    return opnd; 
                }
                NextLex();
                opnd = new Operator(op, opnd, ParseAdditiveExpr(qyInput));
            }while (true); 
        }
 
        //>> AdditiveOp   ::= '+' | '-' 
        //>> AdditiveExpr ::= ( AdditiveExpr AdditiveOp )? MultiplicativeExpr
        private AstNode ParseAdditiveExpr(AstNode  qyInput) { 
            AstNode  opnd = ParseMultiplicativeExpr(qyInput);

            do {
                Operator.Op op = ( 
                    this.scanner.Kind == XPathScanner.LexKind.Plus  ? Operator.Op.PLUS  :
                    this.scanner.Kind == XPathScanner.LexKind.Minus ? Operator.Op.MINUS : 
                    /*default :*/                                     Operator.Op.INVALID 
                );
                if (op == Operator.Op.INVALID) { 
                    return opnd;
                }
                NextLex();
                opnd = new Operator(op, opnd, ParseMultiplicativeExpr(qyInput)); 
            }while (true);
        } 
 
        //>> MultiplicativeOp   ::= '*' | 'div' | 'mod'
        //>> MultiplicativeExpr ::= ( MultiplicativeExpr MultiplicativeOp )? UnaryExpr 
        private AstNode ParseMultiplicativeExpr(AstNode  qyInput) {
            AstNode  opnd = ParseUnaryExpr(qyInput);

            do { 
                Operator.Op op = (
                    this.scanner.Kind == XPathScanner.LexKind.Star  ? Operator.Op.MUL : 
                    TestOp("div")                                   ? Operator.Op.DIV : 
                    TestOp("mod")                                   ? Operator.Op.MOD :
                    /*default :*/                                     Operator.Op.INVALID 
                );
                if (op == Operator.Op.INVALID) {
                    return opnd;
                } 
                NextLex();
                opnd = new Operator(op, opnd, ParseUnaryExpr(qyInput)); 
            }while (true); 
        }
 
        //>> UnaryExpr    ::= UnionExpr | '-' UnaryExpr
        private AstNode ParseUnaryExpr(AstNode  qyInput) {
            if (this.scanner.Kind == XPathScanner.LexKind.Minus) {
                NextLex(); 
                return new Operator(Operator.Op.MUL, ParseUnaryExpr(qyInput), new Operand(-1));
            } 
            else { 
                return ParseUnionExpr(qyInput);
            } 
        }

        //>> UnionExpr ::= ( UnionExpr '|' )? PathExpr
        private AstNode ParseUnionExpr(AstNode  qyInput) { 
            AstNode opnd = ParsePathExpr(qyInput);
 
            do { 
                if (this.scanner.Kind != XPathScanner.LexKind.Union) {
                    return opnd; 
                }
                NextLex();
                AstNode opnd2 = ParsePathExpr(qyInput);
                CheckNodeSet(opnd.ReturnType); 
                CheckNodeSet(opnd2.ReturnType);
                opnd = new Operator(Operator.Op.UNION, opnd, opnd2); 
            }while (true); 
        }
 
        private static bool IsNodeType(XPathScanner scaner) {
            return (
                scaner.Prefix.Length == 0 && (
                    scaner.Name == "node"                   || 
                    scaner.Name == "text"                   ||
                    scaner.Name == "processing-instruction" || 
                    scaner.Name == "comment" 
                )
            ); 
        }

        //>> PathOp   ::= '/' | '//'
        //>> PathExpr ::= LocationPath | 
        //>>              FilterExpr ( PathOp  RelativeLocationPath )?
        private AstNode ParsePathExpr(AstNode qyInput) { 
			AstNode opnd; 
 			if (IsPrimaryExpr(this.scanner)) { // in this moment we shoud distinct LocationPas vs FilterExpr (which starts from is PrimaryExpr)
			    opnd = ParseFilterExpr(qyInput); 
 				if (this.scanner.Kind == XPathScanner.LexKind.Slash) {
 					NextLex();
					opnd = ParseRelativeLocationPath(opnd);
 				} 
				else if (this.scanner.Kind == XPathScanner.LexKind.SlashSlash) {
					NextLex(); 
					opnd = ParseRelativeLocationPath(new Axis(Axis.AxisType.DescendantOrSelf, opnd)); 
 				}
			} 
 			else {
                opnd = ParseLocationPath(null);
            }
 
            return opnd;
        } 
 
        //>> FilterExpr ::= PrimaryExpr | FilterExpr Predicate
        private AstNode ParseFilterExpr(AstNode  qyInput) { 
            AstNode  opnd = ParsePrimaryExpr(qyInput);
            while (this.scanner.Kind == XPathScanner.LexKind.LBracket) {
                // opnd must be a query
                opnd = new Filter(opnd, ParsePredicate(opnd)); 
            }
            return opnd; 
        } 

        //>> Predicate ::= '[' Expr ']' 
        private AstNode ParsePredicate(AstNode  qyInput) {
            AstNode  opnd;

            // we have predicates. Check that input type is NodeSet 
            CheckNodeSet(qyInput.ReturnType);
 
            PassToken(XPathScanner.LexKind.LBracket); 
            opnd = ParseExpresion(qyInput);
            PassToken(XPathScanner.LexKind.RBracket); 

            return opnd;
        }
 
        //>> LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
        private AstNode ParseLocationPath(AstNode qyInput) { 
            if (this.scanner.Kind == XPathScanner.LexKind.Slash) { 
                NextLex();
                AstNode opnd = new Root(); 

                if (IsStep(this.scanner.Kind)) {
                    opnd = ParseRelativeLocationPath(opnd);
                } 
                return opnd;
            } 
            else if (this.scanner.Kind == XPathScanner.LexKind.SlashSlash) { 
                NextLex();
                return ParseRelativeLocationPath(new Axis(Axis.AxisType.DescendantOrSelf, new Root())); 
            }
            else {
                return  ParseRelativeLocationPath(qyInput);
            } 
        } // ParseLocationPath
 
        //>> PathOp   ::= '/' | '//' 
        //>> RelativeLocationPath ::= ( RelativeLocationPath PathOp )? Step
        private AstNode ParseRelativeLocationPath(AstNode  qyInput) { 
            AstNode  opnd = ParseStep(qyInput);
            if (XPathScanner.LexKind.SlashSlash == this.scanner.Kind) {
                NextLex();
                opnd = ParseRelativeLocationPath(new Axis(Axis.AxisType.DescendantOrSelf,opnd)); 
            }
 			else if (XPathScanner.LexKind.Slash == this.scanner.Kind) { 
                NextLex(); 
                opnd = ParseRelativeLocationPath(opnd);
            } 
            return opnd;
        }

		private static bool IsStep(XPathScanner.LexKind lexKind) { 
 			return (
				lexKind == XPathScanner.LexKind.Dot    || 
				lexKind == XPathScanner.LexKind.DotDot || 
				lexKind == XPathScanner.LexKind.At     ||
 				lexKind == XPathScanner.LexKind.Axe    || 
				lexKind == XPathScanner.LexKind.Star   ||
 				lexKind == XPathScanner.LexKind.Name          // NodeTest is also Name
 			);
		} 

        //>> Step ::= '.' | '..' | ( AxisName '::' | '@' )? NodeTest Predicate* 
        private AstNode ParseStep(AstNode  qyInput) { 
            AstNode  opnd;
            if (XPathScanner.LexKind.Dot == this.scanner.Kind) {         //>> '.' 
                NextLex();
                opnd = new Axis(Axis.AxisType.Self, qyInput);
            }
            else if (XPathScanner.LexKind.DotDot == this.scanner.Kind) { //>> '..' 
                NextLex();
                opnd = new Axis(Axis.AxisType.Parent, qyInput); 
            } 
            else {                                                          //>> ( AxisName '::' | '@' )? NodeTest Predicate*
                Axis.AxisType axisType = Axis.AxisType.Child; 
                switch (this.scanner.Kind) {
                case XPathScanner.LexKind.At:                               //>> '@'
                    axisType = Axis.AxisType.Attribute;
                    NextLex(); 
                    break;
                case XPathScanner.LexKind.Axe:                              //>> AxisName '::' 
                    axisType = GetAxis(this.scanner); 
                    NextLex();
                    break; 
                }
                XPathNodeType nodeType = (
                    axisType == Axis.AxisType.Attribute ? XPathNodeType.Attribute :
//                    axisType == Axis.AxisType.Namespace ? XPathNodeType.Namespace : // No Idea why it's this way but othervise Axes doesn't work 
                    /* default: */                        XPathNodeType.Element
                ); 
 
                opnd = ParseNodeTest(qyInput, axisType, nodeType);
 
                while (XPathScanner.LexKind.LBracket == this.scanner.Kind) {
                    opnd = new Filter(opnd, ParsePredicate(opnd));
                }
            } 
            return opnd;
        } 
 
        //>> NodeTest ::= NameTest | 'comment ()' | 'text ()' | 'node ()' | 'processing-instruction ('  Literal ? ')'
        private AstNode ParseNodeTest(AstNode qyInput, Axis.AxisType axisType, XPathNodeType nodeType) { 
            string        nodeName, nodePrefix;

            switch (this.scanner.Kind) {
            case XPathScanner.LexKind.Name : 
                if (this.scanner.CanBeFunction && IsNodeType(this.scanner)) {
                    nodePrefix = string.Empty; 
                    nodeName   = string.Empty; 
                    nodeType = (
                        this.scanner.Name == "comment"                ? XPathNodeType.Comment : 
                        this.scanner.Name == "text"                   ? XPathNodeType.Text :
                        this.scanner.Name == "node"                   ? XPathNodeType.All :
                        this.scanner.Name == "processing-instruction" ? XPathNodeType.ProcessingInstruction :
                        /* default: */ XPathNodeType.Root 
                    );
                    Debug.Assert(nodeType != XPathNodeType.Root); 
                    NextLex(); 

                    PassToken(XPathScanner.LexKind.LParens); 

                    if (nodeType == XPathNodeType.ProcessingInstruction) {
                        if (this.scanner.Kind != XPathScanner.LexKind.RParens) { //>> 'processing-instruction (' Literal ')'
                            CheckToken(XPathScanner.LexKind.String); 
                            nodeName = this.scanner.StringValue;
                            NextLex(); 
                        } 
                    }
 
                    PassToken(XPathScanner.LexKind.RParens);
                }
                else {
                    nodePrefix = this.scanner.Prefix; 
                    nodeName   = this.scanner.Name;
 	                NextLex(); 
                    if (nodeName == "*") { 
                        nodeName = string.Empty;
                    } 
                }
                break;
            case XPathScanner.LexKind.Star :
                nodePrefix = string.Empty; 
                nodeName   = string.Empty;
                NextLex(); 
                break; 
            default :
	            throw XPathException.Create(Res.Xp_NodeSetExpected, this.scanner.SourceText); 
            }
            return new Axis(axisType, qyInput, nodePrefix, nodeName, nodeType);
        }
 
		private static bool IsPrimaryExpr(XPathScanner scanner) {
			return ( 
 				scanner.Kind == XPathScanner.LexKind.String  || 
				scanner.Kind == XPathScanner.LexKind.Number  ||
 				scanner.Kind == XPathScanner.LexKind.Dollar  || 
 				scanner.Kind == XPathScanner.LexKind.LParens ||
				scanner.Kind == XPathScanner.LexKind.Name && scanner.CanBeFunction && ! IsNodeType(scanner)
 			);
		} 

        //>> PrimaryExpr ::= Literal | Number | VariableReference | '(' Expr ')' | FunctionCall 
        private AstNode ParsePrimaryExpr(AstNode  qyInput) { 
            Debug.Assert(IsPrimaryExpr(this.scanner));
            AstNode  opnd = null; 
            switch (this.scanner.Kind) {
            case XPathScanner.LexKind.String:
                opnd = new Operand(this.scanner.StringValue);
                NextLex(); 
                break;
            case XPathScanner.LexKind.Number: 
                opnd = new Operand(this.scanner.NumberValue); 
                NextLex();
                break; 
            case XPathScanner.LexKind.Dollar:
                NextLex();
                CheckToken(XPathScanner.LexKind.Name);
                opnd = new Variable(this.scanner.Name, this.scanner.Prefix); 
                NextLex();
                break; 
            case XPathScanner.LexKind.LParens: 
                NextLex();
                opnd = ParseExpresion(qyInput); 
                if (opnd.Type != AstNode.AstType.ConstantOperand) {
                    opnd = new Group(opnd);
                }
                PassToken(XPathScanner.LexKind.RParens); 
                break;
            case XPathScanner.LexKind.Name : 
                if (this.scanner.CanBeFunction && ! IsNodeType(this.scanner)) { 
                    opnd = ParseMethod(null);
                } 
                break;
            }
			Debug.Assert(opnd != null, "IsPrimaryExpr() was true. We should recognize this lex.");
            return opnd; 
        }
 
        private AstNode ParseMethod(AstNode qyInput) { 
            ArrayList argList = new ArrayList();
            string name   = this.scanner.Name; 
            string prefix = this.scanner.Prefix;
            PassToken(XPathScanner.LexKind.Name);
            PassToken(XPathScanner.LexKind.LParens);
            if (this.scanner.Kind != XPathScanner.LexKind.RParens) { 
                do {
                    argList.Add(ParseExpresion(qyInput)); 
                    if (this.scanner.Kind == XPathScanner.LexKind.RParens) { 
                        break;
                    } 
                    PassToken(XPathScanner.LexKind.Comma);
                }while (true);
            }
            PassToken(XPathScanner.LexKind.RParens); 
            if (prefix.Length == 0) {
                ParamInfo pi = (ParamInfo) functionTable[name]; 
                if (pi != null) { 
                    int argCount = argList.Count;
                    if (argCount < pi.Minargs) { 
                        throw XPathException.Create(Res.Xp_InvalidNumArgs, name, this.scanner.SourceText);
                    }
                    if (pi.FType == Function.FunctionType.FuncConcat) {
                        for (int i = 0; i < argCount; i ++) { 
                            AstNode arg = (AstNode)argList[i];
                            if (arg.ReturnType != XPathResultType.String) { 
                                arg = new Function(Function.FunctionType.FuncString, arg); 
                            }
                            argList[i] = arg; 
                        }
                    }
                    else {
                        if (pi.Maxargs < argCount) { 
                            throw XPathException.Create(Res.Xp_InvalidNumArgs, name, this.scanner.SourceText);
                        } 
                        if (pi.ArgTypes.Length < argCount) { 
                            argCount = pi.ArgTypes.Length;    // argument we have the type specified (can be < pi.Minargs)
                        } 
                        for (int i = 0; i < argCount; i ++) {
                            AstNode arg = (AstNode)argList[i];
                            if (
                                pi.ArgTypes[i] != XPathResultType.Any && 
                                pi.ArgTypes[i] != arg.ReturnType
                            ) { 
                                switch (pi.ArgTypes[i]) { 
                                case  XPathResultType.NodeSet :
                                    if (!(arg is Variable) && !(arg is Function && arg.ReturnType == XPathResultType.Any) ) { 
                                        throw XPathException.Create(Res.Xp_InvalidArgumentType, name, this.scanner.SourceText);
                                    }
                                    break;
                                case  XPathResultType.String : 
                                    arg = new Function(Function.FunctionType.FuncString, arg);
                                    break; 
                                case  XPathResultType.Number : 
                                    arg = new Function(Function.FunctionType.FuncNumber, arg);
                                    break; 
                                case  XPathResultType.Boolean :
                                    arg = new Function(Function.FunctionType.FuncBoolean, arg);
                                    break;
                                } 
                                argList[i] = arg;
                            } 
                        } 
                    }
					return new Function(pi.FType, argList); 
                }
            }
            return new Function(prefix, name, argList);
        } 

        // --------------- Pattern Parsing ---------------------- 
 
        //>> Pattern ::= ( Pattern '|' )? LocationPathPattern
        private AstNode ParsePattern(AstNode  qyInput) { 
            AstNode opnd = ParseLocationPathPattern(qyInput);

            do {
                if (this.scanner.Kind != XPathScanner.LexKind.Union) { 
                    return opnd;
                } 
                NextLex(); 
                opnd = new Operator(Operator.Op.UNION, opnd, ParseLocationPathPattern(qyInput));
            }while (true); 
        }

        //>> LocationPathPattern ::= '/' | RelativePathPattern | '//' RelativePathPattern  |  '/' RelativePathPattern
        //>>                       | IdKeyPattern (('/' | '//') RelativePathPattern)? 
        private AstNode ParseLocationPathPattern(AstNode qyInput) {
            AstNode opnd = null; 
            switch (this.scanner.Kind) { 
            case XPathScanner.LexKind.Slash :
                NextLex(); 
                opnd = new Root();
                if (this.scanner.Kind == XPathScanner.LexKind.Eof || this.scanner.Kind == XPathScanner.LexKind.Union) {
                    return opnd;
                } 
                break;
            case XPathScanner.LexKind.SlashSlash : 
                NextLex(); 
                opnd = new Axis(Axis.AxisType.DescendantOrSelf, new Root());
                break; 
            case XPathScanner.LexKind.Name :
                if (this.scanner.CanBeFunction) {
                    opnd = ParseIdKeyPattern(qyInput);
                    if (opnd != null) { 
                        switch (this.scanner.Kind) {
                        case XPathScanner.LexKind.Slash : 
                            NextLex(); 
                            break;
                        case XPathScanner.LexKind.SlashSlash : 
                            NextLex();
                            opnd = new Axis(Axis.AxisType.DescendantOrSelf, opnd);
                            break;
                        default : 
                            return opnd;
                        } 
                    } 
                }
                break; 
            }
            return ParseRelativePathPattern(opnd);
        }
 
        //>> IdKeyPattern ::= 'id' '(' Literal ')' | 'key' '(' Literal ',' Literal ')'
        private AstNode ParseIdKeyPattern(AstNode qyInput) { 
            Debug.Assert(this.scanner.CanBeFunction); 
            ArrayList argList = new ArrayList();
            if (this.scanner.Prefix.Length == 0) { 
                if (this.scanner.Name == "id") {
                    ParamInfo pi = (ParamInfo) functionTable["id"];
                    NextLex();;
                    PassToken(XPathScanner.LexKind.LParens); 
                    CheckToken(XPathScanner.LexKind.String);
                    argList.Add(new Operand(this.scanner.StringValue)); 
                    NextLex(); 
                    PassToken(XPathScanner.LexKind.RParens);
 					return new Function(pi.FType, argList); 
                }
                if (this.scanner.Name == "key") {
                    NextLex();
                    PassToken(XPathScanner.LexKind.LParens); 
                    CheckToken(XPathScanner.LexKind.String);
                    argList.Add(new Operand(this.scanner.StringValue)); 
                    NextLex(); 
                    PassToken(XPathScanner.LexKind.Comma);
                    CheckToken(XPathScanner.LexKind.String); 
                    argList.Add(new Operand(this.scanner.StringValue));
                    NextLex();
                    PassToken(XPathScanner.LexKind.RParens);
					return new Function("", "key", argList); 
                }
            } 
            return null; 
        }
 
        //>> PathOp   ::= '/' | '//'
        //>> RelativePathPattern ::= ( RelativePathPattern PathOp )? StepPattern
        private AstNode ParseRelativePathPattern(AstNode qyInput) {
            AstNode  opnd = ParseStepPattern(qyInput); 
            if (XPathScanner.LexKind.SlashSlash == this.scanner.Kind) {
                NextLex(); 
                opnd = ParseRelativePathPattern(new Axis(Axis.AxisType.DescendantOrSelf, opnd)); 
            }
 			else if (XPathScanner.LexKind.Slash == this.scanner.Kind) { 
                NextLex();
                opnd = ParseRelativePathPattern(opnd);
            }
            return opnd; 
        }
 
        //>> StepPattern    ::=    ChildOrAttributeAxisSpecifier NodeTest Predicate* 
        //>> ChildOrAttributeAxisSpecifier    ::=    @ ? | ('child' | 'attribute') '::'
        private AstNode ParseStepPattern(AstNode qyInput) { 
            AstNode  opnd;
            Axis.AxisType axisType = Axis.AxisType.Child;
            switch (this.scanner.Kind) {
            case XPathScanner.LexKind.At:                               //>> '@' 
                axisType = Axis.AxisType.Attribute;
                NextLex(); 
                break; 
            case XPathScanner.LexKind.Axe:                              //>> AxisName '::'
                axisType = GetAxis(this.scanner); 
                if (axisType != Axis.AxisType.Child && axisType != Axis.AxisType.Attribute) {
                    throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
                }
                NextLex(); 
                break;
            } 
            XPathNodeType nodeType = ( 
                axisType == Axis.AxisType.Attribute ? XPathNodeType.Attribute :
                /* default: */                        XPathNodeType.Element 
            );

            opnd = ParseNodeTest(qyInput, axisType, nodeType);
 
            while (XPathScanner.LexKind.LBracket == this.scanner.Kind) {
                opnd = new Filter(opnd, ParsePredicate(opnd)); 
            } 
            return opnd;
        } 

        // --------------- Helper methods ----------------------

        void CheckToken(XPathScanner.LexKind t) { 
            if (this.scanner.Kind != t) {
                throw XPathException.Create(Res.Xp_InvalidToken, this.scanner.SourceText); 
            } 
        }
 
        void PassToken(XPathScanner.LexKind t) {
            CheckToken(t);
            NextLex();
        } 

        void NextLex() { 
            this.scanner.NextLex(); 
        }
 
        private bool TestOp(string op) {
            return (
                this.scanner.Kind == XPathScanner.LexKind.Name &&
                this.scanner.Prefix.Length == 0 && 
                this.scanner.Name.Equals(op)
            ); 
        } 

        void CheckNodeSet(XPathResultType t) { 
            if (t != XPathResultType.NodeSet && t != XPathResultType.Any) {
                throw XPathException.Create(Res.Xp_NodeSetExpected, this.scanner.SourceText);
            }
        } 

        // --------------------------------------------------------------- 
        static readonly XPathResultType[] temparray1 = {}; 
        static readonly XPathResultType[] temparray2 = {XPathResultType.NodeSet};
        static readonly XPathResultType[] temparray3 = {XPathResultType.Any}; 
        static readonly XPathResultType[] temparray4 = {XPathResultType.String};
        static readonly XPathResultType[] temparray5 = {XPathResultType.String, XPathResultType.String};
        static readonly XPathResultType[] temparray6 = {XPathResultType.String, XPathResultType.Number, XPathResultType.Number};
        static readonly XPathResultType[] temparray7 = {XPathResultType.String, XPathResultType.String, XPathResultType.String}; 
        static readonly XPathResultType[] temparray8 = {XPathResultType.Boolean};
        static readonly XPathResultType[] temparray9 = {XPathResultType.Number}; 
 
        private class ParamInfo {
            private Function.FunctionType ftype; 
            private int                   minargs;
            private int                   maxargs;
            private XPathResultType[]     argTypes;
 
            public Function.FunctionType FType    { get { return this.ftype;    } }
            public int                   Minargs  { get { return this.minargs;  } } 
            public int                   Maxargs  { get { return this.maxargs;  } } 
            public XPathResultType[]     ArgTypes { get { return this.argTypes; } }
 
            internal ParamInfo(Function.FunctionType ftype, int minargs,  int maxargs, XPathResultType[] argTypes) {
                this.ftype   = ftype;
                this.minargs = minargs;
                this.maxargs = maxargs; 
                this.argTypes = argTypes;
            } 
        } //ParamInfo 

        private static Hashtable functionTable = CreateFunctionTable(); 
        private static Hashtable CreateFunctionTable(){
            Hashtable table = new Hashtable(36);
            table.Add("last"               , new ParamInfo(Function.FunctionType.FuncLast           , 0, 0, temparray1));
            table.Add("position"           , new ParamInfo(Function.FunctionType.FuncPosition       , 0, 0, temparray1)); 
            table.Add("name"               , new ParamInfo(Function.FunctionType.FuncName           , 0, 1, temparray2));
            table.Add("namespace-uri"      , new ParamInfo(Function.FunctionType.FuncNameSpaceUri   , 0, 1, temparray2)); 
            table.Add("local-name"         , new ParamInfo(Function.FunctionType.FuncLocalName      , 0, 1, temparray2)); 
            table.Add("count"              , new ParamInfo(Function.FunctionType.FuncCount          , 1, 1, temparray2));
            table.Add("id"                 , new ParamInfo(Function.FunctionType.FuncID             , 1, 1, temparray3)); 
            table.Add("string"             , new ParamInfo(Function.FunctionType.FuncString         , 0, 1, temparray3));
            table.Add("concat"             , new ParamInfo(Function.FunctionType.FuncConcat         , 2, 100, temparray4));
            table.Add("starts-with"        , new ParamInfo(Function.FunctionType.FuncStartsWith     , 2, 2, temparray5));
            table.Add("contains"           , new ParamInfo(Function.FunctionType.FuncContains       , 2, 2, temparray5)); 
            table.Add("substring-before"   , new ParamInfo(Function.FunctionType.FuncSubstringBefore, 2, 2, temparray5));
            table.Add("substring-after"    , new ParamInfo(Function.FunctionType.FuncSubstringAfter , 2, 2, temparray5)); 
            table.Add("substring"          , new ParamInfo(Function.FunctionType.FuncSubstring      , 2, 3, temparray6)); 
            table.Add("string-length"      , new ParamInfo(Function.FunctionType.FuncStringLength   , 0, 1, temparray4));
            table.Add("normalize-space"    , new ParamInfo(Function.FunctionType.FuncNormalize      , 0, 1, temparray4)); 
            table.Add("translate"          , new ParamInfo(Function.FunctionType.FuncTranslate      , 3, 3, temparray7));
            table.Add("boolean"            , new ParamInfo(Function.FunctionType.FuncBoolean        , 1, 1, temparray3));
            table.Add("not"                , new ParamInfo(Function.FunctionType.FuncNot            , 1, 1, temparray8));
            table.Add("true"               , new ParamInfo(Function.FunctionType.FuncTrue           , 0, 0 ,temparray8)); 
            table.Add("false"              , new ParamInfo(Function.FunctionType.FuncFalse          , 0, 0, temparray8));
            table.Add("lang"               , new ParamInfo(Function.FunctionType.FuncLang           , 1, 1, temparray4)); 
            table.Add("number"             , new ParamInfo(Function.FunctionType.FuncNumber         , 0, 1, temparray3)); 
            table.Add("sum"                , new ParamInfo(Function.FunctionType.FuncSum            , 1, 1, temparray2));
            table.Add("floor"              , new ParamInfo(Function.FunctionType.FuncFloor          , 1, 1, temparray9)); 
            table.Add("ceiling"            , new ParamInfo(Function.FunctionType.FuncCeiling        , 1, 1, temparray9));
            table.Add("round"              , new ParamInfo(Function.FunctionType.FuncRound          , 1, 1, temparray9));
            return table;
        } 

        private static Hashtable AxesTable = CreateAxesTable(); 
        private static Hashtable CreateAxesTable() { 
            Hashtable table = new Hashtable(13);
            table.Add("ancestor"          , Axis.AxisType.Ancestor         ); 
            table.Add("ancestor-or-self"  , Axis.AxisType.AncestorOrSelf   );
            table.Add("attribute"         , Axis.AxisType.Attribute        );
            table.Add("child"             , Axis.AxisType.Child            );
            table.Add("descendant"        , Axis.AxisType.Descendant       ); 
            table.Add("descendant-or-self", Axis.AxisType.DescendantOrSelf );
            table.Add("following"         , Axis.AxisType.Following        ); 
            table.Add("following-sibling" , Axis.AxisType.FollowingSibling ); 
            table.Add("namespace"         , Axis.AxisType.Namespace        );
            table.Add("parent"            , Axis.AxisType.Parent           ); 
            table.Add("preceding"         , Axis.AxisType.Preceding        );
            table.Add("preceding-sibling" , Axis.AxisType.PrecedingSibling );
            table.Add("self"              , Axis.AxisType.Self             );
            return table; 
        }
 
        private Axis.AxisType GetAxis(XPathScanner scaner) { 
            Debug.Assert(scaner.Kind == XPathScanner.LexKind.Axe);
            object axis = AxesTable[scaner.Name]; 
            if (axis == null) {
                throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
            }
            return (Axis.AxisType) axis; 
        }
 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace MS.Internal.Xml.XPath { 
    using System;
    using System.Xml; 
    using System.Xml.XPath;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.InteropServices; 
    using System.Collections;
 
    internal class XPathParser { 
        XPathScanner scanner;
 
 		private XPathParser(XPathScanner scanner) {
            this.scanner = scanner;
		}
 
		public static AstNode ParseXPathExpresion(string xpathExpresion) {
			XPathScanner scanner = new XPathScanner(xpathExpresion); 
 			XPathParser  parser  = new XPathParser(scanner); 
            AstNode result = parser.ParseExpresion(null);
            if (scanner.Kind != XPathScanner.LexKind.Eof) { 
                throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
            }
			return result;
 		} 

 		public static AstNode ParseXPathPattern(string xpathPattern) { 
			XPathScanner scanner = new XPathScanner(xpathPattern); 
 			XPathParser  parser  = new XPathParser(scanner);
            AstNode result = parser.ParsePattern(null); 
            if (scanner.Kind != XPathScanner.LexKind.Eof) {
                throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
            }
			return result; 
		}
 
        // --------------- Expresion Parsing ---------------------- 

        private AstNode ParseExpresion(AstNode qyInput) { 
            return ParseOrExpr(qyInput);
        }

        //>> OrExpr ::= ( OrExpr 'or' )? AndExpr 
        private AstNode ParseOrExpr(AstNode qyInput) {
            AstNode opnd = ParseAndExpr(qyInput); 
 
            do {
                if (! TestOp("or")) { 
                    return opnd;
                }
                NextLex();
                opnd = new Operator(Operator.Op.OR, opnd, ParseAndExpr(qyInput)); 
            }while (true);
        } 
 
        //>> AndExpr ::= ( AndExpr 'and' )? EqualityExpr
        private AstNode ParseAndExpr(AstNode qyInput) { 
            AstNode opnd = ParseEqualityExpr(qyInput);

            do {
                if (! TestOp("and")) { 
                    return opnd;
                } 
                NextLex(); 
                opnd = new Operator(Operator.Op.AND, opnd, ParseEqualityExpr(qyInput));
            }while (true); 
        }

        //>> EqualityOp ::= '=' | '!='
        //>> EqualityExpr    ::= ( EqualityExpr EqualityOp )? RelationalExpr 
        private AstNode ParseEqualityExpr(AstNode  qyInput) {
            AstNode opnd = ParseRelationalExpr(qyInput); 
 
            do {
                Operator.Op op = ( 
                    this.scanner.Kind == XPathScanner.LexKind.Eq ? Operator.Op.EQ :
                    this.scanner.Kind == XPathScanner.LexKind.Ne ? Operator.Op.NE :
                    /*default :*/                                  Operator.Op.INVALID
                ); 
                if (op == Operator.Op.INVALID) {
                    return opnd; 
                } 
                NextLex();
                opnd = new Operator(op, opnd, ParseRelationalExpr(qyInput)); 
            }while (true);
        }

        //>> RelationalOp ::= '<' | '>' | '<=' | '>=' 
        //>> RelationalExpr    ::= ( RelationalExpr RelationalOp )? AdditiveExpr
        private AstNode ParseRelationalExpr(AstNode  qyInput) { 
            AstNode  opnd = ParseAdditiveExpr(qyInput); 

            do { 
                Operator.Op op = (
                    this.scanner.Kind == XPathScanner.LexKind.Lt ? Operator.Op.LT :
                    this.scanner.Kind == XPathScanner.LexKind.Le ? Operator.Op.LE :
                    this.scanner.Kind == XPathScanner.LexKind.Gt ? Operator.Op.GT : 
                    this.scanner.Kind == XPathScanner.LexKind.Ge ? Operator.Op.GE :
                    /*default :*/                                  Operator.Op.INVALID 
                ); 
                if (op == Operator.Op.INVALID) {
                    return opnd; 
                }
                NextLex();
                opnd = new Operator(op, opnd, ParseAdditiveExpr(qyInput));
            }while (true); 
        }
 
        //>> AdditiveOp   ::= '+' | '-' 
        //>> AdditiveExpr ::= ( AdditiveExpr AdditiveOp )? MultiplicativeExpr
        private AstNode ParseAdditiveExpr(AstNode  qyInput) { 
            AstNode  opnd = ParseMultiplicativeExpr(qyInput);

            do {
                Operator.Op op = ( 
                    this.scanner.Kind == XPathScanner.LexKind.Plus  ? Operator.Op.PLUS  :
                    this.scanner.Kind == XPathScanner.LexKind.Minus ? Operator.Op.MINUS : 
                    /*default :*/                                     Operator.Op.INVALID 
                );
                if (op == Operator.Op.INVALID) { 
                    return opnd;
                }
                NextLex();
                opnd = new Operator(op, opnd, ParseMultiplicativeExpr(qyInput)); 
            }while (true);
        } 
 
        //>> MultiplicativeOp   ::= '*' | 'div' | 'mod'
        //>> MultiplicativeExpr ::= ( MultiplicativeExpr MultiplicativeOp )? UnaryExpr 
        private AstNode ParseMultiplicativeExpr(AstNode  qyInput) {
            AstNode  opnd = ParseUnaryExpr(qyInput);

            do { 
                Operator.Op op = (
                    this.scanner.Kind == XPathScanner.LexKind.Star  ? Operator.Op.MUL : 
                    TestOp("div")                                   ? Operator.Op.DIV : 
                    TestOp("mod")                                   ? Operator.Op.MOD :
                    /*default :*/                                     Operator.Op.INVALID 
                );
                if (op == Operator.Op.INVALID) {
                    return opnd;
                } 
                NextLex();
                opnd = new Operator(op, opnd, ParseUnaryExpr(qyInput)); 
            }while (true); 
        }
 
        //>> UnaryExpr    ::= UnionExpr | '-' UnaryExpr
        private AstNode ParseUnaryExpr(AstNode  qyInput) {
            if (this.scanner.Kind == XPathScanner.LexKind.Minus) {
                NextLex(); 
                return new Operator(Operator.Op.MUL, ParseUnaryExpr(qyInput), new Operand(-1));
            } 
            else { 
                return ParseUnionExpr(qyInput);
            } 
        }

        //>> UnionExpr ::= ( UnionExpr '|' )? PathExpr
        private AstNode ParseUnionExpr(AstNode  qyInput) { 
            AstNode opnd = ParsePathExpr(qyInput);
 
            do { 
                if (this.scanner.Kind != XPathScanner.LexKind.Union) {
                    return opnd; 
                }
                NextLex();
                AstNode opnd2 = ParsePathExpr(qyInput);
                CheckNodeSet(opnd.ReturnType); 
                CheckNodeSet(opnd2.ReturnType);
                opnd = new Operator(Operator.Op.UNION, opnd, opnd2); 
            }while (true); 
        }
 
        private static bool IsNodeType(XPathScanner scaner) {
            return (
                scaner.Prefix.Length == 0 && (
                    scaner.Name == "node"                   || 
                    scaner.Name == "text"                   ||
                    scaner.Name == "processing-instruction" || 
                    scaner.Name == "comment" 
                )
            ); 
        }

        //>> PathOp   ::= '/' | '//'
        //>> PathExpr ::= LocationPath | 
        //>>              FilterExpr ( PathOp  RelativeLocationPath )?
        private AstNode ParsePathExpr(AstNode qyInput) { 
			AstNode opnd; 
 			if (IsPrimaryExpr(this.scanner)) { // in this moment we shoud distinct LocationPas vs FilterExpr (which starts from is PrimaryExpr)
			    opnd = ParseFilterExpr(qyInput); 
 				if (this.scanner.Kind == XPathScanner.LexKind.Slash) {
 					NextLex();
					opnd = ParseRelativeLocationPath(opnd);
 				} 
				else if (this.scanner.Kind == XPathScanner.LexKind.SlashSlash) {
					NextLex(); 
					opnd = ParseRelativeLocationPath(new Axis(Axis.AxisType.DescendantOrSelf, opnd)); 
 				}
			} 
 			else {
                opnd = ParseLocationPath(null);
            }
 
            return opnd;
        } 
 
        //>> FilterExpr ::= PrimaryExpr | FilterExpr Predicate
        private AstNode ParseFilterExpr(AstNode  qyInput) { 
            AstNode  opnd = ParsePrimaryExpr(qyInput);
            while (this.scanner.Kind == XPathScanner.LexKind.LBracket) {
                // opnd must be a query
                opnd = new Filter(opnd, ParsePredicate(opnd)); 
            }
            return opnd; 
        } 

        //>> Predicate ::= '[' Expr ']' 
        private AstNode ParsePredicate(AstNode  qyInput) {
            AstNode  opnd;

            // we have predicates. Check that input type is NodeSet 
            CheckNodeSet(qyInput.ReturnType);
 
            PassToken(XPathScanner.LexKind.LBracket); 
            opnd = ParseExpresion(qyInput);
            PassToken(XPathScanner.LexKind.RBracket); 

            return opnd;
        }
 
        //>> LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
        private AstNode ParseLocationPath(AstNode qyInput) { 
            if (this.scanner.Kind == XPathScanner.LexKind.Slash) { 
                NextLex();
                AstNode opnd = new Root(); 

                if (IsStep(this.scanner.Kind)) {
                    opnd = ParseRelativeLocationPath(opnd);
                } 
                return opnd;
            } 
            else if (this.scanner.Kind == XPathScanner.LexKind.SlashSlash) { 
                NextLex();
                return ParseRelativeLocationPath(new Axis(Axis.AxisType.DescendantOrSelf, new Root())); 
            }
            else {
                return  ParseRelativeLocationPath(qyInput);
            } 
        } // ParseLocationPath
 
        //>> PathOp   ::= '/' | '//' 
        //>> RelativeLocationPath ::= ( RelativeLocationPath PathOp )? Step
        private AstNode ParseRelativeLocationPath(AstNode  qyInput) { 
            AstNode  opnd = ParseStep(qyInput);
            if (XPathScanner.LexKind.SlashSlash == this.scanner.Kind) {
                NextLex();
                opnd = ParseRelativeLocationPath(new Axis(Axis.AxisType.DescendantOrSelf,opnd)); 
            }
 			else if (XPathScanner.LexKind.Slash == this.scanner.Kind) { 
                NextLex(); 
                opnd = ParseRelativeLocationPath(opnd);
            } 
            return opnd;
        }

		private static bool IsStep(XPathScanner.LexKind lexKind) { 
 			return (
				lexKind == XPathScanner.LexKind.Dot    || 
				lexKind == XPathScanner.LexKind.DotDot || 
				lexKind == XPathScanner.LexKind.At     ||
 				lexKind == XPathScanner.LexKind.Axe    || 
				lexKind == XPathScanner.LexKind.Star   ||
 				lexKind == XPathScanner.LexKind.Name          // NodeTest is also Name
 			);
		} 

        //>> Step ::= '.' | '..' | ( AxisName '::' | '@' )? NodeTest Predicate* 
        private AstNode ParseStep(AstNode  qyInput) { 
            AstNode  opnd;
            if (XPathScanner.LexKind.Dot == this.scanner.Kind) {         //>> '.' 
                NextLex();
                opnd = new Axis(Axis.AxisType.Self, qyInput);
            }
            else if (XPathScanner.LexKind.DotDot == this.scanner.Kind) { //>> '..' 
                NextLex();
                opnd = new Axis(Axis.AxisType.Parent, qyInput); 
            } 
            else {                                                          //>> ( AxisName '::' | '@' )? NodeTest Predicate*
                Axis.AxisType axisType = Axis.AxisType.Child; 
                switch (this.scanner.Kind) {
                case XPathScanner.LexKind.At:                               //>> '@'
                    axisType = Axis.AxisType.Attribute;
                    NextLex(); 
                    break;
                case XPathScanner.LexKind.Axe:                              //>> AxisName '::' 
                    axisType = GetAxis(this.scanner); 
                    NextLex();
                    break; 
                }
                XPathNodeType nodeType = (
                    axisType == Axis.AxisType.Attribute ? XPathNodeType.Attribute :
//                    axisType == Axis.AxisType.Namespace ? XPathNodeType.Namespace : // No Idea why it's this way but othervise Axes doesn't work 
                    /* default: */                        XPathNodeType.Element
                ); 
 
                opnd = ParseNodeTest(qyInput, axisType, nodeType);
 
                while (XPathScanner.LexKind.LBracket == this.scanner.Kind) {
                    opnd = new Filter(opnd, ParsePredicate(opnd));
                }
            } 
            return opnd;
        } 
 
        //>> NodeTest ::= NameTest | 'comment ()' | 'text ()' | 'node ()' | 'processing-instruction ('  Literal ? ')'
        private AstNode ParseNodeTest(AstNode qyInput, Axis.AxisType axisType, XPathNodeType nodeType) { 
            string        nodeName, nodePrefix;

            switch (this.scanner.Kind) {
            case XPathScanner.LexKind.Name : 
                if (this.scanner.CanBeFunction && IsNodeType(this.scanner)) {
                    nodePrefix = string.Empty; 
                    nodeName   = string.Empty; 
                    nodeType = (
                        this.scanner.Name == "comment"                ? XPathNodeType.Comment : 
                        this.scanner.Name == "text"                   ? XPathNodeType.Text :
                        this.scanner.Name == "node"                   ? XPathNodeType.All :
                        this.scanner.Name == "processing-instruction" ? XPathNodeType.ProcessingInstruction :
                        /* default: */ XPathNodeType.Root 
                    );
                    Debug.Assert(nodeType != XPathNodeType.Root); 
                    NextLex(); 

                    PassToken(XPathScanner.LexKind.LParens); 

                    if (nodeType == XPathNodeType.ProcessingInstruction) {
                        if (this.scanner.Kind != XPathScanner.LexKind.RParens) { //>> 'processing-instruction (' Literal ')'
                            CheckToken(XPathScanner.LexKind.String); 
                            nodeName = this.scanner.StringValue;
                            NextLex(); 
                        } 
                    }
 
                    PassToken(XPathScanner.LexKind.RParens);
                }
                else {
                    nodePrefix = this.scanner.Prefix; 
                    nodeName   = this.scanner.Name;
 	                NextLex(); 
                    if (nodeName == "*") { 
                        nodeName = string.Empty;
                    } 
                }
                break;
            case XPathScanner.LexKind.Star :
                nodePrefix = string.Empty; 
                nodeName   = string.Empty;
                NextLex(); 
                break; 
            default :
	            throw XPathException.Create(Res.Xp_NodeSetExpected, this.scanner.SourceText); 
            }
            return new Axis(axisType, qyInput, nodePrefix, nodeName, nodeType);
        }
 
		private static bool IsPrimaryExpr(XPathScanner scanner) {
			return ( 
 				scanner.Kind == XPathScanner.LexKind.String  || 
				scanner.Kind == XPathScanner.LexKind.Number  ||
 				scanner.Kind == XPathScanner.LexKind.Dollar  || 
 				scanner.Kind == XPathScanner.LexKind.LParens ||
				scanner.Kind == XPathScanner.LexKind.Name && scanner.CanBeFunction && ! IsNodeType(scanner)
 			);
		} 

        //>> PrimaryExpr ::= Literal | Number | VariableReference | '(' Expr ')' | FunctionCall 
        private AstNode ParsePrimaryExpr(AstNode  qyInput) { 
            Debug.Assert(IsPrimaryExpr(this.scanner));
            AstNode  opnd = null; 
            switch (this.scanner.Kind) {
            case XPathScanner.LexKind.String:
                opnd = new Operand(this.scanner.StringValue);
                NextLex(); 
                break;
            case XPathScanner.LexKind.Number: 
                opnd = new Operand(this.scanner.NumberValue); 
                NextLex();
                break; 
            case XPathScanner.LexKind.Dollar:
                NextLex();
                CheckToken(XPathScanner.LexKind.Name);
                opnd = new Variable(this.scanner.Name, this.scanner.Prefix); 
                NextLex();
                break; 
            case XPathScanner.LexKind.LParens: 
                NextLex();
                opnd = ParseExpresion(qyInput); 
                if (opnd.Type != AstNode.AstType.ConstantOperand) {
                    opnd = new Group(opnd);
                }
                PassToken(XPathScanner.LexKind.RParens); 
                break;
            case XPathScanner.LexKind.Name : 
                if (this.scanner.CanBeFunction && ! IsNodeType(this.scanner)) { 
                    opnd = ParseMethod(null);
                } 
                break;
            }
			Debug.Assert(opnd != null, "IsPrimaryExpr() was true. We should recognize this lex.");
            return opnd; 
        }
 
        private AstNode ParseMethod(AstNode qyInput) { 
            ArrayList argList = new ArrayList();
            string name   = this.scanner.Name; 
            string prefix = this.scanner.Prefix;
            PassToken(XPathScanner.LexKind.Name);
            PassToken(XPathScanner.LexKind.LParens);
            if (this.scanner.Kind != XPathScanner.LexKind.RParens) { 
                do {
                    argList.Add(ParseExpresion(qyInput)); 
                    if (this.scanner.Kind == XPathScanner.LexKind.RParens) { 
                        break;
                    } 
                    PassToken(XPathScanner.LexKind.Comma);
                }while (true);
            }
            PassToken(XPathScanner.LexKind.RParens); 
            if (prefix.Length == 0) {
                ParamInfo pi = (ParamInfo) functionTable[name]; 
                if (pi != null) { 
                    int argCount = argList.Count;
                    if (argCount < pi.Minargs) { 
                        throw XPathException.Create(Res.Xp_InvalidNumArgs, name, this.scanner.SourceText);
                    }
                    if (pi.FType == Function.FunctionType.FuncConcat) {
                        for (int i = 0; i < argCount; i ++) { 
                            AstNode arg = (AstNode)argList[i];
                            if (arg.ReturnType != XPathResultType.String) { 
                                arg = new Function(Function.FunctionType.FuncString, arg); 
                            }
                            argList[i] = arg; 
                        }
                    }
                    else {
                        if (pi.Maxargs < argCount) { 
                            throw XPathException.Create(Res.Xp_InvalidNumArgs, name, this.scanner.SourceText);
                        } 
                        if (pi.ArgTypes.Length < argCount) { 
                            argCount = pi.ArgTypes.Length;    // argument we have the type specified (can be < pi.Minargs)
                        } 
                        for (int i = 0; i < argCount; i ++) {
                            AstNode arg = (AstNode)argList[i];
                            if (
                                pi.ArgTypes[i] != XPathResultType.Any && 
                                pi.ArgTypes[i] != arg.ReturnType
                            ) { 
                                switch (pi.ArgTypes[i]) { 
                                case  XPathResultType.NodeSet :
                                    if (!(arg is Variable) && !(arg is Function && arg.ReturnType == XPathResultType.Any) ) { 
                                        throw XPathException.Create(Res.Xp_InvalidArgumentType, name, this.scanner.SourceText);
                                    }
                                    break;
                                case  XPathResultType.String : 
                                    arg = new Function(Function.FunctionType.FuncString, arg);
                                    break; 
                                case  XPathResultType.Number : 
                                    arg = new Function(Function.FunctionType.FuncNumber, arg);
                                    break; 
                                case  XPathResultType.Boolean :
                                    arg = new Function(Function.FunctionType.FuncBoolean, arg);
                                    break;
                                } 
                                argList[i] = arg;
                            } 
                        } 
                    }
					return new Function(pi.FType, argList); 
                }
            }
            return new Function(prefix, name, argList);
        } 

        // --------------- Pattern Parsing ---------------------- 
 
        //>> Pattern ::= ( Pattern '|' )? LocationPathPattern
        private AstNode ParsePattern(AstNode  qyInput) { 
            AstNode opnd = ParseLocationPathPattern(qyInput);

            do {
                if (this.scanner.Kind != XPathScanner.LexKind.Union) { 
                    return opnd;
                } 
                NextLex(); 
                opnd = new Operator(Operator.Op.UNION, opnd, ParseLocationPathPattern(qyInput));
            }while (true); 
        }

        //>> LocationPathPattern ::= '/' | RelativePathPattern | '//' RelativePathPattern  |  '/' RelativePathPattern
        //>>                       | IdKeyPattern (('/' | '//') RelativePathPattern)? 
        private AstNode ParseLocationPathPattern(AstNode qyInput) {
            AstNode opnd = null; 
            switch (this.scanner.Kind) { 
            case XPathScanner.LexKind.Slash :
                NextLex(); 
                opnd = new Root();
                if (this.scanner.Kind == XPathScanner.LexKind.Eof || this.scanner.Kind == XPathScanner.LexKind.Union) {
                    return opnd;
                } 
                break;
            case XPathScanner.LexKind.SlashSlash : 
                NextLex(); 
                opnd = new Axis(Axis.AxisType.DescendantOrSelf, new Root());
                break; 
            case XPathScanner.LexKind.Name :
                if (this.scanner.CanBeFunction) {
                    opnd = ParseIdKeyPattern(qyInput);
                    if (opnd != null) { 
                        switch (this.scanner.Kind) {
                        case XPathScanner.LexKind.Slash : 
                            NextLex(); 
                            break;
                        case XPathScanner.LexKind.SlashSlash : 
                            NextLex();
                            opnd = new Axis(Axis.AxisType.DescendantOrSelf, opnd);
                            break;
                        default : 
                            return opnd;
                        } 
                    } 
                }
                break; 
            }
            return ParseRelativePathPattern(opnd);
        }
 
        //>> IdKeyPattern ::= 'id' '(' Literal ')' | 'key' '(' Literal ',' Literal ')'
        private AstNode ParseIdKeyPattern(AstNode qyInput) { 
            Debug.Assert(this.scanner.CanBeFunction); 
            ArrayList argList = new ArrayList();
            if (this.scanner.Prefix.Length == 0) { 
                if (this.scanner.Name == "id") {
                    ParamInfo pi = (ParamInfo) functionTable["id"];
                    NextLex();;
                    PassToken(XPathScanner.LexKind.LParens); 
                    CheckToken(XPathScanner.LexKind.String);
                    argList.Add(new Operand(this.scanner.StringValue)); 
                    NextLex(); 
                    PassToken(XPathScanner.LexKind.RParens);
 					return new Function(pi.FType, argList); 
                }
                if (this.scanner.Name == "key") {
                    NextLex();
                    PassToken(XPathScanner.LexKind.LParens); 
                    CheckToken(XPathScanner.LexKind.String);
                    argList.Add(new Operand(this.scanner.StringValue)); 
                    NextLex(); 
                    PassToken(XPathScanner.LexKind.Comma);
                    CheckToken(XPathScanner.LexKind.String); 
                    argList.Add(new Operand(this.scanner.StringValue));
                    NextLex();
                    PassToken(XPathScanner.LexKind.RParens);
					return new Function("", "key", argList); 
                }
            } 
            return null; 
        }
 
        //>> PathOp   ::= '/' | '//'
        //>> RelativePathPattern ::= ( RelativePathPattern PathOp )? StepPattern
        private AstNode ParseRelativePathPattern(AstNode qyInput) {
            AstNode  opnd = ParseStepPattern(qyInput); 
            if (XPathScanner.LexKind.SlashSlash == this.scanner.Kind) {
                NextLex(); 
                opnd = ParseRelativePathPattern(new Axis(Axis.AxisType.DescendantOrSelf, opnd)); 
            }
 			else if (XPathScanner.LexKind.Slash == this.scanner.Kind) { 
                NextLex();
                opnd = ParseRelativePathPattern(opnd);
            }
            return opnd; 
        }
 
        //>> StepPattern    ::=    ChildOrAttributeAxisSpecifier NodeTest Predicate* 
        //>> ChildOrAttributeAxisSpecifier    ::=    @ ? | ('child' | 'attribute') '::'
        private AstNode ParseStepPattern(AstNode qyInput) { 
            AstNode  opnd;
            Axis.AxisType axisType = Axis.AxisType.Child;
            switch (this.scanner.Kind) {
            case XPathScanner.LexKind.At:                               //>> '@' 
                axisType = Axis.AxisType.Attribute;
                NextLex(); 
                break; 
            case XPathScanner.LexKind.Axe:                              //>> AxisName '::'
                axisType = GetAxis(this.scanner); 
                if (axisType != Axis.AxisType.Child && axisType != Axis.AxisType.Attribute) {
                    throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
                }
                NextLex(); 
                break;
            } 
            XPathNodeType nodeType = ( 
                axisType == Axis.AxisType.Attribute ? XPathNodeType.Attribute :
                /* default: */                        XPathNodeType.Element 
            );

            opnd = ParseNodeTest(qyInput, axisType, nodeType);
 
            while (XPathScanner.LexKind.LBracket == this.scanner.Kind) {
                opnd = new Filter(opnd, ParsePredicate(opnd)); 
            } 
            return opnd;
        } 

        // --------------- Helper methods ----------------------

        void CheckToken(XPathScanner.LexKind t) { 
            if (this.scanner.Kind != t) {
                throw XPathException.Create(Res.Xp_InvalidToken, this.scanner.SourceText); 
            } 
        }
 
        void PassToken(XPathScanner.LexKind t) {
            CheckToken(t);
            NextLex();
        } 

        void NextLex() { 
            this.scanner.NextLex(); 
        }
 
        private bool TestOp(string op) {
            return (
                this.scanner.Kind == XPathScanner.LexKind.Name &&
                this.scanner.Prefix.Length == 0 && 
                this.scanner.Name.Equals(op)
            ); 
        } 

        void CheckNodeSet(XPathResultType t) { 
            if (t != XPathResultType.NodeSet && t != XPathResultType.Any) {
                throw XPathException.Create(Res.Xp_NodeSetExpected, this.scanner.SourceText);
            }
        } 

        // --------------------------------------------------------------- 
        static readonly XPathResultType[] temparray1 = {}; 
        static readonly XPathResultType[] temparray2 = {XPathResultType.NodeSet};
        static readonly XPathResultType[] temparray3 = {XPathResultType.Any}; 
        static readonly XPathResultType[] temparray4 = {XPathResultType.String};
        static readonly XPathResultType[] temparray5 = {XPathResultType.String, XPathResultType.String};
        static readonly XPathResultType[] temparray6 = {XPathResultType.String, XPathResultType.Number, XPathResultType.Number};
        static readonly XPathResultType[] temparray7 = {XPathResultType.String, XPathResultType.String, XPathResultType.String}; 
        static readonly XPathResultType[] temparray8 = {XPathResultType.Boolean};
        static readonly XPathResultType[] temparray9 = {XPathResultType.Number}; 
 
        private class ParamInfo {
            private Function.FunctionType ftype; 
            private int                   minargs;
            private int                   maxargs;
            private XPathResultType[]     argTypes;
 
            public Function.FunctionType FType    { get { return this.ftype;    } }
            public int                   Minargs  { get { return this.minargs;  } } 
            public int                   Maxargs  { get { return this.maxargs;  } } 
            public XPathResultType[]     ArgTypes { get { return this.argTypes; } }
 
            internal ParamInfo(Function.FunctionType ftype, int minargs,  int maxargs, XPathResultType[] argTypes) {
                this.ftype   = ftype;
                this.minargs = minargs;
                this.maxargs = maxargs; 
                this.argTypes = argTypes;
            } 
        } //ParamInfo 

        private static Hashtable functionTable = CreateFunctionTable(); 
        private static Hashtable CreateFunctionTable(){
            Hashtable table = new Hashtable(36);
            table.Add("last"               , new ParamInfo(Function.FunctionType.FuncLast           , 0, 0, temparray1));
            table.Add("position"           , new ParamInfo(Function.FunctionType.FuncPosition       , 0, 0, temparray1)); 
            table.Add("name"               , new ParamInfo(Function.FunctionType.FuncName           , 0, 1, temparray2));
            table.Add("namespace-uri"      , new ParamInfo(Function.FunctionType.FuncNameSpaceUri   , 0, 1, temparray2)); 
            table.Add("local-name"         , new ParamInfo(Function.FunctionType.FuncLocalName      , 0, 1, temparray2)); 
            table.Add("count"              , new ParamInfo(Function.FunctionType.FuncCount          , 1, 1, temparray2));
            table.Add("id"                 , new ParamInfo(Function.FunctionType.FuncID             , 1, 1, temparray3)); 
            table.Add("string"             , new ParamInfo(Function.FunctionType.FuncString         , 0, 1, temparray3));
            table.Add("concat"             , new ParamInfo(Function.FunctionType.FuncConcat         , 2, 100, temparray4));
            table.Add("starts-with"        , new ParamInfo(Function.FunctionType.FuncStartsWith     , 2, 2, temparray5));
            table.Add("contains"           , new ParamInfo(Function.FunctionType.FuncContains       , 2, 2, temparray5)); 
            table.Add("substring-before"   , new ParamInfo(Function.FunctionType.FuncSubstringBefore, 2, 2, temparray5));
            table.Add("substring-after"    , new ParamInfo(Function.FunctionType.FuncSubstringAfter , 2, 2, temparray5)); 
            table.Add("substring"          , new ParamInfo(Function.FunctionType.FuncSubstring      , 2, 3, temparray6)); 
            table.Add("string-length"      , new ParamInfo(Function.FunctionType.FuncStringLength   , 0, 1, temparray4));
            table.Add("normalize-space"    , new ParamInfo(Function.FunctionType.FuncNormalize      , 0, 1, temparray4)); 
            table.Add("translate"          , new ParamInfo(Function.FunctionType.FuncTranslate      , 3, 3, temparray7));
            table.Add("boolean"            , new ParamInfo(Function.FunctionType.FuncBoolean        , 1, 1, temparray3));
            table.Add("not"                , new ParamInfo(Function.FunctionType.FuncNot            , 1, 1, temparray8));
            table.Add("true"               , new ParamInfo(Function.FunctionType.FuncTrue           , 0, 0 ,temparray8)); 
            table.Add("false"              , new ParamInfo(Function.FunctionType.FuncFalse          , 0, 0, temparray8));
            table.Add("lang"               , new ParamInfo(Function.FunctionType.FuncLang           , 1, 1, temparray4)); 
            table.Add("number"             , new ParamInfo(Function.FunctionType.FuncNumber         , 0, 1, temparray3)); 
            table.Add("sum"                , new ParamInfo(Function.FunctionType.FuncSum            , 1, 1, temparray2));
            table.Add("floor"              , new ParamInfo(Function.FunctionType.FuncFloor          , 1, 1, temparray9)); 
            table.Add("ceiling"            , new ParamInfo(Function.FunctionType.FuncCeiling        , 1, 1, temparray9));
            table.Add("round"              , new ParamInfo(Function.FunctionType.FuncRound          , 1, 1, temparray9));
            return table;
        } 

        private static Hashtable AxesTable = CreateAxesTable(); 
        private static Hashtable CreateAxesTable() { 
            Hashtable table = new Hashtable(13);
            table.Add("ancestor"          , Axis.AxisType.Ancestor         ); 
            table.Add("ancestor-or-self"  , Axis.AxisType.AncestorOrSelf   );
            table.Add("attribute"         , Axis.AxisType.Attribute        );
            table.Add("child"             , Axis.AxisType.Child            );
            table.Add("descendant"        , Axis.AxisType.Descendant       ); 
            table.Add("descendant-or-self", Axis.AxisType.DescendantOrSelf );
            table.Add("following"         , Axis.AxisType.Following        ); 
            table.Add("following-sibling" , Axis.AxisType.FollowingSibling ); 
            table.Add("namespace"         , Axis.AxisType.Namespace        );
            table.Add("parent"            , Axis.AxisType.Parent           ); 
            table.Add("preceding"         , Axis.AxisType.Preceding        );
            table.Add("preceding-sibling" , Axis.AxisType.PrecedingSibling );
            table.Add("self"              , Axis.AxisType.Self             );
            return table; 
        }
 
        private Axis.AxisType GetAxis(XPathScanner scaner) { 
            Debug.Assert(scaner.Kind == XPathScanner.LexKind.Axe);
            object axis = AxesTable[scaner.Name]; 
            if (axis == null) {
                throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
            }
            return (Axis.AxisType) axis; 
        }
 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK