Skip to content
adqlGrammar.jj 74.4 KiB
Newer Older
				if (debug)		te.printStackTrace(System.err);
				System.exit(4);
			}

		}catch(IOException ioe){
			System.err.println("\n((X)) Error while reading the file \""+file+"\": "+ioe.getMessage()+" ((X))");
			if (debug)		ioe.printStackTrace(System.err);
			System.exit(2);
		}
    	
    }
							/* ########### */
							/* # GRAMMAR # */
							/* ########### */
/* ******************** */
/* Characters to ignore */
/* ******************** */
SKIP : { < " " | "\t" | "\n" | "\r" | "\r\n" > }

/* ************************************************************************** */
/* Reserved SQL words                                                         */
/*                                                                            */
/* NOTE:                                                                      */
/*   This list is the one provided by the ADQL-2.0 standard after removal of  */
/*   all ADQL used words (e.g. SELECT, AS, LIKE, AVG, ABS, COS, POINT).       */
/*   (see ParseException.initialise(Token, int[][], String[]) for more        */
/*   details)                                                                 */
/* ************************************************************************** */

TOKEN : {
	< SQL_RESERVED_WORD: ("ABSOLUTE"|"ACTION"|"ADD"|"ALLOCATE"|"ALTER"|"ANY"|"ARE"|"ASSERTION"|"AT"|"AUTHORIZATION"|"BEGIN"|"BIT"|"BIT_LENGTH"|"BOTH"|"CASCADE"|"CASCADED"|"CASE"|"CAST"|"CATALOG"|"CHAR"|"CHARACTER"|"CHAR_LENGTH"|"CHARACTER_LENGTH"|"CHECK"|"CLOSE"|"COALESCE"|"COLLATE"|"COLLATION"|"COLUMN"|"COMMIT"|"CONNECT"|"CONNECTION"|"CONSTRAINT"|"CONSTRAINTS"|"CONTINUE"|"CONVERT"|"CORRESPONDING"|"CREATE"|"CURRENT"|"CURRENT_DATE"|"CURRENT_TIME"|"CURRENT_TIMESTAMP"|"CURRENT_USER"|"CURSOR"|"DATE"|"DAY"|"DEALLOCATE"|"DECIMAL"|"DECLARE"|"DEFAULT"|"DEFERRABLE"|"DEFERRED"|"DELETE"|"DESCRIBE"|"DESCRIPTOR"|"DIAGNOSTICS"|"DISCONNECT"|"DOMAIN"|"DOUBLE"|"DROP"|"ELSE"|"END"|"END-EXEC"|"ESCAPE"|"EXCEPT"|"EXCEPTION"|"EXEC"|"EXECUTE"|"EXTERNAL"|"EXTRACT"|"FALSE"|"FETCH"|"FIRST"|"FLOAT"|"FOR"|"FOREIGN"|"FOUND"|"GET"|"GLOBAL"|"GO"|"GOTO"|"GRANT"|"HOUR"|"IDENTITY"|"IMMEDIATE"|"INDICATOR"|"INITIALLY"|"INPUT"|"INSENSITIVE"|"INSERT"|"INT"|"INTEGER"|"INTERSECT"|"INTERVAL"|"INTO"|"ISOLATION"|"KEY"|"LANGUAGE"|"LAST"|"LEADING"|"LEVEL"|"LOCAL"|"LOWER"|"MATCH"|"MINUTE"|"MODULE"|"MONTH"|"NAMES"|"NATIONAL"|"NCHAR"|"NEXT"|"NO"|"NULLIF"|"NUMERIC"|"OCTET_LENGTH"|"OF"|"ONLY"|"OPEN"|"OPTION"|"OUTPUT"|"OVERLAPS"|"PAD"|"PARTIAL"|"POSITION"|"PRECISION"|"PREPARE"|"PRESERVE"|"PRIMARY"|"PRIOR"|"PRIVILEGES"|"PROCEDURE"|"PUBLIC"|"READ"|"REAL"|"REFERENCES"|"RELATIVE"|"RESTRICT"|"REVOKE"|"ROLLBACK"|"ROWS"|"SCHEMA"|"SCROLL"|"SECOND"|"SECTION"|"SESSION"|"SESSION_USER"|"SET"|"SIZE"|"SMALLINT"|"SOME"|"SPACE"|"SQL"|"SQLCODE"|"SQLERROR"|"SQLSTATE"|"SUBSTRING"|"SYSTEM_USER"|"TABLE"|"TEMPORARY"|"THEN"|"TIME"|"TIMESTAMP"|"TIMEZONE_HOUR"|"TIMEZONE_MINUTE"|"TO"|"TRAILING"|"TRANSACTION"|"TRANSLATE"|"TRANSLATION"|"TRIM"|"TRUE"|"UNION"|"UNIQUE"|"UNKNOWN"|"UPDATE"|"UPPER"|"USAGE"|"USER"|"VALUE"|"VALUES"|"VARCHAR"|"VARYING"|"VIEW"|"WHEN"|"WHENEVER"|"WITH"|"WORK"|"WRITE"|"YEAR"|"ZONE") >
}

