Revert changing ID types and minor ui fixes

This commit is contained in:
IRBorisov 2023-08-22 22:38:27 +03:00
parent 503ad5d0dc
commit b85f057c03
16 changed files with 191 additions and 139 deletions

View File

@ -14,8 +14,8 @@ interface ILibraryContext {
filter: (params: ILibraryFilter) => IRSFormMeta[]
createSchema: (data: IRSFormCreateData, callback?: DataCallback<IRSFormMeta>) => void
cloneSchema: (target: string, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => void
destroySchema: (target: string, callback?: () => void) => void
cloneSchema: (target: number, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => void
destroySchema: (target: number, callback?: () => void) => void
}
const LibraryContext = createContext<ILibraryContext | null>(null)
@ -62,7 +62,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
getLibrary({
setLoading: setLoading,
showError: true,
onError: (error) => setError(error),
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<IRSFormData>) => {
(target: number, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => {
if (!user) {
return;
}
setError(undefined)
postCloneRSForm(target, {
postCloneRSForm(String(target), {
data: data,
showError: true,
setLoading: setProcessing,

View File

@ -205,7 +205,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
const cstUpdate = useCallback(
(data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => {
setError(undefined)
patchConstituenta(data.id, {
patchConstituenta(String(data.id), {
data: data,
showError: true,
setLoading: setProcessing,

View File

@ -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<IExpressionParse>) {
function checkExpression(expression: string, activeCst?: IConstituenta, onSuccess?: DataCallback<IExpressionParse>) {
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);
}
});
}

View File

@ -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();

View File

@ -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) {

View File

@ -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<string[]>([]);
const [selected, setSelected] = useState<number[]>([]);
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
const [toggledClearRows, setToggledClearRows] = useState(false);

View File

@ -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,

View File

@ -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<Graph>(new Graph());
const [ dismissed, setDismissed ] = useState<string[]>([]);
const [ selectedDismissed, setSelectedDismissed ] = useState<string[]>([]);
const [ dismissed, setDismissed ] = useState<number[]>([]);
const [ selectedDismissed, setSelectedDismissed ] = useState<number[]>([]);
const graphRef = useRef<GraphCanvasRef | null>(null);
const [showOptions, setShowOptions] = useState(false);
const [toggleUpdate, setToggleUpdate] = useState(false);
const [hoverID, setHoverID] = useState<string | undefined>(undefined);
const [hoverID, setHoverID] = useState<number | undefined>(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}
/>}
<div className='flex flex-col border-t border-r max-w-[12.44rem] pr-2 pb-2 text-sm select-none' style={{height: canvasHeight}}>
<div className='flex flex-col border-t border-r max-w-[12.5rem] pr-2 pb-2 text-sm select-none' style={{height: canvasHeight}}>
{hoverCst &&
<div className='relative'>
<InfoConstituenta
@ -408,10 +408,10 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
</div>
<ConceptSelect
className='mt-1 w-fit'
className='w-full mt-1'
options={GraphLayoutSelector}
searchable={false}
placeholder='Выберите тип'
placeholder='Способ расположения'
values={layout ? [{ value: layout, label: mapLayoutLabels.get(layout) }] : []}
onChange={data => { setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value); }}
/>

View File

@ -40,7 +40,7 @@ function RSTabs() {
const { destroySchema } = useLibrary();
const [activeTab, setActiveTab] = useState(RSTabsList.CARD);
const [activeID, setActiveID] = useState<string | undefined>(undefined);
const [activeID, setActiveID] = useState<number | undefined>(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<string[]>([]);
const [afterDelete, setAfterDelete] = useState<((items: number[]) => void) | undefined>(undefined);
const [toBeDeleted, setToBeDeleted] = useState<number[]>([]);
const [showDeleteCst, setShowDeleteCst] = useState(false);
const [createInitialData, setCreateInitialData] = useState<ICstCreateData>();
@ -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]);

View File

@ -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

View File

@ -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]);
});
});

View File

@ -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<string, GraphNode> = new Map();
nodes: Map<number, GraphNode> = 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<string, boolean>();
expandOutputs(origin: number[]): number[] {
const result: number[] = [];
const marked = new Map<number, boolean>();
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<string, boolean>();
expandInputs(origin: number[]): number[] {
const result: number[] = [];
const marked = new Map<number, boolean>();
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<string, boolean>();
const toVisit: string[] = [];
tolopogicalOrder(): number[] {
const result: number[] = [];
const marked = new Map<number, boolean>();
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<string, boolean>();
const marked = new Map<number, boolean>();
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];

View File

@ -196,7 +196,7 @@ export function postNewConstituenta(schema: string, request: FrontExchange<ICstC
export function patchDeleteConstituenta(schema: string, request: FrontExchange<IConstituentaList, IRSFormData>) {
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
});

View File

@ -152,6 +152,11 @@ export enum RSErrorType {
typesNotCompatible = 0x8825, // Типы не совместимы в данном контексте
orderingNotSupported = 0x8826, // Для данного типа не поддерживается порядок элементов
// !!!! Добавлены по сравнению с ConceptCore !!!!!
globalNonemptyBase = 0x8827, // Непустое выражение базисного/константного множества
globalUnexpectedType = 0x8828, // Типизация выражения не соответствует типу конституенты
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
globalNoValue = 0x8840, // Используется неинтерпретируемый глобальный идентификатор
invalidPropertyUsage = 0x8841, // Использование свойства в качестве значения
globalMissingAST = 0x8842, // Не удалось получить дерево разбора для глобального идентификатора

View File

@ -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<IConstituentaMeta, 'alias' | 'cst_type' | 'definition_raw' | 'term_raw' | 'convention' | 'definition_formal' > {
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; }

View File

@ -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';
}