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