/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

options {
  // Generate non-static functions
  STATIC = false;
  // Case is ignored in keywords
  IGNORE_CASE = true;
}

PARSER_BEGIN(DOTParser)

package org.apache.pig.test.utils.dotGraph.parser;

import java.util.Map;
import java.util.HashMap;

import org.apache.pig.test.utils.dotGraph.DotGraph;
import org.apache.pig.test.utils.dotGraph.DotNode;
import org.apache.pig.test.utils.dotGraph.DotEdge;
import org.apache.pig.test.utils.dotGraph.DotGraphReader;

public class DOTParser {
    public static Map<String, DotNode> nodeMap = new HashMap<String, DotNode>();

    static String unquote(String s) {
        return s.substring(1, s.length()-1);
    }

    public static DotNode getNodeByName(String name) {
        DotNode node = nodeMap.get(name);
        if (node == null) {
            node = new DotNode(name);
            nodeMap.put(name, node);
        }
        return node;
    }
}

PARSER_END(DOTParser)

// Skip all the new lines, tabs and spaces
SKIP : { " " |	"\r" |	"\t" |	"\n" }

MORE :
{
  "//" : SINGLE_COMMENT
  |
  "#" : SINGLE_COMMENT
  |
  "/*" : MULTI_COMMENT
}

<MULTI_COMMENT> SPECIAL_TOKEN  :
{
	<("\n" | "\r" | "\r\n")>
	|
	<"*/"> : DEFAULT
}

<SINGLE_COMMENT> SPECIAL_TOKEN :
{
  < ("\n" | "\r" | "\r\n") > : DEFAULT
}

<MULTI_COMMENT> MORE :
{
	< ~[] >
}

<SINGLE_COMMENT> MORE :
{
	< ~[] >
}


TOKEN:
{

      <LPAREN : "{">
    | <RPAREN : "}">
    | <LSQBRACKET: "[">
    | <RSQBRACKET: "]">
    | <EQUAL: "=">
    | <COMMA: ",">
    | <SEMICOLON: ";">
    | <DIRECTED_EDGE: "->">
    | <EDGE: "edge">
    | <NODE: "node">
    | <GRAPH: "graph">
    | <DIGRAPH : "digraph">
    | <SUBGRAPH : "subgraph">
    | <#LETTER : ["a"-"z", "A"-"Z"] >
    | <#DIGIT : ["0"-"9"] >
    | <#SPECIAL_CHAR : "_" | "$" >
    | <NAME :  <LETTER> ( <LETTER> | <DIGIT> | <SPECIAL_CHAR> )* >
    | <NUMBER : ( <DIGIT> )+ >
    | <QUOTEDSTRING : "\"" (~["\""])* "\"">
}

DotGraph Parse() :
{
    DotGraph dotGraph = null ;
	Token graphName ;
}
{
	(
		<DIGRAPH>
		(graphName = <NAME> { dotGraph = new DotGraph(graphName.image); dotGraph.topLevel = true; })?
		<LPAREN>
        StatementList(dotGraph)
		<RPAREN>
	)
	{ return dotGraph ; }
}

void StatementList(DotGraph dotGraph) :
{
    ;
}
{
    (
    Statement(dotGraph)
    )+
}

void Statement(DotGraph dotGraph) :
{
    String[] attr;
    DotGraph subGraph;
}
{
    (
         LOOKAHEAD(2) EdgeStatement(dotGraph)
       | AttributeStatement(dotGraph)
       | LOOKAHEAD(2) ( attr = Attribute() ) { dotGraph.attributes.put(attr[0], attr[1]); }
       | LOOKAHEAD(2) NodeStatement(dotGraph)
       | subGraph = SubGraph() { dotGraph.nodes.add(subGraph); }
    )
    ( <SEMICOLON> )?
}

DotGraph SubGraph() :
{
    DotGraph dotGraph = null ;
    Token graphName ;
}
{
    <SUBGRAPH>
    graphName = <NAME> { dotGraph = new DotGraph(graphName.image); }
    <LPAREN>
    StatementList(dotGraph)
    <RPAREN>
    { return dotGraph; }
}

void AttributeStatement(DotGraph dotGraph) :
{
    Map<String,String> attributes ;
}
{
    (
      ( <EDGE> attributes = AttributeList() { dotGraph.edgeAttributes = attributes; } )
    | ( <NODE> attributes = AttributeList() { dotGraph.nodeAttributes = attributes; } )
    | ( <GRAPH> attributes = AttributeList() { dotGraph.attributes = attributes ; } )
    )
}

void NodeStatement(DotGraph dotGraph) :
{
    String nodeName ;
    DotNode node ;
    Map<String,String> attributes ;
}
{
    ( nodeName = NodeName() ) { node = getNodeByName(nodeName); }
    ( attributes = AttributeList()  {
                                        node.attributes = new HashMap<String,String>() ;
                                        node.attributes.putAll(attributes) ;
                                    }
    )?
    { dotGraph.nodes.add(node) ; }
}

void EdgeStatement(DotGraph dotGraph) :
{
    String nodeName1 ;
    String nodeName2 ;
    DotNode startingNode ;
    Map<String,String> attributes ;
    DotEdge edge = new DotEdge() ;
}
{
    nodeName1 = NodeName() { startingNode = getNodeByName(nodeName1) ; }
    (
     <DIRECTED_EDGE>
     nodeName2 = NodeName()
     {
        edge.fromNode = startingNode ;
        DotNode node2 = getNodeByName(nodeName2) ;
        edge.toNode = node2 ;

        dotGraph.nodes.add(startingNode);
        dotGraph.nodes.add(node2);
        dotGraph.edges.add(edge) ;

        if (startingNode != node2)
            startingNode.edgeTo.add(node2);

        startingNode = node2 ;
     }
    )+
    ( attributes = AttributeList()  {
                                        edge.attributes = new HashMap<String,String>() ;
                                        edge.attributes.putAll(attributes) ;
                                    }
    )?
}

Map<String,String> AttributeList() :
{
    Map<String,String> attributes = new HashMap<String,String>() ;
    String[] keyValuePair ;
}
{
    (
    <LSQBRACKET>
    (keyValuePair = Attribute() { attributes.put(keyValuePair[0], keyValuePair[1]) ; }  )
        (
            <COMMA>
            (keyValuePair = Attribute() { attributes.put(keyValuePair[0], keyValuePair[1]) ; }  )
        )*
    <RSQBRACKET>
    )
    { return attributes ; }
}

String[] Attribute() :
{
    Token attName ;
    String value ;
    String[] keyValuePair = new String[2] ;
}
{
    (
    attName = <NAME> { keyValuePair[0] = attName.image ; }
    <EQUAL>
    value = Value() { keyValuePair[1] = value; }
    )
    { return keyValuePair ; }
}

String Value() :
{
    Token value;
}
{
    (
      value = <QUOTEDSTRING>
      | value = <NAME>
      | value = <NUMBER>
    )
    { return value.image; }
}

String NodeName() :
{
    Token name ;
}
{
    (
          name = <NAME>
        | name = <NUMBER>
    )
    { return name.image; }
}
