options {
  DEBUG_PARSER = false;
  DEBUG_LOOKAHEAD = false;
  DEBUG_TOKEN_MANAGER = false;
  LOOKAHEAD=3;
  FORCE_LA_CHECK=true;
  STATIC = false;
}

PARSER_BEGIN(GroupMatcherParser)
package org.apache.maven.surefire.group.parse;

import org.apache.maven.surefire.group.match.*;
import java.io.*;

public class GroupMatcherParser
{
    public static void main( String[] args )
        throws Exception
    {
        BufferedReader reader = new BufferedReader( new InputStreamReader( System.in ) );
        while( true )
        {
        try
        {
            System.out.print( "Enter expression: " );
            String expr = reader.readLine();
            GroupMatcherParser parser = new GroupMatcherParser( expr );
            GroupMatcher matcher = parser.parse();
            System.out.println( matcher );
        }
        catch ( ParseException e )
        {
            e.printStackTrace();
        }
        }
    }

    public GroupMatcherParser( String expression )
    {
        this( (Reader)(new StringReader( expression ) ) );
    }

    private GroupMatcher getMatcher( Op op, GroupMatcher...matchers )
    {
      GroupMatcher matcher = null;
      if ( Op.AND == op )
      {
        matcher = new AndGroupMatcher( matchers );
      }
      else
      {
        matcher = new OrGroupMatcher( matchers );
      }

      return matcher;
    }

    public enum Op
    {
      AND, OR;
    }
}

PARSER_END(GroupMatcherParser)

SKIP :
{
  " "
| "\t"
| "\r"
| "\n"
}

TOKEN:
{
  <AND: (["A", "a"] ["N", "n"] ["D", "d"]) >
| <OR:  (["O","o"] ["R", "r"]) >
| <AMP2: "&&">
| <PIPE2: "||">
| <COMMA: ",">
| <NOT:   "NOT">
| <BANG:  "!">
| <LPAREN: "(">
| <RPAREN: ")">
| <STRING: (["A"-"Z", "a"-"z", "-", "0"-"9", "_", "$", "#", ".", "*", "\\"])+ >
| <CLS:    ".class">
}

GroupMatcher parse() :
{GroupMatcher matcher = null;}
{
  matcher=expr()
  {
    return matcher;
  }
}

GroupMatcher expr() :
{
GroupMatcher matcher=null;
}
{
(
  matcher=igroup()
| matcher=group()
)
  {return matcher;}
}

GroupMatcher igroup() :
{
GroupMatcher lhs=null;
GroupMatcher rhs=null;
Op op=null;
}
{
  (lhs=value() (op=op() rhs=expr())?)
  {
    GroupMatcher matcher = lhs;
    if ( op != null )
    {
      matcher = getMatcher( op, lhs, rhs );
    }

    return matcher;
  }
}

GroupMatcher group() :
{
boolean inverted=false;
GroupMatcher lhs=null;
GroupMatcher rhs=null;
Op op=null;
}
{
  (not()
  {
    inverted=true;
  }
  )?
  <LPAREN>
  lhs=expr()
  (
  op=op()
  rhs=expr()
  )?
  <RPAREN>
  {
    GroupMatcher matcher = lhs;
    if ( op != null )
    {
      matcher = getMatcher( op, lhs, rhs );
    }

    return inverted ? new InverseGroupMatcher( matcher ) : matcher;
  }
}

GroupMatcher value() :
{
boolean inverted=false;
Token val=null;
}
{
( not()
  {inverted=true;}
)?
  val=<STRING>
  {
    GroupMatcher m = new SingleGroupMatcher( val.image );
    return inverted ? new InverseGroupMatcher( m ) : m;
  }
}

Op op() :
{Op o=null;}
{
(
  <AND>
  {o=Op.AND;}
| <OR>
  {o=Op.OR;}
| <AMP2>
  {o=Op.AND;}
| <PIPE2>
  {o=Op.OR;}
| <COMMA>
  {o=Op.OR;}
)
  {return o;}
}

void not() :
{}
{
  <NOT> | <BANG>
}
