Сколько раз при изобретении очередного метода обработки структурированных данных наталкиваешься на мысль о дежавю? Работа со списками файлов, словарями имен, объектными полями, связывание разнотипных данных. В каждом новом более удобном или более быстром переизобретении проглядывается что-то общее, непреходящее. Концептуальное ядро, связующее все возможные производные множества и включающее их в свою орбиту. Что-то чему язык затрудняется сходу подобрать название, а мозг очертить предельные границы. Одновременно всеобъемлющая и при этом неуловимо малая деталь. Абсолютная абстракция. Линейный примитив.

Ася и голые си

Лень — двигатель прогресса. Парадоксальное утверждение, частично приоткрывающее завесу тайны над тем, каким образом достигается высокоэффективный результат. Кто‑то скажет: механизмы, разработанные на высокоуровневом языке (типа С) обладают лаконичностью при написании рутинного кода, кроссплатформенностью, высокой степенью простоты и понятности и даже непревзойденной оптимизацией (при известных ограничениях). И обязательно найдутся те, кто возразят, что концептуальные разработки, внедрение инноваций и экзистенциальные изыскания за пределами неизвестного — это без всяких сомнений самое низкоуровневое программирование. Только ассемблер, только неограниченный доступ, только хардкор. Но я лично одно без другого не представляю. Задействовать Intrinsic обрабатывая дробные данные в регистре и при этом экономить обращения к памяти, вычленять из последовательности бит с помощью маски нужные значения векторной проекции и оценивать относительную разницу с предыдущим состоянием. Одна мысль об этом способна породить непреодолимую депрессию. Поэтому мой подход‑ это pure C для рутинных процедур, большей частью выполняющихся при загрузке программы и ассемблер для высокоприоритетной real‑time механики. Минимум затраченных усилий при максимальной функциональности — тот компромисс, который напрашивается естественным образом, когда не хочешь разрывать свой мозг пополам, пытаясь определиться умная ты обезьяна или все‑таки красивая.

Цепочки указателей или массивы ?

Как связать разнородные данные в большую кучу и найти потом то, что привязано к нужному объекту? Каждую запись, каждую ячейку, каждое поле? А если данные имеют некоторую динамику и внушительные пределы? Чаще всего нет возможности создать структуру из предельного множества полей для всех объектов, если реально каждый их них будет использовать лишь малую часть. Также ограничение в ресурсах (какими бы они ни были) вступает в противоречие с жесткостью структуры. Линейный массив это быстрое и простое решение, но для динамических данных необоснованно дорогостоящее. Как создать универсальный концепт, сочетающий плюсы отдельных решений и избежавший их минусов. Способный и размещать данные линейно и сохранять их непоследовательными порциями, растянутыми во времени? Универсальный как идеальный самурай — неотразимый, беспринципный, лишенный эмоций, элементов персональности (даже волос) и потому неуловимый. Достигший в стихийных непредсказуемых обстоятельствах высокой степени непрерывности. Мой подход — использовать и массивы и цепочки указателей. Ограничения, как и итоговую структуру, должен выстраивать сам программист. Концепт при этом его ни в чем не ограничивает.

Сеть неопределенных данных

Реализация представляет из себя два метода: секвенирование цепочек указателей и рекомбинация цепочек указателей. Каждый указатель ведет к некоему линейному массиву. Размер массива произвольный, указывается параметром flexiSize (пластичный размер). Ниже приведенная реализация использует 64бит указатели и 32бит смещения. Но концепт никак не мешает реализовать другие версии.