/* *********** */
/* Punctuation */
/* *********** */
TOKEN : {
	< LEFT_PAR: "(" >
|	< RIGHT_PAR: ")" > 
|	< DOT: "." >
|	< COMMA: "," >
|	< EOQ: ";">
|	< CONCAT: "||" >
}

/* ******************** */
/* Arithmetic operators */
/* ******************** */
TOKEN : {
	< PLUS: "+" >
|	< MINUS: "-" >
|	< ASTERISK: "*" >
|	< DIVIDE: "/" >
}

/* ******************** */
/* Comparison operators */
/* ******************** */
TOKEN : {
	< EQUAL: "=" >
|	< NOT_EQUAL: "<>" | "!=" >
|	< LESS_THAN: "<" >
|	< LESS_EQUAL_THAN: "<=" >
|	< GREATER_THAN: ">" >
|	< GREATER_EQUAL_THAN: ">=" >
}

/* *************** */
/* SELECT's tokens */
/* *************** */
TOKEN : {
	< SELECT: "SELECT" >
|	< QUANTIFIER: "DISTINCT" | "ALL" >
|	< TOP: "TOP" >
}

/* ************* */
/* FROM's tokens */
/* ************* */
TOKEN : {
	< FROM: "FROM" >
|	< AS: "AS" >
|	< NATURAL: "NATURAL" >
|	< INNER: "INNER" >
|	< OUTER: "OUTER" >
|	< RIGHT: "RIGHT" >
|	< LEFT: "LEFT" >
|	< FULL: "FULL" >
|	< JOIN: "JOIN" >
|	< ON: "ON" >
|	< USING: "USING" >
}

/* ************** */
/* WHERE's tokens */
/* ************** */
TOKEN : {
	< WHERE: "WHERE" >
|	< AND: "AND" >
|	< OR: "OR" >
|	< NOT: "NOT" >
|	< IS: "IS" >
|	< NULL: "NULL" >
|	< BETWEEN: "BETWEEN" >
|	< LIKE: "LIKE" >
|	< IN: "IN" >
|	< EXISTS: "EXISTS" >
}

/* ********************* */
/* Other clauses' tokens */
/* ********************* */
TOKEN : {
	< BY: "BY" >
|	< GROUP: "GROUP" >
|	< HAVING: "HAVING" >
|	< ORDER: "ORDER" >
|	< ASC: "ASC" >
|	< DESC: "DESC" >
}

/* ************* */
/* SQL functions */
/* ************* */
TOKEN : {
	< AVG: "AVG" >
|	< MAX: "MAX" >
|	< MIN: "MIN" >
|	< SUM: "SUM" >
|	< COUNT: "COUNT" >
}

/* ************** */
/* ADQL functions */
/* ************** */
TOKEN : {
	< BOX: "BOX" >
|	< CENTROID: "CENTROID" >
|	< CIRCLE: "CIRCLE" >
|	< POINT: "POINT" >
|	< POLYGON: "POLYGON" >
|	< REGION: "REGION" >

|	< CONTAINS: "CONTAINS" >
|	< INTERSECTS: "INTERSECTS" >
|	< AREA: "AREA" >
|	< COORD1: "COORD1" >
|	< COORD2: "COORD2" >
|	< COORDSYS: "COORDSYS" >
|	< DISTANCE: "DISTANCE" >
}

/* ********************** */
/* Mathematical functions */
/* ********************** */
TOKEN : {
	< ABS: "ABS" >
|	< CEILING: "CEILING" >
|	< DEGREES: "DEGREES" >
|	< EXP: "EXP" >
|	< FLOOR: "FLOOR" >
|	< LOG: "LOG" >
|	< LOG10: "LOG10" >
|	< MOD: "MOD" >
|	< PI: "PI" >
|	< POWER: "POWER" >
|	< RADIANS: "RADIANS" >
|	< RAND: "RAND" >
|	< ROUND: "ROUND" >
|	< SQRT: "SQRT" >
|	< TRUNCATE: "TRUNCATE" >
}

