Skip to content
adqlGrammar.jj 53.6 KiB
Newer Older
	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;} {
	try{
		(
			<NATURAL> {natural=true;} [<INNER> | ((<LEFT> {type = JoinType.OUTER_LEFT;}|<RIGHT> {type = JoinType.OUTER_RIGHT;}|<FULL> {type = JoinType.OUTER_FULL;}) [<OUTER>])] <JOIN> rightTable=TableRef()
			{
			  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=TableRef()
			(
				<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; String str=""; StringConstant cst;} {
	(t=<STRING_LITERAL> {str += t.image;})+
	{
		try{
		  str = (str!=null)?str.substring(1,str.length()-1):str;
		  cst = queryFactory.createStringConstant(str);
		  cst.setPosition(new TextPosition(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>)
	{
		try{
		  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>)
	{
		try{
		  	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 ValueExpressionPrimary(): {ADQLColumn column; ADQLOperand op; Token left,right;} {
  	try{
gmantele's avatar
gmantele committed
		(// unsigned_value_specification
gmantele's avatar
gmantele committed
		// string
gmantele's avatar
gmantele committed
		// column_reference
		| column=Column() {return column;}
		// set_function_specification
		| op=SqlFunction() {return op;}
		// 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;})
	}catch(Exception ex){
		throw generateParseException(ex);
gmantele's avatar
gmantele committed
	}
ADQLOperand ValueExpression(): {ADQLOperand valueExpr = null; } {
	(valueExpr=GeometryValueFunction()
	| LOOKAHEAD(<PLUS> | <MINUS>) valueExpr=NumericExpression()
	| LOOKAHEAD(<COORDSYS>) valueExpr=StringExpression()
	| LOOKAHEAD(StringFactor() <CONCAT>) valueExpr=StringExpression()
	| valueExpr=NumericExpression())

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=ValueExpressionPrimary())
	)
	
	{
		if (negative){
			try{
				op = queryFactory.createNegativeOperand(op);
				NegativeOperand negativeOp = (NegativeOperand)op;
				negativeOp.setPosition(new TextPosition(minusSign.beginLine, minusSign.beginColumn, negativeOp.getPosition().endLine, negativeOp.getPosition().endColumn));
			}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()
	| op=ValueExpressionPrimary())
	{return op;}
}

GeometryValue<GeometryFunction> GeometryExpression(): {ADQLColumn col = null; GeometryFunction gf = null;} {
	(col=Column() | gf=GeometryValueFunction())
	{
		if (col != null)
			return new GeometryValue<GeometryFunction>(col);
		else
			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(Predicate()) 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>
				if (fct.image.equalsIgnoreCase("contains"))
					gf = queryFactory.createContains(gvf1, gvf2);
				else
					gf = queryFactory.createIntersects(gvf1, gvf2);
			})
		// non_predicate_geometry_function
		|	(fct=<AREA> <LEFT_PAR> gvf1=GeometryExpression() end=<RIGHT_PAR>) {gf = queryFactory.createArea(gvf1);}
		|	(fct=<COORD1> <LEFT_PAR> (p1=Point() {gf = queryFactory.createCoord1(p1);} | col1=Column() {gf = queryFactory.createCoord1(col1);}) end=<RIGHT_PAR>)
		|	(fct=<COORD2> <LEFT_PAR> (p1=Point() {gf = queryFactory.createCoord2(p1);} | col1=Column() {gf = queryFactory.createCoord2(col1);}) end=<RIGHT_PAR>)
		|	(fct=<DISTANCE>
				<LEFT_PAR>
				(p1=Point()|col1=Column()) 
				{
					if (p1 != null)
						gvp1 = new GeometryValue<PointFunction>(p1);
					else
						gvp1 = new GeometryValue<PointFunction>(col1);
				}
				<COMMA>
				(p2=Point()|col2=Column())
				{
					if (p2 != null)
						gvp2 = new GeometryValue<PointFunction>(p2);
					else
						gvp2 = new GeometryValue<PointFunction>(col2);
				} 
				{gf = queryFactory.createDistance(gvp1, gvp2);}
			)
		)
	}catch(Exception ex){
		throw generateParseException(ex);
	}
	
	{
	  gf.setPosition(new TextPosition(fct, end));
	  return gf;
	}
}

ADQLOperand CoordinateSystem(): { Token oldToken = token; ADQLOperand coordSys=null;}{
	coordSys=StringExpression()
	{
		if (allowedCoordSys.size() > 0){
			TextPosition position = new TextPosition(oldToken.next, token);
			if (coordSys == null)
				throw new ParseException("A coordinate system must always be provided !", position);
			if (coordSys instanceof StringConstant && !isAllowedCoordSys(((StringConstant)coordSys).getValue()))
				throw new ParseException("\""+coordSys.toADQL()+"\" is not an allowed coordinate systems !", position);
		}
			
		return coordSys;
	}
}

GeometryFunction GeometryValueFunction(): {Token fct=null, end=null; ADQLOperand coordSys; ADQLOperand width, height; ADQLOperand[] coords, tmp; Vector<ADQLOperand> vCoords; ADQLOperand op=null; GeometryValue<GeometryFunction> gvf = null; GeometryFunction gf = null;} {
		((fct=<BOX> <LEFT_PAR> coordSys=CoordinateSystem() // coord_sys
				<COMMA> coords=Coordinates() // coordinates
				<COMMA> width=NumericExpression() <COMMA> height=NumericExpression() end=<RIGHT_PAR>)
		 {gf = queryFactory.createBox(coordSys, coords[0], coords[1], width, height);}
		 
		// CENTROID:
		| (fct=<CENTROID> <LEFT_PAR> gvf=GeometryExpression() end=<RIGHT_PAR>) {gf = queryFactory.createCentroid(gvf);}
		| (fct=<CIRCLE> <LEFT_PAR> coordSys=CoordinateSystem() // coord_sys
				<COMMA> coords=Coordinates() // coordinates
				<COMMA> width=NumericExpression() end=<RIGHT_PAR>) // radius
		 {gf = queryFactory.createCircle(coordSys, coords[0], coords[1], width);}
		
		// POINT: 
		| gf=Point()
		
		// POLYGON:
		| (fct=<POLYGON> <LEFT_PAR> coordSys=CoordinateSystem() // coord_sys
				{ vCoords = new Vector<ADQLOperand>(); } // coordinates
				<COMMA> tmp=Coordinates() {vCoords.add(tmp[0]); vCoords.add(tmp[1]);}
				<COMMA> tmp=Coordinates() {vCoords.add(tmp[0]); vCoords.add(tmp[1]);}
				<COMMA> tmp=Coordinates() {vCoords.add(tmp[0]); vCoords.add(tmp[1]);}
				(<COMMA> tmp=Coordinates() {vCoords.add(tmp[0]); vCoords.add(tmp[1]);})*
		  { gf = queryFactory.createPolygon(coordSys, vCoords); }
		  
		// REGION:
		| (fct=<REGION> <LEFT_PAR> op=StringExpression() end=<RIGHT_PAR>) {gf = queryFactory.createRegion(op);})
	}catch(Exception ex){
		throw generateParseException(ex);
	}
	
	{
	  if (fct != null && end != null) // = !(gf instanceof Point)
	  	gf.setPosition(new TextPosition(fct, end));
	  return gf;
	}
PointFunction Point(): {Token start, end; ADQLOperand coordSys; ADQLOperand[] coords;} {
	start=<POINT> <LEFT_PAR> coordSys=CoordinateSystem() // coord_sys
			<COMMA> coords=Coordinates() end=<RIGHT_PAR> // coordinates
			PointFunction pf = queryFactory.createPoint(coordSys, coords[0], coords[1]);
			pf.setPosition(new TextPosition(start, end));
			return pf;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

GeometryFunction ExtractCoordSys(): {Token start, end; GeometryValue<GeometryFunction> gvf;} {
	start=<COORDSYS> <LEFT_PAR> gvf=GeometryExpression() end=<RIGHT_PAR>
			GeometryFunction gf = queryFactory.createExtractCoordSys(gvf);
			gf.setPosition(new TextPosition(start, end));
			return gf;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

/* ***************** */
/* NUMERIC FUNCTIONS */
/* ***************** */
ADQLFunction NumericFunction(): {ADQLFunction fct;} {
	(fct=MathFunction()
	| fct=TrigFunction()
	| fct=GeometryFunction()
	| fct=UserDefinedFunction())
	{return fct;}
}

MathFunction MathFunction(): {Token fct=null, end; ADQLOperand param1=null, param2=null; NumericConstant integerValue = null;} {
		((fct=<ABS> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<CEILING> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<DEGREES> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<EXP> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<FLOOR> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<LOG> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<LOG10> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<MOD> <LEFT_PAR> param1=NumericExpression() <COMMA> param2=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<PI> <LEFT_PAR> end=<RIGHT_PAR>)
		| (fct=<POWER> <LEFT_PAR> param1=NumericExpression() <COMMA> param2=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<RADIANS> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<RAND> <LEFT_PAR> (param1=NumericExpression())? end=<RIGHT_PAR>)
		| (fct=<ROUND> <LEFT_PAR> param1=NumericExpression() (<COMMA> param2=SignedInteger())? end=<RIGHT_PAR>)
		| (fct=<SQRT> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
		| (fct=<TRUNCATE> <LEFT_PAR> param1=NumericExpression() (<COMMA> param2=SignedInteger())? end=<RIGHT_PAR>))
			if (param1 != null){
			  	MathFunction mf = queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2);
			  	mf.setPosition(new TextPosition(fct, end));
				return mf;
			}else
				return null;
		}
	}catch(Exception ex){
		throw generateParseException(ex);
	}
}

MathFunction TrigFunction(): {Token fct=null, end; ADQLOperand param1=null, param2=null;} {
	((fct=<ACOS> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
	| (fct=<ASIN> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
	| (fct=<ATAN> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
	| (fct=<ATAN2> <LEFT_PAR> param1=NumericExpression() <COMMA> param2=NumericExpression() end=<RIGHT_PAR>)
	| (fct=<COS> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
	| (fct=<COT> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
	| (fct=<SIN> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
	| (fct=<TAN> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>))
			if (param1 != null){
			  	MathFunction mf = queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2);
			  	mf.setPosition(new TextPosition(fct, end));
			  	return mf;
			}else
				return null;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

/* /!\ WARNING: The function name may be prefixed by "udf_" but there is no way to check it here ! */
UserDefinedFunction UserDefinedFunction(): {Token fct, end; Vector<ADQLOperand> params = new Vector<ADQLOperand>(); ADQLOperand op;} {
	fct=<REGULAR_IDENTIFIER> <LEFT_PAR> (op=ValueExpression() {params.add(op);} (<COMMA> op=ValueExpression() {params.add(op);})*)? end=<RIGHT_PAR>
	{
		//System.out.println("INFO [ADQLParser]: \""+fct.image+"\" (from line "+fct.beginLine+" and column "+fct.beginColumn+" to line "+token.endLine+" and column "+(token.endColumn+1)+") is considered as an user defined function !");
		try{
			ADQLOperand[] parameters = new ADQLOperand[params.size()];
			for(int i=0; i<params.size(); i++)
				parameters[i] = params.get(i);
			UserDefinedFunction udf = queryFactory.createUserDefinedFunction(fct.image, parameters);
			udf.setPosition(new TextPosition(fct, end));
			return udf;
		}catch(UnsupportedOperationException uoe){
			throw new ParseException(uoe.getMessage(), new TextPosition(fct, token));
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}