ui64 difSequencer(ui64 memPtr, ui64 nullPoint, ui64 stringAddr, ui64 flexiSize)
{
	ui64 memAddress = 0;
	ui64 node = nullPoint;
	i32 *vector = &memAddress, size = 0;
	ui8 step;


	// Секвенирование состоит из вложенных друг в друга циклов - байтового и битового.

	for (ui32 i = 0; i < (ui32)flexiSize; i++) {

		// Цикл байтового чтения разбивается на цикл 2-битового 4-тактного чтения по 4 блока (что пространственно равзнозначно, но быстрее 1-битового 8-тактного по 2 блока)

		step = *(ui8*)(stringAddr + i);


		// Битовый цикл проходит считываемый байт за четыре такта, загружая два бита по направлению от конца к началу.

		for (ui32 j = 0; j < 4; j++) {

			// Для продвижения в вектор-указатель загружается одна из четырех позиций текущего блока, указуемая текущей битовой комбинацией.

			vector = node + ((ui64)(step & 0xC0) >> 4);

			if (*vector == 0) {

				// В случае, если вектор пуст и не содержит связей, процедура должна вернуть отсутствие искомого пути, либо создать новый.

				if (memAddress == 0) {
					if (memPtr != 0) {
						size = ((ui32)flexiSize - i) * (ui32)(flexiSize >> 32);
						size = ((((ui32)flexiSize - i - 1) << 2) + 4 - j) * difLinksSize + size;

						memAddress = memoryArbitrary(size, memPtr);
						if (memAddress == 0)
							return 0;
					}
					else
						return 0;
				}


				// Расстояние до следующей точки сохраняется в векторе, указуемым текущей битовой комбинацией.

				*vector = memAddress - node;


				// Каждый четвертый битовый блок является несущим и может содержать кроме связей также диапазон для связанных данных.

				memAddress += (j < 3) ? difLinksSize : difLinksSize + (ui32)(flexiSize >> 32);
			}


			// Для продвижения к следующей точке в вектор добавляется относительная ссылка, являющаяся байтовым расстоянием.

			node += *vector;
			step <<= 2;
		}
	}


	return node;
}

Входящий параметр memPtr - это указатель на диапазон памяти, где будет размещаться создаваемая структура. В данной вариации используется возможность передать большой буфер, который постепенно будет размечать пользовательская процедура memoryArbitrary.

nullPoint - это точка нуля, от которой будет происходить поиск. Для тех кто не знает, что точкой нуля может быть какая угодно точка, приведу аналогию. В реализации работы с коллекцией файлов очень удобно каждой следующей точкой нуля назначать нижележащий каталог. Это как namespace, позволяет избежать повторного прохождения уже прочитанных данных.

stringAddr - это указатель на путь. Процедура работает с путем как массивом char, но по большому счету путь это просто последовательность бит. Данная вариация делает 8битные шаги по указанному пути. Но у меня есть и другие вариации. Путем может быть как ассоциативный токен, так и что-нибудь нечленораздельное.

flexiSize - это структура, содержащая размеры. Нижние 32бит содержат количество шагов в пути. Верхние 32бит содержат байтовый размер линейного массива, присоединенного к указателю.

Создать тестовую структуру предельно просто. Для начала понадобится лишь формальное определение структуры (а можно и без него) и точка нуля.


typedef struct _difPoint {
	ui32 links[4];
	ui64 general;
	ui64 extend;
} difPoint, *pdifPoint;


ui64 tmNull = memoryArbitrary(sizeof(difPoint), arbitraryBuffer);

Точка нуля это произвольный обнуленный отрезок памяти, выделенный пользовательской процедурой (malloc или allocate for mdl или что-нибудь еще). Как видно в структуре сами связи представляют из себя 4 блока links. Всё остальное это присоединенный линейный массив, который можно настроить на свой манер.

Переусложнение линейной структуры

Нетрудно догадаться, что простое структурирование данных по типу дерева каталогов приведет к структурированию по типу дерева указателей. Возможность управлять точкой нуля наподобие namespace это конечно хорошо, но как быть с произвольными запросами глубоко вложенных данных? В СУБД есть возможность отсортировать массивы по нужному признаку, сформировав последовательность цепочки нужным образом.

Опрометчиво было бы игнорировать подобную необходимость. Реализация альтернативных путей (или точек входа) для линейного массива данных обеспечивается рекомбинацией указателей. Процедура рекомбинации в целом повторяет секвенирование, с той лишь разницей, что указатель на последний элемент (целевые данные) не возвращается, а перезаписывается на перенаправляемые данные.


