private Yytoken[] Lex(string value) { ArrayList tokens = new ArrayList(); using (StringReader reader = new StringReader(value)) { OPathLexer lexer = new OPathLexer(reader); Yytoken token; while ((token = lexer.Lex()) != null) { tokens.Add(token); } } return((Yytoken[])tokens.ToArray(typeof(Yytoken))); }
public OrderBy Parse(Type type, string expression) { // Example expressions for an Employee query: // "FirstName, LastName" // "FirstName ASC, LastName DESC" // "Company.Name" // "Company.Name ASC" // "Company.Address.State ASC" // "Company.Name ASC, Company.Address.State ASC, FirstName ASC" // "Company.Name ASC, HomeAddress.ZipCode, Company.Address.State DESC, ZipCode" _expression = expression; EntityMap entity = _maps[type]; if (entity == null) { throw new Exception("Type " + type + " does not have an entity mapping defined to the database."); } OrderByItemCollection items = new OrderByItemCollection(); OrderByJoinCollection joins = new OrderByJoinCollection(); using (StringReader reader = new StringReader(expression)) { OPathLexer lexer = new OPathLexer(reader); lexer.Lex(); while (lexer.CurrentToken != null) { OrderByItem item = ParseItem(lexer, entity, joins, null); items.Add(item); } if (lexer.LastToken != null && lexer.LastToken.Type == TokenType.COMMA) { throw new OPathException("A trailing comma was detected in sort expression '" + expression + "'."); } } return(new OrderBy(null, items, joins)); }
public OrderBy Parse(Type type, string expression) { // Example expressions for an Employee query: // "FirstName, LastName" // "FirstName ASC, LastName DESC" // "Company.Name" // "Company.Name ASC" // "Company.Address.State ASC" // "Company.Name ASC, Company.Address.State ASC, FirstName ASC" // "Company.Name ASC, HomeAddress.ZipCode, Company.Address.State DESC, ZipCode" _expression = expression; EntityMap entity = _maps[type]; if( entity == null ) { throw new Exception("Type " + type + " does not have an entity mapping defined to the database."); } OrderByItemCollection items = new OrderByItemCollection(); OrderByJoinCollection joins = new OrderByJoinCollection(); using( StringReader reader = new StringReader(expression) ) { OPathLexer lexer = new OPathLexer(reader); lexer.Lex(); while( lexer.CurrentToken != null ) { OrderByItem item = ParseItem(lexer, entity, joins, null); items.Add(item); } if( lexer.LastToken != null && lexer.LastToken.Type == TokenType.COMMA ) { throw new OPathException("A trailing comma was detected in sort expression '" + expression + "'."); } } return new OrderBy(null, items, joins); }
private OrderByItem ParseItem(OPathLexer lexer, EntityMap entity, OrderByJoinCollection joins, OrderByJoin parentJoin) { Yytoken token = lexer.CurrentToken; if (token.Type != TokenType.IDENT) { throw new OPathException("'" + token.Text + "' encountered where a property or relationship was expected in sort expression '" + _expression + "'."); } string propertyName = token.Text; token = lexer.Lex(); if (token == null) { FieldMap field; try { field = entity.GetFieldMap(propertyName); } catch { throw new OPathException(string.Format("The specified property '{0}' in the sort '{1}' is not defined in the entity map for type '{2}'.", propertyName, _expression, entity.Type)); } return(new OrderByItem(propertyName, field, true, parentJoin)); } if (token.Type != TokenType.PERIOD) { if (token.Type != TokenType.ASCEND && token.Type != TokenType.DESCEND && token.Type != TokenType.COMMA) { throw new OPathException("'" + token.Text + "' is not valid in sort expression '" + _expression + "'."); } FieldMap field; try { field = entity.GetFieldMap(propertyName); } catch { throw new OPathException(string.Format("The specified property '{0}' in the sort '{1}' is not defined in the entity map for type '{2}'.", propertyName, _expression, entity.Type)); } bool ascending = (token.Type != TokenType.DESCEND); if (token.Type != TokenType.COMMA) { lexer.MoveToNext(); } lexer.MoveToNext(); return(new OrderByItem(propertyName, field, ascending, parentJoin)); } else // dot operator (.) { token = lexer.Lex(); if (token == null) { throw new OPathException("End of expression encountered where a property or relationship was expected in sort expression '" + _expression + "'."); } if (token.Type != TokenType.IDENT) { throw new OPathException("'" + token.Text + "' encountered where a property or relationship was expected in sort expression '" + _expression + "'."); } RelationMap relation = entity.Relation(propertyName); if (relation == null) { throw new OPathException(string.Format("The specified relationship '{0}' in the sort expression '{1}' is not defined in the entity map for type '{2}'.", propertyName, _expression, entity.Type)); } if (relation.Relationship != Relationship.Parent) { throw new Exception("Relationship '" + relation.Alias + "' is not a ManyToOne relation. Only ManyToOne relations are allowed in sort expressions."); } EntityMap relEntity = _maps[relation.Type]; OrderByJoin join = joins[propertyName]; if (join == null) { join = new OrderByJoin(relation); joins.Add(join); } // recursive call return(ParseItem(lexer, relEntity, join.NestedJoins, join)); } }
public Expression Parse(string expression) { // reset our member vars _args.Clear(); _ops.Clear(); _parameterCount = 0; if (expression == null) { expression = string.Empty; } using (StringReader reader = new StringReader(expression)) { OPathLexer lexer = new OPathLexer(reader); Yytoken token; while ((token = lexer.Lex()) != null) { #if DEBUG_PARSER Debug.WriteLine("----------------------------------------"); DumpStacks("Starting Token: " + token.ToString()); #endif ProcessToken(token, lexer); #if DEBUG_PARSER Debug.WriteLine("----------------------------------------\n"); Debug.WriteLine(""); #endif } } #if DEBUG_PARSER Debug.WriteLine("----------------------------------------"); Debug.WriteLine("Starting Final Reduction"); #endif // see if we have a empty expression if (_args.Count == 0) { return(new Empty()); } // reduce remaining operators on the stack while (_ops.Count > 0) { Reduce(); // reduce the stacks } #if DEBUG_PARSER Debug.WriteLine("----------------------------------------"); Debug.WriteLine(""); #endif // expression better be completely reduced if (_args.Count > 1) { throw new OPathException("Expression is not valid."); } // return root of the tree return((Expression)_args.Pop()); }
private void ProcessToken(Yytoken token, OPathLexer lexer) { switch (token.Type) { case TokenType.IDENT: { Property node = new Property(token.Text, new Context()); _args.Push(node); break; } case TokenType.PARENT: { if (lexer.NextToken == null || lexer.NextToken.Type != TokenType.PERIOD) { throw new OPathException("Position " + token.Position + ": Parent relationship operator must be followed by a dot operator."); } Parent node = new Parent(new Context()); _args.Push(node); break; } case TokenType.PARAM: { Parameter node = new Parameter(_parameterCount); _parameterCount += 1; _args.Push(node); break; } case TokenType.CONST: { Literal node = new Literal(token.Value); _args.Push(node); break; } case TokenType.PERIOD: { Yytoken last = lexer.LastToken; Yytoken next = lexer.NextToken; if (last == null || next == null) { throw new OPathException("Position " + token.Position + ": The dot operator (.) cannot be at the very beginning or end of an expression."); } // dot operator not valid unless it follows a property/relationship identifier, parent operator, or filter if (last.Type != TokenType.IDENT && last.Type != TokenType.PARENT && last.Type != TokenType.RBRACE) { throw new OPathException("Position " + token.Position + ": Dot operators cannot be used in this way."); } // parent operator cannot be after a property/relationship identifer if (next.Type == TokenType.PARENT && last.Type != TokenType.PARENT) { throw new OPathException("Position " + token.Position + ": Parent relationship operators (^) cannot be used in this way."); } goto case TokenType.MODULO; } case TokenType.MINUS: { // change the token to a negation unless followed by a token that supports subtraction Yytoken last = lexer.LastToken; if (last == null || (last.Type != TokenType.CONST && last.Type != TokenType.IDENT && last.Type != TokenType.PARAM && last.Type != TokenType.RPAREN)) { token.Type = TokenType.NEGATE; } goto case TokenType.COMMA; } case TokenType.COMMA: case TokenType.OP_EQ: case TokenType.OP_NE: case TokenType.OP_GT: case TokenType.OP_GE: case TokenType.OP_LT: case TokenType.OP_LE: case TokenType.OP_LIKE: case TokenType.AND: case TokenType.OR: case TokenType.NOT: case TokenType.PLUS: case TokenType.MULTIPLY: case TokenType.DIVIDE: case TokenType.MODULO: { if (_ops.Count == 0 || ComparePrecedence(token, (Yytoken)_ops.Peek()) > 0) // new token has higher precedence { _ops.Push(token); } else // new token has lower or same precedence { Reduce(); // process this token again ProcessToken(token, lexer); // note: recursive call } break; } case TokenType.OP_IN: case TokenType.FN_ISNULL: case TokenType.FN_LEN: case TokenType.FN_TRIM: case TokenType.FN_LEFT: case TokenType.FN_RIGHT: case TokenType.FN_SUBSTR: case TokenType.FN_UPPER: case TokenType.FN_LOWER: case TokenType.FN_EXISTS: { Yytoken nextToken = lexer.Lex(); if (nextToken.Type != TokenType.LPAREN) { throw new OPathException("Function '" + token.Text + "' must be followed by an opening parenthesis."); } _ops.Push(nextToken); _ops.Push(token); break; } case TokenType.LPAREN: case TokenType.LBRACE: { _ops.Push(token); break; } case TokenType.RPAREN: { if (_ops.Count == 0) { throw new OPathException("Closing parenthesis encountered without matching opening parenthesis."); } if (((Yytoken)_ops.Peek()).Type == TokenType.LPAREN) { _ops.Pop(); // "cancel" the operator pair } else { Reduce(); // process this token again ProcessToken(token, lexer); // note: recursive call } break; } case TokenType.RBRACE: { if (_ops.Count == 0) { throw new OPathException("Closing brace encountered without matching opening brace."); } if (((Yytoken)_ops.Peek()).Type == TokenType.LBRACE) { Reduce(); // a final reduce to "cancel" the pair } else { Reduce(); // process this token again ProcessToken(token, lexer); // note: recursive call } break; } default: { throw new NotSupportedException("Token type " + token.Type + " at position " + token.Position + " in expression is not supported."); } } #if DEBUG_PARSER DumpStacks("\nAfter Token: " + token.ToString()); #endif }
private Yytoken[] Lex(string value) { ArrayList tokens = new ArrayList(); using( StringReader reader = new StringReader(value) ) { OPathLexer lexer = new OPathLexer(reader); Yytoken token; while( (token = lexer.Lex()) != null ) { tokens.Add(token); } } return (Yytoken[])tokens.ToArray(typeof(Yytoken)); }
private OrderByItem ParseItem(OPathLexer lexer, EntityMap entity, OrderByJoinCollection joins, OrderByJoin parentJoin) { Yytoken token = lexer.CurrentToken; if( token.Type != TokenType.IDENT ) { throw new OPathException("'" + token.Text + "' encountered where a property or relationship was expected in sort expression '" + _expression + "'."); } string propertyName = token.Text; token = lexer.Lex(); if( token == null ) { FieldMap field; try { field = entity.GetFieldMap(propertyName); } catch { throw new OPathException(string.Format("The specified property '{0}' in the sort '{1}' is not defined in the entity map for type '{2}'.", propertyName, _expression, entity.Type)); } return new OrderByItem(propertyName, field, true, parentJoin); } if( token.Type != TokenType.PERIOD ) { if( token.Type != TokenType.ASCEND && token.Type != TokenType.DESCEND && token.Type != TokenType.COMMA ) { throw new OPathException("'" + token.Text + "' is not valid in sort expression '" + _expression + "'."); } FieldMap field; try { field = entity.GetFieldMap(propertyName); } catch { throw new OPathException(string.Format("The specified property '{0}' in the sort '{1}' is not defined in the entity map for type '{2}'.", propertyName, _expression, entity.Type)); } bool ascending = (token.Type != TokenType.DESCEND); if( token.Type != TokenType.COMMA ) { lexer.MoveToNext(); } lexer.MoveToNext(); return new OrderByItem(propertyName, field, ascending, parentJoin); } else // dot operator (.) { token = lexer.Lex(); if( token == null ) { throw new OPathException("End of expression encountered where a property or relationship was expected in sort expression '" + _expression + "'."); } if( token.Type != TokenType.IDENT ) { throw new OPathException("'" + token.Text + "' encountered where a property or relationship was expected in sort expression '" + _expression + "'."); } RelationMap relation = entity.Relation(propertyName); if( relation == null ) { throw new OPathException(string.Format("The specified relationship '{0}' in the sort expression '{1}' is not defined in the entity map for type '{2}'.", propertyName, _expression, entity.Type)); } if( relation.Relationship != Relationship.Parent ) { throw new Exception("Relationship '" + relation.Alias + "' is not a ManyToOne relation. Only ManyToOne relations are allowed in sort expressions."); } EntityMap relEntity = _maps[relation.Type]; OrderByJoin join = joins[propertyName]; if( join == null ) { join = new OrderByJoin(relation); joins.Add(join); } // recursive call return ParseItem(lexer, relEntity, join.NestedJoins, join); } }
public Expression Parse(string expression) { // reset our member vars _args.Clear(); _ops.Clear(); _parameterCount = 0; if( expression == null ) { expression = string.Empty; } using( StringReader reader = new StringReader(expression) ) { OPathLexer lexer = new OPathLexer(reader); Yytoken token; while( (token = lexer.Lex()) != null ) { #if DEBUG_PARSER Debug.WriteLine("----------------------------------------"); DumpStacks("Starting Token: " + token.ToString()); #endif ProcessToken(token, lexer); #if DEBUG_PARSER Debug.WriteLine("----------------------------------------\n"); Debug.WriteLine(""); #endif } } #if DEBUG_PARSER Debug.WriteLine("----------------------------------------"); Debug.WriteLine("Starting Final Reduction"); #endif // see if we have a empty expression if( _args.Count == 0 ) { return new Empty(); } // reduce remaining operators on the stack while( _ops.Count > 0 ) { Reduce(); // reduce the stacks } #if DEBUG_PARSER Debug.WriteLine("----------------------------------------"); Debug.WriteLine(""); #endif // expression better be completely reduced if( _args.Count > 1 ) { throw new OPathException("Expression is not valid."); } // return root of the tree return (Expression)_args.Pop(); }
private void ProcessToken(Yytoken token, OPathLexer lexer) { switch( token.Type ) { case TokenType.IDENT: { Property node = new Property(token.Text, new Context()); _args.Push(node); break; } case TokenType.PARENT: { if( lexer.NextToken == null || lexer.NextToken.Type != TokenType.PERIOD ) { throw new OPathException("Position " + token.Position + ": Parent relationship operator must be followed by a dot operator."); } Parent node = new Parent(new Context()); _args.Push(node); break; } case TokenType.PARAM: { Parameter node = new Parameter(_parameterCount); _parameterCount += 1; _args.Push(node); break; } case TokenType.CONST: { Literal node = new Literal(token.Value); _args.Push(node); break; } case TokenType.PERIOD: { Yytoken last = lexer.LastToken; Yytoken next = lexer.NextToken; if( last == null || next == null ) { throw new OPathException("Position " + token.Position + ": The dot operator (.) cannot be at the very beginning or end of an expression."); } // dot operator not valid unless it follows a property/relationship identifier, parent operator, or filter if( last.Type != TokenType.IDENT && last.Type != TokenType.PARENT && last.Type != TokenType.RBRACE ) { throw new OPathException("Position " + token.Position + ": Dot operators cannot be used in this way."); } // parent operator cannot be after a property/relationship identifer if( next.Type == TokenType.PARENT && last.Type != TokenType.PARENT ) { throw new OPathException("Position " + token.Position + ": Parent relationship operators (^) cannot be used in this way."); } goto case TokenType.MODULO; } case TokenType.MINUS: { // change the token to a negation unless followed by a token that supports subtraction Yytoken last = lexer.LastToken; if( last == null || (last.Type != TokenType.CONST && last.Type != TokenType.IDENT && last.Type != TokenType.PARAM && last.Type != TokenType.RPAREN) ) { token.Type = TokenType.NEGATE; } goto case TokenType.COMMA; } case TokenType.COMMA: case TokenType.OP_EQ: case TokenType.OP_NE: case TokenType.OP_GT: case TokenType.OP_GE: case TokenType.OP_LT: case TokenType.OP_LE: case TokenType.OP_LIKE: case TokenType.AND: case TokenType.OR: case TokenType.NOT: case TokenType.PLUS: case TokenType.MULTIPLY: case TokenType.DIVIDE: case TokenType.MODULO: { if( _ops.Count == 0 || ComparePrecedence(token, (Yytoken)_ops.Peek()) > 0 ) // new token has higher precedence { _ops.Push(token); } else // new token has lower or same precedence { Reduce(); // process this token again ProcessToken(token, lexer); // note: recursive call } break; } case TokenType.OP_IN: case TokenType.FN_ISNULL: case TokenType.FN_LEN: case TokenType.FN_TRIM: case TokenType.FN_LEFT: case TokenType.FN_RIGHT: case TokenType.FN_SUBSTR: case TokenType.FN_UPPER: case TokenType.FN_LOWER: case TokenType.FN_EXISTS: { Yytoken nextToken = lexer.Lex(); if( nextToken.Type != TokenType.LPAREN ) { throw new OPathException("Function '" + token.Text + "' must be followed by an opening parenthesis."); } _ops.Push(nextToken); _ops.Push(token); break; } case TokenType.LPAREN: case TokenType.LBRACE: { _ops.Push(token); break; } case TokenType.RPAREN: { if( _ops.Count == 0 ) throw new OPathException("Closing parenthesis encountered without matching opening parenthesis."); if( ((Yytoken)_ops.Peek()).Type == TokenType.LPAREN ) { _ops.Pop(); // "cancel" the operator pair } else { Reduce(); // process this token again ProcessToken(token, lexer); // note: recursive call } break; } case TokenType.RBRACE: { if( _ops.Count == 0 ) throw new OPathException("Closing brace encountered without matching opening brace."); if( ((Yytoken)_ops.Peek()).Type == TokenType.LBRACE ) { Reduce(); // a final reduce to "cancel" the pair } else { Reduce(); // process this token again ProcessToken(token, lexer); // note: recursive call } break; } default: { throw new NotSupportedException("Token type " + token.Type + " at position " + token.Position + " in expression is not supported."); } } #if DEBUG_PARSER DumpStacks("\nAfter Token: " + token.ToString()); #endif }