Refactor models: split API in separate module

This commit is contained in:
IRBorisov 2023-11-06 20:21:30 +03:00
parent 643f43c819
commit 97bec834f0
13 changed files with 305 additions and 220 deletions

View File

@ -1,4 +1,5 @@
import { IConstituenta, isMockCst } from '../../models/rsform'; import { IConstituenta } from '../../models/rsform';
import { isMockCst } from '../../models/rsformAPI';
import { colorfgCstStatus,IColorTheme } from '../../utils/color'; import { colorfgCstStatus,IColorTheme } from '../../utils/color';
import { describeExpressionStatus } from '../../utils/labels'; import { describeExpressionStatus } from '../../utils/labels';
import ConceptTooltip from '../Common/ConceptTooltip'; import ConceptTooltip from '../Common/ConceptTooltip';

View File

@ -2,7 +2,8 @@ import { useEffect, useMemo, useState } from 'react';
import { useConceptTheme } from '../../context/ThemeContext'; import { useConceptTheme } from '../../context/ThemeContext';
import { CstMatchMode } from '../../models/miscelanious'; import { CstMatchMode } from '../../models/miscelanious';
import { IConstituenta, matchConstituenta } from '../../models/rsform'; import { IConstituenta } from '../../models/rsform';
import { matchConstituenta } from '../../models/rsformAPI';
import { prefixes } from '../../utils/constants'; import { prefixes } from '../../utils/constants';
import { describeConstituenta } from '../../utils/labels'; import { describeConstituenta } from '../../utils/labels';
import ConceptSearch from '../Common/ConceptSearch'; import ConceptSearch from '../Common/ConceptSearch';

View File

@ -4,7 +4,8 @@ import { ErrorInfo } from '../components/BackendError';
import { matchLibraryItem } from '../models/library'; import { matchLibraryItem } from '../models/library';
import { ILibraryItem } from '../models/library'; import { ILibraryItem } from '../models/library';
import { ILibraryFilter } from '../models/miscelanious'; import { ILibraryFilter } from '../models/miscelanious';
import { IRSForm, IRSFormCreateData, IRSFormData, loadRSFormData } from '../models/rsform'; import { IRSForm, IRSFormCreateData, IRSFormData } from '../models/rsform';
import { loadRSFormData } from '../models/rsformAPI';
import { DataCallback, deleteLibraryItem, getLibrary, getRSFormDetails, getTemplates, postCloneLibraryItem, postNewRSForm } from '../utils/backendAPI'; import { DataCallback, deleteLibraryItem, getLibrary, getRSFormDetails, getTemplates, postCloneLibraryItem, postNewRSForm } from '../utils/backendAPI';
import { useAuth } from './AuthContext'; import { useAuth } from './AuthContext';

View File

