Библиотека PREPR — API-документация

Пакет TMC Suite. Статическая библиотека — препроцессор входных текстовых файлов-шаблонов (.tpl) для расчётных ядер. Язык документации: русский. Все сигнатуры приведены по исходникам из src/libs/PREPR/ и общему заголовку src/Include/Prepr1.h.


1. Назначение библиотеки

PREPR — статическая библиотека (StaticLibrary, toolset v145, собирается под Win32 и x64) — это препроцессор входных текстовых файлов-шаблонов (.tpl) для расчётных ядер пакета (PlanarRT_H / PlanarRT_X).

Важно: PREPR — это НЕ препроцессор языка C/C++. Несмотря на то что синтаксис директив внешне похож на директивы C-препроцессора (#define, #include), PREPR обрабатывает текстовые входные файлы расчёта (шаблоны .tpl) и не имеет отношения к компиляции C++.

Библиотека выполняет над входным шаблоном следующие преобразования:

  • обработку директив #define ИМЯ@ТЕЛО — определение текстовых макросов, в том числе с формальными параметрами (разделитель @, параметры через ;);
  • обработку директив #include имя_файла — подстановку содержимого включаемого файла на место строки #include;
  • удаление комментариев — всё после символа ! отбрасывается; маркер \\ обрабатывается как склейка строк;
  • подстановку макросов с фактическими параметрами — рекурсивно, от последнего #define к первому;
  • сохранение всех определений #define во временный файл $$vr$$s.prc.

Результат работы — развёрнутый текстовый файл, который далее читается расчётным ядром как обычный входной файл расчёта.

Точка входа — функция MainPrepr1(). Она вызывается из обёртки MainPrepr() (в библиотеке TMCIndan), а та, в свою очередь, — из счётного ядра. Это единственная точка вызова PREPR.


2. Расположение исходников в сборке

Исходники: src/libs/PREPR/. Заголовок: src/Include/Prepr1.h (в проекте подключается как ..\..\INCLUDE\Prepr1.h).

Проект сборки — Prepr.vcxproj (toolset v145, конфигурации Win32/x64, тип StaticLibrary). В сборку входят 9 файлов .cpp:

Файл Назначение (кратко)
DEF_DEST.CPP Подстановка/назначение макроопределений (def_main_macdef, работа с целевым телом)
DEF_ER.CPP Тексты сообщений об ошибках (def_err_get)
DEF_ERD.CPP Проверка повторного определения макросов (def_err_dec)
DEF_MACR.CPP Поиск и разбор макроопределений (def_macro, def_separ_mac_name и др.)
DEF_STR.CPP Низкоуровневые операции со связным буфером строк
DEFMIS.CPP Вспомогательные функции (DefineIncludeName, GetCurrentPath, DeleteComment)
PREP_LD.CPP Загрузка/инициализация, работа с include и временным файлом (def_include, load_vr_file)
PREP_MN.CPP Главная логика препроцессора (MainPrepr1, main_preproc, put_error_messege)
READ_BUF.CPP Чтение буфера/строк (read_buf и сопутствующее)

Файлы вне сборки (legacy / мёртвый код)

Следующие файлы присутствуют в каталоге, но не включены в Prepr.vcxproj и поэтому не описываются:

Файл Причина
DR_BUF.CPP Старый автономный тестер со своей функцией main(); содержит единственную внешнюю ссылку на load_preproc_buff
INCPATH.CPP Функции SetIncludePathName / GetIncludePathName — отсутствуют в Prepr1.h, наружу не объявлены

3. Структуры данных

Объявлены в Prepr1.h.

3.1. BUFFER — связный буфер символов

Главный рабочий буфер препроцессора. Реализован как двусвязный список символов через два массива фиксированного размера NMAX. Вставка и удаление символов выполняются операциями над списком (изменением индексов) без физического сдвига памяти.

typedef struct
{
  char  *lsplex;      // буфер символов (как связный список)
  int   *ilex;        // индекс «следующего» элемента (связи списка)
  int    inp_p;       // текущая позиция чтения
  int    inp_p_nul;   // начало списка свободных ячеек
} BUFFER;
Поле Тип Смысл
lsplex char* Массив-буфер символов; каждая ячейка хранит один символ, доступ — как к элементу связного списка
ilex int* Массив индексов: ilex[i] — номер следующего за i элемента в списке. Через него реализуются вставка/удаление без сдвига памяти
inp_p int Текущая позиция чтения (индекс «головы» обрабатываемого участка)
inp_p_nul int Начало списка свободных ячеек (откуда берутся ячейки при вставке и куда возвращаются при удалении)

Размер массивов задаётся макросом NMAX (по умолчанию 30 000 000, см. Prepr1.h).

3.2. MACROS — описание одного макроопределения

Хранит разобранное определение #define: имя, формальные параметры и тело, разбитое на чередующиеся фрагменты («маску»).

typedef struct
{
  char   mac[ BUF_STR_N_MAX ];    // строка тела #define
  char  *m[ BUF_PAR_MAX ];        // m[0] — имя; m[1..j1] — имена формальных параметров
  char  *m1[ BUF_MASKA_MAX ];     // куски тела между параметрами («маска»)
  int    im1[ BUF_MASKA_MAX ];    // индекс формального параметра перед куском m1[i], или -1
  int    j1;                      // число формальных параметров
  int    i2;                      // число элементов маски
} MACROS;
Поле Тип Смысл
mac[] char[BUF_STR_N_MAX] Строка тела #define (исходное тело макроса)
m[] char*[BUF_PAR_MAX] Указатели на имена: m[0]имя макроса; m[1]..m[j1] — имена формальных параметров
m1[] char*[BUF_MASKA_MAX] Текстовые фрагменты тела между параметрами — «маска». Тело хранится разбитым на чередующиеся куски
im1[] int[BUF_MASKA_MAX] Для каждого фрагмента m1[i] — индекс формального параметра, стоящего перед ним; -1, если параметра нет
j1 int Число формальных параметров
i2 int Число элементов маски (фрагментов тела)

Тело макроса хранится разбитым на чередующиеся текстовые фрагменты (m1) с привязанными к ним индексами формальных параметров (im1). При подстановке фрагменты выводятся подряд, а на места параметров вставляются фактические значения.

3.3. Константы размеров и имена (Prepr1.h)

Константа Значение Смысл
NMAX 30000000 Размер массивов буфера BUFFER (число ячеек)
BUF_STR_INLINE_MAX 500000 Максимальная длина строки при построчной обработке
BUF_STR_N_MAX 500000 Размер поля MACROS::mac[] (длина тела #define)
BUF_MASKA_MAX 800000 Размер массивов маски MACROS::m1[] / im1[]
BUF_PAR_MAX 60 Размер массива MACROS::m[] (имя + формальные параметры)
VR_MAC_FILE_NAME "$$vr$$s.prc" Имя временного файла для сохранения всех #define
VR_MAC_INCLUDE "#include $$vr$$s.prc" Строка include для временного файла
DEF_SEPARATOR '@' Разделитель «имя+параметры / тело» в #define

Примечание: DEF_SEPARATOR упомянут в коде как символ-разделитель '@'; в самом Prepr1.h он может определяться в другом месте — при сомнении опираться на исходники DEF_MACR.CPP.


4. Синтаксис директив входного файла

4.1. #define — определение макроса

#define ИМЯ par1; par2; ... @ ТЕЛО
  • Распознаются обе формы начала директивы: #defineпробелом) и #defineтабуляцией).
  • Символ-разделитель @ (DEF_SEPARATOR = '@') отделяет «имя + список формальных параметров» от тела макроса.
  • Формальные параметры перечисляются после имени и разделяются символом ; (точка с запятой).
  • Если параметров нет, директива имеет вид:

#define ИМЯ@ТЕЛО

  • Тело может занимать несколько строк (для переноса используется маркер склейки \\, см. ниже).

4.2. #include — включение файла

#include имя_файла
  • Распознаются обе формы: #includeпробелом) и #includeтабуляцией).
  • Полный путь к включаемому файлу формируется как <GetCurrentPath()>\<имя_файла> — то есть имя трактуется относительно текущего каталога (см. MainPrepr1, параметр csCurrentPath).
  • Содержимое включаемого файла подставляется на место строки #include.

4.3. Комментарии и склейка строк

Маркер Обработка Функция
! Всё после ! в строке отбрасывается (заменяется на " \n") DeleteComment
\\ Маркер склейки строк: заменяется на " \n" (используется для переноса длинного тела #define) def_sl

4.4. Вызов (использование) макроса

ИМЯ(парам1; парам2; ...)
  • Фактические параметры перечисляются в круглых скобках и разделяются символом ;.
  • Если фактический параметр целиком заключён в круглые скобки, эти скобки при подстановке заменяются пробелами.
  • Подстановка выполняется рекурсивно функцией def_main_macro — обход определений идёт от последнего #define к первому.
  • Все определения #define дополнительно сохраняются во временный файл $$vr$$s.prc (функция load_vr_file).

5. Публичные функции

О макросах _near, _fortran. Это атрибуты модели памяти и соглашения о вызове из старого 16/32-битного кода. В современной сборке они на семантику не влияют. Ниже в сигнатурах опускаются. Где функция объявлена с такими атрибутами, это отмечено в описании.

5.1. Главная точка входа

void MainPrepr1(char *csTplFileNamePrepr, char *csTplFileName, char *csCurrentPath, char *csError)

Главная точка входа в препроцессор. Выделяет внутренние буферы (szError, szPathName), запускает основную обработку main_preproc, затем освобождает буфер (unload_prep_buf) и удаляет временный файл (dest_prep).

  • Параметры:
  • csTplFileNamePrepr (char*) — имя выходного (развёрнутого, обработанного) файла;
  • csTplFileName (char*) — имя исходного файла-шаблона .tpl;
  • csCurrentPath (char*) — текущий каталог (используется для разрешения относительных путей в #include);
  • csError (char*) — выход: строка ошибки. Пустая строка ("") при успехе, иначе — текст возникшей ошибки.
  • Возврат: void (результат сообщается через csError).

char* main_preproc(char *szTargetFileName, char *szSourceFileName)

Основная процедура обработки. Читает исходный файл построчно, удаляет комментарии (DeleteComment), загружает текст в буфер, затем последовательно выполняет:

  1. def_include — раскрытие #include;
  2. load_vr_file — сохранение всех #define во временный файл;
  3. def_err_dec — проверку повторных определений;
  4. def_main_macro — рекурсивную подстановку макросов;

и записывает результат в целевой файл szTargetFileName.

  • Параметры:
  • szTargetFileName (char*) — имя выходного (развёрнутого) файла;
  • szSourceFileName (char*) — имя исходного файла-шаблона.
  • Возврат: указатель на глобальную строку ошибки szError (значение "" при успехе, иначе — текст ошибки).

5.2. Жизненный цикл и управление памятью

int unload_prep_buf(void)

Освобождает память рабочего буфера: массивы buf.lsplex и buf.ilex.

  • Возврат: всегда 0.

int load_preproc_buff(void)

  • Статус: функция объявлена в Prepr1.h, но реализации в библиотеке нет. Единственная внешняя ссылка на неё — в неиспользуемом файле DR_BUF.CPP (вне сборки).
  • Возврат: не определён (тела нет).

int dest_prep(void)

Удаляет временный файл <CurrentPath>\$$vr$$s.prc (вызовом remove).

  • Возврат: код возврата remove() (0 при успехе удаления).

int buf_init(BUFFER *buf, int n)

Инициализирует буфер: выделяет массивы lsplex (char[n]) и ilex (int[n]) через alloc_mem_err, строит начальный список свободных ячеек.

  • Параметры:
  • buf (BUFFER*) — инициализируемый буфер;
  • n (int) — размер буфера (число ячеек), обычно NMAX.
  • Возврат: BUF_NULL (0) при успехе; ERROR_INIT (-11) при нехватке памяти.

int InitCharArray(char **ch, int n)

Инициализация символьного массива: если *ch не NULL, освобождает его, затем выделяет malloc(n).

  • Параметры:
  • ch (char**) — адрес указателя на массив;
  • n (int) — размер выделяемого блока (байт).
  • Возврат: 0 при успехе; -1 при ошибке выделения.

int FreeCharArray(char **ch)

Освобождает символьный массив (free(*ch)) и обнуляет указатель (*ch = NULL).

  • Параметры: ch (char**) — адрес указателя на массив.
  • Возврат: 0.

5.3. Обработка #include и макросов

int def_include(BUFFER *buf)

Ищет директивы #include, подставляет содержимое включаемого файла вместо строки #include, затем выполняет обработку склейки строк (def_sl).

  • Параметры: buf (BUFFER*) — рабочий буфер.
  • Возврат: 0 при успехе; -1, DEF_ERROR_INFILE (-7) при ошибке открытия включаемого файла; DEF_ERROR_MEMORY (-8) при нехватке памяти.

int def_sl(BUFFER *buf)

Заменяет маркер склейки строк \\ на " \n" (перенос строки в теле макроса).

  • Параметры: buf (BUFFER*) — рабочий буфер.
  • Возврат: 0 при успехе; -1 при ошибке.

int def_macro(BUFFER *buf)

Ищет очередную директиву #define / #define в буфере, не сдвигая текущую позицию inp_p.

  • Параметры: buf (BUFFER*) — рабочий буфер.
  • Возврат: позиция найденной директивы (>= 0) или -1, если #define не найдено.

int def_main_macro(BUFFER *buf)

Рекурсивно подставляет все макроопределения: обход идёт от последнего #define к первому (для корректного раскрытия макросов, использующих другие макросы).

  • Параметры: buf (BUFFER*) — рабочий буфер.
  • Возврат: BUF_NULL (0) при успехе; отрицательный код ошибки (< 0) при сбое.

int def_main_macdef(BUFFER *buf)

Подставляет одно макроопределение: разбирает заголовок (def_separ_mac_name), формирует маску тела (def_set_formal_param), удаляет строку #define из буфера, выполняет подстановку (def_subst_mac).

  • Параметры: buf (BUFFER*) — рабочий буфер.
  • Возврат: BUF_NULL (0) при успехе; отрицательный код ошибки при сбое.
  • Примечание: объявлена с атрибутами _near fortran, но фактически экспортируется и используется.

int def_subst_mac(BUFFER *buf, MACROS *mac)

Ищет в буфере вызовы макроса mac->m[0] с фактическими параметрами (считываемыми через def_buf_gets_param) и подставляет тело макроса, заменяя формальные параметры на фактические.

  • Параметры:
  • buf (BUFFER*) — рабочий буфер;
  • mac (MACROS*) — разобранное определение макроса.
  • Возврат: BUF_NULL (0) при успехе; отрицательный код ошибки при сбое.

int def_set_formal_param(BUFFER *buf, MACROS *mac, int i)

Разбирает тело макроса, заполняя массивы маски mac->m1[] (текстовые фрагменты) и mac->im1[] (индексы формальных параметров).

  • Параметры:
  • buf (BUFFER*) — рабочий буфер;
  • mac (MACROS*) — заполняемое определение;
  • i (int) — начальная позиция (тела) в буфере.
  • Возврат: позиция конца тела (>= 0) или -1 при ошибке.

int def_separ_mac_name(BUFFER *buf, MACROS *mac)

Разбирает заголовок #define ИМЯ par1; par2; ... @ ТЕЛО: записывает имя в mac->m[0], формальные параметры — в mac->m[1..], число параметров — в mac->j1, находит разделитель @.

  • Параметры:
  • buf (BUFFER*) — рабочий буфер;
  • mac (MACROS*) — заполняемое определение.
  • Возврат: позиция разделителя @ (>= 0) или код ошибки: BUF_ERROR_NO_SEP (-6), BUF_ERROR_NO_FORMAL_PARAM (-9).

int def_err_dec(BUFFER *buf)

Проверяет повторное определение имени макроса (одно и то же имя в двух #define).

  • Параметры: buf (BUFFER*) — рабочий буфер.
  • Возврат: BUF_NULL (0), если повторов нет; BUF_ERROR_REDEC (-10) при обнаружении повторного определения.

int load_vr_file(BUFFER *buf)

Сохраняет все определения #define во временный файл $$vr$$s.prc (VR_MAC_FILE_NAME). Многострочные определения разбиваются с маркером склейки \\.

  • Параметры: buf (BUFFER*) — рабочий буфер.
  • Возврат: BUF_NULL (0) при успехе; -1 при ошибке файла; -11 (ERROR_INIT) при нехватке памяти.

5.4. Низкоуровневые операции со связным буфером (DEF_STR.CPP)

int buf_gets(char *str, int n, BUFFER *buf)

Читает строку из связного буфера в str (до завершающего \0), но не более n символов.

  • Параметры: str (char*) — приёмный буфер; n (int) — лимит символов; buf (BUFFER*) — рабочий буфер.
  • Возврат: BUF_NULL / BUF_EOF / BUF_ABN (см. раздел 6).

int read_buf(char *str, int n, BUFFER *buf)

Аналогично buf_gets, но чтение завершается по \0 или по \n (читает одну строку).

  • Параметры: как у buf_gets.
  • Возврат: BUF_NULL / BUF_EOF / BUF_ABN.

int buf_puts(char *str, BUFFER *buf)

Вставляет строку str (вместе с завершающим \0) в текущую позицию буфера, не сдвигая указатель.

  • Параметры: str (char*) — вставляемая строка; buf (BUFFER*) — рабочий буфер.
  • Возврат: код результата (BUF_NULL при успехе).

int buf_puts1(char *str, BUFFER *buf)

Как buf_puts, но без завершающего \0; указатель не сдвигается.

int buf_puts2(char *str, BUFFER *buf)

Без завершающего \0; указатель сдвигается на конец вставленного фрагмента.

int buf_puts3(char *str, BUFFER *buf)

С завершающим \0; указатель сдвигается на конец вставленного фрагмента. Используется при построчном чтении файла в буфер.

Сводка вариантов buf_puts*:

Функция Завершающий \0 Сдвиг указателя
buf_puts да нет
buf_puts1 нет нет
buf_puts2 нет да
buf_puts3 да да

int buf_dels(BUFFER *buf, int n)

Удаляет n элементов начиная с текущей позиции (удалённые ячейки возвращаются в список свободных).

  • Параметры: buf (BUFFER*) — рабочий буфер; n (int) — число удаляемых элементов.
  • Возврат: код результата.

void def_gets(char *mac1, char *ch)

Копирует из ch в mac1 идентификатор (символы A-Z a-z 0-9 _) до первого символа, не входящего в идентификатор.

  • Параметры: mac1 (char*) — приёмник идентификатора; ch (char*) — исходная строка.

int def_buf_gets_mac(BUFFER *buf, char *mac1)

Считывает из буфера идентификатор (имя макроса при поиске вызова) в mac1.

  • Параметры: buf (BUFFER*) — рабочий буфер; mac1 (char*) — приёмник имени.
  • Возврат: 0 при успехе; BUF_EOF при конце буфера.

int def_buf_gets_param(BUFFER *buf, char *mac1)

Считывает один фактический параметр вызова макроса до символа ; (с учётом вложенных скобок). Если параметр целиком в круглых скобках, скобки заменяются пробелами.

  • Параметры: buf (BUFFER*) — рабочий буфер; mac1 (char*) — приёмник параметра.
  • Возврат: BUF_NULL / BUF_EOF / BUF_ERROR_FACT_PARAM (-5).

int buf_cmpn(BUFFER *buf, int n, char *mac)

Сравнивает n символов буфера (с текущей позиции) со строкой mac.

  • Параметры: buf (BUFFER*) — рабочий буфер; n (int) — число символов; mac (char*) — образец.
  • Возврат: BUF_NULL (совпало) / BUF_EOF (не совпало или конец).

int buf_cmpn2(BUFFER *buf, int n, char *mac1, char *mac2)

Сравнивает n символов буфера с одной из двух строк mac1 / mac2. Применяется для распознавания пар форм директив: #define / #define и #include / #include.

  • Параметры: buf (BUFFER*) — рабочий буфер; n (int) — число символов; mac1, mac2 (char*) — два образца.
  • Возврат: BUF_NULL (совпало с одним из) / BUF_EOF.

5.5. Ошибки и вспомогательные функции

char* def_err_get(int i_err)

Возвращает текст сообщения об ошибке по её коду (внутренний массив error[], индексация error[-i_err]).

  • Параметры: i_err (int) — код ошибки (0..-11, см. раздел 6).
  • Возврат: указатель на строку-описание ошибки.

char* DefineIncludeName(char *str, char *str1)

  • Статус: реализация в DEFMIS.CPP фактически пустая (код закомментирован); функция возвращает str без изменений.
  • Параметры: str, str1 (char*).
  • Возврат: str (без обработки).

char* GetCurrentPath(void)

Возвращает текущий каталог — глобальную строку szPathName (устанавливается в MainPrepr1 из csCurrentPath). Используется при формировании путей #include.

  • Возврат: указатель на строку текущего каталога.

void DeleteComment(char *pszStr)

Обрезает строку по символу !: всё начиная с ! заменяется на " \n" (удаление комментария).

  • Параметры: pszStr (char*) — обрабатываемая строка (изменяется на месте).

int put_error_messege(char *szError1)

Копирует текст ошибки szError1 в глобальную строку szError.

  • Параметры: szError1 (char*) — текст ошибки.
  • Возврат: код результата.
  • Примечание: в Prepr1.h не объявлена; реализована в PREP_MN.CPP и используется внутри библиотеки.

6. Коды ошибок

Определены в Prepr1.h. Тексты возвращаются функцией def_err_get (файл DEF_ER.CPP).

Константа Код Текст сообщения
BUF_NULL 0 all right (успех)
BUF_EOF -1 very small memory
BUF_ABN -2 error in buffer
DEF_ER_BRACKET -3 error in bracket
BUF_ERROR_MACR -4 error in macro define
BUF_ERROR_FACT_PARAM -5 error in fact parameters
BUF_ERROR_NO_SEP -6 not separator or error in formal param
DEF_ERROR_INFILE -7 error when open include file
DEF_ERROR_MEMORY -8 not memory
BUF_ERROR_NO_FORMAL_PARAM -9 error in formal parameters
BUF_ERROR_REDEC -10 redefine macros
ERROR_INIT -11 error not enought memory

Замечание о точности кодов. Некоторые функции при ошибке выделения памяти возвращают «сырой» -1, что по таблице соответствует BUF_EOF с текстом very small memory. Текст не всегда точно отражает реальную причину сбоя — при диагностике следует учитывать контекст вызова, а не только сам код.


7. Зависимости

  • Библиотеки exprint и complex — НЕ используются. PREPR работает только с текстом, без вычисления выражений и комплексной арифметики.
  • Библиотека TMCLibError — упоминается лишь в закомментированном коде. В рабочей MainPrepr1 вместо класса CTmcLibError используется простой параметр char *csError.
  • Вызывающая сторона: PREPR вызывается из библиотеки TMCIndan — обёртка MainPrepr() (TmcRTH_Indan.cpp:846) поверх MainPrepr1 (с переходом от CString / CTmcLibError& к char*). Это единственная точка вызова PREPR.

Общие заголовки (src/Include/), фигурирующие в исходниках PREPR: Prepr1.h, typedef.h, error1.h, tmclimit.h, mainwndw.h, tpl_file.h, tmc_lib.h (предоставляет alloc_mem_err / free_mem), run_flag.h, listing.h, indan.h, descrptr.h.


The PREPR library — API documentation

TMC Suite package. A static library — a preprocessor of the input text template files (.tpl) for the compute kernels. All signatures are taken from the sources in src/libs/PREPR/ and the shared header src/Include/Prepr1.h.


1. Purpose of the library

PREPR is a static library (StaticLibrary, toolset v145, built for Win32 and x64) — a preprocessor of the input text template files (.tpl) for the package's compute kernels (PlanarRT_H / PlanarRT_X).

Important: PREPR is NOT a C/C++ language preprocessor. Even though the syntax of the directives outwardly resembles the C-preprocessor directives (#define, #include), PREPR processes text computation input files (.tpl templates) and has no relation to C++ compilation.

The library performs the following transformations on the input template:

  • processing of #define NAME@BODY directives — the definition of text macros, including ones with formal parameters (the @ separator, parameters separated by ;);
  • processing of #include file_name directives — the substitution of the included file's contents in place of the #include line;
  • removal of comments — everything after the ! character is discarded; the \\ marker is processed as a line join;
  • substitution of macros with actual parameters — recursively, from the last #define to the first;
  • saving all the #define definitions into a temporary file $$vr$$s.prc.

The result of the work is an expanded text file, which is then read by the compute kernel as an ordinary computation input file.

The entry point is the MainPrepr1() function. It is called from the wrapper MainPrepr() (in the TMCIndan library), which in turn is called from the compute kernel. This is the only call site of PREPR.


2. Location of the sources in the build

Sources: src/libs/PREPR/. Header: src/Include/Prepr1.h (in the project it is included as ..\..\INCLUDE\Prepr1.h).

The build project is Prepr.vcxproj (toolset v145, Win32/x64 configurations, type StaticLibrary). The build contains 9 .cpp files:

File Purpose (briefly)
DEF_DEST.CPP Substitution/assignment of macro definitions (def_main_macdef, working with the target body)
DEF_ER.CPP Error message texts (def_err_get)
DEF_ERD.CPP Checking for macro re-definition (def_err_dec)
DEF_MACR.CPP Finding and parsing macro definitions (def_macro, def_separ_mac_name, etc.)
DEF_STR.CPP Low-level operations on the linked string buffer
DEFMIS.CPP Auxiliary functions (DefineIncludeName, GetCurrentPath, DeleteComment)
PREP_LD.CPP Loading/initialization, handling include and the temporary file (def_include, load_vr_file)
PREP_MN.CPP The main preprocessor logic (MainPrepr1, main_preproc, put_error_messege)
READ_BUF.CPP Reading the buffer/lines (read_buf and related)

Files outside the build (legacy / dead code)

The following files are present in the directory but are not included in Prepr.vcxproj and are therefore not described:

File Reason
DR_BUF.CPP An old standalone tester with its own main() function; contains the only external reference to load_preproc_buff
INCPATH.CPP The functions SetIncludePathName / GetIncludePathName — absent from Prepr1.h, not declared externally

3. Data structures

Declared in Prepr1.h.

3.1. BUFFER — a linked character buffer

The main working buffer of the preprocessor. It is implemented as a doubly linked list of characters through two fixed-size arrays NMAX. Insertion and deletion of characters are performed by operations on the list (changing indices) without a physical memory shift.

typedef struct
{
  char  *lsplex;      // character buffer (as a linked list)
  int   *ilex;        // index of the "next" element (list links)
  int    inp_p;       // current reading position
  int    inp_p_nul;   // start of the free-cell list
} BUFFER;
Field Type Meaning
lsplex char* The character buffer array; each cell stores one character, accessed as an element of a linked list
ilex int* The index array: ilex[i] is the number of the element following i in the list. Through it insertion/deletion are implemented without a memory shift
inp_p int The current reading position (the index of the "head" of the processed segment)
inp_p_nul int The start of the list of free cells (from which cells are taken on insertion and to which they are returned on deletion)

The size of the arrays is set by the NMAX macro (by default 30,000,000, see Prepr1.h).

3.2. MACROS — the description of one macro definition

Stores a parsed #define definition: the name, the formal parameters, and the body split into alternating fragments (a "mask").

typedef struct
{
  char   mac[ BUF_STR_N_MAX ];    // the #define body string
  char  *m[ BUF_PAR_MAX ];        // m[0] — the name; m[1..j1] — the formal parameter names
  char  *m1[ BUF_MASKA_MAX ];     // the body pieces between the parameters (the "mask")
  int    im1[ BUF_MASKA_MAX ];    // the index of the formal parameter before the m1[i] piece, or -1
  int    j1;                      // the number of formal parameters
  int    i2;                      // the number of mask elements
} MACROS;
Field Type Meaning
mac[] char[BUF_STR_N_MAX] The #define body string (the original macro body)
m[] char*[BUF_PAR_MAX] Pointers to the names: m[0] — the macro name; m[1]..m[j1] — the names of the formal parameters
m1[] char*[BUF_MASKA_MAX] The text fragments of the body between the parameters — the "mask". The body is stored split into alternating pieces
im1[] int[BUF_MASKA_MAX] For each fragment m1[i] — the index of the formal parameter standing before it; -1 if there is no parameter
j1 int The number of formal parameters
i2 int The number of mask elements (body fragments)

The macro body is stored split into alternating text fragments (m1) with the indices of the formal parameters bound to them (im1). During substitution the fragments are output in sequence, and the actual values are inserted in place of the parameters.

3.3. Size constants and names (Prepr1.h)

Constant Value Meaning
NMAX 30000000 The size of the BUFFER buffer arrays (the number of cells)
BUF_STR_INLINE_MAX 500000 The maximum line length during line-by-line processing
BUF_STR_N_MAX 500000 The size of the MACROS::mac[] field (the #define body length)
BUF_MASKA_MAX 800000 The size of the mask arrays MACROS::m1[] / im1[]
BUF_PAR_MAX 60 The size of the MACROS::m[] array (the name + formal parameters)
VR_MAC_FILE_NAME "$$vr$$s.prc" The name of the temporary file for saving all #defines
VR_MAC_INCLUDE "#include $$vr$$s.prc" The include line for the temporary file
DEF_SEPARATOR '@' The "name+parameters / body" separator in #define

Note: DEF_SEPARATOR is mentioned in the code as the separator character '@'; in Prepr1.h itself it may be defined elsewhere — when in doubt, rely on the DEF_MACR.CPP sources.


4. Syntax of the input-file directives

4.1. #define — a macro definition

#define NAME par1; par2; ... @ BODY
  • Both forms of the directive start are recognized: #define (with a space) and #define (with a tab).
  • The separator @ (DEF_SEPARATOR = '@') separates the "name + list of formal parameters" from the macro body.
  • The formal parameters are listed after the name and separated by the ; (semicolon).
  • If there are no parameters, the directive has the form:

#define NAME@BODY

  • The body may span several lines (a \\ join marker is used for continuation, see below).

4.2. #include — file inclusion

#include file_name
  • Both forms are recognized: #include (with a space) and #include (with a tab).
  • The full path to the included file is formed as <GetCurrentPath()>\<file_name> — that is, the name is treated relative to the current directory (see MainPrepr1, the csCurrentPath parameter).
  • The contents of the included file are substituted in place of the #include line.

4.3. Comments and line joining

Marker Processing Function
! Everything after ! in the line is discarded (replaced with " \n") DeleteComment
\\ A line-join marker: replaced with " \n" (used to continue a long #define body) def_sl

4.4. Calling (using) a macro

NAME(param1; param2; ...)
  • The actual parameters are listed in parentheses and separated by the ;.
  • If an actual parameter is entirely enclosed in parentheses, those parentheses are replaced with spaces during substitution.
  • Substitution is performed recursively by the def_main_macro function — the definitions are traversed from the last #define to the first.
  • All the #define definitions are additionally saved into the temporary file $$vr$$s.prc (the load_vr_file function).

5. Public functions

About the _near, _fortran macros. These are memory-model and calling-convention attributes from old 16/32-bit code. In the modern build they do not affect the semantics. They are omitted from the signatures below. Where a function is declared with such attributes, this is noted in the description.

5.1. The main entry point

void MainPrepr1(char *csTplFileNamePrepr, char *csTplFileName, char *csCurrentPath, char *csError)

The main entry point into the preprocessor. It allocates the internal buffers (szError, szPathName), runs the main processing main_preproc, then frees the buffer (unload_prep_buf) and deletes the temporary file (dest_prep).

  • Parameters:
  • csTplFileNamePrepr (char*) — the name of the output (expanded, processed) file;
  • csTplFileName (char*) — the name of the source template file .tpl;
  • csCurrentPath (char*) — the current directory (used to resolve relative paths in #include);
  • csError (char*) — output: the error string. An empty string ("") on success, otherwise the text of the error that occurred.
  • Return: void (the result is reported through csError).

char* main_preproc(char *szTargetFileName, char *szSourceFileName)

The main processing procedure. It reads the source file line by line, removes comments (DeleteComment), loads the text into the buffer, then sequentially performs:

  1. def_include — the expansion of #include;
  2. load_vr_file — saving all #defines into the temporary file;
  3. def_err_dec — checking for re-definitions;
  4. def_main_macro — the recursive substitution of macros;

and writes the result into the target file szTargetFileName.

  • Parameters:
  • szTargetFileName (char*) — the name of the output (expanded) file;
  • szSourceFileName (char*) — the name of the source template file.
  • Return: a pointer to the global error string szError (the value "" on success, otherwise the error text).

5.2. Lifecycle and memory management

int unload_prep_buf(void)

Frees the memory of the working buffer: the arrays buf.lsplex and buf.ilex.

  • Return: always 0.

int load_preproc_buff(void)

  • Status: the function is declared in Prepr1.h, but there is no implementation in the library. The only external reference to it is in the unused file DR_BUF.CPP (outside the build).
  • Return: undefined (no body).

int dest_prep(void)

Deletes the temporary file <CurrentPath>\$$vr$$s.prc (by calling remove).

  • Return: the return code of remove() (0 on successful deletion).

int buf_init(BUFFER *buf, int n)

Initializes the buffer: allocates the arrays lsplex (char[n]) and ilex (int[n]) via alloc_mem_err, builds the initial free-cell list.

  • Parameters:
  • buf (BUFFER*) — the buffer being initialized;
  • n (int) — the buffer size (the number of cells), usually NMAX.
  • Return: BUF_NULL (0) on success; ERROR_INIT (-11) on out of memory.

int InitCharArray(char **ch, int n)

Initializes a character array: if *ch is not NULL, frees it, then allocates malloc(n).

  • Parameters:
  • ch (char**) — the address of the pointer to the array;
  • n (int) — the size of the allocated block (bytes).
  • Return: 0 on success; -1 on allocation error.

int FreeCharArray(char **ch)

Frees a character array (free(*ch)) and nulls the pointer (*ch = NULL).

  • Parameters: ch (char**) — the address of the pointer to the array.
  • Return: 0.

5.3. Processing #include and macros

int def_include(BUFFER *buf)

Finds #include directives, substitutes the contents of the included file in place of the #include line, then performs line-join processing (def_sl).

  • Parameters: buf (BUFFER*) — the working buffer.
  • Return: 0 on success; -1, DEF_ERROR_INFILE (-7) on an error opening the included file; DEF_ERROR_MEMORY (-8) on out of memory.

int def_sl(BUFFER *buf)

Replaces the line-join marker \\ with " \n" (a line break in a macro body).

  • Parameters: buf (BUFFER*) — the working buffer.
  • Return: 0 on success; -1 on error.

int def_macro(BUFFER *buf)

Finds the next #define / #define directive in the buffer without shifting the current position inp_p.

  • Parameters: buf (BUFFER*) — the working buffer.
  • Return: the position of the found directive (>= 0) or -1 if #define was not found.

int def_main_macro(BUFFER *buf)

Recursively substitutes all macro definitions: the traversal goes from the last #define to the first (for the correct expansion of macros that use other macros).

  • Parameters: buf (BUFFER*) — the working buffer.
  • Return: BUF_NULL (0) on success; a negative error code (< 0) on failure.

int def_main_macdef(BUFFER *buf)

Substitutes one macro definition: parses the header (def_separ_mac_name), forms the body mask (def_set_formal_param), removes the #define line from the buffer, performs the substitution (def_subst_mac).

  • Parameters: buf (BUFFER*) — the working buffer.
  • Return: BUF_NULL (0) on success; a negative error code on failure.
  • Note: declared with the _near fortran attributes, but is actually exported and used.

int def_subst_mac(BUFFER *buf, MACROS *mac)

Finds in the buffer calls of the macro mac->m[0] with actual parameters (read via def_buf_gets_param) and substitutes the macro body, replacing the formal parameters with the actual ones.

  • Parameters:
  • buf (BUFFER*) — the working buffer;
  • mac (MACROS*) — the parsed macro definition.
  • Return: BUF_NULL (0) on success; a negative error code on failure.

int def_set_formal_param(BUFFER *buf, MACROS *mac, int i)

Parses the macro body, filling the mask arrays mac->m1[] (text fragments) and mac->im1[] (formal-parameter indices).

  • Parameters:
  • buf (BUFFER*) — the working buffer;
  • mac (MACROS*) — the definition being filled;
  • i (int) — the start position (of the body) in the buffer.
  • Return: the position of the end of the body (>= 0) or -1 on error.

int def_separ_mac_name(BUFFER *buf, MACROS *mac)

Parses the header #define NAME par1; par2; ... @ BODY: writes the name into mac->m[0], the formal parameters into mac->m[1..], the number of parameters into mac->j1, and finds the @ separator.

  • Parameters:
  • buf (BUFFER*) — the working buffer;
  • mac (MACROS*) — the definition being filled.
  • Return: the position of the @ separator (>= 0) or an error code: BUF_ERROR_NO_SEP (-6), BUF_ERROR_NO_FORMAL_PARAM (-9).

int def_err_dec(BUFFER *buf)

Checks for the re-definition of a macro name (the same name in two #defines).

  • Parameters: buf (BUFFER*) — the working buffer.
  • Return: BUF_NULL (0) if there are no repeats; BUF_ERROR_REDEC (-10) if a re-definition is detected.

int load_vr_file(BUFFER *buf)

Saves all the #define definitions into the temporary file $$vr$$s.prc (VR_MAC_FILE_NAME). Multi-line definitions are split with the join marker \\.

  • Parameters: buf (BUFFER*) — the working buffer.
  • Return: BUF_NULL (0) on success; -1 on a file error; -11 (ERROR_INIT) on out of memory.

5.4. Low-level operations on the linked buffer (DEF_STR.CPP)

int buf_gets(char *str, int n, BUFFER *buf)

Reads a string from the linked buffer into str (up to the terminating \0), but no more than n characters.

  • Parameters: str (char*) — the receiving buffer; n (int) — the character limit; buf (BUFFER*) — the working buffer.
  • Return: BUF_NULL / BUF_EOF / BUF_ABN (see section 6).

int read_buf(char *str, int n, BUFFER *buf)

Similar to buf_gets, but reading ends at \0 or at \n (reads one line).

  • Parameters: as in buf_gets.
  • Return: BUF_NULL / BUF_EOF / BUF_ABN.

int buf_puts(char *str, BUFFER *buf)

Inserts the string str (together with the terminating \0) at the current buffer position without shifting the pointer.

  • Parameters: str (char*) — the string to insert; buf (BUFFER*) — the working buffer.
  • Return: the result code (BUF_NULL on success).

int buf_puts1(char *str, BUFFER *buf)

Like buf_puts, but without the terminating \0; the pointer is not shifted.

int buf_puts2(char *str, BUFFER *buf)

Without the terminating \0; the pointer is shifted to the end of the inserted fragment.

int buf_puts3(char *str, BUFFER *buf)

With the terminating \0; the pointer is shifted to the end of the inserted fragment. Used when reading a file into the buffer line by line.

A summary of the buf_puts* variants:

Function Terminating \0 Pointer shift
buf_puts yes no
buf_puts1 no no
buf_puts2 no yes
buf_puts3 yes yes

int buf_dels(BUFFER *buf, int n)

Deletes n elements starting from the current position (the deleted cells are returned to the free list).

  • Parameters: buf (BUFFER*) — the working buffer; n (int) — the number of elements to delete.
  • Return: the result code.

void def_gets(char *mac1, char *ch)

Copies from ch into mac1 an identifier (the characters A-Z a-z 0-9 _) up to the first character that is not part of the identifier.

  • Parameters: mac1 (char*) — the identifier receiver; ch (char*) — the source string.

int def_buf_gets_mac(BUFFER *buf, char *mac1)

Reads an identifier from the buffer (the macro name when searching for a call) into mac1.

  • Parameters: buf (BUFFER*) — the working buffer; mac1 (char*) — the name receiver.
  • Return: 0 on success; BUF_EOF at the end of the buffer.

int def_buf_gets_param(BUFFER *buf, char *mac1)

Reads one actual parameter of a macro call up to a ; character (taking nested parentheses into account). If the parameter is entirely in parentheses, the parentheses are replaced with spaces.

  • Parameters: buf (BUFFER*) — the working buffer; mac1 (char*) — the parameter receiver.
  • Return: BUF_NULL / BUF_EOF / BUF_ERROR_FACT_PARAM (-5).

int buf_cmpn(BUFFER *buf, int n, char *mac)

Compares n characters of the buffer (from the current position) with the string mac.

  • Parameters: buf (BUFFER*) — the working buffer; n (int) — the number of characters; mac (char*) — the pattern.
  • Return: BUF_NULL (matched) / BUF_EOF (not matched or end).

int buf_cmpn2(BUFFER *buf, int n, char *mac1, char *mac2)

Compares n characters of the buffer with one of two strings mac1 / mac2. Used to recognize the pairs of directive forms: #define / #define and #include / #include.

  • Parameters: buf (BUFFER*) — the working buffer; n (int) — the number of characters; mac1, mac2 (char*) — the two patterns.
  • Return: BUF_NULL (matched one of them) / BUF_EOF.

5.5. Errors and auxiliary functions

char* def_err_get(int i_err)

Returns the text of an error message by its code (the internal array error[], indexed as error[-i_err]).

  • Parameters: i_err (int) — the error code (0..-11, see section 6).
  • Return: a pointer to the error description string.

char* DefineIncludeName(char *str, char *str1)

  • Status: the implementation in DEFMIS.CPP is in fact empty (the code is commented out); the function returns str unchanged.
  • Parameters: str, str1 (char*).
  • Return: str (without processing).

char* GetCurrentPath(void)

Returns the current directory — the global string szPathName (set in MainPrepr1 from csCurrentPath). Used when forming #include paths.

  • Return: a pointer to the current-directory string.

void DeleteComment(char *pszStr)

Truncates the string at the ! character: everything from ! onward is replaced with " \n" (comment removal).

  • Parameters: pszStr (char*) — the string being processed (modified in place).

int put_error_messege(char *szError1)

Copies the error text szError1 into the global string szError.

  • Parameters: szError1 (char*) — the error text.
  • Return: the result code.
  • Note: not declared in Prepr1.h; implemented in PREP_MN.CPP and used inside the library.

6. Error codes

Defined in Prepr1.h. The texts are returned by the def_err_get function (the DEF_ER.CPP file).

Constant Code Message text
BUF_NULL 0 all right (success)
BUF_EOF -1 very small memory
BUF_ABN -2 error in buffer
DEF_ER_BRACKET -3 error in bracket
BUF_ERROR_MACR -4 error in macro define
BUF_ERROR_FACT_PARAM -5 error in fact parameters
BUF_ERROR_NO_SEP -6 not separator or error in formal param
DEF_ERROR_INFILE -7 error when open include file
DEF_ERROR_MEMORY -8 not memory
BUF_ERROR_NO_FORMAL_PARAM -9 error in formal parameters
BUF_ERROR_REDEC -10 redefine macros
ERROR_INIT -11 error not enought memory

A note on the accuracy of the codes. On a memory-allocation error some functions return a "raw" -1, which by the table corresponds to BUF_EOF with the text very small memory. The text does not always accurately reflect the real cause of the failure — when diagnosing, take the call context into account, not just the code itself.


7. Dependencies

  • The exprint and complex libraries are NOT used. PREPR works only with text, without expression evaluation and complex arithmetic.
  • The TMCLibError library is mentioned only in commented-out code. In the working MainPrepr1, instead of the CTmcLibError class, a simple parameter char *csError is used.
  • The calling side: PREPR is called from the TMCIndan library — the wrapper MainPrepr() (TmcRTH_Indan.cpp:846) over MainPrepr1 (with a transition from CString / CTmcLibError& to char*). This is the only call site of PREPR.

The shared headers (src/Include/) that appear in the PREPR sources: Prepr1.h, typedef.h, error1.h, tmclimit.h, mainwndw.h, tpl_file.h, tmc_lib.h (provides alloc_mem_err / free_mem), run_flag.h, listing.h, indan.h, descrptr.h.