// The DOT Language
//
// http://www.graphviz.org/doc/info/lang.html

// ### [ Tokens ] ##############################################################

// The keywords node, edge, graph, digraph, subgraph, and strict are case-
// independent.

node
	: 'n' 'o' 'd' 'e'
	| 'N' 'o' 'd' 'e'
	| 'N' 'O' 'D' 'E'
;

edge
	: 'e' 'd' 'g' 'e'
	| 'E' 'd' 'g' 'e'
	| 'E' 'D' 'G' 'E'
;

// TODO: Rename graphx to graph once gocc#20 is fixed [1].
//
// [1]: https://github.com/goccmack/gocc/issues/20

graphx
	: 'g' 'r' 'a' 'p' 'h'
	| 'G' 'r' 'a' 'p' 'h'
	| 'G' 'R' 'A' 'P' 'H'
;

digraph
	: 'd' 'i' 'g' 'r' 'a' 'p' 'h'
	| 'D' 'i' 'g' 'r' 'a' 'p' 'h'
	| 'd' 'i' 'G' 'r' 'a' 'p' 'h'
	| 'D' 'i' 'G' 'r' 'a' 'p' 'h'
	| 'D' 'I' 'G' 'R' 'A' 'P' 'H'
;

subgraph
	: 's' 'u' 'b' 'g' 'r' 'a' 'p' 'h'
	| 'S' 'u' 'b' 'g' 'r' 'a' 'p' 'h'
	| 's' 'u' 'b' 'G' 'r' 'a' 'p' 'h'
	| 'S' 'u' 'b' 'G' 'r' 'a' 'p' 'h'
	| 'S' 'U' 'B' 'G' 'R' 'A' 'P' 'H'
;

strict
	: 's' 't' 'r' 'i' 'c' 't'
	| 'S' 't' 'r' 'i' 'c' 't'
	| 'S' 'T' 'R' 'I' 'C' 'T'
;

// An arbitrary ASCII character except null (0x00), double quote (0x22) and
// backslash (0x5C).
_ascii_char
	// skip null (0x00)
	: '\x01' - '\x21'
	// skip double quote (0x22)
	| '\x23' - '\x5B'
	// skip backslash (0x5C)
	| '\x5D' - '\x7F'
;

_ascii_letter
	: 'a' - 'z'
	| 'A' - 'Z'
;

_ascii_digit : '0' - '9' ;

_unicode_char
	: _ascii_char
	| _unicode_byte
;

_unicode_byte
	: '\u0080' - '\uFFFC'
	// skip invalid code point (\uFFFD)
	| '\uFFFE' - '\U0010FFFF'
;

_letter        : _ascii_letter | _unicode_byte | '_' ;
_decimal_digit : _ascii_digit ;
_decimals      : _decimal_digit { _decimal_digit } ;

// An ID is one of the following:
//
//    1) Any string of alphabetic ([a-zA-Z\200-\377]) characters, underscores
//       ('_') or digits ([0-9]), not beginning with a digit;
//
//    2) a numeral [-]?(.[0-9]+ | [0-9]+(.[0-9]*)? );
//
//    3) any double-quoted string ("...") possibly containing escaped quotes
//       (\");
//
//    4) an HTML string (<...>).

id
	: _letter { _letter | _decimal_digit }
	| _int_lit
	| _string_lit
	| _html_lit
;

_int_lit
	: [ '-' ] '.' _decimals
	| [ '-' ] _decimals [ '.' { _decimal_digit } ]
;

// In quoted strings in DOT, the only escaped character is double-quote (").
// That is, in quoted strings, the dyad \" is converted to "; all other
// characters are left unchanged. In particular, \\ remains \\.

// As another aid for readability, dot allows double-quoted strings to span
// multiple physical lines using the standard C convention of a backslash
// immediately preceding a newline character.

// In addition, double-quoted strings can be concatenated using a '+' operator.

_escaped_char : '\\' ( _unicode_char | '"' | '\\' ) ;
_char         : _unicode_char | _escaped_char ;
_string_lit   : '"' { _char } '"' ;

// An arbitrary HTML character except null (0x00), left angle bracket (0x3C) and
// right angle bracket (0x3E).
_html_char
	// skip null (0x00)
	: '\x01' - '\x3B'
	// skip left angle bracket (0x3C)
	| '\x3D'
	// skip right angle bracket (0x3E)
	| '\x3F' - '\xFF'
	| _unicode_byte
;

_html_chars : { _html_char } ;
_html_tag   : '<' _html_chars '>' ;
_html_lit   : '<' { _html_chars | _html_tag } '>' ;

// The language supports C++-style comments: /* */ and //. In addition, a line
// beginning with a '#' character is considered a line output from a C
// preprocessor (e.g., # 34 to indicate line 34 ) and discarded.

_line_comment
	: '/' '/' { . } '\n'
	| '#' { . } '\n'
;

_block_comment : '/' '*' { . | '*' } '*' '/' ;
!comment       : _line_comment | _block_comment ;