@ -5,7 +5,8 @@ import TextArea from '../../components/Common/TextArea';
import RSInput from '../../components/RSInput'; import RSInput from '../../components/RSInput';
import ConstituentaPicker from '../../components/Shared/ConstituentaPicker'; import ConstituentaPicker from '../../components/Shared/ConstituentaPicker';
import { useLibrary } from '../../context/LibraryContext'; import { useLibrary } from '../../context/LibraryContext';
import { applyFilterCategory, CATEGORY_CST_TYPE, IConstituenta, IRSForm } from '../../models/rsform'; import { CATEGORY_CST_TYPE, IConstituenta, IRSForm } from '../../models/rsform';
import { applyFilterCategory } from '../../models/rsformAPI';
import { prefixes } from '../../utils/constants'; import { prefixes } from '../../utils/constants';
export interface ITemplateState { export interface ITemplateState {
templateID?: number templateID?: number

View File

@ -7,7 +7,8 @@ import Modal, { ModalProps } from '../../components/Common/Modal';
import HelpRSTemplates from '../../components/Help/HelpRSTemplates'; import HelpRSTemplates from '../../components/Help/HelpRSTemplates';
import { HelpIcon } from '../../components/Icons'; import { HelpIcon } from '../../components/Icons';
import usePartialUpdate from '../../hooks/usePartialUpdate'; import usePartialUpdate from '../../hooks/usePartialUpdate';
import { CstType, ICstCreateData, inferTemplatedType, IRSForm, substituteTemplateArgs } from '../../models/rsform'; import { CstType, ICstCreateData, IRSForm } from '../../models/rsform';
import { inferTemplatedType, substituteTemplateArgs } from '../../models/rslangAPI';
import { createAliasFor, validateCstAlias } from '../../utils/misc'; import { createAliasFor, validateCstAlias } from '../../utils/misc';
import ArgumentsTab, { IArgumentsState } from './ArgumentsTab'; import ArgumentsTab, { IArgumentsState } from './ArgumentsTab';
import ConstituentaTab from './ConstituentaTab'; import ConstituentaTab from './ConstituentaTab';

View File

@ -14,7 +14,8 @@ import {
parseSyntacticReference, ReferenceType parseSyntacticReference, ReferenceType
} from '../../models/language'; } from '../../models/language';
import { CstMatchMode } from '../../models/miscelanious'; import { CstMatchMode } from '../../models/miscelanious';
import { IConstituenta, matchConstituenta } from '../../models/rsform'; import { IConstituenta } from '../../models/rsform';
import { matchConstituenta } from '../../models/rsformAPI';
import { prefixes } from '../../utils/constants'; import { prefixes } from '../../utils/constants';
import { compareGrammemeOptions, IGrammemeOption, PremadeWordForms, SelectorGrammems } from '../../utils/selectors'; import { compareGrammemeOptions, IGrammemeOption, PremadeWordForms, SelectorGrammems } from '../../utils/selectors';
import ReferenceTypeButton from './ReferenceTypeButton'; import ReferenceTypeButton from './ReferenceTypeButton';

View File

@ -1,7 +1,8 @@
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { type ErrorInfo } from '../components/BackendError'; import { type ErrorInfo } from '../components/BackendError';
import { IRSForm, IRSFormData, loadRSFormData } from '../models/rsform' import { IRSForm, IRSFormData } from '../models/rsform'
import { loadRSFormData } from '../models/rsformAPI';
import { getRSFormDetails } from '../utils/backendAPI'; import { getRSFormDetails } from '../utils/backendAPI';
export function useRSFormDetails({ target }: { target?: string }) { export function useRSFormDetails({ target }: { target?: string }) {

View File

@ -1,9 +1,7 @@
import { Graph } from '../utils/Graph' import { Graph } from '../utils/Graph'
import { TextMatcher } from '../utils/utils'
import { ILibraryUpdateData } from './library' import { ILibraryUpdateData } from './library'
import { ILibraryItem } from './library' import { ILibraryItem } from './library'
import { CstMatchMode } from './miscelanious' import { IArgumentInfo, ParsingStatus, ValueClass } from './rslang'
import { IArgumentInfo, IArgumentValue, ParsingStatus, ValueClass } from './rslang'
export enum CstType { export enum CstType {
BASE = 'basic', BASE = 'basic',
@ -142,208 +140,3 @@ export interface IRSFormUploadData {
file: File file: File
fileName: string fileName: string
} }
// ========== API =================
export function extractGlobals(expression: string): Set<string> {
return new Set(expression.match(/[XCSADFPT]\d+/g) ?? []);
}
export function inferTemplatedType(templateType: CstType, args: IArgumentValue[]): CstType {
if (args.length === 0 || args.some(arg => !arg.value)) {
return templateType;
} else if (templateType === CstType.PREDICATE) {
return CstType.AXIOM;
} else {
return CstType.TERM;
}
}
export function substituteTemplateArgs(expression: string, args: IArgumentValue[]): string {
if (args.every(arg => !arg.value)) {
return expression;
}
// TODO: figure out actual substitution
return expression;
}
export function loadRSFormData(schema: IRSFormData): IRSForm {
const result = schema as IRSForm
result.graph = new Graph;
if (!result.items) {
result.stats = {
count_all: 0,
count_errors: 0,
count_property: 0,
count_incalc: 0,
count_termin: 0,
count_definition: 0,
count_convention: 0,
count_base: 0,
count_constant: 0,
count_structured: 0,
count_axiom: 0,
count_term: 0,
count_function: 0,
count_predicate: 0,
count_theorem: 0
}
return result;
}
result.stats = {
count_all: result.items.length || 0,
count_errors: result.items.reduce(
(sum, cst) => sum + (cst.parse?.status === ParsingStatus.INCORRECT ? 1 : 0) || 0, 0),
count_property: result.items.reduce(
(sum, cst) => sum + (cst.parse?.valueClass === ValueClass.PROPERTY ? 1 : 0) || 0, 0),
count_incalc: result.items.reduce(
(sum, cst) => sum +
((cst.parse?.status === ParsingStatus.VERIFIED && cst.parse?.valueClass === ValueClass.INVALID) ? 1 : 0) || 0, 0),
count_termin: result.items.reduce(
(sum, cst) => (sum + (cst.term_raw ? 1 : 0) || 0), 0),
count_definition: result.items.reduce(
(sum, cst) => (sum + (cst.definition_raw ? 1 : 0) || 0), 0),
count_convention: result.items.reduce(
(sum, cst) => (sum + (cst.convention ? 1 : 0) || 0), 0),
count_base: result.items.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.BASE ? 1 : 0), 0),
count_constant: result.items?.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.CONSTANT ? 1 : 0), 0),
count_structured: result.items?.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.STRUCTURED ? 1 : 0), 0),
count_axiom: result.items?.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.AXIOM ? 1 : 0), 0),
count_term: result.items.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.TERM ? 1 : 0), 0),
count_function: result.items.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.FUNCTION ? 1 : 0), 0),
count_predicate: result.items.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.PREDICATE ? 1 : 0), 0),
count_theorem: result.items.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.THEOREM ? 1 : 0), 0)
}
result.items.forEach(cst => {
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
cst.is_template = inferTemplate(cst.definition_formal);
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
result.graph.addNode(cst.id);
const dependencies = extractGlobals(cst.definition_formal);
dependencies.forEach(value => {
const source = schema.items.find(cst => cst.alias === value)
if (source) {
result.graph.addEdge(source.id, cst.id);
}
});
});
return result;
}
export function matchConstituenta(target: IConstituenta, query: string, mode: CstMatchMode): boolean {
const matcher = new TextMatcher(query);
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.NAME) &&
matcher.test(target.alias)) {
return true;
}
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.TERM) &&
matcher.test(target.term_resolved)) {
return true;
}
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.EXPR) &&
matcher.test(target.definition_formal)) {
return true;
}
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.TEXT)) {
return (matcher.test(target.definition_resolved) || matcher.test(target.convention));
}
return false;
}
export function inferStatus(parse?: ParsingStatus, value?: ValueClass): ExpressionStatus {
if (!parse || !value) {
return ExpressionStatus.UNDEFINED;
}
if (parse === ParsingStatus.UNDEF) {
return ExpressionStatus.UNKNOWN;
}
if (parse === ParsingStatus.INCORRECT) {
return ExpressionStatus.INCORRECT;
}
if (value === ValueClass.INVALID) {
return ExpressionStatus.INCALCULABLE;
}
if (value === ValueClass.PROPERTY) {
return ExpressionStatus.PROPERTY;
}
return ExpressionStatus.VERIFIED;
}
export function inferTemplate(expression: string): boolean {
const match = expression.match(/R\d+/g);
return (match && match?.length > 0) ?? false;
}
export function inferClass(type: CstType, isTemplate: boolean): CstClass {
if (isTemplate) {
return CstClass.TEMPLATE;
}
switch (type) {
case CstType.BASE: return CstClass.BASIC;
case CstType.CONSTANT: return CstClass.BASIC;
case CstType.STRUCTURED: return CstClass.BASIC;
case CstType.TERM: return CstClass.DERIVED;
case CstType.FUNCTION: return CstClass.DERIVED;
case CstType.AXIOM: return CstClass.STATEMENT;
case CstType.PREDICATE: return CstClass.DERIVED;
case CstType.THEOREM: return CstClass.STATEMENT;
}
}
export function isMockCst(cst: IConstituenta) {
return cst.id <= 0;
}
export function createMockConstituenta(schema: number, id: number, alias: string, type: CstType, comment: string): IConstituenta {
return {
id: id,
order: -1,
schema: schema,
alias: alias,
convention: comment,
cst_type: type,
term_raw: '',
term_resolved: '',
term_forms: [],
definition_formal: '',
definition_raw: '',
definition_resolved: '',
status: ExpressionStatus.INCORRECT,
is_template: false,
cst_class: CstClass.DERIVED,
parse: {
status: ParsingStatus.INCORRECT,
valueClass: ValueClass.INVALID,
typification: 'N/A',
syntaxTree: '',
args: []
}
};
}
export function applyFilterCategory(target: IConstituenta, schema: IRSFormData): IConstituenta[] {
const nextCategory = schema.items.find(
cst => (
cst.order > target.order &&
cst.cst_type === CATEGORY_CST_TYPE
)
);
return schema.items.filter(
cst => (
cst.order > target.order &&
(!nextCategory || cst.order <= nextCategory.order)
)
);
}