ui64 difRecombine(ui64 target, ui64 nullPoint, ui64 stringPtr, ui64 size)
{
	ui64 node = nullPoint;
	i32 *vector = &vector;
	ui8 step;

	for (ui64 i = 0; i < size; i++) {
		step = *(ui8*)(stringPtr + i);						// цикл байтового чтения

		for (ui64 j = 0; j < 4; j++) {						// цикл битового чтения
			vector = node + ((ui64)(step & 0xC0) >> 4);		// считывание по два бита от конца к началу
			if (*vector == 0)								// выход при отсутствии искомого пути
				return 0;

			node += *vector;								// продвижение указателя
			step <<= 2;
		}
	}

	*vector = target - (node - *vector);					// подмена конечной точки на новую

	return node;
}

Параметр target - это данные, на которые будет указывать новый путь.

nullPoint - точка начала поиска, stringPtr - путь, size - количество шагов в пути.

Вот создание двух альтернативных путей для одного массива данных.

	pdifPoint test = difSequencer(someBuffer, tmNull, "test", sizeof(data128) << 32 | 4);
	pdifPoint z = difSequencer(someBuffer, tmNull, "z", (ui64)0 | 1);
	difRecombine(test, tmNull, "z", 1);

В первой строке создаем некий массив размером 16 байт. Во второй создаем еще один, с более коротким путем и без присоединенных данных. В третьей заменяем конечную точку короткого пути на ту, куда указывает длинный. Теперь на один и тот же массив данных указывают два пути: короткий и длинный. Если подобные структуры размещать во временном буфере (например списки файлов при синхронизации двух каталогов), то после отработки основных действий никакой утилизации не требуется. Я лично использовал подобную технику в фильтр‑драйвере файловой системы при разработке сервиса подобного облаку oneDrive, но с хранилищем данных внутри локальной сети. Для синхронизации с основной структуры создавалась такая копия в виде расслоения. Часть расчетов велась на локальном диске со своей структурой каталогов, часть на сетевом со своей. Внутренняя же часть (совпадающая) в виде наложенного слоя размещенная в отдельном временном буфере переносилась с одной структуры на другую. Это позволило увидеть в локальной папке файлы, которых реально на локальном диске не было и которые были размещены на сетевом хранилище. Все необходимые поля были скопированы из структуры и использованы в драйвере при построении списка. При первом обращении к файлу тут же начиналось копирование. Я конечно не знаю, как реализован OneDrive от Microsoft, но зная как в принципе реализуются различные драйвера Microsoft, подозреваю, что это очень громоздкая механика размером гораздо более тех 100 кб, что понадобились мне. При закрытии папки с файлами никаких утилизационных действий не требуется. Буфер не очищается, потому‑что он многоразовый и используется по принципу «мыть непосредственно перед едой».

Ничего не понял, но хочу посмотреть в деле

Рекомбинация достаточно универсальный метод и позволяет не только создать множественные точки входа, но и строить гораздо более сложные интегральные схемы с рекурсией, прыжками и ветвлением. Для того чтобы сразу показать более продвинутые возможности абстрактного структуризатора можно испробовать его в семантических механиках. Поскольку это тема для отдельного серьезного исследования, я ограничусь реализацией сферического (циклического) парсера в вакууме (в оторванном от реальности примере) и реализую обработчик, который читает строку со случайным текстом и разбирает ее на токены. Настройка интегральных схем на pure c, обработчик на ассемблере.

вариант секвенсера под ассемблер

difLinksSize			EQU		(4 * 4)					// размер блока связей
difPointSize			EQU		(difLinksSize + 2 * 8)	;// размер типового блока с данными

difGeneral				EQU		(difLinksSize)			;// позиция данных дифференциальной точки
difExtend				EQU		(difGeneral + 8)		;// позиция расширения данных дифференциальной точки


;//
;// Шаговый секвенсер
;// Входящие данные:	[rcx] - битовая последовательность; [rdx] - указатель на буфер для размещения данных; [r8] - точка нуля; [r9] - flexiSize;
;// Рабочие регистры:	[r9], [rax]
;//


difStepSequencer PROC
	and rcx, 0FFh						;// подготовка регистра и вводных данных
	shl rcx, 8
	shl r9, 32							;// сохранение flexiSize
	or rcx, r9