!whitespace : ' ' | '\t' | '\r' | '\n' ;

// ### [ Syntax ] ##############################################################

<< import (
	"gonum.org/v1/gonum/graph/formats/dot/ast"
	"gonum.org/v1/gonum/graph/formats/dot/internal/astx"
) >>

// === [ Files ] ===============================================================

File
	: Graph                                       << astx.NewFile($0) >>
	| File Graph                                  << astx.AppendGraph($0, $1) >>
;

// === [ Graphs ] ==============================================================

// Graph : [ "strict" ] ( "graph" | "digraph" ) [ ID ] "{" [ StmtList ] "}"

Graph
	: OptStrict DirectedGraph OptID
	  "{" OptStmtList "}"                         << astx.NewGraph($0, $1, $2, $4) >>
;

OptStrict
	: empty                                       << false, nil >>
	| strict                                      << true, nil >>
;

DirectedGraph
	: graphx                                      << false, nil >>
	| digraph                                     << true, nil >>
;

// === [ Statements ] ==========================================================

// StmtList
//    : Stmt [ ";" ]
//    | StmtList Stmt [ ";" ]

StmtList
	: Stmt OptSemi                                << astx.NewStmtList($0) >>
	| StmtList Stmt OptSemi                       << astx.AppendStmt($0, $1) >>
;

OptStmtList
	: empty
	| StmtList
;

Stmt
	: NodeStmt
	| EdgeStmt
	| AttrStmt
	| Attr
	| Subgraph
;

OptSemi
	: empty
	| ";"
;

// --- [ Node statement ] ------------------------------------------------------

// NodeStmt : Node [ AttrList ]

NodeStmt
	: Node OptAttrList                            << astx.NewNodeStmt($0, $1) >>
;

// --- [ Edge statement ] ------------------------------------------------------

// EdgeStmt : ( Node | Subgraph ) Edge [ AttrList ]

EdgeStmt
	: Vertex Edge OptAttrList                     << astx.NewEdgeStmt($0, $1, $2) >>
;

// Edge : ( "--" | "-->" ) ( Node | Subgraph ) [ Edge ]

Edge
	: DirectedEdge Vertex OptEdge                 << astx.NewEdge($0, $1, $2) >>
;

DirectedEdge
	: "--"                                        << false, nil >>
	| "->"                                        << true, nil >>
;

OptEdge
	: empty
	| Edge
;

// --- [ Attribute statement ] -------------------------------------------------

// AttrStmt : ( "graph" | "node" | "edge" ) AttrList

AttrStmt
	: Component AttrList                          << astx.NewAttrStmt($0, $1) >>
;

Component
	: graphx                                      << ast.GraphKind, nil >>
	| node                                        << ast.NodeKind, nil >>
	| edge                                        << ast.EdgeKind, nil >>
;

// AttrList : "[" [ AList ] "]" [ AttrList ]

AttrList
	: "[" OptAList "]"                            << $1, nil >>
	| AttrList "[" OptAList "]"                   << astx.AppendAttrList($0, $2) >>
;

OptAttrList
	: empty
	| AttrList
;

// AList
//    : Attr [ ( ";" | "," ) ]
//    | AList Attr [ ( ";" | "," ) ]

AList
	: Attr OptSep                                 << astx.NewAttrList($0) >>
	| AList Attr OptSep                           << astx.AppendAttr($0, $1) >>
;

OptAList
	: empty
	| AList
;

OptSep
	: empty
	| ";"
	| ","
;

// --- [ Attribute ] -----------------------------------------------------------

Attr
	: ID "=" ID                                   << astx.NewAttr($0, $2) >>
;

// --- [ Subgraph ] ------------------------------------------------------------

// Subgraph : [ "subgraph" [ ID ] ] "{" [ StmtList ] "}"

Subgraph
	: OptSubgraphID "{" OptStmtList "}"           << astx.NewSubgraph($0, $2) >>
;

OptSubgraphID
	: empty
	| subgraph OptID                              << $1, nil >>
;

// === [ Vertices ] ============================================================

Vertex
	: Node
	| Subgraph
;

// --- [ Node identifier ] -----------------------------------------------------

// Node : ID [ Port ]

Node
	: ID OptPort                                  << astx.NewNode($0, $1) >>
;

// Port
//    : ":" ID [ ":" CompassPoint ]
//    | ":" CompassPoint
//
// CompassPoint
//    : "n" | "ne" | "e" | "se" | "s" | "sw" | "w" | "nw" | "c" | "_"

// Note also that the allowed compass point values are not keywords, so these
// strings can be used elsewhere as ordinary identifiers and, conversely, the
// parser will actually accept any identifier.

Port
	: ":" ID                                      << astx.NewPort($1, nil) >>
	| ":" ID ":" ID                               << astx.NewPort($1, $3) >>
;

OptPort
	: empty
	| Port
;

// === [ Identifiers ] =========================================================

ID
	: id                                          << astx.NewID($0) >>
;

OptID
	: empty                                       << "", nil >>
	| ID
;
