diff --git a/src/index.ts b/src/index.ts index 223b7f0..96a48e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,18 @@ import * as fs from 'fs/promises'; +import { Node } from './node-types/abstract-node'; import { QuestionNode } from "./node-types/question"; import { ClaimNode } from './node-types/claim'; import { EvidenceNode } from './node-types/evidence'; import { SourceNode } from './node-types/source'; +import { Edge } from './edge-types/abstract-edge'; +import { GenerateEdge } from './edge-types/generate'; +import { InformEdge } from './edge-types/inform'; +import { SupportEdge } from './edge-types/support'; +import { OpposeEdge } from './edge-types/oppose'; + export class Compiler { public static nodes = [ @@ -15,6 +22,13 @@ export class Compiler { SourceNode ]; + public static edges = [ + GenerateEdge, + InformEdge, + SupportEdge, + OpposeEdge + ]; + public static getNodeType(name: string) { for (const nodeType of Compiler.nodes) { if (nodeType.TYPE_PATTERN.test(name)) return nodeType; @@ -22,14 +36,21 @@ export class Compiler { throw new Error(`File name does not match any known node type \n - ${name}`); } + public static getEdgeType(name: string) { + for (const edgeType of Compiler.edges) { + if (name.includes(edgeType.EDGE_TYPE)) return edgeType; + } + throw new Error(`Edge name does not match any known edge types \n - ${name}`); + } - static async loadNodes(directory: string) { - console.log('Loading nodes...'); + public static async loadNodes(directory: string) { + console.log('\nLoading nodes...'); const nodes = []; const files = await fs.readdir(directory); for (const file of files) try { + const data = await fs.readFile(`${directory}/${file}`, 'utf-8'); const NodeType = Compiler.getNodeType(file); - const node = NodeType.load(file, ""); + const node = NodeType.load(file, data); nodes.push(node); console.log(`Loaded ${NodeType.TYPE} node "${node.name}"`); } catch (err) { @@ -38,8 +59,62 @@ export class Compiler { return nodes; } + public static loadEdges(nodes: Node[]) { + console.log('\nLoading edges...'); + const edges = []; + for (const node of nodes) { + const references = node.parseDataEdges(); + for (const reference of references) try { + const source = node; + const destination = nodes.find(node => node.name === reference.referenceName); + if (!destination) throw new Error(`Could not find referenced node "${reference.referenceName}"`); + const EdgeClass = Compiler.getEdgeType(reference.edgeType); + const edge = EdgeClass.createEdge(source, destination); + edges.push(edge); + console.log(`Loaded ${EdgeClass.EDGE_TYPE} edge from "${edge.source.name}" to "${edge.reference.name}"`); + } catch (err) { + console.error(err.message); + } + } + return edges; + } + + public static registerEdges(edges: Edge[]) { + console.log('\nRegistering edges...'); + for (const edge of edges) try { + edge.source.registerEdge(edge); + edge.reference.registerEdge(edge); + console.log(`Registered ${edge.constructor.name} from "${edge.source.name}" to "${edge.reference.name}"`); + } catch (err) { + console.error(err.message); + } + } + + public static traverseGraph(node: Node, relationship: string | null, indent: number = 0) { + const currentIndent = ' '.repeat(indent); + const nodeType = node.constructor as typeof Node; + const bullet = relationship ? '> ' + relationship : '-'; + console.log(`${currentIndent}${bullet} ${nodeType.SYMBOL} ${node.name}`); + for (const edge of node.incomingReferences) { + if (edge.source instanceof QuestionNode) continue; + const edgeType = edge.constructor as typeof Edge; + Compiler.traverseGraph(edge.source, edgeType.RELATIONSHIP, indent + 2); + } + } } -const graphPath = "./example_graph"; -Compiler.loadNodes(graphPath); \ No newline at end of file +async function runTest() { + console.log("Running Test"); + const graphPath = "./example_graph"; + const nodes = await Compiler.loadNodes(graphPath); + const edges = await Compiler.loadEdges(nodes); + Compiler.registerEdges(edges); + console.log("\nGenerating Artifact..."); + const questions = nodes.filter(node => node instanceof QuestionNode); + for (const question of questions) Compiler.traverseGraph(question, null); + console.log("\nTest Complete"); +} + + +runTest().then(() => console.log("Done")); \ No newline at end of file diff --git a/src/node-types/abstract-node.ts b/src/node-types/abstract-node.ts index 5c11a88..9e79a4d 100644 --- a/src/node-types/abstract-node.ts +++ b/src/node-types/abstract-node.ts @@ -1,19 +1,46 @@ +import { Edge } from "../edge-types/abstract-edge"; + export abstract class Node { - public static readonly TYPE: string; - public static readonly TYPE_PATTERN: RegExp; - - public readonly name: string; - public readonly data: string; - - public constructor(name: string, data: string) { - this.name = name; - this.data = data; - } - - public static load(fileName: string, fileData: string): Node { - throw new Error('You must override the static parse method in specific node implementations'); - } + public static readonly EDGE_PATTERN: RegExp = /^- (\S+) \[\[({.+}) (.+)\]\]$/; + public static readonly TYPE: string; + public static readonly TYPE_PATTERN: RegExp; + public static readonly SYMBOL: string; + + public readonly name: string; + public readonly data: string; + public readonly outgoingReferences: Edge[] = []; + public readonly incomingReferences: Edge[] = []; + + public constructor(name: string, data: string) { + this.name = name; + this.data = data; + } + + public static load(fileName: string, fileData: string): Node { + throw new Error('You must override the static parse method in specific node implementations'); + } + + public parseDataEdges() { + type EdgeData = {edgeType: string, referenceType: string, referenceName: string}; + const edges: EdgeData[] = []; + const lines = this.data.split('\n'); + for (const line of lines) { + const match = line.match(Node.EDGE_PATTERN); + if (match) edges.push({ + edgeType: match[1], + referenceType: match[2], + referenceName: match[3] + }); + } + return edges; + } + + public registerEdge(edge: Edge) { + if (edge.source === this) this.outgoingReferences.push(edge); + else if (edge.reference === this) this.incomingReferences.push(edge); + else throw new Error('Edge does not reference this node'); + } } \ No newline at end of file diff --git a/src/node-types/claim.ts b/src/node-types/claim.ts index 0f6721c..e8bc6e6 100644 --- a/src/node-types/claim.ts +++ b/src/node-types/claim.ts @@ -5,6 +5,7 @@ export class ClaimNode extends Node{ public static readonly TYPE = 'claim'; public static readonly TYPE_PATTERN = /^\{CLM\}/; + public static readonly SYMBOL = "{CLM}"; public constructor(name: string, data: string) { super(name, data); diff --git a/src/node-types/evidence.ts b/src/node-types/evidence.ts index ae4be4e..4f2827f 100644 --- a/src/node-types/evidence.ts +++ b/src/node-types/evidence.ts @@ -5,6 +5,7 @@ export class EvidenceNode extends Node{ public static readonly TYPE = 'evidence'; public static readonly TYPE_PATTERN = /^\{EVD\}/; + public static readonly SYMBOL = "{EVD}"; public constructor(name: string, data: string) { super(name, data); diff --git a/src/node-types/question.ts b/src/node-types/question.ts index 753760a..0eea24f 100644 --- a/src/node-types/question.ts +++ b/src/node-types/question.ts @@ -5,6 +5,7 @@ export class QuestionNode extends Node{ public static readonly TYPE = 'question'; public static readonly TYPE_PATTERN = /^\{QUE\}/; + public static readonly SYMBOL = "{QUE}"; public constructor(name: string, data: string) { super(name, data); diff --git a/src/node-types/source.ts b/src/node-types/source.ts index ba01a73..1417caa 100644 --- a/src/node-types/source.ts +++ b/src/node-types/source.ts @@ -5,6 +5,7 @@ export class SourceNode extends Node{ public static readonly TYPE = 'source'; public static readonly TYPE_PATTERN = /^\{SRC\}/; + public static readonly SYMBOL = "{SRC}"; public constructor(name: string, data: string) { super(name, data);