Browse Source

Implemented support for node sets in DOT parser

css_transitions
josdejong 11 years ago
parent
commit
b3d2a1499a
6 changed files with 308 additions and 90 deletions
  1. +1
    -2
      examples/graph/15_dot_language_playground.html
  2. +133
    -43
      src/graph/dotparser.js
  3. +3
    -0
      test/dot.txt
  4. +36
    -0
      test/dotparser.js
  5. +132
    -42
      vis.js
  6. +3
    -3
      vis.min.js

+ 1
- 2
examples/graph/15_dot_language_playground.html View File

@ -140,8 +140,7 @@ digraph {
A -> A[label=0.5]; A -> A[label=0.5];
B -> B[label=1.2] -> C[label=0.7] -- A; B -> B[label=1.2] -> C[label=0.7] -- A;
B -> D; B -> D;
D -> B;
D -> C;
D -> {B; C}
D -> E[label=0.2]; D -> E[label=0.2];
F -> F; F -> F;
A [ A [

+ 133
- 43
src/graph/dotparser.js View File

@ -399,80 +399,166 @@
*/ */
function parseStatement() { function parseStatement() {
var attr; var attr;
var id = token; // can be as string or a number
getToken();
attr = parseAttributeStatement();
if (!attr) {
var id = token; // can be a string or a number
getToken();
if (token == '=') {
// id statement
getToken();
if (!graph.attr) {
graph.attr = {};
}
graph.attr[id] = token;
getToken();
}
else {
// node statement
var node = {
id: id
};
attr = parseAttributes();
if (attr) {
node.attr = attr;
}
addNode(node);
// edge statements
parseEdge(id);
}
}
}
/**
* parse an attribute statement like "node [shape=circle fontSize=16]".
* Available keywords are 'node', 'edge', 'graph'
* @returns {Object | null} attr
*/
function parseAttributeStatement () {
var attr = null;
// attribute statements // attribute statements
if (id == 'node') {
if (token == 'node') {
getToken();
// node attributes // node attributes
attr = parseAttributes(); attr = parseAttributes();
if (attr) { if (attr) {
nodeAttr = merge(nodeAttr, attr); nodeAttr = merge(nodeAttr, attr);
} }
} }
else if (id == 'edge') {
else if (token == 'edge') {
getToken();
// edge attributes // edge attributes
attr = parseAttributes(); attr = parseAttributes();
if (attr) { if (attr) {
edgeAttr = merge(edgeAttr, attr); edgeAttr = merge(edgeAttr, attr);
} }
} }
else if (id == 'graph') {
else if (token == 'graph') {
getToken();
// graph attributes // graph attributes
attr = parseAttributes(); attr = parseAttributes();
if (attr) { if (attr) {
graph.attr = merge(graph.attr, attr); graph.attr = merge(graph.attr, attr);
} }
} }
else {
if (token == '=') {
// id statement
getToken();
if (!graph.attr) {
graph.attr = {};
}
graph.attr[id] = token;
getToken();
return attr;
}
/**
* Parse an edge or a series of edges
* @param {String | Number} from Id of the from node
*/
function parseEdge(from) {
while (token == '->' || token == '--') {
var type = token;
getToken();
if (token == '{') {
// parse a set of nodes, like "node1 -> {node2, node3}"
parseEdgeSet(from, type);
break;
} }
else { else {
// node statement
var node = {
id: id
// parse a single edge, like "node1 -> node2 -> node3"
var to = token;
addNode({
id: to
});
getToken();
var attr = parseAttributes();
// create edge
var edge = {
from: from,
to: to,
type: type
}; };
attr = parseAttributes();
if (attr) { if (attr) {
node.attr = attr;
edge.attr = attr;
} }
addNode(node);
addEdge(edge);
// edge statements
var from = id;
while (token == '->' || token == '--') {
var type = token;
getToken();
from = to;
}
}
}
var to = token;
addNode({
id: to
});
getToken();
attr = parseAttributes();
// create edge
var edge = {
from: from,
to: to,
type: type
};
if (attr) {
edge.attr = attr;
}
addEdge(edge);
/**
* Parse a set of nodes, like "{node1; node2; node3}"
* @param {String | Number} from Id of the from node
* @param {String} type Edge type, '--' or '->'
* @return {Node[] | null} nodes
*/
function parseEdgeSet(from, type) {
var nodes = null;
if (token == '{') {
getToken();
while (token !== '' && token != '}') {
// create to node
if (tokenType != TOKENTYPE.IDENTIFIER) {
throw newSyntaxError('Identifier expected');
}
var to = token;
addNode({
id: to
});
getToken();
from = to;
// create edge
var edge = {
from: from,
to: to,
type: type
};
var attr = parseAttributes();
if (attr) {
edge.attr = attr;
}
addEdge(edge);
// separator
if (token == ';') {
getToken();
} }
} }
// closing bracket
if (token != '}') {
throw newSyntaxError('bracket } expected');
}
getToken();
} }
return nodes;
} }
/** /**
@ -507,6 +593,10 @@
getToken(); getToken();
} }
} }
if (token != ']') {
throw newSyntaxError('Bracket ] expected');
}
getToken(); getToken();
return attr; return attr;

+ 3
- 0
test/dot.txt View File

@ -17,5 +17,8 @@ digraph test_graph {
"node2" -> node3 [label = "b" ]; "node2" -> node3 [label = "b" ];
"node1" -- "node4" [ label = "c" ]; "node1" -- "node4" [ label = "c" ];
node3-> node4 [ label=d] -> node5 -> 6 node3-> node4 [ label=d] -> node5 -> 6
A -> {B C}
} }

+ 36
- 0
test/dotparser.js View File

@ -55,6 +55,24 @@ fs.readFile('test/dot.txt', function (err, data) {
"attr": { "attr": {
"shape": "circle" "shape": "circle"
} }
},
{
"id": "A",
"attr": {
"shape": "circle"
}
},
{
"id": "B",
"attr": {
"shape": "circle"
}
},
{
"id": "C",
"attr": {
"shape": "circle"
}
} }
], ],
"edges": [ "edges": [
@ -115,6 +133,24 @@ fs.readFile('test/dot.txt', function (err, data) {
"length": 170, "length": 170,
"fontSize": 12 "fontSize": 12
} }
},
{
"from": "A",
"to": "B",
"type": "->",
"attr": {
"length": 170,
"fontSize": 12
}
},
{
"from": "A",
"to": "C",
"type": "->",
"attr": {
"length": 170,
"fontSize": 12
}
} }
] ]
}); });

+ 132
- 42
vis.js View File

@ -7207,80 +7207,166 @@ Timeline.prototype.getItemRange = function getItemRange() {
*/ */
function parseStatement() { function parseStatement() {
var attr; var attr;
var id = token; // can be as string or a number
getToken();
attr = parseAttributeStatement();
if (!attr) {
var id = token; // can be a string or a number
getToken();
if (token == '=') {
// id statement
getToken();
if (!graph.attr) {
graph.attr = {};
}
graph.attr[id] = token;
getToken();
}
else {
// node statement
var node = {
id: id
};
attr = parseAttributes();
if (attr) {
node.attr = attr;
}
addNode(node);
// edge statements
parseEdge(id);
}
}
}
/**
* parse an attribute statement like "node [shape=circle fontSize=16]".
* Available keywords are 'node', 'edge', 'graph'
* @returns {Object | null} attr
*/
function parseAttributeStatement () {
var attr = null;
// attribute statements // attribute statements
if (id == 'node') {
if (token == 'node') {
getToken();
// node attributes // node attributes
attr = parseAttributes(); attr = parseAttributes();
if (attr) { if (attr) {
nodeAttr = merge(nodeAttr, attr); nodeAttr = merge(nodeAttr, attr);
} }
} }
else if (id == 'edge') {
else if (token == 'edge') {
getToken();
// edge attributes // edge attributes
attr = parseAttributes(); attr = parseAttributes();
if (attr) { if (attr) {
edgeAttr = merge(edgeAttr, attr); edgeAttr = merge(edgeAttr, attr);
} }
} }
else if (id == 'graph') {
else if (token == 'graph') {
getToken();
// graph attributes // graph attributes
attr = parseAttributes(); attr = parseAttributes();
if (attr) { if (attr) {
graph.attr = merge(graph.attr, attr); graph.attr = merge(graph.attr, attr);
} }
} }
else {
if (token == '=') {
// id statement
getToken();
if (!graph.attr) {
graph.attr = {};
}
graph.attr[id] = token;
getToken();
return attr;
}
/**
* Parse an edge or a series of edges
* @param {String | Number} from Id of the from node
*/
function parseEdge(from) {
while (token == '->' || token == '--') {
var type = token;
getToken();
if (token == '{') {
// parse a set of nodes, like "node1 -> {node2, node3}"
parseEdgeSet(from, type);
break;
} }
else { else {
// node statement
var node = {
id: id
// parse a single edge, like "node1 -> node2 -> node3"
var to = token;
addNode({
id: to
});
getToken();
var attr = parseAttributes();
// create edge
var edge = {
from: from,
to: to,
type: type
}; };
attr = parseAttributes();
if (attr) { if (attr) {
node.attr = attr;
edge.attr = attr;
} }
addNode(node);
addEdge(edge);
// edge statements
var from = id;
while (token == '->' || token == '--') {
var type = token;
getToken();
from = to;
}
}
}
var to = token;
addNode({
id: to
});
getToken();
attr = parseAttributes();
/**
* Parse a set of nodes, like "{node1; node2; node3}"
* @param {String | Number} from Id of the from node
* @param {String} type Edge type, '--' or '->'
* @return {Node[] | null} nodes
*/
function parseEdgeSet(from, type) {
var nodes = null;
// create edge
var edge = {
from: from,
to: to,
type: type
};
if (attr) {
edge.attr = attr;
}
addEdge(edge);
if (token == '{') {
getToken();
while (token !== '' && token != '}') {
// create to node
if (tokenType != TOKENTYPE.IDENTIFIER) {
throw newSyntaxError('Identifier expected');
}
var to = token;
addNode({
id: to
});
getToken();
from = to;
// create edge
var edge = {
from: from,
to: to,
type: type
};
var attr = parseAttributes();
if (attr) {
edge.attr = attr;
}
addEdge(edge);
// separator
if (token == ';') {
getToken();
} }
} }
// closing bracket
if (token != '}') {
throw newSyntaxError('bracket } expected');
}
getToken();
} }
return nodes;
} }
/** /**
@ -7315,6 +7401,10 @@ Timeline.prototype.getItemRange = function getItemRange() {
getToken(); getToken();
} }
} }
if (token != ']') {
throw newSyntaxError('Bracket ] expected');
}
getToken(); getToken();
return attr; return attr;

+ 3
- 3
vis.min.js
File diff suppressed because it is too large
View File


Loading…
Cancel
Save