diff --git a/lib/build-graph.js b/lib/build-graph.js index e57fcf8..cd91e8e 100644 --- a/lib/build-graph.js +++ b/lib/build-graph.js @@ -40,20 +40,22 @@ function handleEdgeStmt(g, stmt, defaultStack, sg) { handleStmt(g, elem, defaultStack, sg); switch(elem.type) { - case "node": curr = [elem.id]; break; - case "subgraph": curr = collectNodeIds(elem); break; + case "node": curr = [elem]; break; + case "subgraph": curr = collectNodes(elem); break; } _.each(prev, function(v) { _.each(curr, function(w) { var name; - if (g.hasEdge(v, w) && g.isMultigraph()) { + if (g.hasEdge(v.id, w.id) && g.isMultigraph()) { name = _.uniqueId("edge"); } - if (!g.hasEdge(v, w, name)) { - g.setEdge(v, w, _.clone(_.last(defaultStack).edge), name); + if (!g.hasEdge(v.id, w.id, name)) { + g.setEdge(v.id, w.id, _.clone(_.last(defaultStack).edge), name); } - _.merge(g.edge(v, w, name), attrs); + if (v.port) { _.merge(g.edge(v.id, w.id, name), { $out: v.port }); } + if (w.port) { _.merge(g.edge(v.id, w.id, name), { $in: w.port }); } + _.merge(g.edge(v.id, w.id, name), attrs); }); }); @@ -107,8 +109,8 @@ function maybeCreateNode(g, v, defaultStack, sg) { } // Collect all nodes involved in a subgraph statement -function collectNodeIds(stmt) { - var ids = {}, +function collectNodes(stmt) { + var nodes = [], stack = [], curr; @@ -118,12 +120,12 @@ function collectNodeIds(stmt) { while(stack.length) { curr = stack.pop(); switch(curr.type) { - case "node": ids[curr.id] = true; break; + case "node": nodes.push(curr); break; case "edge": _.each(curr.elems, push); break; case "subgraph": _.each(curr.stmts, push); break; } } - return _.keys(ids); + return _.uniq(nodes); } diff --git a/lib/dot-grammar.js b/lib/dot-grammar.js index 6a6c9e0..5430759 100644 --- a/lib/dot-grammar.js +++ b/lib/dot-grammar.js @@ -61,7 +61,7 @@ module.exports = (function() { attrs[k] = v; return { type: "inlineAttr", attrs: attrs }; }, - peg$c15 = function(id, attrs) { return {type: "node", id: id, attrs: attrs || {}}; }, + peg$c15 = function(nodeId, attrs) { return {type: "node", id: nodeId.id, port: nodeId.port, attrs: attrs || {}}; }, peg$c16 = function(lhs, rhs, attrs) { var elems = [lhs]; for (var i = 0; i < rhs.length; ++i) { @@ -114,91 +114,95 @@ module.exports = (function() { result[k] = v[3]; return result; }, - peg$c35 = function(id) { return { type: "node", id: id, attrs: {} }; }, - peg$c36 = function(id) { return id; }, + peg$c35 = function(nodeId) { + var attrs = {} + return { type: "node", id: nodeId.id, port: nodeId.port, attrs: attrs }; + }, + peg$c36 = function(id, port) { return { id: id, port: port }; }, peg$c37 = ":", peg$c38 = { type: "literal", value: ":", description: "\":\"" }, - peg$c39 = "ne", - peg$c40 = { type: "literal", value: "ne", description: "\"ne\"" }, - peg$c41 = "se", - peg$c42 = { type: "literal", value: "se", description: "\"se\"" }, - peg$c43 = "sw", - peg$c44 = { type: "literal", value: "sw", description: "\"sw\"" }, - peg$c45 = "nw", - peg$c46 = { type: "literal", value: "nw", description: "\"nw\"" }, - peg$c47 = "n", - peg$c48 = { type: "literal", value: "n", description: "\"n\"" }, - peg$c49 = "e", - peg$c50 = { type: "literal", value: "e", description: "\"e\"" }, - peg$c51 = "s", - peg$c52 = { type: "literal", value: "s", description: "\"s\"" }, - peg$c53 = "w", - peg$c54 = { type: "literal", value: "w", description: "\"w\"" }, - peg$c55 = "c", - peg$c56 = { type: "literal", value: "c", description: "\"c\"" }, - peg$c57 = "_", - peg$c58 = { type: "literal", value: "_", description: "\"_\"" }, - peg$c59 = { type: "other", description: "identifier" }, - peg$c60 = /^[a-zA-Z\u0200-\u0377_]/, - peg$c61 = { type: "class", value: "[a-zA-Z\\u0200-\\u0377_]", description: "[a-zA-Z\\u0200-\\u0377_]" }, - peg$c62 = /^[a-zA-Z\u0200-\u0377_0-9]/, - peg$c63 = { type: "class", value: "[a-zA-Z\\u0200-\\u0377_0-9]", description: "[a-zA-Z\\u0200-\\u0377_0-9]" }, - peg$c64 = function(fst, rest) { return fst + rest.join(""); }, - peg$c65 = "-", - peg$c66 = { type: "literal", value: "-", description: "\"-\"" }, - peg$c67 = ".", - peg$c68 = { type: "literal", value: ".", description: "\".\"" }, - peg$c69 = /^[0-9]/, - peg$c70 = { type: "class", value: "[0-9]", description: "[0-9]" }, - peg$c71 = function(sign, dot, after) { + peg$c39 = function(id) { return id; }, + peg$c40 = "ne", + peg$c41 = { type: "literal", value: "ne", description: "\"ne\"" }, + peg$c42 = "se", + peg$c43 = { type: "literal", value: "se", description: "\"se\"" }, + peg$c44 = "sw", + peg$c45 = { type: "literal", value: "sw", description: "\"sw\"" }, + peg$c46 = "nw", + peg$c47 = { type: "literal", value: "nw", description: "\"nw\"" }, + peg$c48 = "n", + peg$c49 = { type: "literal", value: "n", description: "\"n\"" }, + peg$c50 = "e", + peg$c51 = { type: "literal", value: "e", description: "\"e\"" }, + peg$c52 = "s", + peg$c53 = { type: "literal", value: "s", description: "\"s\"" }, + peg$c54 = "w", + peg$c55 = { type: "literal", value: "w", description: "\"w\"" }, + peg$c56 = "c", + peg$c57 = { type: "literal", value: "c", description: "\"c\"" }, + peg$c58 = "_", + peg$c59 = { type: "literal", value: "_", description: "\"_\"" }, + peg$c60 = { type: "other", description: "identifier" }, + peg$c61 = /^[a-zA-Z\u0200-\u0377_]/, + peg$c62 = { type: "class", value: "[a-zA-Z\\u0200-\\u0377_]", description: "[a-zA-Z\\u0200-\\u0377_]" }, + peg$c63 = /^[a-zA-Z\u0200-\u0377_0-9]/, + peg$c64 = { type: "class", value: "[a-zA-Z\\u0200-\\u0377_0-9]", description: "[a-zA-Z\\u0200-\\u0377_0-9]" }, + peg$c65 = function(fst, rest) { return fst + rest.join(""); }, + peg$c66 = "-", + peg$c67 = { type: "literal", value: "-", description: "\"-\"" }, + peg$c68 = ".", + peg$c69 = { type: "literal", value: ".", description: "\".\"" }, + peg$c70 = /^[0-9]/, + peg$c71 = { type: "class", value: "[0-9]", description: "[0-9]" }, + peg$c72 = function(sign, dot, after) { return (sign || "") + dot + after.join(""); }, - peg$c72 = function(sign, before, after) { + peg$c73 = function(sign, before, after) { return (sign || "") + before.join("") + (after ? after[0] : "") + (after ? after[1].join("") : ""); }, - peg$c73 = "\"", - peg$c74 = { type: "literal", value: "\"", description: "\"\\\"\"" }, - peg$c75 = "\\\"", - peg$c76 = { type: "literal", value: "\\\"", description: "\"\\\\\\\"\"" }, - peg$c77 = function() { return '"'; }, - peg$c78 = "\\", - peg$c79 = { type: "literal", value: "\\", description: "\"\\\\\"" }, - peg$c80 = /^[^"]/, - peg$c81 = { type: "class", value: "[^\"]", description: "[^\"]" }, - peg$c82 = function(ch) { return "\\" + ch; }, - peg$c83 = function(id) { + peg$c74 = "\"", + peg$c75 = { type: "literal", value: "\"", description: "\"\\\"\"" }, + peg$c76 = "\\\"", + peg$c77 = { type: "literal", value: "\\\"", description: "\"\\\\\\\"\"" }, + peg$c78 = function() { return '"'; }, + peg$c79 = "\\", + peg$c80 = { type: "literal", value: "\\", description: "\"\\\\\"" }, + peg$c81 = /^[^"]/, + peg$c82 = { type: "class", value: "[^\"]", description: "[^\"]" }, + peg$c83 = function(ch) { return "\\" + ch; }, + peg$c84 = function(id) { return id.join(""); }, - peg$c84 = "node", - peg$c85 = { type: "literal", value: "node", description: "\"node\"" }, - peg$c86 = function(k) { return k.toLowerCase(); }, - peg$c87 = "edge", - peg$c88 = { type: "literal", value: "edge", description: "\"edge\"" }, - peg$c89 = "graph", - peg$c90 = { type: "literal", value: "graph", description: "\"graph\"" }, - peg$c91 = "digraph", - peg$c92 = { type: "literal", value: "digraph", description: "\"digraph\"" }, - peg$c93 = "subgraph", - peg$c94 = { type: "literal", value: "subgraph", description: "\"subgraph\"" }, - peg$c95 = "strict", - peg$c96 = { type: "literal", value: "strict", description: "\"strict\"" }, - peg$c97 = function(graph) { + peg$c85 = "node", + peg$c86 = { type: "literal", value: "node", description: "\"node\"" }, + peg$c87 = function(k) { return k.toLowerCase(); }, + peg$c88 = "edge", + peg$c89 = { type: "literal", value: "edge", description: "\"edge\"" }, + peg$c90 = "graph", + peg$c91 = { type: "literal", value: "graph", description: "\"graph\"" }, + peg$c92 = "digraph", + peg$c93 = { type: "literal", value: "digraph", description: "\"digraph\"" }, + peg$c94 = "subgraph", + peg$c95 = { type: "literal", value: "subgraph", description: "\"subgraph\"" }, + peg$c96 = "strict", + peg$c97 = { type: "literal", value: "strict", description: "\"strict\"" }, + peg$c98 = function(graph) { directed = graph === "digraph"; return graph; }, - peg$c98 = { type: "other", description: "whitespace" }, - peg$c99 = /^[ \t\r\n]/, - peg$c100 = { type: "class", value: "[ \\t\\r\\n]", description: "[ \\t\\r\\n]" }, - peg$c101 = { type: "other", description: "comment" }, - peg$c102 = "//", - peg$c103 = { type: "literal", value: "//", description: "\"//\"" }, - peg$c104 = /^[^\n]/, - peg$c105 = { type: "class", value: "[^\\n]", description: "[^\\n]" }, - peg$c106 = "/*", - peg$c107 = { type: "literal", value: "/*", description: "\"/*\"" }, - peg$c108 = "*/", - peg$c109 = { type: "literal", value: "*/", description: "\"*/\"" }, - peg$c110 = { type: "any", description: "any character" }, + peg$c99 = { type: "other", description: "whitespace" }, + peg$c100 = /^[ \t\r\n]/, + peg$c101 = { type: "class", value: "[ \\t\\r\\n]", description: "[ \\t\\r\\n]" }, + peg$c102 = { type: "other", description: "comment" }, + peg$c103 = "//", + peg$c104 = { type: "literal", value: "//", description: "\"//\"" }, + peg$c105 = /^[^\n]/, + peg$c106 = { type: "class", value: "[^\\n]", description: "[^\\n]" }, + peg$c107 = "/*", + peg$c108 = { type: "literal", value: "/*", description: "\"/*\"" }, + peg$c109 = "*/", + peg$c110 = { type: "literal", value: "*/", description: "\"*/\"" }, + peg$c111 = { type: "any", description: "any character" }, peg$currPos = 0, peg$reportedPos = 0, @@ -1468,7 +1472,7 @@ module.exports = (function() { } if (s3 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c36(s1); + s1 = peg$c36(s1, s3); s0 = s1; } else { peg$currPos = s0; @@ -1550,7 +1554,8 @@ module.exports = (function() { s5 = peg$c2; } if (s5 !== peg$FAILED) { - s1 = [s1, s2, s3, s4, s5]; + peg$reportedPos = s0; + s1 = peg$c39(s3); s0 = s1; } else { peg$currPos = s0; @@ -1579,84 +1584,84 @@ module.exports = (function() { function peg$parsecompassPt() { var s0; - if (input.substr(peg$currPos, 2) === peg$c39) { - s0 = peg$c39; + if (input.substr(peg$currPos, 2) === peg$c40) { + s0 = peg$c40; peg$currPos += 2; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c40); } + if (peg$silentFails === 0) { peg$fail(peg$c41); } } if (s0 === peg$FAILED) { - if (input.substr(peg$currPos, 2) === peg$c41) { - s0 = peg$c41; + if (input.substr(peg$currPos, 2) === peg$c42) { + s0 = peg$c42; peg$currPos += 2; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c42); } + if (peg$silentFails === 0) { peg$fail(peg$c43); } } if (s0 === peg$FAILED) { - if (input.substr(peg$currPos, 2) === peg$c43) { - s0 = peg$c43; + if (input.substr(peg$currPos, 2) === peg$c44) { + s0 = peg$c44; peg$currPos += 2; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c44); } + if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s0 === peg$FAILED) { - if (input.substr(peg$currPos, 2) === peg$c45) { - s0 = peg$c45; + if (input.substr(peg$currPos, 2) === peg$c46) { + s0 = peg$c46; peg$currPos += 2; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c46); } + if (peg$silentFails === 0) { peg$fail(peg$c47); } } if (s0 === peg$FAILED) { if (input.charCodeAt(peg$currPos) === 110) { - s0 = peg$c47; + s0 = peg$c48; peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c48); } + if (peg$silentFails === 0) { peg$fail(peg$c49); } } if (s0 === peg$FAILED) { if (input.charCodeAt(peg$currPos) === 101) { - s0 = peg$c49; + s0 = peg$c50; peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c50); } + if (peg$silentFails === 0) { peg$fail(peg$c51); } } if (s0 === peg$FAILED) { if (input.charCodeAt(peg$currPos) === 115) { - s0 = peg$c51; + s0 = peg$c52; peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c52); } + if (peg$silentFails === 0) { peg$fail(peg$c53); } } if (s0 === peg$FAILED) { if (input.charCodeAt(peg$currPos) === 119) { - s0 = peg$c53; + s0 = peg$c54; peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c54); } + if (peg$silentFails === 0) { peg$fail(peg$c55); } } if (s0 === peg$FAILED) { if (input.charCodeAt(peg$currPos) === 99) { - s0 = peg$c55; + s0 = peg$c56; peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c56); } + if (peg$silentFails === 0) { peg$fail(peg$c57); } } if (s0 === peg$FAILED) { if (input.charCodeAt(peg$currPos) === 95) { - s0 = peg$c57; + s0 = peg$c58; peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c58); } + if (peg$silentFails === 0) { peg$fail(peg$c59); } } } } @@ -1676,35 +1681,35 @@ module.exports = (function() { peg$silentFails++; s0 = peg$currPos; - if (peg$c60.test(input.charAt(peg$currPos))) { + if (peg$c61.test(input.charAt(peg$currPos))) { s1 = input.charAt(peg$currPos); peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c61); } + if (peg$silentFails === 0) { peg$fail(peg$c62); } } if (s1 !== peg$FAILED) { s2 = []; - if (peg$c62.test(input.charAt(peg$currPos))) { + if (peg$c63.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c63); } + if (peg$silentFails === 0) { peg$fail(peg$c64); } } while (s3 !== peg$FAILED) { s2.push(s3); - if (peg$c62.test(input.charAt(peg$currPos))) { + if (peg$c63.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c63); } + if (peg$silentFails === 0) { peg$fail(peg$c64); } } } if (s2 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c64(s1, s2); + s1 = peg$c65(s1, s2); s0 = s1; } else { peg$currPos = s0; @@ -1717,41 +1722,41 @@ module.exports = (function() { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 45) { - s1 = peg$c65; + s1 = peg$c66; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c66); } + if (peg$silentFails === 0) { peg$fail(peg$c67); } } if (s1 === peg$FAILED) { s1 = peg$c2; } if (s1 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 46) { - s2 = peg$c67; + s2 = peg$c68; peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c68); } + if (peg$silentFails === 0) { peg$fail(peg$c69); } } if (s2 !== peg$FAILED) { s3 = []; - if (peg$c69.test(input.charAt(peg$currPos))) { + if (peg$c70.test(input.charAt(peg$currPos))) { s4 = input.charAt(peg$currPos); peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c70); } + if (peg$silentFails === 0) { peg$fail(peg$c71); } } if (s4 !== peg$FAILED) { while (s4 !== peg$FAILED) { s3.push(s4); - if (peg$c69.test(input.charAt(peg$currPos))) { + if (peg$c70.test(input.charAt(peg$currPos))) { s4 = input.charAt(peg$currPos); peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c70); } + if (peg$silentFails === 0) { peg$fail(peg$c71); } } } } else { @@ -1759,7 +1764,7 @@ module.exports = (function() { } if (s3 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c71(s1, s2, s3); + s1 = peg$c72(s1, s2, s3); s0 = s1; } else { peg$currPos = s0; @@ -1776,33 +1781,33 @@ module.exports = (function() { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 45) { - s1 = peg$c65; + s1 = peg$c66; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c66); } + if (peg$silentFails === 0) { peg$fail(peg$c67); } } if (s1 === peg$FAILED) { s1 = peg$c2; } if (s1 !== peg$FAILED) { s2 = []; - if (peg$c69.test(input.charAt(peg$currPos))) { + if (peg$c70.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c70); } + if (peg$silentFails === 0) { peg$fail(peg$c71); } } if (s3 !== peg$FAILED) { while (s3 !== peg$FAILED) { s2.push(s3); - if (peg$c69.test(input.charAt(peg$currPos))) { + if (peg$c70.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c70); } + if (peg$silentFails === 0) { peg$fail(peg$c71); } } } } else { @@ -1811,29 +1816,29 @@ module.exports = (function() { if (s2 !== peg$FAILED) { s3 = peg$currPos; if (input.charCodeAt(peg$currPos) === 46) { - s4 = peg$c67; + s4 = peg$c68; peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c68); } + if (peg$silentFails === 0) { peg$fail(peg$c69); } } if (s4 !== peg$FAILED) { s5 = []; - if (peg$c69.test(input.charAt(peg$currPos))) { + if (peg$c70.test(input.charAt(peg$currPos))) { s6 = input.charAt(peg$currPos); peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c70); } + if (peg$silentFails === 0) { peg$fail(peg$c71); } } while (s6 !== peg$FAILED) { s5.push(s6); - if (peg$c69.test(input.charAt(peg$currPos))) { + if (peg$c70.test(input.charAt(peg$currPos))) { s6 = input.charAt(peg$currPos); peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c70); } + if (peg$silentFails === 0) { peg$fail(peg$c71); } } } if (s5 !== peg$FAILED) { @@ -1852,7 +1857,7 @@ module.exports = (function() { } if (s3 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c72(s1, s2, s3); + s1 = peg$c73(s1, s2, s3); s0 = s1; } else { peg$currPos = s0; @@ -1869,47 +1874,47 @@ module.exports = (function() { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 34) { - s1 = peg$c73; + s1 = peg$c74; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c74); } + if (peg$silentFails === 0) { peg$fail(peg$c75); } } if (s1 !== peg$FAILED) { s2 = []; s3 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c75) { - s4 = peg$c75; + if (input.substr(peg$currPos, 2) === peg$c76) { + s4 = peg$c76; peg$currPos += 2; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c76); } + if (peg$silentFails === 0) { peg$fail(peg$c77); } } if (s4 !== peg$FAILED) { peg$reportedPos = s3; - s4 = peg$c77(); + s4 = peg$c78(); } s3 = s4; if (s3 === peg$FAILED) { s3 = peg$currPos; if (input.charCodeAt(peg$currPos) === 92) { - s4 = peg$c78; + s4 = peg$c79; peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c79); } + if (peg$silentFails === 0) { peg$fail(peg$c80); } } if (s4 !== peg$FAILED) { - if (peg$c80.test(input.charAt(peg$currPos))) { + if (peg$c81.test(input.charAt(peg$currPos))) { s5 = input.charAt(peg$currPos); peg$currPos++; } else { s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c81); } + if (peg$silentFails === 0) { peg$fail(peg$c82); } } if (s5 !== peg$FAILED) { peg$reportedPos = s3; - s4 = peg$c82(s5); + s4 = peg$c83(s5); s3 = s4; } else { peg$currPos = s3; @@ -1920,50 +1925,50 @@ module.exports = (function() { s3 = peg$c1; } if (s3 === peg$FAILED) { - if (peg$c80.test(input.charAt(peg$currPos))) { + if (peg$c81.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c81); } + if (peg$silentFails === 0) { peg$fail(peg$c82); } } } } while (s3 !== peg$FAILED) { s2.push(s3); s3 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c75) { - s4 = peg$c75; + if (input.substr(peg$currPos, 2) === peg$c76) { + s4 = peg$c76; peg$currPos += 2; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c76); } + if (peg$silentFails === 0) { peg$fail(peg$c77); } } if (s4 !== peg$FAILED) { peg$reportedPos = s3; - s4 = peg$c77(); + s4 = peg$c78(); } s3 = s4; if (s3 === peg$FAILED) { s3 = peg$currPos; if (input.charCodeAt(peg$currPos) === 92) { - s4 = peg$c78; + s4 = peg$c79; peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c79); } + if (peg$silentFails === 0) { peg$fail(peg$c80); } } if (s4 !== peg$FAILED) { - if (peg$c80.test(input.charAt(peg$currPos))) { + if (peg$c81.test(input.charAt(peg$currPos))) { s5 = input.charAt(peg$currPos); peg$currPos++; } else { s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c81); } + if (peg$silentFails === 0) { peg$fail(peg$c82); } } if (s5 !== peg$FAILED) { peg$reportedPos = s3; - s4 = peg$c82(s5); + s4 = peg$c83(s5); s3 = s4; } else { peg$currPos = s3; @@ -1974,27 +1979,27 @@ module.exports = (function() { s3 = peg$c1; } if (s3 === peg$FAILED) { - if (peg$c80.test(input.charAt(peg$currPos))) { + if (peg$c81.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c81); } + if (peg$silentFails === 0) { peg$fail(peg$c82); } } } } } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 34) { - s3 = peg$c73; + s3 = peg$c74; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c74); } + if (peg$silentFails === 0) { peg$fail(peg$c75); } } if (s3 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c83(s2); + s1 = peg$c84(s2); s0 = s1; } else { peg$currPos = s0; @@ -2014,7 +2019,7 @@ module.exports = (function() { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c59); } + if (peg$silentFails === 0) { peg$fail(peg$c60); } } return s0; @@ -2024,16 +2029,16 @@ module.exports = (function() { var s0, s1; s0 = peg$currPos; - if (input.substr(peg$currPos, 4).toLowerCase() === peg$c84) { + if (input.substr(peg$currPos, 4).toLowerCase() === peg$c85) { s1 = input.substr(peg$currPos, 4); peg$currPos += 4; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c85); } + if (peg$silentFails === 0) { peg$fail(peg$c86); } } if (s1 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c86(s1); + s1 = peg$c87(s1); } s0 = s1; @@ -2044,16 +2049,16 @@ module.exports = (function() { var s0, s1; s0 = peg$currPos; - if (input.substr(peg$currPos, 4).toLowerCase() === peg$c87) { + if (input.substr(peg$currPos, 4).toLowerCase() === peg$c88) { s1 = input.substr(peg$currPos, 4); peg$currPos += 4; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c88); } + if (peg$silentFails === 0) { peg$fail(peg$c89); } } if (s1 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c86(s1); + s1 = peg$c87(s1); } s0 = s1; @@ -2064,16 +2069,16 @@ module.exports = (function() { var s0, s1; s0 = peg$currPos; - if (input.substr(peg$currPos, 5).toLowerCase() === peg$c89) { + if (input.substr(peg$currPos, 5).toLowerCase() === peg$c90) { s1 = input.substr(peg$currPos, 5); peg$currPos += 5; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c90); } + if (peg$silentFails === 0) { peg$fail(peg$c91); } } if (s1 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c86(s1); + s1 = peg$c87(s1); } s0 = s1; @@ -2084,16 +2089,16 @@ module.exports = (function() { var s0, s1; s0 = peg$currPos; - if (input.substr(peg$currPos, 7).toLowerCase() === peg$c91) { + if (input.substr(peg$currPos, 7).toLowerCase() === peg$c92) { s1 = input.substr(peg$currPos, 7); peg$currPos += 7; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c92); } + if (peg$silentFails === 0) { peg$fail(peg$c93); } } if (s1 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c86(s1); + s1 = peg$c87(s1); } s0 = s1; @@ -2104,16 +2109,16 @@ module.exports = (function() { var s0, s1; s0 = peg$currPos; - if (input.substr(peg$currPos, 8).toLowerCase() === peg$c93) { + if (input.substr(peg$currPos, 8).toLowerCase() === peg$c94) { s1 = input.substr(peg$currPos, 8); peg$currPos += 8; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c94); } + if (peg$silentFails === 0) { peg$fail(peg$c95); } } if (s1 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c86(s1); + s1 = peg$c87(s1); } s0 = s1; @@ -2124,16 +2129,16 @@ module.exports = (function() { var s0, s1; s0 = peg$currPos; - if (input.substr(peg$currPos, 6).toLowerCase() === peg$c95) { + if (input.substr(peg$currPos, 6).toLowerCase() === peg$c96) { s1 = input.substr(peg$currPos, 6); peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c96); } + if (peg$silentFails === 0) { peg$fail(peg$c97); } } if (s1 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c86(s1); + s1 = peg$c87(s1); } s0 = s1; @@ -2149,7 +2154,7 @@ module.exports = (function() { s1 = peg$parsedigraph(); if (s1 !== peg$FAILED) { peg$reportedPos = s0; - s1 = peg$c97(s1); + s1 = peg$c98(s1); } s0 = s1; } @@ -2162,22 +2167,22 @@ module.exports = (function() { peg$silentFails++; s0 = []; - if (peg$c99.test(input.charAt(peg$currPos))) { + if (peg$c100.test(input.charAt(peg$currPos))) { s1 = input.charAt(peg$currPos); peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c100); } + if (peg$silentFails === 0) { peg$fail(peg$c101); } } if (s1 !== peg$FAILED) { while (s1 !== peg$FAILED) { s0.push(s1); - if (peg$c99.test(input.charAt(peg$currPos))) { + if (peg$c100.test(input.charAt(peg$currPos))) { s1 = input.charAt(peg$currPos); peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c100); } + if (peg$silentFails === 0) { peg$fail(peg$c101); } } } } else { @@ -2186,7 +2191,7 @@ module.exports = (function() { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c98); } + if (peg$silentFails === 0) { peg$fail(peg$c99); } } return s0; @@ -2197,30 +2202,30 @@ module.exports = (function() { peg$silentFails++; s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c102) { - s1 = peg$c102; + if (input.substr(peg$currPos, 2) === peg$c103) { + s1 = peg$c103; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c103); } + if (peg$silentFails === 0) { peg$fail(peg$c104); } } if (s1 !== peg$FAILED) { s2 = []; - if (peg$c104.test(input.charAt(peg$currPos))) { + if (peg$c105.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c105); } + if (peg$silentFails === 0) { peg$fail(peg$c106); } } while (s3 !== peg$FAILED) { s2.push(s3); - if (peg$c104.test(input.charAt(peg$currPos))) { + if (peg$c105.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c105); } + if (peg$silentFails === 0) { peg$fail(peg$c106); } } } if (s2 !== peg$FAILED) { @@ -2236,24 +2241,24 @@ module.exports = (function() { } if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c106) { - s1 = peg$c106; + if (input.substr(peg$currPos, 2) === peg$c107) { + s1 = peg$c107; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c107); } + if (peg$silentFails === 0) { peg$fail(peg$c108); } } if (s1 !== peg$FAILED) { s2 = []; s3 = peg$currPos; s4 = peg$currPos; peg$silentFails++; - if (input.substr(peg$currPos, 2) === peg$c108) { - s5 = peg$c108; + if (input.substr(peg$currPos, 2) === peg$c109) { + s5 = peg$c109; peg$currPos += 2; } else { s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c109); } + if (peg$silentFails === 0) { peg$fail(peg$c110); } } peg$silentFails--; if (s5 === peg$FAILED) { @@ -2268,7 +2273,7 @@ module.exports = (function() { peg$currPos++; } else { s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c110); } + if (peg$silentFails === 0) { peg$fail(peg$c111); } } if (s5 !== peg$FAILED) { s4 = [s4, s5]; @@ -2286,12 +2291,12 @@ module.exports = (function() { s3 = peg$currPos; s4 = peg$currPos; peg$silentFails++; - if (input.substr(peg$currPos, 2) === peg$c108) { - s5 = peg$c108; + if (input.substr(peg$currPos, 2) === peg$c109) { + s5 = peg$c109; peg$currPos += 2; } else { s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c109); } + if (peg$silentFails === 0) { peg$fail(peg$c110); } } peg$silentFails--; if (s5 === peg$FAILED) { @@ -2306,7 +2311,7 @@ module.exports = (function() { peg$currPos++; } else { s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c110); } + if (peg$silentFails === 0) { peg$fail(peg$c111); } } if (s5 !== peg$FAILED) { s4 = [s4, s5]; @@ -2321,12 +2326,12 @@ module.exports = (function() { } } if (s2 !== peg$FAILED) { - if (input.substr(peg$currPos, 2) === peg$c108) { - s3 = peg$c108; + if (input.substr(peg$currPos, 2) === peg$c109) { + s3 = peg$c109; peg$currPos += 2; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c109); } + if (peg$silentFails === 0) { peg$fail(peg$c110); } } if (s3 !== peg$FAILED) { s1 = [s1, s2, s3]; @@ -2347,7 +2352,7 @@ module.exports = (function() { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c101); } + if (peg$silentFails === 0) { peg$fail(peg$c102); } } return s0; diff --git a/src/dot-grammar.pegjs b/src/dot-grammar.pegjs index cbd311e..2bb7a95 100644 --- a/src/dot-grammar.pegjs +++ b/src/dot-grammar.pegjs @@ -43,10 +43,10 @@ inlineAttrStmt var attrs = {}; attrs[k] = v; return { type: "inlineAttr", attrs: attrs }; - } + } nodeStmt - = id:nodeId _* attrs:attrList? { return {type: "node", id: id, attrs: attrs || {}}; } + = nodeId:nodeId _* attrs:attrList? { return {type: "node", id: nodeId.id, port: nodeId.port, attrs: attrs || {}}; } edgeStmt = lhs:(nodeIdOrSubgraph) _* rhs:edgeRHS _* attrs:attrList? { @@ -104,13 +104,16 @@ idDef nodeIdOrSubgraph = subgraphStmt - / id:nodeId { return { type: "node", id: id, attrs: {} }; } + / nodeId:nodeId { + var attrs = {} + return { type: "node", id: nodeId.id, port: nodeId.port, attrs: attrs }; + } nodeId - = id:id _* port? { return id; } + = id:id _* port:port? { return { id: id, port: port }; } port - = ':' _* id _* (':' _* compassPt)? + = ':' _* id:id _* (':' _* compassPt)? { return id; } compassPt = "ne" / "se" / "sw" / "nw" / "n" / "e" / "s" / "w" / "c" / "_" diff --git a/test/read-one-test.js b/test/read-one-test.js index f5dee4d..92b2fe4 100644 --- a/test/read-one-test.js +++ b/test/read-one-test.js @@ -339,6 +339,29 @@ describe("read", function() { }); }); + describe("ports", function() { + it("allows ports on edges", function() { + var g = read("digraph { a:foo -> b:bar -> c:baz }"); + expect(g.edge("a", "b")).to.eql({ $out: "foo", $in: "bar" }); + expect(g.edge("b", "c")).to.eql({ $out: "bar", $in: "baz" }); + }); + + it("allow recieving port", function() { + var g = read("digraph { a -> b:bar }"); + expect(g.edge("a", "b")).to.eql({ $in: "bar" }); + }); + + it("allow sending port", function() { + var g = read("digraph { a:foo -> b }"); + expect(g.edge("a", "b")).to.eql({ $out: "foo" }); + }); + + it("allow multiple outs", function() { + var g = read("digraph { a:foo -> b; a:bar -> b; a:baz -> b }"); + expect(g.edgeCount()).to.equal(3); + }); + }); + describe("failure cases", function() { it("fails if the graph block is not closed", function() { expect(function() { read("digraph {"); }).to.throw();