Implement RSExpr check UI

This commit is contained in:
IRBorisov 2023-07-29 03:31:21 +03:00
parent 35ef677c60
commit 4f45b9978e
20 changed files with 336 additions and 162 deletions

View File

@ -2,8 +2,8 @@ import { ThreeDots } from 'react-loader-spinner';
export function Loader() {
return (
<div className='flex justify-center'>
<ThreeDots color='rgb(96 165 250)' height='100' width='100' radius='10' />
<div className='flex justify-center w-full h-full'>
<ThreeDots color='rgb(96 165 250)' height='100' width='100' radius='10' />
</div>
);
}

View File

@ -7,23 +7,18 @@ import Button from './Button';
interface ModalProps {
title?: string
submitText?: string
show: boolean
canSubmit: boolean
canSubmit?: boolean
hideWindow: () => void
onSubmit: () => void
onCancel?: () => void
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);
useClickedOutside({ ref, callback: hideWindow });
useEscapeKey(hideWindow);
if (!show) {
return null;
}
const handleCancel = () => {
hideWindow();
if (onCancel) onCancel();

View File

@ -2,18 +2,17 @@ import { useCallback, useState } from 'react'
import { type ErrorInfo } from '../components/BackendError';
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 }) {
const [loading, setLoading] = useState(false);
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); }, []);
function checkExpression(expression: string, onSuccess?: DataCallback<ExpressionParse>) {
function checkExpression(expression: string, onSuccess?: DataCallback<IExpressionParse>) {
setError(undefined);
setParseData(undefined);
postCheckExpression(String(schema?.id), {
data: { expression: expression },
showError: true,

View File

@ -77,7 +77,7 @@
}
.text-red {
@apply text-red-400 dark:text-red-600
@apply text-red-600 dark:text-red-400
}
.text-green {

View File

@ -11,11 +11,10 @@ import { IRSFormCreateData } from '../../utils/models';
import { getCloneTitle } from '../../utils/staticUI';
interface DlgCloneRSFormProps {
show: boolean
hideWindow: () => void
}
function DlgCloneRSForm({ show, hideWindow }: DlgCloneRSFormProps) {
function DlgCloneRSForm({ hideWindow }: DlgCloneRSFormProps) {
const navigate = useNavigate();
const [title, setTitle] = useState('');
const [alias, setAlias] = useState('');
@ -34,7 +33,6 @@ function DlgCloneRSForm({ show, hideWindow }: DlgCloneRSFormProps) {
}, [schema, schema?.title, schema?.alias, schema?.comment, schema?.is_common]);
const handleSubmit = () => {
hideWindow();
const data: IRSFormCreateData = {
title: title,
alias: alias,
@ -50,7 +48,6 @@ function DlgCloneRSForm({ show, hideWindow }: DlgCloneRSFormProps) {
return (
<Modal
title='Создание копии концептуальной схемы'
show={show}
hideWindow={hideWindow}
canSubmit={true}
submitText='Создать'
@ -76,7 +73,7 @@ function DlgCloneRSForm({ show, hideWindow }: DlgCloneRSFormProps) {
onChange={event => { setCommon(event.target.checked); }}
/>
</Modal>
)
);
}
export default DlgCloneRSForm;

View File

@ -6,13 +6,12 @@ import { type CstType } from '../../utils/models';
import { CstTypeSelector, getCstTypeLabel } from '../../utils/staticUI';
interface DlgCreateCstProps {
show: boolean
hideWindow: () => void
defaultType?: CstType
onCreate: (type: CstType) => void
}
function DlgCreateCst({ show, hideWindow, defaultType, onCreate }: DlgCreateCstProps) {
function DlgCreateCst({ hideWindow, defaultType, onCreate }: DlgCreateCstProps) {
const [validated, setValidated] = useState(false);
const [selectedType, setSelectedType] = useState<CstType | undefined>(undefined);
@ -32,7 +31,6 @@ function DlgCreateCst({ show, hideWindow, defaultType, onCreate }: DlgCreateCstP
return (
<Modal
title='Создание конституенты'
show={show}
hideWindow={hideWindow}
canSubmit={validated}
onSubmit={handleSubmit}
@ -45,7 +43,7 @@ function DlgCreateCst({ show, hideWindow, defaultType, onCreate }: DlgCreateCstP
onChange={(data) => { setSelectedType(data?.value); }}
/>
</Modal>
)
);
}
export default DlgCreateCst;

View 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;

View File

@ -8,17 +8,15 @@ import { useRSForm } from '../../context/RSFormContext';
import { IRSFormUploadData } from '../../utils/models';
interface DlgUploadRSFormProps {
show: boolean
hideWindow: () => void
}
function DlgUploadRSForm({ show, hideWindow }: DlgUploadRSFormProps) {
function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
const { upload } = useRSForm();
const [loadMetadata, setLoadMetadata] = useState(false);
const [file, setFile] = useState<File | undefined>()
const handleSubmit = () => {
hideWindow();
if (!file) {
return;
}
@ -41,7 +39,6 @@ function DlgUploadRSForm({ show, hideWindow }: DlgUploadRSFormProps) {
return (
<Modal
title='Загрузка схемы из Экстеор'
show={show}
hideWindow={hideWindow}
canSubmit={!!file}
onSubmit={handleSubmit}
@ -57,7 +54,7 @@ function DlgUploadRSForm({ show, hideWindow }: DlgUploadRSFormProps) {
/>
</div>
</Modal>
)
);
}
export default DlgUploadRSForm;

View File

@ -7,14 +7,18 @@ import SubmitButton from '../../components/Common/SubmitButton';
import TextArea from '../../components/Common/TextArea';
import { DumpBinIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
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 DlgCreateCst from './DlgCreateCst';
import EditorRSExpression from './EditorRSExpression';
import ViewSideConstituents from './elements/ViewSideConstituents';
import { RSTabsList } from './RSTabs';
function EditorConstituenta() {
interface EditorConstituentaProps {
onShowAST: (ast: SyntaxTree) => void
}
function EditorConstituenta({onShowAST}: EditorConstituentaProps) {
const navigate = useNavigate();
const {
activeCst, activeID, schema, setActiveID, processing, isEditable,
@ -132,12 +136,11 @@ function EditorConstituenta() {
return (
<div className='flex items-start w-full gap-2'>
<DlgCreateCst
show={showCstModal}
{showCstModal && <DlgCreateCst
hideWindow={() => { setShowCstModal(false); }}
onCreate={handleAddNew}
defaultType={activeCst?.cstType as CstType}
/>
/>}
<form onSubmit={handleSubmit} className='flex-grow min-w-[50rem] max-w-min px-4 py-2 border'>
<div className='flex items-start justify-between'>
<button type='submit'
@ -204,6 +207,7 @@ function EditorConstituenta() {
disabled={!isEnabled}
isActive={editMode === EditMode.RSLANG}
toggleEditMode={() => { setEditMode(EditMode.RSLANG); }}
onShowAST={onShowAST}
onChange={event => { setExpression(event.target.value); }}
setValue={setExpression}
setTypification={setTypification}

View File

@ -284,11 +284,10 @@ function EditorItems({ onOpenEdit }: EditorItemsProps) {
);
return (<>
<DlgCreateCst
show={showCstModal}
{showCstModal && <DlgCreateCst
hideWindow={() => { setShowCstModal(false); }}
onCreate={handleAddNew}
/>
/>}
<div className='w-full'>
<div
className={'flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem] clr-app' +

View File

@ -7,7 +7,7 @@ import { Loader } from '../../components/Common/Loader';
import { useRSForm } from '../../context/RSFormContext';
import useCheckExpression from '../../hooks/useCheckExpression';
import { TokenID } from '../../utils/enums';
import { CstType } from '../../utils/models';
import { CstType, SyntaxTree } from '../../utils/models';
import ParsingResult from './elements/ParsingResult';
import RSLocalButton from './elements/RSLocalButton';
import RSTokenButton from './elements/RSTokenButton';
@ -22,13 +22,14 @@ interface EditorRSExpressionProps {
placeholder?: string
value: string
onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void
onShowAST: (ast: SyntaxTree) => void
toggleEditMode: () => void
setTypification: (typificaiton: string) => void
setValue: (expression: string) => void
}
function EditorRSExpression({
id, label, disabled, isActive, placeholder, value, setValue,
id, label, disabled, isActive, placeholder, value, setValue, onShowAST,
toggleEditMode, setTypification, onChange
}: EditorRSExpressionProps) {
const { schema, activeCst } = useRSForm();
@ -48,11 +49,11 @@ function EditorRSExpression({
const prefix = activeCst?.alias + (activeCst?.cstType === CstType.STRUCTURED ? '::=' : ':==');
const expression = prefix + value;
checkExpression(expression, parse => {
// TODO: update cursor position
if (!parse.parseResult && parse.errors.length > 0) {
const errorPosition = parse.errors[0].position - prefix.length
expressionCtrl.current!.selectionStart = errorPosition;
expressionCtrl.current!.selectionEnd = errorPosition;
expressionCtrl.current!.focus();
}
setIsModified(false);
setTypification(parse.typification);
@ -85,25 +86,26 @@ function EditorRSExpression({
if (!expressionCtrl.current) {
return;
}
if (event.altKey) {
const text = new TextWrapper(expressionCtrl.current);
if (text.processAltKey(event.key)) {
event.preventDefault();
text.finalize();
setValue(text.value);
setIsModified(true);
const text = new TextWrapper(expressionCtrl.current);
if (event.shiftKey && event.key === '*' && !event.altKey) {
text.insertToken(TokenID.DECART);
} else if (event.altKey) {
if (!text.processAltKey(event.key)) {
return;
}
} else if (!event.ctrlKey) {
const newSymbol = getSymbolSubstitute(event.key);
if (newSymbol) {
event.preventDefault();
const text = new TextWrapper(expressionCtrl.current);
text.replaceWith(newSymbol);
text.finalize();
setValue(text.value);
setIsModified(true);
if (!newSymbol) {
return;
}
text.replaceWith(newSymbol);
} else {
return;
}
event.preventDefault();
text.finalize();
setValue(text.value);
setIsModified(true);
}, [expressionCtrl, setValue]);
const handleFocusIn = useCallback(() => {
@ -225,8 +227,11 @@ function EditorRSExpression({
parseData={parseData}
/>}
</div>
{ loading && <Loader />}
{ parseData && <ParsingResult data={parseData} />}
{ (loading || 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>
);
}

View File

@ -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 BackendError from '../../components/BackendError';
@ -6,8 +6,9 @@ import ConceptTab from '../../components/Common/ConceptTab';
import { Loader } from '../../components/Common/Loader';
import { useRSForm } from '../../context/RSFormContext';
import useLocalStorage from '../../hooks/useLocalStorage';
import { type IConstituenta } from '../../utils/models';
import { type IConstituenta,SyntaxTree } from '../../utils/models';
import DlgCloneRSForm from './DlgCloneRSForm';
import DlgShowAST from './DlgShowAST';
import DlgUploadRSForm from './DlgUploadRSForm';
import EditorConstituenta from './EditorConstituenta';
import EditorItems from './EditorItems';
@ -26,8 +27,16 @@ function RSTabs() {
const [tabIndex, setTabIndex] = useLocalStorage('rsform_edit_tab', RSTabsList.CARD);
const [init, setInit] = useState(false);
const [showUploadDialog, setShowUploadDialog] = useState(false);
const [showCloneDialog, setShowCloneDialog] = useState(false);
const [showUpload, setShowUpload] = 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) => {
setActiveID(cst.id);
@ -90,14 +99,9 @@ function RSTabs() {
{ error && <BackendError error={error} />}
{ schema && !loading &&
<>
<DlgUploadRSForm
show={showUploadDialog}
hideWindow={() => { setShowUploadDialog(false); }}
/>
<DlgCloneRSForm
show={showCloneDialog}
hideWindow={() => { setShowCloneDialog(false); }}
/>
{showUpload && <DlgUploadRSForm hideWindow={() => { setShowUpload(false); }}/>}
{showClone && <DlgCloneRSForm hideWindow={() => { setShowClone(false); }}/>}
{showAST && <DlgShowAST syntaxTree={syntaxTree} hideWindow={() => { setShowAST(false); }}/>}
<Tabs
selectedIndex={tabIndex}
onSelect={onSelectTab}
@ -106,8 +110,8 @@ function RSTabs() {
>
<TabList className='flex items-start w-fit clr-bg-pop'>
<RSTabsMenu
showCloneDialog={() => setShowCloneDialog(true)}
showUploadDialog={() => setShowUploadDialog(true)}
showCloneDialog={() => setShowClone(true)}
showUploadDialog={() => setShowUpload(true)}
/>
<ConceptTab>Паспорт схемы</ConceptTab>
<ConceptTab className='border-x-2 clr-border min-w-[10rem] flex justify-between gap-2'>
@ -127,7 +131,7 @@ function RSTabs() {
</TabPanel>
<TabPanel>
<EditorConstituenta />
<EditorConstituenta onShowAST={onShowAST} />
</TabPanel>
</Tabs></>
}

View File

@ -1,20 +1,41 @@
import PrettyJson from '../../../components/Common/PrettyJSON';
import { ExpressionParse } from '../../../utils/models';
import { IExpressionParse, SyntaxTree } from '../../../utils/models';
import { getRSErrorMessage, getRSErrorPrefix } from '../../../utils/staticUI';
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 (
<div className='w-full px-3 py-2 mt-2 border'>
<PrettyJson data={data} />
{/* <textarea
className={'leading-tight border shadow dark:bg-gray-800 '}
rows={3}
placeholder='Результаты анализа выражения'
value={data}
/> */}
<div className='px-3 py-2'>
<p>Ошибок: <b>{errorCount}</b> | Предупреждений: <b>{warningsCount}</b></p>
{data.errors.map(error => {
return (
<p className='text-red'>
<span className='font-semibold'>{error.isCritical ? 'Ошибка' : 'Предупреждение'} {getRSErrorPrefix(error)}: </span>
<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>
)
}

View File

@ -1,11 +1,11 @@
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';
interface StatusBarProps {
isModified?: boolean
parseData?: ExpressionParse
parseData?: IExpressionParse
constituenta?: IConstituenta
}

View File

@ -75,7 +75,7 @@ function ViewSideConstituents({ expression }: ViewSideConstituentsProps) {
name: 'Описание',
id: 'description',
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',
wrap: true,
conditionalCellStyles: [

View File

@ -171,6 +171,7 @@ export class TextWrapper implements IManagedText {
case 'q': return this.insertToken(TokenID.BIGPR);
case 'w': return this.insertToken(TokenID.SMALLPR);
case 'e': return this.insertToken(TokenID.BOOLEAN);
case 'E': return this.insertToken(TokenID.DECART);
case 'r': return this.insertToken(TokenID.REDUCE);
case 't': return this.insertToken(TokenID.NT_RECURSIVE_FULL);
case 'a': return this.insertToken(TokenID.INTERSECTION);

View File

@ -5,14 +5,14 @@ import { type ErrorInfo } from '../components/BackendError'
import { FilterType, RSFormsFilter } from '../hooks/useRSForms'
import { config } from './constants'
import {
ExpressionParse,
IConstituentaList,
IConstituentaMeta,
ICstCreateData, ICstCreatedResponse, ICstMovetoData, ICstUpdateData,
ICurrentUser, IRSFormCreateData, IRSFormData,
ICurrentUser, IExpressionParse,
IRSExpression,
IRSFormCreateData, IRSFormData,
IRSFormMeta, IRSFormUpdateData, IRSFormUploadData, IUserInfo,
IUserLoginData, IUserProfile, IUserSignupData, RSExpression
} from './models'
IUserLoginData, IUserProfile, IUserSignupData} from './models'
// ================ Data transfer types ================
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({
title: `Check expression for RSForm id=${schema}: ${request.data.expression }`,
endpoint: `${config.url.BASE}rsforms/${schema}/check/`,

View File

@ -103,58 +103,72 @@ export enum TokenID {
END,
}
export enum RSError {
export enum RSErrorClass {
LEXER,
PARSER,
SEMANTIC,
UNKNOWN
}
// '8201': 'Число превышает максимально допустимое значение 2147483647!',
// '8203': 'Нераспознанный символ!',
// '8400': 'Неопределенная синтаксическая ошибка!',
// '8406': 'Пропущена скобка )!',
// '8407': 'Пропущена скобка }!',
// '8408': 'Некорректная кванторная декларация переменной!',
// '8414': 'Некорректное объявление аргументов функции!',
// '8415': 'Некорректное имя локальной переменной в декларации функции!',
// '2801': 'Повторное объявление локальной переменной!',
// '2802': 'Локальная переменная объявлена, но не использована!',
// '8801': 'Использование необъявленной локальной переменной!',
// '8802': 'Повторное объявление локальной переменной внутри области действия!',
// '8803': 'Типизация операндов не совпадает!',
// '8804': 'Использована конституента с неопределенной типизацией!',
// '8805': 'Одна из проекций декартова произведения не является типизированным множеством имеющим характер множества!',
// '8806': 'Аргумент булеана не является типизированным множеством имеющим характер множества!',
// '8807': 'Операнд теоретико-множественного оператора не является типизированным множеством имеющим характер множества!',
// '8808': 'Операнд оператора card не является типизированным множеством имеющим характер множества!',
// '8809': 'Операнд оператора debool не является типизированным множеством имеющим характер множества!',
// '880A': 'Неизвестное имя функции!',
// '880B': 'Некорректное использование имени функции без аргументов!',
// '8810': 'Операнд оператора red не является типизированным множеством имеющим характер двойного булеана!',
// '8811': 'Некорректная типизация аргумента: проекция не определена!',
// '8812': 'Некорректная типизация аргумента: T(Pri(a)) = B(Pi(D(T(a))))!',
// '8813': 'Типизация элементов перечисления не совпадает!',
// '8814': 'Некорректная декларация связанных локальных переменных: количестве переменных в кортеже не соответствует размерности декартова произведения типизации!',
// '8815': 'Локальная переменная используется вне области действия!',
// '8816': 'Несоответствие типизаций операндов для предиката!',
// '8818': 'Некорректное количество аргументов терм-функции!',
// '8819': 'Типизация аргумента терм-функции не совпадает с объявленной!',
// '881A': 'Сравнение кортежа или элемента с пустым множеством!',
// '881C': 'Выражение родовой структуры должно быть ступенью!',
// '881F': 'Ожидалось выражение объявления функции!',
// '8820': 'Некорректное использование пустого множества как типизированного выражения!',
// '8821': 'Радикалы запрещены вне деклараций терм-функций!',
// '8822': 'Типизация аргумента фильтра не корректна!',
// '8823': 'Количество параметров фильтра не соответствует количеству индексов!',
// '8824': 'Для выбранного типа не поддерживаются арифметические операции!',
// '8825': 'Типизации не совместимы для выбранной операции/предиката!',
// '8826': 'Для выбранного типа не поддерживаются предикаты порядка!',
// '8840': 'Используется неинтерпретируемый глобальный идентификатор!',
// '8841': 'Использование свойства в качестве значения!',
// '8842': 'Не удалось получить дерево разбора для глобального идентификатора!',
// '8843': 'Функция не интерпретируется для данных аргументов!',
// '8A00': 'Неизвестная ошибка: вычисление прервано!',
// '8A01': 'Превышен пределен количества элементов множества!',
// '8A02': 'Превышен пределен количества элементов в основании булеана!',
// '8A03': 'Использование конституенты с неопределенным значением!',
// '8A04': 'Превышен предел количества итераций!',
// '8A05': 'Попытка взять debool от многоэлементного множества!',
// '8A06': 'Попытка перебрать бесконечное множество!'
export enum RSErrorType {
syntax = 0x8400, // Неизвестная синтаксическая ошибка
missingParanthesis = 0x8406, // Пропущена скобка ')'
missingCurlyBrace = 0x8407, // Пропущена скобка '}'
invalidQuantifier = 0x8408, // Некорректная кванторная декларация
expectedArgDeclaration = 0x8414, // Ожидалось объявление аргументов
expectedLocal = 0x8415, // Ожидалось имя локальной переменной
localDoubleDeclare = 0x2801, // Повторное использование одного и того же имени переменной
localNotUsed = 0x2802, // Переменная объявлена но не использована
localUndeclared = 0x8801, // Использование необъявленной переменной
localShadowing = 0x8802, // Повторное объявление переменной
typesNotEqual = 0x8803, // Некорректное использование операций
globalNotTyped = 0x8804, // Не определена типизация глобальной конституенты
invalidDecart = 0x8805, // Одна из проекций не является множеством
invalidBoolean = 0x8806, // Попытка взять булеан от элемента, не имеющего характер множества
invalidTypeOperation = 0x8807, // Применение ТМО к операндам, не имеющим характер множества
invalidCard = 0x8808, // Мощность множества не определена для элемента
invalidDebool = 0x8809, // Дебулеан берется от немножества
globalFuncMissing = 0x880A, // Неизвестное имя функции
globalFuncWithoutArgs = 0x880B, // Некорректное использование имени функции без аргументов
invalidReduce = 0x8810, // Red можно брать только от двойного булеана
invalidProjectionTuple = 0x8811, // Не определена проекция
invalidProjectionSet = 0x8812, // Большая проекция определена только для множеств!
invalidEnumeration = 0x8813, // Типизация аргументов перечисления не совпадает
ivalidBinding = 0x8814, // Количество переменных в кортеже не соответствует размерности декартова произведения
localOutOfScope = 0x8815, // Использование имени вне области видимости
invalidElementPredicat = 0x8816, // Несоответствие типов для проверки принадлежности
invalidArgsArtity = 0x8818, // Некорректное количество аргументов терм-функции
invalidArgumentType = 0x8819, // Типизация аргумента не совпадает с объявленной
invalidEqualsEmpty = 0x881A, // Сравнение с пустым множеством не множества
globalStructure = 0x881C, // Родовая структура должна быть ступенью
globalExpectedFunction = 0x881F, // Ожидалось выражение объявления функции
emptySetUsage = 0x8820, // Некорректное использование пустого множества как типизированного выражения
radicalUsage = 0x8821, // Радикалы запрещены вне деклараций терм-функций
invalidFilterArgumentType = 0x8822, // Типизация аргумента фильтра не корректна
invalidFilterArity = 0x8823, // Количество параметров фильра не соответствует количеству индексов
arithmeticNotSupported = 0x8824, // Для данного типа не поддерживается арифметика
typesNotCompatible = 0x8825, // Типы не совместимы в данном контексте
orderingNotSupported = 0x8826, // Для данного типа не поддерживается порядок элементов
globalNoValue = 0x8840, // Используется неинтерпретируемый глобальный идентификатор
invalidPropertyUsage = 0x8841, // Использование свойства в качестве значения
globalMissingAST = 0x8842, // Не удалось получить дерево разбора для глобального идентификатора
globalFuncNoInterpretation = 0x8843, // Функция не интерпретируется для данных аргументов
}
const ERRCODE_LEXER_MASK = 0x0200;
const ERRCODE_PARSER_MASK = 0x0400;
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;
}
}

View File

@ -1,3 +1,5 @@
import { RSErrorType, TokenID } from './enums'
// ========= Users ===========
export interface IUser {
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 IUserInfo extends Omit<IUserProfile, 'email'> {}
// ======== Parsing ============
// ValueClass
export enum ValueClass {
INVALID = 'invalid',
VALUE = 'value',
PROPERTY = 'property'
}
// Syntax
// ======== RS Parsing ============
export enum Syntax {
UNDEF = 'undefined',
ASCII = 'ascii',
MATH = 'math'
}
// ParsingStatus
export enum ValueClass {
INVALID = 'invalid',
VALUE = 'value',
PROPERTY = 'property'
}
export enum ParsingStatus {
UNDEF = 'undefined',
VERIFIED = 'verified',
INCORRECT = 'incorrect'
}
export interface RSErrorDescription {
errorType: number
export interface IRSErrorDescription {
errorType: RSErrorType
position: number
isCritical: boolean
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
syntax: Syntax
typification: string
valueClass: ValueClass
errors: IRSErrorDescription[]
astText: string
errors: RSErrorDescription[]
ast: SyntaxTree
}
export interface RSExpression {
export interface IRSExpression {
expression: string
}

View File

@ -1,5 +1,5 @@
import { TokenID } from './enums';
import { CstType, ExpressionStatus, type IConstituenta, type IRSForm, ParsingStatus, ValueClass } from './models';
import { resolveErrorClass,RSErrorClass, RSErrorType, TokenID } from './enums';
import { CstType, ExpressionStatus, type IConstituenta, IRSErrorDescription,type IRSForm, ParsingStatus, ValueClass } from './models';
export interface IRSButtonData {
text: string
@ -30,7 +30,7 @@ export function getRSButtonData(id: TokenID): IRSButtonData {
};
case TokenID.DECART: return {
text: '×',
tooltip: 'Декартово произведение [Shift + 8]'
tooltip: 'Декартово произведение [Shift + 8 / Alt + Shift + E]'
};
case TokenID.PUNC_PL: return {
text: '( )',
@ -321,4 +321,104 @@ export function getCloneTitle(schema: IRSForm): string {
} else {
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';
}