Skip to content

Commit

Permalink
feat(text): allow multiple paths separated by semicolons
Browse files Browse the repository at this point in the history
  • Loading branch information
davidballester committed Aug 9, 2019
1 parent 1fbf9c9 commit 9c53f69
Show file tree
Hide file tree
Showing 2 changed files with 287 additions and 47 deletions.
143 changes: 96 additions & 47 deletions src/services/graph-grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ import uuid from 'uuid/v4';
const grammar = `
Grapher {
paths
= pathWithSeparator+ path --multiplePaths
| path --singlePath
pathWithSeparator = path ";"
path
= partialPath+ node --partials
| node --node
Expand Down Expand Up @@ -36,62 +42,24 @@ Grapher {
}
`;

const mapGroup = (group) => {
return {
id: uuid(),
name: group.name,
};
};
const mapNode = (node) =>
_omitBy(
{
id: node.id,
groups: !!node.groups ? node.groups.map(mapGroup) : undefined,
},
_isNil
);

class GraphGrammar {
grammar;
semantics;

async initialize() {
this.grammar = ohm.grammar(grammar);
this.semantics = this.grammar.createSemantics().addOperation('eval', {
path_partials: (partialPaths, node) => {
const entities = _flattenDeep([...partialPaths.eval(), node.eval()]);
const groups = entities.filter(({ type }) => type === 'group');
const nodesAndLinks = entities.filter(({ type }) => type !== 'group');
return {
nodes: nodesAndLinks.filter((entity) => entity.type === 'node').map(mapNode),
links: nodesAndLinks
.map((entity, index) => [entity, index])
.filter(([entity]) => entity.type === 'link')
.map(([entity, index]) => {
const source = entity.direction === 'back' ? nodesAndLinks[index + 1].id : nodesAndLinks[index - 1].id;
const target = entity.direction === 'back' ? nodesAndLinks[index - 1].id : nodesAndLinks[index + 1].id;
const label = entity.label || `${source}-${target}`;
return {
id: entity.id,
label,
source,
target,
groups: !!entity.groups ? entity.groups.map(mapGroup) : undefined,
};
}),
groups: groups.map(mapGroup).filter((item, index, groups) => groups.findIndex((candidate) => candidate.name === item.name) === index),
};
paths_multiplePaths: (pathWithSeparators, path) => {
const entities = _flattenDeep([...pathWithSeparators.eval(), ...path.eval()]);
return mapEntities(entities);
},
path_node: (node) => {
const entities = node.eval();
return {
nodes: entities.filter(({ type }) => type === 'node').map(mapNode),
groups: entities
.filter(({ type }) => type === 'group')
.map(mapGroup)
.filter((item, index, groups) => groups.findIndex((candidate) => candidate.name === item.name) === index),
};
paths_singlePath: (path) => {
const entities = path.eval();
return mapEntities(entities);
},
pathWithSeparator: (path, separator) => path.eval(),
path_partials: (partialPaths, node) => _flattenDeep([...partialPaths.eval(), node.eval()]),
path_node: (node) => node.eval(),
partialPath: (node, link) => _flattenDeep([node.eval(), link.eval()]),
node_nodeNoGroups: (open, identifier, close) => [
{
Expand Down Expand Up @@ -200,3 +168,84 @@ class GraphGrammar {
const graphGrammar = new GraphGrammar();
graphGrammar.initialize();
export default graphGrammar;

const mapGroup = (group) => {
return {
id: uuid(),
name: group.name,
};
};

const mapNode = (node) =>
_omitBy(
{
id: node.id,
groups: !!node.groups ? node.groups.map(mapGroup) : [],
},
_isNil
);

const mapEntities = (entities) => {
const groups = entities
.filter(({ type }) => type === 'group')
.map(mapGroup)
.filter((item, index, groups) => groups.findIndex((candidate) => candidate.name === item.name) === index);
const nodesAndLinks = entities.filter(({ type }) => type !== 'group');
const nodes = entities
.filter(({ type }) => type === 'node')
.map(mapNode)
.map(({ groups, ...node }) => ({
...node,
groups: groups.map(({ name }) => groups.find(({ name: candidateName }) => candidateName === name)),
}))
.reduce((allNodes, node) => {
const existingNode = allNodes[node.id] || {};
const existingGroups = existingNode.groups || [];
return {
...allNodes,
[node.id]: {
...existingNode,
...node,
groups: [...existingGroups, ...node.groups].filter(
(item, index, groups) => groups.findIndex((candidate) => candidate.name === item.name) === index
),
},
};
}, {});
const links = nodesAndLinks
.map((entity, index) => [entity, index])
.filter(([entity]) => entity.type === 'link')
.map(([entity, index]) => {
const source = entity.direction === 'back' ? nodesAndLinks[index + 1].id : nodesAndLinks[index - 1].id;
const target = entity.direction === 'back' ? nodesAndLinks[index - 1].id : nodesAndLinks[index + 1].id;
const label = entity.label || `${source}-${target}`;
const linkGroups = (entity.groups || []).map(({ name }) => groups.find(({ name: candidateName }) => candidateName === name));
return {
id: entity.id,
label,
source,
target,
groups: linkGroups,
};
})
.reduce((linksBySourceAndTarget, link) => {
const sourceAndTargetId = `${link.source}-${link.target}`;
const existingLink = linksBySourceAndTarget[sourceAndTargetId] || {};
const existingGroups = existingLink.groups || [];
return {
...linksBySourceAndTarget,
[sourceAndTargetId]: {
...existingLink,
...link,
groups: [...existingGroups, ...link.groups].filter(
(item, index, groups) => groups.findIndex((candidate) => candidate.name === item.name) === index
),
},
};
}, {});
return {
nodes: Object.keys(nodes).map((nodeId) => nodes[nodeId]),
links: Object.keys(links).map((key) => links[key]),
groups,
};
};
Loading

0 comments on commit 9c53f69

Please sign in to comment.