Препроцессинг, условная компиляция, настройка

Сразу после лексического анализа и перед синтаксическим анализом каждый модуль Фикус проходит предварительную обработку. На данном этапе возможна вставка, исключение и модификация любых частей модуля, включая директивы import и другие, основываясь на текущих условиях среды и желаемой конфигурации. Определение символов препроцессора

Во-первых, существует опция -D компилятора Фикус, используемая для задания определённых символов препроцессора. Она выглядит следующим образом:

-D символ_имени или
-D символ_имени=значение

где значение может быть:

  • логическое значение: true (синонимы TRUE, ON, on), false (синонимы FALSE, OFF, off);
  • целое число;
  • строка текста.

Отсутствующее значение заменяется на true.

Определяемые символы размещаются в начале каждого компилируемого модуля в следующем виде:

@define символ значение

Можно задать свои определения:

@define символ выражение_препроцессора

где выражение_препроцессора это арифметическое выражение, включающее литералы, ранее определённые символы и встроенные функции и операции:

  • abs(x) – абсолютное значение
  • int(x) – преобразование строки или логического значения в число
  • string(x) – преобразование логического значения или числа в строку
  • defined(x)true, если символ x определен
  • f"..." – интерполяция строки, см. раздел Строковые выражения.

Обратная операция

@undef символ

удаляет определение, так что последующий вызов defined(символ) возвращает false, пока символ снова не будет объявлен.

Условная компиляция

При компиляции можно включать или исключать части компилируемого модуля с помощью директив условной компиляции, аналогичных директивам C:

@if выражение_препроцессора
    ...
[@elif другое_выражение_препроцессора
    ...] // необязательные ветви else_if
[@else
    ...] // необязательная ветвь else
@endif // соответствующая закрывающая директива для @if

Также можно использовать и сокращенные формы:

@ifdef символ // эквивалентно @if defined(символ)
...

@ifndef символ  // эквивалентно @if !defined(символ)
...

Отчёт о проблемах

также аналогичен препроцессору C:

// выводит
// '<локализация>: предупреждение: <результат_выражения>'
// и продолжает компиляцию
@warning выражение_препроцессора

// выводит
// '<локализация>: ошибка: <результат_выражения>'
// и прерывает компиляцию
@error выражение_препроцессора

Аргументfvb выражений @error и @warning должны быть строковыми выражениями или выражениями, результатом которых является строка.

Приведем умозрительный пример.

Допустим, у нас есть модуль win32_ui.fx:

// win32_ui.fx: Backend интерфейса пользователя Win32, зависит от Windows API
...

и модуль linux_ui.fx:

// linux_ui.fx: Интерфейс пользователя на основе GTK+ с аналогичным API, как у Win32 UI
...

Теперь кросс-платформенный код может выглядеть так:

// my_app.fx
@if Platform == "Win32"
import win32_ui as ui
@elif Platform == "Linux"
import linux_ui as ui
@else
@error "Неподдерживаемая платформа"
@endif

ui.make_button("Нажмите меня!", fun() {println("Привет!")})
ui.run()

Этот код можно собрать на разных платформах с помощью команды:

ficus -app -D Platform=<целевая_платформа> my_app.fx

Подмена результата

Помимо условной компиляции, значения выражений препроцессора можно вставить непосредственно в итоговый код с помощью специальной директивы @{выражение_препроцессора}, например:

// myexample.fx
...
@define x 5
@define y x + opt
val x = @{y*y}
println(x)

При сборке с аргументом ficus -D opt=1 myexample.fx пример выше выведет 36.

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