@pointing:
	mov r9, rcx							;// расчет логических связей
	shl r9, cl							;// прокрутка битовой последовательности по два бита от конца к началу
	and r9, 0C000h						;// отсечение неиспользуемых бит
	shr r9, 12							;// масштабирование до байтового значения в диапазоне памяти
	movsxd rax, dword ptr [r8 + r9]		;// проецирование от точки нуля и получение связанного вектора со знаковым расширением
	add r8, rax							;// индексация точки нуля

	cmp rax, 0							;// проверка пределов целеуказания
	jne @promote						;// переход к продвижению или прерывание при зацикливании
	bt rcx, 16							;// проверка флага имеющегося запаса памяти
	jc @repointing						;// перенаправление в область расширения при наличии
	cmp edx, 0							;// проверка указателя на произвольный буфер для запроса памяти
	je @close							;// выход при невозможности размещения

	shl r9, 24							;// сохраненние вводных данных перед прыжком во вложенную процедуру
	mov r9w, cx
	mov rax, 8							;// расчет количества необходимой памяти вычитанием пройденных циклов
	sub al, r9b
	imul rax, rax, (difLinksSize / 2)	;// произведение необходимого количества связей в байтовый размер
	shr rcx, 32							;// добавление flexiSize
	add rcx, rax
	call memoryArbitrary				;// запрос памяти из произвольного буфера для размещения данных
	mov rdx, rax						;// сохранение указателя на полученный диапазон памяти
	mov rcx, 010000h					;// очистка регистра вводных данных и установка флага наличия запаса памяти
	mov cx, r9w
	shr r9, 24							;// восстановление рабочих регистров

@repointing:
	mov rax, rdx						;// расчет целевого вектора от текущей точки нуля
	sub rax, r8
	mov dword ptr [r8 + r9], eax		;// сохранение вектора
	mov r8, rdx							;// продвижение точки нуля
	add rdx, difLinksSize				;// индексация указателя запаса памяти
	
@promote:
	add cl, 2							;// индексация счетчика циклов
	cmp cl, 8							;// проверка пределов
	jb @pointing						;// повтор
	mov rax, r8							;// выгрузка результата при достижении пределов

@close:
	ret
difStepSequencer ENDP

Для ассемблерного обработчика скриптовой строки используется ассемблерный же секвенсер. Он шаговый, т.к. чтение и обработка строки посимвольные. Реализована возможность записи новых цепочек, которая в данном примере не используется. memoryArbitrary — такая же пользовательская процедура выдачи памяти, buffer — такой же произвольный буфер. Чтение одного шага — это также 4х кратное обращение к оперативной памяти как в С варианте.

ассемблерный парсер для скриптов

_splitter PROC
	mov r13, r9										;// сохранение указателя стека в регистр
	mov r12, r8										;// сохранение транзакционного контекста в регистр
	mov r11, rdx									;// сохранение циклического контекста в регистр
	sub r11, r13
	mov r11d, r11d
	mov r10, rcx									;// сохранение указателя текстового потока в регистр


@action:
	movzx rcx, byte ptr [r10]						;// загрузка фрагмента текстового потока
	inc r10											;// продвижение указателя потока
	xor rdx, rdx									;// обнуление указателя на произвольный буфер
	movsxd r8, r11d									;// извлечение циклического контекстного указателя (нижн 32бит)
	add r8, r13										;// получение 64бит указателя
	call difStepSequencer							;// чтение сегмента
	movdqa xmm15, xmmword ptr [rax + difGeneral]	;// загрузка связанных данных из дифференциального узла

	mov ebx, r11d									;// сохранение абстрактного шлейфа (верхн 32бит)
	xor rbx, r11
	movq r11, xmm15									;// получение точки прерывания циклического контекста (нижн 32бит) и нового абстрактного фрагмента (верхн 32бит)
	movsxd r8, r11d									;// преобразование в буфере указателя прерывания в актуальный 32бит адрес со смещением в рамках текущего стека
	add rax, r8
	sub rax, r13
	xor eax, r11d									;// продвижение циклического контекста с помощью обмена с буфером методом двойной экструзии
	xor r11, rax
	test rbx, r11									;// проверка совпадения нового абстрактного фрагмента со шлейфом
	jnz @cycle										;// разрыв приводит к переключению транзакционного контекста


@transaction:
	nop												;// произвольные действия с полученными от токена данными


@cycle:
	cmp cx, 0FFh									;// чтение включает нуль-терминирующий символ
	ja @action
	ret
_splitter ENDP

