mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-08-16 05:40:37 +03:00
Implement RSExpr check UI
This commit is contained in:
parent
35ef677c60
commit
4f45b9978e
|
@ -2,8 +2,8 @@ import { ThreeDots } from 'react-loader-spinner';
|
||||||
|
|
||||||
export function Loader() {
|
export function Loader() {
|
||||||
return (
|
return (
|
||||||
<div className='flex justify-center'>
|
<div className='flex justify-center w-full h-full'>
|
||||||
<ThreeDots color='rgb(96 165 250)' height='100' width='100' radius='10' />
|
<ThreeDots color='rgb(96 165 250)' height='100' width='100' radius='10' />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,23 +7,18 @@ import Button from './Button';
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
title?: string
|
title?: string
|
||||||
submitText?: string
|
submitText?: string
|
||||||
show: boolean
|
canSubmit?: boolean
|
||||||
canSubmit: boolean
|
|
||||||
hideWindow: () => void
|
hideWindow: () => void
|
||||||
onSubmit: () => void
|
onSubmit: () => void
|
||||||
onCancel?: () => void
|
onCancel?: () => void
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
function Modal({ title, show, hideWindow, onSubmit, onCancel, canSubmit, children, submitText = 'Продолжить' }: ModalProps) {
|
function Modal({ title, hideWindow, onSubmit, onCancel, canSubmit, children, submitText = 'Продолжить' }: ModalProps) {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
useClickedOutside({ ref, callback: hideWindow });
|
useClickedOutside({ ref, callback: hideWindow });
|
||||||
useEscapeKey(hideWindow);
|
useEscapeKey(hideWindow);
|
||||||
|
|
||||||
if (!show) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
hideWindow();
|
hideWindow();
|
||||||
if (onCancel) onCancel();
|
if (onCancel) onCancel();
|
||||||
|
|
|
@ -2,18 +2,17 @@ import { useCallback, useState } from 'react'
|
||||||
|
|
||||||
import { type ErrorInfo } from '../components/BackendError';
|
import { type ErrorInfo } from '../components/BackendError';
|
||||||
import { DataCallback, postCheckExpression } from '../utils/backendAPI';
|
import { DataCallback, postCheckExpression } from '../utils/backendAPI';
|
||||||
import { ExpressionParse, type IRSForm } from '../utils/models';
|
import { IExpressionParse, type IRSForm } from '../utils/models';
|
||||||
|
|
||||||
function useCheckExpression({ schema }: { schema?: IRSForm }) {
|
function useCheckExpression({ schema }: { schema?: IRSForm }) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<ErrorInfo>(undefined);
|
const [error, setError] = useState<ErrorInfo>(undefined);
|
||||||
const [parseData, setParseData] = useState<ExpressionParse | undefined>(undefined);
|
const [parseData, setParseData] = useState<IExpressionParse | undefined>(undefined);
|
||||||
|
|
||||||
const resetParse = useCallback(() => { setParseData(undefined); }, []);
|
const resetParse = useCallback(() => { setParseData(undefined); }, []);
|
||||||
|
|
||||||
function checkExpression(expression: string, onSuccess?: DataCallback<ExpressionParse>) {
|
function checkExpression(expression: string, onSuccess?: DataCallback<IExpressionParse>) {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
setParseData(undefined);
|
|
||||||
postCheckExpression(String(schema?.id), {
|
postCheckExpression(String(schema?.id), {
|
||||||
data: { expression: expression },
|
data: { expression: expression },
|
||||||
showError: true,
|
showError: true,
|
||||||
|
|
|
@ -77,7 +77,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-red {
|
.text-red {
|
||||||
@apply text-red-400 dark:text-red-600
|
@apply text-red-600 dark:text-red-400
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-green {
|
.text-green {
|
||||||
|
|
|
@ -11,11 +11,10 @@ import { IRSFormCreateData } from '../../utils/models';
|
||||||
import { getCloneTitle } from '../../utils/staticUI';
|
import { getCloneTitle } from '../../utils/staticUI';
|
||||||
|
|
||||||
interface DlgCloneRSFormProps {
|
interface DlgCloneRSFormProps {
|
||||||
show: boolean
|
|
||||||
hideWindow: () => void
|
hideWindow: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgCloneRSForm({ show, hideWindow }: DlgCloneRSFormProps) {
|
function DlgCloneRSForm({ hideWindow }: DlgCloneRSFormProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [title, setTitle] = useState('');
|
const [title, setTitle] = useState('');
|
||||||
const [alias, setAlias] = useState('');
|
const [alias, setAlias] = useState('');
|
||||||
|
@ -34,7 +33,6 @@ function DlgCloneRSForm({ show, hideWindow }: DlgCloneRSFormProps) {
|
||||||
}, [schema, schema?.title, schema?.alias, schema?.comment, schema?.is_common]);
|
}, [schema, schema?.title, schema?.alias, schema?.comment, schema?.is_common]);
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
hideWindow();
|
|
||||||
const data: IRSFormCreateData = {
|
const data: IRSFormCreateData = {
|
||||||
title: title,
|
title: title,
|
||||||
alias: alias,
|
alias: alias,
|
||||||
|
@ -50,7 +48,6 @@ function DlgCloneRSForm({ show, hideWindow }: DlgCloneRSFormProps) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title='Создание копии концептуальной схемы'
|
title='Создание копии концептуальной схемы'
|
||||||
show={show}
|
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
canSubmit={true}
|
canSubmit={true}
|
||||||
submitText='Создать'
|
submitText='Создать'
|
||||||
|
@ -76,7 +73,7 @@ function DlgCloneRSForm({ show, hideWindow }: DlgCloneRSFormProps) {
|
||||||
onChange={event => { setCommon(event.target.checked); }}
|
onChange={event => { setCommon(event.target.checked); }}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DlgCloneRSForm;
|
export default DlgCloneRSForm;
|
||||||
|
|
|
@ -6,13 +6,12 @@ import { type CstType } from '../../utils/models';
|
||||||
import { CstTypeSelector, getCstTypeLabel } from '../../utils/staticUI';
|
import { CstTypeSelector, getCstTypeLabel } from '../../utils/staticUI';
|
||||||
|
|
||||||
interface DlgCreateCstProps {
|
interface DlgCreateCstProps {
|
||||||
show: boolean
|
|
||||||
hideWindow: () => void
|
hideWindow: () => void
|
||||||
defaultType?: CstType
|
defaultType?: CstType
|
||||||
onCreate: (type: CstType) => void
|
onCreate: (type: CstType) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgCreateCst({ show, hideWindow, defaultType, onCreate }: DlgCreateCstProps) {
|
function DlgCreateCst({ hideWindow, defaultType, onCreate }: DlgCreateCstProps) {
|
||||||
const [validated, setValidated] = useState(false);
|
const [validated, setValidated] = useState(false);
|
||||||
const [selectedType, setSelectedType] = useState<CstType | undefined>(undefined);
|
const [selectedType, setSelectedType] = useState<CstType | undefined>(undefined);
|
||||||
|
|
||||||
|
@ -32,7 +31,6 @@ function DlgCreateCst({ show, hideWindow, defaultType, onCreate }: DlgCreateCstP
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title='Создание конституенты'
|
title='Создание конституенты'
|
||||||
show={show}
|
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
canSubmit={validated}
|
canSubmit={validated}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
@ -45,7 +43,7 @@ function DlgCreateCst({ show, hideWindow, defaultType, onCreate }: DlgCreateCstP
|
||||||
onChange={(data) => { setSelectedType(data?.value); }}
|
onChange={(data) => { setSelectedType(data?.value); }}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DlgCreateCst;
|
export default DlgCreateCst;
|
||||||
|
|
30
rsconcept/frontend/src/pages/RSFormPage/DlgShowAST.tsx
Normal file
30
rsconcept/frontend/src/pages/RSFormPage/DlgShowAST.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import Modal from '../../components/Common/Modal';
|
||||||
|
import PrettyJson from '../../components/Common/PrettyJSON';
|
||||||
|
import { SyntaxTree } from '../../utils/models';
|
||||||
|
|
||||||
|
interface DlgShowASTProps {
|
||||||
|
hideWindow: () => void
|
||||||
|
syntaxTree: SyntaxTree
|
||||||
|
}
|
||||||
|
|
||||||
|
function DlgShowAST({ hideWindow, syntaxTree }: DlgShowASTProps) {
|
||||||
|
const handleSubmit = () => {
|
||||||
|
// Do nothing
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title='Просмотр дерева разбора'
|
||||||
|
hideWindow={hideWindow}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
submitText='Закрыть'
|
||||||
|
canSubmit={true}
|
||||||
|
>
|
||||||
|
<div className='max-w-[40rem] max-h-[30rem] overflow-auto'>
|
||||||
|
<PrettyJson data={syntaxTree}/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DlgShowAST;
|
|
@ -8,17 +8,15 @@ import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { IRSFormUploadData } from '../../utils/models';
|
import { IRSFormUploadData } from '../../utils/models';
|
||||||
|
|
||||||
interface DlgUploadRSFormProps {
|
interface DlgUploadRSFormProps {
|
||||||
show: boolean
|
|
||||||
hideWindow: () => void
|
hideWindow: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgUploadRSForm({ show, hideWindow }: DlgUploadRSFormProps) {
|
function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
|
||||||
const { upload } = useRSForm();
|
const { upload } = useRSForm();
|
||||||
const [loadMetadata, setLoadMetadata] = useState(false);
|
const [loadMetadata, setLoadMetadata] = useState(false);
|
||||||
const [file, setFile] = useState<File | undefined>()
|
const [file, setFile] = useState<File | undefined>()
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
hideWindow();
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +39,6 @@ function DlgUploadRSForm({ show, hideWindow }: DlgUploadRSFormProps) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title='Загрузка схемы из Экстеор'
|
title='Загрузка схемы из Экстеор'
|
||||||
show={show}
|
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
canSubmit={!!file}
|
canSubmit={!!file}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
@ -57,7 +54,7 @@ function DlgUploadRSForm({ show, hideWindow }: DlgUploadRSFormProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DlgUploadRSForm;
|
export default DlgUploadRSForm;
|
||||||
|
|
|
@ -7,14 +7,18 @@ import SubmitButton from '../../components/Common/SubmitButton';
|
||||||
import TextArea from '../../components/Common/TextArea';
|
import TextArea from '../../components/Common/TextArea';
|
||||||
import { DumpBinIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
import { DumpBinIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { type CstType, EditMode, type ICstCreateData, ICstUpdateData } from '../../utils/models';
|
import { type CstType, EditMode, type ICstCreateData, ICstUpdateData, SyntaxTree } from '../../utils/models';
|
||||||
import { createAliasFor, getCstTypeLabel } from '../../utils/staticUI';
|
import { createAliasFor, getCstTypeLabel } from '../../utils/staticUI';
|
||||||
import DlgCreateCst from './DlgCreateCst';
|
import DlgCreateCst from './DlgCreateCst';
|
||||||
import EditorRSExpression from './EditorRSExpression';
|
import EditorRSExpression from './EditorRSExpression';
|
||||||
import ViewSideConstituents from './elements/ViewSideConstituents';
|
import ViewSideConstituents from './elements/ViewSideConstituents';
|
||||||
import { RSTabsList } from './RSTabs';
|
import { RSTabsList } from './RSTabs';
|
||||||
|
|
||||||
function EditorConstituenta() {
|
interface EditorConstituentaProps {
|
||||||
|
onShowAST: (ast: SyntaxTree) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditorConstituenta({onShowAST}: EditorConstituentaProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const {
|
const {
|
||||||
activeCst, activeID, schema, setActiveID, processing, isEditable,
|
activeCst, activeID, schema, setActiveID, processing, isEditable,
|
||||||
|
@ -132,12 +136,11 @@ function EditorConstituenta() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-start w-full gap-2'>
|
<div className='flex items-start w-full gap-2'>
|
||||||
<DlgCreateCst
|
{showCstModal && <DlgCreateCst
|
||||||
show={showCstModal}
|
|
||||||
hideWindow={() => { setShowCstModal(false); }}
|
hideWindow={() => { setShowCstModal(false); }}
|
||||||
onCreate={handleAddNew}
|
onCreate={handleAddNew}
|
||||||
defaultType={activeCst?.cstType as CstType}
|
defaultType={activeCst?.cstType as CstType}
|
||||||
/>
|
/>}
|
||||||
<form onSubmit={handleSubmit} className='flex-grow min-w-[50rem] max-w-min px-4 py-2 border'>
|
<form onSubmit={handleSubmit} className='flex-grow min-w-[50rem] max-w-min px-4 py-2 border'>
|
||||||
<div className='flex items-start justify-between'>
|
<div className='flex items-start justify-between'>
|
||||||
<button type='submit'
|
<button type='submit'
|
||||||
|
@ -204,6 +207,7 @@ function EditorConstituenta() {
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
isActive={editMode === EditMode.RSLANG}
|
isActive={editMode === EditMode.RSLANG}
|
||||||
toggleEditMode={() => { setEditMode(EditMode.RSLANG); }}
|
toggleEditMode={() => { setEditMode(EditMode.RSLANG); }}
|
||||||
|
onShowAST={onShowAST}
|
||||||
onChange={event => { setExpression(event.target.value); }}
|
onChange={event => { setExpression(event.target.value); }}
|
||||||
setValue={setExpression}
|
setValue={setExpression}
|
||||||
setTypification={setTypification}
|
setTypification={setTypification}
|
||||||
|
|
|
@ -284,11 +284,10 @@ function EditorItems({ onOpenEdit }: EditorItemsProps) {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
<DlgCreateCst
|
{showCstModal && <DlgCreateCst
|
||||||
show={showCstModal}
|
|
||||||
hideWindow={() => { setShowCstModal(false); }}
|
hideWindow={() => { setShowCstModal(false); }}
|
||||||
onCreate={handleAddNew}
|
onCreate={handleAddNew}
|
||||||
/>
|
/>}
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<div
|
<div
|
||||||
className={'flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem] clr-app' +
|
className={'flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem] clr-app' +
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Loader } from '../../components/Common/Loader';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import useCheckExpression from '../../hooks/useCheckExpression';
|
import useCheckExpression from '../../hooks/useCheckExpression';
|
||||||
import { TokenID } from '../../utils/enums';
|
import { TokenID } from '../../utils/enums';
|
||||||
import { CstType } from '../../utils/models';
|
import { CstType, SyntaxTree } from '../../utils/models';
|
||||||
import ParsingResult from './elements/ParsingResult';
|
import ParsingResult from './elements/ParsingResult';
|
||||||
import RSLocalButton from './elements/RSLocalButton';
|
import RSLocalButton from './elements/RSLocalButton';
|
||||||
import RSTokenButton from './elements/RSTokenButton';
|
import RSTokenButton from './elements/RSTokenButton';
|
||||||
|
@ -22,13 +22,14 @@ interface EditorRSExpressionProps {
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
value: string
|
value: string
|
||||||
onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void
|
onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void
|
||||||
|
onShowAST: (ast: SyntaxTree) => void
|
||||||
toggleEditMode: () => void
|
toggleEditMode: () => void
|
||||||
setTypification: (typificaiton: string) => void
|
setTypification: (typificaiton: string) => void
|
||||||
setValue: (expression: string) => void
|
setValue: (expression: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorRSExpression({
|
function EditorRSExpression({
|
||||||
id, label, disabled, isActive, placeholder, value, setValue,
|
id, label, disabled, isActive, placeholder, value, setValue, onShowAST,
|
||||||
toggleEditMode, setTypification, onChange
|
toggleEditMode, setTypification, onChange
|
||||||
}: EditorRSExpressionProps) {
|
}: EditorRSExpressionProps) {
|
||||||
const { schema, activeCst } = useRSForm();
|
const { schema, activeCst } = useRSForm();
|
||||||
|
@ -48,11 +49,11 @@ function EditorRSExpression({
|
||||||
const prefix = activeCst?.alias + (activeCst?.cstType === CstType.STRUCTURED ? '::=' : ':==');
|
const prefix = activeCst?.alias + (activeCst?.cstType === CstType.STRUCTURED ? '::=' : ':==');
|
||||||
const expression = prefix + value;
|
const expression = prefix + value;
|
||||||
checkExpression(expression, parse => {
|
checkExpression(expression, parse => {
|
||||||
// TODO: update cursor position
|
|
||||||
if (!parse.parseResult && parse.errors.length > 0) {
|
if (!parse.parseResult && parse.errors.length > 0) {
|
||||||
const errorPosition = parse.errors[0].position - prefix.length
|
const errorPosition = parse.errors[0].position - prefix.length
|
||||||
expressionCtrl.current!.selectionStart = errorPosition;
|
expressionCtrl.current!.selectionStart = errorPosition;
|
||||||
expressionCtrl.current!.selectionEnd = errorPosition;
|
expressionCtrl.current!.selectionEnd = errorPosition;
|
||||||
|
expressionCtrl.current!.focus();
|
||||||
}
|
}
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
setTypification(parse.typification);
|
setTypification(parse.typification);
|
||||||
|
@ -85,25 +86,26 @@ function EditorRSExpression({
|
||||||
if (!expressionCtrl.current) {
|
if (!expressionCtrl.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.altKey) {
|
const text = new TextWrapper(expressionCtrl.current);
|
||||||
const text = new TextWrapper(expressionCtrl.current);
|
if (event.shiftKey && event.key === '*' && !event.altKey) {
|
||||||
if (text.processAltKey(event.key)) {
|
text.insertToken(TokenID.DECART);
|
||||||
event.preventDefault();
|
} else if (event.altKey) {
|
||||||
text.finalize();
|
if (!text.processAltKey(event.key)) {
|
||||||
setValue(text.value);
|
return;
|
||||||
setIsModified(true);
|
|
||||||
}
|
}
|
||||||
} else if (!event.ctrlKey) {
|
} else if (!event.ctrlKey) {
|
||||||
const newSymbol = getSymbolSubstitute(event.key);
|
const newSymbol = getSymbolSubstitute(event.key);
|
||||||
if (newSymbol) {
|
if (!newSymbol) {
|
||||||
event.preventDefault();
|
return;
|
||||||
const text = new TextWrapper(expressionCtrl.current);
|
|
||||||
text.replaceWith(newSymbol);
|
|
||||||
text.finalize();
|
|
||||||
setValue(text.value);
|
|
||||||
setIsModified(true);
|
|
||||||
}
|
}
|
||||||
|
text.replaceWith(newSymbol);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
text.finalize();
|
||||||
|
setValue(text.value);
|
||||||
|
setIsModified(true);
|
||||||
}, [expressionCtrl, setValue]);
|
}, [expressionCtrl, setValue]);
|
||||||
|
|
||||||
const handleFocusIn = useCallback(() => {
|
const handleFocusIn = useCallback(() => {
|
||||||
|
@ -225,8 +227,11 @@ function EditorRSExpression({
|
||||||
parseData={parseData}
|
parseData={parseData}
|
||||||
/>}
|
/>}
|
||||||
</div>
|
</div>
|
||||||
{ loading && <Loader />}
|
{ (loading || parseData) &&
|
||||||
{ parseData && <ParsingResult data={parseData} />}
|
<div className='w-full overflow-y-auto border mt-2 max-h-[14rem] min-h-[7rem]'>
|
||||||
|
{ loading && <Loader />}
|
||||||
|
{ !loading && parseData && <ParsingResult data={parseData} onShowAST={onShowAST} />}
|
||||||
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useLayoutEffect, useState } from 'react';
|
import { useCallback, useEffect, useLayoutEffect, useState } from 'react';
|
||||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
|
||||||
import BackendError from '../../components/BackendError';
|
import BackendError from '../../components/BackendError';
|
||||||
|
@ -6,8 +6,9 @@ import ConceptTab from '../../components/Common/ConceptTab';
|
||||||
import { Loader } from '../../components/Common/Loader';
|
import { Loader } from '../../components/Common/Loader';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import useLocalStorage from '../../hooks/useLocalStorage';
|
import useLocalStorage from '../../hooks/useLocalStorage';
|
||||||
import { type IConstituenta } from '../../utils/models';
|
import { type IConstituenta,SyntaxTree } from '../../utils/models';
|
||||||
import DlgCloneRSForm from './DlgCloneRSForm';
|
import DlgCloneRSForm from './DlgCloneRSForm';
|
||||||
|
import DlgShowAST from './DlgShowAST';
|
||||||
import DlgUploadRSForm from './DlgUploadRSForm';
|
import DlgUploadRSForm from './DlgUploadRSForm';
|
||||||
import EditorConstituenta from './EditorConstituenta';
|
import EditorConstituenta from './EditorConstituenta';
|
||||||
import EditorItems from './EditorItems';
|
import EditorItems from './EditorItems';
|
||||||
|
@ -26,8 +27,16 @@ function RSTabs() {
|
||||||
const [tabIndex, setTabIndex] = useLocalStorage('rsform_edit_tab', RSTabsList.CARD);
|
const [tabIndex, setTabIndex] = useLocalStorage('rsform_edit_tab', RSTabsList.CARD);
|
||||||
const [init, setInit] = useState(false);
|
const [init, setInit] = useState(false);
|
||||||
|
|
||||||
const [showUploadDialog, setShowUploadDialog] = useState(false);
|
const [showUpload, setShowUpload] = useState(false);
|
||||||
const [showCloneDialog, setShowCloneDialog] = useState(false);
|
const [showClone, setShowClone] = useState(false);
|
||||||
|
const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]);
|
||||||
|
const [showAST, setShowAST] = useState(false);
|
||||||
|
|
||||||
|
const onShowAST = useCallback(
|
||||||
|
(ast: SyntaxTree) => {
|
||||||
|
setSyntaxTree(ast);
|
||||||
|
setShowAST(true);
|
||||||
|
}, [])
|
||||||
|
|
||||||
const onEditCst = (cst: IConstituenta) => {
|
const onEditCst = (cst: IConstituenta) => {
|
||||||
setActiveID(cst.id);
|
setActiveID(cst.id);
|
||||||
|
@ -90,14 +99,9 @@ function RSTabs() {
|
||||||
{ error && <BackendError error={error} />}
|
{ error && <BackendError error={error} />}
|
||||||
{ schema && !loading &&
|
{ schema && !loading &&
|
||||||
<>
|
<>
|
||||||
<DlgUploadRSForm
|
{showUpload && <DlgUploadRSForm hideWindow={() => { setShowUpload(false); }}/>}
|
||||||
show={showUploadDialog}
|
{showClone && <DlgCloneRSForm hideWindow={() => { setShowClone(false); }}/>}
|
||||||
hideWindow={() => { setShowUploadDialog(false); }}
|
{showAST && <DlgShowAST syntaxTree={syntaxTree} hideWindow={() => { setShowAST(false); }}/>}
|
||||||
/>
|
|
||||||
<DlgCloneRSForm
|
|
||||||
show={showCloneDialog}
|
|
||||||
hideWindow={() => { setShowCloneDialog(false); }}
|
|
||||||
/>
|
|
||||||
<Tabs
|
<Tabs
|
||||||
selectedIndex={tabIndex}
|
selectedIndex={tabIndex}
|
||||||
onSelect={onSelectTab}
|
onSelect={onSelectTab}
|
||||||
|
@ -106,8 +110,8 @@ function RSTabs() {
|
||||||
>
|
>
|
||||||
<TabList className='flex items-start w-fit clr-bg-pop'>
|
<TabList className='flex items-start w-fit clr-bg-pop'>
|
||||||
<RSTabsMenu
|
<RSTabsMenu
|
||||||
showCloneDialog={() => setShowCloneDialog(true)}
|
showCloneDialog={() => setShowClone(true)}
|
||||||
showUploadDialog={() => setShowUploadDialog(true)}
|
showUploadDialog={() => setShowUpload(true)}
|
||||||
/>
|
/>
|
||||||
<ConceptTab>Паспорт схемы</ConceptTab>
|
<ConceptTab>Паспорт схемы</ConceptTab>
|
||||||
<ConceptTab className='border-x-2 clr-border min-w-[10rem] flex justify-between gap-2'>
|
<ConceptTab className='border-x-2 clr-border min-w-[10rem] flex justify-between gap-2'>
|
||||||
|
@ -127,7 +131,7 @@ function RSTabs() {
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<EditorConstituenta />
|
<EditorConstituenta onShowAST={onShowAST} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Tabs></>
|
</Tabs></>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,41 @@
|
||||||
import PrettyJson from '../../../components/Common/PrettyJSON';
|
import { IExpressionParse, SyntaxTree } from '../../../utils/models';
|
||||||
import { ExpressionParse } from '../../../utils/models';
|
import { getRSErrorMessage, getRSErrorPrefix } from '../../../utils/staticUI';
|
||||||
|
|
||||||
interface ParsingResultProps {
|
interface ParsingResultProps {
|
||||||
data?: ExpressionParse
|
data: IExpressionParse
|
||||||
|
onShowAST: (ast: SyntaxTree) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function ParsingResult({ data }: ParsingResultProps) {
|
function ParsingResult({ data, onShowAST }: ParsingResultProps) {
|
||||||
|
const errorCount = data.errors.reduce((total, error) => (error.isCritical ? total + 1 : total), 0);
|
||||||
|
const warningsCount = data.errors.length - errorCount;
|
||||||
|
|
||||||
|
function handleShowAST() {
|
||||||
|
onShowAST(data.ast);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full px-3 py-2 mt-2 border'>
|
<div className='px-3 py-2'>
|
||||||
<PrettyJson data={data} />
|
<p>Ошибок: <b>{errorCount}</b> | Предупреждений: <b>{warningsCount}</b></p>
|
||||||
{/* <textarea
|
{data.errors.map(error => {
|
||||||
className={'leading-tight border shadow dark:bg-gray-800 '}
|
return (
|
||||||
rows={3}
|
<p className='text-red'>
|
||||||
placeholder='Результаты анализа выражения'
|
<span className='font-semibold'>{error.isCritical ? 'Ошибка' : 'Предупреждение'} {getRSErrorPrefix(error)}: </span>
|
||||||
value={data}
|
<span>{getRSErrorMessage(error)}</span>
|
||||||
/> */}
|
</p>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{data.astText &&
|
||||||
|
<p>
|
||||||
|
<button type='button'
|
||||||
|
className='font-semibold underline text-primary'
|
||||||
|
title='отобразить дерево разбора'
|
||||||
|
onClick={handleShowAST}
|
||||||
|
>
|
||||||
|
Дерево разбора:
|
||||||
|
</button>
|
||||||
|
<span> {data.astText}</span>
|
||||||
|
</p>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { ExpressionParse,ExpressionStatus, type IConstituenta, inferStatus, ParsingStatus } from '../../../utils/models';
|
import { ExpressionStatus, type IConstituenta, IExpressionParse,inferStatus, ParsingStatus } from '../../../utils/models';
|
||||||
import { getStatusInfo } from '../../../utils/staticUI';
|
import { getStatusInfo } from '../../../utils/staticUI';
|
||||||
|
|
||||||
interface StatusBarProps {
|
interface StatusBarProps {
|
||||||
isModified?: boolean
|
isModified?: boolean
|
||||||
parseData?: ExpressionParse
|
parseData?: IExpressionParse
|
||||||
constituenta?: IConstituenta
|
constituenta?: IConstituenta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ function ViewSideConstituents({ expression }: ViewSideConstituentsProps) {
|
||||||
name: 'Описание',
|
name: 'Описание',
|
||||||
id: 'description',
|
id: 'description',
|
||||||
selector: (cst: IConstituenta) =>
|
selector: (cst: IConstituenta) =>
|
||||||
cst.term?.resolved ?? cst.definition?.text.resolved ?? cst.definition?.formal ?? cst.convention ?? '',
|
cst.term.resolved || cst.definition.text.resolved || cst.definition.formal || cst.convention,
|
||||||
minWidth: '350px',
|
minWidth: '350px',
|
||||||
wrap: true,
|
wrap: true,
|
||||||
conditionalCellStyles: [
|
conditionalCellStyles: [
|
||||||
|
|
|
@ -171,6 +171,7 @@ export class TextWrapper implements IManagedText {
|
||||||
case 'q': return this.insertToken(TokenID.BIGPR);
|
case 'q': return this.insertToken(TokenID.BIGPR);
|
||||||
case 'w': return this.insertToken(TokenID.SMALLPR);
|
case 'w': return this.insertToken(TokenID.SMALLPR);
|
||||||
case 'e': return this.insertToken(TokenID.BOOLEAN);
|
case 'e': return this.insertToken(TokenID.BOOLEAN);
|
||||||
|
case 'E': return this.insertToken(TokenID.DECART);
|
||||||
case 'r': return this.insertToken(TokenID.REDUCE);
|
case 'r': return this.insertToken(TokenID.REDUCE);
|
||||||
case 't': return this.insertToken(TokenID.NT_RECURSIVE_FULL);
|
case 't': return this.insertToken(TokenID.NT_RECURSIVE_FULL);
|
||||||
case 'a': return this.insertToken(TokenID.INTERSECTION);
|
case 'a': return this.insertToken(TokenID.INTERSECTION);
|
||||||
|
|
|
@ -5,14 +5,14 @@ import { type ErrorInfo } from '../components/BackendError'
|
||||||
import { FilterType, RSFormsFilter } from '../hooks/useRSForms'
|
import { FilterType, RSFormsFilter } from '../hooks/useRSForms'
|
||||||
import { config } from './constants'
|
import { config } from './constants'
|
||||||
import {
|
import {
|
||||||
ExpressionParse,
|
|
||||||
IConstituentaList,
|
IConstituentaList,
|
||||||
IConstituentaMeta,
|
IConstituentaMeta,
|
||||||
ICstCreateData, ICstCreatedResponse, ICstMovetoData, ICstUpdateData,
|
ICstCreateData, ICstCreatedResponse, ICstMovetoData, ICstUpdateData,
|
||||||
ICurrentUser, IRSFormCreateData, IRSFormData,
|
ICurrentUser, IExpressionParse,
|
||||||
|
IRSExpression,
|
||||||
|
IRSFormCreateData, IRSFormData,
|
||||||
IRSFormMeta, IRSFormUpdateData, IRSFormUploadData, IUserInfo,
|
IRSFormMeta, IRSFormUpdateData, IRSFormUploadData, IUserInfo,
|
||||||
IUserLoginData, IUserProfile, IUserSignupData, RSExpression
|
IUserLoginData, IUserProfile, IUserSignupData} from './models'
|
||||||
} from './models'
|
|
||||||
|
|
||||||
// ================ Data transfer types ================
|
// ================ Data transfer types ================
|
||||||
export type DataCallback<ResponseData = undefined> = (data: ResponseData) => void;
|
export type DataCallback<ResponseData = undefined> = (data: ResponseData) => void;
|
||||||
|
@ -201,7 +201,7 @@ export function patchMoveConstituenta(schema: string, request: FrontExchange<ICs
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function postCheckExpression(schema: string, request: FrontExchange<RSExpression, ExpressionParse>) {
|
export function postCheckExpression(schema: string, request: FrontExchange<IRSExpression, IExpressionParse>) {
|
||||||
AxiosPost({
|
AxiosPost({
|
||||||
title: `Check expression for RSForm id=${schema}: ${request.data.expression }`,
|
title: `Check expression for RSForm id=${schema}: ${request.data.expression }`,
|
||||||
endpoint: `${config.url.BASE}rsforms/${schema}/check/`,
|
endpoint: `${config.url.BASE}rsforms/${schema}/check/`,
|
||||||
|
|
|
@ -103,58 +103,72 @@ export enum TokenID {
|
||||||
END,
|
END,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum RSError {
|
export enum RSErrorClass {
|
||||||
|
LEXER,
|
||||||
|
PARSER,
|
||||||
|
SEMANTIC,
|
||||||
|
UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
// '8201': 'Число превышает максимально допустимое значение 2147483647!',
|
export enum RSErrorType {
|
||||||
// '8203': 'Нераспознанный символ!',
|
syntax = 0x8400, // Неизвестная синтаксическая ошибка
|
||||||
// '8400': 'Неопределенная синтаксическая ошибка!',
|
missingParanthesis = 0x8406, // Пропущена скобка ')'
|
||||||
// '8406': 'Пропущена скобка ‘)’!',
|
missingCurlyBrace = 0x8407, // Пропущена скобка '}'
|
||||||
// '8407': 'Пропущена скобка ‘}’!',
|
invalidQuantifier = 0x8408, // Некорректная кванторная декларация
|
||||||
// '8408': 'Некорректная кванторная декларация переменной!',
|
expectedArgDeclaration = 0x8414, // Ожидалось объявление аргументов
|
||||||
// '8414': 'Некорректное объявление аргументов функции!',
|
expectedLocal = 0x8415, // Ожидалось имя локальной переменной
|
||||||
// '8415': 'Некорректное имя локальной переменной в декларации функции!',
|
localDoubleDeclare = 0x2801, // Повторное использование одного и того же имени переменной
|
||||||
// '2801': 'Повторное объявление локальной переменной!',
|
localNotUsed = 0x2802, // Переменная объявлена но не использована
|
||||||
// '2802': 'Локальная переменная объявлена, но не использована!',
|
|
||||||
// '8801': 'Использование необъявленной локальной переменной!',
|
localUndeclared = 0x8801, // Использование необъявленной переменной
|
||||||
// '8802': 'Повторное объявление локальной переменной внутри области действия!',
|
localShadowing = 0x8802, // Повторное объявление переменной
|
||||||
// '8803': 'Типизация операндов не совпадает!',
|
|
||||||
// '8804': 'Использована конституента с неопределенной типизацией!',
|
typesNotEqual = 0x8803, // Некорректное использование операций
|
||||||
// '8805': 'Одна из проекций декартова произведения не является типизированным множеством имеющим характер множества!',
|
globalNotTyped = 0x8804, // Не определена типизация глобальной конституенты
|
||||||
// '8806': 'Аргумент булеана не является типизированным множеством имеющим характер множества!',
|
invalidDecart = 0x8805, // Одна из проекций не является множеством
|
||||||
// '8807': 'Операнд теоретико-множественного оператора не является типизированным множеством имеющим характер множества!',
|
invalidBoolean = 0x8806, // Попытка взять булеан от элемента, не имеющего характер множества
|
||||||
// '8808': 'Операнд оператора card не является типизированным множеством имеющим характер множества!',
|
invalidTypeOperation = 0x8807, // Применение ТМО к операндам, не имеющим характер множества
|
||||||
// '8809': 'Операнд оператора debool не является типизированным множеством имеющим характер множества!',
|
invalidCard = 0x8808, // Мощность множества не определена для элемента
|
||||||
// '880A': 'Неизвестное имя функции!',
|
invalidDebool = 0x8809, // Дебулеан берется от немножества
|
||||||
// '880B': 'Некорректное использование имени функции без аргументов!',
|
globalFuncMissing = 0x880A, // Неизвестное имя функции
|
||||||
// '8810': 'Операнд оператора red не является типизированным множеством имеющим характер двойного булеана!',
|
globalFuncWithoutArgs = 0x880B, // Некорректное использование имени функции без аргументов
|
||||||
// '8811': 'Некорректная типизация аргумента: проекция не определена!',
|
invalidReduce = 0x8810, // Red можно брать только от двойного булеана
|
||||||
// '8812': 'Некорректная типизация аргумента: T(Pri(a)) = B(Pi(D(T(a))))!',
|
invalidProjectionTuple = 0x8811, // Не определена проекция
|
||||||
// '8813': 'Типизация элементов перечисления не совпадает!',
|
invalidProjectionSet = 0x8812, // Большая проекция определена только для множеств!
|
||||||
// '8814': 'Некорректная декларация связанных локальных переменных: количестве переменных в кортеже не соответствует размерности декартова произведения типизации!',
|
invalidEnumeration = 0x8813, // Типизация аргументов перечисления не совпадает
|
||||||
// '8815': 'Локальная переменная используется вне области действия!',
|
ivalidBinding = 0x8814, // Количество переменных в кортеже не соответствует размерности декартова произведения
|
||||||
// '8816': 'Несоответствие типизаций операндов для предиката!',
|
localOutOfScope = 0x8815, // Использование имени вне области видимости
|
||||||
// '8818': 'Некорректное количество аргументов терм-функции!',
|
invalidElementPredicat = 0x8816, // Несоответствие типов для проверки принадлежности
|
||||||
// '8819': 'Типизация аргумента терм-функции не совпадает с объявленной!',
|
invalidArgsArtity = 0x8818, // Некорректное количество аргументов терм-функции
|
||||||
// '881A': 'Сравнение кортежа или элемента с пустым множеством!',
|
invalidArgumentType = 0x8819, // Типизация аргумента не совпадает с объявленной
|
||||||
// '881C': 'Выражение родовой структуры должно быть ступенью!',
|
invalidEqualsEmpty = 0x881A, // Сравнение с пустым множеством не множества
|
||||||
// '881F': 'Ожидалось выражение объявления функции!',
|
globalStructure = 0x881C, // Родовая структура должна быть ступенью
|
||||||
// '8820': 'Некорректное использование пустого множества как типизированного выражения!',
|
globalExpectedFunction = 0x881F, // Ожидалось выражение объявления функции
|
||||||
// '8821': 'Радикалы запрещены вне деклараций терм-функций!',
|
emptySetUsage = 0x8820, // Некорректное использование пустого множества как типизированного выражения
|
||||||
// '8822': 'Типизация аргумента фильтра не корректна!',
|
radicalUsage = 0x8821, // Радикалы запрещены вне деклараций терм-функций
|
||||||
// '8823': 'Количество параметров фильтра не соответствует количеству индексов!',
|
invalidFilterArgumentType = 0x8822, // Типизация аргумента фильтра не корректна
|
||||||
// '8824': 'Для выбранного типа не поддерживаются арифметические операции!',
|
invalidFilterArity = 0x8823, // Количество параметров фильра не соответствует количеству индексов
|
||||||
// '8825': 'Типизации не совместимы для выбранной операции/предиката!',
|
arithmeticNotSupported = 0x8824, // Для данного типа не поддерживается арифметика
|
||||||
// '8826': 'Для выбранного типа не поддерживаются предикаты порядка!',
|
typesNotCompatible = 0x8825, // Типы не совместимы в данном контексте
|
||||||
// '8840': 'Используется неинтерпретируемый глобальный идентификатор!',
|
orderingNotSupported = 0x8826, // Для данного типа не поддерживается порядок элементов
|
||||||
// '8841': 'Использование свойства в качестве значения!',
|
|
||||||
// '8842': 'Не удалось получить дерево разбора для глобального идентификатора!',
|
globalNoValue = 0x8840, // Используется неинтерпретируемый глобальный идентификатор
|
||||||
// '8843': 'Функция не интерпретируется для данных аргументов!',
|
invalidPropertyUsage = 0x8841, // Использование свойства в качестве значения
|
||||||
// '8A00': 'Неизвестная ошибка: вычисление прервано!',
|
globalMissingAST = 0x8842, // Не удалось получить дерево разбора для глобального идентификатора
|
||||||
// '8A01': 'Превышен пределен количества элементов множества!',
|
globalFuncNoInterpretation = 0x8843, // Функция не интерпретируется для данных аргументов
|
||||||
// '8A02': 'Превышен пределен количества элементов в основании булеана!',
|
}
|
||||||
// '8A03': 'Использование конституенты с неопределенным значением!',
|
|
||||||
// '8A04': 'Превышен предел количества итераций!',
|
const ERRCODE_LEXER_MASK = 0x0200;
|
||||||
// '8A05': 'Попытка взять debool от многоэлементного множества!',
|
const ERRCODE_PARSER_MASK = 0x0400;
|
||||||
// '8A06': 'Попытка перебрать бесконечное множество!'
|
const ERRCODE_TYPE_MASK = 0x0800;
|
||||||
|
export function resolveErrorClass(error: RSErrorType): RSErrorClass {
|
||||||
|
if ((error & ERRCODE_LEXER_MASK) != 0) {
|
||||||
|
return RSErrorClass.LEXER;
|
||||||
|
} else if ((error & ERRCODE_PARSER_MASK) != 0) {
|
||||||
|
return RSErrorClass.PARSER;
|
||||||
|
} else if ((error & ERRCODE_TYPE_MASK) != 0) {
|
||||||
|
return RSErrorClass.SEMANTIC;
|
||||||
|
} else {
|
||||||
|
return RSErrorClass.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { RSErrorType, TokenID } from './enums'
|
||||||
|
|
||||||
// ========= Users ===========
|
// ========= Users ===========
|
||||||
export interface IUser {
|
export interface IUser {
|
||||||
id: number | null
|
id: number | null
|
||||||
|
@ -21,45 +23,53 @@ export interface IUserSignupData extends Omit<IUser, 'is_staff' | 'id'> {
|
||||||
export interface IUserProfile extends Omit<IUser, 'is_staff'> {}
|
export interface IUserProfile extends Omit<IUser, 'is_staff'> {}
|
||||||
export interface IUserInfo extends Omit<IUserProfile, 'email'> {}
|
export interface IUserInfo extends Omit<IUserProfile, 'email'> {}
|
||||||
|
|
||||||
// ======== Parsing ============
|
// ======== RS Parsing ============
|
||||||
// ValueClass
|
|
||||||
export enum ValueClass {
|
|
||||||
INVALID = 'invalid',
|
|
||||||
VALUE = 'value',
|
|
||||||
PROPERTY = 'property'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Syntax
|
|
||||||
export enum Syntax {
|
export enum Syntax {
|
||||||
UNDEF = 'undefined',
|
UNDEF = 'undefined',
|
||||||
ASCII = 'ascii',
|
ASCII = 'ascii',
|
||||||
MATH = 'math'
|
MATH = 'math'
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParsingStatus
|
export enum ValueClass {
|
||||||
|
INVALID = 'invalid',
|
||||||
|
VALUE = 'value',
|
||||||
|
PROPERTY = 'property'
|
||||||
|
}
|
||||||
|
|
||||||
export enum ParsingStatus {
|
export enum ParsingStatus {
|
||||||
UNDEF = 'undefined',
|
UNDEF = 'undefined',
|
||||||
VERIFIED = 'verified',
|
VERIFIED = 'verified',
|
||||||
INCORRECT = 'incorrect'
|
INCORRECT = 'incorrect'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RSErrorDescription {
|
export interface IRSErrorDescription {
|
||||||
errorType: number
|
errorType: RSErrorType
|
||||||
position: number
|
position: number
|
||||||
isCritical: boolean
|
isCritical: boolean
|
||||||
params: string[]
|
params: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExpressionParse {
|
export interface ISyntaxTreeNode {
|
||||||
|
uid: number
|
||||||
|
parent: number
|
||||||
|
typeID: TokenID
|
||||||
|
start: number
|
||||||
|
finish: number
|
||||||
|
data: unknown
|
||||||
|
}
|
||||||
|
export type SyntaxTree = ISyntaxTreeNode[]
|
||||||
|
|
||||||
|
export interface IExpressionParse {
|
||||||
parseResult: boolean
|
parseResult: boolean
|
||||||
syntax: Syntax
|
syntax: Syntax
|
||||||
typification: string
|
typification: string
|
||||||
valueClass: ValueClass
|
valueClass: ValueClass
|
||||||
|
errors: IRSErrorDescription[]
|
||||||
astText: string
|
astText: string
|
||||||
errors: RSErrorDescription[]
|
ast: SyntaxTree
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RSExpression {
|
export interface IRSExpression {
|
||||||
expression: string
|
expression: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { TokenID } from './enums';
|
import { resolveErrorClass,RSErrorClass, RSErrorType, TokenID } from './enums';
|
||||||
import { CstType, ExpressionStatus, type IConstituenta, type IRSForm, ParsingStatus, ValueClass } from './models';
|
import { CstType, ExpressionStatus, type IConstituenta, IRSErrorDescription,type IRSForm, ParsingStatus, ValueClass } from './models';
|
||||||
|
|
||||||
export interface IRSButtonData {
|
export interface IRSButtonData {
|
||||||
text: string
|
text: string
|
||||||
|
@ -30,7 +30,7 @@ export function getRSButtonData(id: TokenID): IRSButtonData {
|
||||||
};
|
};
|
||||||
case TokenID.DECART: return {
|
case TokenID.DECART: return {
|
||||||
text: '×',
|
text: '×',
|
||||||
tooltip: 'Декартово произведение [Shift + 8]'
|
tooltip: 'Декартово произведение [Shift + 8 / Alt + Shift + E]'
|
||||||
};
|
};
|
||||||
case TokenID.PUNC_PL: return {
|
case TokenID.PUNC_PL: return {
|
||||||
text: '( )',
|
text: '( )',
|
||||||
|
@ -321,4 +321,104 @@ export function getCloneTitle(schema: IRSForm): string {
|
||||||
} else {
|
} else {
|
||||||
return (schema.title + '+');
|
return (schema.title + '+');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRSErrorPrefix(error: IRSErrorDescription): string {
|
||||||
|
const id = error.errorType.toString(16)
|
||||||
|
switch(resolveErrorClass(error.errorType)) {
|
||||||
|
case RSErrorClass.LEXER: return 'L' + id;
|
||||||
|
case RSErrorClass.PARSER: return 'P' + id;
|
||||||
|
case RSErrorClass.SEMANTIC: return 'S' + id;
|
||||||
|
case RSErrorClass.UNKNOWN: return 'U' + id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRSErrorMessage(error: IRSErrorDescription): string {
|
||||||
|
switch (error.errorType) {
|
||||||
|
case RSErrorType.syntax:
|
||||||
|
return 'UNKNOWN SYNTAX ERROR';
|
||||||
|
case RSErrorType.missingParanthesis:
|
||||||
|
return 'Некорректная конструкция языка родов структур, проверьте структуру выражения';
|
||||||
|
case RSErrorType.missingCurlyBrace:
|
||||||
|
return "Пропущен символ '}'";
|
||||||
|
case RSErrorType.invalidQuantifier:
|
||||||
|
return 'Некорректная кванторная декларация';
|
||||||
|
case RSErrorType.expectedArgDeclaration:
|
||||||
|
return 'Ожидалось объявление аргументов терм-функции';
|
||||||
|
case RSErrorType.expectedLocal:
|
||||||
|
return 'Ожидалось имя локальной переменной';
|
||||||
|
|
||||||
|
case RSErrorType.localDoubleDeclare:
|
||||||
|
return `Предупреждение! Повторное объявление локальной переменной ${error.params[0]}`;
|
||||||
|
case RSErrorType.localNotUsed:
|
||||||
|
return `Предупреждение! Переменная объявлена но не использована: ${error.params[0]}`;
|
||||||
|
case RSErrorType.localShadowing:
|
||||||
|
return `Повторное объявление переменной: ${error.params[0]}`;
|
||||||
|
|
||||||
|
case RSErrorType.typesNotEqual:
|
||||||
|
return `Типизация операндов не совпадает! ${error.params[0]} != ${error.params[1]}`;
|
||||||
|
case RSErrorType.globalNotTyped:
|
||||||
|
return `Типизация конституенты не определена: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidDecart:
|
||||||
|
return `τ(α×b) = ℬ(𝔇τ(α)×𝔇τ(b)). Некорректная типизация аргумента: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidBoolean:
|
||||||
|
return `τ(ℬ(a)) = ℬℬ𝔇τ(a). Некорректная типизация аргумента: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidTypeOperation:
|
||||||
|
return `Типизация операнда теоретико-множественной операции не корректна: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidCard:
|
||||||
|
return `Некорректная типизация аргумента операции мощности: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidDebool:
|
||||||
|
return `τ(debool(a)) = 𝔇τ(a). Некорректная типизация аргумента: ${error.params[0]}`;
|
||||||
|
case RSErrorType.globalFuncMissing:
|
||||||
|
return `Неизвестное имя функции: ${error.params[0]}`;
|
||||||
|
case RSErrorType.globalFuncWithoutArgs:
|
||||||
|
return `Некорректное использование имени функции без аргументов: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidReduce:
|
||||||
|
return `τ(red(a)) = ℬ𝔇𝔇τ(a). Некорректная типизация аргумента: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidProjectionTuple:
|
||||||
|
return `Проекция не определена: ${error.params[0]} -> ${error.params[1]}`;
|
||||||
|
case RSErrorType.invalidProjectionSet:
|
||||||
|
return `τ(Pri(a)) = ℬ𝒞i𝔇τ(a). Некорректная типизация аргумента: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidEnumeration:
|
||||||
|
return `Типизация аргументов перечисления не совпадает: ${error.params[0]} != ${error.params[1]}`;
|
||||||
|
case RSErrorType.ivalidBinding:
|
||||||
|
return `Количество переменных в кортеже не соответствует размерности декартова произведения`;
|
||||||
|
case RSErrorType.localOutOfScope:
|
||||||
|
return `Использование имени переменной вне области действия: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidElementPredicat:
|
||||||
|
return `Несоответствие типизаций операндов для оператора: ${error.params[0]}${error.params[1]}${error.params[2]}`;
|
||||||
|
case RSErrorType.invalidArgsArtity:
|
||||||
|
return `Неверное число аргументов терм-функции: ${error.params[0]} != ${error.params[1]}`;
|
||||||
|
case RSErrorType.invalidArgumentType:
|
||||||
|
return `Типизация аргумента терм-функции не соответствует объявленной: ${error.params[0]} != ${error.params[1]}`;
|
||||||
|
case RSErrorType.invalidEqualsEmpty:
|
||||||
|
return `Только множества можно сравнивать с пустым множеством: ${error.params[0]}`;
|
||||||
|
case RSErrorType.globalStructure:
|
||||||
|
return `Выражение родовой структуры должно быть ступенью`;
|
||||||
|
case RSErrorType.globalExpectedFunction:
|
||||||
|
return `Ожидалось выражение объявления функции`;
|
||||||
|
case RSErrorType.emptySetUsage:
|
||||||
|
return `Запрещено использование пустого множества как типизированного выражения`;
|
||||||
|
case RSErrorType.radicalUsage:
|
||||||
|
return `Радикалы запрещены вне деклараций терм-функци: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidFilterArgumentType:
|
||||||
|
return `Типизация аргумента фильтра не корректна: ${error.params[0]}(${error.params[1]})`;
|
||||||
|
case RSErrorType.invalidFilterArity:
|
||||||
|
return `Количество параметров фильтра не соответствует количеству индексов`;
|
||||||
|
case RSErrorType.arithmeticNotSupported:
|
||||||
|
return `Тип не поддерживает арифметические операторы: ${error.params[0]}`;
|
||||||
|
case RSErrorType.typesNotCompatible:
|
||||||
|
return `Типы не совместимы для выбранной операции: ${error.params[0]} и ${error.params[1]}`;
|
||||||
|
case RSErrorType.orderingNotSupported:
|
||||||
|
return `Тип не поддерживает предикаты порядка: ${error.params[0]}`;
|
||||||
|
case RSErrorType.globalNoValue:
|
||||||
|
return `Используется неинтерпретируемый глобальный идентификатор: ${error.params[0]}`;
|
||||||
|
case RSErrorType.invalidPropertyUsage:
|
||||||
|
return `Использование неитерируемого множества в качестве значения`;
|
||||||
|
case RSErrorType.globalMissingAST:
|
||||||
|
return `Не удалось получить дерево разбора для глобального идентификатора: ${error.params[0]}`;
|
||||||
|
case RSErrorType.globalFuncNoInterpretation:
|
||||||
|
return `Функция не интерпретируется для данных аргументов`;
|
||||||
|
}
|
||||||
|
return 'UNKNOWN ERROR';
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user