mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
F: Update pyconcept and implement improved typechecking
This commit is contained in:
parent
7f88d030d4
commit
35bfc86533
|
@ -107,6 +107,7 @@ class LibraryItem(Model):
|
||||||
verbose_name = 'Схема'
|
verbose_name = 'Схема'
|
||||||
verbose_name_plural = 'Схемы'
|
verbose_name_plural = 'Схемы'
|
||||||
|
|
||||||
|
# pylint: disable=invalid-str-returned
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f'{self.alias}'
|
return f'{self.alias}'
|
||||||
|
|
||||||
|
|
|
@ -400,7 +400,7 @@ class OperationSchema:
|
||||||
if child_schema.change_cst_type(successor_id, ctype):
|
if child_schema.change_cst_type(successor_id, ctype):
|
||||||
self._cascade_change_cst_type(child_id, successor_id, ctype)
|
self._cascade_change_cst_type(child_id, successor_id, ctype)
|
||||||
|
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments, too-many-positional-arguments
|
||||||
def _cascade_update_cst(
|
def _cascade_update_cst(
|
||||||
self,
|
self,
|
||||||
operation: int,
|
operation: int,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
from .basics import (
|
from .basics import (
|
||||||
ASTNodeSerializer,
|
ASTNodeSerializer,
|
||||||
|
ConstituentaCheckSerializer,
|
||||||
ExpressionParseSerializer,
|
ExpressionParseSerializer,
|
||||||
ExpressionSerializer,
|
ExpressionSerializer,
|
||||||
InheritanceDataSerializer,
|
InheritanceDataSerializer,
|
||||||
|
|
|
@ -10,6 +10,13 @@ class ExpressionSerializer(serializers.Serializer):
|
||||||
expression = serializers.CharField()
|
expression = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
|
class ConstituentaCheckSerializer(serializers.Serializer):
|
||||||
|
''' Serializer: RSLang expression. '''
|
||||||
|
alias = serializers.CharField()
|
||||||
|
definition_formal = serializers.CharField(allow_blank=True)
|
||||||
|
cst_type = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class WordFormSerializer(serializers.Serializer):
|
class WordFormSerializer(serializers.Serializer):
|
||||||
''' Serializer: inflect request. '''
|
''' Serializer: inflect request. '''
|
||||||
text = serializers.CharField()
|
text = serializers.CharField()
|
||||||
|
@ -85,6 +92,7 @@ class ASTNodeSerializer(serializers.Serializer):
|
||||||
class ExpressionParseSerializer(serializers.Serializer):
|
class ExpressionParseSerializer(serializers.Serializer):
|
||||||
''' Serializer: RSlang expression parse result. '''
|
''' Serializer: RSlang expression parse result. '''
|
||||||
parseResult = serializers.BooleanField()
|
parseResult = serializers.BooleanField()
|
||||||
|
prefixLen = serializers.IntegerField()
|
||||||
syntax = serializers.CharField()
|
syntax = serializers.CharField()
|
||||||
typification = serializers.CharField()
|
typification = serializers.CharField()
|
||||||
valueClass = serializers.CharField()
|
valueClass = serializers.CharField()
|
||||||
|
|
|
@ -113,8 +113,8 @@ class TestRSFormViewset(EndpointTester):
|
||||||
self.executeForbidden(item=self.private_id)
|
self.executeForbidden(item=self.private_id)
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/rsforms/{item}/check', method='post')
|
@decl_endpoint('/api/rsforms/{item}/check-expression', method='post')
|
||||||
def test_check(self):
|
def test_check_expression(self):
|
||||||
self.owned.insert_new('X1')
|
self.owned.insert_new('X1')
|
||||||
data = {'expression': 'X1=X1'}
|
data = {'expression': 'X1=X1'}
|
||||||
response = self.executeOK(data=data, item=self.owned_id)
|
response = self.executeOK(data=data, item=self.owned_id)
|
||||||
|
@ -127,6 +127,24 @@ class TestRSFormViewset(EndpointTester):
|
||||||
self.executeOK(data=data, item=self.unowned_id)
|
self.executeOK(data=data, item=self.unowned_id)
|
||||||
|
|
||||||
|
|
||||||
|
@decl_endpoint('/api/rsforms/{item}/check-constituenta', method='post')
|
||||||
|
def test_check_constituenta(self):
|
||||||
|
self.owned.insert_new('X1')
|
||||||
|
data = {'definition_formal': 'X1=X1', 'alias': 'A111', 'cst_type': CstType.AXIOM}
|
||||||
|
response = self.executeOK(data=data, item=self.owned_id)
|
||||||
|
self.assertEqual(response.data['parseResult'], True)
|
||||||
|
self.assertEqual(response.data['syntax'], 'math')
|
||||||
|
self.assertEqual(response.data['astText'], '[:==[A111][=[X1][X1]]]')
|
||||||
|
self.assertEqual(response.data['typification'], 'LOGIC')
|
||||||
|
self.assertEqual(response.data['valueClass'], 'value')
|
||||||
|
|
||||||
|
@decl_endpoint('/api/rsforms/{item}/check-constituenta', method='post')
|
||||||
|
def test_check_constituenta_error(self):
|
||||||
|
self.owned.insert_new('X1')
|
||||||
|
data = {'definition_formal': 'X1=X1', 'alias': 'D111', 'cst_type': CstType.TERM}
|
||||||
|
response = self.executeOK(data=data, item=self.owned_id)
|
||||||
|
self.assertEqual(response.data['parseResult'], False)
|
||||||
|
|
||||||
@decl_endpoint('/api/rsforms/{item}/resolve', method='post')
|
@decl_endpoint('/api/rsforms/{item}/resolve', method='post')
|
||||||
def test_resolve(self):
|
def test_resolve(self):
|
||||||
x1 = self.owned.insert_new(
|
x1 = self.owned.insert_new(
|
||||||
|
|
|
@ -56,7 +56,8 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
'details',
|
'details',
|
||||||
'export_trs',
|
'export_trs',
|
||||||
'resolve',
|
'resolve',
|
||||||
'check'
|
'check_expression',
|
||||||
|
'check_constituenta'
|
||||||
]:
|
]:
|
||||||
permission_list = [permissions.ItemAnyone]
|
permission_list = [permissions.ItemAnyone]
|
||||||
else:
|
else:
|
||||||
|
@ -424,8 +425,8 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
c.HTTP_404_NOT_FOUND: None
|
c.HTTP_404_NOT_FOUND: None
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@action(detail=True, methods=['post'], url_path='check')
|
@action(detail=True, methods=['post'], url_path='check-expression')
|
||||||
def check(self, request: Request, pk) -> HttpResponse:
|
def check_expression(self, request: Request, pk) -> HttpResponse:
|
||||||
''' Endpoint: Check RSLang expression against schema context. '''
|
''' Endpoint: Check RSLang expression against schema context. '''
|
||||||
serializer = s.ExpressionSerializer(data=request.data)
|
serializer = s.ExpressionSerializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
@ -437,6 +438,31 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
data=json.loads(result)
|
data=json.loads(result)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
summary='check expression for specific CstType',
|
||||||
|
tags=['RSForm', 'FormalLanguage'],
|
||||||
|
request=s.ConstituentaCheckSerializer,
|
||||||
|
responses={
|
||||||
|
c.HTTP_200_OK: s.ExpressionParseSerializer,
|
||||||
|
c.HTTP_404_NOT_FOUND: None
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@action(detail=True, methods=['post'], url_path='check-constituenta')
|
||||||
|
def check_constituenta(self, request: Request, pk) -> HttpResponse:
|
||||||
|
''' Endpoint: Check RSLang expression against schema context. '''
|
||||||
|
serializer = s.ConstituentaCheckSerializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
expression = serializer.validated_data['definition_formal']
|
||||||
|
alias = serializer.validated_data['alias']
|
||||||
|
cst_type = cast(m.CstType, serializer.validated_data['cst_type'])
|
||||||
|
|
||||||
|
pySchema = s.PyConceptAdapter(m.RSForm(self.get_object()))
|
||||||
|
result = pyconcept.check_constituenta(json.dumps(pySchema.data), alias, expression, cst_type)
|
||||||
|
return Response(
|
||||||
|
status=c.HTTP_200_OK,
|
||||||
|
data=json.loads(result)
|
||||||
|
)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
summary='resolve text with references',
|
summary='resolve text with references',
|
||||||
tags=['RSForm', 'NaturalLanguage'],
|
tags=['RSForm', 'NaturalLanguage'],
|
||||||
|
|
|
@ -8,13 +8,12 @@ drf-spectacular-sidecar==2024.7.1
|
||||||
coreapi==2.3.3
|
coreapi==2.3.3
|
||||||
django-rest-passwordreset==1.4.1
|
django-rest-passwordreset==1.4.1
|
||||||
cctext==0.1.4
|
cctext==0.1.4
|
||||||
pyconcept==0.1.6
|
pyconcept==0.1.8
|
||||||
|
|
||||||
psycopg2-binary==2.9.9
|
psycopg2-binary==2.9.9
|
||||||
gunicorn==23.0.0
|
gunicorn==23.0.0
|
||||||
|
|
||||||
djangorestframework-stubs==3.15.1
|
djangorestframework-stubs==3.15.1
|
||||||
django-extensions==3.2.3
|
django-extensions==3.2.3
|
||||||
mypy==1.11.2
|
mypy==1.11.2
|
||||||
pylint==3.2.7
|
pylint==3.3.0
|
||||||
coverage==7.6.1
|
coverage==7.6.1
|
|
@ -8,7 +8,7 @@ drf-spectacular-sidecar==2024.7.1
|
||||||
coreapi==2.3.3
|
coreapi==2.3.3
|
||||||
django-rest-passwordreset==1.4.1
|
django-rest-passwordreset==1.4.1
|
||||||
cctext==0.1.4
|
cctext==0.1.4
|
||||||
pyconcept==0.1.6
|
pyconcept==0.1.8
|
||||||
|
|
||||||
psycopg2-binary==2.9.9
|
psycopg2-binary==2.9.9
|
||||||
gunicorn==23.0.0
|
gunicorn==23.0.0
|
|
@ -5,6 +5,7 @@
|
||||||
import { ILibraryCreateData, ILibraryItem } from '@/models/library';
|
import { ILibraryCreateData, ILibraryItem } from '@/models/library';
|
||||||
import { ICstSubstituteData } from '@/models/oss';
|
import { ICstSubstituteData } from '@/models/oss';
|
||||||
import {
|
import {
|
||||||
|
ICheckConstituentaData,
|
||||||
IConstituentaList,
|
IConstituentaList,
|
||||||
IConstituentaMeta,
|
IConstituentaMeta,
|
||||||
ICstCreateData,
|
ICstCreateData,
|
||||||
|
@ -18,7 +19,7 @@ import {
|
||||||
IRSFormUploadData,
|
IRSFormUploadData,
|
||||||
ITargetCst
|
ITargetCst
|
||||||
} from '@/models/rsform';
|
} from '@/models/rsform';
|
||||||
import { IExpressionParse, IRSExpression } from '@/models/rslang';
|
import { IExpressionParse } from '@/models/rslang';
|
||||||
|
|
||||||
import { AxiosGet, AxiosPatch, AxiosPost, FrontExchange, FrontPull } from './apiTransport';
|
import { AxiosGet, AxiosPatch, AxiosPost, FrontExchange, FrontPull } from './apiTransport';
|
||||||
|
|
||||||
|
@ -113,9 +114,12 @@ export function patchMoveConstituenta(schema: string, request: FrontExchange<ICs
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function postCheckExpression(schema: string, request: FrontExchange<IRSExpression, IExpressionParse>) {
|
export function postCheckConstituenta(
|
||||||
|
schema: string,
|
||||||
|
request: FrontExchange<ICheckConstituentaData, IExpressionParse>
|
||||||
|
) {
|
||||||
AxiosPost({
|
AxiosPost({
|
||||||
endpoint: `/api/rsforms/${schema}/check`,
|
endpoint: `/api/rsforms/${schema}/check-constituenta`,
|
||||||
request: request
|
request: request
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
40
rsconcept/frontend/src/hooks/useCheckConstituenta.ts
Normal file
40
rsconcept/frontend/src/hooks/useCheckConstituenta.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { postCheckConstituenta } from '@/backend/rsforms';
|
||||||
|
import { type ErrorData } from '@/components/info/InfoError';
|
||||||
|
import { ICheckConstituentaData, IConstituenta, type IRSForm } from '@/models/rsform';
|
||||||
|
import { IExpressionParse } from '@/models/rslang';
|
||||||
|
|
||||||
|
function useCheckConstituenta({ schema }: { schema?: IRSForm }) {
|
||||||
|
const [processing, setProcessing] = useState(false);
|
||||||
|
const [error, setError] = useState<ErrorData>(undefined);
|
||||||
|
const [parseData, setParseData] = useState<IExpressionParse | undefined>(undefined);
|
||||||
|
|
||||||
|
const resetParse = useCallback(() => setParseData(undefined), []);
|
||||||
|
|
||||||
|
function checkConstituenta(expression: string, activeCst: IConstituenta, onSuccess?: DataCallback<IExpressionParse>) {
|
||||||
|
const data: ICheckConstituentaData = {
|
||||||
|
definition_formal: expression,
|
||||||
|
alias: activeCst.alias,
|
||||||
|
cst_type: activeCst.cst_type
|
||||||
|
};
|
||||||
|
setError(undefined);
|
||||||
|
postCheckConstituenta(String(schema!.id), {
|
||||||
|
data: data,
|
||||||
|
showError: true,
|
||||||
|
setLoading: setProcessing,
|
||||||
|
onError: setError,
|
||||||
|
onSuccess: parse => {
|
||||||
|
setParseData(parse);
|
||||||
|
if (onSuccess) onSuccess(parse);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { parseData, checkConstituenta, resetParse, error, setError, processing };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useCheckConstituenta;
|
|
@ -1,100 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useCallback, useState } from 'react';
|
|
||||||
|
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
|
||||||
import { postCheckExpression } from '@/backend/rsforms';
|
|
||||||
import { type ErrorData } from '@/components/info/InfoError';
|
|
||||||
import { CstType, IConstituenta, type IRSForm } from '@/models/rsform';
|
|
||||||
import { getDefinitionPrefix } from '@/models/rsformAPI';
|
|
||||||
import { IArgumentInfo, IExpressionParse } from '@/models/rslang';
|
|
||||||
import { RSErrorType } from '@/models/rslang';
|
|
||||||
import { PARAMETER } from '@/utils/constants';
|
|
||||||
|
|
||||||
function useCheckExpression({ schema }: { schema?: IRSForm }) {
|
|
||||||
const [processing, setProcessing] = useState(false);
|
|
||||||
const [error, setError] = useState<ErrorData>(undefined);
|
|
||||||
const [parseData, setParseData] = useState<IExpressionParse | undefined>(undefined);
|
|
||||||
|
|
||||||
const resetParse = useCallback(() => setParseData(undefined), []);
|
|
||||||
|
|
||||||
function checkExpression(expression: string, activeCst?: IConstituenta, onSuccess?: DataCallback<IExpressionParse>) {
|
|
||||||
setError(undefined);
|
|
||||||
postCheckExpression(String(schema!.id), {
|
|
||||||
data: { expression: expression },
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setError,
|
|
||||||
onSuccess: parse => {
|
|
||||||
if (activeCst) {
|
|
||||||
adjustResults(parse, expression.trim() === getDefinitionPrefix(activeCst), activeCst.cst_type);
|
|
||||||
}
|
|
||||||
setParseData(parse);
|
|
||||||
if (onSuccess) onSuccess(parse);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { parseData, checkExpression, resetParse, error, setError, processing };
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useCheckExpression;
|
|
||||||
|
|
||||||
// ===== Internals ========
|
|
||||||
function checkTypeConsistency(type: CstType, typification: string, args: IArgumentInfo[]): boolean {
|
|
||||||
switch (type) {
|
|
||||||
case CstType.BASE:
|
|
||||||
case CstType.CONSTANT:
|
|
||||||
case CstType.STRUCTURED:
|
|
||||||
case CstType.TERM:
|
|
||||||
return typification !== PARAMETER.logicLabel && args.length === 0;
|
|
||||||
|
|
||||||
case CstType.AXIOM:
|
|
||||||
case CstType.THEOREM:
|
|
||||||
return typification === PARAMETER.logicLabel && args.length === 0;
|
|
||||||
|
|
||||||
case CstType.FUNCTION:
|
|
||||||
return typification !== PARAMETER.logicLabel && args.length !== 0;
|
|
||||||
|
|
||||||
case CstType.PREDICATE:
|
|
||||||
return typification === PARAMETER.logicLabel && args.length !== 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function adjustResults(parse: IExpressionParse, emptyExpression: boolean, cstType: CstType) {
|
|
||||||
if (!parse.parseResult && parse.errors.length > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cstType === CstType.BASE || cstType === CstType.CONSTANT) {
|
|
||||||
if (!emptyExpression) {
|
|
||||||
parse.parseResult = false;
|
|
||||||
parse.errors.push({
|
|
||||||
errorType: RSErrorType.globalNonemptyBase,
|
|
||||||
isCritical: true,
|
|
||||||
params: [],
|
|
||||||
position: 0
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (emptyExpression) {
|
|
||||||
parse.parseResult = false;
|
|
||||||
parse.errors.push({
|
|
||||||
errorType: RSErrorType.globalEmptyDerived,
|
|
||||||
isCritical: true,
|
|
||||||
params: [],
|
|
||||||
position: 0
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!checkTypeConsistency(cstType, parse.typification, parse.args)) {
|
|
||||||
parse.parseResult = false;
|
|
||||||
parse.errors.push({
|
|
||||||
errorType: RSErrorType.globalUnexpectedType,
|
|
||||||
isCritical: true,
|
|
||||||
params: [],
|
|
||||||
position: 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -144,6 +144,11 @@ export interface IConstituentaList {
|
||||||
items: ConstituentaID[];
|
items: ConstituentaID[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IConstituenta} data, used for checking expression.
|
||||||
|
*/
|
||||||
|
export interface ICheckConstituentaData extends Pick<IConstituentaMeta, 'alias' | 'cst_type' | 'definition_formal'> {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents {@link IConstituenta} data, used in creation process.
|
* Represents {@link IConstituenta} data, used in creation process.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,13 +7,6 @@
|
||||||
*/
|
*/
|
||||||
export type AliasMapping = Record<string, string>;
|
export type AliasMapping = Record<string, string>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents formal expression.
|
|
||||||
*/
|
|
||||||
export interface IRSExpression {
|
|
||||||
expression: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents syntax type.
|
* Represents syntax type.
|
||||||
*/
|
*/
|
||||||
|
@ -91,6 +84,7 @@ export interface IArgumentValue extends IArgumentInfo {
|
||||||
*/
|
*/
|
||||||
export interface IExpressionParse {
|
export interface IExpressionParse {
|
||||||
parseResult: boolean;
|
parseResult: boolean;
|
||||||
|
prefixLen: number;
|
||||||
syntax: Syntax;
|
syntax: Syntax;
|
||||||
typification: string;
|
typification: string;
|
||||||
valueClass: ValueClass;
|
valueClass: ValueClass;
|
||||||
|
@ -248,16 +242,17 @@ export enum RSErrorType {
|
||||||
typesNotCompatible = 34853,
|
typesNotCompatible = 34853,
|
||||||
orderingNotSupported = 34854,
|
orderingNotSupported = 34854,
|
||||||
|
|
||||||
// !!!! Добавлены по сравнению с ConceptCore !!!!!
|
|
||||||
globalNonemptyBase = 34855,
|
|
||||||
globalUnexpectedType = 34856,
|
|
||||||
globalEmptyDerived = 34857,
|
|
||||||
|
|
||||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
globalNoValue = 34880,
|
globalNoValue = 34880,
|
||||||
invalidPropertyUsage = 34881,
|
invalidPropertyUsage = 34881,
|
||||||
globalMissingAST = 34882,
|
globalMissingAST = 34882,
|
||||||
globalFuncNoInterpretation = 34883
|
globalFuncNoInterpretation = 34883,
|
||||||
|
|
||||||
|
cstNonemptyBase = 34912,
|
||||||
|
cstEmptyDerived = 34913,
|
||||||
|
cstCallableNoArgs = 34914,
|
||||||
|
cstNonCallableHasArgs = 34915,
|
||||||
|
cstExpectedLogical = 34916,
|
||||||
|
cstExpectedTyped = 34917
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { RSTextWrapper } from '@/components/RSInput/textEditing';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import DlgShowAST from '@/dialogs/DlgShowAST';
|
import DlgShowAST from '@/dialogs/DlgShowAST';
|
||||||
import useCheckExpression from '@/hooks/useCheckExpression';
|
import useCheckConstituenta from '@/hooks/useCheckConstituenta';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { ConstituentaID, IConstituenta } from '@/models/rsform';
|
import { ConstituentaID, IConstituenta } from '@/models/rsform';
|
||||||
|
@ -54,7 +54,7 @@ function EditorRSExpression({
|
||||||
const model = useRSForm();
|
const model = useRSForm();
|
||||||
|
|
||||||
const [isModified, setIsModified] = useState(false);
|
const [isModified, setIsModified] = useState(false);
|
||||||
const parser = useCheckExpression({ schema: model.schema });
|
const parser = useCheckConstituenta({ schema: model.schema });
|
||||||
const { resetParse } = parser;
|
const { resetParse } = parser;
|
||||||
const rsInput = useRef<ReactCodeMirrorRef>(null);
|
const rsInput = useRef<ReactCodeMirrorRef>(null);
|
||||||
|
|
||||||
|
@ -74,11 +74,9 @@ function EditorRSExpression({
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCheckExpression(callback?: (parse: IExpressionParse) => void) {
|
function handleCheckExpression(callback?: (parse: IExpressionParse) => void) {
|
||||||
const prefix = getDefinitionPrefix(activeCst);
|
parser.checkConstituenta(value, activeCst, parse => {
|
||||||
const expression = prefix + value;
|
|
||||||
parser.checkExpression(expression, activeCst, parse => {
|
|
||||||
if (parse.errors.length > 0) {
|
if (parse.errors.length > 0) {
|
||||||
onShowError(parse.errors[0]);
|
onShowError(parse.errors[0], parse.prefixLen);
|
||||||
} else {
|
} else {
|
||||||
rsInput.current?.view?.focus();
|
rsInput.current?.view?.focus();
|
||||||
}
|
}
|
||||||
|
@ -95,12 +93,11 @@ function EditorRSExpression({
|
||||||
}
|
}
|
||||||
|
|
||||||
const onShowError = useCallback(
|
const onShowError = useCallback(
|
||||||
(error: IRSErrorDescription) => {
|
(error: IRSErrorDescription, prefixLen: number) => {
|
||||||
if (!rsInput.current) {
|
if (!rsInput.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const prefix = getDefinitionPrefix(activeCst);
|
let errorPosition = error.position - prefixLen;
|
||||||
let errorPosition = error.position - prefix.length;
|
|
||||||
if (errorPosition < 0) errorPosition = 0;
|
if (errorPosition < 0) errorPosition = 0;
|
||||||
rsInput.current?.view?.dispatch({
|
rsInput.current?.view?.dispatch({
|
||||||
selection: {
|
selection: {
|
||||||
|
@ -133,6 +130,7 @@ function EditorRSExpression({
|
||||||
toast.error(errors.astFailed);
|
toast.error(errors.astFailed);
|
||||||
} else {
|
} else {
|
||||||
setSyntaxTree(parse.ast);
|
setSyntaxTree(parse.ast);
|
||||||
|
// TODO: return prefix from parser API instead of prefixLength
|
||||||
setExpression(getDefinitionPrefix(activeCst) + value);
|
setExpression(getDefinitionPrefix(activeCst) + value);
|
||||||
setShowAST(true);
|
setShowAST(true);
|
||||||
}
|
}
|
||||||
|
@ -199,7 +197,7 @@ function EditorRSExpression({
|
||||||
isOpen={!!parser.parseData && parser.parseData.errors.length > 0}
|
isOpen={!!parser.parseData && parser.parseData.errors.length > 0}
|
||||||
data={parser.parseData}
|
data={parser.parseData}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onShowError={onShowError}
|
onShowError={error => onShowError(error, parser.parseData?.prefixLen ?? 0)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -34,7 +34,7 @@ export const PARAMETER = {
|
||||||
statSmallThreshold: 3, // characters - threshold for small labels - small font
|
statSmallThreshold: 3, // characters - threshold for small labels - small font
|
||||||
|
|
||||||
logicLabel: 'LOGIC',
|
logicLabel: 'LOGIC',
|
||||||
exteorVersion: '4.9.4',
|
exteorVersion: '4.9.5',
|
||||||
|
|
||||||
TOOLTIP_WIDTH: 'max-w-[29rem]'
|
TOOLTIP_WIDTH: 'max-w-[29rem]'
|
||||||
};
|
};
|
||||||
|
|
|
@ -787,13 +787,20 @@ export function describeRSError(error: IRSErrorDescription): string {
|
||||||
case RSErrorType.globalMissingAST:
|
case RSErrorType.globalMissingAST:
|
||||||
return `Не удалось получить дерево разбора для глобального идентификатора: ${error.params[0]}`;
|
return `Не удалось получить дерево разбора для глобального идентификатора: ${error.params[0]}`;
|
||||||
case RSErrorType.globalFuncNoInterpretation:
|
case RSErrorType.globalFuncNoInterpretation:
|
||||||
return `Функция не интерпретируется для данных аргументов`;
|
return 'Функция не интерпретируется для данных аргументов';
|
||||||
case RSErrorType.globalNonemptyBase:
|
|
||||||
return `Непустое выражение базисного/константного множества`;
|
case RSErrorType.cstNonemptyBase:
|
||||||
case RSErrorType.globalUnexpectedType:
|
return 'Непустое выражение базисного/константного множества';
|
||||||
return `Типизация выражения не соответствует типу конституенты`;
|
case RSErrorType.cstEmptyDerived:
|
||||||
case RSErrorType.globalEmptyDerived:
|
return 'Пустое выражение для сложного понятия или утверждения';
|
||||||
return `Пустое выражение для выводимого понятия или утверждения`;
|
case RSErrorType.cstCallableNoArgs:
|
||||||
|
return 'Отсутствуют аргументы для параметризованной конституенты';
|
||||||
|
case RSErrorType.cstNonCallableHasArgs:
|
||||||
|
return 'Параметризованное выражение не подходит для данного типа конституенты';
|
||||||
|
case RSErrorType.cstExpectedLogical:
|
||||||
|
return 'Данный тип конституенты требует логического выражения';
|
||||||
|
case RSErrorType.cstExpectedTyped:
|
||||||
|
return 'Данный тип конституенты требует теоретико-множественного выражения';
|
||||||
}
|
}
|
||||||
return 'UNKNOWN ERROR';
|
return 'UNKNOWN ERROR';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user