//
// ExpressionParser.jay
//
// Author:
//   Atsushi Enomoto (atsushi@xamarin.com)
//
// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
%{

using System;
using System.Text;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Framework;

/*

Pseudo formal syntax for .NET 4.0 expression:

Condition = Expression
Include = Expression*

 Expression
	BooleanLiteral
		TrueLiteral
		FalseLiteral
	BinaryExpression
		Expression "==" Expression
		Expression "!=" Expression
		Expression ">" Expression
		Expression ">=" Expression
		Expression "<" Expression
		Expression "<=" Expression
		Expression "And" Expression
		Expression "Or" Expression
	UnaryExpression
		"!" Expression
	PropertyExpression
		"$(" PropertyApplication ")"
	ItemExpression
		"@(" ItemApplication ")"
	MetadataBatchingExpression
		"%(" MetadataBatchingApplication ")"
  StringLiteralOrFunction
		StringLiteralOrFunctionName ( "(" FunctionArguments ")" )?

.NET error messages are so detailed which is something like "you forgot '(' after '$' ?" - so
it is likely that the MS tokenizer is hand-written.

*/

namespace Microsoft.Build.Internal.Expressions
{
	class ExpressionParser
	{
		static readonly int yacc_verbose_flag = Environment.GetEnvironmentVariable ("MONO_MSBUILD_PARSER_DEBUG") == "1" ? 1 : 0;

		object debug_obj = yacc_verbose_flag == 0 ? null : new yydebug.yyDebugSimple ();
		
		public ExpressionList Parse (string source, ExpressionValidationType validationType)
		{
			var tokenizer = new ExpressionTokenizer (source, validationType);
			return (ExpressionList) yyparse (tokenizer, debug_obj);
		}
		
		BinaryExpression Binary (Operator op, object left, object right)
		{
			return new BinaryExpression () { Operator = op, Left = (Expression) left, Right = (Expression) right, Location = (ILocation) left };
		}
%}

%token TRUE_LITERAL
%token FALSE_LITERAL
%token STRING_LITERAL
%token EQ // ==
%token NE // !=
%token GT // >
%token GE // >=
%token LT // <
%token LE // <=
%token AND // AND
%token OR // OR
%token NOT //!
%token DOT //.
%token COMMA //,
%token PROP_OPEN // $(
%token ITEM_OPEN // @(
%token METADATA_OPEN // %(
%token PAREN_OPEN // (
%token PAREN_CLOSE // )
%token BRACE_OPEN // [
%token BRACE_CLOSE // ]
%token COLON2 // ::
%token ARROW // ->
%token NAME
%token ERROR

%start ExpressionList

%%

ExpressionList
	: /* empty */
	  { $$ = new ExpressionList (); }
	| ExpressionList Expression
	  { $$ = ((ExpressionList) $1).Add ((Expression) $2); }
	;

Expression
	: LogicalExpression
	;

LogicalExpression
	: ComparisonExpression
	| LogicalExpression AND LogicalExpression
	  { $$ = Binary (Operator.And, $1, $3); }
	| LogicalExpression OR LogicalExpression
	  { $$ = Binary (Operator.Or, $1, $3); }
	;

ComparisonExpression
	: UnaryExpression
	| UnaryExpression EQ UnaryExpression
	  { $$ = Binary (Operator.EQ, $1, $3); }
	| UnaryExpression NE UnaryExpression
	  { $$ = Binary (Operator.NE, $1, $3); }
	| UnaryExpression GT UnaryExpression
	  { $$ = Binary (Operator.GT, $1, $3); }
	| UnaryExpression GE UnaryExpression
	  { $$ = Binary (Operator.GE, $1, $3); }
	| UnaryExpression LT UnaryExpression
	  { $$ = Binary (Operator.LT, $1, $3); }
	| UnaryExpression LE UnaryExpression
	  { $$ = Binary (Operator.LE, $1, $3); }
	;

UnaryExpression
	: PrimaryExpression
	| NOT UnaryExpression
	  { $$ = new NotExpression () { Negated = (Expression) $2, Location = (ILocation) $1 }; }
	;

PrimaryExpression
	: BooleanLiteral
	| StringLiteral
	| UnaryExpression
	| PropertyAccessExpression
	| ItemAccessExpression
	| MetadataAccessExpression
	| RawStringLiteralOrFunction
	| ParenthesizedExpression
	;
	
BooleanLiteral
	: TRUE_LITERAL
	  { $$ = new BooleanLiteral () { Value = true, Location = (ILocation) $1 }; }
	| FALSE_LITERAL
	  { $$ = new BooleanLiteral () { Value = false, Location = (ILocation) $1 }; }
	;

PropertyAccessExpression
	: PROP_OPEN PropertyAccess PAREN_CLOSE
	  { $$ = new PropertyAccessExpression () { Access = (PropertyAccess) $2, Location = (ILocation) $1 }; }
	;

PropertyAccess
	: NAME
	  { $$ = new PropertyAccess () { Name = (NameToken) $1, TargetType = PropertyTargetType.Object, Location = (NameToken) $1 }; }
	| Expression DOT NAME
	  { $$ = new PropertyAccess () { Name = (NameToken) $3, Target = (Expression) $1, TargetType = PropertyTargetType.Object, Location = (ILocation) $1 }; }
	| BRACE_OPEN QualifiedNameExpression BRACE_CLOSE COLON2 NAME
	  { $$ = new PropertyAccess () { Name = (NameToken) $5, Target = (Expression) $2, TargetType = PropertyTargetType.Type, Location = (ILocation) $1 }; }
	| BRACE_OPEN QualifiedNameExpression BRACE_CLOSE COLON2 NAME PAREN_OPEN FunctionCallArguments PAREN_CLOSE
	  { $$ = new PropertyAccess () { Name = (NameToken) $5, Target = (Expression) $2, TargetType = PropertyTargetType.Type, Arguments = (ExpressionList) $7, Location = (ILocation) $1 }; }
	;

QualifiedNameExpression
	: QualifiedName
	  { $$ = new StringLiteral () { Value = (NameToken) $1, Location = (ILocation) $1 }; }
	;

QualifiedName
	: NAME
	| QualifiedName DOT NAME
	  { $$ = new NameToken () { Name = ((NameToken) $1).Name + "." + ((NameToken) $3).Name, Column = ((ILocation) $1).Column }; }
	;

ItemAccessExpression
	: ITEM_OPEN ItemApplication PAREN_CLOSE
	  { $$ = new ItemAccessExpression () { Application = (ItemApplication) $2, Location = (ILocation) $1 }; }
	;

// looking a bit messy, but gives different location
ItemApplication
	: NAME
	  { $$ = new ItemApplication () { Name = (NameToken) $1, Location = (ILocation) $1 }; }
	| NAME ARROW ExpressionList
	  { $$ = new ItemApplication () { Name = (NameToken) $1, Expressions = (ExpressionList) $3, Location = (ILocation) $1 }; }
	;

MetadataAccessExpression
	: METADATA_OPEN MetadataAccess PAREN_CLOSE
	  { $$ = new MetadataAccessExpression () { Access = (MetadataAccess) $2, Location = (ILocation) $1 }; }
	;

// looking a bit messy, but gives different location
MetadataAccess
	: NAME
	  { $$ = new MetadataAccess () { Metadata = (NameToken) $1, Location = (ILocation) $1 }; }
	| NAME DOT NAME
	  { $$ = new MetadataAccess () { ItemType = (NameToken) $1, Metadata = (NameToken) $3, Location = (ILocation) $1 }; }
	;

StringLiteral
	: STRING_LITERAL
	  { $$ = new StringLiteral () { Value = (NameToken) $1, Location = (ILocation) $1 }; }
	;

RawStringLiteralOrFunction
	: NAME
	  { $$ = new RawStringLiteral () { Value = (NameToken) $1, Location = (ILocation) $1 }; }
	| NAME PAREN_OPEN PAREN_CLOSE
	  { $$ = new FunctionCallExpression () { Name = (NameToken) $1, Arguments = new ExpressionList (), Location = (ILocation) $1 }; }
	| NAME PAREN_OPEN FunctionCallArguments PAREN_CLOSE
	  { $$ = new FunctionCallExpression () { Name = (NameToken) $1, Arguments = (ExpressionList) $3, Location = (ILocation) $1 }; }
	;

FunctionCallArguments
	: /* empty */
	  { $$ = new ExpressionList (); }
	| Expression
	  { $$ = new ExpressionList ().Add ((Expression) $1); }
	| FunctionCallArguments COMMA Expression
	  { $$ = ((ExpressionList) $1).Add ((Expression) $3); }
	;

ParenthesizedExpression
	: PAREN_OPEN Expression PAREN_CLOSE
	  { $$ = (Expression) $2; }
	;

%%

	}