View File

@ -0,0 +1,192 @@
// ========== RSForm API =================
import { Graph } from '../utils/Graph';
import { TextMatcher } from '../utils/utils';
import { CstMatchMode } from './miscelanious';
import {
CATEGORY_CST_TYPE, CstClass, CstType,
ExpressionStatus, IConstituenta, IRSForm, IRSFormData
} from './rsform';
import { ParsingStatus, ValueClass } from './rslang';
import { extractGlobals } from './rslangAPI';
export function loadRSFormData(schema: IRSFormData): IRSForm {
const result = schema as IRSForm
result.graph = new Graph;
if (!result.items) {
result.stats = {
count_all: 0,
count_errors: 0,
count_property: 0,
count_incalc: 0,
count_termin: 0,
count_definition: 0,
count_convention: 0,
count_base: 0,
count_constant: 0,
count_structured: 0,
count_axiom: 0,
count_term: 0,
count_function: 0,
count_predicate: 0,
count_theorem: 0
}
return result;
}
result.stats = {
count_all: result.items.length || 0,
count_errors: result.items.reduce(
(sum, cst) => sum + (cst.parse?.status === ParsingStatus.INCORRECT ? 1 : 0) || 0, 0),
count_property: result.items.reduce(
(sum, cst) => sum + (cst.parse?.valueClass === ValueClass.PROPERTY ? 1 : 0) || 0, 0),
count_incalc: result.items.reduce(
(sum, cst) => sum +
((cst.parse?.status === ParsingStatus.VERIFIED && cst.parse?.valueClass === ValueClass.INVALID) ? 1 : 0) || 0, 0),
count_termin: result.items.reduce(
(sum, cst) => (sum + (cst.term_raw ? 1 : 0) || 0), 0),
count_definition: result.items.reduce(
(sum, cst) => (sum + (cst.definition_raw ? 1 : 0) || 0), 0),
count_convention: result.items.reduce(
(sum, cst) => (sum + (cst.convention ? 1 : 0) || 0), 0),
count_base: result.items.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.BASE ? 1 : 0), 0),
count_constant: result.items?.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.CONSTANT ? 1 : 0), 0),
count_structured: result.items?.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.STRUCTURED ? 1 : 0), 0),
count_axiom: result.items?.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.AXIOM ? 1 : 0), 0),
count_term: result.items.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.TERM ? 1 : 0), 0),
count_function: result.items.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.FUNCTION ? 1 : 0), 0),
count_predicate: result.items.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.PREDICATE ? 1 : 0), 0),
count_theorem: result.items.reduce(
(sum, cst) => sum + (cst.cst_type === CstType.THEOREM ? 1 : 0), 0)
}
result.items.forEach(cst => {
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
cst.is_template = inferTemplate(cst.definition_formal);
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
result.graph.addNode(cst.id);
const dependencies = extractGlobals(cst.definition_formal);
dependencies.forEach(value => {
const source = schema.items.find(cst => cst.alias === value)
if (source) {
result.graph.addEdge(source.id, cst.id);
}
});
});
return result;
}
export function matchConstituenta(target: IConstituenta, query: string, mode: CstMatchMode): boolean {
const matcher = new TextMatcher(query);
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.NAME) &&
matcher.test(target.alias)) {
return true;
}
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.TERM) &&
matcher.test(target.term_resolved)) {
return true;
}
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.EXPR) &&
matcher.test(target.definition_formal)) {
return true;
}
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.TEXT)) {
return (matcher.test(target.definition_resolved) || matcher.test(target.convention));
}
return false;
}
export function inferStatus(parse?: ParsingStatus, value?: ValueClass): ExpressionStatus {
if (!parse || !value) {
return ExpressionStatus.UNDEFINED;
}
if (parse === ParsingStatus.UNDEF) {
return ExpressionStatus.UNKNOWN;
}
if (parse === ParsingStatus.INCORRECT) {
return ExpressionStatus.INCORRECT;
}
if (value === ValueClass.INVALID) {
return ExpressionStatus.INCALCULABLE;
}
if (value === ValueClass.PROPERTY) {
return ExpressionStatus.PROPERTY;
}
return ExpressionStatus.VERIFIED;
}
export function inferTemplate(expression: string): boolean {
const match = expression.match(/R\d+/g);
return (match && match?.length > 0) ?? false;
}
export function inferClass(type: CstType, isTemplate: boolean): CstClass {
if (isTemplate) {
return CstClass.TEMPLATE;
}
switch (type) {
case CstType.BASE: return CstClass.BASIC;
case CstType.CONSTANT: return CstClass.BASIC;
case CstType.STRUCTURED: return CstClass.BASIC;
case CstType.TERM: return CstClass.DERIVED;
case CstType.FUNCTION: return CstClass.DERIVED;
case CstType.AXIOM: return CstClass.STATEMENT;
case CstType.PREDICATE: return CstClass.DERIVED;
case CstType.THEOREM: return CstClass.STATEMENT;
}
}
export function isMockCst(cst: IConstituenta) {
return cst.id <= 0;
}
export function createMockConstituenta(schema: number, id: number, alias: string, type: CstType, comment: string): IConstituenta {
return {
id: id,
order: -1,
schema: schema,
alias: alias,
convention: comment,
cst_type: type,
term_raw: '',
term_resolved: '',
term_forms: [],
definition_formal: '',
definition_raw: '',
definition_resolved: '',
status: ExpressionStatus.INCORRECT,
is_template: false,
cst_class: CstClass.DERIVED,
parse: {
status: ParsingStatus.INCORRECT,
valueClass: ValueClass.INVALID,
typification: 'N/A',
syntaxTree: '',
args: []
}
};
}
export function applyFilterCategory(target: IConstituenta, schema: IRSFormData): IConstituenta[] {
const nextCategory = schema.items.find(
cst => (
cst.order > target.order &&
cst.cst_type === CATEGORY_CST_TYPE
)
);
return schema.items.filter(
cst => (
cst.order > target.order &&
(!nextCategory || cst.order <= nextCategory.order)
)
);
}

