F: Implementing Association and Nominal pt1

This commit is contained in:
Ivan 2025-08-10 12:28:03 +03:00
parent b20787a741
commit 4742d241c3
22 changed files with 328 additions and 45 deletions

View File

@ -179,6 +179,7 @@
"viewsets",
"wordform",
"Wordforms",
"XCSDATN",
"Айзенштат",
"Акименков",
"Астрина",

View File

@ -299,7 +299,7 @@ class LibraryViewSet(viewsets.ModelViewSet):
with transaction.atomic():
added, deleted = m.Editor.set_and_return_diff(item.pk, editors)
if len(added) >= 0 or len(deleted) >= 0:
if added or deleted:
owned_schemas = OperationSchema.owned_schemasQ(item).only('pk')
if owned_schemas.exists():
m.Editor.objects.filter(

View File

@ -45,7 +45,7 @@ class OperationSchemaCached:
self.cache.ensure_loaded_subs()
operation = self.cache.operation_by_id[target]
children = self.cache.extend_graph.outputs[target]
if operation.result is not None and len(children) > 0:
if operation.result is not None and children:
ids = list(Constituenta.objects.filter(schema=operation.result).values_list('pk', flat=True))
if not keep_constituents:
self.engine.on_delete_inherited(operation.pk, ids)

View File

@ -47,7 +47,7 @@ class BlockSerializer(StrictModelSerializer):
class ArgumentSerializer(StrictModelSerializer):
''' Serializer: Operation data. '''
''' Serializer: Operation arguments. '''
class Meta:
''' serializer metadata. '''
model = Argument
@ -55,7 +55,7 @@ class ArgumentSerializer(StrictModelSerializer):
class ReplicaSerializer(StrictModelSerializer):
''' Serializer: Replica data. '''
''' Serializer: Replica relation. '''
class Meta:
''' serializer metadata. '''
model = Replica

View File

@ -10,3 +10,11 @@ class ConstituentaAdmin(admin.ModelAdmin):
ordering = ['schema', 'order']
list_display = ['schema', 'order', 'alias', 'term_resolved', 'definition_resolved', 'crucial']
search_fields = ['term_resolved', 'definition_resolved']
@admin.register(models.Association)
class AssociationAdmin(admin.ModelAdmin):
''' Admin model: Association. '''
ordering = ['container__schema', 'container', 'associate']
list_display = ['container__schema__alias', 'container__alias', 'associate__alias']
search_fields = ['container', 'associate']

View File

@ -0,0 +1,32 @@
# Generated by Django 5.2.4 on 2025-08-09 10:55
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rsform', '0004_constituenta_crucial'),
]
operations = [
migrations.AlterField(
model_name='constituenta',
name='cst_type',
field=models.CharField(choices=[('nominal', 'Nominal'), ('basic', 'Base'), ('constant', 'Constant'), ('structure', 'Structured'), ('axiom', 'Axiom'), ('term', 'Term'), ('function', 'Function'), ('predicate', 'Predicate'), ('theorem', 'Theorem')], default='basic', max_length=10, verbose_name='Тип'),
),
migrations.CreateModel(
name='Association',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('associate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='as_associate', to='rsform.constituenta', verbose_name='Ассоциированная конституента')),
('container', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='as_container', to='rsform.constituenta', verbose_name='Составная конституента')),
],
options={
'verbose_name': 'Ассоциация конституент',
'verbose_name_plural': 'Ассоциации конституент',
'unique_together': {('container', 'associate')},
},
),
]

View File

@ -0,0 +1,28 @@
''' Models: Synthesis Inheritance. '''
from django.db.models import CASCADE, ForeignKey, Model
class Association(Model):
''' Association links nominal constituent to its content.'''
container = ForeignKey(
verbose_name='Составная конституента',
to='rsform.Constituenta',
on_delete=CASCADE,
related_name='as_container'
)
associate = ForeignKey(
verbose_name='Ассоциированная конституента',
to='rsform.Constituenta',
on_delete=CASCADE,
related_name='as_associate'
)
class Meta:
''' Model metadata. '''
verbose_name = 'Ассоциация конституент'
verbose_name_plural = 'Ассоциации конституент'
unique_together = [['container', 'associate']]
def __str__(self) -> str:
return f'{self.container} -> {self.associate}'

View File

