Refactoring: split common models/API and add docs

This commit is contained in:
IRBorisov 2023-11-17 20:51:13 +03:00
parent 8bd0ef2334
commit 5638fef857
21 changed files with 526 additions and 119 deletions

View File

@ -1,8 +1,8 @@
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
import { ErrorInfo } from '../components/BackendError';
import { matchLibraryItem } from '../models/library';
import { ILibraryItem } from '../models/library';
import { matchLibraryItem } from '../models/libraryAPI';
import { ILibraryFilter } from '../models/miscelanious';
import { IRSForm, IRSFormCreateData, IRSFormData } from '../models/rsform';
import { loadRSFormData } from '../models/rsformAPI';
@ -68,7 +68,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
result = result.filter(item => user?.subscriptions.includes(item.id) || item.owner === user?.id);
}
if (params.query) {
result = result.filter(item => matchLibraryItem(params.query!, item));
result = result.filter(item => matchLibraryItem(item, params.query!));
}
return result;
}, [items, user]);

View File

@ -44,5 +44,5 @@ createRoot(document.getElementById('root')!).render(
</ThemeState>
</IntlProvider>
</ErrorBoundary>
</React.StrictMode>,
</React.StrictMode>
)

View File

@ -260,6 +260,7 @@ export interface ISyntacticReference {
nominal: string
}
/**
* Represents text 0-indexed position inside another text.
*/

View File