/* ************************* */
/* Trigonometrical functions */
/* ************************* */
TOKEN : {
	< ACOS: "ACOS" >
|	< ASIN: "ASIN" >
|	< ATAN: "ATAN" >
|	< ATAN2: "ATAN2" >
|	< COS: "COS" >
|	< COT: "COT" >
|	< SIN: "SIN" >
|	< TAN: "TAN" >
SKIP : { < <MINUS><MINUS> (~["\n","\r"])* ("\n"|"\r"|"\r\n")? > }

/* ****** */
/* String */
/* ****** */
<DEFAULT> MORE : { "'" : WithinString }
<WithinString> MORE : { < ~["'"] | ("''") > }
<WithinString> TOKEN : { < STRING_LITERAL: "'" >: DEFAULT }

/* *************** */
/* Primary numbers */
/* *************** */
TOKEN : {
	< SCIENTIFIC_NUMBER: (<UNSIGNED_FLOAT>|<UNSIGNED_INTEGER>) "E" (<PLUS>|<MINUS>)? <UNSIGNED_INTEGER> >
|	< UNSIGNED_FLOAT: (<UNSIGNED_INTEGER> <DOT> (<UNSIGNED_INTEGER>)?) | (<DOT> <UNSIGNED_INTEGER>) >
|	< UNSIGNED_INTEGER: (<DIGIT>)+ >
|	< #DIGIT: ["0"-"9"] >
}

/* ************************************************* */
/* Identifier (column, tables, ...) */
/* ************************************************* */
<DEFAULT> MORE : { "\"" : WithinDelimitedId }
<WithinDelimitedId> MORE : { < ~["\""] | ("\"\"") > }
<WithinDelimitedId> TOKEN : { < DELIMITED_IDENTIFIER: "\"" >: DEFAULT }

TOKEN : {
	< REGULAR_IDENTIFIER_CANDIDATE: ((<Letter>)+ (<DIGIT> | <Letter>)* | (<DIGIT>)+ <Letter> (<DIGIT> | <Letter>)*) >
|	< #Letter: ["a"-"z","A"-"Z","_","?","$","@","^","#","`","~","[","]","{","}"] >
}

							/* ########## */
							/* # SYNTAX # */
							/* ########## */
							
/* ******************* */
/* GENERAL ADQL SYNTAX */
/* ******************* */
/**
* Parses the ADQL query given at the parser creation or in the {@link ADQLParser#ReInit(java.io.InputStream)}
* or in the <i>parseQuery</i> functions.
*
* @return					The object representation of the query.
* @throws ParseException	If the query syntax is incorrect.
*/
ADQLQuery Query(): {ADQLQuery q = null;}{
	q=QueryExpression() (<EOF> | <EOQ>)
	{
		// check the query:
		if (queryChecker != null)
			queryChecker.check(q);
			
		return q;
	}
ADQLQuery QueryExpression(): {TextPosition endPos = null;} {
	{
		try{
			// create the query:
			query = queryFactory.createQuery();
			stackQuery.push(query);
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
	Select()
	From()     {endPos = query.getFrom().getPosition();}
	[Where()   {endPos = query.getWhere().getPosition();}]
	[GroupBy() {endPos = query.getGroupBy().getPosition();}]
	[Having()  {endPos = query.getHaving().getPosition();}]
	[OrderBy() {endPos = query.getOrderBy().getPosition();}]
		// set the position of the query:
		query.setPosition(new TextPosition(query.getSelect().getPosition(), endPos));
		
		// get the previous query (!= null if the current query is a sub-query):
		ADQLQuery previousQuery = stackQuery.pop();
		if (stackQuery.isEmpty())
			query = null;
		else
			query = stackQuery.peek();
			
		return previousQuery;
	}
}

ADQLQuery SubQueryExpression(): {ADQLQuery q = null; Token start, end;} {
	start=<LEFT_PAR> q=QueryExpression() end=<RIGHT_PAR>
	{
		q.setPosition(new TextPosition(start, end));
		return q;
	}
void Select(): {ClauseSelect select = query.getSelect(); SelectItem item=null; Token start,t = null;} {
	start=<SELECT>
	[t=<QUANTIFIER> {select.setDistinctColumns(t.image.equalsIgnoreCase("DISTINCT"));}]
	[<TOP> t=<UNSIGNED_INTEGER>
	 {
	  try{
	  	select.setLimit(Integer.parseInt(t.image));
	  }catch(NumberFormatException nfe){
	  	throw new ParseException("[l."+t.beginLine+";c."+t.beginColumn+"] The TOP limit (\""+t.image+"\") isn't a regular unsigned integer !");
	  }
	 }
	]
	
	item=SelectItem() {select.add(item);}
	(<COMMA> item=SelectItem() {select.add(item);})*
	{
		TextPosition lastItemPos = query.getSelect().get(query.getSelect().size()-1).getPosition();
		select.setPosition(new TextPosition(start.beginLine, start.beginColumn, lastItemPos.endLine, lastItemPos.endColumn));
	}
SelectItem SelectItem(): {IdentifierItems identifiers = new IdentifierItems(true); IdentifierItem id = null, label = null; ADQLOperand op = null; SelectItem item; Token starToken;} {
		( starToken=<ASTERISK>
		  {
		    item = new SelectAllColumns(query);
		    item.setPosition(new TextPosition(starToken));
		    return item;
		  }
		)
	|LOOKAHEAD(7)
		(
			id=Identifier() <DOT> { identifiers.append(id); }
			(
				id=Identifier() <DOT> { identifiers.append(id); }
				(
					id=Identifier() <DOT> { identifiers.append(id); }
				)?
			)?
				try{
					item = new SelectAllColumns( queryFactory.createTable(identifiers, null) );
					TextPosition firstPos = identifiers.get(0).position;
					item.setPosition(new TextPosition(firstPos.beginLine, firstPos.beginColumn, starToken.endLine, (starToken.endColumn < 0) ? -1 : (starToken.endColumn + 1)));
					return item;
				}catch(Exception ex) {
					throw generateParseException(ex);
				}
			}
		)
		
	| 
		(op=ValueExpression()[[<AS>] label=Identifier()])
			item = queryFactory.createSelectItem(op, (label==null)?null:label.identifier);
			if (label != null){
				item.setCaseSensitive(label.caseSensitivity);
				item.setPosition(new TextPosition(op.getPosition(), label.position));
			}else
				item.setPosition(new TextPosition(op.getPosition()));
			return item;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

void From():{FromContent content = null, content2 = null;}{
	try{
		<FROM> content=TableRef()
		(<COMMA> content2=TableRef()
		 {
		   TextPosition startPos = content.getPosition(), endPos = content2.getPosition();
		   content = queryFactory.createJoin(JoinType.CROSS, content, content2);
		   content.setPosition(new TextPosition(startPos, endPos));
		 }
		)*
		{ query.setFrom(content); }
	}catch(Exception ex){
		throw generateParseException(ex);
	}
}

void Where(): {ClauseConstraints where = query.getWhere(); ADQLConstraint condition; Token start;} {
	start=<WHERE> ConditionsList(where)
	{
	  TextPosition endPosition = where.getPosition();
	  where.setPosition(new TextPosition(start.beginLine, start.beginColumn, endPosition.endLine, endPosition.endColumn));
	}
void GroupBy(): {ClauseADQL<ADQLColumn> groupBy = query.getGroupBy(); ADQLColumn colRef = null; Token start;} {
	start=<GROUP> <BY> colRef=Column() { groupBy.add(colRef); }
	( <COMMA> colRef=Column() { groupBy.add(colRef); } )*
	{ groupBy.setPosition(new TextPosition(start.beginLine, start.beginColumn, colRef.getPosition().endLine, colRef.getPosition().endColumn)); }
void Having(): {ClauseConstraints having = query.getHaving(); Token start;} {
	start=<HAVING> ConditionsList(having)
	{
	  TextPosition endPosition = having.getPosition();
	  having.setPosition(new TextPosition(start.beginLine, start.beginColumn, endPosition.endLine, endPosition.endColumn));
	}
void OrderBy(): {ClauseADQL<ADQLOrder> orderBy = query.getOrderBy(); ADQLOrder order = null; Token start;} {
	start=<ORDER> <BY> order=OrderItem() {orderBy.add(order);}
	( <COMMA> order=OrderItem() {orderBy.add(order);} )*
	{ orderBy.setPosition(new TextPosition(start, token)); }

/* *************************** */
/* COLUMN AND TABLE REFERENCES */
/* *************************** */
IdentifierItem Identifier(): {Token t;} {
	(
		t=<REGULAR_IDENTIFIER_CANDIDATE>
		{
		  	testRegularIdentifier(t);
			return new IdentifierItem(t, false);
		}
	|
		t=<DELIMITED_IDENTIFIER>
		{ return new IdentifierItem(t, true); }
	)
}

/**
 * Extracts the name of a table with its possible catalog and schema prefixes.
 * 
 * @return A {@link IdentifierItems} which contains at most three items: catalogName, schemaName and tableName.
 */
IdentifierItems TableName(): {IdentifierItems identifiers=new IdentifierItems(true); IdentifierItem id=null;} {
	(
		id=Identifier() {identifiers.append(id);}						// catalog
		(LOOKAHEAD(1) <DOT> id=Identifier() {identifiers.append(id);})?	// schema
		(LOOKAHEAD(1) <DOT> id=Identifier() {identifiers.append(id);})?	// table
	)
	{ return identifiers; }
}

/**
 * Extracts the name of a column with its possible catalog, schema and table prefixes.
 * 
 * @return A {@link IdentifierItems} which contains at most four items: catalogName, schemaName, tableName and columnName.
 */
IdentifierItems ColumnName(): {IdentifierItem id; IdentifierItems table=null, identifiers=new IdentifierItems(false);} {
	( id=Identifier() (LOOKAHEAD(1) <DOT> table=TableName())? )
	{
		identifiers.append(id);
		if (table != null){
			for(int i=0; i<table.size(); i++)
				identifiers.append(table.get(i));
		}
		return identifiers;
	}
}

ADQLColumn Column(): {IdentifierItems identifiers;} {
	identifiers = ColumnName()
	{
		try{
			return queryFactory.createColumn(identifiers);
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

ADQLOrder OrderItem(): {IdentifierItem identifier = null; Token ind = null, desc = null;}{
	(identifier=Identifier() | ind=<UNSIGNED_INTEGER>) (<ASC> | desc=<DESC>)?
			if (identifier != null){
				order = queryFactory.createOrder(identifier, desc!=null);
				order.setPosition(identifier.position);
			}else{
				order = queryFactory.createOrder(Integer.parseInt(ind.image), desc!=null);
				order.setPosition(new TextPosition(ind));
			}
			return order;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

FromContent SimpleTableRef(): {IdentifierItem alias = null; IdentifierItems identifiers = null; ADQLQuery subQuery = null; FromContent content = null; Token start,end;} {
	try{
		(
			identifiers=TableName() [[<AS>] alias=Identifier()]
			{
			  content = queryFactory.createTable(identifiers, alias);
			  if (alias == null)
			  	content.setPosition(new TextPosition(identifiers.get(0).position, identifiers.get(identifiers.size()-1).position));
			  else
			  	content.setPosition(new TextPosition(identifiers.get(0).position, alias.position));
			  return content;
			}
		|LOOKAHEAD(2)
			subQuery=SubQueryExpression() [<AS>] alias=Identifier()
			{
			  content = queryFactory.createTable(subQuery, alias);
			  if (alias == null)
			  	content.setPosition(new TextPosition(subQuery.getPosition()));
			  else
			  	content.setPosition(new TextPosition(subQuery.getPosition(), alias.position));
			  return content;
			}
			start=<LEFT_PAR> content=JoinedTable() end=<RIGHT_PAR>
			{
			  content.setPosition(new TextPosition(start, end));
			  return content;
			}
		)
	}catch(Exception ex){
		throw generateParseException(ex);
	}
}

FromContent TableRef(): { FromContent content; } {
	content=SimpleTableRef()
	( LOOKAHEAD(2) content=JoinSpecification(content) )*
	{ return content; }
}

FromContent JoinedTable(): { FromContent content; } {
	content=SimpleTableRef()
	( content=JoinSpecification(content) )+
	{ return content; }
}


ADQLJoin JoinSpecification(FromContent leftTable): { boolean natural = false; JoinType type = JoinType.INNER;  ClauseConstraints condition = new ClauseConstraints("ON"); ArrayList<ADQLColumn> lstColumns=new ArrayList<ADQLColumn>(); IdentifierItem id; FromContent rightTable; ADQLJoin join; Token lastPar;} {
			<NATURAL> {natural=true;} [<INNER> | ((<LEFT> {type = JoinType.OUTER_LEFT;}|<RIGHT> {type = JoinType.OUTER_RIGHT;}|<FULL> {type = JoinType.OUTER_FULL;}) [<OUTER>])] <JOIN> rightTable=SimpleTableRef()
			{
			  join = queryFactory.createJoin(type, leftTable, rightTable);
			  join.setPosition(new TextPosition(leftTable.getPosition(), rightTable.getPosition()));
			  return join;
			}
			[<INNER> | ((<LEFT> {type = JoinType.OUTER_LEFT;}|<RIGHT> {type = JoinType.OUTER_RIGHT;}|<FULL> {type = JoinType.OUTER_FULL;}) [<OUTER>])] <JOIN> rightTable=SimpleTableRef()
			(
				<ON> ConditionsList(condition)
				{
				  join = queryFactory.createJoin(type, leftTable, rightTable, condition);
				  join.setPosition(new TextPosition(leftTable.getPosition(), condition.getPosition()));
				  return join;
				}
			|
				<USING> <LEFT_PAR> id=Identifier()
						{ lstColumns.add( queryFactory.createColumn(id) ); }
						(
							<COMMA> id=Identifier()
							{ lstColumns.add( queryFactory.createColumn(id) ); }
						)* lastPar=<RIGHT_PAR>
				{
				  join = queryFactory.createJoin(type, leftTable, rightTable, lstColumns);
				  join.setPosition(new TextPosition(leftTable.getPosition().beginLine, leftTable.getPosition().beginColumn, lastPar.endLine, (lastPar.endColumn < 0) ? -1 : (lastPar.endColumn + 1)));
				  return join;
				}
			)
		)
	}catch(Exception ex){
		throw generateParseException(ex);
	}
}

/* ****** */
/* STRING */
/* ****** */
StringConstant String(): {Token t, start=null; String str=""; StringConstant cst;} {
	(t=<STRING_LITERAL>
	 {
	   	str += t.image.substring(1, t.image.length()-1).replaceAll("''", "'");
	   	if (start == null)
	   		start = t;
	 }
	)+
	{
		try{
		  cst = queryFactory.createStringConstant(str);
		  cst.setPosition(new TextPosition(start, t));
		  return cst;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

/* ************* */
/* NUMERIC TYPES */
/* ************* */
NumericConstant UnsignedNumeric(): {Token t; NumericConstant cst;} {
	(t=<SCIENTIFIC_NUMBER>
	| t=<UNSIGNED_FLOAT>
	| t=<UNSIGNED_INTEGER>)
		  cst = queryFactory.createNumericConstant(t.image);
		  cst.setPosition(new TextPosition(t));
		  return cst;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
NumericConstant UnsignedFloat(): {Token t; NumericConstant cst;} {
	(t=<UNSIGNED_INTEGER>
	| t=<UNSIGNED_FLOAT>)
	{
		try{
			cst = queryFactory.createNumericConstant(t.image);
		  	cst.setPosition(new TextPosition(t));
			return cst;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
NumericConstant SignedInteger(): {Token sign=null, number; NumericConstant cst;} {
	((sign=<PLUS>|sign=<MINUS>)? number=<UNSIGNED_INTEGER>)
		  	if (sign == null){		  		cst = queryFactory.createNumericConstant(number.image);
		  		cst.setPosition(new TextPosition(number));
		 	}else{		 		cst = queryFactory.createNumericConstant(sign.image+number.image);
		  		cst.setPosition(new TextPosition(sign, number));
		 	}
		 	return cst;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

/* *********** */
/* EXPRESSIONS */
/* *********** */
ADQLOperand NumericValueExpressionPrimary(): {ADQLColumn column; ADQLOperand op; Token left, right;} {
gmantele's avatar
gmantele committed
	try{
		(// unsigned_value_specification
gmantele's avatar
gmantele committed
		// column_reference
		| column=Column() {column.setExpectedType('N'); return column;}
gmantele's avatar
gmantele committed
		// set_function_specification
		| op=SqlFunction() {return op;}
		// LEFT_PAR value_expression RIGHT_PAR
		| (left=<LEFT_PAR> op=NumericExpression() right=<RIGHT_PAR>) { WrappedOperand wop = queryFactory.createWrappedOperand(op); wop.setPosition(new TextPosition(left, right)); return wop; })
	}catch(Exception ex){
		throw generateParseException(ex);
	}
}

ADQLOperand StringValueExpressionPrimary(): {StringConstant expr; ADQLColumn column; ADQLOperand op; Token left, right;} {
		  expr=String() {return expr;}
		// unsigned numeric
		| op=UnsignedNumeric() {return op;}
		// set_function_specification
		| op=SqlFunction() {return op;}
		| column=Column() {column.setExpectedType('*'); return column;}
		// LEFT_PAR value_expression RIGHT_PAR
		| (left=<LEFT_PAR> (op=ValueExpression()) right=<RIGHT_PAR>) { WrappedOperand wop = queryFactory.createWrappedOperand(op); wop.setPosition(new TextPosition(left, right)); return wop; })
gmantele's avatar
gmantele committed
	}catch(Exception ex){
		throw generateParseException(ex);
	}
ADQLOperand ValueExpression(): {ADQLOperand valueExpr = null; Token left, right; } {
	try{
		(LOOKAHEAD((<PLUS>|<MINUS>) | (Factor() (<PLUS>|<MINUS>|<ASTERISK>|<DIVIDE>))) valueExpr=NumericExpression()
		| LOOKAHEAD(<COORDSYS> | (StringFactor() <CONCAT>)) valueExpr=StringExpression()
		| LOOKAHEAD(<LEFT_PAR>) left=<LEFT_PAR> valueExpr=ValueExpression() right=<RIGHT_PAR> { valueExpr = queryFactory.createWrappedOperand(valueExpr); ((WrappedOperand)valueExpr).setPosition(new TextPosition(left, right)); }
		| LOOKAHEAD(<REGULAR_IDENTIFIER_CANDIDATE> <LEFT_PAR>) valueExpr=UserDefinedFunction()
		| LOOKAHEAD(2) valueExpr=GeometryValueFunction()
		| LOOKAHEAD(Column()) valueExpr=Column()
		| LOOKAHEAD(String()) valueExpr=StringFactor()
		| LOOKAHEAD(3) valueExpr=Factor()

		/* At this position in this switch, all possibilities (including
		 * Column()) have already been tested and failed.
		 * 
		 * So, this final choice actually aims to throw an error set with the
		 * current token and with an error message implying that a column name
		 * was expected (which is generally the case in an ADQL query).
		 *
		 * Note: This choice will generally be reached if an unexpected ADQL/SQL
		 *       word is ending the query. */
		| valueExpr=Column() )
		{return valueExpr;}
	}catch(Exception ex){
		throw generateParseException(ex);
	}

ADQLOperand NumericExpression(): {Token sign=null; ADQLOperand leftOp, rightOp=null;} {
	(leftOp=NumericTerm() ((sign=<PLUS> | sign=<MINUS>) rightOp=NumericExpression())?)
	{
	if (sign == null)
		return leftOp;
	else{
		try{
			Operation operation = queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp);
			operation.setPosition(new TextPosition(leftOp.getPosition(), rightOp.getPosition()));
			return operation;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
	}
}

ADQLOperand NumericTerm(): {Token sign=null; ADQLOperand leftOp, rightOp=null;} {
	(leftOp=Factor() ((sign=<ASTERISK> | sign=<DIVIDE>) rightOp=NumericTerm())?)
	{
	if (sign == null)
		return leftOp;
	else{
		try{
			Operation operation = queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp);
			operation.setPosition(new TextPosition(leftOp.getPosition(), rightOp.getPosition()));
			return operation;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
	}
}

ADQLOperand Factor(): {boolean negative = false; Token minusSign = null; ADQLOperand op;} {
		(<PLUS> | (minusSign=<MINUS> {negative = true;}))?
		(LOOKAHEAD(2) op=NumericFunction() | op=NumericValueExpressionPrimary())
			  	TextPosition position = op.getPosition();
				op = queryFactory.createNegativeOperand(op);
				NegativeOperand negativeOp = (NegativeOperand)op;
				if (minusSign != null)
					negativeOp.setPosition(new TextPosition(minusSign.beginLine, minusSign.beginColumn, position.endLine, position.endColumn));
				else
					negativeOp.setPosition(position);
			}catch(Exception ex){
				throw generateParseException(ex);
			}
		}
		
		return op;
	}
}

ADQLOperand StringExpression(): {ADQLOperand leftOp; ADQLOperand rightOp = null;} {
	leftOp=StringFactor()
	(
		<CONCAT>
		rightOp=StringFactor()
		{
			if (!(leftOp instanceof Concatenation)){
				try{
					ADQLOperand temp = leftOp;
					leftOp = queryFactory.createConcatenation();
					((Concatenation)leftOp).add(temp);
				}catch(Exception ex){
					throw generateParseException(ex);
				}
			}
			((Concatenation)leftOp).add(rightOp);
		}
	)*
	{
		if (leftOp instanceof Concatenation){
			Concatenation concat = (Concatenation)leftOp;
			concat.setPosition(new TextPosition(concat.get(0).getPosition(), concat.get(concat.size()-1).getPosition()));
		}
	  return leftOp;
	}

ADQLOperand StringFactor(): {ADQLOperand op;} {
	(op=ExtractCoordSys()
	| LOOKAHEAD(2) op=UserDefinedFunction() { ((UserDefinedFunction)op).setExpectedType('S'); }
	| op=StringValueExpressionPrimary())
	{return op;}
}

GeometryValue<GeometryFunction> GeometryExpression(): {ADQLColumn col = null; GeometryFunction gf = null;} {
	(col=Column() | gf=GeometryValueFunction())
	{
		if (col != null){
		  	col.setExpectedType('G');
			return new GeometryValue<GeometryFunction>(col);
			return new GeometryValue<GeometryFunction>(gf);
	}
}

/* ********************************** */
/* BOOLEAN EXPRESSIONS (WHERE clause) */
/* ********************************** */
ClauseConstraints ConditionsList(ClauseConstraints clause): {ADQLConstraint constraint = null; Token op = null; boolean notOp = false;} {
	try{
			if (notOp){
			  	TextPosition oldPos = constraint.getPosition();
				constraint = queryFactory.createNot(constraint);
				((NotConstraint)constraint).setPosition(new TextPosition(op.beginLine, op.beginColumn, oldPos.endLine, oldPos.endColumn));
			}
			if (clause instanceof ADQLConstraint)
				clause.add(constraint);
			else
				clause.add(constraint);
		}
		(
			(op=<AND> | op=<OR>)
			[<NOT> {notOp = true;}]
			constraint=Constraint()
			{
				if (notOp){
			  		TextPosition oldPos = constraint.getPosition();
					constraint = queryFactory.createNot(constraint);
					((NotConstraint)constraint).setPosition(new TextPosition(op.beginLine, op.beginColumn, oldPos.endLine, oldPos.endColumn));
				}
				if (clause instanceof ADQLConstraint)
					clause.add(op.image, constraint);
				else
					clause.add(op.image, constraint);
			}
		)*
	}catch(Exception ex){
		throw generateParseException(ex);
	}
	{
	  	if (!clause.isEmpty()){
	  		TextPosition start = clause.get(0).getPosition();
	  		TextPosition end = clause.get(clause.size()-1).getPosition();
			clause.setPosition(new TextPosition(start, end));
		}
		return clause;
	}
ADQLConstraint Constraint(): {ADQLConstraint constraint =  null; Token start, end;} {
	(LOOKAHEAD(<EXISTS> | ValueExpression()) constraint=Predicate()
		{
			try{
				constraint = queryFactory.createGroupOfConstraints();
			}catch(Exception ex){
				throw generateParseException(ex);
			}
		}
		ConditionsList((ConstraintsGroup)constraint)
		end=<RIGHT_PAR>
		{ ((ConstraintsGroup)constraint).setPosition(new TextPosition(start, end)); }
ADQLConstraint Predicate(): {ADQLQuery q=null; ADQLColumn column=null; ADQLOperand strExpr1=null, strExpr2=null; ADQLOperand op; Token start, notToken = null, end; ADQLConstraint constraint = null;} {
		(
		  (start=<EXISTS> q=SubQueryExpression()
			{
			  Exists e = queryFactory.createExists(q);
			  e.setPosition(new TextPosition(start.beginLine, start.beginColumn, q.getPosition().endLine, q.getPosition().endColumn));
			  return e;
			}
		  )
		| LOOKAHEAD(Column() <IS>)(column=Column() <IS> [notToken=<NOT>] end=<NULL>
		    {
		      IsNull in = queryFactory.createIsNull((notToken!=null), column);
		      in.setPosition(new TextPosition(column.getPosition().beginLine, column.getPosition().beginColumn, end.endLine, (end.endColumn < 0) ? -1 : (end.endColumn + 1)));
		      return in;
		    }
		   )
		| LOOKAHEAD(StringExpression() [<NOT>] <LIKE>) (strExpr1=StringExpression() [notToken=<NOT>] <LIKE> strExpr2=StringExpression()
		    {
		      Comparison comp = queryFactory.createComparison(strExpr1, (notToken==null)?ComparisonOperator.LIKE:ComparisonOperator.NOTLIKE, strExpr2);
		      comp.setPosition(new TextPosition(strExpr1.getPosition(), strExpr2.getPosition()));
		      return comp;
		    }
		  )
		| (op=ValueExpression()
			(// comparison_predicate
			(constraint=ComparisonEnd(op))
			// between predicate
			| LOOKAHEAD(2) constraint=BetweenEnd(op)
			// in_predicate
			| constraint=InEnd(op)
			)
	}catch(Exception ex){
		throw generateParseException(ex);
	}
	{return constraint;}
}

Comparison ComparisonEnd(ADQLOperand leftOp): {Token comp; ADQLOperand rightOp;} {
	((comp=<EQUAL> | comp=<NOT_EQUAL> | comp=<LESS_THAN> | comp=<LESS_EQUAL_THAN> | comp=<GREATER_THAN> | comp=<GREATER_EQUAL_THAN>) rightOp=ValueExpression())
	{
		try{
		  	Comparison comparison = queryFactory.createComparison(leftOp, ComparisonOperator.getOperator(comp.image), rightOp);
		  	comparison.setPosition(new TextPosition(leftOp.getPosition(), rightOp.getPosition()));
			return comparison;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

Between BetweenEnd(ADQLOperand leftOp): {Token start,notToken=null; ADQLOperand min, max;} {
	[notToken=<NOT>] start=<BETWEEN> min=ValueExpression() <AND> max=ValueExpression()
		  	Between bet = queryFactory.createBetween((notToken!=null), leftOp, min, max);
		  	if (notToken != null) start = notToken;
		  	bet.setPosition(new TextPosition(start.beginLine, start.beginColumn, max.getPosition().endLine, max.getPosition().endColumn));
			return bet;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

In InEnd(ADQLOperand leftOp): {Token not=null, start; ADQLQuery q = null; ADQLOperand item; Vector<ADQLOperand> items = new Vector<ADQLOperand>();} {
	[not=<NOT>] start=<IN>
	(LOOKAHEAD(2) q=SubQueryExpression()
	| (<LEFT_PAR> item=ValueExpression() {items.add(item);} (<COMMA> item=ValueExpression() {items.add(item);})* <RIGHT_PAR>))
	{
		try{
		  	In in;
		  	start = (not!=null) ? not : start;
			if (q != null){
				in = queryFactory.createIn(leftOp, q, not!=null);
				in.setPosition(new TextPosition(start.beginLine, start.beginColumn, q.getPosition().endLine, q.getPosition().endColumn));
			}else{
				ADQLOperand[] list = new ADQLOperand[items.size()];
				int i=0;
				for(ADQLOperand op : items)
					list[i++] = op;
				in = queryFactory.createIn(leftOp, list, not!=null);
				in.setPosition(new TextPosition(start.beginLine, start.beginColumn, list[list.length-1].getPosition().endLine, list[list.length-1].getPosition().endColumn));
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}


/* ************* */
/* SQL FUNCTIONS */
/* ************* */
SQLFunction SqlFunction(): {Token fct, all=null, distinct=null, end; ADQLOperand op=null; SQLFunction funct = null;}{
			(fct=<COUNT> <LEFT_PAR> [distinct=<QUANTIFIER>] (all=<ASTERISK> | op=ValueExpression()) end=<RIGHT_PAR>
			{
			  funct = queryFactory.createSQLFunction((all!=null)?SQLFunctionType.COUNT_ALL:SQLFunctionType.COUNT, op, distinct != null && distinct.image.equalsIgnoreCase("distinct"));
			  funct.setPosition(new TextPosition(fct, end));
			})
			((fct=<AVG> | fct=<MAX> | fct=<MIN> | fct=<SUM>) <LEFT_PAR> [distinct=<QUANTIFIER>] op=ValueExpression() end=<RIGHT_PAR> 
			{
			  funct = queryFactory.createSQLFunction(SQLFunctionType.valueOf(fct.image.toUpperCase()), op, distinct != null && distinct.image.equalsIgnoreCase("distinct"));
			  funct.setPosition(new TextPosition(fct, end));
			})
		)
	}catch(Exception ex){
		throw generateParseException(ex);
	}


/* ************** */
/* ADQL FUNCTIONS */
/* ************** */
ADQLOperand[] Coordinates(): {ADQLOperand[] ops = new ADQLOperand[2];} {
	ops[0]=NumericExpression() <COMMA> ops[1]=NumericExpression()
	{return ops;}
}

GeometryFunction GeometryFunction(): {Token fct=null, end; GeometryValue<GeometryFunction> gvf1, gvf2; GeometryValue<PointFunction> gvp1, gvp2; GeometryFunction gf = null; PointFunction p1=null, p2=null; ADQLColumn col1 = null, col2 = null;} {
	try{
		// predicate_geometry_function
		(
			((fct=<CONTAINS> | fct=<INTERSECTS>) <LEFT_PAR> gvf1=GeometryExpression() <COMMA> gvf2=GeometryExpression() end=<RIGHT_PAR>