@ -16,9 +16,9 @@ from django.db.models import (
from ..utils import apply_pattern
_RE_GLOBALS = r'[XCSADFPT]\d+' # cspell:disable-line
_RE_GLOBALS = r'[XCSADFPTN]\d+' # cspell:disable-line
_REF_ENTITY_PATTERN = re.compile(r'@{([^0-9\-].*?)\|.*?}')
_GLOBAL_ID_PATTERN = re.compile(r'([XCSADFPT][0-9]+)') # cspell:disable-line
_GLOBAL_ID_PATTERN = re.compile(r'([XCSADFPTN][0-9]+)') # cspell:disable-line
def extract_globals(expression: str) -> set[str]:
@ -38,6 +38,7 @@ def replace_entities(expression: str, mapping: dict[str, str]) -> str:
class CstType(TextChoices):
''' Type of constituenta. '''
NOMINAL = 'nominal'
BASE = 'basic'
CONSTANT = 'constant'
STRUCTURED = 'structure'

View File

@ -1,5 +1,6 @@
''' Django: Models. '''
from .Association import Association
from .Constituenta import Constituenta, CstType, extract_globals, replace_entities, replace_globals
from .OrderManager import OrderManager
from .RSForm import DELETED_ALIAS, INSERT_LAST, RSForm

View File

@ -37,6 +37,7 @@ def get_type_prefix(cst_type: str) -> str:
case CstType.FUNCTION: return 'F'
case CstType.PREDICATE: return 'P'
case CstType.THEOREM: return 'T'
case CstType.NOMINAL: return 'N'
return 'X'

View File

@ -133,7 +133,7 @@ class ReferenceSerializer(StrictSerializer):
class InheritanceDataSerializer(StrictSerializer):
''' Serializer: inheritance data. '''
''' Serializer: Inheritance data. '''
child = serializers.IntegerField()
child_source = serializers.IntegerField()
parent = serializers.IntegerField() # type: ignore

View File

@ -17,11 +17,19 @@ from apps.oss.models import Inheritance
from shared import messages as msg
from shared.serializers import StrictModelSerializer, StrictSerializer
from ..models import Constituenta, CstType, RSForm
from ..models import Association, Constituenta, CstType, RSForm
from .basics import CstParseSerializer, InheritanceDataSerializer
from .io_pyconcept import PyConceptAdapter
class AssociationSerializer(StrictModelSerializer):
''' Serializer: Association relation. '''
class Meta:
''' serializer metadata. '''
model = Association
fields = ('argument', 'operation')
class CstBaseSerializer(StrictModelSerializer):
''' Serializer: Constituenta all data. '''
class Meta:
@ -134,6 +142,9 @@ class RSFormSerializer(StrictModelSerializer):
inheritance = serializers.ListField(
child=InheritanceDataSerializer()
)
association = serializers.ListField(
child=AssociationSerializer()
)
oss = serializers.ListField(
child=LibraryItemReferenceSerializer()
)
@ -164,6 +175,7 @@ class RSFormSerializer(StrictModelSerializer):
result['items'] = []
result['oss'] = []
result['inheritance'] = []
result['association'] = []
for cst in Constituenta.objects.filter(schema=instance).defer('order').order_by('order'):
result['items'].append(CstInfoSerializer(cst).data)
for oss in LibraryItem.objects.filter(operations__result=instance).only('alias'):
@ -171,6 +183,11 @@ class RSFormSerializer(StrictModelSerializer):
'id': oss.pk,
'alias': oss.alias
})
for assoc in Association.objects.filter(container__schema=instance).only('container_id', 'associate_id'):
result['association'].append({
'container': assoc.container_id,
'associate': assoc.associate_id
})
return result
def to_versioned_data(self) -> dict:
@ -200,37 +217,38 @@ class RSFormSerializer(StrictModelSerializer):
instance = cast(LibraryItem, self.instance)
schema = RSForm(instance)
items: list[dict] = data['items']
ids: list[int] = [item['id'] for item in items]
processed: list[int] = []
stored_ids: list[int] = [item['id'] for item in items]
id_map: dict[int, int] = {}
for cst in schema.constituentsQ():
if not cst.pk in ids:
cst.delete()
for existing_cst in schema.constituentsQ():
if not existing_cst.pk in stored_ids:
existing_cst.delete()
else:
cst_data = next(x for x in items if x['id'] == cst.pk)
cst_data = next(x for x in items if x['id'] == existing_cst.pk)
cst_data['schema'] = instance.pk
new_cst = CstBaseSerializer(data=cst_data)
new_cst.is_valid(raise_exception=True)
new_cst.validated_data['order'] = ids.index(cst.pk)
new_cst.update(
instance=cst,
validated_data=new_cst.validated_data
cst_serializer = CstBaseSerializer(data=cst_data)
cst_serializer.is_valid(raise_exception=True)
cst_serializer.validated_data['order'] = stored_ids.index(existing_cst.pk)
cst_serializer.update(
instance=existing_cst,
validated_data=cst_serializer.validated_data
)
processed.append(cst.pk)
id_map[cst_data['id']] = existing_cst.pk
for cst_data in items:
if cst_data['id'] not in processed:
cst = schema.insert_last(cst_data['alias'])
if cst_data['id'] not in id_map:
old_id = cst_data['id']
cst_data['id'] = cst.pk
inserted_cst = schema.insert_last(cst_data['alias'])
cst_data['id'] = inserted_cst.pk
cst_data['schema'] = instance.pk
new_cst = CstBaseSerializer(data=cst_data)
new_cst.is_valid(raise_exception=True)
new_cst.validated_data['order'] = ids.index(old_id)
new_cst.update(
instance=cst,
validated_data=new_cst.validated_data
cst_serializer = CstBaseSerializer(data=cst_data)
cst_serializer.is_valid(raise_exception=True)
cst_serializer.validated_data['order'] = stored_ids.index(old_id)
cst_serializer.update(
instance=inserted_cst,
validated_data=cst_serializer.validated_data
)
id_map[old_id] = inserted_cst.pk
loaded_item = LibraryItemBaseNonStrictSerializer(data=data)
loaded_item.is_valid(raise_exception=True)
@ -239,6 +257,23 @@ class RSFormSerializer(StrictModelSerializer):
validated_data=loaded_item.validated_data
)
Association.objects.filter(container__schema=instance).delete()
associations_to_create: list[Association] = []
for assoc in data.get('association', []):
old_container_id = assoc['container']
old_associate_id = assoc['associate']
container_id = id_map.get(old_container_id)
associate_id = id_map.get(old_associate_id)
if container_id and associate_id:
associations_to_create.append(
Association(
container_id=container_id,
associate_id=associate_id
)
)
if associations_to_create:
Association.objects.bulk_create(associations_to_create)
class RSFormParseSerializer(StrictModelSerializer):
''' Serializer: Detailed data for RSForm including parse. '''

View File

@ -6,11 +6,11 @@ from apps.library.models import LibraryItem
from shared import messages as msg
from shared.serializers import StrictSerializer
from ..models import Constituenta, RSFormCached
from ..models import Constituenta, CstType, RSFormCached
from ..utils import fix_old_references
_CST_TYPE = 'constituenta'
_TRS_TYPE = 'rsform'
_ENTITY_CONSTITUENTA = 'constituenta'
_ENTITY_SCHEMA = 'rsform'
_TRS_VERSION_MIN = 16
_TRS_VERSION = 16
_TRS_HEADER = 'Exteor 4.8.13.1000 - 30/05/2022'
@ -30,11 +30,11 @@ class RSFormUploadSerializer(StrictSerializer):
def generate_trs(schema: LibraryItem) -> dict:
''' Generate TRS file for RSForm. '''
items = []
for cst in Constituenta.objects.filter(schema=schema).order_by('order'):
for cst in Constituenta.objects.filter(schema=schema).exclude(cst_type=CstType.NOMINAL).order_by('order'):
items.append(
{
'entityUID': cst.pk,
'type': _CST_TYPE,
'type': _ENTITY_CONSTITUENTA,
'cstType': cst.cst_type,
'alias': cst.alias,
'convention': cst.convention,
@ -53,7 +53,7 @@ def generate_trs(schema: LibraryItem) -> dict:
}
)
return {
'type': _TRS_TYPE,
'type': _ENTITY_SCHEMA,
'title': schema.title,
'alias': schema.alias,
'comment': schema.description,
@ -72,7 +72,7 @@ class RSFormTRSSerializer(serializers.Serializer):
def load_versioned_data(data: dict) -> dict:
''' Load data from version. '''
result = {
'type': _TRS_TYPE,
'type': _ENTITY_SCHEMA,
'title': data['title'],
'alias': data['alias'],
'comment': data['description'],
@ -85,7 +85,7 @@ class RSFormTRSSerializer(serializers.Serializer):
for cst in data['items']:
result['items'].append({
'entityUID': cst['id'],
'type': _CST_TYPE,
'type': _ENTITY_CONSTITUENTA,
'cstType': cst['cst_type'],
'alias': cst['alias'],
'convention': cst['convention'],

View File

@ -1,4 +1,5 @@
''' Tests for Django Models. '''
from .t_Association import *
from .t_Constituenta import *
from .t_RSForm import *
from .t_RSFormCached import *

View File

@ -0,0 +1,175 @@
''' Testing models: Association. '''
from django.db.utils import IntegrityError
from django.test import TestCase
from apps.rsform.models import Association, Constituenta, CstType, RSForm
class TestAssociation(TestCase):
''' Testing Association model. '''
def setUp(self):
self.schema = RSForm.create(title='Test1')
# Create test constituents
self.container1 = Constituenta.objects.create(
alias='C1',
schema=self.schema.model,
order=1,
cst_type=CstType.NOMINAL
)
self.associate1 = Constituenta.objects.create(
alias='A1',
schema=self.schema.model,
order=2,
cst_type=CstType.BASE
)
def test_str(self):
''' Test string representation. '''
association = Association.objects.create(
container=self.container1,
associate=self.associate1
)
expected_str = f'{self.container1} -> {self.associate1}'
self.assertEqual(str(association), expected_str)
def test_create_association(self):
''' Test basic association creation. '''
association = Association.objects.create(
container=self.container1,
associate=self.associate1
)
self.assertEqual(association.container, self.container1)
self.assertEqual(association.associate, self.associate1)
self.assertIsNotNone(association.id)
def test_unique_constraint(self):
''' Test unique constraint on container and associate. '''
# Create first association
Association.objects.create(
container=self.container1,
associate=self.associate1
)
# Try to create duplicate association
with self.assertRaises(IntegrityError):
Association.objects.create(
container=self.container1,
associate=self.associate1
)
def test_container_not_null(self):
''' Test container field cannot be null. '''
with self.assertRaises(IntegrityError):
Association.objects.create(
container=None,
associate=self.associate1
)
def test_associate_not_null(self):
''' Test associate field cannot be null. '''
with self.assertRaises(IntegrityError):
Association.objects.create(
container=self.container1,
associate=None
)
def test_cascade_delete_container(self):
''' Test cascade delete when container is deleted. '''
association = Association.objects.create(
container=self.container1,
associate=self.associate1
)
association_id = association.id
# Delete the container
self.container1.delete()
# Association should be deleted due to CASCADE
with self.assertRaises(Association.DoesNotExist):
Association.objects.get(id=association_id)
def test_cascade_delete_associate(self):
''' Test cascade delete when associate is deleted. '''
association = Association.objects.create(
container=self.container1,
associate=self.associate1
)
association_id = association.id
# Delete the associate
self.associate1.delete()
# Association should be deleted due to CASCADE
with self.assertRaises(Association.DoesNotExist):
Association.objects.get(id=association_id)
def test_related_names(self):
''' Test related names for foreign key relationships. '''
association = Association.objects.create(
container=self.container1,
associate=self.associate1
)
# Test container related name
container_associations = self.container1.as_container.all()
self.assertEqual(len(container_associations), 1)
self.assertEqual(container_associations[0], association)
# Test associate related name
associate_associations = self.associate1.as_associate.all()
self.assertEqual(len(associate_associations), 1)
self.assertEqual(associate_associations[0], association)
def test_multiple_associations_same_container(self):
''' Test multiple associations with same container. '''
associate3 = Constituenta.objects.create(
alias='A3',
schema=self.schema.model,
order=3,
cst_type=CstType.BASE
)
association1 = Association.objects.create(
container=self.container1,
associate=self.associate1
)
association2 = Association.objects.create(
container=self.container1,
associate=associate3
)
container_associations = self.container1.as_container.all()
self.assertEqual(len(container_associations), 2)
self.assertIn(association1, container_associations)
self.assertIn(association2, container_associations)
def test_multiple_associations_same_associate(self):
''' Test multiple associations with same associate. '''
container3 = Constituenta.objects.create(
alias='C3',
schema=self.schema.model,
order=3,
cst_type=CstType.NOMINAL
)
association1 = Association.objects.create(
container=self.container1,
associate=self.associate1
)
association2 = Association.objects.create(
container=container3,
associate=self.associate1
)
associate_associations = self.associate1.as_associate.all()
self.assertEqual(len(associate_associations), 2)
self.assertIn(association1, associate_associations)
self.assertIn(association2, associate_associations)
def test_meta_unique_together(self):
''' Test Meta class unique_together constraint. '''
unique_together = Association._meta.unique_together
self.assertEqual(len(unique_together), 1)
self.assertIn(('container', 'associate'), unique_together)

View File

@ -13,7 +13,7 @@ export const parser = LRParser.deserialize({
skippedNodes: [0],
repeatNodeCount: 2,
tokenData:
".V~R!UOX$eXZ%VZ^%z^p$epq%Vqr'sr|$e|}'x}!O(f!O!Q$e!Q!R)i!R![*i![!b$e!b!c+k!c!d+v!d!e)i!e!f+v!f!g+v!g!h)i!h!i+v!i!r)i!r!s+v!s!t)i!t!u+v!u!v+v!v!w+v!w!z)i!z!{+v!{!})i!}#T$e#T#o)i#p#q-{#q#r.Q#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$eP$jVgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eP%SP;=`<%l$e~%[Y^~X^%Vpq%V#y#z%V$f$g%V#BY#BZ%V$IS$I_%V$I|$JO%V$JT$JU%V$KV$KW%V&FU&FV%V~&RjgP^~OX$eXZ%VZ^%z^p$epq%Vr!b$e!c#o$e#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$e~'xOh~R(PVfQgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eV(m^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eT)p]eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![)i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV*r]UQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~+nP#o#p+q~+vOb~V+}^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV-S]RQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~.QOc~~.VOa~",
".]~R!WOX$kXZ%]Z^&Q^p$kpq%]qr'yr|$k|}(O}!O(l!O!Q$k!Q!R)o!R![*o![!b$k!b!c+q!c!d+|!d!e)o!e!f+|!f!g+|!g!h)o!h!i+|!i!p)o!p!q+|!q!r)o!r!s+|!s!t)o!t!u+|!u!v+|!v!w+|!w!z)o!z!{+|!{!})o!}#T$k#T#o)o#p#q.R#q#r.W#r#y$k#y#z&Q#z$f$k$f$g&Q$g#BY$k#BY#BZ&Q#BZ$IS$k$IS$I_&Q$I_$I|$k$I|$JO&Q$JO$JT$k$JT$JU&Q$JU$KV$k$KV$KW&Q$KW&FU$k&FU&FV&Q&FV;'S$k;'S;=`%V<%lO$kP$pVgPOX$kZp$kr!b$k!c#o$k#r;'S$k;'S;=`%V<%lO$kP%YP;=`<%l$k~%bY^~X^%]pq%]#y#z%]$f$g%]#BY#BZ%]$IS$I_%]$I|$JO%]$JT$JU%]$KV$KW%]&FU&FV%]~&XjgP^~OX$kXZ%]Z^&Q^p$kpq%]r!b$k!c#o$k#r#y$k#y#z&Q#z$f$k$f$g&Q$g#BY$k#BY#BZ&Q#BZ$IS$k$IS$I_&Q$I_$I|$k$I|$JO&Q$JO$JT$k$JT$JU&Q$JU$KV$k$KV$KW&Q$KW&FU$k&FU&FV&Q&FV;'S$k;'S;=`%V<%lO$k~(OOh~R(VVfQgPOX$kZp$kr!b$k!c#o$k#r;'S$k;'S;=`%V<%lO$kV(s^eSgPOX$kZp$kr}$k}!O)o!O!Q$k!Q!R)o!R![*o![!b$k!c!})o!}#T$k#T#o)o#r;'S$k;'S;=`%V<%lO$kT)v]eSgPOX$kZp$kr}$k}!O)o!O!Q$k!Q![)o![!b$k!c!})o!}#T$k#T#o)o#r;'S$k;'S;=`%V<%lO$kV*x]UQeSgPOX$kZp$kr}$k}!O)o!O!Q$k!Q![*o![!b$k!c!})o!}#T$k#T#o)o#r;'S$k;'S;=`%V<%lO$k~+tP#o#p+w~+|Ob~V,T^eSgPOX$kZp$kr}$k}!O)o!O!Q$k!Q!R)o!R![-P![!b$k!c!})o!}#T$k#T#o)o#r;'S$k;'S;=`%V<%lO$kV-Y]RQeSgPOX$kZp$kr}$k}!O)o!O!Q$k!Q![-P![!b$k!c!})o!}#T$k#T#o)o#r;'S$k;'S;=`%V<%lO$k~.WOc~~.]Oa~",
tokenizers: [0, 1, 2],
topRules: { Text: [0, 1] },
tokenPrec: 96

View File

@ -13,7 +13,7 @@
space { @whitespace+ }
Offset { $[-]?$[1-9]$[0-9]* }
Global { $[XCSDATFPR]$[1-9]$[0-9]* }
Global { $[XCSDATFPRN]$[1-9]$[0-9]* }
word { ![@{|}! \t\n]+ }

View File

@ -17,7 +17,7 @@ export const parser = LRParser.deserialize({
skippedNodes: [0],
repeatNodeCount: 0,
tokenData:
"6g~R!iX^%ppq%pvw&exy&jyz&oz{&t{|&y|}'O}!O'T!Q!['Y![!]'b!]!^'u!^!_'z!_!`(P!`!a(U!c!d(Z!e!f(Z!f!g(i!h!i(q!k!l)e!r!s)j!t!u*^!u!v(Z!v!w(Z!z!{(Z!|!}*n!}#O*s#O#P*x#P#Q*}#R#S+S#T#U+S#U#V+j#V#W-Y#W#X.u#X#d+S#d#e1_#e#f+S#f#g2t#g#o+S#o#p4O#p#q4T#q#r4Y#y#z%p$f$g%p$r$s4_%o%p4d5i6S+S#BY#BZ%p$IS$I_%p$I|$JO%p$JT$JU%p$KV$KW%p% l% m4i%%Y%%Z4n%%[%%]4s%&Y%&Z4x%&]%&^4}%&_%&`5S%&`%&a5X%&b%&c5^%&c%&d5c%'S%'T5h%'T%'U5m%'U%'V5r%(^%(_5w%(b%(c5|%(c%(d6R%)Q%)R6W%)S%)T6]%)U%)V6b&FU&FV%p~%uY!x~X^%ppq%p#y#z%p$f$g%p#BY#BZ%p$IS$I_%p$I|$JO%p$JT$JU%p$KV$KW%p&FU&FV%p~&jO!r~~&oOV~~&tOU~~&yOc~~'OOd~~'TO!Y~~'YOe~~'_P]~!Q!['Y~'eQ!_!`'k%&b%&c'p~'pO!P~~'uO!Z~~'zO|~~(PO!b~~(UO!e~~(ZO!`~~(^P!Q![(a~(fP`~!Q![(a~(nPx~!Q![(a~(tQ!Q![(z#]#^)S~)PP!Q~!Q![(z~)VP!R![)Y~)_Qr~|})S!Q![)Y~)jOy~~)mQ!Q![)s#f#g){~)xP!i~!Q![)s~*OP!R![*R~*WQ!S~|}){!Q![*R~*cP}~!Q![*f~*kPa~!Q![*f~*sO_~~*xOs~~*}Og~~+SOt~~+XRT~!Q![+b#T#o+S5i6S+S~+gPT~!Q![+b~+oTT~!Q![+b#T#c+S#c#d,O#d#o+S5i6S+S~,TTT~!Q![+b#T#c+S#c#d,d#d#o+S5i6S+S~,iTT~!Q![+b#T#`+S#`#a,x#a#o+S5i6S+S~-PR!V~T~!Q![+b#T#o+S5i6S+S~-_ST~!Q![+b#T#U-k#U#o+S5i6S+S~-pTT~!Q![+b#T#f+S#f#g.P#g#o+S5i6S+S~.UTT~!Q![+b#T#W+S#W#X.e#X#o+S5i6S+S~.lR!U~T~!Q![+b#T#o+S5i6S+S~.zTT~!Q![+b#T#X+S#X#Y/Z#Y#o+S5i6S+S~/`TT~!Q![+b#T#U+S#U#V/o#V#o+S5i6S+S~/tTT~!Q![+b#T#c+S#c#d0T#d#o+S5i6S+S~0YTT~!Q![+b#T#c+S#c#d0i#d#o+S5i6S+S~0nTT~!Q![+b#T#`+S#`#a0}#a#o+S5i6S+S~1UR!W~T~!Q![+b#T#o+S5i6S+S~1dTT~!Q![+b#T#f+S#f#g1s#g#o+S5i6S+S~1xST~!Q!R+b!R![2U#T#o+S5i6S+S~2]Q!T~T~|}2c!Q![2U~2fP!R![2i~2nQ!T~|}2c!Q![2i~2yTT~!Q![+b#T#X+S#X#Y3Y#Y#o+S5i6S+S~3_TT~!Q![+b#T#W+S#W#X3n#X#o+S5i6S+S~3uR!X~T~!Q![+b#T#o+S5i6S+S~4TOm~~4YOw~~4_Ol~~4dO!h~~4iOj~~4nOp~~4sO!p~~4xO!o~~4}O!k~~5SO!m~~5XO^~~5^Oh~~5cOv~~5hO![~~5mO!q~~5rOi~~5wOf~~5|O!d~~6RO!c~~6WO!a~~6]O!_~~6bO!^~~6gO!]~",
"6j~R!jX^%spq%svw&hxy&myz&rz{&w{|&||}'R}!O'W!Q![']![!]'e!]!^'x!^!_'}!_!`(S!`!a(X!c!d(^!e!f(^!f!g(l!h!i(t!k!l)h!p!q(^!r!s)m!t!u*a!u!v(^!v!w(^!z!{(^!|!}*q!}#O*v#O#P*{#P#Q+Q#R#S+V#T#U+V#U#V+m#V#W-]#W#X.x#X#d+V#d#e1b#e#f+V#f#g2w#g#o+V#o#p4R#p#q4W#q#r4]#y#z%s$f$g%s$r$s4b%o%p4g5i6S+V#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s% l% m4l%%Y%%Z4q%%[%%]4v%&Y%&Z4{%&]%&^5Q%&_%&`5V%&`%&a5[%&b%&c5a%&c%&d5f%'S%'T5k%'T%'U5p%'U%'V5u%(^%(_5z%(b%(c6P%(c%(d6U%)Q%)R6Z%)S%)T6`%)U%)V6e&FU&FV%s~%xY!x~X^%spq%s#y#z%s$f$g%s#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s&FU&FV%s~&mO!r~~&rOV~~&wOU~~&|Oc~~'ROd~~'WO!Y~~']Oe~~'bP]~!Q![']~'hQ!_!`'n%&b%&c's~'sO!P~~'xO!Z~~'}O|~~(SO!b~~(XO!e~~(^O!`~~(aP!Q![(d~(iP`~!Q![(d~(qPx~!Q![(d~(wQ!Q![(}#]#^)V~)SP!Q~!Q![(}~)YP!R![)]~)bQr~|})V!Q![)]~)mOy~~)pQ!Q![)v#f#g*O~){P!i~!Q![)v~*RP!R![*U~*ZQ!S~|}*O!Q![*U~*fP}~!Q![*i~*nPa~!Q![*i~*vO_~~*{Os~~+QOg~~+VOt~~+[RT~!Q![+e#T#o+V5i6S+V~+jPT~!Q![+e~+rTT~!Q![+e#T#c+V#c#d,R#d#o+V5i6S+V~,WTT~!Q![+e#T#c+V#c#d,g#d#o+V5i6S+V~,lTT~!Q![+e#T#`+V#`#a,{#a#o+V5i6S+V~-SR!V~T~!Q![+e#T#o+V5i6S+V~-bST~!Q![+e#T#U-n#U#o+V5i6S+V~-sTT~!Q![+e#T#f+V#f#g.S#g#o+V5i6S+V~.XTT~!Q![+e#T#W+V#W#X.h#X#o+V5i6S+V~.oR!U~T~!Q![+e#T#o+V5i6S+V~.}TT~!Q![+e#T#X+V#X#Y/^#Y#o+V5i6S+V~/cTT~!Q![+e#T#U+V#U#V/r#V#o+V5i6S+V~/wTT~!Q![+e#T#c+V#c#d0W#d#o+V5i6S+V~0]TT~!Q![+e#T#c+V#c#d0l#d#o+V5i6S+V~0qTT~!Q![+e#T#`+V#`#a1Q#a#o+V5i6S+V~1XR!W~T~!Q![+e#T#o+V5i6S+V~1gTT~!Q![+e#T#f+V#f#g1v#g#o+V5i6S+V~1{ST~!Q!R+e!R![2X#T#o+V5i6S+V~2`Q!T~T~|}2f!Q![2X~2iP!R![2l~2qQ!T~|}2f!Q![2l~2|TT~!Q![+e#T#X+V#X#Y3]#Y#o+V5i6S+V~3bTT~!Q![+e#T#W+V#W#X3q#X#o+V5i6S+V~3xR!X~T~!Q![+e#T#o+V5i6S+V~4WOm~~4]Ow~~4bOl~~4gO!h~~4lOj~~4qOp~~4vO!p~~4{O!o~~5QO!k~~5VO!m~~5[O^~~5aOh~~5fOv~~5kO![~~5pO!q~~5uOi~~5zOf~~6PO!d~~6UO!c~~6ZO!a~~6`O!_~~6eO!^~~6jO!]~",
tokenizers: [0],
topRules: { Expression: [0, 1] },
tokenPrec: 1739

View File

@ -15,7 +15,7 @@ export const parser = LRParser.deserialize({
skippedNodes: [0],
repeatNodeCount: 0,
tokenData:
"6P~R!jX^%spq%svw&hxy&myz&rz{&w{|&||}'R}!O'W!Q!R']!R!['e![!](T!]!^(h!^!_(m!_!`(r!`!a(w!c!d(|!e!f(|!f!g)[!h!i)d!k!l)z!r!s*P!t!u*g!u!v(|!v!w(|!z!{(|!|!}*w!}#O*|#O#P+R#P#Q+W#R#S+]#T#U+]#U#V+s#V#W-c#W#X/O#X#d+]#d#e1h#e#f+]#f#g2^#g#o+]#o#p3h#p#q3m#q#r3r#y#z%s$f$g%s$r$s3w%o%p3|5i6S+]#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s% l% m4R%%Y%%Z4W%%[%%]4]%&Y%&Z4b%&]%&^4g%&_%&`4l%&`%&a4q%&b%&c4v%&c%&d4{%'S%'T5Q%'T%'U5V%'U%'V5[%(^%(_5a%(b%(c5f%(c%(d5k%)Q%)R5p%)S%)T5u%)U%)V5z&FU&FV%s~%xY!V~X^%spq%s#y#z%s$f$g%s#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s&FU&FV%s~&mO!S~~&rOR~~&wO_~~&|OV~~'ROW~~'WO!s~~']OX~P'bP!`P!Q![']R'lQdQ!`P|}'r!Q!['eQ'uP!R!['xQ'}QdQ|}'r!Q!['x~(WQ!_!`(^%&b%&c(c~(cOm~~(hOp~~(mOk~~(rOw~~(wOz~~(|Ou~~)PP!Q![)S~)XPT~!Q![)S~)aPi~!Q![)S~)gQ!Q![)m#]#^)u~)rPn~!Q![)m~)zOc~~*POj~~*SQ!Q![*Y#f#g*b~*_P|~!Q![*Y~*gO!m~~*lPl~!Q![*o~*tPU~!Q![*o~*|O!b~~+ROe~~+WOZ~~+]Of~~+bRQ~!Q![+k#T#o+]5i6S+]~+pPQ~!Q![+k~+xTQ~!Q![+k#T#c+]#c#d,X#d#o+]5i6S+]~,^TQ~!Q![+k#T#c+]#c#d,m#d#o+]5i6S+]~,rTQ~!Q![+k#T#`+]#`#a-R#a#o+]5i6S+]~-YR!p~Q~!Q![+k#T#o+]5i6S+]~-hSQ~!Q![+k#T#U-t#U#o+]5i6S+]~-yTQ~!Q![+k#T#f+]#f#g.Y#g#o+]5i6S+]~._TQ~!Q![+k#T#W+]#W#X.n#X#o+]5i6S+]~.uR!o~Q~!Q![+k#T#o+]5i6S+]~/TTQ~!Q![+k#T#X+]#X#Y/d#Y#o+]5i6S+]~/iTQ~!Q![+k#T#U+]#U#V/x#V#o+]5i6S+]~/}TQ~!Q![+k#T#c+]#c#d0^#d#o+]5i6S+]~0cTQ~!Q![+k#T#c+]#c#d0r#d#o+]5i6S+]~0wTQ~!Q![+k#T#`+]#`#a1W#a#o+]5i6S+]~1_R!q~Q~!Q![+k#T#o+]5i6S+]~1mTQ~!Q![+k#T#f+]#f#g1|#g#o+]5i6S+]~2TR!n~Q~!Q![+k#T#o+]5i6S+]~2cTQ~!Q![+k#T#X+]#X#Y2r#Y#o+]5i6S+]~2wTQ~!Q![+k#T#W+]#W#X3W#X#o+]5i6S+]~3_R!r~Q~!Q![+k#T#o+]5i6S+]~3mO`~~3rOh~~3wOa~~3|O{~~4RO^~~4WOb~~4]O!Q~~4bO!P~~4gO}~~4lO!O~~4qO!a~~4vO[~~4{Og~~5QOq~~5VO!R~~5[O]~~5aOY~~5fOy~~5kOx~~5pOv~~5uOt~~5zOs~~6POr~",
"6S~R!kX^%vpq%vvw&kxy&pyz&uz{&z{|'P|}'U}!O'Z!Q!R'`!R!['h![!](W!]!^(k!^!_(p!_!`(u!`!a(z!c!d)P!e!f)P!f!g)_!h!i)g!k!l)}!p!q)P!r!s*S!t!u*j!u!v)P!v!w)P!z!{)P!|!}*z!}#O+P#O#P+U#P#Q+Z#R#S+`#T#U+`#U#V+v#V#W-f#W#X/R#X#d+`#d#e1k#e#f+`#f#g2a#g#o+`#o#p3k#p#q3p#q#r3u#y#z%v$f$g%v$r$s3z%o%p4P5i6S+`#BY#BZ%v$IS$I_%v$I|$JO%v$JT$JU%v$KV$KW%v% l% m4U%%Y%%Z4Z%%[%%]4`%&Y%&Z4e%&]%&^4j%&_%&`4o%&`%&a4t%&b%&c4y%&c%&d5O%'S%'T5T%'T%'U5Y%'U%'V5_%(^%(_5d%(b%(c5i%(c%(d5n%)Q%)R5s%)S%)T5x%)U%)V5}&FU&FV%v~%{Y!V~X^%vpq%v#y#z%v$f$g%v#BY#BZ%v$IS$I_%v$I|$JO%v$JT$JU%v$KV$KW%v&FU&FV%v~&pO!S~~&uOR~~&zO_~~'POV~~'UOW~~'ZO!s~~'`OX~P'eP!`P!Q!['`R'oQdQ!`P|}'u!Q!['hQ'xP!R!['{Q(QQdQ|}'u!Q!['{~(ZQ!_!`(a%&b%&c(f~(fOm~~(kOp~~(pOk~~(uOw~~(zOz~~)POu~~)SP!Q![)V~)[PT~!Q![)V~)dPi~!Q![)V~)jQ!Q![)p#]#^)x~)uPn~!Q![)p~)}Oc~~*SOj~~*VQ!Q![*]#f#g*e~*bP|~!Q![*]~*jO!m~~*oPl~!Q![*r~*wPU~!Q![*r~+PO!b~~+UOe~~+ZOZ~~+`Of~~+eRQ~!Q![+n#T#o+`5i6S+`~+sPQ~!Q![+n~+{TQ~!Q![+n#T#c+`#c#d,[#d#o+`5i6S+`~,aTQ~!Q![+n#T#c+`#c#d,p#d#o+`5i6S+`~,uTQ~!Q![+n#T#`+`#`#a-U#a#o+`5i6S+`~-]R!p~Q~!Q![+n#T#o+`5i6S+`~-kSQ~!Q![+n#T#U-w#U#o+`5i6S+`~-|TQ~!Q![+n#T#f+`#f#g.]#g#o+`5i6S+`~.bTQ~!Q![+n#T#W+`#W#X.q#X#o+`5i6S+`~.xR!o~Q~!Q![+n#T#o+`5i6S+`~/WTQ~!Q![+n#T#X+`#X#Y/g#Y#o+`5i6S+`~/lTQ~!Q![+n#T#U+`#U#V/{#V#o+`5i6S+`~0QTQ~!Q![+n#T#c+`#c#d0a#d#o+`5i6S+`~0fTQ~!Q![+n#T#c+`#c#d0u#d#o+`5i6S+`~0zTQ~!Q![+n#T#`+`#`#a1Z#a#o+`5i6S+`~1bR!q~Q~!Q![+n#T#o+`5i6S+`~1pTQ~!Q![+n#T#f+`#f#g2P#g#o+`5i6S+`~2WR!n~Q~!Q![+n#T#o+`5i6S+`~2fTQ~!Q![+n#T#X+`#X#Y2u#Y#o+`5i6S+`~2zTQ~!Q![+n#T#W+`#W#X3Z#X#o+`5i6S+`~3bR!r~Q~!Q![+n#T#o+`5i6S+`~3pO`~~3uOh~~3zOa~~4PO{~~4UO^~~4ZOb~~4`O!Q~~4eO!P~~4jO}~~4oO!O~~4tO!a~~4yO[~~5OOg~~5TOq~~5YO!R~~5_O]~~5dOY~~5iOy~~5nOx~~5sOv~~5xOt~~5}Os~~6SOr~",
tokenizers: [0, 1],
topRules: { Expression: [0, 1] },
tokenPrec: 1749

View File

@ -40,7 +40,7 @@
Debool { "debool" }
Red { "red" }
Global { $[XCSDAT]$[0-9]+ }
Global { $[XCSDATN]$[0-9]+ }
Function { "F"$[0-9]+ }
Predicate { "P"$[0-9]+ }
Radical { "R"$[0-9]+ }

View File

@ -42,7 +42,7 @@
debool { "debool" }
red { "red" }
Global { $[XCSDAT]$[0-9]+ }
Global { $[XCSDATN]$[0-9]+ }
Function { "F"$[0-9]+ }
Predicate { "P"$[0-9]+ }
Radical { "R"$[0-9]+ }

View File

@ -16,7 +16,7 @@ import { type AliasMapping, type IArgumentValue, RSErrorClass, type SyntaxTree }
// cspell:disable
const LOCALS_REGEXP = /[_a-zα-ω][a-zα-ω]*\d*/g;
const GLOBALS_REGEXP = /[XCSADFPT]\d+/g;
const GLOBALS_REGEXP = /[XCSADFPTN]\d+/g;
const COMPLEX_SYMBOLS_REGEXP = /[∀∃×ℬ;|:]/g;
const TYPIFICATION_SET = /^+\([\(X\d+\)×]*\)$/g;
// cspell:enable