From 4742d241c358d90c6b67cf2c052de17f01a86042 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Sun, 10 Aug 2025 12:28:03 +0300 Subject: [PATCH] F: Implementing Association and Nominal pt1 --- .vscode/settings.json | 1 + .../backend/apps/library/views/library.py | 2 +- .../apps/oss/models/OperationSchemaCached.py | 2 +- .../apps/oss/serializers/data_access.py | 4 +- rsconcept/backend/apps/rsform/admin.py | 8 + ...alter_constituenta_cst_type_association.py | 32 ++++ .../backend/apps/rsform/models/Association.py | 28 +++ .../apps/rsform/models/Constituenta.py | 5 +- .../backend/apps/rsform/models/__init__.py | 1 + .../apps/rsform/models/api_RSLanguage.py | 1 + .../backend/apps/rsform/serializers/basics.py | 2 +- .../apps/rsform/serializers/data_access.py | 81 +++++--- .../apps/rsform/serializers/io_files.py | 16 +- .../apps/rsform/tests/s_models/__init__.py | 1 + .../rsform/tests/s_models/t_Association.py | 175 ++++++++++++++++++ .../components/refs-input/parse/parser.ts | 2 +- .../refs-input/parse/refs-text.grammar | 2 +- .../components/rs-input/rslang/parser-ast.ts | 2 +- .../components/rs-input/rslang/parser.ts | 2 +- .../rs-input/rslang/rslang-ast.grammar | 2 +- .../rs-input/rslang/rslang-fast.grammar | 2 +- .../src/features/rsform/models/rslang-api.ts | 2 +- 22 files changed, 328 insertions(+), 45 deletions(-) create mode 100644 rsconcept/backend/apps/rsform/migrations/0005_alter_constituenta_cst_type_association.py create mode 100644 rsconcept/backend/apps/rsform/models/Association.py create mode 100644 rsconcept/backend/apps/rsform/tests/s_models/t_Association.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 709589a5..1885f80d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -179,6 +179,7 @@ "viewsets", "wordform", "Wordforms", + "XCSDATN", "Айзенштат", "Акименков", "Астрина", diff --git a/rsconcept/backend/apps/library/views/library.py b/rsconcept/backend/apps/library/views/library.py index 747733c1..730d5418 100644 --- a/rsconcept/backend/apps/library/views/library.py +++ b/rsconcept/backend/apps/library/views/library.py @@ -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( diff --git a/rsconcept/backend/apps/oss/models/OperationSchemaCached.py b/rsconcept/backend/apps/oss/models/OperationSchemaCached.py index 433ca611..b50f1e22 100644 --- a/rsconcept/backend/apps/oss/models/OperationSchemaCached.py +++ b/rsconcept/backend/apps/oss/models/OperationSchemaCached.py @@ -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) diff --git a/rsconcept/backend/apps/oss/serializers/data_access.py b/rsconcept/backend/apps/oss/serializers/data_access.py index 97b431f6..66cc7e4b 100644 --- a/rsconcept/backend/apps/oss/serializers/data_access.py +++ b/rsconcept/backend/apps/oss/serializers/data_access.py @@ -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 diff --git a/rsconcept/backend/apps/rsform/admin.py b/rsconcept/backend/apps/rsform/admin.py index ec4759fb..b0c0c09f 100644 --- a/rsconcept/backend/apps/rsform/admin.py +++ b/rsconcept/backend/apps/rsform/admin.py @@ -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'] diff --git a/rsconcept/backend/apps/rsform/migrations/0005_alter_constituenta_cst_type_association.py b/rsconcept/backend/apps/rsform/migrations/0005_alter_constituenta_cst_type_association.py new file mode 100644 index 00000000..c37b299d --- /dev/null +++ b/rsconcept/backend/apps/rsform/migrations/0005_alter_constituenta_cst_type_association.py @@ -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')}, + }, + ), + ] diff --git a/rsconcept/backend/apps/rsform/models/Association.py b/rsconcept/backend/apps/rsform/models/Association.py new file mode 100644 index 00000000..f813d4bd --- /dev/null +++ b/rsconcept/backend/apps/rsform/models/Association.py @@ -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}' diff --git a/rsconcept/backend/apps/rsform/models/Constituenta.py b/rsconcept/backend/apps/rsform/models/Constituenta.py index c845cd2a..cfee9332 100644 --- a/rsconcept/backend/apps/rsform/models/Constituenta.py +++ b/rsconcept/backend/apps/rsform/models/Constituenta.py @@ -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' diff --git a/rsconcept/backend/apps/rsform/models/__init__.py b/rsconcept/backend/apps/rsform/models/__init__.py index bffec1b2..25c20890 100644 --- a/rsconcept/backend/apps/rsform/models/__init__.py +++ b/rsconcept/backend/apps/rsform/models/__init__.py @@ -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 diff --git a/rsconcept/backend/apps/rsform/models/api_RSLanguage.py b/rsconcept/backend/apps/rsform/models/api_RSLanguage.py index 32b802d7..2ed621fe 100644 --- a/rsconcept/backend/apps/rsform/models/api_RSLanguage.py +++ b/rsconcept/backend/apps/rsform/models/api_RSLanguage.py @@ -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' diff --git a/rsconcept/backend/apps/rsform/serializers/basics.py b/rsconcept/backend/apps/rsform/serializers/basics.py index 265e107c..1c826645 100644 --- a/rsconcept/backend/apps/rsform/serializers/basics.py +++ b/rsconcept/backend/apps/rsform/serializers/basics.py @@ -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 diff --git a/rsconcept/backend/apps/rsform/serializers/data_access.py b/rsconcept/backend/apps/rsform/serializers/data_access.py index b8bd3056..3e086595 100644 --- a/rsconcept/backend/apps/rsform/serializers/data_access.py +++ b/rsconcept/backend/apps/rsform/serializers/data_access.py @@ -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. ''' diff --git a/rsconcept/backend/apps/rsform/serializers/io_files.py b/rsconcept/backend/apps/rsform/serializers/io_files.py index a56458d5..0b3ad047 100644 --- a/rsconcept/backend/apps/rsform/serializers/io_files.py +++ b/rsconcept/backend/apps/rsform/serializers/io_files.py @@ -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'], diff --git a/rsconcept/backend/apps/rsform/tests/s_models/__init__.py b/rsconcept/backend/apps/rsform/tests/s_models/__init__.py index a1729720..d90d7046 100644 --- a/rsconcept/backend/apps/rsform/tests/s_models/__init__.py +++ b/rsconcept/backend/apps/rsform/tests/s_models/__init__.py @@ -1,4 +1,5 @@ ''' Tests for Django Models. ''' +from .t_Association import * from .t_Constituenta import * from .t_RSForm import * from .t_RSFormCached import * diff --git a/rsconcept/backend/apps/rsform/tests/s_models/t_Association.py b/rsconcept/backend/apps/rsform/tests/s_models/t_Association.py new file mode 100644 index 00000000..69acc3bf --- /dev/null +++ b/rsconcept/backend/apps/rsform/tests/s_models/t_Association.py @@ -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) diff --git a/rsconcept/frontend/src/features/rsform/components/refs-input/parse/parser.ts b/rsconcept/frontend/src/features/rsform/components/refs-input/parse/parser.ts index 0aab7c64..fe1e89b3 100644 --- a/rsconcept/frontend/src/features/rsform/components/refs-input/parse/parser.ts +++ b/rsconcept/frontend/src/features/rsform/components/refs-input/parse/parser.ts @@ -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 diff --git a/rsconcept/frontend/src/features/rsform/components/refs-input/parse/refs-text.grammar b/rsconcept/frontend/src/features/rsform/components/refs-input/parse/refs-text.grammar index d1ecadf4..e43d47fc 100644 --- a/rsconcept/frontend/src/features/rsform/components/refs-input/parse/refs-text.grammar +++ b/rsconcept/frontend/src/features/rsform/components/refs-input/parse/refs-text.grammar @@ -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]+ } diff --git a/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/parser-ast.ts b/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/parser-ast.ts index bb33b3b3..526e3934 100644 --- a/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/parser-ast.ts +++ b/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/parser-ast.ts @@ -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 diff --git a/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/parser.ts b/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/parser.ts index 2e962dbf..e885f2c3 100644 --- a/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/parser.ts +++ b/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/parser.ts @@ -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 diff --git a/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/rslang-ast.grammar b/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/rslang-ast.grammar index 9a78e023..f9e66547 100644 --- a/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/rslang-ast.grammar +++ b/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/rslang-ast.grammar @@ -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]+ } diff --git a/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/rslang-fast.grammar b/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/rslang-fast.grammar index 6140ef76..7fcfcab1 100644 --- a/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/rslang-fast.grammar +++ b/rsconcept/frontend/src/features/rsform/components/rs-input/rslang/rslang-fast.grammar @@ -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]+ } diff --git a/rsconcept/frontend/src/features/rsform/models/rslang-api.ts b/rsconcept/frontend/src/features/rsform/models/rslang-api.ts index 1e1053f7..f4714614 100644 --- a/rsconcept/frontend/src/features/rsform/models/rslang-api.ts +++ b/rsconcept/frontend/src/features/rsform/models/rslang-api.ts @@ -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