diff --git a/rsconcept/frontend/src/context/LibraryContext.tsx b/rsconcept/frontend/src/context/LibraryContext.tsx index 96dc7c49..9a7e92bd 100644 --- a/rsconcept/frontend/src/context/LibraryContext.tsx +++ b/rsconcept/frontend/src/context/LibraryContext.tsx @@ -14,8 +14,8 @@ interface ILibraryContext { filter: (params: ILibraryFilter) => IRSFormMeta[] createSchema: (data: IRSFormCreateData, callback?: DataCallback) => void - cloneSchema: (target: string, data: IRSFormCreateData, callback: DataCallback) => void - destroySchema: (target: string, callback?: () => void) => void + cloneSchema: (target: number, data: IRSFormCreateData, callback: DataCallback) => void + destroySchema: (target: number, callback?: () => void) => void } const LibraryContext = createContext(null) @@ -62,8 +62,8 @@ export const LibraryState = ({ children }: LibraryStateProps) => { getLibrary({ setLoading: setLoading, showError: true, - onError: (error) => setError(error), - onSuccess: newData => { + onError: error => setError(error), + onSuccess: newData => { setItems(newData); if (callback) callback(); } @@ -90,9 +90,9 @@ export const LibraryState = ({ children }: LibraryStateProps) => { }, [reload]); const destroySchema = useCallback( - (target: string, callback?: () => void) => { + (target: number, callback?: () => void) => { setError(undefined) - deleteRSForm(target, { + deleteRSForm(String(target), { showError: true, setLoading: setProcessing, onError: error => setError(error), @@ -103,12 +103,12 @@ export const LibraryState = ({ children }: LibraryStateProps) => { }, [setError, reload]); const cloneSchema = useCallback( - (target: string, data: IRSFormCreateData, callback: DataCallback) => { + (target: number, data: IRSFormCreateData, callback: DataCallback) => { if (!user) { return; } setError(undefined) - postCloneRSForm(target, { + postCloneRSForm(String(target), { data: data, showError: true, setLoading: setProcessing, diff --git a/rsconcept/frontend/src/context/RSFormContext.tsx b/rsconcept/frontend/src/context/RSFormContext.tsx index 07d934b9..109fb8d8 100644 --- a/rsconcept/frontend/src/context/RSFormContext.tsx +++ b/rsconcept/frontend/src/context/RSFormContext.tsx @@ -205,7 +205,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => { const cstUpdate = useCallback( (data: ICstUpdateData, callback?: DataCallback) => { setError(undefined) - patchConstituenta(data.id, { + patchConstituenta(String(data.id), { data: data, showError: true, setLoading: setProcessing, diff --git a/rsconcept/frontend/src/hooks/useCheckExpression.ts b/rsconcept/frontend/src/hooks/useCheckExpression.ts index 66cbb3b1..9fdb713e 100644 --- a/rsconcept/frontend/src/hooks/useCheckExpression.ts +++ b/rsconcept/frontend/src/hooks/useCheckExpression.ts @@ -2,7 +2,29 @@ import { useCallback, useState } from 'react' import { type ErrorInfo } from '../components/BackendError'; import { DataCallback, postCheckExpression } from '../utils/backendAPI'; -import { IExpressionParse, type IRSForm } from '../utils/models'; +import { RSErrorType } from '../utils/enums'; +import { CstType, IConstituenta, IExpressionParse, IFunctionArg, type IRSForm } from '../utils/models'; +import { getCstExpressionPrefix } from '../utils/staticUI'; + +function checkTypeConsistency(type: CstType, typification: string, args: IFunctionArg[]): boolean { + switch (type) { + case CstType.BASE: + case CstType.CONSTANT: + case CstType.STRUCTURED: + case CstType.TERM: + return typification !== '' && args.length === 0; + + case CstType.AXIOM: + case CstType.THEOREM: + return typification === '' && args.length === 0; + + case CstType.FUNCTION: + return typification !== '' && args.length !== 0; + + case CstType.PREDICATE: + return typification === '' && args.length !== 0; + } +} function useCheckExpression({ schema }: { schema?: IRSForm }) { const [loading, setLoading] = useState(false); @@ -11,16 +33,38 @@ function useCheckExpression({ schema }: { schema?: IRSForm }) { const resetParse = useCallback(() => { setParseData(undefined); }, []); - function checkExpression(expression: string, onSuccess?: DataCallback) { + function checkExpression(expression: string, activeCst?: IConstituenta, onSuccess?: DataCallback) { setError(undefined); - postCheckExpression(schema!.id, { + postCheckExpression(String(schema!.id), { data: { expression: expression }, showError: true, setLoading, onError: error => { setError(error); }, - onSuccess: newData => { - setParseData(newData); - if (onSuccess) onSuccess(newData); + onSuccess: parse => { + if (activeCst && parse.parseResult) { + if (activeCst.cstType == CstType.BASE || activeCst.cstType == CstType.CONSTANT) { + if (expression !== getCstExpressionPrefix(activeCst)) { + parse.parseResult = false; + parse.errors.push({ + errorType: RSErrorType.globalNonemptyBase, + isCritical: true, + params: [], + position: 0 + }); + } + } + if (!checkTypeConsistency(activeCst.cstType, parse.typification, parse.args)) { + parse.parseResult = false; + parse.errors.push({ + errorType: RSErrorType.globalUnexpectedType, + isCritical: true, + params: [], + position: 0 + }); + } + } + setParseData(parse); + if (onSuccess) onSuccess(parse); } }); } diff --git a/rsconcept/frontend/src/pages/RSFormPage/DlgDeleteCst.tsx b/rsconcept/frontend/src/pages/RSFormPage/DlgDeleteCst.tsx index b9da09df..e04144f1 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/DlgDeleteCst.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/DlgDeleteCst.tsx @@ -7,15 +7,15 @@ import { getCstLabel } from '../../utils/staticUI'; interface DlgDeleteCstProps { hideWindow: () => void - selected: string[] - onDelete: (items: string[]) => void + selected: number[] + onDelete: (items: number[]) => void } function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) { const { schema } = useRSForm(); const [ expandOut, setExpandOut ] = useState(false); - const expansion: string[] = useMemo(() => schema?.graph.expandOutputs(selected) ?? [], [selected, schema?.graph]); + const expansion: number[] = useMemo(() => schema?.graph.expandOutputs(selected) ?? [], [selected, schema?.graph]); function handleSubmit() { hideWindow(); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx index d49d7899..5f4468b7 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx @@ -18,11 +18,11 @@ import ViewSideConstituents from './elements/ViewSideConstituents'; const UNFOLDED_HEIGHT = '59.1rem'; interface EditorConstituentaProps { - activeID?: string - onOpenEdit: (cstID: string) => void + activeID?: number + onOpenEdit: (cstID: number) => void onShowAST: (expression: string, ast: SyntaxTree) => void onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void - onDeleteCst: (selected: string[], callback?: (items: string[]) => void) => void + onDeleteCst: (selected: number[], callback?: (items: number[]) => void) => void } function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDeleteCst }: EditorConstituentaProps) { diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx index afa4d4ed..3cd7a411 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx @@ -14,15 +14,15 @@ import { CstType, IConstituenta, ICstCreateData, ICstMovetoData } from '../../ut import { getCstTypePrefix, getCstTypeShortcut, getCstTypificationLabel, mapStatusInfo } from '../../utils/staticUI'; interface EditorItemsProps { - onOpenEdit: (cstID: string) => void + onOpenEdit: (cstID: number) => void onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void - onDeleteCst: (selected: string[], callback: (items: string[]) => void) => void + onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void } function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps) { const { schema, isEditable, cstMoveTo, resetAliases } = useRSForm(); const { noNavigation } = useConceptTheme(); - const [selected, setSelected] = useState([]); + const [selected, setSelected] = useState([]); const nothingSelected = useMemo(() => selected.length === 0, [selected]); const [toggledClearRows, setToggledClearRows] = useState(false); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression.tsx index 0f2790c4..5ef5cd2c 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression.tsx @@ -60,7 +60,7 @@ function EditorRSExpression({ } const prefix = getCstExpressionPrefix(activeCst); const expression = prefix + value; - checkExpression(expression, parse => { + checkExpression(expression, activeCst, parse => { if (parse.errors.length > 0) { onShowError(parse.errors[0]); } else { @@ -81,7 +81,8 @@ function EditorRSExpression({ return; } const prefix = getCstExpressionPrefix(activeCst); - const errorPosition = error.position - prefix.length; + let errorPosition = error.position - prefix.length; + if (errorPosition < 0) errorPosition = 0; rsInput.current?.view?.dispatch({ selection: { anchor: errorPosition, diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx index 2ecb5690..0d6caeea 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx @@ -56,9 +56,9 @@ export interface GraphEditorParams { } interface EditorTermGraphProps { - onOpenEdit: (cstID: string) => void + onOpenEdit: (cstID: number) => void onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void - onDeleteCst: (selected: string[], callback: (items: string[]) => void) => void + onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void } function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGraphProps) { @@ -83,16 +83,16 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra const [ allowTheorem, setAllowTheorem ] = useLocalStorage('graph_allow_theorem', true); const [ filtered, setFiltered ] = useState(new Graph()); - const [ dismissed, setDismissed ] = useState([]); - const [ selectedDismissed, setSelectedDismissed ] = useState([]); + const [ dismissed, setDismissed ] = useState([]); + const [ selectedDismissed, setSelectedDismissed ] = useState([]); const graphRef = useRef(null); const [showOptions, setShowOptions] = useState(false); const [toggleUpdate, setToggleUpdate] = useState(false); - const [hoverID, setHoverID] = useState(undefined); + const [hoverID, setHoverID] = useState(undefined); const hoverCst = useMemo( () => { - return schema?.items.find(cst => String(cst.id) === hoverID); + return schema?.items.find(cst => cst.id === hoverID); }, [schema?.items, hoverID]); const is3D = useMemo(() => layout.includes('3d'), [layout]); @@ -137,7 +137,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra } }); } - const newDismissed: string[] = []; + const newDismissed: number[] = []; schema.items.forEach(cst => { if (!graph.nodes.has(cst.id)) { newDismissed.push(cst.id); @@ -149,7 +149,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra setHoverID(undefined); }, [schema, noHermits, noTransitive, noTemplates, allowedTypes, toggleUpdate]); - function toggleDismissed(cstID: string) { + function toggleDismissed(cstID: number) { setSelectedDismissed(prev => { const index = prev.findIndex(id => cstID === id); if (index !== -1) { @@ -171,7 +171,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra const cst = schema.items.find(cst => cst.id === node.id); if (cst) { result.push({ - id: node.id, + id: String(node.id), fill: getCstNodeColor(cst, coloringScheme, darkMode), label: cst.term.resolved && !noTerms ? `${cst.alias}: ${cst.term.resolved}` : cst.alias }); @@ -189,8 +189,8 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra source.outputs.forEach(target => { result.push({ id: String(edgeID), - source: source.id, - target: target + source: String(source.id), + target: String(target) }); edgeID += 1; }); @@ -216,9 +216,9 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra focusOnSelect: false }); - const allSelected: string[] = useMemo( + const allSelected: number[] = useMemo( () => { - return [ ... selectedDismissed, ... selections]; + return [ ... selectedDismissed, ... selections.map(id => Number(id))]; }, [selectedDismissed, selections]); const nothingSelected = useMemo(() => allSelected.length === 0, [allSelected]); @@ -230,7 +230,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra const handleHoverIn = useCallback( (node: GraphNode) => { - setHoverID(node.id); + setHoverID(Number(node.id)); if (onNodePointerOver) onNodePointerOver(node); }, [onNodePointerOver]); @@ -243,7 +243,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra const handleNodeClick = useCallback( (node: GraphNode) => { if (selections.includes(node.id)) { - onOpenEdit(node.id); + onOpenEdit(Number(node.id)); return; } if (onNodeClick) onNodeClick(node); @@ -346,7 +346,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra }, [noNavigation]); const dismissedStyle = useCallback( - (cstID: string) => { + (cstID: number) => { return selectedDismissed.includes(cstID) ? {outlineWidth: '2px', outlineStyle: 'solid'}: {}; }, [selectedDismissed]); @@ -358,7 +358,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra initial={getOptions()} onConfirm={handleChangeOptions} />} -
+
{hoverCst &&
{ setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value); }} /> diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx index 11f48670..8dbe0057 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx @@ -40,7 +40,7 @@ function RSTabs() { const { destroySchema } = useLibrary(); const [activeTab, setActiveTab] = useState(RSTabsList.CARD); - const [activeID, setActiveID] = useState(undefined); + const [activeID, setActiveID] = useState(undefined); const [showUpload, setShowUpload] = useState(false); const [showClone, setShowClone] = useState(false); @@ -49,8 +49,8 @@ function RSTabs() { const [expression, setExpression] = useState(''); const [showAST, setShowAST] = useState(false); - const [afterDelete, setAfterDelete] = useState<((items: string[]) => void) | undefined>(undefined); - const [toBeDeleted, setToBeDeleted] = useState([]); + const [afterDelete, setAfterDelete] = useState<((items: number[]) => void) | undefined>(undefined); + const [toBeDeleted, setToBeDeleted] = useState([]); const [showDeleteCst, setShowDeleteCst] = useState(false); const [createInitialData, setCreateInitialData] = useState(); @@ -70,7 +70,7 @@ function RSTabs() { const activeTab = Number(new URLSearchParams(search).get('tab')) ?? RSTabsList.CARD; const cstQuery = new URLSearchParams(search).get('active'); setActiveTab(activeTab); - setActiveID(cstQuery ?? ((schema && schema?.items.length > 0) ? schema.items[0].id : undefined)) + setActiveID(Number(cstQuery) ?? ((schema && schema?.items.length > 0) ? schema.items[0].id : undefined)) }, [search, setActiveTab, setActiveID, schema]); function onSelectTab(index: number) { @@ -78,7 +78,7 @@ function RSTabs() { } const navigateTo = useCallback( - (tab: RSTabsList, activeID?: string) => { + (tab: RSTabsList, activeID?: number) => { if (activeID) { navigate(`/rsforms/${schema!.id}?tab=${tab}&active=${activeID}`, { replace: tab === activeTab && tab !== RSTabsList.CST_EDIT @@ -123,7 +123,7 @@ function RSTabs() { }, [handleCreateCst]); const handleDeleteCst = useCallback( - (deleted: string[]) => { + (deleted: number[]) => { if (!schema) { return; } @@ -148,9 +148,9 @@ function RSTabs() { }, [afterDelete, cstDelete, schema, activeID, activeTab, navigateTo]); const promptDeleteCst = useCallback( - (selected: string[], callback?: (items: string[]) => void) => { + (selected: number[], callback?: (items: number[]) => void) => { setAfterDelete(() => ( - (items: string[]) => { + (items: number[]) => { if (callback) callback(items); })); setToBeDeleted(selected); @@ -165,7 +165,7 @@ function RSTabs() { }, []); const onOpenCst = useCallback( - (cstID: string) => { + (cstID: number) => { navigateTo(RSTabsList.CST_EDIT, cstID) }, [navigateTo]); diff --git a/rsconcept/frontend/src/pages/RSFormPage/elements/ViewSideConstituents.tsx b/rsconcept/frontend/src/pages/RSFormPage/elements/ViewSideConstituents.tsx index 2951b95e..bba4aad3 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/elements/ViewSideConstituents.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/elements/ViewSideConstituents.tsx @@ -17,12 +17,12 @@ const LOCAL_NAVIGATION_H = '2.6rem'; interface ViewSideConstituentsProps { expression: string baseHeight: string - activeID?: string - onOpenEdit: (cstID: string) => void + activeID?: number + onOpenEdit: (cstID: number) => void } function isMockCst(cst: IConstituenta) { - return cst.id[0] === '-' + return cst.id <= 0; } function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }: ViewSideConstituentsProps) { @@ -49,7 +49,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }: const diff = Array.from(aliases).filter(name => !names.includes(name)); if (diff.length > 0) { diff.forEach( - (alias, index) => filtered.push(getMockConstituenta(`-${index}`, alias, CstType.BASE, 'Конституента отсутствует'))); + (alias, index) => filtered.push(getMockConstituenta(-index, alias, CstType.BASE, 'Конституента отсутствует'))); } } else if (!activeID) { filtered = schema.items diff --git a/rsconcept/frontend/src/utils/Graph.test.ts b/rsconcept/frontend/src/utils/Graph.test.ts index 303a6c36..40371d6c 100644 --- a/rsconcept/frontend/src/utils/Graph.test.ts +++ b/rsconcept/frontend/src/utils/Graph.test.ts @@ -8,94 +8,94 @@ describe('Testing Graph constuction', () => { test('adding edges should create nodes', () => { const graph = new Graph(); - graph.addEdge('13', '37'); - expect([... graph.nodes.keys()]).toStrictEqual(['13', '37']); + graph.addEdge(13, 37); + expect([... graph.nodes.keys()]).toStrictEqual([13, 37]); - graph.addEdge('13', '38'); - expect([... graph.nodes.keys()]).toStrictEqual(['13', '37', '38']); + graph.addEdge(13, 38); + expect([... graph.nodes.keys()]).toStrictEqual([13, 37, 38]); }); test('creating from array', () => { - const graph = new Graph([['1', '2'], ['3'], ['4', '1']]); - expect([... graph.nodes.keys()]).toStrictEqual(['1', '2', '3', '4']); - expect([... graph.nodes.get('1')!.outputs]).toStrictEqual(['2']); + const graph = new Graph([[1, 2], [3], [4, 1]]); + expect([... graph.nodes.keys()]).toStrictEqual([1, 2, 3, 4]); + expect([... graph.nodes.get(1)!.outputs]).toStrictEqual([2]); }); test('cloning', () => { - const graph = new Graph([['1', '2'], ['3'], ['4', '1']]); + const graph = new Graph([[1, 2], [3], [4, 1]]); const clone = graph.clone(); expect([... graph.nodes.keys()]).toStrictEqual([... clone.nodes.keys()]); expect([... graph.nodes.values()]).toStrictEqual([... clone.nodes.values()]); - clone.removeNode('3'); - expect(clone.nodes.get('3')).toBeUndefined(); - expect(graph.nodes.get('3')).not.toBeUndefined(); + clone.removeNode(3); + expect(clone.nodes.get(3)).toBeUndefined(); + expect(graph.nodes.get(3)).not.toBeUndefined(); }); }); describe('Testing Graph editing', () => { test('removing edges should not remove nodes', () => { - const graph = new Graph([['1', '2'], ['3'], ['4', '1']]); - expect(graph.hasEdge('4', '1')).toBeTruthy(); + const graph = new Graph([[1, 2], [3], [4, 1]]); + expect(graph.hasEdge(4, 1)).toBeTruthy(); - graph.removeEdge('5', '0'); - graph.removeEdge('4', '1'); + graph.removeEdge(5, 0); + graph.removeEdge(4, 1); - expect([... graph.nodes.keys()]).toStrictEqual(['1', '2', '3', '4']); - expect(graph.hasEdge('4', '1')).toBeFalsy(); + expect([... graph.nodes.keys()]).toStrictEqual([1, 2, 3, 4]); + expect(graph.hasEdge(4, 1)).toBeFalsy(); }); test('folding node redirectes edges', () => { - const graph = new Graph([['1', '3'], ['2', '3'], ['3', '4'], ['3', '5'], ['3', '3']]); - graph.foldNode('3'); - expect(graph.hasNode('3')).toBeFalsy(); - expect(graph.hasEdge('1', '4')).toBeTruthy(); - expect(graph.hasEdge('1', '5')).toBeTruthy(); - expect(graph.hasEdge('2', '4')).toBeTruthy(); - expect(graph.hasEdge('2', '5')).toBeTruthy(); + const graph = new Graph([[1, 3], [2, 3], [3, 4], [3, 5], [3, 3]]); + graph.foldNode(3); + expect(graph.hasNode(3)).toBeFalsy(); + expect(graph.hasEdge(1, 4)).toBeTruthy(); + expect(graph.hasEdge(1, 5)).toBeTruthy(); + expect(graph.hasEdge(2, 4)).toBeTruthy(); + expect(graph.hasEdge(2, 5)).toBeTruthy(); }); test('removing isolated nodes', () => { - const graph = new Graph([['9', '1'], ['9', '2'], ['2', '1'], ['4', '3'], ['5', '9'], ['7'], ['8']]); + const graph = new Graph([[9, 1], [9, 2], [2, 1], [4, 3], [5, 9], [7], [8]]); graph.removeIsolated() - expect([... graph.nodes.keys()]).toStrictEqual(['9', '1', '2', '4', '3', '5']); + expect([... graph.nodes.keys()]).toStrictEqual([9, 1, 2, 4, 3, 5]); }); test('transitive reduction', () => { - const graph = new Graph([['1', '3'], ['1', '2'], ['2', '3']]); + const graph = new Graph([[1, 3], [1, 2], [2, 3]]); graph.transitiveReduction(); - expect(graph.hasEdge('1', '2')).toBeTruthy(); - expect(graph.hasEdge('2', '3')).toBeTruthy(); - expect(graph.hasEdge('1', '3')).toBeFalsy(); + expect(graph.hasEdge(1, 2)).toBeTruthy(); + expect(graph.hasEdge(2, 3)).toBeTruthy(); + expect(graph.hasEdge(1, 3)).toBeFalsy(); }); }); describe('Testing Graph sort', () => { test('topological order', () => { - const graph = new Graph([['9', '1'], ['9', '2'], ['2', '1'], ['4', '3'], ['5', '9']]); - expect(graph.tolopogicalOrder()).toStrictEqual(['5', '4', '3', '9', '2', '1']); + const graph = new Graph([[9, 1], [9, 2], [2, 1], [4, 3], [5, 9]]); + expect(graph.tolopogicalOrder()).toStrictEqual([5, 4, 3, 9, 2, 1]); }); }); describe('Testing Graph queries', () => { test('expand outputs', () => { - const graph = new Graph([['1', '2'], ['2', '3'], ['2', '5'], ['5', '6'], ['6', '1'], ['7']]); + const graph = new Graph([[1, 2], [2, 3], [2, 5], [5, 6], [6, 1], [7]]); expect(graph.expandOutputs([])).toStrictEqual([]); - expect(graph.expandOutputs(['3'])).toStrictEqual([]); - expect(graph.expandOutputs(['7'])).toStrictEqual([]); - expect(graph.expandOutputs(['2', '5'])).toStrictEqual(['3', '6', '1']); + expect(graph.expandOutputs([3])).toStrictEqual([]); + expect(graph.expandOutputs([7])).toStrictEqual([]); + expect(graph.expandOutputs([2, 5])).toStrictEqual([3, 6, 1]); }); test('expand into unique array', () => { - const graph = new Graph([['1', '2'], ['1', '3'], ['2', '5'], ['3', '5']]); - expect(graph.expandOutputs(['1'])).toStrictEqual(['2', '3' , '5']); + const graph = new Graph([[1, 2], [1, 3], [2, 5], [3, 5]]); + expect(graph.expandOutputs([1])).toStrictEqual([2, 3 , 5]); }); test('expand inputs', () => { - const graph = new Graph([['1', '2'], ['2', '3'], ['2', '5'], ['5', '6'], ['6', '1'], ['7']]); + const graph = new Graph([[1, 2], [2, 3], [2, 5], [5, 6], [6, 1], [7]]); expect(graph.expandInputs([])).toStrictEqual([]); - expect(graph.expandInputs(['7'])).toStrictEqual([]); - expect(graph.expandInputs(['6'])).toStrictEqual(['5', '2', '1']); + expect(graph.expandInputs([7])).toStrictEqual([]); + expect(graph.expandInputs([6])).toStrictEqual([5, 2, 1]); }); }); \ No newline at end of file diff --git a/rsconcept/frontend/src/utils/Graph.ts b/rsconcept/frontend/src/utils/Graph.ts index 49c13e80..32f66d94 100644 --- a/rsconcept/frontend/src/utils/Graph.ts +++ b/rsconcept/frontend/src/utils/Graph.ts @@ -1,10 +1,10 @@ // ======== ID based fast Graph implementation ============= export class GraphNode { - id: string; - outputs: string[]; - inputs: string[]; + id: number; + outputs: number[]; + inputs: number[]; - constructor(id: string) { + constructor(id: number) { this.id = id; this.outputs = []; this.inputs = []; @@ -17,29 +17,29 @@ export class GraphNode { return result; } - addOutput(node: string): void { + addOutput(node: number): void { this.outputs.push(node); } - addInput(node: string): void { + addInput(node: number): void { this.inputs.push(node); } - removeInput(target: string): string | null { + removeInput(target: number): number | null { const index = this.inputs.findIndex(node => node === target); return index > -1 ? this.inputs.splice(index, 1)[0] : null; } - removeOutput(target: string): string | null { + removeOutput(target: number): number | null { const index = this.outputs.findIndex(node => node === target); return index > -1 ? this.outputs.splice(index, 1)[0] : null; } } export class Graph { - nodes: Map = new Map(); + nodes: Map = new Map(); - constructor(arr?: string[][]) { + constructor(arr?: number[][]) { if (!arr) { return; } @@ -58,7 +58,7 @@ export class Graph { return result; } - addNode(target: string): GraphNode { + addNode(target: number): GraphNode { let node = this.nodes.get(target); if (!node) { node = new GraphNode(target); @@ -67,11 +67,11 @@ export class Graph { return node; } - hasNode(target: string): boolean { + hasNode(target: number): boolean { return !!this.nodes.get(target); } - removeNode(target: string): GraphNode | null { + removeNode(target: number): GraphNode | null { const nodeToRemove = this.nodes.get(target); if (!nodeToRemove) { return null; @@ -84,7 +84,7 @@ export class Graph { return nodeToRemove; } - foldNode(target: string): GraphNode | null { + foldNode(target: number): GraphNode | null { const nodeToRemove = this.nodes.get(target); if (!nodeToRemove) { return null; @@ -107,14 +107,14 @@ export class Graph { return result; } - addEdge(source: string, destination: string): void { + addEdge(source: number, destination: number): void { const sourceNode = this.addNode(source); const destinationNode = this.addNode(destination); sourceNode.addOutput(destinationNode.id); destinationNode.addInput(sourceNode.id); } - removeEdge(source: string, destination: string): void { + removeEdge(source: number, destination: number): void { const sourceNode = this.nodes.get(source); const destinationNode = this.nodes.get(destination); if (sourceNode && destinationNode) { @@ -123,7 +123,7 @@ export class Graph { } } - hasEdge(source: string, destination: string): boolean { + hasEdge(source: number, destination: number): boolean { const sourceNode = this.nodes.get(source); if (!sourceNode) { return false; @@ -131,9 +131,9 @@ export class Graph { return !!sourceNode.outputs.find(id => id === destination); } - expandOutputs(origin: string[]): string[] { - const result: string[] = []; - const marked = new Map(); + expandOutputs(origin: number[]): number[] { + const result: number[] = []; + const marked = new Map(); origin.forEach(id => marked.set(id, true)); origin.forEach(id => { const node = this.nodes.get(id); @@ -161,9 +161,9 @@ export class Graph { return result; } - expandInputs(origin: string[]): string[] { - const result: string[] = []; - const marked = new Map(); + expandInputs(origin: number[]): number[] { + const result: number[] = []; + const marked = new Map(); origin.forEach(id => marked.set(id, true)); origin.forEach(id => { const node = this.nodes.get(id); @@ -191,10 +191,10 @@ export class Graph { return result; } - tolopogicalOrder(): string[] { - const result: string[] = []; - const marked = new Map(); - const toVisit: string[] = []; + tolopogicalOrder(): number[] { + const result: number[] = []; + const marked = new Map(); + const toVisit: number[] = []; this.nodes.forEach(node => { if (marked.get(node.id)) { return; @@ -225,12 +225,12 @@ export class Graph { transitiveReduction() { const order = this.tolopogicalOrder(); - const marked = new Map(); + const marked = new Map(); order.forEach(nodeID => { if (marked.get(nodeID)) { return; } - const stack: {id: string, parents: string[]}[] = []; + const stack: {id: number, parents: number[]}[] = []; stack.push({id: nodeID, parents: []}); while (stack.length > 0) { const item = stack.splice(0, 1)[0]; diff --git a/rsconcept/frontend/src/utils/backendAPI.ts b/rsconcept/frontend/src/utils/backendAPI.ts index 084f9eb8..c74807b1 100644 --- a/rsconcept/frontend/src/utils/backendAPI.ts +++ b/rsconcept/frontend/src/utils/backendAPI.ts @@ -196,7 +196,7 @@ export function postNewConstituenta(schema: string, request: FrontExchange) { AxiosPatch({ - title: `Delete Constituents for RSForm id=${schema}: ${request.data.items.map(item => item.id).join(' ')}`, + title: `Delete Constituents for RSForm id=${schema}: ${request.data.items.map(item => String(item.id)).join(' ')}`, endpoint: `/api/rsforms/${schema}/cst-multidelete/`, request: request }); diff --git a/rsconcept/frontend/src/utils/enums.ts b/rsconcept/frontend/src/utils/enums.ts index 24c37c84..003059f5 100644 --- a/rsconcept/frontend/src/utils/enums.ts +++ b/rsconcept/frontend/src/utils/enums.ts @@ -152,6 +152,11 @@ export enum RSErrorType { typesNotCompatible = 0x8825, // Типы не совместимы в данном контексте orderingNotSupported = 0x8826, // Для данного типа не поддерживается порядок элементов + // !!!! Добавлены по сравнению с ConceptCore !!!!! + globalNonemptyBase = 0x8827, // Непустое выражение базисного/константного множества + globalUnexpectedType = 0x8828, // Типизация выражения не соответствует типу конституенты + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + globalNoValue = 0x8840, // Используется неинтерпретируемый глобальный идентификатор invalidPropertyUsage = 0x8841, // Использование свойства в качестве значения globalMissingAST = 0x8842, // Не удалось получить дерево разбора для глобального идентификатора diff --git a/rsconcept/frontend/src/utils/models.ts b/rsconcept/frontend/src/utils/models.ts index 703715d6..85c354de 100644 --- a/rsconcept/frontend/src/utils/models.ts +++ b/rsconcept/frontend/src/utils/models.ts @@ -109,7 +109,7 @@ export enum CstClass { } export interface IConstituenta { - id: string + id: number alias: string cstType: CstType convention: string @@ -138,8 +138,8 @@ export interface IConstituenta { } export interface IConstituentaMeta { - id: string - schema: string + id: number + schema: number order: number alias: string convention: string @@ -158,7 +158,7 @@ export interface IConstituentaList { export interface ICstCreateData extends Pick { - insert_after: string | null + insert_after: number | null } export interface ICstMovetoData extends IConstituentaList { @@ -196,7 +196,7 @@ export interface IRSFormStats { } export interface IRSForm { - id: string + id: number title: string alias: string comment: string @@ -368,9 +368,7 @@ export function LoadRSFormData(schema: IRSFormData): IRSForm { count_theorem: result.items.reduce( (sum, cst) => sum + (cst.cstType === CstType.THEOREM ? 1 : 0), 0) } - result.id = String(result.id) result.items.forEach(cst => { - cst.id = String(cst.id) cst.status = inferStatus(cst.parse.status, cst.parse.valueClass); cst.isTemplate = inferTemplate(cst.definition.formal); cst.cstClass = inferClass(cst.cstType, cst.isTemplate); @@ -416,11 +414,11 @@ export function matchRSFormMeta(query: string, target: IRSFormMeta) { } } -export function applyGraphFilter(schema: IRSForm, start: string, mode: DependencyMode): IConstituenta[] { +export function applyGraphFilter(schema: IRSForm, start: number, mode: DependencyMode): IConstituenta[] { if (mode === DependencyMode.ALL) { return schema.items; } - let ids: string[] | undefined = undefined + let ids: number[] | undefined = undefined switch (mode) { case DependencyMode.OUTPUTS: { ids = schema.graph.nodes.get(start)?.outputs; break; } case DependencyMode.INPUTS: { ids = schema.graph.nodes.get(start)?.inputs; break; } diff --git a/rsconcept/frontend/src/utils/staticUI.ts b/rsconcept/frontend/src/utils/staticUI.ts index 62f042ed..7c62f495 100644 --- a/rsconcept/frontend/src/utils/staticUI.ts +++ b/rsconcept/frontend/src/utils/staticUI.ts @@ -407,7 +407,7 @@ export function createAliasFor(type: CstType, schema: IRSForm): string { return `${prefix}${index}`; } -export function getMockConstituenta(id: string, alias: string, type: CstType, comment: string): IConstituenta { +export function getMockConstituenta(id: number, alias: string, type: CstType, comment: string): IConstituenta { return { id: id, alias: alias, @@ -568,6 +568,10 @@ export function getRSErrorMessage(error: IRSErrorDescription): string { return `Не удалось получить дерево разбора для глобального идентификатора: ${error.params[0]}`; case RSErrorType.globalFuncNoInterpretation: return `Функция не интерпретируется для данных аргументов`; + case RSErrorType.globalNonemptyBase: + return `Непустое выражение базисного/константного множества`; + case RSErrorType.globalUnexpectedType: + return `Типизация выражения не соответствует типу конституенты`; } return 'UNKNOWN ERROR'; }