// CONSIDER: configure the scanner : local info
internal ExpressionNode Parse()
{
// free all nodes
_expression = null;
StartScan();
int cParens = 0;
OperatorInfo opInfo;
while (_token != Tokens.EOS)
{
loop:
Scan();
switch (_token)
{
case Tokens.EOS:
// End of string: must be operand; force out expression;
// check for bomb; check nothing left on stack.
if (_prevOperand == Empty)
{
if (_topNode == 0)
{
// we have an empty expression
break;
}
// set error missing operator
// read the last operator info
opInfo = _ops[_topOperator - 1];
throw ExprException.MissingOperand(opInfo);
}
// collect all nodes
BuildExpression(Operators.priLow);
if (_topOperator != 1)
{
throw ExprException.MissingRightParen();
}
break;
case Tokens.Name:
case Tokens.Parent:
case Tokens.Numeric:
case Tokens.Decimal:
case Tokens.Float:
case Tokens.StringConst:
case Tokens.Date:
ExpressionNode node = null;
string str = null;
/* Constants and identifiers: create leaf node */
if (_prevOperand != Empty)
{
// set error missing operator
throw ExprException.MissingOperator(new string(_text, _start, _pos - _start));
}
if (_topOperator > 0)
{
// special check for IN without parentheses
opInfo = _ops[_topOperator - 1];
if (opInfo._type == Nodes.Binop && opInfo._op == Operators.In && _token != Tokens.Parent)
{
throw ExprException.InWithoutParentheses();
}
}
_prevOperand = Scalar;
switch (_token)
{
case Tokens.Parent:
string relname;
string colname;
// parsing Parent[(relation_name)].column_name)
try
{
// expecting an '(' or '.'
Scan();
if (_token == Tokens.LeftParen)
{
//read the relation name
ScanToken(Tokens.Name);
relname = NameNode.ParseName(_text, _start, _pos);
ScanToken(Tokens.RightParen);
ScanToken(Tokens.Dot);
}
else
{
relname = null;
CheckToken(Tokens.Dot);
}
}
catch (Exception e) when(Common.ADP.IsCatchableExceptionType(e))
{
throw ExprException.LookupArgument();
}
ScanToken(Tokens.Name);
colname = NameNode.ParseName(_text, _start, _pos);
opInfo = _ops[_topOperator - 1];
node = new LookupNode(_table, colname, relname);
break;
case Tokens.Name:
/* Qualify name now for nice error checking */
opInfo = _ops[_topOperator - 1];
/* Create tree element - */
// CONSIDER: Check for reserved proc names here
node = new NameNode(_table, _text, _start, _pos);
break;
case Tokens.Numeric:
str = new string(_text, _start, _pos - _start);
node = new ConstNode(_table, ValueType.Numeric, str);
break;
case Tokens.Decimal:
str = new string(_text, _start, _pos - _start);
node = new ConstNode(_table, ValueType.Decimal, str);
break;
case Tokens.Float:
str = new string(_text, _start, _pos - _start);
node = new ConstNode(_table, ValueType.Float, str);
break;
case Tokens.StringConst:
Debug.Assert(_text[_start] == '\'' && _text[_pos - 1] == '\'', "The expression contains an invalid string constant");
Debug.Assert(_pos - _start > 1, "The expression contains an invalid string constant");
// Store string without quotes..
str = new string(_text, _start + 1, _pos - _start - 2);
node = new ConstNode(_table, ValueType.Str, str);
break;
case Tokens.Date:
Debug.Assert(_text[_start] == '#' && _text[_pos - 1] == '#', "The expression contains invalid date constant.");
Debug.Assert(_pos - _start > 2, "The expression contains invalid date constant '{0}'.");
// Store date without delimiters(#s)..
str = new string(_text, _start + 1, _pos - _start - 2);
node = new ConstNode(_table, ValueType.Date, str);
break;
default:
Debug.Fail("unhandled token");
break;
}
NodePush(node);
goto loop;
case Tokens.LeftParen:
cParens++;
if (_prevOperand == Empty)
{
// Check for ( following IN/IFF. if not, we have a normal (.
// Peek: take a look at the operators stack
Debug.Assert(_topOperator > 0, "Empty operator stack!!");
opInfo = _ops[_topOperator - 1];
if (opInfo._type == Nodes.Binop && opInfo._op == Operators.In)
{
/* IN - handle as procedure call */
node = new FunctionNode(_table, "In");
NodePush(node);
/* Push operator decriptor */
_ops[_topOperator++] = new OperatorInfo(Nodes.Call, Operators.Noop, Operators.priParen);
}
else
{ /* Normal ( */
/* Push operator decriptor */
_ops[_topOperator++] = new OperatorInfo(Nodes.Paren, Operators.Noop, Operators.priParen);
}
}
else
{
// This is a procedure call or () qualification
// Force out any dot qualifiers; check for bomb
BuildExpression(Operators.priProc);
_prevOperand = Empty;
ExpressionNode nodebefore = NodePeek();
if (nodebefore == null || nodebefore.GetType() != typeof(NameNode))
{
// this is more like an assert, so we not care about "nice" exception text..
throw ExprException.SyntaxError();
}
/* Get the proc name */
NameNode name = (NameNode)NodePop();
// Make sure that we can bind the name as a Function
// then get the argument count and types, and parse arguments..
node = new FunctionNode(_table, name._name);
// check to see if this is an aggregate function
Aggregate agg = (Aggregate)(int)((FunctionNode)node).Aggregate;
if (agg != Aggregate.None)
{
node = ParseAggregateArgument((FunctionId)(int)agg);
NodePush(node);
_prevOperand = Expr;
goto loop;
}
NodePush(node);
_ops[_topOperator++] = new OperatorInfo(Nodes.Call, Operators.Noop, Operators.priParen);
}
goto loop;
case Tokens.RightParen:
{
/* Right parentheses: Build expression if we have an operand. */
if (_prevOperand != Empty)
{
BuildExpression(Operators.priLow);
}
/* We must have Tokens.LeftParen on stack. If no operand, must be procedure call. */
if (_topOperator <= 1)
{
// set error, syntax: too many right parens..
throw ExprException.TooManyRightParentheses();
}
Debug.Assert(_topOperator > 1, "melformed operator stack.");
_topOperator--;
opInfo = _ops[_topOperator];
if (_prevOperand == Empty && opInfo._type != Nodes.Call)
{
// set error, syntax: missing operand.
throw ExprException.MissingOperand(opInfo);
}
Debug.Assert(opInfo._priority == Operators.priParen, "melformed operator stack.");
if (opInfo._type == Nodes.Call)
{
/* add argument to the function call. */
if (_prevOperand != Empty)
{
// read last function argument
ExpressionNode argument = NodePop();
/* Get the procedure name and append argument */
Debug.Assert(_topNode > 0 && NodePeek().GetType() == typeof(FunctionNode), "The function node should be created on '('");
FunctionNode func = (FunctionNode)NodePop();
func.AddArgument(argument);
func.Check();
NodePush(func);
}
}
else
{
/* Normal parentheses: create tree node */
// Construct & Put the Nodes.Paren node on node stack
node = NodePop();
node = new UnaryNode(_table, Operators.Noop, node);
NodePush(node);
}
_prevOperand = Expr;
cParens--;
goto loop;
}
case Tokens.ListSeparator:
{
/* Comma encountered: Must be operand; force out subexpression */
if (_prevOperand == Empty)
{
throw ExprException.MissingOperandBefore(",");
}
/* We are be in a procedure call */
/* build next argument */
BuildExpression(Operators.priLow);
opInfo = _ops[_topOperator - 1];
if (opInfo._type != Nodes.Call)
{
throw ExprException.SyntaxError();
}
ExpressionNode argument2 = NodePop();
/* Get the procedure name */
FunctionNode func = (FunctionNode)NodePop();
func.AddArgument(argument2);
NodePush(func);
_prevOperand = Empty;
goto loop;
}
case Tokens.BinaryOp:
if (_prevOperand == Empty)
{
/* Check for unary plus/minus */
if (_op == Operators.Plus)
{
_op = Operators.UnaryPlus;
// fall through to UnaryOperator;
}
else if (_op == Operators.Minus)
{
/* Unary minus */
_op = Operators.Negative;
// fall through to UnaryOperator;
}
else
{
// Error missing operand:
throw ExprException.MissingOperandBefore(Operators.ToString(_op));
}
}
else
{
_prevOperand = Empty;
/* CNSIDER: If we are going to support BETWEEN Translate AND to special BetweenAnd if it is. */
/* Force out to appropriate precedence; push operator. */
BuildExpression(Operators.Priority(_op));
// PushOperator descriptor
_ops[_topOperator++] = new OperatorInfo(Nodes.Binop, _op, Operators.Priority(_op));
goto loop;
}
goto
case Tokens.UnaryOp; // fall through to UnaryOperator;
case Tokens.UnaryOp:
/* Must be no operand. Push it. */
_ops[_topOperator++] = new OperatorInfo(Nodes.Unop, _op, Operators.Priority(_op));
goto loop;
case Tokens.ZeroOp:
// check the we have operator on the stack
if (_prevOperand != Empty)
{
// set error missing operator
throw ExprException.MissingOperator(new string(_text, _start, _pos - _start));
}
// PushOperator descriptor
_ops[_topOperator++] = new OperatorInfo(Nodes.Zop, _op, Operators.priMax);
_prevOperand = Expr;
goto loop;
case Tokens.Dot:
//if there is a name on the stack append it.
ExpressionNode before = NodePeek();
if (before != null && before.GetType() == typeof(NameNode))
{
Scan();
if (_token == Tokens.Name)
{
NameNode nameBefore = (NameNode)NodePop();
string newName = nameBefore._name + "." + NameNode.ParseName(_text, _start, _pos);
NodePush(new NameNode(_table, newName));
goto loop;
}
}
// fall through to default
goto default;
default:
throw ExprException.UnknownToken(new string(_text, _start, _pos - _start), _start + 1);
}
}
goto end_loop;
end_loop:
Debug.Assert(_topNode == 1 || _topNode == 0, "Invalid Node Stack");
_expression = _nodeStack[0];
return(_expression);
}