View File

@ -0,0 +1,91 @@
// Module: RSLang model API
import { CstType } from './rsform';
import { IArgumentValue } from './rslang'
export function extractGlobals(expression: string): Set<string> {
return new Set(expression.match(/[XCSADFPT]\d+/g) ?? []);
}
export function inferTemplatedType(templateType: CstType, args: IArgumentValue[]): CstType {
if (args.length === 0 || args.some(arg => !arg.value)) {
return templateType;
} else if (templateType === CstType.PREDICATE) {
return CstType.AXIOM;
} else {
return CstType.TERM;
}
}
export function substituteTemplateArgs(expression: string, args: IArgumentValue[]): string {
if (args.every(arg => !arg.value)) {
return expression;
}
const mapping: { [key: string]: string } = {};
args.filter(arg => !!arg.value).forEach(arg => { mapping[arg.alias] = arg.value!; })
// TODO: figure out actual substitution
return expression
}
export function splitTemplateDefinition(target: string) {
let start = 0;
for (; start < target.length && target[start] !== '['; ++start) ;
if (start < target.length) {
for (let counter = 0, end = start + 1; end < target.length; ++end) {
if (target[end] === '[') {
++counter;
} else if (target[end] === ']') {
if (counter !== 0) {
--counter;
} else {
}
}
}
}
return {
head: '',
body: target
}
}
// function applyPattern(text: string, mapping: { [key: string]: string }, pattern: RegExp): string {
// /** Apply mapping to matching in regular expression patter subgroup 1. */
// if (text === '' || pattern === null) {
// return text;
// }
// let posInput: number = 0;
// let output: string = '';
// const patternMatches = text.matchAll(pattern);
// for (const segment of patternMatches) {
// const entity = segment[1];
// if (entity in mapping) {
// output += text.substring(posInput, segment.index);
// output += mapping[entity];
// output += text.substring(segment.index, segment.index + segment[0].length);
// posInput = segment.index + segment[0].length;
// }
// }
// output += text.substring(posInput);
// return output;
// }
// def apply_pattern(text: str, mapping: dict[str, str], pattern: re.Pattern[str]) -> str:
// ''' Apply mapping to matching in regular expression patter subgroup 1. '''
// if text == '' or pattern == '':
// return text
// pos_input: int = 0
// output: str = ''
// for segment in re.finditer(pattern, text):
// entity = segment.group(1)
// if entity in mapping:
// output += text[pos_input : segment.start(1)]
// output += mapping[entity]
// output += text[segment.end(1) : segment.end(0)]
// pos_input = segment.end(0)
// output += text[pos_input : len(text)]
// return output

