1 //------------------------------------------------------------------------------
2 // <copyright file="XPathParser.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
8 namespace MS.Internal.Xml.XPath {
11 using System.Xml.XPath;
12 using System.Diagnostics;
13 using System.Globalization;
14 using System.Runtime.InteropServices;
15 using System.Collections;
17 internal class XPathParser {
20 private XPathParser(XPathScanner scanner) {
21 this.scanner = scanner;
24 public static AstNode ParseXPathExpresion(string xpathExpresion) {
25 XPathScanner scanner = new XPathScanner(xpathExpresion);
26 XPathParser parser = new XPathParser(scanner);
27 AstNode result = parser.ParseExpresion(null);
28 if (scanner.Kind != XPathScanner.LexKind.Eof) {
29 throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
34 public static AstNode ParseXPathPattern(string xpathPattern) {
35 XPathScanner scanner = new XPathScanner(xpathPattern);
36 XPathParser parser = new XPathParser(scanner);
37 AstNode result = parser.ParsePattern(null);
38 if (scanner.Kind != XPathScanner.LexKind.Eof) {
39 throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
44 // --------------- Expresion Parsing ----------------------
47 //The recursive is like
48 //ParseOrExpr->ParseAndExpr->ParseEqualityExpr->ParseRelationalExpr...->ParseFilterExpr->ParsePredicate->ParseExpresion
49 //So put 200 limitation here will max cause about 2000~3000 depth stack.
50 private int parseDepth = 0;
51 private const int MaxParseDepth = 200;
53 private AstNode ParseExpresion(AstNode qyInput) {
54 if (++parseDepth > MaxParseDepth) {
55 throw XPathException.Create(Res.Xp_QueryTooComplex);
57 AstNode result = ParseOrExpr(qyInput);
62 //>> OrExpr ::= ( OrExpr 'or' )? AndExpr
63 private AstNode ParseOrExpr(AstNode qyInput) {
64 AstNode opnd = ParseAndExpr(qyInput);
71 opnd = new Operator(Operator.Op.OR, opnd, ParseAndExpr(qyInput));
75 //>> AndExpr ::= ( AndExpr 'and' )? EqualityExpr
76 private AstNode ParseAndExpr(AstNode qyInput) {
77 AstNode opnd = ParseEqualityExpr(qyInput);
80 if (! TestOp("and")) {
84 opnd = new Operator(Operator.Op.AND, opnd, ParseEqualityExpr(qyInput));
88 //>> EqualityOp ::= '=' | '!='
89 //>> EqualityExpr ::= ( EqualityExpr EqualityOp )? RelationalExpr
90 private AstNode ParseEqualityExpr(AstNode qyInput) {
91 AstNode opnd = ParseRelationalExpr(qyInput);
95 this.scanner.Kind == XPathScanner.LexKind.Eq ? Operator.Op.EQ :
96 this.scanner.Kind == XPathScanner.LexKind.Ne ? Operator.Op.NE :
97 /*default :*/ Operator.Op.INVALID
99 if (op == Operator.Op.INVALID) {
103 opnd = new Operator(op, opnd, ParseRelationalExpr(qyInput));
107 //>> RelationalOp ::= '<' | '>' | '<=' | '>='
108 //>> RelationalExpr ::= ( RelationalExpr RelationalOp )? AdditiveExpr
109 private AstNode ParseRelationalExpr(AstNode qyInput) {
110 AstNode opnd = ParseAdditiveExpr(qyInput);
114 this.scanner.Kind == XPathScanner.LexKind.Lt ? Operator.Op.LT :
115 this.scanner.Kind == XPathScanner.LexKind.Le ? Operator.Op.LE :
116 this.scanner.Kind == XPathScanner.LexKind.Gt ? Operator.Op.GT :
117 this.scanner.Kind == XPathScanner.LexKind.Ge ? Operator.Op.GE :
118 /*default :*/ Operator.Op.INVALID
120 if (op == Operator.Op.INVALID) {
124 opnd = new Operator(op, opnd, ParseAdditiveExpr(qyInput));
128 //>> AdditiveOp ::= '+' | '-'
129 //>> AdditiveExpr ::= ( AdditiveExpr AdditiveOp )? MultiplicativeExpr
130 private AstNode ParseAdditiveExpr(AstNode qyInput) {
131 AstNode opnd = ParseMultiplicativeExpr(qyInput);
135 this.scanner.Kind == XPathScanner.LexKind.Plus ? Operator.Op.PLUS :
136 this.scanner.Kind == XPathScanner.LexKind.Minus ? Operator.Op.MINUS :
137 /*default :*/ Operator.Op.INVALID
139 if (op == Operator.Op.INVALID) {
143 opnd = new Operator(op, opnd, ParseMultiplicativeExpr(qyInput));
147 //>> MultiplicativeOp ::= '*' | 'div' | 'mod'
148 //>> MultiplicativeExpr ::= ( MultiplicativeExpr MultiplicativeOp )? UnaryExpr
149 private AstNode ParseMultiplicativeExpr(AstNode qyInput) {
150 AstNode opnd = ParseUnaryExpr(qyInput);
154 this.scanner.Kind == XPathScanner.LexKind.Star ? Operator.Op.MUL :
155 TestOp("div") ? Operator.Op.DIV :
156 TestOp("mod") ? Operator.Op.MOD :
157 /*default :*/ Operator.Op.INVALID
159 if (op == Operator.Op.INVALID) {
163 opnd = new Operator(op, opnd, ParseUnaryExpr(qyInput));
167 //>> UnaryExpr ::= UnionExpr | '-' UnaryExpr
168 private AstNode ParseUnaryExpr(AstNode qyInput) {
170 while (this.scanner.Kind == XPathScanner.LexKind.Minus) {
176 return new Operator(Operator.Op.MUL, ParseUnionExpr(qyInput), new Operand(-1));
179 return ParseUnionExpr(qyInput);
183 //>> UnionExpr ::= ( UnionExpr '|' )? PathExpr
184 private AstNode ParseUnionExpr(AstNode qyInput) {
185 AstNode opnd = ParsePathExpr(qyInput);
188 if (this.scanner.Kind != XPathScanner.LexKind.Union) {
192 AstNode opnd2 = ParsePathExpr(qyInput);
193 CheckNodeSet(opnd.ReturnType);
194 CheckNodeSet(opnd2.ReturnType);
195 opnd = new Operator(Operator.Op.UNION, opnd, opnd2);
199 private static bool IsNodeType(XPathScanner scaner) {
201 scaner.Prefix.Length == 0 && (
202 scaner.Name == "node" ||
203 scaner.Name == "text" ||
204 scaner.Name == "processing-instruction" ||
205 scaner.Name == "comment"
210 //>> PathOp ::= '/' | '//'
211 //>> PathExpr ::= LocationPath |
212 //>> FilterExpr ( PathOp RelativeLocationPath )?
213 private AstNode ParsePathExpr(AstNode qyInput) {
215 if (IsPrimaryExpr(this.scanner)) { // in this moment we shoud distinct LocationPas vs FilterExpr (which starts from is PrimaryExpr)
216 opnd = ParseFilterExpr(qyInput);
217 if (this.scanner.Kind == XPathScanner.LexKind.Slash) {
219 opnd = ParseRelativeLocationPath(opnd);
221 else if (this.scanner.Kind == XPathScanner.LexKind.SlashSlash) {
223 opnd = ParseRelativeLocationPath(new Axis(Axis.AxisType.DescendantOrSelf, opnd));
227 opnd = ParseLocationPath(null);
233 //>> FilterExpr ::= PrimaryExpr | FilterExpr Predicate
234 private AstNode ParseFilterExpr(AstNode qyInput) {
235 AstNode opnd = ParsePrimaryExpr(qyInput);
236 while (this.scanner.Kind == XPathScanner.LexKind.LBracket) {
237 // opnd must be a query
238 opnd = new Filter(opnd, ParsePredicate(opnd));
243 //>> Predicate ::= '[' Expr ']'
244 private AstNode ParsePredicate(AstNode qyInput) {
247 // we have predicates. Check that input type is NodeSet
248 CheckNodeSet(qyInput.ReturnType);
250 PassToken(XPathScanner.LexKind.LBracket);
251 opnd = ParseExpresion(qyInput);
252 PassToken(XPathScanner.LexKind.RBracket);
257 //>> LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
258 private AstNode ParseLocationPath(AstNode qyInput) {
259 if (this.scanner.Kind == XPathScanner.LexKind.Slash) {
261 AstNode opnd = new Root();
263 if (IsStep(this.scanner.Kind)) {
264 opnd = ParseRelativeLocationPath(opnd);
268 else if (this.scanner.Kind == XPathScanner.LexKind.SlashSlash) {
270 return ParseRelativeLocationPath(new Axis(Axis.AxisType.DescendantOrSelf, new Root()));
273 return ParseRelativeLocationPath(qyInput);
275 } // ParseLocationPath
277 //>> PathOp ::= '/' | '//'
278 //>> RelativeLocationPath ::= ( RelativeLocationPath PathOp )? Step
279 private AstNode ParseRelativeLocationPath(AstNode qyInput) {
280 AstNode opnd = qyInput;
282 opnd = ParseStep(opnd);
283 if (XPathScanner.LexKind.SlashSlash == this.scanner.Kind) {
285 opnd = new Axis(Axis.AxisType.DescendantOrSelf, opnd);
287 else if (XPathScanner.LexKind.Slash == this.scanner.Kind) {
299 private static bool IsStep(XPathScanner.LexKind lexKind) {
301 lexKind == XPathScanner.LexKind.Dot ||
302 lexKind == XPathScanner.LexKind.DotDot ||
303 lexKind == XPathScanner.LexKind.At ||
304 lexKind == XPathScanner.LexKind.Axe ||
305 lexKind == XPathScanner.LexKind.Star ||
306 lexKind == XPathScanner.LexKind.Name // NodeTest is also Name
310 //>> Step ::= '.' | '..' | ( AxisName '::' | '@' )? NodeTest Predicate*
311 private AstNode ParseStep(AstNode qyInput) {
313 if (XPathScanner.LexKind.Dot == this.scanner.Kind) { //>> '.'
315 opnd = new Axis(Axis.AxisType.Self, qyInput);
317 else if (XPathScanner.LexKind.DotDot == this.scanner.Kind) { //>> '..'
319 opnd = new Axis(Axis.AxisType.Parent, qyInput);
321 else { //>> ( AxisName '::' | '@' )? NodeTest Predicate*
322 Axis.AxisType axisType = Axis.AxisType.Child;
323 switch (this.scanner.Kind) {
324 case XPathScanner.LexKind.At: //>> '@'
325 axisType = Axis.AxisType.Attribute;
328 case XPathScanner.LexKind.Axe: //>> AxisName '::'
329 axisType = GetAxis(this.scanner);
333 XPathNodeType nodeType = (
334 axisType == Axis.AxisType.Attribute ? XPathNodeType.Attribute :
335 // axisType == Axis.AxisType.Namespace ? XPathNodeType.Namespace : // No Idea why it's this way but othervise Axes doesn't work
336 /* default: */ XPathNodeType.Element
339 opnd = ParseNodeTest(qyInput, axisType, nodeType);
341 while (XPathScanner.LexKind.LBracket == this.scanner.Kind) {
342 opnd = new Filter(opnd, ParsePredicate(opnd));
348 //>> NodeTest ::= NameTest | 'comment ()' | 'text ()' | 'node ()' | 'processing-instruction (' Literal ? ')'
349 private AstNode ParseNodeTest(AstNode qyInput, Axis.AxisType axisType, XPathNodeType nodeType) {
350 string nodeName, nodePrefix;
352 switch (this.scanner.Kind) {
353 case XPathScanner.LexKind.Name :
354 if (this.scanner.CanBeFunction && IsNodeType(this.scanner)) {
355 nodePrefix = string.Empty;
356 nodeName = string.Empty;
358 this.scanner.Name == "comment" ? XPathNodeType.Comment :
359 this.scanner.Name == "text" ? XPathNodeType.Text :
360 this.scanner.Name == "node" ? XPathNodeType.All :
361 this.scanner.Name == "processing-instruction" ? XPathNodeType.ProcessingInstruction :
362 /* default: */ XPathNodeType.Root
364 Debug.Assert(nodeType != XPathNodeType.Root);
367 PassToken(XPathScanner.LexKind.LParens);
369 if (nodeType == XPathNodeType.ProcessingInstruction) {
370 if (this.scanner.Kind != XPathScanner.LexKind.RParens) { //>> 'processing-instruction (' Literal ')'
371 CheckToken(XPathScanner.LexKind.String);
372 nodeName = this.scanner.StringValue;
377 PassToken(XPathScanner.LexKind.RParens);
380 nodePrefix = this.scanner.Prefix;
381 nodeName = this.scanner.Name;
383 if (nodeName == "*") {
384 nodeName = string.Empty;
388 case XPathScanner.LexKind.Star :
389 nodePrefix = string.Empty;
390 nodeName = string.Empty;
394 throw XPathException.Create(Res.Xp_NodeSetExpected, this.scanner.SourceText);
396 return new Axis(axisType, qyInput, nodePrefix, nodeName, nodeType);
399 private static bool IsPrimaryExpr(XPathScanner scanner) {
401 scanner.Kind == XPathScanner.LexKind.String ||
402 scanner.Kind == XPathScanner.LexKind.Number ||
403 scanner.Kind == XPathScanner.LexKind.Dollar ||
404 scanner.Kind == XPathScanner.LexKind.LParens ||
405 scanner.Kind == XPathScanner.LexKind.Name && scanner.CanBeFunction && ! IsNodeType(scanner)
409 //>> PrimaryExpr ::= Literal | Number | VariableReference | '(' Expr ')' | FunctionCall
410 private AstNode ParsePrimaryExpr(AstNode qyInput) {
411 Debug.Assert(IsPrimaryExpr(this.scanner));
413 switch (this.scanner.Kind) {
414 case XPathScanner.LexKind.String:
415 opnd = new Operand(this.scanner.StringValue);
418 case XPathScanner.LexKind.Number:
419 opnd = new Operand(this.scanner.NumberValue);
422 case XPathScanner.LexKind.Dollar:
424 CheckToken(XPathScanner.LexKind.Name);
425 opnd = new Variable(this.scanner.Name, this.scanner.Prefix);
428 case XPathScanner.LexKind.LParens:
430 opnd = ParseExpresion(qyInput);
431 if (opnd.Type != AstNode.AstType.ConstantOperand) {
432 opnd = new Group(opnd);
434 PassToken(XPathScanner.LexKind.RParens);
436 case XPathScanner.LexKind.Name :
437 if (this.scanner.CanBeFunction && ! IsNodeType(this.scanner)) {
438 opnd = ParseMethod(null);
442 Debug.Assert(opnd != null, "IsPrimaryExpr() was true. We should recognize this lex.");
446 private AstNode ParseMethod(AstNode qyInput) {
447 ArrayList argList = new ArrayList();
448 string name = this.scanner.Name;
449 string prefix = this.scanner.Prefix;
450 PassToken(XPathScanner.LexKind.Name);
451 PassToken(XPathScanner.LexKind.LParens);
452 if (this.scanner.Kind != XPathScanner.LexKind.RParens) {
454 argList.Add(ParseExpresion(qyInput));
455 if (this.scanner.Kind == XPathScanner.LexKind.RParens) {
458 PassToken(XPathScanner.LexKind.Comma);
461 PassToken(XPathScanner.LexKind.RParens);
462 if (prefix.Length == 0) {
463 ParamInfo pi = (ParamInfo) functionTable[name];
465 int argCount = argList.Count;
466 if (argCount < pi.Minargs) {
467 throw XPathException.Create(Res.Xp_InvalidNumArgs, name, this.scanner.SourceText);
469 if (pi.FType == Function.FunctionType.FuncConcat) {
470 for (int i = 0; i < argCount; i ++) {
471 AstNode arg = (AstNode)argList[i];
472 if (arg.ReturnType != XPathResultType.String) {
473 arg = new Function(Function.FunctionType.FuncString, arg);
479 if (pi.Maxargs < argCount) {
480 throw XPathException.Create(Res.Xp_InvalidNumArgs, name, this.scanner.SourceText);
482 if (pi.ArgTypes.Length < argCount) {
483 argCount = pi.ArgTypes.Length; // argument we have the type specified (can be < pi.Minargs)
485 for (int i = 0; i < argCount; i ++) {
486 AstNode arg = (AstNode)argList[i];
488 pi.ArgTypes[i] != XPathResultType.Any &&
489 pi.ArgTypes[i] != arg.ReturnType
491 switch (pi.ArgTypes[i]) {
492 case XPathResultType.NodeSet :
493 if (!(arg is Variable) && !(arg is Function && arg.ReturnType == XPathResultType.Any) ) {
494 throw XPathException.Create(Res.Xp_InvalidArgumentType, name, this.scanner.SourceText);
497 case XPathResultType.String :
498 arg = new Function(Function.FunctionType.FuncString, arg);
500 case XPathResultType.Number :
501 arg = new Function(Function.FunctionType.FuncNumber, arg);
503 case XPathResultType.Boolean :
504 arg = new Function(Function.FunctionType.FuncBoolean, arg);
511 return new Function(pi.FType, argList);
514 return new Function(prefix, name, argList);
517 // --------------- Pattern Parsing ----------------------
519 //>> Pattern ::= ( Pattern '|' )? LocationPathPattern
520 private AstNode ParsePattern(AstNode qyInput) {
521 AstNode opnd = ParseLocationPathPattern(qyInput);
524 if (this.scanner.Kind != XPathScanner.LexKind.Union) {
528 opnd = new Operator(Operator.Op.UNION, opnd, ParseLocationPathPattern(qyInput));
532 //>> LocationPathPattern ::= '/' | RelativePathPattern | '//' RelativePathPattern | '/' RelativePathPattern
533 //>> | IdKeyPattern (('/' | '//') RelativePathPattern)?
534 private AstNode ParseLocationPathPattern(AstNode qyInput) {
536 switch (this.scanner.Kind) {
537 case XPathScanner.LexKind.Slash :
540 if (this.scanner.Kind == XPathScanner.LexKind.Eof || this.scanner.Kind == XPathScanner.LexKind.Union) {
544 case XPathScanner.LexKind.SlashSlash :
546 opnd = new Axis(Axis.AxisType.DescendantOrSelf, new Root());
548 case XPathScanner.LexKind.Name :
549 if (this.scanner.CanBeFunction) {
550 opnd = ParseIdKeyPattern(qyInput);
552 switch (this.scanner.Kind) {
553 case XPathScanner.LexKind.Slash :
556 case XPathScanner.LexKind.SlashSlash :
558 opnd = new Axis(Axis.AxisType.DescendantOrSelf, opnd);
567 return ParseRelativePathPattern(opnd);
570 //>> IdKeyPattern ::= 'id' '(' Literal ')' | 'key' '(' Literal ',' Literal ')'
571 private AstNode ParseIdKeyPattern(AstNode qyInput) {
572 Debug.Assert(this.scanner.CanBeFunction);
573 ArrayList argList = new ArrayList();
574 if (this.scanner.Prefix.Length == 0) {
575 if (this.scanner.Name == "id") {
576 ParamInfo pi = (ParamInfo) functionTable["id"];
578 PassToken(XPathScanner.LexKind.LParens);
579 CheckToken(XPathScanner.LexKind.String);
580 argList.Add(new Operand(this.scanner.StringValue));
582 PassToken(XPathScanner.LexKind.RParens);
583 return new Function(pi.FType, argList);
585 if (this.scanner.Name == "key") {
587 PassToken(XPathScanner.LexKind.LParens);
588 CheckToken(XPathScanner.LexKind.String);
589 argList.Add(new Operand(this.scanner.StringValue));
591 PassToken(XPathScanner.LexKind.Comma);
592 CheckToken(XPathScanner.LexKind.String);
593 argList.Add(new Operand(this.scanner.StringValue));
595 PassToken(XPathScanner.LexKind.RParens);
596 return new Function("", "key", argList);
602 //>> PathOp ::= '/' | '//'
603 //>> RelativePathPattern ::= ( RelativePathPattern PathOp )? StepPattern
604 private AstNode ParseRelativePathPattern(AstNode qyInput) {
605 AstNode opnd = ParseStepPattern(qyInput);
606 if (XPathScanner.LexKind.SlashSlash == this.scanner.Kind) {
608 opnd = ParseRelativePathPattern(new Axis(Axis.AxisType.DescendantOrSelf, opnd));
610 else if (XPathScanner.LexKind.Slash == this.scanner.Kind) {
612 opnd = ParseRelativePathPattern(opnd);
617 //>> StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
618 //>> ChildOrAttributeAxisSpecifier ::= @ ? | ('child' | 'attribute') '::'
619 private AstNode ParseStepPattern(AstNode qyInput) {
621 Axis.AxisType axisType = Axis.AxisType.Child;
622 switch (this.scanner.Kind) {
623 case XPathScanner.LexKind.At: //>> '@'
624 axisType = Axis.AxisType.Attribute;
627 case XPathScanner.LexKind.Axe: //>> AxisName '::'
628 axisType = GetAxis(this.scanner);
629 if (axisType != Axis.AxisType.Child && axisType != Axis.AxisType.Attribute) {
630 throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
635 XPathNodeType nodeType = (
636 axisType == Axis.AxisType.Attribute ? XPathNodeType.Attribute :
637 /* default: */ XPathNodeType.Element
640 opnd = ParseNodeTest(qyInput, axisType, nodeType);
642 while (XPathScanner.LexKind.LBracket == this.scanner.Kind) {
643 opnd = new Filter(opnd, ParsePredicate(opnd));
648 // --------------- Helper methods ----------------------
650 void CheckToken(XPathScanner.LexKind t) {
651 if (this.scanner.Kind != t) {
652 throw XPathException.Create(Res.Xp_InvalidToken, this.scanner.SourceText);
656 void PassToken(XPathScanner.LexKind t) {
662 this.scanner.NextLex();
665 private bool TestOp(string op) {
667 this.scanner.Kind == XPathScanner.LexKind.Name &&
668 this.scanner.Prefix.Length == 0 &&
669 this.scanner.Name.Equals(op)
673 void CheckNodeSet(XPathResultType t) {
674 if (t != XPathResultType.NodeSet && t != XPathResultType.Any) {
675 throw XPathException.Create(Res.Xp_NodeSetExpected, this.scanner.SourceText);
679 // ----------------------------------------------------------------
680 static readonly XPathResultType[] temparray1 = {};
681 static readonly XPathResultType[] temparray2 = {XPathResultType.NodeSet};
682 static readonly XPathResultType[] temparray3 = {XPathResultType.Any};
683 static readonly XPathResultType[] temparray4 = {XPathResultType.String};
684 static readonly XPathResultType[] temparray5 = {XPathResultType.String, XPathResultType.String};
685 static readonly XPathResultType[] temparray6 = {XPathResultType.String, XPathResultType.Number, XPathResultType.Number};
686 static readonly XPathResultType[] temparray7 = {XPathResultType.String, XPathResultType.String, XPathResultType.String};
687 static readonly XPathResultType[] temparray8 = {XPathResultType.Boolean};
688 static readonly XPathResultType[] temparray9 = {XPathResultType.Number};
690 private class ParamInfo {
691 private Function.FunctionType ftype;
694 private XPathResultType[] argTypes;
696 public Function.FunctionType FType { get { return this.ftype; } }
697 public int Minargs { get { return this.minargs; } }
698 public int Maxargs { get { return this.maxargs; } }
699 public XPathResultType[] ArgTypes { get { return this.argTypes; } }
701 internal ParamInfo(Function.FunctionType ftype, int minargs, int maxargs, XPathResultType[] argTypes) {
703 this.minargs = minargs;
704 this.maxargs = maxargs;
705 this.argTypes = argTypes;
709 private static Hashtable functionTable = CreateFunctionTable();
710 private static Hashtable CreateFunctionTable(){
711 Hashtable table = new Hashtable(36);
712 table.Add("last" , new ParamInfo(Function.FunctionType.FuncLast , 0, 0, temparray1));
713 table.Add("position" , new ParamInfo(Function.FunctionType.FuncPosition , 0, 0, temparray1));
714 table.Add("name" , new ParamInfo(Function.FunctionType.FuncName , 0, 1, temparray2));
715 table.Add("namespace-uri" , new ParamInfo(Function.FunctionType.FuncNameSpaceUri , 0, 1, temparray2));
716 table.Add("local-name" , new ParamInfo(Function.FunctionType.FuncLocalName , 0, 1, temparray2));
717 table.Add("count" , new ParamInfo(Function.FunctionType.FuncCount , 1, 1, temparray2));
718 table.Add("id" , new ParamInfo(Function.FunctionType.FuncID , 1, 1, temparray3));
719 table.Add("string" , new ParamInfo(Function.FunctionType.FuncString , 0, 1, temparray3));
720 table.Add("concat" , new ParamInfo(Function.FunctionType.FuncConcat , 2, 100, temparray4));
721 table.Add("starts-with" , new ParamInfo(Function.FunctionType.FuncStartsWith , 2, 2, temparray5));
722 table.Add("contains" , new ParamInfo(Function.FunctionType.FuncContains , 2, 2, temparray5));
723 table.Add("substring-before" , new ParamInfo(Function.FunctionType.FuncSubstringBefore, 2, 2, temparray5));
724 table.Add("substring-after" , new ParamInfo(Function.FunctionType.FuncSubstringAfter , 2, 2, temparray5));
725 table.Add("substring" , new ParamInfo(Function.FunctionType.FuncSubstring , 2, 3, temparray6));
726 table.Add("string-length" , new ParamInfo(Function.FunctionType.FuncStringLength , 0, 1, temparray4));
727 table.Add("normalize-space" , new ParamInfo(Function.FunctionType.FuncNormalize , 0, 1, temparray4));
728 table.Add("translate" , new ParamInfo(Function.FunctionType.FuncTranslate , 3, 3, temparray7));
729 table.Add("boolean" , new ParamInfo(Function.FunctionType.FuncBoolean , 1, 1, temparray3));
730 table.Add("not" , new ParamInfo(Function.FunctionType.FuncNot , 1, 1, temparray8));
731 table.Add("true" , new ParamInfo(Function.FunctionType.FuncTrue , 0, 0 ,temparray8));
732 table.Add("false" , new ParamInfo(Function.FunctionType.FuncFalse , 0, 0, temparray8));
733 table.Add("lang" , new ParamInfo(Function.FunctionType.FuncLang , 1, 1, temparray4));
734 table.Add("number" , new ParamInfo(Function.FunctionType.FuncNumber , 0, 1, temparray3));
735 table.Add("sum" , new ParamInfo(Function.FunctionType.FuncSum , 1, 1, temparray2));
736 table.Add("floor" , new ParamInfo(Function.FunctionType.FuncFloor , 1, 1, temparray9));
737 table.Add("ceiling" , new ParamInfo(Function.FunctionType.FuncCeiling , 1, 1, temparray9));
738 table.Add("round" , new ParamInfo(Function.FunctionType.FuncRound , 1, 1, temparray9));
742 private static Hashtable AxesTable = CreateAxesTable();
743 private static Hashtable CreateAxesTable() {
744 Hashtable table = new Hashtable(13);
745 table.Add("ancestor" , Axis.AxisType.Ancestor );
746 table.Add("ancestor-or-self" , Axis.AxisType.AncestorOrSelf );
747 table.Add("attribute" , Axis.AxisType.Attribute );
748 table.Add("child" , Axis.AxisType.Child );
749 table.Add("descendant" , Axis.AxisType.Descendant );
750 table.Add("descendant-or-self", Axis.AxisType.DescendantOrSelf );
751 table.Add("following" , Axis.AxisType.Following );
752 table.Add("following-sibling" , Axis.AxisType.FollowingSibling );
753 table.Add("namespace" , Axis.AxisType.Namespace );
754 table.Add("parent" , Axis.AxisType.Parent );
755 table.Add("preceding" , Axis.AxisType.Preceding );
756 table.Add("preceding-sibling" , Axis.AxisType.PrecedingSibling );
757 table.Add("self" , Axis.AxisType.Self );
761 private Axis.AxisType GetAxis(XPathScanner scaner) {
762 Debug.Assert(scaner.Kind == XPathScanner.LexKind.Axe);
763 object axis = AxesTable[scaner.Name];
765 throw XPathException.Create(Res.Xp_InvalidToken, scanner.SourceText);
767 return (Axis.AxisType) axis;