Особенности работы препроцессирования lcc
=========================================

Пара слов о запуске препроцессирования
--------------------------------------

Технически в компиляторах lcc и gcc запуск препроцессирования возможен двумя способами:

* Использование компилятора lcc или gcc с дополнительной опцией ``-E`` или ``-P``,
  которая включает режим препроцессирования
* Использование препроцессора cpp. Здесь нужно понимать, что сами препроцессоры из
  поставок lcc и gcc имеют одно и то же имя cpp, но имеют разное устройство

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

Примеры отличия препроцессирования lcc от препроцессирования gcc
----------------------------------------------------------------

Техника работы препроцессирования в компиляторе lcc в некоторых случаях немного
отличается от техники работы препроцессирования в gcc. Это может привести к ошибкам при
сборке программ, которые используют особенности работы препроцессирования gcc.
Рассмотрим несколько примеров, демонстрирующих отличие препроцессирования lcc от
препроцессирования gcc

Пример 1
^^^^^^^^

.. code-block:: c

  #define MACRO a.b.c
  MACRO

.. code-block:: bash

  $ gcc -E t.c
  a.b.c

.. code-block:: bash

  $ lcc -E t.c
  a . b . c

В препроцессорной выдаче lcc появились лишние пробелы вокруг точек. При сборке некоторых
программ это может оказаться критичным

Пример 2
^^^^^^^^

.. code-block:: c

  #define MACRO a >>> b
  MACRO

.. code-block:: bash

  $ gcc -E t.c
  a >>> b

.. code-block:: bash

  $ lcc -E t.c
  a >> > c

В препроцессорной выдаче lcc появился лишний пробел между второй и третьей угловой
скобкой. При сборке некоторых программ это может оказаться критичным

Причина отличия препроцессирования lcc от препроцессирования gcc и метод исправления проблемы
---------------------------------------------------------------------------------------------

В современных версиях компиляторов препроцессор встроен в компилятор, и не является
отдельной программой. У gcc и lcc номинально присутствует отдельная компонента cpp, но
при работе cpp в реальности используется тот же самый препроцессор, который встроен
в компилятор

Компилятор lcc построен на базе frontend'а от EDG. Разработчики EDG позиционируют свой
препроцессор как "препроцессор с языков C и C++". При этом препроцессор в gcc условно
можно считать "универсальным препроцессором" (хотя в реальности это не совсем так).
Препроцессор от EDG тоже умеет работать как "универсальный препроцессор", но технически
это является отдельным режимом работы. Таким образом, в lcc существуют два режима работы
препроцессора: "режим препроцессора с языков C и C++" и "режим универсального
препроцессора". При этом одновременно может работать только один из двух режимов.
Препроцессор в gcc всегда работает в гибридном режиме, при котором в некоторых случаях
умеет на ходу подстраиваться под один из двух режимов работы

Выбор одного из двух режимов в lcc осуществляется на основании типа исходного файла:
если исходный файл имеет тип C или C++, то препроцессор работает в "режиме
препроцессора с языков C и C++", иначе в "режиме универсального препроцессора". Тип
файла определяется по стандартным правилам: если указана опциия ``-x``, то тип файла
определяется в соответствии с опцией ``-x``, иначе по расширению имени файла.
Исторически сложилось так, что в lcc для включения "режима универсального препроцессора"
требуется использовать тип файла "ассемблерный файл, требюующий предварительного
препроцессирования". Это означает, что либо файл должен иметь расширение \*.S, либо
в командной строке запуска компилятора где-то слева от имени файла нужно добавить опцию
``-xassembler-with-cpp``

В качестве примера опишем способ исправления проблемы, описанной выше по именем
"Пример 1"

.. code-block:: c

  #define MACRO a.b.c
  MACRO

.. code-block:: bash

  $ gcc -E t.c
  a.b.c

.. code-block:: bash

  $ lcc -E t.c
  a . b . c

.. code-block:: bash

  $ lcc -xassembler-with-cpp -E t.c
  a.b.c

.. code-block:: bash

  $ cp t.c t.S
  $ lcc -E t.S
  a.b.c


Почему нельзя в lcc включить режим универсального препроцессора по умолчанию
----------------------------------------------------------------------------

Может показаться, что в lcc можно было бы включить "режим универсального препроцессора"
по умолчанию и не возиться с выбором одного из двух режимов. Но так не получится, потому
что не все коды, написанные на C или C++, корректно препроцессируются в "режиме
универсального препроцессора"

.. code-block:: c

  #define MACRO -1
  x=0-MACRO;

.. code-block:: bash

  $ gcc -E t.c
  x=0- -1;

.. code-block:: bash

  $ lcc -E t.c
  x=0- -1;

.. code-block:: bash

  $ lcc -xassembler-with-cpp -E t.c
  x=0--1;

Здесь имеем обратную ситуацию. Чтобы на выходе получить текст, который в будет корректен
с точки зрения языков C и C++, требуется добавить лишний пробел. При использовании в lcc
"режима универсального препроцессора" этого не происходит и препроцессированный текст
получается некорректным с точки зрения языков C и C++


