2023-11-17 20:51:13 +03:00
/ * *
* Module : API for RSLanguage .
* /
2023-11-06 20:21:30 +03:00
2025-02-22 14:04:01 +03:00
import { type Tree } from '@lezer/common' ;
2025-02-12 20:53:31 +03:00
import { cursorNode } from '@/utils/codemirror' ;
import { PARAMETER } from '@/utils/constants' ;
2025-02-22 14:04:01 +03:00
import { CstType , type IRSErrorDescription , type RSErrorType } from '../backend/types' ;
2025-04-22 11:32:58 +03:00
import { labelCstTypification } from '../labels' ;
2025-02-17 14:40:18 +03:00
2025-04-16 21:06:59 +03:00
import { type IRSForm } from './rsform' ;
2025-02-21 21:15:05 +03:00
import { type AliasMapping , type IArgumentValue , RSErrorClass , type SyntaxTree } from './rslang' ;
2023-11-06 20:21:30 +03:00
2024-04-05 15:53:05 +03:00
// cspell:disable
2023-11-06 22:21:36 +03:00
const LOCALS_REGEXP = /[_a-zα -ω][a-zα -ω]*\d*/g ;
2024-04-05 15:53:05 +03:00
const GLOBALS_REGEXP = /[XCSADFPT]\d+/g ;
const COMPLEX_SYMBOLS_REGEXP = /[∀∃×ℬ;|:]/g ;
2024-08-26 22:53:27 +03:00
const TYPIFICATION_SET = /^ℬ +\([ℬ \(X\d+\)× ]*\)$/g ;
2024-04-05 15:53:05 +03:00
// cspell:enable
2023-11-06 22:21:36 +03:00
2025-04-16 21:06:59 +03:00
/** Extracts global variable names from a given expression. */
2023-11-06 20:21:30 +03:00
export function extractGlobals ( expression : string ) : Set < string > {
2024-04-05 15:53:05 +03:00
return new Set ( expression . match ( GLOBALS_REGEXP ) ? ? [ ] ) ;
}
2025-04-16 21:06:59 +03:00
/** Check if expression is simple derivation. */
2024-04-05 15:53:05 +03:00
export function isSimpleExpression ( text : string ) : boolean {
2024-04-05 20:04:12 +03:00
return ! text . match ( COMPLEX_SYMBOLS_REGEXP ) ;
2023-11-06 20:21:30 +03:00
}
2025-04-16 21:06:59 +03:00
/** Check if expression is set typification. */
2024-08-26 22:53:27 +03:00
export function isSetTypification ( text : string ) : boolean {
return ! ! text . match ( TYPIFICATION_SET ) ;
}
2025-04-16 21:06:59 +03:00
/** Infers type of constituent for a given template and arguments. */
2023-11-06 20:21:30 +03:00
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 ;
}
}
2023-11-17 20:51:13 +03:00
/ * *
* 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
2023-12-17 20:19:28 +03:00
* const template = '[header] body content' ;
2023-11-17 20:51:13 +03:00
* const result = splitTemplateDefinition ( template ) ;
* // result: `{ head: 'header', body: 'body content' }`
* /
2023-11-06 20:21:30 +03:00
export function splitTemplateDefinition ( target : string ) {
let start = 0 ;
2023-12-28 14:04:44 +03:00
for ( ; start < target . length && target [ start ] !== '[' ; ++ start ) ;
2023-11-06 20:21:30 +03:00
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 {
2023-11-06 22:21:36 +03:00
return {
head : target.substring ( start + 1 , end ) . trim ( ) ,
body : target.substring ( end + 1 ) . trim ( )
2023-12-28 14:04:44 +03:00
} ;
2023-11-06 20:21:30 +03:00
}
}
}
}
return {
head : '' ,
body : target
2023-12-28 14:04:44 +03:00
} ;
2023-11-06 20:21:30 +03:00
}
2023-11-17 20:51:13 +03:00
/ * *
* 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 .
* /
2023-11-06 22:21:36 +03:00
export function substituteTemplateArgs ( expression : string , args : IArgumentValue [ ] ) : string {
if ( args . every ( arg = > ! arg . value ) ) {
return expression ;
}
2024-08-26 22:53:27 +03:00
const mapping : AliasMapping = { } ;
2023-12-28 14:04:44 +03:00
args
. filter ( arg = > ! ! arg . value )
. forEach ( arg = > {
mapping [ arg . alias ] = arg . value ! ;
} ) ;
2023-11-06 22:21:36 +03:00
let { head , body } = splitTemplateDefinition ( expression ) ;
body = applyPattern ( body , mapping , LOCALS_REGEXP ) ;
const argTexts = head . split ( ',' ) . map ( text = > text . trim ( ) ) ;
head = argTexts
2023-12-28 14:04:44 +03:00
. filter ( arg = > [ . . . arg . matchAll ( LOCALS_REGEXP ) ] . every ( local = > local . every ( match = > ! ( match in mapping ) ) ) )
. join ( ', ' ) ;
2023-11-06 22:21:36 +03:00
if ( ! head ) {
return body ;
} else {
2023-12-28 14:04:44 +03:00
return ` [ ${ head } ] ${ body } ` ;
2023-11-06 22:21:36 +03:00
}
}
2023-11-17 20:51:13 +03:00
2024-01-04 14:35:46 +03:00
/ * *
* Generate ErrorID label .
* /
export function getRSErrorPrefix ( error : IRSErrorDescription ) : string {
const id = error . errorType . toString ( 16 ) ;
// prettier-ignore
switch ( inferErrorClass ( 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 ;
}
}
2024-08-26 22:53:27 +03:00
2025-04-16 21:06:59 +03:00
/** Apply alias mapping. */
2024-08-26 22:53:27 +03:00
export function applyAliasMapping ( target : string , mapping : AliasMapping ) : string {
return applyPattern ( target , mapping , GLOBALS_REGEXP ) ;
}
2025-04-16 21:06:59 +03:00
/** Apply alias typification mapping. */
2024-08-26 22:53:27 +03:00
export function applyTypificationMapping ( target : string , mapping : AliasMapping ) : string {
2024-08-28 00:00:04 +03:00
const modified = applyAliasMapping ( target , mapping ) ;
if ( modified === target ) {
2024-08-26 22:53:27 +03:00
return target ;
}
2024-08-28 00:00:04 +03:00
const deleteBrackets : number [ ] = [ ] ;
const positions : number [ ] = [ ] ;
const booleans : number [ ] = [ ] ;
let boolCount : number = 0 ;
let stackSize : number = 0 ;
for ( let i = 0 ; i < modified . length ; i ++ ) {
const char = modified [ i ] ;
if ( char === 'ℬ ' ) {
boolCount ++ ;
continue ;
}
if ( char === '(' ) {
stackSize ++ ;
positions . push ( i ) ;
booleans . push ( boolCount ) ;
}
boolCount = 0 ;
if ( char === ')' ) {
if (
i < modified . length - 1 &&
modified [ i + 1 ] === ')' &&
stackSize > 1 &&
positions [ stackSize - 2 ] + booleans [ stackSize - 1 ] + 1 === positions [ stackSize - 1 ]
) {
deleteBrackets . push ( i ) ;
deleteBrackets . push ( positions [ stackSize - 2 ] ) ;
}
if ( i === modified . length - 1 && stackSize === 1 && positions [ 0 ] === 0 ) {
deleteBrackets . push ( i ) ;
deleteBrackets . push ( positions [ 0 ] ) ;
}
stackSize -- ;
positions . pop ( ) ;
booleans . pop ( ) ;
}
}
let result = '' ;
for ( let i = 0 ; i < modified . length ; i ++ ) {
if ( ! deleteBrackets . includes ( i ) ) {
result += modified [ i ] ;
}
}
2024-08-26 22:53:27 +03:00
return result ;
}
2025-02-12 20:53:31 +03:00
2025-04-16 21:06:59 +03:00
/** Transform Tree to {@link SyntaxTree}. */
2025-02-12 20:53:31 +03:00
export function transformAST ( tree : Tree ) : SyntaxTree {
const result : SyntaxTree = [ ] ;
const parents : number [ ] = [ ] ;
const cursor = tree . cursor ( ) ;
let finished = false ;
let leave = true ;
while ( ! finished ) {
let node = cursorNode ( cursor ) ;
node . isLeaf = ! cursor . firstChild ( ) ;
leave = true ;
result . push ( {
uid : result.length ,
parent : parents.length > 0 ? parents [ parents . length - 1 ] : result . length ,
typeID : node.type.id ,
start : node.from ,
finish : node.to ,
data : {
dataType : 'string' ,
value : node.type.name == '⚠' ? PARAMETER.errorNodeLabel : node.type.name
}
} ) ;
parents . push ( result . length - 1 ) ;
if ( ! node . isLeaf ) continue ;
for ( ; ; ) {
node = cursorNode ( cursor , node . isLeaf ) ;
if ( leave ) {
parents . pop ( ) ;
}
leave = cursor . type . isAnonymous ;
node . isLeaf = false ;
if ( cursor . nextSibling ( ) ) {
break ;
}
if ( ! cursor . parent ( ) ) {
finished = true ;
break ;
}
leave = true ;
}
}
return result ;
}
2025-04-13 23:14:00 +03:00
2025-04-16 21:06:59 +03:00
export function generatePrompt ( schema : IRSForm ) : string {
const intro =
2025-04-22 11:32:58 +03:00
' К о н ц е п т у а л ь н а я с х е м а — э т о ф о р м а л и з о в а н н а я м о д е л ь п р е д м е т н о й о б л а с т и , в ы р а ж е н н а я с п о м о щ ь ю я з ы к а р о д о в с т р у к т у р , о с н о в а н н о г о н а а п п а р а т е ф о р м а л ь н о й л о г и к и и т е о р и и м н о ж е с т в , и д о п о л н е н н а я е с т е с т в е н н о - я з ы к о в ы м и п о я с н е н и я м и . О н а п р е д с т а в л я е т с о б о й с и с т е м у в з а и м о с в я з а н н ы х о п р е д е л е н и й , г д е к а ж д о е п о н я т и е и л и у т в е р ж д е н и е з а д а ё т с я в с т р о г о м ф о р м а т е О б о з н а ч е н и е - "Типизация" - "Термин" - "Определение в языке родов структур" - "Определение в естественном языке" - "Конвенция или комментарий" . \ nО б о зна че ние — у н и к а л ь н ы й и д е н т и ф и к а т о р п о н я т и я ( н а п р и м е р , X1 , S3 , F14 ) . \ nТ ипиза ция — с т р у к т у р а э л е м е н т о в м н о ж е с т в а , м о д е л и р у ю щ е г о д а н н о е п о н я т и е ( н а п р и м е р , ℬ ( X1 ) д л я п о д м н о ж е с т в а и н д и в и д о в и л и ℬ ( X1 × X1 ) д л я б и н а р н ы х о т н о ш е н и й и л и ℬ ( X1 ) 🠔 [ ℬ ( X1 × ℬ ( X1 ) ) ] — д л я т е р м - ф у н к ц и и ) . \ nТ е р мин — н а з в а н и е п о н я т и я в е с т е с т в е н н о м я з ы к е . \ nК о нве нция о п и с ы в а е т н е о п р е д е л я е м ы е п о н я т и я п р е д м е т н ы м я з ы к о м , в к л ю ч а я у т о ч н е н и я , о г р а н и ч е н и я и л и п р и м е р ы , в к л ю ч а я с с ы л к и н а в н е ш н и е д а н н ы е ( н а п р и м е р , д о к у м е н т ы ) . \ nВ ф о р м а л ь н о м в ы р а ж е н и и и с п о л ь з у е т с я р я д ф о р м а л ь н ы х к о н с т р у к ц и й . \ npr1 ( α ) — м а л а я п р о е к ц и я , в о з в р а щ а ю щ а я к о м п о н е н т к о р т е ж а , с о о т в е т с т в у ю щ и й и н д е к с у . \ nPr2 , 1 ( S1 ) — б о л ь ш а я п р о е к ц и я , к о т о р а я п р е в р а щ а е т м н о ж е с т в о к о р т е ж е й в н о в у ю с т р у к т у р у в с о о т в е т с т в и и с н а б о р о м и н д е к с о в . В д а н н о м п р и м е р е э л е м е н т ы S1 п р е в р а щ а ю т с я в п а р ы , г д е н а 1 м е с т е с т о и т в т о р о й к о м п о н е н т , а н а 2 м е с т е — п е р в ы й . \ nFi1 [ S1 ] ( S2 ) — ф и л ь т р , к о т о р ы й п о з а д а н н ы м в к в а д р а т н ы х с к о б к а х п а р а м е т р а м о т б и р а е т и з м н о ж е с т в а к о р т е ж е й в к р у г л ы х с к о б к а х п о д м н о ж е с т в о т а к о е , ч т о с о о т в е т с т в у ю щ и е з а д а н н ы м и н д е к с а м к о м п о н е н т ы к о р т е ж е й и з S2 в к л ю ч е н ы в с о о т в е т с т в у ю щ и й п а р а м е т р в к в а д р а т н ы х с к о б к а х . \ nD { ξ ∈ X1 | P1 [ ξ ] } — д е к л а р а т и в н о е о б ъ я в л е н и е , к о т о р о е п о з в о л я е т з а д а т ь о б л а с т ь о п р е д е л е н и я ( с л е в а о т в е р т и к а л ь н о й ч е р т ы ) и л о г и ч е с к о е в ы р а ж е н и е ( с п р а в а о т ч е р т ы ) , к о т о р о е и с п о л ь з у е т с я д л я о т б о р а п о д м н о ж е с т в а и з о б л а с т и о п р е д е л е н и я . \ nI { ( σ , γ ) | σ : ∈ X1 ; γ : = F1 [ σ ] ; P1 [ σ , γ ] } — и м п е р а т и в н о е о п р е д е л е н и я , з а д а в а е м о е в ы ч и с л я е м ы м в ы р а ж е н и е м э л е м е н т а ( с л е в а о т ч е р т ы ) и п о с л е д о в а т е л ь н о с т ь ю д е й с т в и я ( с п р а в а о т ч е р т ы ) , п о з в о л я ю щ и х с ф о р м и р о в а т ь н е о б х о д и м ы е д л я в ы ч и с л е н и я в ы р а ж е н и я п е р е м е н н ы е . Д е й с т в и я р а з д е л е н ы т о ч к о й с з а п я т о й и п р е д с т а в л е н ы 3 видами : перебор э л е м е н т о в м н о ж е с т в а ч е р е з : ∈ , в ы ч и с л е н и е в ы р а ж е н и я и п р и с в о е н и я з н а ч е н и я п е р е м е н н о й ч е р е з : = и <EFBFBD>
2025-04-16 21:06:59 +03:00
const outro =
'\n------\nПр и ответе на следующий вопрос используй представленные в концептуальной схеме понятия и определения.\n' ;
let body = ` Название концептуальной схемы: ${ schema . title } \ n ` ;
body += ` [ ${ schema . alias } ] Описание: " ${ schema . description } " \ n \ n ` ;
body += 'Понятия:\n' ;
schema . items . forEach ( item = > {
2025-04-22 11:32:58 +03:00
body += ` ${ item . alias } - " ${ labelCstTypification ( item ) } " - " ${ item . term_resolved } " - " ${
item . definition_formal
} " - " $ { item . definition_resolved } " - " $ { item . convention } " \ n ` ;
2025-04-16 21:06:59 +03:00
} ) ;
return ` ${ intro } ${ body } ${ outro } ` ;
}
2025-04-13 23:14:00 +03:00
// ====== Internals =========
/** Text substitution guided by mapping and regular expression. */
function applyPattern ( text : string , mapping : AliasMapping , pattern : RegExp ) : string {
if ( text === '' || pattern === null ) {
return text ;
}
let posInput = 0 ;
let output = '' ;
const patternMatches = text . matchAll ( pattern ) ;
for ( const segment of patternMatches ) {
const entity = segment [ 0 ] ;
const start = segment . index ? ? 0 ;
if ( entity in mapping ) {
output += text . substring ( posInput , start ) ;
output += mapping [ entity ] ;
posInput = start + segment [ 0 ] . length ;
}
}
output += text . substring ( posInput ) ;
return output ;
}
const ERROR_LEXER_MASK = 512 ;
const ERROR_PARSER_MASK = 1024 ;
const ERROR_SEMANTIC_MASK = 2048 ;
/** Infers error class from error type (code). */
function inferErrorClass ( error : RSErrorType ) : RSErrorClass {
if ( ( error & ERROR_LEXER_MASK ) !== 0 ) {
return RSErrorClass . LEXER ;
} else if ( ( error & ERROR_PARSER_MASK ) !== 0 ) {
return RSErrorClass . PARSER ;
} else if ( ( error & ERROR_SEMANTIC_MASK ) !== 0 ) {
return RSErrorClass . SEMANTIC ;
} else {
return RSErrorClass . UNKNOWN ;
}
}