Обработчик Splitter (делитель строки на токены) обходит посимвольно скрипт (цикл action), считывает интегральную схему, в которую внесен весь алфавит символов. Прерываясь после каждого отрезка, выделяющего токен, получает доступ к связанным данным. Потенциально может выполнять дальнейшие действия с токенами, основываясь на полученной информации (цикл transaction). Формальная система выносится за скобки и содержится в дифференциальной сети. В ней используются прыжки, рекурсии и ветвления. В обработчике нет правил обработки строк, а формальная система не содержит инструкций. Весь процесс разделения строится исключительно за счет организации связанных данных. Это легко можно понять, если представить, что строка (поток бит) — это волна. Формальная система — частотный модулятор. Считывая в обработчике по одному знаку, мы отправляем его в дифференциальную сеть и получаем из нее связанные данные. Таким образом полученный фрагмент строки становится несущей волной. Информация, которую можно извлечь из частотной дифференциации также абстрактна и несет смысл исключительно в контексте потока. Главное, что с ее помощью можно объединить некоторое множество символов, а поток соответственно разделить на отрезки. Придумаем несколько отличных друг от друга видов привязанных данных, пусть это будут просто последовательные битовые смещения.

// Отвлеченные классы (охватывают всё символическое множество)

	ui64 abTrash = 1 << 0;					// Строгие последовательности накапливаются до момента переключения
	ui64 abLabel = 3 << 1;					// Перетекающие последовательности без разрыва переходят из одного контекста в другой
	ui64 abSequence = 1 << 2;
	ui64 abSeparator = 1 << 3;
	ui64 abEnumerator = 1 << 4;
	ui64 abExprsnMajor = 1 << 5;
	ui64 abExprsnMinor = 1 << 6;
	ui64 abExprsnDelay = 1 << 7;
	ui64 abBracketIn = 1 << 8;				// Несмешиваемые последовательности колеблются в рамках выделенного диапазона и разрываются в каждой точке
	ui64 abBracketOut = 1 << 10;
	ui64 abBlockIn = 1 << 12;
	ui64 abBlockOut = 1 << 14;

Дадим разным смещениям осмысленные названия, исключительно для собственного удобства. Чтобы не запутаться какие множества символов с какими связанными данными будут группироваться. Таким образом получаем как бы типы для этих отрезков (или типы данных), которые не несут никакой функциональной нагрузки, кроме их явного отделения друг от друга. Далее можно загрузить алфавит в дифференциальную сеть (столько раз, сколько различных контекстов будет существовать для каждого отдельного знака). Каждый символ алфавита это путь от точки нуля, указующий на линейный массив. В нем размещаются привязанные данные (тип текущего символа в текущем контексте). Придумаем несколько самых распространенных контекстов — идентификатор, десятичное число, шестнадцатеричное число, неопределенное число и один общий контекст разнотипных данных. Сделаем два разнотипных контекста — четный и нечетный, для тех случаев когда последовательно в одном отрезке мы можем встретить несколько символов к примеру скобок «(» и «)», но при этом объединять в один токен их будет неправильно, ведь это несколько разных токенов. Реализация на С, настраивается один раз при старте процесса.

