1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Diagnostics; 6 using System.Globalization; 7 8 namespace System.Data 9 { 10 internal enum ValueType 11 { 12 Unknown = -1, 13 Null = 0, 14 Bool = 1, 15 Numeric = 2, 16 Str = 3, 17 Float = 4, 18 Decimal = 5, 19 Object = 6, 20 Date = 7, 21 } 22 /// <summary> 23 /// ExpressionParser: expression node types 24 /// </summary> 25 internal enum Nodes 26 { 27 Noop = 0, 28 Unop = 1, /* Unary operator */ 29 UnopSpec = 2, /* Special unop: IFF does not eval args */ 30 Binop = 3, /* Binary operator */ 31 BinopSpec = 4, /* Special binop: BETWEEN, IN does not eval args */ 32 Zop = 5, /* "0-ary operator" - intrinsic constant. */ 33 Call = 6, /* Function call or rhs of IN or IFF */ 34 Const = 7, /* Constant value */ 35 Name = 8, /* Identifier */ 36 Paren = 9, /* Parentheses */ 37 Conv = 10, /* Type conversion */ 38 } 39 40 internal sealed class ExpressionParser 41 { 42 /// <summary> 43 /// Operand situations for parser 44 /// </summary> 45 private const int Empty = 0; /* There was no previous operand */ 46 private const int Scalar = 1; /* The previous operand was a constant or id */ 47 private const int Expr = 2; /* The previous operand was a complex expression */ 48 49 50 private readonly struct ReservedWords 51 { 52 internal readonly string _word; // the word 53 internal readonly Tokens _token; 54 internal readonly int _op; 55 ReservedWordsSystem.Data.ExpressionParser.ReservedWords56 internal ReservedWords(string word, Tokens token, int op) 57 { 58 _word = word; 59 _token = token; 60 _op = op; 61 } 62 } 63 64 // this should be maintained as a invariantculture sorted array for binary searching 65 private static readonly ReservedWords[] s_reservedwords = new ReservedWords[] { 66 new ReservedWords("And", Tokens.BinaryOp, Operators.And), 67 /* 68 the following operator is not implemented in the current version of the 69 Expression language, but we need to add them to the Reserved words list 70 to prevent future compatibility problems. 71 */ 72 new ReservedWords("Between", Tokens.BinaryOp, Operators.Between), 73 74 new ReservedWords("Child", Tokens.Child, Operators.Noop), 75 new ReservedWords("False", Tokens.ZeroOp, Operators.False), 76 new ReservedWords("In", Tokens.BinaryOp, Operators.In), 77 new ReservedWords("Is", Tokens.BinaryOp, Operators.Is), 78 new ReservedWords("Like", Tokens.BinaryOp, Operators.Like), 79 new ReservedWords("Not", Tokens.UnaryOp, Operators.Not), 80 new ReservedWords("Null", Tokens.ZeroOp, Operators.Null), 81 new ReservedWords("Or", Tokens.BinaryOp, Operators.Or), 82 new ReservedWords("Parent", Tokens.Parent, Operators.Noop), 83 new ReservedWords("True", Tokens.ZeroOp, Operators.True), 84 }; 85 86 87 /* the following is the Scanner local configuration, Default settings is US 88 * CONSIDER: should we read the user (or system) local settings? 89 * CONSIDER: make this configurable by the user, and system locale for the string compare 90 */ 91 private char _escape = '\\'; 92 private char _decimalSeparator = '.'; 93 //not used: private char ThousandSeparator = ','; 94 private char _listSeparator = ','; 95 //not used: private char DateSeparator = '/'; 96 private char _exponentL = 'e'; 97 private char _exponentU = 'E'; 98 99 internal char[] _text; 100 internal int _pos = 0; 101 internal int _start = 0; 102 internal Tokens _token; 103 internal int _op = Operators.Noop; 104 105 internal OperatorInfo[] _ops = new OperatorInfo[MaxPredicates]; 106 internal int _topOperator = 0; 107 internal int _topNode = 0; 108 109 private readonly DataTable _table; 110 111 private const int MaxPredicates = 100; 112 internal ExpressionNode[] _nodeStack = new ExpressionNode[MaxPredicates]; 113 114 internal int _prevOperand; 115 116 internal ExpressionNode _expression = null; 117 ExpressionParser(DataTable table)118 internal ExpressionParser(DataTable table) 119 { 120 _table = table; 121 } 122 LoadExpression(string data)123 internal void LoadExpression(string data) 124 { 125 int length; 126 127 if (data == null) 128 { 129 length = 0; 130 _text = new char[length + 1]; 131 } 132 else 133 { 134 length = data.Length; 135 _text = new char[length + 1]; 136 data.CopyTo(0, _text, 0, length); 137 } 138 139 _text[length] = '\0'; 140 141 if (_expression != null) 142 { 143 // free all nodes 144 _expression = null; 145 } 146 } 147 StartScan()148 internal void StartScan() 149 { 150 _op = Operators.Noop; 151 _pos = 0; 152 _start = 0; 153 154 _topOperator = 0; 155 _ops[_topOperator++] = new OperatorInfo(Nodes.Noop, Operators.Noop, Operators.priStart); 156 } 157 158 // CONSIDER: configure the scanner : local info 159 Parse()160 internal ExpressionNode Parse() 161 { 162 // free all nodes 163 _expression = null; 164 165 StartScan(); 166 167 int cParens = 0; 168 OperatorInfo opInfo; 169 170 while (_token != Tokens.EOS) 171 { 172 loop: 173 Scan(); 174 175 switch (_token) 176 { 177 case Tokens.EOS: 178 // End of string: must be operand; force out expression; 179 // check for bomb; check nothing left on stack. 180 181 if (_prevOperand == Empty) 182 { 183 if (_topNode == 0) 184 { 185 // we have an empty expression 186 break; 187 } 188 // set error missing operator 189 // read the last operator info 190 opInfo = _ops[_topOperator - 1]; 191 192 throw ExprException.MissingOperand(opInfo); 193 } 194 // collect all nodes 195 BuildExpression(Operators.priLow); 196 if (_topOperator != 1) 197 { 198 throw ExprException.MissingRightParen(); 199 } 200 break; 201 202 case Tokens.Name: 203 case Tokens.Parent: 204 case Tokens.Numeric: 205 case Tokens.Decimal: 206 case Tokens.Float: 207 case Tokens.StringConst: 208 case Tokens.Date: 209 ExpressionNode node = null; 210 string str = null; 211 212 /* Constants and identifiers: create leaf node */ 213 214 if (_prevOperand != Empty) 215 { 216 // set error missing operator 217 throw ExprException.MissingOperator(new string(_text, _start, _pos - _start)); 218 } 219 220 if (_topOperator > 0) 221 { 222 // special check for IN without parentheses 223 224 opInfo = _ops[_topOperator - 1]; 225 226 if (opInfo._type == Nodes.Binop && opInfo._op == Operators.In && _token != Tokens.Parent) 227 { 228 throw ExprException.InWithoutParentheses(); 229 } 230 } 231 232 _prevOperand = Scalar; 233 234 switch (_token) 235 { 236 case Tokens.Parent: 237 string relname; 238 string colname; 239 240 // parsing Parent[(relation_name)].column_name) 241 try 242 { 243 // expecting an '(' or '.' 244 Scan(); 245 if (_token == Tokens.LeftParen) 246 { 247 //read the relation name 248 ScanToken(Tokens.Name); 249 relname = NameNode.ParseName(_text, _start, _pos); 250 ScanToken(Tokens.RightParen); 251 ScanToken(Tokens.Dot); 252 } 253 else 254 { 255 relname = null; 256 CheckToken(Tokens.Dot); 257 } 258 } 259 catch (Exception e) when (Common.ADP.IsCatchableExceptionType(e)) 260 { 261 throw ExprException.LookupArgument(); 262 } 263 264 ScanToken(Tokens.Name); 265 colname = NameNode.ParseName(_text, _start, _pos); 266 267 opInfo = _ops[_topOperator - 1]; 268 node = new LookupNode(_table, colname, relname); 269 270 break; 271 272 case Tokens.Name: 273 /* Qualify name now for nice error checking */ 274 275 opInfo = _ops[_topOperator - 1]; 276 277 /* Create tree element - */ 278 // CONSIDER: Check for reserved proc names here 279 node = new NameNode(_table, _text, _start, _pos); 280 281 break; 282 283 case Tokens.Numeric: 284 str = new string(_text, _start, _pos - _start); 285 node = new ConstNode(_table, ValueType.Numeric, str); 286 break; 287 case Tokens.Decimal: 288 str = new string(_text, _start, _pos - _start); 289 node = new ConstNode(_table, ValueType.Decimal, str); 290 break; 291 case Tokens.Float: 292 str = new string(_text, _start, _pos - _start); 293 node = new ConstNode(_table, ValueType.Float, str); 294 break; 295 case Tokens.StringConst: 296 Debug.Assert(_text[_start] == '\'' && _text[_pos - 1] == '\'', "The expression contains an invalid string constant"); 297 Debug.Assert(_pos - _start > 1, "The expression contains an invalid string constant"); 298 // Store string without quotes.. 299 str = new string(_text, _start + 1, _pos - _start - 2); 300 node = new ConstNode(_table, ValueType.Str, str); 301 break; 302 case Tokens.Date: 303 Debug.Assert(_text[_start] == '#' && _text[_pos - 1] == '#', "The expression contains invalid date constant."); 304 Debug.Assert(_pos - _start > 2, "The expression contains invalid date constant '{0}'."); 305 // Store date without delimiters(#s).. 306 str = new string(_text, _start + 1, _pos - _start - 2); 307 node = new ConstNode(_table, ValueType.Date, str); 308 break; 309 default: 310 Debug.Assert(false, "unhandled token"); 311 break; 312 } 313 314 NodePush(node); 315 goto loop; 316 317 case Tokens.LeftParen: 318 cParens++; 319 if (_prevOperand == Empty) 320 { 321 // Check for ( following IN/IFF. if not, we have a normal (. 322 // Peek: take a look at the operators stack 323 324 Debug.Assert(_topOperator > 0, "Empty operator stack!!"); 325 opInfo = _ops[_topOperator - 1]; 326 327 if (opInfo._type == Nodes.Binop && opInfo._op == Operators.In) 328 { 329 /* IN - handle as procedure call */ 330 331 node = new FunctionNode(_table, "In"); 332 NodePush(node); 333 /* Push operator decriptor */ 334 _ops[_topOperator++] = new OperatorInfo(Nodes.Call, Operators.Noop, Operators.priParen); 335 } 336 else 337 { /* Normal ( */ 338 /* Push operator decriptor */ 339 _ops[_topOperator++] = new OperatorInfo(Nodes.Paren, Operators.Noop, Operators.priParen); 340 } 341 } 342 else 343 { 344 // This is a procedure call or () qualification 345 // Force out any dot qualifiers; check for bomb 346 347 BuildExpression(Operators.priProc); 348 _prevOperand = Empty; 349 ExpressionNode nodebefore = NodePeek(); 350 351 if (nodebefore == null || nodebefore.GetType() != typeof(NameNode)) 352 { 353 // this is more like an assert, so we not care about "nice" exception text.. 354 throw ExprException.SyntaxError(); 355 } 356 357 /* Get the proc name */ 358 NameNode name = (NameNode)NodePop(); 359 360 // Make sure that we can bind the name as a Function 361 // then get the argument count and types, and parse arguments.. 362 363 node = new FunctionNode(_table, name._name); 364 365 // check to see if this is an aggregate function 366 Aggregate agg = (Aggregate)(int)((FunctionNode)node).Aggregate; 367 if (agg != Aggregate.None) 368 { 369 node = ParseAggregateArgument((FunctionId)(int)agg); 370 NodePush(node); 371 _prevOperand = Expr; 372 goto loop; 373 } 374 375 NodePush(node); 376 _ops[_topOperator++] = new OperatorInfo(Nodes.Call, Operators.Noop, Operators.priParen); 377 } 378 goto loop; 379 380 case Tokens.RightParen: 381 { 382 /* Right parentheses: Build expression if we have an operand. */ 383 if (_prevOperand != Empty) 384 { 385 BuildExpression(Operators.priLow); 386 } 387 388 /* We must have Tokens.LeftParen on stack. If no operand, must be procedure call. */ 389 if (_topOperator <= 1) 390 { 391 // set error, syntax: too many right parens.. 392 throw ExprException.TooManyRightParentheses(); 393 } 394 395 Debug.Assert(_topOperator > 1, "melformed operator stack."); 396 _topOperator--; 397 opInfo = _ops[_topOperator]; 398 399 if (_prevOperand == Empty && opInfo._type != Nodes.Call) 400 { 401 // set error, syntax: missing operand. 402 throw ExprException.MissingOperand(opInfo); 403 } 404 405 Debug.Assert(opInfo._priority == Operators.priParen, "melformed operator stack."); 406 407 if (opInfo._type == Nodes.Call) 408 { 409 /* add argument to the function call. */ 410 411 if (_prevOperand != Empty) 412 { 413 // read last function argument 414 ExpressionNode argument = NodePop(); 415 416 /* Get the procedure name and append argument */ 417 Debug.Assert(_topNode > 0 && NodePeek().GetType() == typeof(FunctionNode), "The function node should be created on '('"); 418 419 FunctionNode func = (FunctionNode)NodePop(); 420 func.AddArgument(argument); 421 func.Check(); 422 NodePush(func); 423 } 424 } 425 else 426 { 427 /* Normal parentheses: create tree node */ 428 // Construct & Put the Nodes.Paren node on node stack 429 node = NodePop(); 430 node = new UnaryNode(_table, Operators.Noop, node); 431 NodePush(node); 432 } 433 434 _prevOperand = Expr; 435 cParens--; 436 goto loop; 437 } 438 case Tokens.ListSeparator: 439 { 440 /* Comma encountered: Must be operand; force out subexpression */ 441 442 if (_prevOperand == Empty) 443 { 444 throw ExprException.MissingOperandBefore(","); 445 } 446 447 /* We are be in a procedure call */ 448 449 /* build next argument */ 450 BuildExpression(Operators.priLow); 451 452 opInfo = _ops[_topOperator - 1]; 453 454 if (opInfo._type != Nodes.Call) 455 throw ExprException.SyntaxError(); 456 457 ExpressionNode argument2 = NodePop(); 458 459 /* Get the procedure name */ 460 461 FunctionNode func = (FunctionNode)NodePop(); 462 463 func.AddArgument(argument2); 464 465 NodePush(func); 466 467 _prevOperand = Empty; 468 469 goto loop; 470 } 471 case Tokens.BinaryOp: 472 if (_prevOperand == Empty) 473 { 474 /* Check for unary plus/minus */ 475 if (_op == Operators.Plus) 476 { 477 _op = Operators.UnaryPlus; 478 // fall through to UnaryOperator; 479 } 480 else if (_op == Operators.Minus) 481 { 482 /* Unary minus */ 483 _op = Operators.Negative; 484 // fall through to UnaryOperator; 485 } 486 else 487 { 488 // Error missing operand: 489 throw ExprException.MissingOperandBefore(Operators.ToString(_op)); 490 } 491 } 492 else 493 { 494 _prevOperand = Empty; 495 496 /* CNSIDER: If we are going to support BETWEEN Translate AND to special BetweenAnd if it is. */ 497 498 /* Force out to appropriate precedence; push operator. */ 499 500 BuildExpression(Operators.Priority(_op)); 501 502 // PushOperator descriptor 503 _ops[_topOperator++] = new OperatorInfo(Nodes.Binop, _op, Operators.Priority(_op)); 504 goto loop; 505 } 506 goto 507 case Tokens.UnaryOp; // fall through to UnaryOperator; 508 509 case Tokens.UnaryOp: 510 /* Must be no operand. Push it. */ 511 _ops[_topOperator++] = new OperatorInfo(Nodes.Unop, _op, Operators.Priority(_op)); 512 goto loop; 513 514 case Tokens.ZeroOp: 515 // check the we have operator on the stack 516 if (_prevOperand != Empty) 517 { 518 // set error missing operator 519 throw ExprException.MissingOperator(new string(_text, _start, _pos - _start)); 520 } 521 522 // PushOperator descriptor 523 _ops[_topOperator++] = new OperatorInfo(Nodes.Zop, _op, Operators.priMax); 524 _prevOperand = Expr; 525 goto loop; 526 527 case Tokens.Dot: 528 //if there is a name on the stack append it. 529 ExpressionNode before = NodePeek(); 530 531 if (before != null && before.GetType() == typeof(NameNode)) 532 { 533 Scan(); 534 535 if (_token == Tokens.Name) 536 { 537 NameNode nameBefore = (NameNode)NodePop(); 538 string newName = nameBefore._name + "." + NameNode.ParseName(_text, _start, _pos); 539 NodePush(new NameNode(_table, newName)); 540 goto loop; 541 } 542 } 543 // fall through to default 544 goto default; 545 default: 546 throw ExprException.UnknownToken(new string(_text, _start, _pos - _start), _start + 1); 547 } 548 } 549 goto end_loop; 550 end_loop: 551 Debug.Assert(_topNode == 1 || _topNode == 0, "Invalid Node Stack"); 552 _expression = _nodeStack[0]; 553 554 return _expression; 555 } 556 557 /// <summary> 558 /// Parse the argument to an Aggregate function. The syntax is 559 /// Func(child[(relation_name)].column_name) 560 /// When the function is called we have already parsed the Aggregate name, and open paren 561 /// </summary> ParseAggregateArgument(FunctionId aggregate)562 private ExpressionNode ParseAggregateArgument(FunctionId aggregate) 563 { 564 Debug.Assert(_token == Tokens.LeftParen, "ParseAggregateArgument(): Invalid argument, token <> '('"); 565 566 bool child; 567 string relname; 568 string colname; 569 570 Scan(); 571 572 try 573 { 574 if (_token != Tokens.Child) 575 { 576 if (_token != Tokens.Name) 577 throw ExprException.AggregateArgument(); 578 579 colname = NameNode.ParseName(_text, _start, _pos); 580 ScanToken(Tokens.RightParen); 581 return new AggregateNode(_table, aggregate, colname); 582 } 583 584 child = (_token == Tokens.Child); 585 _prevOperand = Scalar; 586 587 // expecting an '(' or '.' 588 Scan(); 589 590 if (_token == Tokens.LeftParen) 591 { 592 //read the relation name 593 ScanToken(Tokens.Name); 594 relname = NameNode.ParseName(_text, _start, _pos); 595 ScanToken(Tokens.RightParen); 596 ScanToken(Tokens.Dot); 597 } 598 else 599 { 600 relname = null; 601 CheckToken(Tokens.Dot); 602 } 603 604 ScanToken(Tokens.Name); 605 colname = NameNode.ParseName(_text, _start, _pos); 606 ScanToken(Tokens.RightParen); 607 } 608 catch (Exception e) when (Common.ADP.IsCatchableExceptionType(e)) 609 { 610 throw ExprException.AggregateArgument(); 611 } 612 return new AggregateNode(_table, aggregate, colname, !child, relname); 613 } 614 615 /// <summary> 616 /// NodePop - Pop an operand node from the node stack. 617 /// </summary> NodePop()618 private ExpressionNode NodePop() 619 { 620 Debug.Assert(_topNode > 0, "NodePop(): Corrupted node stack"); 621 ExpressionNode node = _nodeStack[--_topNode]; 622 Debug.Assert(null != node, "null NodePop"); 623 return node; 624 } 625 626 /// <summary> 627 /// NodePeek - Peek at the top node. 628 /// </summary> NodePeek()629 private ExpressionNode NodePeek() 630 { 631 if (_topNode <= 0) 632 return null; 633 634 return _nodeStack[_topNode - 1]; 635 } 636 637 /// <summary> 638 /// Push an operand node onto the node stack 639 /// </summary> NodePush(ExpressionNode node)640 private void NodePush(ExpressionNode node) 641 { 642 Debug.Assert(null != node, "null NodePush"); 643 644 if (_topNode >= MaxPredicates - 2) 645 { 646 throw ExprException.ExpressionTooComplex(); 647 } 648 _nodeStack[_topNode++] = node; 649 } 650 651 /// <summary> 652 /// Builds expression tree for higher-precedence operator to be used as left 653 /// operand of current operator. May cause errors - always do ErrorCheck() upin return. 654 /// </summary> 655 BuildExpression(int pri)656 private void BuildExpression(int pri) 657 { 658 ExpressionNode expr = null; 659 660 Debug.Assert(pri > Operators.priStart && pri <= Operators.priMax, "Invalid priority value"); 661 662 /* For all operators of higher or same precedence (we are always 663 left-associative) */ 664 while (true) 665 { 666 Debug.Assert(_topOperator > 0, "Empty operator stack!!"); 667 OperatorInfo opInfo = _ops[_topOperator - 1]; 668 669 if (opInfo._priority < pri) 670 goto end_loop; 671 672 Debug.Assert(opInfo._priority >= pri, "Invalid prioriry value"); 673 _topOperator--; 674 675 ExpressionNode nodeLeft; 676 ExpressionNode nodeRight; 677 switch (opInfo._type) 678 { 679 case Nodes.Binop: 680 { 681 // get right, left operands. Bind them. 682 683 nodeRight = NodePop(); 684 nodeLeft = NodePop(); 685 686 /* This is the place to do type and other checks */ 687 688 switch (opInfo._op) 689 { 690 case Operators.Between: 691 case Operators.BetweenAnd: 692 case Operators.BitwiseAnd: 693 case Operators.BitwiseOr: 694 case Operators.BitwiseXor: 695 case Operators.BitwiseNot: 696 throw ExprException.UnsupportedOperator(opInfo._op); 697 698 case Operators.Is: 699 case Operators.Or: 700 case Operators.And: 701 case Operators.EqualTo: 702 case Operators.NotEqual: 703 case Operators.Like: 704 case Operators.LessThen: 705 case Operators.LessOrEqual: 706 case Operators.GreaterThen: 707 case Operators.GreaterOrEqual: 708 case Operators.In: 709 break; 710 711 default: 712 Debug.Assert(opInfo._op == Operators.Plus || 713 opInfo._op == Operators.Minus || 714 opInfo._op == Operators.Multiply || 715 opInfo._op == Operators.Divide || 716 opInfo._op == Operators.Modulo, 717 "Invalud Binary operation"); 718 719 break; 720 } 721 Debug.Assert(nodeLeft != null, "Invalid left operand"); 722 Debug.Assert(nodeRight != null, "Invalid right operand"); 723 724 if (opInfo._op == Operators.Like) 725 { 726 expr = new LikeNode(_table, opInfo._op, nodeLeft, nodeRight); 727 } 728 else 729 { 730 expr = new BinaryNode(_table, opInfo._op, nodeLeft, nodeRight); 731 } 732 733 break; 734 } 735 case Nodes.Unop: 736 /* Unary operator: Pop and bind right op. */ 737 nodeLeft = null; 738 nodeRight = NodePop(); 739 740 /* Check for special cases */ 741 switch (opInfo._op) 742 { 743 case Operators.Not: 744 break; 745 746 case Operators.BitwiseNot: 747 throw ExprException.UnsupportedOperator(opInfo._op); 748 749 case Operators.Negative: 750 break; 751 } 752 753 Debug.Assert(nodeLeft == null, "Invalid left operand"); 754 Debug.Assert(nodeRight != null, "Invalid right operand"); 755 756 expr = new UnaryNode(_table, opInfo._op, nodeRight); 757 break; 758 759 case Nodes.Zop: 760 /* Intrinsic constant: just create node. */ 761 expr = new ZeroOpNode(opInfo._op); 762 break; 763 764 default: 765 Debug.Assert(false, "Unhandled operator type"); 766 goto end_loop; 767 } 768 Debug.Assert(expr != null, "Failed to create expression"); 769 770 NodePush(expr); 771 // countinue while loop; 772 } 773 end_loop: 774 ; 775 } 776 777 CheckToken(Tokens token)778 internal void CheckToken(Tokens token) 779 { 780 if (_token != token) 781 { 782 throw ExprException.UnknownToken(token, _token, _pos); 783 } 784 } 785 Scan()786 internal Tokens Scan() 787 { 788 char ch; 789 char[] text = _text; 790 791 _token = Tokens.None; 792 793 while (true) 794 { 795 loop: 796 _start = _pos; 797 _op = Operators.Noop; 798 ch = text[_pos++]; 799 switch (ch) 800 { 801 case (char)0: 802 _token = Tokens.EOS; 803 goto end_loop; 804 805 case ' ': 806 case '\t': 807 case '\n': 808 case '\r': 809 ScanWhite(); 810 goto loop; 811 812 case '(': 813 _token = Tokens.LeftParen; 814 goto end_loop; 815 816 case ')': 817 _token = Tokens.RightParen; 818 goto end_loop; 819 820 case '#': 821 ScanDate(); 822 CheckToken(Tokens.Date); 823 goto end_loop; 824 825 case '\'': 826 ScanString('\''); 827 CheckToken(Tokens.StringConst); 828 goto end_loop; 829 830 case '=': 831 _token = Tokens.BinaryOp; 832 _op = Operators.EqualTo; 833 goto end_loop; 834 835 case '>': 836 _token = Tokens.BinaryOp; 837 ScanWhite(); 838 if (text[_pos] == '=') 839 { 840 _pos++; 841 _op = Operators.GreaterOrEqual; 842 } 843 else 844 _op = Operators.GreaterThen; 845 goto end_loop; 846 case '<': 847 _token = Tokens.BinaryOp; 848 ScanWhite(); 849 if (text[_pos] == '=') 850 { 851 _pos++; 852 _op = Operators.LessOrEqual; 853 } 854 else if (text[_pos] == '>') 855 { 856 _pos++; 857 _op = Operators.NotEqual; 858 } 859 else 860 _op = Operators.LessThen; 861 goto end_loop; 862 863 case '+': 864 _token = Tokens.BinaryOp; 865 _op = Operators.Plus; 866 goto end_loop; 867 868 case '-': 869 _token = Tokens.BinaryOp; 870 _op = Operators.Minus; 871 goto end_loop; 872 873 case '*': 874 _token = Tokens.BinaryOp; 875 _op = Operators.Multiply; 876 goto end_loop; 877 878 case '/': 879 _token = Tokens.BinaryOp; 880 _op = Operators.Divide; 881 goto end_loop; 882 883 case '%': 884 _token = Tokens.BinaryOp; 885 _op = Operators.Modulo; 886 goto end_loop; 887 888 /* Beginning of bitwise operators */ 889 case '&': 890 _token = Tokens.BinaryOp; 891 _op = Operators.BitwiseAnd; 892 goto end_loop; 893 894 case '|': 895 _token = Tokens.BinaryOp; 896 _op = Operators.BitwiseOr; 897 goto end_loop; 898 case '^': 899 _token = Tokens.BinaryOp; 900 _op = Operators.BitwiseXor; 901 goto end_loop; 902 case '~': 903 _token = Tokens.BinaryOp; 904 _op = Operators.BitwiseNot; 905 goto end_loop; 906 907 /* we have bracketed identifier */ 908 case '[': 909 // BUG: special case 910 ScanName(']', _escape, "]\\"); 911 CheckToken(Tokens.Name); 912 goto end_loop; 913 914 case '`': 915 ScanName('`', '`', "`"); 916 CheckToken(Tokens.Name); 917 goto end_loop; 918 919 default: 920 /* Check for list separator */ 921 922 if (ch == _listSeparator) 923 { 924 _token = Tokens.ListSeparator; 925 goto end_loop; 926 } 927 928 if (ch == '.') 929 { 930 if (_prevOperand == Empty) 931 { 932 ScanNumeric(); 933 } 934 else 935 { 936 _token = Tokens.Dot; 937 } 938 goto end_loop; 939 } 940 941 /* Check for binary constant */ 942 if (ch == '0' && (text[_pos] == 'x' || text[_pos] == 'X')) 943 { 944 ScanBinaryConstant(); 945 _token = Tokens.BinaryConst; 946 goto end_loop; 947 } 948 949 /* Check for number: digit is always good; . or - only if osNil. */ 950 if (IsDigit(ch)) 951 { 952 ScanNumeric(); 953 goto end_loop; 954 } 955 956 /* Check for reserved word */ 957 ScanReserved(); 958 if (_token != Tokens.None) 959 { 960 goto end_loop; 961 } 962 963 /* Alpha means identifier */ 964 965 if (IsAlphaNumeric(ch)) 966 { 967 ScanName(); 968 if (_token != Tokens.None) 969 { 970 CheckToken(Tokens.Name); 971 goto end_loop; 972 } 973 } 974 975 /* Don't understand that banter at all. */ 976 _token = Tokens.Unknown; 977 throw ExprException.UnknownToken(new string(text, _start, _pos - _start), _start + 1); 978 } 979 } 980 end_loop: 981 return _token; 982 } 983 984 /// <summary> 985 /// ScanNumeric - parse number. 986 /// In format: [digit|.]*{[e|E]{[+|-]}{digit*}} 987 /// Further checking is done by constant parser. 988 /// </summary> ScanNumeric()989 private void ScanNumeric() 990 { 991 char[] text = _text; 992 bool fDot = false; 993 bool fSientific = false; 994 995 Debug.Assert(_pos != 0, "We have at least one digit in the buffer, ScanNumeric()"); 996 Debug.Assert(IsDigit(text[_pos - 1]), "We have at least one digit in the buffer, ScanNumeric(), not a digit"); 997 998 while (IsDigit(text[_pos])) 999 { 1000 _pos++; 1001 } 1002 1003 if (text[_pos] == _decimalSeparator) 1004 { 1005 fDot = true; 1006 _pos++; 1007 } 1008 1009 while (IsDigit(text[_pos])) 1010 { 1011 _pos++; 1012 } 1013 1014 if (text[_pos] == _exponentL || text[_pos] == _exponentU) 1015 { 1016 fSientific = true; 1017 _pos++; 1018 1019 if (text[_pos] == '-' || text[_pos] == '+') 1020 { 1021 _pos++; 1022 } 1023 while (IsDigit(text[_pos])) 1024 { 1025 _pos++; 1026 } 1027 } 1028 if (fSientific) 1029 _token = Tokens.Float; 1030 else if (fDot) 1031 _token = Tokens.Decimal; 1032 else 1033 _token = Tokens.Numeric; 1034 } 1035 /// <summary> 1036 /// Just a string of alphanumeric characters. 1037 /// </summary> ScanName()1038 private void ScanName() 1039 { 1040 char[] text = _text; 1041 1042 while (IsAlphaNumeric(text[_pos])) 1043 _pos++; 1044 1045 _token = Tokens.Name; 1046 } 1047 1048 /// <summary> 1049 /// recognize bracketed identifiers. 1050 /// Special case: we are using '\' character to escape '[' and ']' only, so '\' by itself is not an escape 1051 /// </summary> ScanName(char chEnd, char esc, string charsToEscape)1052 private void ScanName(char chEnd, char esc, string charsToEscape) 1053 { 1054 char[] text = _text; 1055 1056 Debug.Assert(chEnd != '\0', "Invalid bracket value"); 1057 Debug.Assert(esc != '\0', "Invalid escape value"); 1058 do 1059 { 1060 if (text[_pos] == esc) 1061 { 1062 if (_pos + 1 < text.Length && charsToEscape.IndexOf(text[_pos + 1]) >= 0) 1063 { 1064 _pos++; 1065 } 1066 } 1067 _pos++; 1068 } while (_pos < text.Length && text[_pos] != chEnd); 1069 1070 if (_pos >= text.Length) 1071 { 1072 throw ExprException.InvalidNameBracketing(new string(text, _start, (_pos - 1) - _start)); 1073 } 1074 1075 Debug.Assert(text[_pos] == chEnd, "Invalid bracket value"); 1076 1077 _pos++; 1078 1079 _token = Tokens.Name; 1080 } 1081 1082 /// <summary> 1083 /// Just read the string between '#' signs, and parse it later 1084 /// </summary> ScanDate()1085 private void ScanDate() 1086 { 1087 char[] text = _text; 1088 1089 do _pos++; while (_pos < text.Length && text[_pos] != '#'); 1090 1091 if (_pos >= text.Length || text[_pos] != '#') 1092 { 1093 // Bad date constant 1094 if (_pos >= text.Length) 1095 throw ExprException.InvalidDate(new string(text, _start, (_pos - 1) - _start)); 1096 else 1097 throw ExprException.InvalidDate(new string(text, _start, _pos - _start)); 1098 } 1099 else 1100 { 1101 _token = Tokens.Date; 1102 } 1103 _pos++; 1104 } 1105 ScanBinaryConstant()1106 private void ScanBinaryConstant() 1107 { 1108 char[] text = _text; 1109 } 1110 ScanReserved()1111 private void ScanReserved() 1112 { 1113 char[] text = _text; 1114 1115 if (IsAlpha(text[_pos])) 1116 { 1117 ScanName(); 1118 1119 Debug.Assert(_token == Tokens.Name, "Exprecing an identifier."); 1120 Debug.Assert(_pos > _start, "Exprecing an identifier."); 1121 1122 string name = new string(text, _start, _pos - _start); 1123 Debug.Assert(name != null, "Make sure the arguments for Compare method are OK"); 1124 1125 1126 CompareInfo comparer = CultureInfo.InvariantCulture.CompareInfo; 1127 // binary search reserved words 1128 int lo = 0; 1129 int hi = s_reservedwords.Length - 1; 1130 do 1131 { 1132 int i = (lo + hi) / 2; 1133 Debug.Assert(s_reservedwords[i]._word != null, "Make sure the arguments for Compare method are OK"); 1134 int c = comparer.Compare(s_reservedwords[i]._word, name, CompareOptions.IgnoreCase); 1135 1136 if (c == 0) 1137 { 1138 // we found the reserved word.. 1139 _token = s_reservedwords[i]._token; 1140 _op = s_reservedwords[i]._op; 1141 return; 1142 } 1143 if (c < 0) 1144 { 1145 lo = i + 1; 1146 } 1147 else 1148 { 1149 hi = i - 1; 1150 } 1151 } while (lo <= hi); 1152 1153 Debug.Assert(_token == Tokens.Name, "Exprecing an identifier."); 1154 } 1155 } 1156 ScanString(char escape)1157 private void ScanString(char escape) 1158 { 1159 char[] text = _text; 1160 1161 while (_pos < text.Length) 1162 { 1163 char ch = text[_pos++]; 1164 1165 if (ch == escape && _pos < text.Length && text[_pos] == escape) 1166 { 1167 _pos++; 1168 } 1169 else if (ch == escape) 1170 break; 1171 } 1172 1173 if (_pos >= text.Length) 1174 { 1175 throw ExprException.InvalidString(new string(text, _start, (_pos - 1) - _start)); 1176 } 1177 1178 _token = Tokens.StringConst; 1179 } 1180 1181 // scan the next token, and error if it doesn't match the requested token ScanToken(Tokens token)1182 internal void ScanToken(Tokens token) 1183 { 1184 Scan(); 1185 CheckToken(token); 1186 } 1187 ScanWhite()1188 private void ScanWhite() 1189 { 1190 char[] text = _text; 1191 1192 while (_pos < text.Length && IsWhiteSpace(text[_pos])) 1193 { 1194 _pos++; 1195 } 1196 } 1197 1198 /// <summary> 1199 /// is the character a whitespace character? 1200 /// Consider using CharacterInfo().IsWhiteSpace(ch) (System.Globalization) 1201 /// </summary> IsWhiteSpace(char ch)1202 private bool IsWhiteSpace(char ch) 1203 { 1204 return ch <= 32 && ch != '\0'; 1205 } 1206 1207 /// <summary> 1208 /// is the character an alphanumeric? 1209 /// </summary> IsAlphaNumeric(char ch)1210 private bool IsAlphaNumeric(char ch) 1211 { 1212 //single comparison 1213 switch (ch) 1214 { 1215 case 'a': 1216 case 'b': 1217 case 'c': 1218 case 'd': 1219 case 'e': 1220 case 'f': 1221 case 'g': 1222 case 'h': 1223 case 'i': 1224 case 'j': 1225 case 'k': 1226 case 'l': 1227 case 'm': 1228 case 'n': 1229 case 'o': 1230 case 'p': 1231 case 'q': 1232 case 'r': 1233 case 's': 1234 case 't': 1235 case 'u': 1236 case 'v': 1237 case 'w': 1238 case 'x': 1239 case 'y': 1240 case 'z': 1241 case 'A': 1242 case 'B': 1243 case 'C': 1244 case 'D': 1245 case 'E': 1246 case 'F': 1247 case 'G': 1248 case 'H': 1249 case 'I': 1250 case 'J': 1251 case 'K': 1252 case 'L': 1253 case 'M': 1254 case 'N': 1255 case 'O': 1256 case 'P': 1257 case 'Q': 1258 case 'R': 1259 case 'S': 1260 case 'T': 1261 case 'U': 1262 case 'V': 1263 case 'W': 1264 case 'X': 1265 case 'Y': 1266 case 'Z': 1267 case '0': 1268 case '1': 1269 case '2': 1270 case '3': 1271 case '4': 1272 case '5': 1273 case '6': 1274 case '7': 1275 case '8': 1276 case '9': 1277 case '_': 1278 case '$': 1279 return true; 1280 default: 1281 if (ch > 0x7f) 1282 return true; 1283 1284 return false; 1285 } 1286 } 1287 IsDigit(char ch)1288 private bool IsDigit(char ch) 1289 { 1290 //single comparison 1291 switch (ch) 1292 { 1293 case '0': 1294 case '1': 1295 case '2': 1296 case '3': 1297 case '4': 1298 case '5': 1299 case '6': 1300 case '7': 1301 case '8': 1302 case '9': 1303 return true; 1304 default: 1305 return false; 1306 } 1307 } 1308 1309 /// <summary> 1310 /// is the character an alpha? 1311 /// </summary> IsAlpha(char ch)1312 private bool IsAlpha(char ch) 1313 { 1314 //single comparison 1315 switch (ch) 1316 { 1317 case 'a': 1318 case 'b': 1319 case 'c': 1320 case 'd': 1321 case 'e': 1322 case 'f': 1323 case 'g': 1324 case 'h': 1325 case 'i': 1326 case 'j': 1327 case 'k': 1328 case 'l': 1329 case 'm': 1330 case 'n': 1331 case 'o': 1332 case 'p': 1333 case 'q': 1334 case 'r': 1335 case 's': 1336 case 't': 1337 case 'u': 1338 case 'v': 1339 case 'w': 1340 case 'x': 1341 case 'y': 1342 case 'z': 1343 case 'A': 1344 case 'B': 1345 case 'C': 1346 case 'D': 1347 case 'E': 1348 case 'F': 1349 case 'G': 1350 case 'H': 1351 case 'I': 1352 case 'J': 1353 case 'K': 1354 case 'L': 1355 case 'M': 1356 case 'N': 1357 case 'O': 1358 case 'P': 1359 case 'Q': 1360 case 'R': 1361 case 'S': 1362 case 'T': 1363 case 'U': 1364 case 'V': 1365 case 'W': 1366 case 'X': 1367 case 'Y': 1368 case 'Z': 1369 case '_': 1370 return true; 1371 default: 1372 return false; 1373 } 1374 } 1375 } 1376 1377 internal enum Tokens 1378 { 1379 None = 0, 1380 Name = 1, /* Identifier */ 1381 Numeric = 2, 1382 Decimal = 3, 1383 Float = 4, 1384 BinaryConst = 5, /* Binary Constant e.g. 0x12ef */ 1385 StringConst = 6, 1386 Date = 7, 1387 ListSeparator = 8, /* List Tokens.ListSeparator/Comma */ 1388 LeftParen = 9, /* '('; */ 1389 RightParen = 10, /* ')'; */ 1390 ZeroOp = 11, /* 0-array operator like "NULL" */ 1391 UnaryOp = 12, 1392 BinaryOp = 13, 1393 Child = 14, 1394 Parent = 15, 1395 Dot = 16, 1396 Unknown = 17, /* do not understend the token */ 1397 EOS = 18, /* End of string */ 1398 } 1399 1400 /// <summary> 1401 /// Operator stack element 1402 /// </summary> 1403 internal sealed class OperatorInfo 1404 { 1405 internal Nodes _type = 0; 1406 internal int _op = 0; 1407 internal int _priority = 0; 1408 OperatorInfo(Nodes type, int op, int pri)1409 internal OperatorInfo(Nodes type, int op, int pri) 1410 { 1411 _type = type; 1412 _op = op; 1413 _priority = pri; 1414 } 1415 } 1416 } 1417