//Copyright 2013 GoGraphviz Authors // //Licensed 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. //This bnf has been derived from https://graphviz.gitlab.io/_pages/doc/info/lang.html //The rules have been copied and are shown in the comments, with their derived bnf rules below. // ### [ 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 \\. _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' ; _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 "github.com/awalterschulze/gographviz/ast" >> //graph : [ strict ] (graph | digraph) [ ID ] '{' stmt_list '}' DotGraph : graphx "{" "}" << ast.NewGraph(ast.GRAPH, ast.FALSE, nil, nil) >> | strict graphx "{" "}" << ast.NewGraph(ast.GRAPH, ast.TRUE, nil, nil) >> | graphx Id "{" "}" << ast.NewGraph(ast.GRAPH, ast.FALSE, $1, nil) >> | strict graphx Id "{" "}" << ast.NewGraph(ast.GRAPH, ast.TRUE, $2, nil) >> | graphx "{" StmtList "}" << ast.NewGraph(ast.GRAPH, ast.FALSE, nil, $2) >> | graphx Id "{" StmtList "}" << ast.NewGraph(ast.GRAPH, ast.FALSE, $1, $3) >> | strict graphx "{" StmtList "}" << ast.NewGraph(ast.GRAPH, ast.TRUE, nil, $3) >> | strict graphx Id "{" StmtList "}" << ast.NewGraph(ast.GRAPH, ast.TRUE, $2, $4) >> | digraph "{" "}" << ast.NewGraph(ast.DIGRAPH, ast.FALSE, nil, nil) >> | strict digraph "{" "}" << ast.NewGraph(ast.DIGRAPH, ast.TRUE, nil, nil) >> | digraph Id "{" "}" << ast.NewGraph(ast.DIGRAPH, ast.FALSE, $1, nil) >> | strict digraph Id "{" "}" << ast.NewGraph(ast.DIGRAPH, ast.TRUE, $2, nil) >> | digraph "{" StmtList "}" << ast.NewGraph(ast.DIGRAPH, ast.FALSE, nil, $2) >> | digraph Id "{" StmtList "}" << ast.NewGraph(ast.DIGRAPH, ast.FALSE, $1, $3) >> | strict digraph "{" StmtList "}" << ast.NewGraph(ast.DIGRAPH, ast.TRUE, nil, $3) >> | strict digraph Id "{" StmtList "}" << ast.NewGraph(ast.DIGRAPH, ast.TRUE, $2, $4) >> ; //stmt_list : [ stmt [ ';' ] [ stmt_list ] ] StmtList : Stmt1 << ast.NewStmtList($0) >> | StmtList Stmt1 << ast.AppendStmtList($0, $1) >> ; Stmt1 : Stmt << $0, nil >> | Stmt ";" << $0, nil >> ; //stmt : node_stmt | edge_stmt | attr_stmt | (ID '=' ID) | subgraph Stmt : Id "=" Id << ast.NewAttr($0, $2) >> | NodeStmt << $0, nil >> | EdgeStmt << $0, nil >> | AttrStmt << $0, nil >> | SubGraphStmt << $0, nil >> ; //attr_stmt : (graph | node | edge) attr_list AttrStmt : graphx AttrList << ast.NewGraphAttrs($1) >> | node AttrList << ast.NewNodeAttrs($1) >> | edge AttrList << ast.NewEdgeAttrs($1) >> ; //attr_list : '[' [ a_list ] ']' [ attr_list ] AttrList : "[" "]" << ast.NewAttrList(nil) >> | "[" AList "]" << ast.NewAttrList($1) >> | AttrList "[" "]" << ast.AppendAttrList($0, nil) >> | AttrList "[" AList "]" << ast.AppendAttrList($0, $2) >> ; //a_list : ID [ '=' ID ] [ ',' ] [ a_list ] AList : Attr << ast.NewAList($0) >> | AList Attr << ast.AppendAList($0, $1) >> | AList "," Attr << ast.AppendAList($0, $2) >> ; //An a_list clause of the form ID is equivalent to ID=true. Attr : Id << ast.NewAttr($0, nil) >> | Id "=" Id << ast.NewAttr($0, $2) >> ; //edge_stmt : (node_id | subgraph) edgeRHS [ attr_list ] EdgeStmt : NodeId EdgeRHS << ast.NewEdgeStmt($0, $1, nil) >> | NodeId EdgeRHS AttrList << ast.NewEdgeStmt($0, $1, $2) >> | SubGraphStmt EdgeRHS << ast.NewEdgeStmt($0, $1, nil) >> | SubGraphStmt EdgeRHS AttrList << ast.NewEdgeStmt($0, $1, $2) >> ; //edgeRHS : edgeop (node_id | subgraph) [ edgeRHS ] EdgeRHS : EdgeOp NodeId << ast.NewEdgeRHS($0, $1) >> | EdgeOp SubGraphStmt << ast.NewEdgeRHS($0, $1) >> | EdgeRHS EdgeOp NodeId << ast.AppendEdgeRHS($0, $1, $2) >> | EdgeRHS EdgeOp SubGraphStmt << ast.AppendEdgeRHS($0, $1, $2) >> ; //node_stmt : node_id [ attr_list ] NodeStmt : NodeId << ast.NewNodeStmt($0, nil) >> | NodeId AttrList << ast.NewNodeStmt($0, $1) >> ; //node_id : ID [ port ] NodeId : Id << ast.NewNodeID($0, nil) >> | Id Port << ast.NewNodeID($0, $1) >> ; //compass_pt : (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 [ ':' compass_pt ] // | ':' compass_pt Port : ":" Id << ast.NewPort($1, nil), nil >> | ":" Id ":" Id << ast.NewPort($1, $3), nil >> ; //TODO: Semicolons aid readability but are not required except in the rare case that a named subgraph with no body immediately preceeds an anonymous subgraph, //since the precedence rules cause this sequence to be parsed as a subgraph with a heading and a body. Also, any amount of whitespace may be inserted between terminals. //subgraph : [ subgraph [ ID ] ] '{' stmt_list '}' SubGraphStmt : "{" StmtList "}" << ast.NewSubGraph(nil, $1) >> | subgraph "{" StmtList "}" << ast.NewSubGraph(nil, $2) >> | subgraph Id "{" StmtList "}" << ast.NewSubGraph($1, $3) >> | subgraph "{" "}" << ast.NewSubGraph(nil, nil) >> | subgraph Id "{" "}" << ast.NewSubGraph($1, nil) >> ; //An edgeop is -> in directed graphs and -- in undirected graphs. EdgeOp : "->" << ast.DIRECTED, nil >> | "--" << ast.UNDIRECTED, nil >> ; Id : id << ast.NewID($0) >> ;