Настройка контекстной формальной системы в дифференциальной сети
	//
	// Циклический контекст
	//

  	ui64 tmFirst = memoryArbitrary(difPointSize, chainMem(0));  //точка нуля


	// Проецирование абстрактной дифференциации на битовый алфавит

	ui64 tokenGeneral[256] = { 0 };
	tokenGeneral['('] = abBracketIn << 32;
	tokenGeneral[')'] = abBracketOut << 32;
	tokenGeneral['{'] = abBlockIn << 32;
	tokenGeneral['}'] = abBlockOut << 32;
	tokenGeneral[';'] = abSeparator << 32;
	tokenGeneral[','] = abEnumerator << 32;
	tokenGeneral['+'] = abExprsnMajor << 32;
	tokenGeneral['-'] = abExprsnMajor << 32;
	tokenGeneral['*'] = abExprsnMajor << 32;
	tokenGeneral['/'] = abExprsnMajor << 32;
	tokenGeneral['='] = abExprsnDelay << 32;


	// Интегральные схемы

	ui64 VariousInterrupt[2] = { tmFirst, memoryArbitrary(difPointSize, chainMem(0)) };
	ui64 IdentifierInterrupt = memoryArbitrary(difPointSize, chainMem(0));
	ui64 NumericInterrupt = memoryArbitrary(difPointSize, chainMem(0));
	ui64 DecimalInterrupt = memoryArbitrary(difPointSize, chainMem(0));
	ui64 HexadecInterrupt = memoryArbitrary(difPointSize, chainMem(0));


	for (ui32 i = 0; i < 256; i++) {

		// Спектр связей

		pdifPoint VariousToken[2] = { difSequencer(chainMem(0), VariousInterrupt[0], &i, sizeof(data128) << 32 | 1),
			difSequencer(chainMem(0), VariousInterrupt[1], &i, sizeof(data128) << 32 | 1) };
		pdifPoint IdentifierToken = difSequencer(chainMem(0), IdentifierInterrupt, &i, sizeof(data128) << 32 | 1);
		pdifPoint NumericToken = difSequencer(chainMem(0), NumericInterrupt, &i, sizeof(data128) << 32 | 1);
		pdifPoint DecimalToken = difSequencer(chainMem(0), DecimalInterrupt, &i, sizeof(data128) << 32 | 1);
		pdifPoint HexadecToken = difSequencer(chainMem(0), HexadecInterrupt, &i, sizeof(data128) << 32 | 1);


		// Разнотипный четный/нечетный (несмешиваемые) контексты

		for (ui64 j = 0; j < 2; j++) {
			if (i >= 'A' && i <= 'Z' || i >= 'a' && i <= 'z') {
				difRecombine(IdentifierToken, VariousInterrupt[j], &i, 1);
			}
			else if (i == '0') {
				difRecombine(NumericToken, VariousInterrupt[j], &i, 1);
			}
			else if (i >= '1' && i <= '9') {
				difRecombine(DecimalToken, VariousInterrupt[j], &i, 1);
			}
			else if (i == ';' || i == ',' || i == '=' || i == '+' || i == '-' || i == '*' || i == '/') {
				VariousToken[j]->general = tokenGeneral[i];
			}
			else if (i == '(' || i == ')' || i == '{' || i == '}') {

				// в однотипных несмешиваемых последовательностях связи перекрещиваются с противолежащими точками нуля
				// четность данных при этом устанавливается исходя из четности точки нуля

				difRecombine(VariousToken[j == 0 ? 1 : 0], VariousInterrupt[j], &i, 1);
				VariousToken[j]->general = tokenGeneral[i] << j;
			}
			else {
				VariousToken[j]->general = abTrash << 32;
			}
		}


		// Индентификатор

		if (i == 0 || i == '	' || i == ' ' || i == '\r' || i == '\n') {
			difRecombine(VariousToken[0], IdentifierInterrupt, &i, 1);
		}
		else if (i == ';' || i == ',' || i == '=' || i == '+' || 
			i == '(' || i == ')' || i == '{' || i == '}' || i == '[' || i == ']') {
			difRecombine(VariousToken[0], IdentifierInterrupt, &i, 1);
		}
		else if (i == ':') {
			IdentifierToken->general = abLabel << 32;
		}
		else {
			IdentifierToken->general = abSequence << 32;
		}


		// Неопределенные числовые данные

		if (i == 0 || i == ' ' || i == '	' || i == '\r' || i == '\n') {
			difRecombine(VariousToken[0], NumericInterrupt, &i, 1);
		}
		else if (i == ';' || i == ',' || i == '=' || i == '+' ||
			i == '(' || i == ')' || i == '{' || i == '}' || i == '[' || i == ']') {
			difRecombine(VariousToken[0], NumericInterrupt, &i, 1);
		}
		else if (i == '0') {
			difRecombine(DecimalToken, NumericInterrupt, &i, 1);
			NumericToken->general = abSequence << 32;
		}
		else if (i == 'x') {
			difRecombine(HexadecToken, NumericInterrupt, &i, 1);
		}
		else {
			difRecombine(DeclassfdToken, NumericInterrupt, &i, 1);
		}


		// Десятичное число

		if (i == 0 || i == ' ' || i == '	' || i == '\r' || i == '\n') {
			difRecombine(VariousToken[0], DecimalInterrupt, &i, 1);
		}
		else if (i == ';' || i == ',' || i == '=' || i == '+' ||
			i == '(' || i == ')' || i == '{' || i == '}' || i == '[' || i == ']') {
			difRecombine(VariousToken[0], DecimalInterrupt, &i, 1);
		}
		else if (i >= '0' && i <= '9') {
			DecimalToken->general = abSequence << 32;
		}
		else if (i == 'x') {
			DecimalToken->general = abSequence << 32;
		}
		else {
			difRecombine(DeclassfdToken, DecimalInterrupt, &i, 1);
		}


		// Шестнадцатеричное число

		if (i == 0 || i == ' ' || i == '	' || i == '\r' || i == '\n') {
			difRecombine(VariousToken[0], HexadecInterrupt, &i, 1);
		}
		else if (i == ';' || i == ',' || i == '=' || i == '+' ||
			i == '(' || i == ')' || i == '{' || i == '}' || i == '[' || i == ']') {
			difRecombine(VariousToken[0], HexadecInterrupt, &i, 1);
		}
		else if (i >= '0' && i <= '9') {
			HexadecToken->general = abSequence << 32;
		}
		else if (i >= 'A' && i <= 'F' || i >= 'a' && i <= 'f') {
			HexadecToken->general = abSequence << 32;
		}
		else if (i != 'x') {
			difRecombine(DeclassfdToken, HexadecInterrupt, &i, 1);
		}


		// Циклическое расширение

		VariousToken[0]->general |= (ui32)((ui64)VariousInterrupt[0] - (ui64)VariousToken[0]);
		VariousToken[1]->general |= (ui32)((ui64)VariousInterrupt[1] - (ui64)VariousToken[1]);
		IdentifierToken->general |= (ui32)((ui64)IdentifierInterrupt - (ui64)IdentifierToken);
		NumericToken->general |= (ui32)((ui64)NumericInterrupt - (ui64)NumericToken);
		DecimalToken->general |= (ui32)((ui64)DecimalInterrupt - (ui64)DecimalToken);
		HexadecToken->general |= (ui32)((ui64)HexadecInterrupt - (ui64)HexadecToken);

	}

