graphviz-dot-grammar.pegjs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /*
  2. * Grammar to parse the graphviz dot/xdot language
  3. */
  4. start
  5. = graph
  6. graph "graph"
  7. = _ strict:"strict"i? _ type:("graph"i / "digraph"i) _ id:ID? _ "{" children:stmt_list "}" _ {
  8. var ret = {type:type.toLowerCase(), children:children};
  9. if (strict) { ret.strict = true }
  10. if (id) { ret.id = id }
  11. return ret;
  12. }
  13. stmt_list
  14. = _ v:(s:stmt _ ';'? _ v:stmt_list? {return [s].concat(v||[]);})? { return v; }
  15. stmt
  16. // an assignment as a statement e.g. 'label=4' is shorthand for 'graph [label=4]', so let's just pretend that's what we wrote
  17. = left:ID _ '=' _ right:ID { return {type:'attr_stmt', target:'graph', attr_list:[{type:'attr', id:left, eq:right}]}; }
  18. / attr_stmt
  19. / edge_stmt
  20. / subgraph
  21. / node_stmt
  22. / ID '=' ID
  23. attr_stmt
  24. = target:('graph'i/'node'i/'edge'i) attr:attr_list { return {type:'attr_stmt', target:target, attr_list:attr}; }
  25. attr_list
  26. = _ '[' list:a_list? ']' _ rest:attr_list? { return (list || []).concat(rest || []); }
  27. a_list
  28. = _ id:ID eq:(_ '=' _ id:ID {return id})? _ ','? rest:a_list? {
  29. return [{type:'attr', id:id, eq:eq||null}].concat(rest || []);
  30. }
  31. edge_stmt
  32. = id:(subgraph / node_id) rhs:edgeRHS attr:attr_list? {
  33. var edge_list = [id];
  34. edge_list = edge_list.concat(rhs.map(function(v){return v.id}));
  35. return {type:'edge_stmt', edge_list:edge_list, attr_list:attr||[]};
  36. }
  37. edgeRHS
  38. = _ edgeop:('->'/'--') _ id:(subgraph / node_id) _ rest:edgeRHS? {return [{type:'edgeRHS', edgeop:edgeop, id:id}].concat(rest || []); }
  39. node_stmt
  40. = id:node_id attr:attr_list? { return {type:'node_stmt', node_id:id, attr_list:attr || []}; }
  41. node_id
  42. = id:ID port:port? { return port ? {type:'node_id', id:id, port:port} : {type:'node_id', id:id}; }
  43. port 'port'
  44. = ':' id:ID pt:(':' pt:compass_pt {return pt})? { return {type:'port', id:id, compass_pt:pt || null}; }
  45. //I think this rule is never used...
  46. / ':' pt:compass_pt {return {type:'port', compass_pt:pt||null}}
  47. subgraph
  48. = g:('subgraph'i _ id:ID? _ {return id ? {type:'subgraph', id:id} : {type:'subgraph'}})? '{' s:stmt_list '}' { g=g||{type:'subgraph'}; g.children = s||[]; return g; }
  49. / 'subgraph'i _ id:ID { return {type:'subgraph', id:id, children:[]}; }
  50. compass_pt
  51. = 'n'/'ne'/'e'/'se'/'s'/'sw'/'w'/'nw'
  52. ID
  53. = STRING
  54. / NUMBER
  55. / QUOTED_STRING
  56. / HTML_STRING
  57. STRING
  58. = v:([a-zA-Z_][a-zA-Z0-9_]*) { return v[0]+v[1].join(''); }
  59. NUMBER "NUMBER"
  60. = n:("-"? ("." [0-9]+ / [0-9]+("." [0-9]*)?)) {
  61. return parseFloat(text());
  62. }
  63. /* html strings are enclosed in <>. The inside of those strings is xml. All we care about
  64. * is a balanced number of <'s and >'s, so we can simplify our life a little by just maching
  65. * balanced expressions, and then returning what's inside the outermost <> pair
  66. */
  67. HTML_STRING
  68. = v:html_raw_string { return {type:'id', value:v.slice(1,v.length-1), html:true}; }
  69. html_raw_string
  70. = '<' v:(html_char / html_raw_string)* '>' { return '<' + v.join('') + '>'; }
  71. html_char
  72. = v:(!('>'/'<') v:. {return v})+ { return v.join(""); }
  73. QUOTED_STRING
  74. = '"' '"' {return "";}
  75. / v:('"' chars ("\\" NEWLINE chars)? '"') rest:(_ '+' _ v:QUOTED_STRING {return v})? { return rest === null ? v[1] : (v[1] + rest); }
  76. chars
  77. = chars:char+ { return chars.join(""); }
  78. char
  79. = [^"\\\0-\x1F\x7f]
  80. / '\\"' { return '"'; }
  81. / '\\' NEWLINE { return ""; }
  82. / '\\' { return '\\'; }
  83. COMMENT "COMMENT"
  84. = (BLOCK_COMMENT / C_COMMENT / MACRO_COMMENT)
  85. BLOCK_COMMENT "BLOCK_COMMENT"
  86. = "/*" v:(!"*/" v:. {return v;})* "*/" { return v.join('') }
  87. C_COMMENT "C_COMMENT"
  88. = "//" v:(![\n] v:. { return v; })* [\n]? { return v.join(''); }
  89. MACRO_COMMENT "MACRO_COMMENT"
  90. = "#" v:(![\n] v:. { return v; })* [\n]? { return v.join(''); }
  91. _ "WHITESPACE"
  92. = (WHITESPACE / COMMENT)*
  93. NEWLINE
  94. = [\n\r]+
  95. WHITESPACE
  96. = ([ \t] / NEWLINE)+