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", "viewsets",
"wordform", "wordform",
"Wordforms", "Wordforms",
"XCSDATN",
"Айзенштат", "Айзенштат",
"Акименков", "Акименков",
"Астрина", "Астрина",

View File

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

View File

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

View File

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

View File

@ -10,3 +10,11 @@ class ConstituentaAdmin(admin.ModelAdmin):
ordering = ['schema', 'order'] ordering = ['schema', 'order']
list_display = ['schema', 'order', 'alias', 'term_resolved', 'definition_resolved', 'crucial'] list_display = ['schema', 'order', 'alias', 'term_resolved', 'definition_resolved', 'crucial']
search_fields = ['term_resolved', 'definition_resolved'] 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 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\-].*?)\|.*?}') _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]: def extract_globals(expression: str) -> set[str]:
@ -38,6 +38,7 @@ def replace_entities(expression: str, mapping: dict[str, str]) -> str:
class CstType(TextChoices): class CstType(TextChoices):
''' Type of constituenta. ''' ''' Type of constituenta. '''
NOMINAL = 'nominal'
BASE = 'basic' BASE = 'basic'
CONSTANT = 'constant' CONSTANT = 'constant'
STRUCTURED = 'structure' STRUCTURED = 'structure'

View File

@ -1,5 +1,6 @@
''' Django: Models. ''' ''' Django: Models. '''
from .Association import Association
from .Constituenta import Constituenta, CstType, extract_globals, replace_entities, replace_globals from .Constituenta import Constituenta, CstType, extract_globals, replace_entities, replace_globals
from .OrderManager import OrderManager from .OrderManager import OrderManager
from .RSForm import DELETED_ALIAS, INSERT_LAST, RSForm 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.FUNCTION: return 'F'
case CstType.PREDICATE: return 'P' case CstType.PREDICATE: return 'P'
case CstType.THEOREM: return 'T' case CstType.THEOREM: return 'T'
case CstType.NOMINAL: return 'N'
return 'X' return 'X'

View File