View File

@ -2,7 +2,8 @@ import { useMemo } from 'react';
import { useConceptTheme } from '../../../context/ThemeContext'; import { useConceptTheme } from '../../../context/ThemeContext';
import { ExpressionStatus } from '../../../models/rsform'; import { ExpressionStatus } from '../../../models/rsform';
import { type IConstituenta, inferStatus } from '../../../models/rsform'; import { type IConstituenta } from '../../../models/rsform';
import { inferStatus } from '../../../models/rsformAPI';
import { IExpressionParse, ParsingStatus } from '../../../models/rslang'; import { IExpressionParse, ParsingStatus } from '../../../models/rslang';
import { colorbgCstStatus } from '../../../utils/color'; import { colorbgCstStatus } from '../../../utils/color';
import { describeExpressionStatus, labelExpressionStatus } from '../../../utils/labels'; import { describeExpressionStatus, labelExpressionStatus } from '../../../utils/labels';

View File

@ -14,8 +14,9 @@ import useWindowSize from '../../../hooks/useWindowSize';
import { DependencyMode as CstSource } from '../../../models/miscelanious'; import { DependencyMode as CstSource } from '../../../models/miscelanious';
import { CstMatchMode } from '../../../models/miscelanious'; import { CstMatchMode } from '../../../models/miscelanious';
import { applyGraphFilter } from '../../../models/miscelanious'; import { applyGraphFilter } from '../../../models/miscelanious';
import { CstType, extractGlobals, IConstituenta, isMockCst, matchConstituenta } from '../../../models/rsform'; import { CstType, IConstituenta } from '../../../models/rsform';
import { createMockConstituenta } from '../../../models/rsform'; import { createMockConstituenta, isMockCst, matchConstituenta } from '../../../models/rsformAPI';
import { extractGlobals } from '../../../models/rslangAPI';
import { prefixes } from '../../../utils/constants'; import { prefixes } from '../../../utils/constants';
import { import {
describeConstituenta, describeCstMathchMode, describeConstituenta, describeCstMathchMode,