diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx
new file mode 100644
index 00000000..aa4edbbd
--- /dev/null
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx
@@ -0,0 +1,18 @@
+import PrettyJson from '../../components/Common/PrettyJSON';
+import { useRSForm } from '../../context/RSFormContext';
+import { GraphNode } from '../../utils/Graph';
+
+function EditorTermGraph() {
+ const { schema } = useRSForm();
+
+ const data: GraphNode[] = [];
+ schema?.graph.visitDFS(node => data.push(node));
+
+ return (
+
+ );
+}
+
+export default EditorTermGraph;
diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx
index 0372de1e..6a0bb36b 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx
@@ -17,13 +17,15 @@ import DlgUploadRSForm from './DlgUploadRSForm';
import EditorConstituenta from './EditorConstituenta';
import EditorItems from './EditorItems';
import EditorRSForm from './EditorRSForm';
+import EditorTermGraph from './EditorTermGraph';
import RSFormStats from './elements/RSFormStats';
import RSTabsMenu from './RSTabsMenu';
export enum RSTabsList {
CARD = 0,
CST_LIST = 1,
- CST_EDIT = 2
+ CST_EDIT = 2,
+ TERM_GRAPH = 3
}
function RSTabs() {
@@ -169,6 +171,7 @@ function RSTabs() {
{`${schema.stats?.count_errors ?? 0} | ${schema.stats?.count_all ?? 0}`}
Редактор
+ Граф термов
@@ -183,6 +186,10 @@ function RSTabs() {
+
+
+
+
>
}
diff --git a/rsconcept/frontend/src/pages/RSFormPage/elements/ViewSideConstituents.tsx b/rsconcept/frontend/src/pages/RSFormPage/elements/ViewSideConstituents.tsx
index 641379f5..d7c683e0 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/elements/ViewSideConstituents.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/elements/ViewSideConstituents.tsx
@@ -4,8 +4,8 @@ import Checkbox from '../../../components/Common/Checkbox';
import DataTableThemed from '../../../components/Common/DataTableThemed';
import { useRSForm } from '../../../context/RSFormContext';
import useLocalStorage from '../../../hooks/useLocalStorage';
-import { CstType, type IConstituenta, matchConstituenta } from '../../../utils/models';
-import { extractGlobals, getMockConstituenta } from '../../../utils/staticUI';
+import { CstType, extractGlobals,type IConstituenta, matchConstituenta } from '../../../utils/models';
+import { getMockConstituenta } from '../../../utils/staticUI';
interface ViewSideConstituentsProps {
expression: string
diff --git a/rsconcept/frontend/src/utils/Graph.ts b/rsconcept/frontend/src/utils/Graph.ts
new file mode 100644
index 00000000..8e13e6f2
--- /dev/null
+++ b/rsconcept/frontend/src/utils/Graph.ts
@@ -0,0 +1,88 @@
+// Graph class with basic comparison. Does not work for objects
+export class GraphNode {
+ id: number;
+ adjacent: number[];
+
+ constructor(id: number) {
+ this.id = id;
+ this.adjacent = [];
+ }
+
+ addAdjacent(node: number): void {
+ this.adjacent.push(node);
+ }
+
+ removeAdjacent(target: number): number | null {
+ const index = this.adjacent.findIndex(node => node === target);
+ if (index > -1) {
+ return this.adjacent.splice(index, 1)[0];
+ }
+ return null;
+ }
+}
+
+export class Graph {
+ nodes: Map = new Map();
+
+ constructor() {}
+
+ addNode(target: number): GraphNode {
+ let node = this.nodes.get(target);
+ if (!node) {
+ node = new GraphNode(target);
+ this.nodes.set(target, node);
+ }
+ return node;
+ }
+
+ removeNode(target: number): GraphNode | null {
+ const nodeToRemove = this.nodes.get(target);
+ if (!nodeToRemove) {
+ return null;
+ }
+ this.nodes.forEach((node) => {
+ node.removeAdjacent(nodeToRemove.id);
+ });
+ this.nodes.delete(target);
+ return nodeToRemove;
+ }
+
+ addEdge(source: number, destination: number): void {
+ const sourceNode = this.addNode(source);
+ const destinationNode = this.addNode(destination);
+ sourceNode.addAdjacent(destinationNode.id);
+ }
+
+ removeEdge(source: number, destination: number): void {
+ const sourceNode = this.nodes.get(source);
+ const destinationNode = this.nodes.get(destination);
+ if (sourceNode && destinationNode) {
+ sourceNode.removeAdjacent(destination);
+ }
+ }
+
+ visitDFS(visitor: (node: GraphNode) => void) {
+ const visited: Map = new Map();
+ this.nodes.forEach(node => {
+ if (!visited.has(node.id)) {
+ this.depthFirstSearchAux(node, visited, visitor);
+ }
+ });
+ }
+
+ private depthFirstSearchAux(node: GraphNode, visited: Map, visitor: (node: GraphNode) => void): void {
+ if (!node) {
+ return;
+ }
+ visited.set(node.id, true);
+
+ visitor(node);
+
+ node.adjacent.forEach((item) => {
+ if (!visited.has(item)) {
+ const childNode = this.nodes.get(item);
+ if (childNode) this.depthFirstSearchAux(childNode, visited, visitor);
+ }
+ });
+ }
+}
diff --git a/rsconcept/frontend/src/utils/models.ts b/rsconcept/frontend/src/utils/models.ts
index 2dc456d6..505d8b6b 100644
--- a/rsconcept/frontend/src/utils/models.ts
+++ b/rsconcept/frontend/src/utils/models.ts
@@ -1,4 +1,5 @@
import { RSErrorType, TokenID } from './enums'
+import { Graph } from './Graph'
// ========= Users ===========
export interface IUser {
@@ -175,6 +176,7 @@ export interface IRSForm {
owner: number | null
items: IConstituenta[]
stats: IRSFormStats
+ graph: Graph
}
export interface IRSFormData extends Omit {}
@@ -212,6 +214,11 @@ export enum ExpressionStatus {
VERIFIED
}
+// ========== Model functions =================
+export function extractGlobals(expression: string): Set {
+ return new Set(expression.match(/[XCSADFPT]\d+/g) ?? []);
+}
+
export function inferStatus(parse?: ParsingStatus, value?: ValueClass): ExpressionStatus {
if (!parse || !value) {
return ExpressionStatus.UNDEFINED;
@@ -233,6 +240,7 @@ export function inferStatus(parse?: ParsingStatus, value?: ValueClass): Expressi
export function LoadRSFormData(schema: IRSFormData): IRSForm {
const result = schema as IRSForm
+ result.graph = new Graph;
if (!result.items) {
result.stats = {
count_all: 0,
@@ -254,35 +262,45 @@ export function LoadRSFormData(schema: IRSFormData): IRSForm {
return result;
}
result.stats = {
- count_all: schema.items.length || 0,
- count_errors: schema.items.reduce(
+ count_all: result.items.length || 0,
+ count_errors: result.items.reduce(
(sum, cst) => sum + (cst.parse?.status === ParsingStatus.INCORRECT ? 1 : 0) || 0, 0),
- count_property: schema.items.reduce(
+ count_property: result.items.reduce(
(sum, cst) => sum + (cst.parse?.valueClass === ValueClass.PROPERTY ? 1 : 0) || 0, 0),
- count_incalc: schema.items.reduce(
+ count_incalc: result.items.reduce(
(sum, cst) => sum +
((cst.parse?.status === ParsingStatus.VERIFIED && cst.parse?.valueClass === ValueClass.INVALID) ? 1 : 0) || 0, 0),
- count_termin: schema.items.reduce(
+ count_termin: result.items.reduce(
(sum, cst) => (sum + (cst.term?.raw ? 1 : 0) || 0), 0),
- count_base: schema.items.reduce(
+ count_base: result.items.reduce(
(sum, cst) => sum + (cst.cstType === CstType.BASE ? 1 : 0), 0),
- count_constant: schema.items?.reduce(
+ count_constant: result.items?.reduce(
(sum, cst) => sum + (cst.cstType === CstType.CONSTANT ? 1 : 0), 0),
- count_structured: schema.items?.reduce(
+ count_structured: result.items?.reduce(
(sum, cst) => sum + (cst.cstType === CstType.STRUCTURED ? 1 : 0), 0),
- count_axiom: schema.items?.reduce(
+ count_axiom: result.items?.reduce(
(sum, cst) => sum + (cst.cstType === CstType.AXIOM ? 1 : 0), 0),
- count_term: schema.items.reduce(
+ count_term: result.items.reduce(
(sum, cst) => sum + (cst.cstType === CstType.TERM ? 1 : 0), 0),
- count_function: schema.items.reduce(
+ count_function: result.items.reduce(
(sum, cst) => sum + (cst.cstType === CstType.FUNCTION ? 1 : 0), 0),
- count_predicate: schema.items.reduce(
+ count_predicate: result.items.reduce(
(sum, cst) => sum + (cst.cstType === CstType.PREDICATE ? 1 : 0), 0),
- count_theorem: schema.items.reduce(
+ count_theorem: result.items.reduce(
(sum, cst) => sum + (cst.cstType === CstType.THEOREM ? 1 : 0), 0)
}
+ result.items.forEach(cst => {
+ result.graph.addNode(cst.id);
+ const dependencies = extractGlobals(cst.definition.formal);
+ dependencies.forEach(value => {
+ const destination = schema.items.find(cst => cst.alias === value)
+ if (destination) {
+ result.graph.addEdge(cst.id, destination.id);
+ }
+ });
+ });
return result;
}
diff --git a/rsconcept/frontend/src/utils/staticUI.ts b/rsconcept/frontend/src/utils/staticUI.ts
index 27eeabb2..d098b772 100644
--- a/rsconcept/frontend/src/utils/staticUI.ts
+++ b/rsconcept/frontend/src/utils/staticUI.ts
@@ -269,10 +269,6 @@ export function getStatusInfo(status?: ExpressionStatus): IStatusInfo {
};
}
-export function extractGlobals(expression: string): Set {
- return new Set(expression.match(/[XCSADFPT]\d+/g) ?? []);
-}
-
export function createAliasFor(type: CstType, schema: IRSForm): string {
const prefix = getCstTypePrefix(type);
if (!schema.items || schema.items.length <= 0) {