@ -1,8 +1,11 @@
// Module: Schema library models.
/**
* Module: Models for Library entities and Users.
*/
import { TextMatcher } from '../utils/utils'
// ========= Users ===========
/**
* Represents user detailed information.
* Some information should only be accesible to authorized users
*/
export interface IUser {
id: number | null
username: string
@ -11,31 +14,63 @@ export interface IUser {
first_name: string
last_name: string
}
/**
* Represents CurrentUser information.
*/
export interface ICurrentUser extends Pick<IUser, 'id' | 'username' | 'is_staff'> {
subscriptions: number[]
}
/**
* Represents login data, used to authentificate users.
*/
export interface IUserLoginData extends Pick<IUser, 'username'> {
password: string
}
/**
* Represents signup data, used to create new users.
*/
export interface IUserSignupData extends Omit<IUser, 'is_staff' | 'id'> {
password: string
password2: string
}
/**
* Represents user data, intended to update user profile in persistent storage.
*/
export interface IUserUpdateData extends Omit<IUser, 'is_staff' | 'id'> { }
/**
* Represents user profile for viewing and editing {@link IUser}.
*/
export interface IUserProfile extends Omit<IUser, 'is_staff'> { }
/**
* Represents user reference information.
*/
export interface IUserInfo extends Omit<IUserProfile, 'email'> { }
/**
* Represents data needed to update password for current user.
*/
export interface IUserUpdatePassword {
old_password: string
new_password: string
}
// ========== LibraryItem ============
/**
* Represents type of library items.
*/
export enum LibraryItemType {
RSFORM = 'rsform',
OPERATIONS_SCHEMA = 'oss'
}
/**
* Represents library item common data typical for all item types.
*/
export interface ILibraryItem {
id: number
item_type: LibraryItemType
@ -49,13 +84,9 @@ export interface ILibraryItem {
owner: number | null
}
/**
* Represents update data for editing {@link ILibraryItem}.
*/
export interface ILibraryUpdateData
extends Omit<ILibraryItem, 'time_create' | 'time_update' | 'id' | 'owner'> {
}
// ============= API ===============
export function matchLibraryItem(query: string, target: ILibraryItem): boolean {
const matcher = new TextMatcher(query);
return matcher.test(target.alias) || matcher.test(target.title);
}
}

View File

@ -0,0 +1,18 @@
/**
* Module: API for Library entities and Users.
*/
import { TextMatcher } from '../utils/utils';
import { ILibraryItem } from './library';
/**
* Checks if a given target {@link ILibraryItem} matches the specified query.
*
* @param target - item to be matched
* @param query - text to be found in target attributes
*/
export function matchLibraryItem(target: ILibraryItem, query: string): boolean {
const matcher = new TextMatcher(query);
return matcher.test(target.alias) || matcher.test(target.title);
}

View File

@ -1,6 +1,6 @@
// Module: Miscellanious frontend model types.
import { IConstituenta, IRSForm } from './rsform'
/**
* Module: Miscellanious frontend model types. Future tagets for refactoring aimed at extracting modules.
*/
/**
* Represents graph dependency mode.
@ -84,23 +84,3 @@ export interface GraphEditorParams {
allowConstant: boolean
allowTheorem: boolean
}
// ================== API ====================
export function applyGraphFilter(schema: IRSForm, start: number, mode: DependencyMode): IConstituenta[] {
if (mode === DependencyMode.ALL) {
return schema.items
}
let ids: number[] | undefined = undefined
switch (mode) {
case DependencyMode.OUTPUTS: { ids = schema.graph.nodes.get(start)?.outputs; break}
case DependencyMode.INPUTS: { ids = schema.graph.nodes.get(start)?.inputs; break}
case DependencyMode.EXPAND_OUTPUTS: { ids = schema.graph.expandOutputs([start]); break}
case DependencyMode.EXPAND_INPUTS: { ids = schema.graph.expandInputs([start]); break}
}
if (!ids) {
return schema.items
} else {
return schema.items.filter(cst => ids!.find(id => id === cst.id))
}
}

View File

@ -0,0 +1,29 @@
/**
* Module: API for miscellanious frontend model types. Future tagets for refactoring aimed at extracting modules.
*/
import { DependencyMode } from './miscelanious';
import { IConstituenta, IRSForm } from './rsform';
/**
* Filter list of {@link ILibraryItem} to a given query.
*/
export function applyGraphFilter(target: IRSForm, start: number, mode: DependencyMode): IConstituenta[] {
if (mode === DependencyMode.ALL) {
return target.items
}
let ids: number[] | undefined = undefined
switch (mode) {
case DependencyMode.OUTPUTS: { ids = target.graph.nodes.get(start)?.outputs; break}
case DependencyMode.INPUTS: { ids = target.graph.nodes.get(start)?.inputs; break}
case DependencyMode.EXPAND_OUTPUTS: { ids = target.graph.expandOutputs([start]); break}
case DependencyMode.EXPAND_INPUTS: { ids = target.graph.expandInputs([start]); break}
}
if (!ids) {
return target.items
} else {
return target.items.filter(cst => ids!.find(id => id === cst.id))
}
}

View File

@ -1,8 +1,15 @@
/**
* Module: Models for formal representation for systems of concepts.
*/
import { Graph } from '../utils/Graph'
import { ILibraryUpdateData } from './library'
import { ILibraryItem } from './library'
import { IArgumentInfo, ParsingStatus, ValueClass } from './rslang'
/**
* Represents Constituenta type.
*/
export enum CstType {
BASE = 'basic',
STRUCTURED = 'structure',
@ -17,6 +24,9 @@ export enum CstType {
// CstType constant for category dividers in TemplateSchemas. TODO: create separate sctructure for templates
export const CATEGORY_CST_TYPE = CstType.THEOREM;
/**
* Represents Constituenta classification in terms of system of concepts.
*/
export enum CstClass {
BASIC = 'basic',
DERIVED = 'derived',
@ -24,7 +34,9 @@ export enum CstClass {
TEMPLATE = 'template'
}
// Constituenta expression status
/**
* Represents formal expression Status.
*/
export enum ExpressionStatus {
VERIFIED = 'verified',
INCORRECT = 'incorrect',
@ -34,12 +46,17 @@ export enum ExpressionStatus {
UNKNOWN = 'unknown',
}
/**
* Represents word form for natural languange.
*/
export interface TermForm {
text: string
tags: string
}
// ====== Constituenta ==========
/**
* Represents Constituenta basic persistent data.
*/
export interface IConstituentaMeta {
id: number
schema: number
@ -55,6 +72,9 @@ export interface IConstituentaMeta {
term_forms: TermForm[]
}
/**
* Represents Constituenta.
*/
export interface IConstituenta
extends IConstituentaMeta {
cst_class: CstClass
@ -69,10 +89,16 @@ extends IConstituentaMeta {
}
}
/**
* Represents Constituenta list.
*/
export interface IConstituentaList {
items: number[]
}
/**
* Represents constituenta data, used in creation process.
*/
export interface ICstCreateData
extends Pick<
IConstituentaMeta,
@ -82,23 +108,37 @@ extends Pick<
insert_after: number | null
}
/**
* Represents data, used in ordering constituents in a list.
*/
export interface ICstMovetoData extends IConstituentaList {
move_to: number
}
/**
* Represents data, used in updating persistent attributes in {@link IConstituenta}.
*/
export interface ICstUpdateData
extends Pick<IConstituentaMeta, 'id'>,
Partial<Pick<IConstituentaMeta, | 'alias' | 'convention' | 'definition_formal' | 'definition_raw' | 'term_raw' | 'term_forms'>> {}
/**
* Represents data, used in renaming {@link IConstituenta}.
*/
export interface ICstRenameData
extends Pick<IConstituentaMeta, 'id' | 'alias' | 'cst_type' > {}
/**
* Represents data response when creating {@link IConstituenta}.
*/
export interface ICstCreatedResponse {
new_cst: IConstituentaMeta
schema: IRSFormData
}
// ========== RSForm ============
/**
* Represents {@link IRSForm} statistics.
*/
export interface IRSFormStats {
count_all: number
count_errors: number
@ -119,6 +159,9 @@ export interface IRSFormStats {
count_theorem: number
}
/**
* Represents formal explication for set of concepts.
*/
export interface IRSForm
extends ILibraryItem {
items: IConstituenta[]
@ -127,14 +170,23 @@ extends ILibraryItem {
subscribers: number[]
}
/**
* Represents data for {@link IRSForm} provided by backend.
*/
export interface IRSFormData extends Omit<IRSForm, 'stats' | 'graph'> {}
/**
* Represents data, used for creating {@link IRSForm}.
*/
export interface IRSFormCreateData
extends ILibraryUpdateData {
file?: File
fileName?: string
}
/**
* Represents data, used for uploading {@link IRSForm} as file.
*/
export interface IRSFormUploadData {
load_metadata: boolean
file: File

View File

@ -1,4 +1,7 @@
// ========== RSForm API =================
/**
* Module: API for formal representation for systems of concepts.
*/
import { Graph } from '../utils/Graph';
import { TextMatcher } from '../utils/utils';
import { CstMatchMode } from './miscelanious';
@ -9,8 +12,15 @@ import {
import { ParsingStatus, ValueClass } from './rslang';
import { extractGlobals } from './rslangAPI';
export function loadRSFormData(schema: IRSFormData): IRSForm {
const result = schema as IRSForm
/**
* Loads data into an {@link IRSForm} based on {@link IRSFormData}.
*
* @remarks
* This function processes the provided input, initializes the IRSForm, and calculates statistics
* based on the loaded data. It also establishes dependencies between concepts in the graph.
*/
export function loadRSFormData(input: IRSFormData): IRSForm {
const result = input as IRSForm
result.graph = new Graph;
if (!result.items) {
result.stats = {
@ -75,7 +85,7 @@ export function loadRSFormData(schema: IRSFormData): IRSForm {
result.graph.addNode(cst.id);
const dependencies = extractGlobals(cst.definition_formal);
dependencies.forEach(value => {
const source = schema.items.find(cst => cst.alias === value)
const source = input.items.find(cst => cst.alias === value)
if (source) {
result.graph.addEdge(source.id, cst.id);
}
@ -84,6 +94,13 @@ export function loadRSFormData(schema: IRSFormData): IRSForm {
return result;
}
/**
* Checks if a given target {@link IConstituenta} matches the specified query using the provided matching mode.
*
* @param target - The target object to be matched.
* @param query - The query string used for matching.
* @param mode - The matching mode to determine which properties to include in the matching process.
*/
export function matchConstituenta(target: IConstituenta, query: string, mode: CstMatchMode): boolean {
const matcher = new TextMatcher(query);
if ((mode === CstMatchMode.ALL || mode === CstMatchMode.NAME) &&
@ -104,6 +121,20 @@ export function matchConstituenta(target: IConstituenta, query: string, mode: Cs
return false;
}
/**
* Infers the status of an expression based on parsing and value information.
*
* @param parse - parsing status of the expression.
* @param value - value class of the expression.
*
* @returns The inferred expression status:
* - `ExpressionStatus.UNDEFINED` if either parsing or value is not provided.
* - `ExpressionStatus.UNKNOWN` if parsing status is `ParsingStatus.UNDEF`.
* - `ExpressionStatus.INCORRECT` if parsing status is `ParsingStatus.INCORRECT`.
* - `ExpressionStatus.INCALCULABLE` if value is `ValueClass.INVALID`.
* - `ExpressionStatus.PROPERTY` if value is `ValueClass.PROPERTY`.
* - `ExpressionStatus.VERIFIED` if both parsing and value are valid.
*/
export function inferStatus(parse?: ParsingStatus, value?: ValueClass): ExpressionStatus {
if (!parse || !value) {
return ExpressionStatus.UNDEFINED;
@ -123,11 +154,26 @@ export function inferStatus(parse?: ParsingStatus, value?: ValueClass): Expressi
return ExpressionStatus.VERIFIED;
}
/**
* Checks if given expression is a template.
*/
export function inferTemplate(expression: string): boolean {
const match = expression.match(/R\d+/g);
return (match && match?.length > 0) ?? false;
}
/**
* Infers the {@link CstClass} based on the provided {@link CstType} and template status.
*
* @param type - The CstType representing the type of the Constituenta.
* @param isTemplate - A boolean indicating whether the Constituenta is a template.
*
* @returns The inferred CstClass based on the combination of CstType and template status:
* - `CstClass.TEMPLATE` if the Constituenta is a template.
* - `CstClass.BASIC` if the CstType is BASE, CONSTANT, or STRUCTURED.
* - `CstClass.DERIVED` if the CstType is TERM, FUNCTION, or PREDICATE.
* - `CstClass.STATEMENT` if the CstType is AXIOM or THEOREM.
*/
export function inferClass(type: CstType, isTemplate: boolean): CstClass {
if (isTemplate) {
return CstClass.TEMPLATE;
@ -144,11 +190,16 @@ export function inferClass(type: CstType, isTemplate: boolean): CstClass {
}
}
export function isMockCst(cst: IConstituenta) {
return cst.id <= 0;
}
export function createMockConstituenta(schema: number, id: number, alias: string, type: CstType, comment: string): IConstituenta {
/**
* Creates a mock {@link IConstituenta} object with the provided parameters and default values for other properties.
*/
export function createMockConstituenta(
schema: number,
id: number,
alias: string,
type: CstType,
comment: string
): IConstituenta {
return {
id: id,
order: -1,
@ -175,16 +226,26 @@ export function createMockConstituenta(schema: number, id: number, alias: string
};
}
export function applyFilterCategory(target: IConstituenta, schema: IRSFormData): IConstituenta[] {
/**
* Checks if given {@link IConstituenta} is mock.
*/
export function isMockCst(cst: IConstituenta) {
return cst.id <= 0;
}
/**
* TODO: description
*/
export function applyFilterCategory(start: IConstituenta, schema: IRSFormData): IConstituenta[] {
const nextCategory = schema.items.find(
cst => (
cst.order > target.order &&
cst.order > start.order &&
cst.cst_type === CATEGORY_CST_TYPE
)
);
return schema.items.filter(
cst => (
cst.order > target.order &&
cst.order > start.order &&
(!nextCategory || cst.order <= nextCategory.order)
)
);

View File

@ -1,28 +1,44 @@
// Module: RSLang model types
/**
* Module: Models for RSLanguage.
*/
// ======== RS Parsing ============
/**
* Represents formal expression.
*/
export interface IRSExpression {
expression: string
}
/**
* Represents syntax type.
*/
export enum Syntax {
UNDEF = 'undefined',
ASCII = 'ascii',
MATH = 'math'
}
/**
* Represents computability class.
*/
export enum ValueClass {
INVALID = 'invalid',
INVALID = 'invalid', // incalculable
VALUE = 'value',
PROPERTY = 'property'
}
/**
* Represents parsing status.
*/
export enum ParsingStatus {
UNDEF = 'undefined',
VERIFIED = 'verified',
INCORRECT = 'incorrect'
}
/**
* Represents parsing error description.
*/
export interface IRSErrorDescription {
errorType: RSErrorType
position: number
@ -30,6 +46,9 @@ export interface IRSErrorDescription {
params: string[]
}
/**
* Represents AST node.
*/
export interface ISyntaxTreeNode {
uid: number
parent: number
@ -41,17 +60,30 @@ export interface ISyntaxTreeNode {
value: unknown
}
}
/**
* Represents Syntax tree for RSLang expression.
*/
export type SyntaxTree = ISyntaxTreeNode[]
/**
* Represents function argument definition.
*/
export interface IArgumentInfo {
alias: string
typification: string
}
/**
* Represents function argument value.
*/
export interface IArgumentValue extends IArgumentInfo {
value?: string
}
/**
* Represents results of expression parse in RSLang.
*/
export interface IExpressionParse {
parseResult: boolean
syntax: Syntax
@ -63,7 +95,9 @@ export interface IExpressionParse {
args: IArgumentInfo[]
}
//! RS language token types enumeration
/**
* Represents RSLang token types.
*/
export enum TokenID {
// Global, local IDs and literals
ID_LOCAL = 258,
@ -225,26 +259,12 @@ export enum RSErrorType {
globalFuncNoInterpretation = 34883
}
// Error handling
/**
* Represents error class.
*/
export enum RSErrorClass {
LEXER,
PARSER,
SEMANTIC,
UNKNOWN
}
const ERRCODE_LEXER_MASK = 512;
const ERRCODE_PARSER_MASK = 1024;
const ERRCODE_TYPE_MASK = 2048;
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,15 +1,23 @@
// Module: RSLang model API
/**
* Module: API for RSLanguage.
*/
import { applyPattern } from '../utils/utils';
import { CstType } from './rsform';
import { IArgumentValue } from './rslang'
import { IArgumentValue, RSErrorClass, RSErrorType } from './rslang'
const LOCALS_REGEXP = /[_a-zα-ω][a-zα-ω]*\d*/g;
/**
* Extracts global variable names from a given expression.
*/
export function extractGlobals(expression: string): Set<string> {
return new Set(expression.match(/[XCSADFPT]\d+/g) ?? []);
}
/**
* Infers type of constituent for a given template and arguments.
*/
export function inferTemplatedType(templateType: CstType, args: IArgumentValue[]): CstType {
if (args.length === 0 || args.some(arg => !arg.value)) {
return templateType;
@ -20,6 +28,20 @@ export function inferTemplatedType(templateType: CstType, args: IArgumentValue[]
}
}
/**
* Splits a string containing a template definition into its head and body parts.
*
* A template definition is expected to have the following format: `[head] body`.
* If the input string does not contain the opening square bracket '[', the entire
* string is treated as the body, and an empty string is assigned to the head.
* If the opening bracket is present, the function attempts to find the matching
* closing bracket ']' to determine the head and body parts.
*
* @example
* const template = "[header] body content";
* const result = splitTemplateDefinition(template);
* // result: `{ head: 'header', body: 'body content' }`
*/
export function splitTemplateDefinition(target: string) {
let start = 0;
for (; start < target.length && target[start] !== '['; ++start) ;
@ -45,6 +67,13 @@ export function splitTemplateDefinition(target: string) {
}
}
/**
* Substitutes values for template arguments in a given expression.
*
* This function takes an input mathematical expression and a list of argument values.
* It replaces template argument placeholders in the expression with their corresponding values
* from the provided arguments.
*/
export function substituteTemplateArgs(expression: string, args: IArgumentValue[]): string {
if (args.every(arg => !arg.value)) {
return expression;
@ -73,3 +102,22 @@ export function substituteTemplateArgs(expression: string, args: IArgumentValue[
return `[${head}] ${body}`
}
}
const ERRCODE_LEXER_MASK = 512;
const ERRCODE_PARSER_MASK = 1024;
const ERRCODE_TYPE_MASK = 2048;
/**
* Infers error class from error type (code).
*/
export function inferErrorClass(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

@ -13,7 +13,7 @@ import useLocalStorage from '../../../hooks/useLocalStorage';
import useWindowSize from '../../../hooks/useWindowSize';
import { DependencyMode as CstSource } from '../../../models/miscelanious';
import { CstMatchMode } from '../../../models/miscelanious';
import { applyGraphFilter } from '../../../models/miscelanious';
import { applyGraphFilter } from '../../../models/miscelaniousAPI';
import { CstType, IConstituenta } from '../../../models/rsform';
import { createMockConstituenta, isMockCst, matchConstituenta } from '../../../models/rsformAPI';
import { extractGlobals } from '../../../models/rslangAPI';

View File

@ -1,3 +1,7 @@
/**
* Module: Custom graph data structure.
*/
/**
* Represents single node of a {@link Graph}, as implemented by storing outgoing and incoming connections.
*/

View File

@ -1,3 +1,7 @@
/**
* Module: API for backend communications.
*/
import axios, { AxiosError, AxiosRequestConfig } from 'axios';
import { toast } from 'react-toastify';
@ -17,12 +21,12 @@ import {
ICstCreateData, ICstCreatedResponse, ICstMovetoData, ICstRenameData, ICstUpdateData,
IRSFormCreateData, IRSFormData, IRSFormUploadData} from '../models/rsform';
import { IExpressionParse, IRSExpression } from '../models/rslang';
import { config } from './constants';
import { buidConstants } from './constants';
const defaultOptions = {
xsrfCookieName: 'csrftoken',
xsrfHeaderName: 'x-csrftoken',
baseURL: `${config.backend}`,
baseURL: `${buidConstants.backend}`,
withCredentials: true
}

View File

@ -1,3 +1,7 @@
/**
* Module: CodeMirror customizations.
*/
import { syntaxTree } from '@codemirror/language'
import { NodeType, Tree, TreeCursor } from '@lezer/common'
import { ReactCodeMirrorRef, SelectionRange } from '@uiw/react-codemirror'

View File

@ -1,11 +1,15 @@
// =========== Modules contains all dynamic color definitions ==========
/**
* Module: Single place for all color definitions in code (see index.css for full defs).
*/
import { GramData, Grammeme, NounGrams, PartOfSpeech, VerbGrams } from '../models/language'
import { CstClass, ExpressionStatus } from '../models/rsform'
import { ISyntaxTreeNode, TokenID } from '../models/rslang'
// ============= MAIN COLOR THEMES ==========
/**
* Represents application color theme configuration.
*/
export interface IColorTheme {
bgDefault: string
bgInput: string
@ -39,7 +43,9 @@ export interface IColorTheme {
fgOrange: string
}
// ======= Light =======
/**
* Represents application Light theme.
*/
export const lightT: IColorTheme = {
bgDefault: 'var(--cl-bg-100)',
bgInput: 'var(--cl-bg-120)',
@ -73,7 +79,9 @@ export const lightT: IColorTheme = {
fgOrange: 'hsl(030, 090%, 055%)'
};
// ======= DARK ========
/**
* Represents application Dark theme.
*/
export const darkT: IColorTheme = {
bgDefault: 'var(--cd-bg-100)',
bgInput: 'var(--cd-bg-120)',
@ -107,7 +115,9 @@ export const darkT: IColorTheme = {
fgOrange: 'hsl(035, 100%, 050%)'
};
// ============ SELECT THEMES ==========
/**
* Represents Select component Light theme.
*/
export const selectLightT = {
primary: lightT.bgPrimary,
primary75: lightT.bgSelected,
@ -130,6 +140,9 @@ export const selectLightT = {
neutral90: lightT.fgWarning
}
/**
* Represents Select component Dark theme.
*/
export const selectDarkT = {
primary: darkT.bgPrimary,
primary75: darkT.bgSelected,
@ -152,7 +165,9 @@ export const selectDarkT = {
neutral90: darkT.fgWarning
}
// ============ GRAPH THEMES ==========
/**
* Represents Graph component Light theme.
*/
export const graphLightT = {
canvas: {
background: '#f9fafb',
@ -202,6 +217,9 @@ export const graphLightT = {
}
}
/**
* Represents Graph component Dark theme.
*/
export const graphDarkT = {
canvas: {
background: '#171717' // var(--cd-bg-100)
@ -251,7 +269,9 @@ export const graphDarkT = {
}
}
// ======== Bracket Matching Themes ===========
/**
* Represents Brackets highlights Light theme.
*/
export const bracketsLightT = {
'.cc-nonmatchingBracket': {
color: lightT.fgRed,
@ -263,6 +283,9 @@ export const bracketsLightT = {
},
};
/**
* Represents Brackets highlights Dark theme.
*/
export const bracketsDarkT = {
'.cc-nonmatchingBracket': {
color: darkT.fgRed,
@ -274,7 +297,9 @@ export const bracketsDarkT = {
},
};
// =========== Misc colors ======================
/**
* Determines background color for {@link ISyntaxTreeNode} based on its type.
*/
export function colorbgSyntaxTree(node: ISyntaxTreeNode, colors: IColorTheme): string {
switch (node.typeID) {
case TokenID.PUNC_DEFINE:
@ -354,6 +379,9 @@ export function colorbgSyntaxTree(node: ISyntaxTreeNode, colors: IColorTheme): s
return colors.bgRed;
}
/**
* Determines background color for {@link ExpressionStatus}.
*/
export function colorbgCstStatus(status: ExpressionStatus, colors: IColorTheme): string {
switch (status) {
case ExpressionStatus.VERIFIED: return colors.bgGreen;
@ -365,6 +393,9 @@ export function colorbgCstStatus(status: ExpressionStatus, colors: IColorTheme):
}
}
/**
* Determines foreground color for {@link ExpressionStatus}.
*/
export function colorfgCstStatus(status: ExpressionStatus, colors: IColorTheme): string {
switch (status) {
case ExpressionStatus.VERIFIED: return colors.fgGreen;
@ -376,6 +407,9 @@ export function colorfgCstStatus(status: ExpressionStatus, colors: IColorTheme):
}
}
/**
* Determines background color for {@link IConstituenta} depending on its {@link CstClass}.
*/
export function colorbgCstClass(cstClass: CstClass, colors: IColorTheme): string {
switch (cstClass) {
case CstClass.BASIC: return colors.bgGreen;
@ -385,6 +419,25 @@ export function colorbgCstClass(cstClass: CstClass, colors: IColorTheme): string
}
}
/**
* Determines background color for {@link GramData}.
*/
export function colorbgGrammeme(gram: GramData, colors: IColorTheme): string {
if (PartOfSpeech.includes(gram as Grammeme)) {
return colors.bgBlue;
}
if (NounGrams.includes(gram as Grammeme)) {
return colors.bgGreen;
}
if (VerbGrams.includes(gram as Grammeme)) {
return colors.bgTeal;
}
return colors.bgInput;
}
/**
* Determines foreground color for {@link GramData}.
*/
export function colorfgGrammeme(gram: GramData, colors: IColorTheme): string {
if (PartOfSpeech.includes(gram as Grammeme)) {
return colors.fgBlue;
@ -401,17 +454,3 @@ export function colorfgGrammeme(gram: GramData, colors: IColorTheme): string {
return colors.fgPurple;
}
}
export function colorbgGrammeme(gram: GramData, colors: IColorTheme): string {
if (PartOfSpeech.includes(gram as Grammeme)) {
return colors.bgBlue;
}
if (NounGrams.includes(gram as Grammeme)) {
return colors.bgGreen;
}
if (VerbGrams.includes(gram as Grammeme)) {
return colors.bgTeal;
}
return colors.bgInput;
}

View File

@ -1,16 +1,47 @@
// Constants
export const config = {
/**
* Module: Global constants.
*/
/**
* Variable constants depending on build type.
*/
export const buidConstants = {
backend: import.meta.env.VITE_PORTAL_BACKEND as string
};
/**
* General UI timeout [in ms] for waiting for render.
*/
export const TIMEOUT_UI_REFRESH = 100;
/**
* Timeout [in ms] for graph refresh.
*/
export const TIMEOUT_GRAPH_REFRESH = 200;
/**
* Exteor file extension for RSForm.
*/
export const EXTEOR_TRS_FILE = '.trs';
/**
* Resource relative URIs.
*/
export const resources = {
graph_font: '/DejaVu.ttf'
}
/**
* Youtube IDs for embedding.
*/
export const youtube = {
intro: '0Ty9mu9sOJo'
};
/**
* Constant URLs.
*/
export const urls = {
concept: 'https://www.acconcept.ru/',
exteor32: 'https://drive.google.com/open?id=1IHlMMwaYlAUBRSxU1RU_hXM5mFU9-oyK&usp=drive_fs',
@ -24,14 +55,16 @@ export const urls = {
restapi: 'https://api.portal.acconcept.ru'
};
export const resources = {
graph_font: '/DejaVu.ttf'
}
/**
* Global unique IDs.
*/
export const globalIDs = {
main_scroll: 'main-scroll'
}
/**
* Prefixes for generating unique keys for lists.
*/
export const prefixes = {
cst_list: 'cst-list-',
cst_hidden_list: 'cst-hidden-list-',

View File

@ -1,10 +1,17 @@
// =========== Module contains all text descriptors. ==========
/**
* Module: Text descriptors for UI and model elements.
*
* Label is a short text used to represent an entity.
* Description is a long description used in tooltips.
*/
import { GramData,Grammeme, ReferenceType } from '../models/language';
import { CstMatchMode, DependencyMode, HelpTopic, LibraryFilterStrategy } from '../models/miscelanious';
import { CstClass, CstType, ExpressionStatus, IConstituenta } from '../models/rsform';
import { IArgumentInfo, IRSErrorDescription, ISyntaxTreeNode, ParsingStatus, RSErrorType, TokenID } from '../models/rslang';
/**
* Generates desription for {@link IConstituenta}.
*/
export function describeConstituenta(cst: IConstituenta): string {
if (cst.cst_type === CstType.STRUCTURED) {
return (
@ -23,7 +30,10 @@ export function describeConstituenta(cst: IConstituenta): string {
}
}
export function describeConstituentaTerm(cst: IConstituenta | undefined): string {
/**
* Generates desription for term of a given {@link IConstituenta}.
*/
export function describeConstituentaTerm(cst?: IConstituenta): string {
if (!cst) {
return '!Конституента отсутствует!';
}
@ -34,10 +44,16 @@ export function describeConstituentaTerm(cst: IConstituenta | undefined): string
}
}
/**
* Generates label for {@link IConstituenta}.
*/
export function labelConstituenta(cst: IConstituenta) {
return `${cst.alias}: ${describeConstituenta(cst)}`;
}
/**
* Retrieves label for {@link TokenID}.
*/
export function labelToken(id: TokenID): string {
switch (id) {
case TokenID.BOOLEAN: return '()';
@ -126,6 +142,9 @@ export function describeToken(id: TokenID): string {
return `no description: ${id}`;
}
/**
* Retrieves label for {@link CstMatchMode}.
*/
export function labelCstMathchMode(mode: CstMatchMode): string {
switch (mode) {
case CstMatchMode.ALL: return 'общий';
@ -136,6 +155,9 @@ export function labelCstMathchMode(mode: CstMatchMode): string {
}
}
/**
* Retrieves description for {@link CstMatchMode}.
*/
export function describeCstMathchMode(mode: CstMatchMode): string {
switch (mode) {
case CstMatchMode.ALL: return 'искать во всех атрибутах';
@ -146,6 +168,9 @@ export function describeCstMathchMode(mode: CstMatchMode): string {
}
}
/**
* Retrieves label for {@link DependencyMode}.
*/
export function labelCstSource(mode: DependencyMode): string {
switch (mode) {
case DependencyMode.ALL: return 'не ограничен';
@ -157,6 +182,9 @@ export function labelCstSource(mode: DependencyMode): string {
}
}
/**
* Retrieves description for {@link DependencyMode}.
*/
export function describeCstSource(mode: DependencyMode): string {
switch (mode) {
case DependencyMode.ALL: return 'все конституенты';
@ -168,6 +196,9 @@ export function describeCstSource(mode: DependencyMode): string {
}
}
/**
* Retrieves label for {@link LibraryFilterStrategy}.
*/
export function labelLibraryFilter(strategy: LibraryFilterStrategy): string {
switch (strategy) {
case LibraryFilterStrategy.MANUAL: return 'отображать все';
@ -179,6 +210,9 @@ export function labelLibraryFilter(strategy: LibraryFilterStrategy): string {
}
}
/**
* Retrieves description for {@link LibraryFilterStrategy}.
*/
export function describeLibraryFilter(strategy: LibraryFilterStrategy): string {
switch (strategy) {
case LibraryFilterStrategy.MANUAL: return 'Отображать все схемы';
@ -190,6 +224,9 @@ export function describeLibraryFilter(strategy: LibraryFilterStrategy): string {
}
}
/**
* Retrieves label for graph layout mode.
*/
export const mapLableLayout: Map<string, string> =
new Map([
['forceatlas2', 'Граф: Атлас 2D'],
@ -207,6 +244,9 @@ new Map([
['nooverlap', 'Граф: Без перекрытия']
]);
/**
* Retrieves label for graph coloring mode.
*/
export const mapLabelColoring: Map<string, string> =
new Map([
['none', 'Цвет: моно'],
@ -214,6 +254,9 @@ new Map([
['type', 'Цвет: класс'],
]);
/**
* Retrieves label for {@link ExpressionStatus}.
*/
export function labelExpressionStatus(status: ExpressionStatus): string {
switch (status) {
case ExpressionStatus.VERIFIED: return 'корректно';
@ -225,6 +268,9 @@ export function labelExpressionStatus(status: ExpressionStatus): string {
}
}
/**
* Retrieves description for {@link ExpressionStatus}.
*/
export function describeExpressionStatus(status: ExpressionStatus): string {
switch (status) {
case ExpressionStatus.VERIFIED: return 'выражение корректно и вычислимо';
@ -236,6 +282,9 @@ export function describeExpressionStatus(status: ExpressionStatus): string {
}
}
/**
* Retrieves label for {@link HelpTopic}.
*/
export function labelHelpTopic(topic: HelpTopic): string {
switch (topic) {
case HelpTopic.MAIN: return 'Портал';
@ -252,6 +301,9 @@ export function labelHelpTopic(topic: HelpTopic): string {
}
}
/**
* Retrieves description for {@link HelpTopic}.
*/
export function describeHelpTopic(topic: HelpTopic): string {
switch (topic) {
case HelpTopic.MAIN: return 'Общая справка по порталу';
@ -268,6 +320,9 @@ export function describeHelpTopic(topic: HelpTopic): string {
}
}
/**
* Retrieves label for {@link CstType}.
*/
export function labelCstType(type: CstType): string {
switch (type) {
case CstType.BASE: return 'Базисное множество';
@ -281,6 +336,9 @@ export function labelCstType(type: CstType): string {
}
}
/**
* Retrieves label for {@link ReferenceType}.
*/
export function labelReferenceType(type: ReferenceType): string {
switch(type) {
case ReferenceType.ENTITY: return 'Использование термина';
@ -288,6 +346,9 @@ export function labelReferenceType(type: ReferenceType): string {
}
}
/**
* Retrieves label for {@link CstClass}.
*/
export function labelCstClass(cclass: CstClass): string {
switch (cclass) {
case CstClass.BASIC: return 'базовый';
@ -297,6 +358,9 @@ export function labelCstClass(cclass: CstClass): string {
}
}
/**
* Retrieves description for {@link CstClass}.
*/
export function describeCstClass(cclass: CstClass): string {
switch (cclass) {
case CstClass.BASIC: return 'неопределяемое понятие, требует конвенции';
@ -306,6 +370,9 @@ export function describeCstClass(cclass: CstClass): string {
}
}
/**
* Generates label for typification.
*/
export function labelTypification({ isValid, resultType, args }: {
isValid: boolean;
resultType: string;
@ -332,6 +399,9 @@ export function labelCstTypification(cst: IConstituenta): string {
});
}
/**
* Generates label for {@link ISyntaxTreeNode}.
*/
export function labelSyntaxTree(node: ISyntaxTreeNode): string {
switch (node.typeID) {
case TokenID.ID_LOCAL:
@ -481,6 +551,9 @@ export function labelGrammeme(gram: GramData): string {
}
}
/**
* Generates error description for {@link IRSErrorDescription}.
*/
export function describeRSError(error: IRSErrorDescription): string {
switch (error.errorType) {
case RSErrorType.unknownSymbol:

View File

@ -1,9 +1,10 @@
// Module: miscellaneous static functions to generate UI resources
/**
* Module: miscellaneous static functions to generate UI resources.
*/
import { CstType, IConstituenta, IRSForm } from '../models/rsform';
import { IRSErrorDescription } from '../models/rslang';
import { resolveErrorClass, RSErrorClass } from '../models/rslang';
import { IRSErrorDescription, RSErrorClass } from '../models/rslang';
import { inferErrorClass } from '../models/rslangAPI';
import { labelCstType } from './labels';
export function getCstTypePrefix(type: CstType) {
@ -70,7 +71,7 @@ export function cloneTitle(schema: IRSForm): string {
export function getRSErrorPrefix(error: IRSErrorDescription): string {
const id = error.errorType.toString(16)
switch(resolveErrorClass(error.errorType)) {
switch(inferErrorClass(error.errorType)) {
case RSErrorClass.LEXER: return 'L' + id;
case RSErrorClass.PARSER: return 'P' + id;
case RSErrorClass.SEMANTIC: return 'S' + id;

View File

@ -1,4 +1,6 @@
// Module: Selector maps
/**
* Module: Mappings for selector UI elements.
*/
import { LayoutTypes } from 'reagraph';
import { type GramData, Grammeme, ReferenceType } from '../models/language';

View File

@ -1,6 +1,13 @@
/**
* Module: Utility functions.
*/
/**
* Checks if arguments is Node.
*/
export function assertIsNode(e: EventTarget | null): asserts e is Node {
if (!e || !('nodeType' in e)) {
throw new Error('Node expected');
if (e === null || !('nodeType' in e)) {
throw new TypeError(`Expected 'Node' but recieved '${e?.constructor.name ?? 'null'}'`);
}
}