@ -133,7 +133,7 @@ class ReferenceSerializer(StrictSerializer):
class InheritanceDataSerializer(StrictSerializer): class InheritanceDataSerializer(StrictSerializer):
''' Serializer: inheritance data. ''' ''' Serializer: Inheritance data. '''
child = serializers.IntegerField() child = serializers.IntegerField()
child_source = serializers.IntegerField() child_source = serializers.IntegerField()
parent = serializers.IntegerField() # type: ignore 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 import messages as msg
from shared.serializers import StrictModelSerializer, StrictSerializer 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 .basics import CstParseSerializer, InheritanceDataSerializer
from .io_pyconcept import PyConceptAdapter from .io_pyconcept import PyConceptAdapter
class AssociationSerializer(StrictModelSerializer):
''' Serializer: Association relation. '''
class Meta:
''' serializer metadata. '''
model = Association
fields = ('argument', 'operation')
class CstBaseSerializer(StrictModelSerializer): class CstBaseSerializer(StrictModelSerializer):
''' Serializer: Constituenta all data. ''' ''' Serializer: Constituenta all data. '''
class Meta: class Meta:
@ -134,6 +142,9 @@ class RSFormSerializer(StrictModelSerializer):
inheritance = serializers.ListField( inheritance = serializers.ListField(
child=InheritanceDataSerializer() child=InheritanceDataSerializer()
) )
association = serializers.ListField(
child=AssociationSerializer()
)
oss = serializers.ListField( oss = serializers.ListField(
child=LibraryItemReferenceSerializer() child=LibraryItemReferenceSerializer()
) )
@ -164,6 +175,7 @@ class RSFormSerializer(StrictModelSerializer):
result['items'] = [] result['items'] = []
result['oss'] = [] result['oss'] = []
result['inheritance'] = [] result['inheritance'] = []
result['association'] = []
for cst in Constituenta.objects.filter(schema=instance).defer('order').order_by('order'): for cst in Constituenta.objects.filter(schema=instance).defer('order').order_by('order'):
result['items'].append(CstInfoSerializer(cst).data) result['items'].append(CstInfoSerializer(cst).data)
for oss in LibraryItem.objects.filter(operations__result=instance).only('alias'): for oss in LibraryItem.objects.filter(operations__result=instance).only('alias'):
@ -171,6 +183,11 @@ class RSFormSerializer(StrictModelSerializer):
'id': oss.pk, 'id': oss.pk,
'alias': oss.alias '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 return result
def to_versioned_data(self) -> dict: def to_versioned_data(self) -> dict:
@ -200,37 +217,38 @@ class RSFormSerializer(StrictModelSerializer):
instance = cast(LibraryItem, self.instance) instance = cast(LibraryItem, self.instance)
schema = RSForm(instance) schema = RSForm(instance)
items: list[dict] = data['items'] items: list[dict] = data['items']
ids: list[int] = [item['id'] for item in items] stored_ids: list[int] = [item['id'] for item in items]
processed: list[int] = [] id_map: dict[int, int] = {}
for cst in schema.constituentsQ(): for existing_cst in schema.constituentsQ():
if not cst.pk in ids: if not existing_cst.pk in stored_ids:
cst.delete() existing_cst.delete()
else: 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 cst_data['schema'] = instance.pk
new_cst = CstBaseSerializer(data=cst_data) cst_serializer = CstBaseSerializer(data=cst_data)
new_cst.is_valid(raise_exception=True) cst_serializer.is_valid(raise_exception=True)
new_cst.validated_data['order'] = ids.index(cst.pk) cst_serializer.validated_data['order'] = stored_ids.index(existing_cst.pk)
new_cst.update( cst_serializer.update(
instance=cst, instance=existing_cst,
validated_data=new_cst.validated_data validated_data=cst_serializer.validated_data
) )
processed.append(cst.pk) id_map[cst_data['id']] = existing_cst.pk
for cst_data in items: for cst_data in items:
if cst_data['id'] not in processed: if cst_data['id'] not in id_map:
cst = schema.insert_last(cst_data['alias'])
old_id = cst_data['id'] 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 cst_data['schema'] = instance.pk
new_cst = CstBaseSerializer(data=cst_data) cst_serializer = CstBaseSerializer(data=cst_data)
new_cst.is_valid(raise_exception=True) cst_serializer.is_valid(raise_exception=True)
new_cst.validated_data['order'] = ids.index(old_id) cst_serializer.validated_data['order'] = stored_ids.index(old_id)
new_cst.update( cst_serializer.update(
instance=cst, instance=inserted_cst,
validated_data=new_cst.validated_data validated_data=cst_serializer.validated_data
) )
id_map[old_id] = inserted_cst.pk
loaded_item = LibraryItemBaseNonStrictSerializer(data=data) loaded_item = LibraryItemBaseNonStrictSerializer(data=data)
loaded_item.is_valid(raise_exception=True) loaded_item.is_valid(raise_exception=True)
@ -239,6 +257,23 @@ class RSFormSerializer(StrictModelSerializer):
validated_data=loaded_item.validated_data 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): class RSFormParseSerializer(StrictModelSerializer):
''' Serializer: Detailed data for RSForm including parse. ''' ''' 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 import messages as msg
from shared.serializers import StrictSerializer from shared.serializers import StrictSerializer
from ..models import Constituenta, RSFormCached from ..models import Constituenta, CstType, RSFormCached
from ..utils import fix_old_references from ..utils import fix_old_references
_CST_TYPE = 'constituenta' _ENTITY_CONSTITUENTA = 'constituenta'
_TRS_TYPE = 'rsform' _ENTITY_SCHEMA = 'rsform'
_TRS_VERSION_MIN = 16 _TRS_VERSION_MIN = 16
_TRS_VERSION = 16 _TRS_VERSION = 16
_TRS_HEADER = 'Exteor 4.8.13.1000 - 30/05/2022' _TRS_HEADER = 'Exteor 4.8.13.1000 - 30/05/2022'
@ -30,11 +30,11 @@ class RSFormUploadSerializer(StrictSerializer):
def generate_trs(schema: LibraryItem) -> dict: def generate_trs(schema: LibraryItem) -> dict:
''' Generate TRS file for RSForm. ''' ''' Generate TRS file for RSForm. '''
items = [] 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( items.append(
{ {
'entityUID': cst.pk, 'entityUID': cst.pk,
'type': _CST_TYPE, 'type': _ENTITY_CONSTITUENTA,
'cstType': cst.cst_type, 'cstType': cst.cst_type,
'alias': cst.alias, 'alias': cst.alias,
'convention': cst.convention, 'convention': cst.convention,
@ -53,7 +53,7 @@ def generate_trs(schema: LibraryItem) -> dict:
} }
) )
return { return {
'type': _TRS_TYPE, 'type': _ENTITY_SCHEMA,
'title': schema.title, 'title': schema.title,
'alias': schema.alias, 'alias': schema.alias,
'comment': schema.description, 'comment': schema.description,
@ -72,7 +72,7 @@ class RSFormTRSSerializer(serializers.Serializer):
def load_versioned_data(data: dict) -> dict: def load_versioned_data(data: dict) -> dict:
''' Load data from version. ''' ''' Load data from version. '''
result = { result = {
'type': _TRS_TYPE, 'type': _ENTITY_SCHEMA,
'title': data['title'], 'title': data['title'],
'alias': data['alias'], 'alias': data['alias'],
'comment': data['description'], 'comment': data['description'],
@ -85,7 +85,7 @@ class RSFormTRSSerializer(serializers.Serializer):
for cst in data['items']: for cst in data['items']:
result['items'].append({ result['items'].append({
'entityUID': cst['id'], 'entityUID': cst['id'],
'type': _CST_TYPE, 'type': _ENTITY_CONSTITUENTA,
'cstType': cst['cst_type'], 'cstType': cst['cst_type'],
'alias': cst['alias'], 'alias': cst['alias'],
'convention': cst['convention'], 'convention': cst['convention'],

View File

@ -1,4 +1,5 @@
''' Tests for Django Models. ''' ''' Tests for Django Models. '''
from .t_Association import *
from .t_Constituenta import * from .t_Constituenta import *
from .t_RSForm import * from .t_RSForm import *
from .t_RSFormCached 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], skippedNodes: [0],
repeatNodeCount: 2, repeatNodeCount: 2,
tokenData: 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], tokenizers: [0, 1, 2],
topRules: { Text: [0, 1] }, topRules: { Text: [0, 1] },
tokenPrec: 96 tokenPrec: 96

View File

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

View File

@ -17,7 +17,7 @@ export const parser = LRParser.deserialize({
skippedNodes: [0], skippedNodes: [0],
repeatNodeCount: 0, repeatNodeCount: 0,
tokenData: 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], tokenizers: [0],
topRules: { Expression: [0, 1] }, topRules: { Expression: [0, 1] },
tokenPrec: 1739 tokenPrec: 1739

View File

@ -15,7 +15,7 @@ export const parser = LRParser.deserialize({
skippedNodes: [0], skippedNodes: [0],
repeatNodeCount: 0, repeatNodeCount: 0,
tokenData: 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], tokenizers: [0, 1],
topRules: { Expression: [0, 1] }, topRules: { Expression: [0, 1] },
tokenPrec: 1749 tokenPrec: 1749

View File

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

View File

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

View File

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