Загружая символы в дифференциальную сеть, продвигаемся от точки нуля к следующей. Сохраняем новую точку, она будет следующей точкой нуля. Получая однотипные символы из строки мы продвигаемся в одном и том же контексте. Но есть указатели и на другие контексты. Получив например цифру после пробела мы получим указатель на другой контекст. Символы в нем повторяются, но несут в себе другие типы данных. Таким образом у каждого контекста есть точки входа и выхода для перемещения между разными контекстами. Разнотипный контекст при этом как бы центральный и разветвляется во все остальные контексты. А все остальные контексты в свою очередь выходят в разнотипный. Контекст неопределенных чисел общий для частных контекстов десятичного числа и шестнадцатеричного. Это только основные контексты, вообще их можно придумать гораздо больше: цитаты, комментарии, деклассифицированные данные и т.д.

При помощи приведенной выше формальной системы можно прочитать скрипт для исполнения такого вида:

if(1){if(1){s = 2 + 2}}else{}

Код запуска на С самый простой.

extern ui64 _splitter(ui64 str, ui64 first, ui64 second, ui64 stack);  //прототип
ui64 ret = _splitter(Stack->script, tmFirst, tmSecond, (ui64)Stack);

В обработчик передается указатель на скрипт (текстовая строка char), указатель на точку нуля контекстной формальной системы (tmFirst), точка нуля высокоуровневого контекста (tmSecond - здесь не рассматривается) и указатель на произвольный стек, для возможных стековых операций. Обработчик выделяет каждый токен, при выделении отрезка переходит в блок transaction, где в специальном регистре доступен тип токена и может предпринимать дальнейшие высокоуровневые действия.

P.S.

Внимательный читатель заметил, что пример был выдран из работающей стековой машины, исполняющей скрипты с классическим синтаксисом С. Нюансы реализации намеренно опущены, они не относятся к теме дифференциальных сетей и по объему гораздо больше, чем всё выше написанное. Цель примера показать, как дифференциальные сети способны структурировать самые разные типы данных.

Комментарии (0)